diff options
Diffstat (limited to 'drivers/hwmon')
256 files changed, 40853 insertions, 12485 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 47ac20aee06f..7ac3daaf59ce 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -38,19 +38,6 @@ config HWMON_DEBUG_CHIP comment "Native drivers" -config SENSORS_AB8500 - tristate "AB8500 thermal monitoring" - depends on AB8500_GPADC && AB8500_BM && (IIO = y) - default n - help - If you say yes here you get support for the thermal sensor part - of the AB8500 chip. The driver includes thermal management for - AB8500 die and two GPADC channels. The GPADC channel are preferably - used to access sensors outside the AB8500 chip. - - This driver can also be built as a module. If so, the module - will be called abx500-temp. - config SENSORS_ABITUGURU tristate "Abit uGuru (rev 1 & 2)" depends on X86 && DMI @@ -113,6 +100,7 @@ config SENSORS_AD7418 config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" depends on I2C + depends on SENSORS_LM90=n help If you say yes here you get support for Analog Devices ADM1021 and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, @@ -187,6 +175,7 @@ config SENSORS_ADM9240 config SENSORS_ADT7X10 tristate + select REGMAP help This module contains common code shared by the ADT7310/ADT7320 and ADT7410/ADT7420 temperature monitoring chip drivers. @@ -257,6 +246,29 @@ config SENSORS_ADT7475 This driver can also be built as a module. If so, the module will be called adt7475. +config SENSORS_AHT10 + tristate "Aosong AHT10" + depends on I2C + help + If you say yes here, you get support for the Aosong AHT10 + temperature and humidity sensors + + This driver can also be built as a module. If so, the module + will be called aht10. + +config SENSORS_AQUACOMPUTER_D5NEXT + tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, Farbwerk 360, High Flow Next" + depends on USB_HID + select CRC16 + help + If you say yes here you get support for sensors and fans of + the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan + controllers, Farbwerk and Farbwerk 360 RGB controllers, High Flow + Next sensor, where available. + + This driver can also be built as a module. If so, the module + will be called aquacomputer_d5next. + config SENSORS_AS370 tristate "Synaptics AS370 SoC hardware monitoring driver" help @@ -280,6 +292,15 @@ config SENSORS_ASC7621 This driver can also be built as a module. If so, the module will be called asc7621. +config SENSORS_AXI_FAN_CONTROL + tristate "Analog Devices FAN Control HDL Core driver" + help + If you say yes here you get support for the Analog Devices + AXI HDL FAN monitoring core. + + This driver can also be built as a module. If so, the module + will be called axi-fan-control + config SENSORS_K8TEMP tristate "AMD Athlon64/FX or Opteron temperature sensor" depends on X86 && PCI @@ -361,7 +382,7 @@ config SENSORS_ARM_SCPI config SENSORS_ASB100 tristate "Asus ASB100 Bach" - depends on X86 && I2C + depends on (X86 || COMPILE_TEST) && I2C select HWMON_VID help If you say yes here you get support for the ASB100 Bach sensor @@ -372,6 +393,7 @@ config SENSORS_ASB100 config SENSORS_ASPEED tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver" + depends on ARCH_ASPEED || COMPILE_TEST depends on THERMAL || THERMAL=n select REGMAP help @@ -395,6 +417,55 @@ config SENSORS_ATXP1 This driver can also be built as a module. If so, the module will be called atxp1. +config SENSORS_BT1_PVT + tristate "Baikal-T1 Process, Voltage, Temperature sensor driver" + depends on MIPS_BAIKAL_T1 || COMPILE_TEST + select POLYNOMIAL + help + If you say yes here you get support for Baikal-T1 PVT sensor + embedded into the SoC. + + This driver can also be built as a module. If so, the module will be + called bt1-pvt. + +config SENSORS_BT1_PVT_ALARMS + bool "Enable Baikal-T1 PVT sensor alarms" + depends on SENSORS_BT1_PVT + help + Baikal-T1 PVT IP-block provides threshold registers for each + supported sensor. But the corresponding interrupts might be + generated by the thresholds comparator only in synchronization with + a data conversion. Additionally there is only one sensor data can + be converted at a time. All of these makes the interface impossible + to be used for the hwmon alarms implementation without periodic + switch between the PVT sensors. By default the data conversion is + performed on demand from the user-space. If this config is enabled + the data conversion will be periodically performed and the data will be + saved in the internal driver cache. + +config SENSORS_CORSAIR_CPRO + tristate "Corsair Commander Pro controller" + depends on HID + help + If you say yes here you get support for the Corsair Commander Pro + controller. + + This driver can also be built as a module. If so, the module + will be called corsair-cpro. + +config SENSORS_CORSAIR_PSU + tristate "Corsair PSU HID controller" + depends on HID + help + If you say yes here you get support for Corsair PSUs with a HID + interface. + Currently this driver supports the (RM/HX)550i, (RM/HX)650i, + (RM/HX)750i, (RM/HX)850i, (RM/HX)1000i and HX1200i power supplies + by Corsair. + + This driver can also be built as a module. If so, the module + will be called corsair-psu. + config SENSORS_DRIVETEMP tristate "Hard disk drives with temperature sensors" depends on SCSI && ATA @@ -403,7 +474,7 @@ config SENSORS_DRIVETEMP hard disk drives. This driver can also be built as a module. If so, the module - will be called satatemp. + will be called drivetemp. config SENSORS_DS620 tristate "Dallas Semiconductor DS620" @@ -433,6 +504,7 @@ config SENSORS_DS1621 config SENSORS_DELL_SMM tristate "Dell laptop SMM BIOS hwmon driver" depends on X86 + imply THERMAL help This hwmon driver adds support for reporting temperature of different sensors and controls the fans on Dell laptops via System Management @@ -441,6 +513,21 @@ config SENSORS_DELL_SMM When option I8K is also enabled this driver provides legacy /proc/i8k userspace interface for i8kutils package. +config I8K + bool "Legacy /proc/i8k interface of Dell laptop SMM BIOS hwmon driver" + depends on SENSORS_DELL_SMM + depends on PROC_FS + help + This option enables the legacy /proc/i8k userspace interface of the + dell-smm-hwmon driver. The character file /proc/i8k exposes the BIOS + version, temperatures and allows control of fan speeds of some Dell + laptops. Sometimes it also reports power and hotkey status. + + This interface is required to run programs from the i8kutils package. + + Say Y if you intend to run userspace programs that use this interface. + Say N otherwise. + config SENSORS_DA9052_ADC tristate "Dialog DA9052/DA9053 ADC" depends on PMIC_DA9052 @@ -471,6 +558,16 @@ config SENSORS_I5K_AMB This driver can also be built as a module. If so, the module will be called i5k_amb. +config SENSORS_SPARX5 + tristate "Sparx5 SoC temperature sensor" + depends on ARCH_SPARX5 || COMPILE_TEST + help + If you say yes here you get support for temperature monitoring + with the Microchip Sparx5 SoC. + + This driver can also be built as a module. If so, the module + will be called sparx5-temp. + config SENSORS_F71805F tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG" depends on !PPC @@ -514,6 +611,15 @@ config SENSORS_F75375S This driver can also be built as a module. If so, the module will be called f75375s. +config SENSORS_GSC + tristate "Gateworks System Controller ADC" + depends on MFD_GATEWORKS_GSC + help + Support for the Gateworks System Controller A/D converters. + + To compile this driver as a module, choose M here: + the module will be called gsc-hwmon. + config SENSORS_MC13783_ADC tristate "Freescale MC13783/MC13892 ADC" depends on MFD_MC13XXX @@ -522,7 +628,7 @@ config SENSORS_MC13783_ADC config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" - depends on X86 && I2C + depends on (X86 || COMPILE_TEST) && I2C help If you say yes here you get support for the following Fujitsu Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes, @@ -715,6 +821,18 @@ config SENSORS_POWR1220 This driver can also be built as a module. If so, the module will be called powr1220. +config SENSORS_LAN966X + tristate "Microchip LAN966x Hardware Monitoring" + depends on SOC_LAN966 || COMPILE_TEST + select REGMAP + select POLYNOMIAL + help + If you say yes here you get support for temperature monitoring + on the Microchip LAN966x SoC. + + This driver can also be built as a module. If so, the module + will be called lan966x-hwmon. + config SENSORS_LINEAGE tristate "Lineage Compact Power Line Power Entry Module" depends on I2C @@ -785,6 +903,18 @@ config SENSORS_LTC2990 This driver can also be built as a module. If so, the module will be called ltc2990. +config SENSORS_LTC2992 + tristate "Linear Technology LTC2992" + depends on I2C + depends on GPIOLIB + help + If you say yes here you get support for Linear Technology LTC2992 + I2C System Monitor. The LTC2992 measures current, voltage, and + power of two supplies. + + This driver can also be built as a module. If so, the module will + be called ltc2992. + config SENSORS_LTC4151 tristate "Linear Technology LTC4151" depends on I2C @@ -849,7 +979,7 @@ config SENSORS_LTC4261 config SENSORS_LTQ_CPUTEMP bool "Lantiq cpu temperature sensor driver" - depends on LANTIQ + depends on SOC_XWAY help If you say yes here you get support for the temperature sensor inside your CPU. @@ -864,6 +994,15 @@ config SENSORS_MAX1111 This driver can also be built as a module. If so, the module will be called max1111. +config SENSORS_MAX127 + tristate "Maxim MAX127 12-bit 8-channel Data Acquisition System" + depends on I2C + help + Say y here to support Maxim's MAX127 DAS chips. + + This driver can also be built as a module. If so, the module + will be called max127. + config SENSORS_MAX16065 tristate "Maxim MAX16065 System Manager and compatibles" depends on I2C @@ -928,6 +1067,28 @@ config SENSORS_MAX31730 This driver can also be built as a module. If so, the module will be called max31730. +config SENSORS_MAX31760 + tristate "MAX31760 fan speed controller" + depends on I2C + select REGMAP_I2C + help + Support for the Analog Devices MAX31760 Precision Fan-Speed + Controller. MAX31760 integrates temperature sensing along with + precision PWM fan control. + + This driver can also be built as a module. If so, the module + will be called max31760. + +config SENSORS_MAX6620 + tristate "Maxim MAX6620 fan controller" + depends on I2C + help + If you say yes here you get support for the MAX6620 + fan controller. + + This driver can also be built as a module. If so, the module + will be called max6620. + config SENSORS_MAX6621 tristate "Maxim MAX6621 sensor chip" depends on I2C @@ -955,6 +1116,7 @@ config SENSORS_MAX6639 config SENSORS_MAX6642 tristate "Maxim MAX6642 sensor chip" depends on I2C + depends on SENSORS_LM90=n help If you say yes here you get support for MAX6642 sensor chip. MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor @@ -1007,7 +1169,7 @@ config SENSORS_MCP3021 will be called mcp3021. config SENSORS_MLXREG_FAN - tristate "Mellanox Mellanox FAN driver" + tristate "Mellanox FAN driver" depends on MELLANOX_PLATFORM imply THERMAL select REGMAP @@ -1029,6 +1191,17 @@ config SENSORS_TC654 This driver can also be built as a module. If so, the module will be called tc654. +config SENSORS_TPS23861 + tristate "Texas Instruments TPS23861 PoE PSE" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments + TPS23861 802.3at PoE PSE chips. + + This driver can also be built as a module. If so, the module + will be called tps23861. + config SENSORS_MENF21BMC_HWMON tristate "MEN 14F021P00 BMC Hardware Monitoring" depends on MFD_MENF21BMC @@ -1039,6 +1212,16 @@ config SENSORS_MENF21BMC_HWMON This driver can also be built as a module. If so the module will be called menf21bmc_hwmon. +config SENSORS_MR75203 + tristate "Moortec Semiconductor MR75203 PVT Controller" + select REGMAP_MMIO + help + If you say yes here you get support for Moortec MR75203 + PVT controller. + + This driver can also be built as a module. If so, the module + will be called mr75203. + config SENSORS_ADCXX tristate "National Semiconductor ADCxxxSxxx" depends on SPI_MASTER @@ -1073,8 +1256,8 @@ config SENSORS_LM70 depends on SPI_MASTER help If you say yes here you get support for the National Semiconductor - LM70, LM71, LM74 and Texas Instruments TMP121/TMP123 digital tempera- - ture sensor chips. + LM70, LM71, LM74 and Texas Instruments TMP121/TMP123, TMP122/TMP124, + TMP125 digital temperature sensor chips. This driver can also be built as a module. If so, the module will be called lm70. @@ -1097,6 +1280,7 @@ config SENSORS_LM75 temperature sensor chip, with models including: - Analog Devices ADT75 + - Atmel (now Microchip) AT30TS74 - Dallas Semiconductor DS75, DS1775 and DS7505 - Global Mixed-mode Technology (GMT) G751 - Maxim MAX6625 and MAX6626 @@ -1153,6 +1337,7 @@ config SENSORS_LM80 config SENSORS_LM83 tristate "National Semiconductor LM83 and compatibles" depends on I2C + select REGMAP help If you say yes here you get support for National Semiconductor LM82 and LM83 sensor chips. @@ -1187,12 +1372,16 @@ config SENSORS_LM90 tristate "National Semiconductor LM90 and compatibles" depends on I2C help - If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, - Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, - MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, - Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, and - Texas Instruments TMP451 sensor chips. + If you say yes here you get support for National Semiconductor LM84, + LM90, LM86, LM89 and LM99, Analog Devices ADM1020, ADM2021, ADM1021A, + ADM1023, ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A, + Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, + MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, + MAX6696, + ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218, + Winbond/Nuvoton W83L771W/G/AWG/ASG, + Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461 + sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -1278,8 +1467,8 @@ config SENSORS_PC87427 will be called pc87427. config SENSORS_NTC_THERMISTOR - tristate "NTC thermistor support from Murata" - depends on !OF || IIO=n || IIO + tristate "NTC thermistor support" + depends on IIO depends on THERMAL || !THERMAL_OF help This driver supports NTC thermistors sensor reading and its @@ -1304,10 +1493,23 @@ config SENSORS_NCT6683 This driver can also be built as a module. If so, the module will be called nct6683. +config SENSORS_NCT6775_CORE + tristate + select REGMAP + help + This module contains common code shared by the platform and + i2c versions of the nct6775 driver; it is not useful on its + own. + + If built as a module, the module will be called + nct6775-core. + config SENSORS_NCT6775 - tristate "Nuvoton NCT6775F and compatibles" + tristate "Platform driver for Nuvoton NCT6775F and compatibles" depends on !PPC + depends on ACPI_WMI || ACPI_WMI=n select HWMON_VID + select SENSORS_NCT6775_CORE help If you say yes here you get support for the hardware monitoring functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D, @@ -1318,6 +1520,23 @@ config SENSORS_NCT6775 This driver can also be built as a module. If so, the module will be called nct6775. +config SENSORS_NCT6775_I2C + tristate "I2C driver for Nuvoton NCT6775F and compatibles" + depends on I2C + select REGMAP_I2C + select SENSORS_NCT6775_CORE + help + If you say yes here you get support for the hardware monitoring + functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D, + NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, and compatible + Super-I/O chips via their I2C interface. + + If you're not building a kernel for a BMC, this is probably + not the driver you want (see CONFIG_SENSORS_NCT6775). + + This driver can also be built as a module. If so, the module + will be called nct6775-i2c. + config SENSORS_NCT7802 tristate "Nuvoton NCT7802Y" depends on I2C @@ -1331,10 +1550,12 @@ config SENSORS_NCT7802 config SENSORS_NCT7904 tristate "Nuvoton NCT7904" - depends on I2C + depends on I2C && WATCHDOG + select WATCHDOG_CORE help If you say yes here you get support for the Nuvoton NCT7904 - hardware monitoring chip, including manual fan speed control. + hardware monitoring chip, including manual fan speed control + and support for the integrated watchdog. This driver can also be built as a module. If so, the module will be called nct7904. @@ -1364,6 +1585,26 @@ config SENSORS_NSA320 This driver can also be built as a module. If so, the module will be called nsa320-hwmon. +config SENSORS_NZXT_KRAKEN2 + tristate "NZXT Kraken X42/X51/X62/X72 liquid coolers" + depends on USB_HID + help + If you say yes here you get support for hardware monitoring for the + NZXT Kraken X42/X52/X62/X72 all-in-one CPU liquid coolers. + + This driver can also be built as a module. If so, the module + will be called nzxt-kraken2. + +config SENSORS_NZXT_SMART2 + tristate "NZXT RGB & Fan Controller/Smart Device v2" + depends on USB_HID + help + If you say yes here you get support for hardware monitoring for the + NZXT RGB & Fan Controller/Smart Device v2. + + This driver can also be built as a module. If so, the module + will be called nzxt-smart2. + source "drivers/hwmon/occ/Kconfig" config SENSORS_PCF8591 @@ -1379,6 +1620,8 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. +source "drivers/hwmon/peci/Kconfig" + source "drivers/hwmon/pmbus/Kconfig" config SENSORS_PWM_FAN @@ -1403,6 +1646,36 @@ config SENSORS_RASPBERRYPI_HWMON This driver can also be built as a module. If so, the module will be called raspberrypi-hwmon. +config SENSORS_SL28CPLD + tristate "Kontron sl28cpld hardware monitoring driver" + depends on MFD_SL28CPLD || COMPILE_TEST + help + If you say yes here you get support for the fan supervisor of the + sl28cpld board management controller. + + This driver can also be built as a module. If so, the module + will be called sl28cpld-hwmon. + +config SENSORS_SBTSI + tristate "Emulated SB-TSI temperature sensor" + depends on I2C + help + If you say yes here you get support for emulated temperature + sensors on AMD SoCs with SB-TSI interface connected to a BMC device. + + This driver can also be built as a module. If so, the module will + be called sbtsi_temp. + +config SENSORS_SBRMI + tristate "Emulated SB-RMI sensor" + depends on I2C + help + If you say yes here you get support for emulated RMI + sensors on AMD SoCs with APML interface connected to a BMC device. + + This driver can also be built as a module. If so, the module will + be called sbrmi. + config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." depends on GPIOLIB || COMPILE_TEST @@ -1435,6 +1708,17 @@ config SENSORS_SHT3x This driver can also be built as a module. If so, the module will be called sht3x. +config SENSORS_SHT4x + tristate "Sensiron humidity and temperature sensors. SHT4x and compat." + depends on I2C + select CRC8 + help + If you say yes here you get support for the Sensiron SHT40, SHT41 and + SHT45 humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called sht4x. + config SENSORS_SHTC1 tristate "Sensiron humidity and temperature sensors. SHTC1 and compat." depends on I2C @@ -1472,6 +1756,16 @@ config SENSORS_SIS5595 This driver can also be built as a module. If so, the module will be called sis5595. +config SENSORS_SY7636A + tristate "Silergy SY7636A" + depends on MFD_SY7636A + help + If you say yes here you get support for the thermistor readout of + the Silergy SY7636A PMIC. + + This driver can also be built as a module. If so, the module + will be called sy7636a-hwmon. + config SENSORS_DME1737 tristate "SMSC DME1737, SCH311x and compatibles" depends on I2C && !PPC @@ -1505,6 +1799,19 @@ config SENSORS_EMC2103 This driver can also be built as a module. If so, the module will be called emc2103. +config SENSORS_EMC2305 + tristate "Microchip EMC2305 and compatible EMC2301/2/3" + depends on I2C + imply THERMAL + help + If you say yes here you get support for the Microchip EMC2305 + fan controller chips. + The Microchip EMC2305 is a fan controller for up to 5 fans. + Fan rotation speeds are reported in RPM. + + This driver can also be built as a module. If so, the module + will be called emc2305. + config SENSORS_EMC6W201 tristate "SMSC EMC6W201" depends on I2C @@ -1682,6 +1989,18 @@ config SENSORS_INA2XX This driver can also be built as a module. If so, the module will be called ina2xx. +config SENSORS_INA238 + tristate "Texas Instruments INA238" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the INA238 power monitor + chip. This driver supports voltage, current, power and temperature + measurements as well as alarm configuration. + + This driver can also be built as a module. If so, the module + will be called ina238. + config SENSORS_INA3221 tristate "Texas Instruments INA3221 Triple Power Monitor" depends on I2C @@ -1749,9 +2068,10 @@ config SENSORS_TMP108 config SENSORS_TMP401 tristate "Texas Instruments TMP401 and compatibles" depends on I2C + select REGMAP help If you say yes here you get support for Texas Instruments TMP401, - TMP411, TMP431, TMP432, TMP435, and TMP461 temperature sensor chips. + TMP411, TMP431, TMP432, and TMP435 temperature sensor chips. This driver can also be built as a module. If so, the module will be called tmp401. @@ -1766,6 +2086,17 @@ config SENSORS_TMP421 This driver can also be built as a module. If so, the module will be called tmp421. +config SENSORS_TMP464 + tristate "Texas Instruments TMP464 and compatible" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments TMP464 + and TMP468 temperature sensor chips. + + This driver can also be built as a module. If so, the module + will be called tmp464. + config SENSORS_TMP513 tristate "Texas Instruments TMP513 and compatibles" depends on I2C @@ -1988,6 +2319,17 @@ config SENSORS_XGENE If you say yes here you get support for the temperature and power sensors for APM X-Gene SoC. +config SENSORS_INTEL_M10_BMC_HWMON + tristate "Intel MAX10 BMC Hardware Monitoring" + depends on MFD_INTEL_M10_BMC + help + This driver provides support for the hardware monitoring functionality + on Intel MAX10 BMC chip. + + This BMC Chip is used on Intel FPGA PCIe Acceleration Cards (PAC). Its + sensors monitor various telemetry data of different components on the + card, e.g. board temperature, FPGA core temperature/voltage/current. + if ACPI comment "ACPI drivers" @@ -2014,6 +2356,30 @@ config SENSORS_ATK0110 This driver can also be built as a module. If so, the module will be called asus_atk0110. +config SENSORS_ASUS_WMI + tristate "ASUS WMI X370/X470/B450/X399" + depends on ACPI_WMI + help + If you say yes here you get support for the ACPI hardware monitoring + interface found in X370/X470/B450/X399 ASUS motherboards. This driver + will provide readings of fans, voltages and temperatures through the system + firmware. + + This driver can also be built as a module. If so, the module + will be called asus_wmi_sensors. + +config SENSORS_ASUS_EC + tristate "ASUS EC Sensors" + depends on X86 + help + If you say yes here you get support for the ACPI embedded controller + hardware monitoring interface found in ASUS motherboards. The driver + currently supports B550/X570 boards, although other ASUS boards might + provide this monitoring interface as well. + + This driver can also be built as a module. If so, the module + will be called asus_ec_sensors. + endif # ACPI endif # HWMON diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 613f50987965..11d076cad8a2 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -9,6 +9,8 @@ obj-$(CONFIG_HWMON_VID) += hwmon-vid.o # APCI drivers obj-$(CONFIG_SENSORS_ACPI_POWER) += acpi_power_meter.o obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o +obj-$(CONFIG_SENSORS_ASUS_EC) += asus-ec-sensors.o +obj-$(CONFIG_SENSORS_ASUS_WMI) += asus_wmi_sensors.o # Native drivers # asb100, then w83781d go first, as they can override other drivers' addresses. @@ -21,7 +23,6 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o -obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o obj-$(CONFIG_SENSORS_AD7314) += ad7314.o @@ -45,14 +46,20 @@ obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o +obj-$(CONFIG_SENSORS_AHT10) += aht10.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o +obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o +obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o +obj-$(CONFIG_SENSORS_BT1_PVT) += bt1-pvt.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o +obj-$(CONFIG_SENSORS_CORSAIR_CPRO) += corsair-cpro.o +obj-$(CONFIG_SENSORS_CORSAIR_PSU) += corsair-psu.o obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o obj-$(CONFIG_SENSORS_DELL_SMM) += dell-smm-hwmon.o @@ -62,6 +69,7 @@ obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o +obj-$(CONFIG_SENSORS_EMC2305) += emc2305.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o @@ -73,6 +81,7 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_G762) += g762.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o @@ -84,11 +93,14 @@ obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o obj-$(CONFIG_SENSORS_INA209) += ina209.o obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o +obj-$(CONFIG_SENSORS_INA238) += ina238.o obj-$(CONFIG_SENSORS_INA3221) += ina3221.o +obj-$(CONFIG_SENSORS_INTEL_M10_BMC_HWMON) += intel-m10-bmc-hwmon.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_JC42) += jc42.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o +obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o obj-$(CONFIG_SENSORS_LOCHNAGAR) += lochnagar-hwmon.o obj-$(CONFIG_SENSORS_LM63) += lm63.o @@ -112,6 +124,7 @@ obj-$(CONFIG_SENSORS_LTC2947) += ltc2947-core.o obj-$(CONFIG_SENSORS_LTC2947_I2C) += ltc2947-i2c.o obj-$(CONFIG_SENSORS_LTC2947_SPI) += ltc2947-spi.o obj-$(CONFIG_SENSORS_LTC2990) += ltc2990.o +obj-$(CONFIG_SENSORS_LTC2992) += ltc2992.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o @@ -120,12 +133,15 @@ obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o +obj-$(CONFIG_SENSORS_MAX127) += max127.o obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1668) += max1668.o obj-$(CONFIG_SENSORS_MAX197) += max197.o obj-$(CONFIG_SENSORS_MAX31722) += max31722.o obj-$(CONFIG_SENSORS_MAX31730) += max31730.o +obj-$(CONFIG_SENSORS_MAX31760) += max31760.o +obj-$(CONFIG_SENSORS_MAX6620) += max6620.o obj-$(CONFIG_SENSORS_MAX6621) += max6621.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o @@ -135,15 +151,22 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_TC654) += tc654.o +obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o +obj-$(CONFIG_SENSORS_MR75203) += mr75203.o obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o +obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o +nct6775-objs := nct6775-platform.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o +obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o 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_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o @@ -151,19 +174,25 @@ obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o +obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o +obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o +obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o +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_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o +obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o +obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o obj-$(CONFIG_SENSORS_TC74) += tc74.o obj-$(CONFIG_SENSORS_THMC50) += thmc50.o @@ -172,6 +201,7 @@ obj-$(CONFIG_SENSORS_TMP103) += tmp103.o obj-$(CONFIG_SENSORS_TMP108) += tmp108.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o +obj-$(CONFIG_SENSORS_TMP464) += tmp464.o obj-$(CONFIG_SENSORS_TMP513) += tmp513.o obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress-hwmon.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o @@ -186,6 +216,7 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o obj-$(CONFIG_SENSORS_OCC) += occ/ +obj-$(CONFIG_SENSORS_PECI) += peci/ obj-$(CONFIG_PMBUS) += pmbus/ ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c deleted file mode 100644 index 53f3379d799d..000000000000 --- a/drivers/hwmon/ab8500.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson 2010 - 2013 - * Author: Martin Persson <martin.persson@stericsson.com> - * Hongbo Zhang <hongbo.zhang@linaro.org> - * - * When the AB8500 thermal warning temperature is reached (threshold cannot - * be changed by SW), an interrupt is set, and if no further action is taken - * within a certain time frame, kernel_power_off will be called. - * - * When AB8500 thermal shutdown temperature is reached a hardware shutdown of - * the AB8500 will occur. - */ - -#include <linux/err.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/mfd/abx500.h> -#include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/power/ab8500.h> -#include <linux/reboot.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/iio/consumer.h> -#include "abx500.h" - -#define DEFAULT_POWER_OFF_DELAY (HZ * 10) -#define THERMAL_VCC 1800 -#define PULL_UP_RESISTOR 47000 - -#define AB8500_SENSOR_AUX1 0 -#define AB8500_SENSOR_AUX2 1 -#define AB8500_SENSOR_BTEMP_BALL 2 -#define AB8500_SENSOR_BAT_CTRL 3 -#define NUM_MONITORED_SENSORS 4 - -struct ab8500_gpadc_cfg { - const struct abx500_res_to_temp *temp_tbl; - int tbl_sz; - int vcc; - int r_up; -}; - -struct ab8500_temp { - struct iio_channel *aux1; - struct iio_channel *aux2; - struct ab8500_btemp *btemp; - struct delayed_work power_off_work; - struct ab8500_gpadc_cfg cfg; - struct abx500_temp *abx500_data; -}; - -/* - * The hardware connection is like this: - * VCC----[ R_up ]-----[ NTC ]----GND - * where R_up is pull-up resistance, and GPADC measures voltage on NTC. - * and res_to_temp table is strictly sorted by falling resistance values. - */ -static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg, - int v_ntc, int *temp) -{ - int r_ntc, i = 0, tbl_sz = cfg->tbl_sz; - const struct abx500_res_to_temp *tbl = cfg->temp_tbl; - - if (cfg->vcc < 0 || v_ntc >= cfg->vcc) - return -EINVAL; - - r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc); - if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist) - return -EINVAL; - - while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) && - i < tbl_sz - 2) - i++; - - /* return milli-Celsius */ - *temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 * - (r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist); - - return 0; -} - -static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp) -{ - int voltage, ret; - struct ab8500_temp *ab8500_data = data->plat_data; - - if (sensor == AB8500_SENSOR_BTEMP_BALL) { - *temp = ab8500_btemp_get_temp(ab8500_data->btemp); - } else if (sensor == AB8500_SENSOR_BAT_CTRL) { - *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); - } else if (sensor == AB8500_SENSOR_AUX1) { - ret = iio_read_channel_processed(ab8500_data->aux1, &voltage); - if (ret < 0) - return ret; - ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); - if (ret < 0) - return ret; - } else if (sensor == AB8500_SENSOR_AUX2) { - ret = iio_read_channel_processed(ab8500_data->aux2, &voltage); - if (ret < 0) - return ret; - ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); - if (ret < 0) - return ret; - } - - return 0; -} - -static void ab8500_thermal_power_off(struct work_struct *work) -{ - struct ab8500_temp *ab8500_data = container_of(work, - struct ab8500_temp, power_off_work.work); - struct abx500_temp *abx500_data = ab8500_data->abx500_data; - - dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n"); - - kernel_power_off(); -} - -static ssize_t ab8500_show_name(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - return sprintf(buf, "ab8500\n"); -} - -static ssize_t ab8500_show_label(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - char *label; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int index = attr->index; - - switch (index) { - case 1: - label = "ext_adc1"; - break; - case 2: - label = "ext_adc2"; - break; - case 3: - label = "bat_temp"; - break; - case 4: - label = "bat_ctrl"; - break; - default: - return -EINVAL; - } - - return sprintf(buf, "%s\n", label); -} - -static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data) -{ - struct ab8500_temp *ab8500_data = data->plat_data; - - dev_warn(&data->pdev->dev, "Power off in %d s\n", - DEFAULT_POWER_OFF_DELAY / HZ); - - schedule_delayed_work(&ab8500_data->power_off_work, - DEFAULT_POWER_OFF_DELAY); - return 0; -} - -int abx500_hwmon_init(struct abx500_temp *data) -{ - struct ab8500_temp *ab8500_data; - - ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data), - GFP_KERNEL); - if (!ab8500_data) - return -ENOMEM; - - ab8500_data->btemp = ab8500_btemp_get(); - if (IS_ERR(ab8500_data->btemp)) - return PTR_ERR(ab8500_data->btemp); - - INIT_DELAYED_WORK(&ab8500_data->power_off_work, - ab8500_thermal_power_off); - - ab8500_data->cfg.vcc = THERMAL_VCC; - ab8500_data->cfg.r_up = PULL_UP_RESISTOR; - ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor; - ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size; - - data->plat_data = ab8500_data; - ab8500_data->aux1 = devm_iio_channel_get(&data->pdev->dev, "aux1"); - if (IS_ERR(ab8500_data->aux1)) { - if (PTR_ERR(ab8500_data->aux1) == -ENODEV) - return -EPROBE_DEFER; - dev_err(&data->pdev->dev, "failed to get AUX1 ADC channel\n"); - return PTR_ERR(ab8500_data->aux1); - } - ab8500_data->aux2 = devm_iio_channel_get(&data->pdev->dev, "aux2"); - if (IS_ERR(ab8500_data->aux2)) { - if (PTR_ERR(ab8500_data->aux2) == -ENODEV) - return -EPROBE_DEFER; - dev_err(&data->pdev->dev, "failed to get AUX2 ADC channel\n"); - return PTR_ERR(ab8500_data->aux2); - } - - data->gpadc_addr[0] = AB8500_SENSOR_AUX1; - data->gpadc_addr[1] = AB8500_SENSOR_AUX2; - data->gpadc_addr[2] = AB8500_SENSOR_BTEMP_BALL; - data->gpadc_addr[3] = AB8500_SENSOR_BAT_CTRL; - data->monitored_sensors = NUM_MONITORED_SENSORS; - - data->ops.read_sensor = ab8500_read_sensor; - data->ops.irq_handler = ab8500_temp_irq_handler; - data->ops.show_name = ab8500_show_name; - data->ops.show_label = ab8500_show_label; - data->ops.is_visible = NULL; - - return 0; -} -EXPORT_SYMBOL(abx500_hwmon_init); - -MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>"); -MODULE_DESCRIPTION("AB8500 temperature driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 681f0623868f..a7cae6568155 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1504,7 +1504,6 @@ LEAVE_UPDATE: return NULL; } -#ifdef CONFIG_PM_SLEEP static int abituguru_suspend(struct device *dev) { struct abituguru_data *data = dev_get_drvdata(dev); @@ -1526,16 +1525,12 @@ static int abituguru_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume); -#define ABIT_UGURU_PM (&abituguru_pm) -#else -#define ABIT_UGURU_PM NULL -#endif /* CONFIG_PM */ +static DEFINE_SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume); static struct platform_driver abituguru_driver = { .driver = { .name = ABIT_UGURU_NAME, - .pm = ABIT_UGURU_PM, + .pm = pm_sleep_ptr(&abituguru_pm), }, .probe = abituguru_probe, .remove = abituguru_remove, diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 112dd0d9377c..afb21f73032d 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -145,7 +145,7 @@ struct abituguru3_data { struct device *hwmon_dev; /* hwmon registered device */ struct mutex update_lock; /* protect access to data and uGuru */ unsigned short addr; /* uguru base address */ - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ /* @@ -1083,7 +1083,7 @@ static struct abituguru3_data *abituguru3_update_device(struct device *dev) mutex_lock(&data->update_lock); if (!data->valid || time_after(jiffies, data->last_updated + HZ)) { /* Clear data->valid while updating */ - data->valid = 0; + data->valid = false; /* Read alarms */ if (abituguru3_read_increment_offset(data, ABIT_UGURU3_SETTINGS_BANK, @@ -1117,7 +1117,7 @@ static struct abituguru3_data *abituguru3_update_device(struct device *dev) goto LEAVE_UPDATE; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } LEAVE_UPDATE: mutex_unlock(&data->update_lock); @@ -1127,7 +1127,6 @@ LEAVE_UPDATE: return NULL; } -#ifdef CONFIG_PM_SLEEP static int abituguru3_suspend(struct device *dev) { struct abituguru3_data *data = dev_get_drvdata(dev); @@ -1146,16 +1145,12 @@ static int abituguru3_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume); -#define ABIT_UGURU3_PM (&abituguru3_pm) -#else -#define ABIT_UGURU3_PM NULL -#endif /* CONFIG_PM */ +static DEFINE_SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume); static struct platform_driver abituguru3_driver = { .driver = { .name = ABIT_UGURU3_NAME, - .pm = ABIT_UGURU3_PM + .pm = pm_sleep_ptr(&abituguru3_pm), }, .probe = abituguru3_probe, .remove = abituguru3_remove, diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c deleted file mode 100644 index 50e67cdd8e5e..000000000000 --- a/drivers/hwmon/abx500.c +++ /dev/null @@ -1,487 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson 2010 - 2013 - * Author: Martin Persson <martin.persson@stericsson.com> - * Hongbo Zhang <hongbo.zhang@linaro.org> - * - * ABX500 does not provide auto ADC, so to monitor the required temperatures, - * a periodic work is used. It is more important to not wake up the CPU than - * to perform this job, hence the use of a deferred delay. - * - * A deferred delay for thermal monitor is considered safe because: - * If the chip gets too hot during a sleep state it's most likely due to - * external factors, such as the surrounding temperature. I.e. no SW decisions - * will make any difference. - */ - -#include <linux/err.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/workqueue.h> -#include "abx500.h" - -#define DEFAULT_MONITOR_DELAY HZ -#define DEFAULT_MAX_TEMP 130 - -static inline void schedule_monitor(struct abx500_temp *data) -{ - data->work_active = true; - schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY); -} - -static void threshold_updated(struct abx500_temp *data) -{ - int i; - for (i = 0; i < data->monitored_sensors; i++) - if (data->max[i] != 0 || data->min[i] != 0) { - schedule_monitor(data); - return; - } - - dev_dbg(&data->pdev->dev, "No active thresholds.\n"); - cancel_delayed_work_sync(&data->work); - data->work_active = false; -} - -static void gpadc_monitor(struct work_struct *work) -{ - int temp, i, ret; - char alarm_node[30]; - bool updated_min_alarm, updated_max_alarm; - struct abx500_temp *data; - - data = container_of(work, struct abx500_temp, work.work); - mutex_lock(&data->lock); - - for (i = 0; i < data->monitored_sensors; i++) { - /* Thresholds are considered inactive if set to 0 */ - if (data->max[i] == 0 && data->min[i] == 0) - continue; - - if (data->max[i] < data->min[i]) - continue; - - ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp); - if (ret < 0) { - dev_err(&data->pdev->dev, "GPADC read failed\n"); - continue; - } - - updated_min_alarm = false; - updated_max_alarm = false; - - if (data->min[i] != 0) { - if (temp < data->min[i]) { - if (data->min_alarm[i] == false) { - data->min_alarm[i] = true; - updated_min_alarm = true; - } - } else { - if (data->min_alarm[i] == true) { - data->min_alarm[i] = false; - updated_min_alarm = true; - } - } - } - if (data->max[i] != 0) { - if (temp > data->max[i]) { - if (data->max_alarm[i] == false) { - data->max_alarm[i] = true; - updated_max_alarm = true; - } - } else if (temp < data->max[i] - data->max_hyst[i]) { - if (data->max_alarm[i] == true) { - data->max_alarm[i] = false; - updated_max_alarm = true; - } - } - } - - if (updated_min_alarm) { - ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1); - sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); - } - if (updated_max_alarm) { - ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1); - sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); - } - } - - schedule_monitor(data); - mutex_unlock(&data->lock); -} - -/* HWMON sysfs interfaces */ -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - /* Show chip name */ - return data->ops.show_name(dev, devattr, buf); -} - -static ssize_t label_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - /* Show each sensor label */ - return data->ops.show_label(dev, devattr, buf); -} - -static ssize_t input_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int ret, temp; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - u8 gpadc_addr = data->gpadc_addr[attr->index]; - - ret = data->ops.read_sensor(data, gpadc_addr, &temp); - if (ret < 0) - return ret; - - return sprintf(buf, "%d\n", temp); -} - -/* Set functions (RW nodes) */ -static ssize_t min_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - unsigned long val; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int res = kstrtol(buf, 10, &val); - if (res < 0) - return res; - - val = clamp_val(val, 0, DEFAULT_MAX_TEMP); - - mutex_lock(&data->lock); - data->min[attr->index] = val; - threshold_updated(data); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t max_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - unsigned long val; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int res = kstrtol(buf, 10, &val); - if (res < 0) - return res; - - val = clamp_val(val, 0, DEFAULT_MAX_TEMP); - - mutex_lock(&data->lock); - data->max[attr->index] = val; - threshold_updated(data); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t max_hyst_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - unsigned long val; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int res = kstrtoul(buf, 10, &val); - if (res < 0) - return res; - - val = clamp_val(val, 0, DEFAULT_MAX_TEMP); - - mutex_lock(&data->lock); - data->max_hyst[attr->index] = val; - threshold_updated(data); - mutex_unlock(&data->lock); - - return count; -} - -/* Show functions (RO nodes) */ -static ssize_t min_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%lu\n", data->min[attr->index]); -} - -static ssize_t max_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%lu\n", data->max[attr->index]); -} - -static ssize_t max_hyst_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%lu\n", data->max_hyst[attr->index]); -} - -static ssize_t min_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%d\n", data->min_alarm[attr->index]); -} - -static ssize_t max_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%d\n", data->max_alarm[attr->index]); -} - -static umode_t abx500_attrs_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct abx500_temp *data = dev_get_drvdata(dev); - - if (data->ops.is_visible) - return data->ops.is_visible(attr, n); - - return attr->mode; -} - -/* Chip name, required by hwmon */ -static SENSOR_DEVICE_ATTR_RO(name, name, 0); - -/* GPADC - SENSOR1 */ -static SENSOR_DEVICE_ATTR_RO(temp1_label, label, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_input, input, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_min, min, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max, max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, max_hyst, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, min_alarm, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, max_alarm, 0); - -/* GPADC - SENSOR2 */ -static SENSOR_DEVICE_ATTR_RO(temp2_label, label, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_input, input, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_min, min, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max, max, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, max_hyst, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, min_alarm, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, max_alarm, 1); - -/* GPADC - SENSOR3 */ -static SENSOR_DEVICE_ATTR_RO(temp3_label, label, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_input, input, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_min, min, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max, max, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, max_hyst, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, min_alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, max_alarm, 2); - -/* GPADC - SENSOR4 */ -static SENSOR_DEVICE_ATTR_RO(temp4_label, label, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_input, input, 3); -static SENSOR_DEVICE_ATTR_RW(temp4_min, min, 3); -static SENSOR_DEVICE_ATTR_RW(temp4_max, max, 3); -static SENSOR_DEVICE_ATTR_RW(temp4_max_hyst, max_hyst, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_min_alarm, min_alarm, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, max_alarm, 3); - -static struct attribute *abx500_temp_attributes[] = { - &sensor_dev_attr_name.dev_attr.attr, - - &sensor_dev_attr_temp1_label.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - - &sensor_dev_attr_temp2_label.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - - &sensor_dev_attr_temp3_label.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - - &sensor_dev_attr_temp4_label.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp4_min.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - NULL -}; - -static const struct attribute_group abx500_temp_group = { - .attrs = abx500_temp_attributes, - .is_visible = abx500_attrs_visible, -}; - -static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data) -{ - struct platform_device *pdev = irq_data; - struct abx500_temp *data = platform_get_drvdata(pdev); - - data->ops.irq_handler(irq, data); - return IRQ_HANDLED; -} - -static int setup_irqs(struct platform_device *pdev) -{ - int ret; - int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM"); - - if (irq < 0) { - dev_err(&pdev->dev, "Get irq by name failed\n"); - return irq; - } - - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - abx500_temp_irq_handler, 0, "abx500-temp", pdev); - if (ret < 0) - dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); - - return ret; -} - -static int abx500_temp_probe(struct platform_device *pdev) -{ - struct abx500_temp *data; - int err; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->pdev = pdev; - mutex_init(&data->lock); - - /* Chip specific initialization */ - err = abx500_hwmon_init(data); - if (err < 0 || !data->ops.read_sensor || !data->ops.show_name || - !data->ops.show_label) - return err; - - INIT_DEFERRABLE_WORK(&data->work, gpadc_monitor); - - platform_set_drvdata(pdev, data); - - err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group); - if (err < 0) { - dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err); - return err; - } - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - dev_err(&pdev->dev, "Class registration failed (%d)\n", err); - goto exit_sysfs_group; - } - - if (data->ops.irq_handler) { - err = setup_irqs(pdev); - if (err < 0) - goto exit_hwmon_reg; - } - return 0; - -exit_hwmon_reg: - hwmon_device_unregister(data->hwmon_dev); -exit_sysfs_group: - sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group); - return err; -} - -static int abx500_temp_remove(struct platform_device *pdev) -{ - struct abx500_temp *data = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&data->work); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group); - - return 0; -} - -static int abx500_temp_suspend(struct platform_device *pdev, - pm_message_t state) -{ - struct abx500_temp *data = platform_get_drvdata(pdev); - - if (data->work_active) - cancel_delayed_work_sync(&data->work); - - return 0; -} - -static int abx500_temp_resume(struct platform_device *pdev) -{ - struct abx500_temp *data = platform_get_drvdata(pdev); - - if (data->work_active) - schedule_monitor(data); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id abx500_temp_match[] = { - { .compatible = "stericsson,abx500-temp" }, - {}, -}; -MODULE_DEVICE_TABLE(of, abx500_temp_match); -#endif - -static struct platform_driver abx500_temp_driver = { - .driver = { - .name = "abx500-temp", - .of_match_table = of_match_ptr(abx500_temp_match), - }, - .suspend = abx500_temp_suspend, - .resume = abx500_temp_resume, - .probe = abx500_temp_probe, - .remove = abx500_temp_remove, -}; - -module_platform_driver(abx500_temp_driver); - -MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>"); -MODULE_DESCRIPTION("ABX500 temperature driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abx500.h b/drivers/hwmon/abx500.h deleted file mode 100644 index 4517594260f2..000000000000 --- a/drivers/hwmon/abx500.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson 2010 - 2013 - * Author: Martin Persson <martin.persson@stericsson.com> - * Hongbo Zhang <hongbo.zhang@linaro.com> - */ - -#ifndef _ABX500_H -#define _ABX500_H - -#define NUM_SENSORS 5 - -struct abx500_temp; - -/* - * struct abx500_temp_ops - abx500 chip specific ops - * @read_sensor: reads gpadc output - * @irq_handler: irq handler - * @show_name: hwmon device name - * @show_label: hwmon attribute label - * @is_visible: is attribute visible - */ -struct abx500_temp_ops { - int (*read_sensor)(struct abx500_temp *, u8, int *); - int (*irq_handler)(int, struct abx500_temp *); - ssize_t (*show_name)(struct device *, - struct device_attribute *, char *); - ssize_t (*show_label) (struct device *, - struct device_attribute *, char *); - int (*is_visible)(struct attribute *, int); -}; - -/* - * struct abx500_temp - representation of temp mon device - * @pdev: platform device - * @hwmon_dev: hwmon device - * @ops: abx500 chip specific ops - * @gpadc_addr: gpadc channel address - * @min: sensor temperature min value - * @max: sensor temperature max value - * @max_hyst: sensor temperature hysteresis value for max limit - * @min_alarm: sensor temperature min alarm - * @max_alarm: sensor temperature max alarm - * @work: delayed work scheduled to monitor temperature periodically - * @work_active: True if work is active - * @lock: mutex - * @monitored_sensors: number of monitored sensors - * @plat_data: private usage, usually points to platform specific data - */ -struct abx500_temp { - struct platform_device *pdev; - struct device *hwmon_dev; - struct abx500_temp_ops ops; - u8 gpadc_addr[NUM_SENSORS]; - unsigned long min[NUM_SENSORS]; - unsigned long max[NUM_SENSORS]; - unsigned long max_hyst[NUM_SENSORS]; - bool min_alarm[NUM_SENSORS]; - bool max_alarm[NUM_SENSORS]; - struct delayed_work work; - bool work_active; - struct mutex lock; - int monitored_sensors; - void *plat_data; -}; - -int abx500_hwmon_init(struct abx500_temp *data); - -#endif /* _ABX500_H */ diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 0db8ef4fd6e1..0962c12eba5a 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -20,7 +20,6 @@ #include <linux/acpi.h> #define ACPI_POWER_METER_NAME "power_meter" -ACPI_MODULE_NAME(ACPI_POWER_METER_NAME); #define ACPI_POWER_METER_DEVICE_NAME "Power Meter" #define ACPI_POWER_METER_CLASS "pwr_meter_resource" @@ -114,7 +113,8 @@ static int update_avg_interval(struct acpi_power_meter_resource *resource) status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GAI", NULL, &data); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GAI")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_GAI", + status); return -ENODEV; } @@ -161,12 +161,13 @@ static ssize_t set_avg_interval(struct device *dev, mutex_lock(&resource->lock); status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", &args, &data); - if (!ACPI_FAILURE(status)) + if (ACPI_SUCCESS(status)) resource->avg_interval = temp; mutex_unlock(&resource->lock); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PAI")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI", + status); return -EINVAL; } @@ -186,7 +187,8 @@ static int update_cap(struct acpi_power_meter_resource *resource) status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GHL", NULL, &data); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GHL")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_GHL", + status); return -ENODEV; } @@ -232,12 +234,13 @@ static ssize_t set_cap(struct device *dev, struct device_attribute *devattr, mutex_lock(&resource->lock); status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", &args, &data); - if (!ACPI_FAILURE(status)) + if (ACPI_SUCCESS(status)) resource->cap = temp; mutex_unlock(&resource->lock); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SHL")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL", + status); return -EINVAL; } @@ -270,7 +273,8 @@ static int set_acpi_trip(struct acpi_power_meter_resource *resource) status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PTP", &args, &data); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTP")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PTP", + status); return -EINVAL; } @@ -322,7 +326,8 @@ static int update_meter(struct acpi_power_meter_resource *resource) status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PMM", NULL, &data); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMM")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PMM", + status); return -ENODEV; } @@ -476,7 +481,7 @@ static struct sensor_template meter_attrs[] = { RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1), RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5), RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval, - set_avg_interval, 0), + set_avg_interval, 0), {}, }; @@ -525,12 +530,13 @@ static void remove_domain_devices(struct acpi_power_meter_resource *resource) for (i = 0; i < resource->num_domain_devices; i++) { struct acpi_device *obj = resource->domain_devices[i]; + if (!obj) continue; sysfs_remove_link(resource->holders_dir, kobject_name(&obj->dev.kobj)); - put_device(&obj->dev); + acpi_dev_put(obj); } kfree(resource->domain_devices); @@ -549,7 +555,8 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource) status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMD", NULL, &buffer); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMD")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PMD", + status); return -ENODEV; } @@ -574,7 +581,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource) } resource->holders_dir = kobject_create_and_add("measures", - &resource->acpi_dev->dev.kobj); + &resource->acpi_dev->dev.kobj); if (!resource->holders_dir) { res = -ENOMEM; goto exit_free; @@ -584,25 +591,22 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource) for (i = 0; i < pss->package.count; i++) { struct acpi_device *obj; - union acpi_object *element = &(pss->package.elements[i]); + union acpi_object *element = &pss->package.elements[i]; /* Refuse non-references */ if (element->type != ACPI_TYPE_LOCAL_REFERENCE) continue; /* Create a symlink to domain objects */ - resource->domain_devices[i] = NULL; - if (acpi_bus_get_device(element->reference.handle, - &resource->domain_devices[i])) + obj = acpi_get_acpi_dev(element->reference.handle); + resource->domain_devices[i] = obj; + if (!obj) continue; - obj = resource->domain_devices[i]; - get_device(&obj->dev); - res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj, - kobject_name(&obj->dev.kobj)); + kobject_name(&obj->dev.kobj)); if (res) { - put_device(&obj->dev); + acpi_dev_put(obj); resource->domain_devices[i] = NULL; } } @@ -725,8 +729,10 @@ static void free_capabilities(struct acpi_power_meter_resource *resource) int i; str = &resource->model_number; - for (i = 0; i < 3; i++, str++) + for (i = 0; i < 3; i++, str++) { kfree(*str); + *str = NULL; + } } static int read_capabilities(struct acpi_power_meter_resource *resource) @@ -743,7 +749,8 @@ static int read_capabilities(struct acpi_power_meter_resource *resource) status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMC", NULL, &buffer); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMC")); + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PMC", + status); return -ENODEV; } @@ -763,7 +770,9 @@ static int read_capabilities(struct acpi_power_meter_resource *resource) status = acpi_extract_package(pss, &format, &state); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Invalid data")); + dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME + "_PMC package parsing failed: %s\n", + acpi_format_exception(status)); res = -EFAULT; goto end; } @@ -780,7 +789,7 @@ static int read_capabilities(struct acpi_power_meter_resource *resource) str = &resource->model_number; for (i = 11; i < 14; i++) { - union acpi_object *element = &(pss->package.elements[i]); + union acpi_object *element = &pss->package.elements[i]; if (element->type != ACPI_TYPE_STRING) { res = -EINVAL; @@ -801,9 +810,7 @@ static int read_capabilities(struct acpi_power_meter_resource *resource) dev_info(&resource->acpi_dev->dev, "Found ACPI power meter.\n"); goto end; error: - str = &resource->model_number; - for (i = 0; i < 3; i++, str++) - kfree(*str); + free_capabilities(resource); end: kfree(buffer.pointer); return res; @@ -862,8 +869,7 @@ static int acpi_power_meter_add(struct acpi_device *device) if (!device) return -EINVAL; - resource = kzalloc(sizeof(struct acpi_power_meter_resource), - GFP_KERNEL); + resource = kzalloc(sizeof(*resource), GFP_KERNEL); if (!resource) return -ENOMEM; @@ -874,16 +880,16 @@ static int acpi_power_meter_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_POWER_METER_CLASS); device->driver_data = resource; - free_capabilities(resource); res = read_capabilities(resource); if (res) goto exit_free; - resource->trip[0] = resource->trip[1] = -1; + resource->trip[0] = -1; + resource->trip[1] = -1; res = setup_attrs(resource); if (res) - goto exit_free; + goto exit_free_capability; resource->hwmon_dev = hwmon_device_register(&device->dev); if (IS_ERR(resource->hwmon_dev)) { @@ -896,6 +902,8 @@ static int acpi_power_meter_add(struct acpi_device *device) exit_remove: remove_attrs(resource); +exit_free_capability: + free_capabilities(resource); exit_free: kfree(resource); exit: @@ -919,8 +927,6 @@ static int acpi_power_meter_remove(struct acpi_device *device) return 0; } -#ifdef CONFIG_PM_SLEEP - static int acpi_power_meter_resume(struct device *dev) { struct acpi_power_meter_resource *resource; @@ -938,9 +944,8 @@ static int acpi_power_meter_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ - -static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, + acpi_power_meter_resume); static struct acpi_driver acpi_power_meter_driver = { .name = "power_meter", @@ -951,7 +956,7 @@ static struct acpi_driver acpi_power_meter_driver = { .remove = acpi_power_meter_remove, .notify = acpi_power_meter_notify, }, - .drv.pm = &acpi_power_meter_pm, + .drv.pm = pm_sleep_ptr(&acpi_power_meter_pm), }; /* Module init/exit routines */ diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index a529f2efc790..0afb89c4629d 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -37,7 +37,7 @@ static u8 AD7414_REG_LIMIT[] = { AD7414_REG_T_HIGH, AD7414_REG_T_LOW }; struct ad7414_data { struct i2c_client *client; struct mutex lock; /* atomic read data updates */ - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long next_update; /* In jiffies */ s16 temp_input; /* Register values */ s8 temps[ARRAY_SIZE(AD7414_REG_LIMIT)]; @@ -95,7 +95,7 @@ static struct ad7414_data *ad7414_update_device(struct device *dev) } data->next_update = jiffies + HZ + HZ / 2; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->lock); @@ -169,8 +169,7 @@ static struct attribute *ad7414_attrs[] = { ATTRIBUTE_GROUPS(ad7414); -static int ad7414_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int ad7414_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ad7414_data *data; @@ -222,7 +221,7 @@ static struct i2c_driver ad7414_driver = { .name = "ad7414", .of_match_table = of_match_ptr(ad7414_of_match), }, - .probe = ad7414_probe, + .probe_new = ad7414_probe, .id_table = ad7414_id, }; diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index 74542b8ad8ef..22bdb7e5b9e0 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -46,7 +46,7 @@ struct ad7418_data { enum chips type; struct mutex lock; int adc_max; /* number of ADC channels */ - char valid; + bool valid; unsigned long last_updated; /* In jiffies */ s16 temp[3]; /* Register values */ u16 in[4]; @@ -111,14 +111,14 @@ static int ad7418_update_device(struct device *dev) goto abort; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->lock); return 0; abort: - data->valid = 0; + data->valid = false; mutex_unlock(&data->lock); return val; } @@ -230,8 +230,9 @@ static void ad7418_init_client(struct i2c_client *client) } } -static int ad7418_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id ad7418_id[]; + +static int ad7418_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct i2c_adapter *adapter = client->adapter; @@ -254,7 +255,7 @@ static int ad7418_probe(struct i2c_client *client, if (dev->of_node) data->type = (enum chips)of_device_get_match_data(dev); else - data->type = id->driver_data; + data->type = i2c_match_id(ad7418_id, client)->driver_data; switch (data->type) { case ad7416: @@ -305,7 +306,7 @@ static struct i2c_driver ad7418_driver = { .name = "ad7418", .of_match_table = ad7418_dt_ids, }, - .probe = ad7418_probe, + .probe_new = ad7418_probe, .id_table = ad7418_id, }; diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index f9edec195c35..97b330b6c165 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -248,7 +248,7 @@ static ssize_t adc128_alarm_show(struct device *dev, static umode_t adc128_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct adc128_data *data = dev_get_drvdata(dev); if (index < ADC128_ATTR_NUM_VOLT) { @@ -384,7 +384,7 @@ static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info) if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc) return -ENODEV; - strlcpy(info->type, "adc128d818", I2C_NAME_SIZE); + strscpy(info->type, "adc128d818", I2C_NAME_SIZE); return 0; } @@ -393,6 +393,7 @@ static int adc128_init_client(struct adc128_data *data) { struct i2c_client *client = data->client; int err; + u8 regval = 0x0; /* * Reset chip to defaults. @@ -403,10 +404,17 @@ static int adc128_init_client(struct adc128_data *data) return err; /* Set operation mode, if non-default */ - if (data->mode != 0) { - err = i2c_smbus_write_byte_data(client, - ADC128_REG_CONFIG_ADV, - data->mode << 1); + if (data->mode != 0) + regval |= data->mode << 1; + + /* If external vref is selected, configure the chip to use it */ + if (data->regulator) + regval |= 0x01; + + /* Write advanced configuration register */ + if (regval != 0x0) { + err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG_ADV, + regval); if (err) return err; } @@ -416,19 +424,10 @@ static int adc128_init_client(struct adc128_data *data) if (err) return err; - /* If external vref is selected, configure the chip to use it */ - if (data->regulator) { - err = i2c_smbus_write_byte_data(client, - ADC128_REG_CONFIG_ADV, 0x01); - if (err) - return err; - } - return 0; } -static int adc128_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adc128_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct regulator *regulator; @@ -496,14 +495,12 @@ error: return err; } -static int adc128_remove(struct i2c_client *client) +static void adc128_remove(struct i2c_client *client) { struct adc128_data *data = i2c_get_clientdata(client); if (data->regulator) regulator_disable(data->regulator); - - return 0; } static const struct i2c_device_id adc128_id[] = { @@ -524,7 +521,7 @@ static struct i2c_driver adc128_driver = { .name = "adc128d818", .of_match_table = of_match_ptr(adc128_of_match), }, - .probe = adc128_probe, + .probe_new = adc128_probe, .remove = adc128_remove, .id_table = adc128_id, .detect = adc128_detect, diff --git a/drivers/hwmon/adcxx.c b/drivers/hwmon/adcxx.c index e5bc5ce09f4e..de37bce24fa6 100644 --- a/drivers/hwmon/adcxx.c +++ b/drivers/hwmon/adcxx.c @@ -194,7 +194,7 @@ out_err: return status; } -static int adcxx_remove(struct spi_device *spi) +static void adcxx_remove(struct spi_device *spi) { struct adcxx *adc = spi_get_drvdata(spi); int i; @@ -205,8 +205,6 @@ static int adcxx_remove(struct spi_device *spi) device_remove_file(&spi->dev, &ad_input[i].dev_attr); mutex_unlock(&adc->lock); - - return 0; } static const struct spi_device_id adcxx_ids[] = { diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index c45046241a1c..2dc45e958730 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -72,7 +72,7 @@ struct adm1021_data { const struct attribute_group *groups[3]; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ char low_power; /* !=0 if device in low power mode */ unsigned long last_updated; /* In jiffies */ @@ -135,7 +135,7 @@ static struct adm1021_data *adm1021_update_device(struct device *dev) ADM1023_REG_REM_OFFSET_PREC); } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -324,7 +324,7 @@ static int adm1021_detect(struct i2c_client *client, { struct i2c_adapter *adapter = client->adapter; const char *type_name; - int conv_rate, status, config, man_id, dev_id; + int reg, conv_rate, status, config, man_id, dev_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_debug("detect failed, smbus byte data not supported!\n"); @@ -349,9 +349,19 @@ static int adm1021_detect(struct i2c_client *client, if (man_id < 0 || dev_id < 0) return -ENODEV; - if (man_id == 0x4d && dev_id == 0x01) + if (man_id == 0x4d && dev_id == 0x01) { + /* + * dev_id 0x01 matches MAX6680, MAX6695, MAX6696, and possibly + * others. Read register which is unsupported on MAX1617 but + * exists on all those chips and compare with the dev_id + * register. If it matches, it may be a MAX1617A. + */ + reg = i2c_smbus_read_byte_data(client, + ADM1023_REG_REM_TEMP_PREC); + if (reg != dev_id) + return -ENODEV; type_name = "max1617a"; - else if (man_id == 0x41) { + } else if (man_id == 0x41) { if ((dev_id & 0xF0) == 0x30) type_name = "adm1023"; else if ((dev_id & 0xF0) == 0x00) @@ -395,13 +405,18 @@ static int adm1021_detect(struct i2c_client *client, /* * LM84 Mfr ID is in a different place, - * and it has more unused bits. + * and it has more unused bits. Registers at 0xfe and 0xff + * are undefined and return the most recently read value, + * here the value of the configuration register. */ if (conv_rate == 0x00 + && man_id == config && dev_id == config && (config & 0x7F) == 0x00 && (status & 0xAB) == 0x00) { type_name = "lm84"; } else { + if ((config & 0x3f) || (status & 0x03)) + return -ENODEV; /* fail if low limits are larger than high limits */ if ((s8)llo > lhi || (s8)rlo > rhi) return -ENODEV; @@ -411,7 +426,7 @@ static int adm1021_detect(struct i2c_client *client, pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n", type_name, i2c_adapter_id(adapter), client->addr); - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } @@ -425,8 +440,9 @@ static void adm1021_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04); } -static int adm1021_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id adm1021_id[]; + +static int adm1021_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct adm1021_data *data; @@ -437,7 +453,7 @@ static int adm1021_probe(struct i2c_client *client, return -ENOMEM; data->client = client; - data->type = id->driver_data; + data->type = i2c_match_id(adm1021_id, client)->driver_data; mutex_init(&data->update_lock); /* Initialize the ADM1021 chip */ @@ -472,7 +488,7 @@ static struct i2c_driver adm1021_driver = { .driver = { .name = "adm1021", }, - .probe = adm1021_probe, + .probe_new = adm1021_probe, .id_table = adm1021_id, .detect = adm1021_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index ae7b96945185..2984c4f98496 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -13,7 +13,7 @@ * resolution of about 0.5% of the nominal value). Temperature values are * reported with a 1 deg resolution and a 3 deg accuracy. Complete * datasheet can be obtained from Analog's website at: - * http://www.onsemi.com/PowerSolutions/product.do?id=ADM1025 + * https://www.onsemi.com/PowerSolutions/product.do?id=ADM1025 * * This driver also supports the ADM1025A, which differs from the ADM1025 * only in that it has "open-drain VID inputs while the ADM1025 has @@ -97,7 +97,7 @@ struct adm1025_data { struct i2c_client *client; const struct attribute_group *groups[3]; struct mutex update_lock; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ u8 in[6]; /* register value */ @@ -148,7 +148,7 @@ static struct adm1025_data *adm1025_update_device(struct device *dev) ADM1025_REG_VID4) & 0x01) << 4); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -470,7 +470,7 @@ static int adm1025_detect(struct i2c_client *client, else return -ENODEV; - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -517,8 +517,7 @@ static void adm1025_init_client(struct i2c_client *client) (reg&0x7E)|0x01); } -static int adm1025_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adm1025_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -560,7 +559,7 @@ static struct i2c_driver adm1025_driver = { .driver = { .name = "adm1025", }, - .probe = adm1025_probe, + .probe_new = adm1025_probe, .id_table = adm1025_id, .detect = adm1025_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index e0f630c64152..1f084f708743 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -7,7 +7,7 @@ * * Chip details at: * - * <http://www.onsemi.com/PowerSolutions/product.do?id=ADM1026> + * <https://www.onsemi.com/PowerSolutions/product.do?id=ADM1026> */ #include <linux/module.h> @@ -259,7 +259,7 @@ struct adm1026_data { const struct attribute_group *groups[3]; struct mutex update_lock; - int valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_reading; /* In jiffies */ unsigned long last_config; /* In jiffies */ @@ -459,7 +459,7 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) data->last_config = jiffies; } /* last_config */ - data->valid = 1; + data->valid = true; mutex_unlock(&data->update_lock); return data; } @@ -1610,7 +1610,7 @@ static int adm1026_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "adm1026", I2C_NAME_SIZE); + strscpy(info->type, "adm1026", I2C_NAME_SIZE); return 0; } @@ -1816,8 +1816,7 @@ static void adm1026_init_client(struct i2c_client *client) } } -static int adm1026_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adm1026_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -1860,7 +1859,7 @@ static struct i2c_driver adm1026_driver = { .driver = { .name = "adm1026", }, - .probe = adm1026_probe, + .probe_new = adm1026_probe, .id_table = adm1026_id, .detect = adm1026_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index f7752a5bef31..eaf6e5e04aac 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -99,7 +99,7 @@ static const u8 ADM1029_REG_FAN_DIV[] = { struct adm1029_data { struct i2c_client *client; struct mutex update_lock; /* protect register access */ - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values, signed for temperature, unsigned for other stuff */ @@ -143,7 +143,7 @@ static struct adm1029_data *adm1029_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -329,7 +329,7 @@ static int adm1029_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "adm1029", I2C_NAME_SIZE); + strscpy(info->type, "adm1029", I2C_NAME_SIZE); return 0; } @@ -352,8 +352,7 @@ static int adm1029_init_client(struct i2c_client *client) return 1; } -static int adm1029_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adm1029_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct adm1029_data *data; @@ -390,7 +389,7 @@ static struct i2c_driver adm1029_driver = { .driver = { .name = "adm1029", }, - .probe = adm1029_probe, + .probe_new = adm1029_probe, .id_table = adm1029_id, .detect = adm1029_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 7723a338446d..b42797bcb5b4 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -65,7 +65,7 @@ struct adm1031_data { const struct attribute_group *groups[3]; struct mutex update_lock; int chip_type; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ unsigned int update_interval; /* In milliseconds */ /* @@ -187,7 +187,7 @@ static struct adm1031_data *adm1031_update_device(struct device *dev) ADM1031_REG_PWM) >> (4 * chan)) & 0x0f; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -242,9 +242,8 @@ static int FAN_TO_REG(int reg, int div) static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm) { int ret; - int range = val - AUTO_TEMP_MIN_FROM_REG(reg); + int range = ((val - AUTO_TEMP_MIN_FROM_REG(reg)) * 10) / (16 - pwm); - range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm); ret = ((reg & 0xf8) | (range < 10000 ? 0 : range < 20000 ? 1 : @@ -650,7 +649,7 @@ static ssize_t fan_div_store(struct device *dev, data->fan_min[nr]); /* Invalidate the cache: fan speed is no longer valid */ - data->valid = 0; + data->valid = false; mutex_unlock(&data->update_lock); return count; } @@ -986,7 +985,7 @@ static int adm1031_detect(struct i2c_client *client, return -ENODEV; name = (id == 0x30) ? "adm1030" : "adm1031"; - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -1022,8 +1021,9 @@ static void adm1031_init_client(struct i2c_client *client) data->update_interval = update_intervals[i]; } -static int adm1031_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id adm1031_id[]; + +static int adm1031_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -1035,7 +1035,7 @@ static int adm1031_probe(struct i2c_client *client, i2c_set_clientdata(client, data); data->client = client; - data->chip_type = id->driver_data; + data->chip_type = i2c_match_id(adm1031_id, client)->driver_data; mutex_init(&data->update_lock); if (data->chip_type == adm1030) @@ -1068,7 +1068,7 @@ static struct i2c_driver adm1031_driver = { .driver = { .name = "adm1031", }, - .probe = adm1031_probe, + .probe_new = adm1031_probe, .id_table = adm1031_id, .detect = adm1031_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c index d314223a404a..0c5dbc5e33b4 100644 --- a/drivers/hwmon/adm1177.c +++ b/drivers/hwmon/adm1177.c @@ -25,11 +25,11 @@ /** * struct adm1177_state - driver instance specific data - * @client pointer to i2c client - * @reg regulator info for the 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 + * @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; @@ -196,8 +196,7 @@ static void adm1177_remove(void *data) regulator_disable(st->reg); } -static int adm1177_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adm1177_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -277,7 +276,7 @@ static struct i2c_driver adm1177_driver = { .name = "adm1177", .of_match_table = adm1177_dt_ids, }, - .probe = adm1177_probe, + .probe_new = adm1177_probe, .id_table = adm1177_id, }; module_i2c_driver(adm1177_driver); diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 496d47490e10..40e3558d3709 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -28,6 +28,7 @@ * LM81 extended temp reading not implemented */ +#include <linux/bits.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> @@ -37,7 +38,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/mutex.h> -#include <linux/jiffies.h> +#include <linux/regmap.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -122,289 +123,34 @@ static inline unsigned int AOUT_FROM_REG(u8 reg) /* per client data */ struct adm9240_data { - struct i2c_client *client; + struct device *dev; + struct regmap *regmap; struct mutex update_lock; - char valid; - unsigned long last_updated_measure; - unsigned long last_updated_config; - - u8 in[6]; /* ro in0_input */ - u8 in_max[6]; /* rw in0_max */ - u8 in_min[6]; /* rw in0_min */ - u8 fan[2]; /* ro fan1_input */ - u8 fan_min[2]; /* rw fan1_min */ + u8 fan_div[2]; /* rw fan1_div, read-only accessor */ - s16 temp; /* ro temp1_input, 9-bit sign-extended */ - s8 temp_max[2]; /* rw 0 -> temp_max, 1 -> temp_max_hyst */ - u16 alarms; /* ro alarms */ - u8 aout; /* rw aout_output */ - u8 vid; /* ro vid */ u8 vrm; /* -- vrm set on startup, no accessor */ }; /* write new fan div, callers must hold data->update_lock */ -static void adm9240_write_fan_div(struct i2c_client *client, int nr, - u8 fan_div) +static int adm9240_write_fan_div(struct adm9240_data *data, int channel, u8 fan_div) { - u8 reg, old, shift = (nr + 2) * 2; + unsigned int reg, old, shift = (channel + 2) * 2; + int err; - reg = i2c_smbus_read_byte_data(client, ADM9240_REG_VID_FAN_DIV); + err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®); + if (err < 0) + return err; old = (reg >> shift) & 3; reg &= ~(3 << shift); reg |= (fan_div << shift); - i2c_smbus_write_byte_data(client, ADM9240_REG_VID_FAN_DIV, reg); - dev_dbg(&client->dev, - "fan%d clock divider changed from %u to %u\n", - nr + 1, 1 << old, 1 << fan_div); -} - -static struct adm9240_data *adm9240_update_device(struct device *dev) -{ - struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int i; - - mutex_lock(&data->update_lock); - - /* minimum measurement cycle: 1.75 seconds */ - if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4)) - || !data->valid) { - - for (i = 0; i < 6; i++) { /* read voltages */ - data->in[i] = i2c_smbus_read_byte_data(client, - ADM9240_REG_IN(i)); - } - data->alarms = i2c_smbus_read_byte_data(client, - ADM9240_REG_INT(0)) | - i2c_smbus_read_byte_data(client, - ADM9240_REG_INT(1)) << 8; - - /* - * read temperature: assume temperature changes less than - * 0.5'C per two measurement cycles thus ignore possible - * but unlikely aliasing error on lsb reading. --Grant - */ - data->temp = (i2c_smbus_read_byte_data(client, - ADM9240_REG_TEMP) << 8) | - i2c_smbus_read_byte_data(client, - ADM9240_REG_TEMP_CONF); - - for (i = 0; i < 2; i++) { /* read fans */ - data->fan[i] = i2c_smbus_read_byte_data(client, - ADM9240_REG_FAN(i)); - - /* adjust fan clock divider on overflow */ - if (data->valid && data->fan[i] == 255 && - data->fan_div[i] < 3) { - - adm9240_write_fan_div(client, i, - ++data->fan_div[i]); - - /* adjust fan_min if active, but not to 0 */ - if (data->fan_min[i] < 255 && - data->fan_min[i] >= 2) - data->fan_min[i] /= 2; - } - } - data->last_updated_measure = jiffies; - } - - /* minimum config reading cycle: 300 seconds */ - if (time_after(jiffies, data->last_updated_config + (HZ * 300)) - || !data->valid) { - - for (i = 0; i < 6; i++) { - data->in_min[i] = i2c_smbus_read_byte_data(client, - ADM9240_REG_IN_MIN(i)); - data->in_max[i] = i2c_smbus_read_byte_data(client, - ADM9240_REG_IN_MAX(i)); - } - for (i = 0; i < 2; i++) { - data->fan_min[i] = i2c_smbus_read_byte_data(client, - ADM9240_REG_FAN_MIN(i)); - } - data->temp_max[0] = i2c_smbus_read_byte_data(client, - ADM9240_REG_TEMP_MAX(0)); - data->temp_max[1] = i2c_smbus_read_byte_data(client, - ADM9240_REG_TEMP_MAX(1)); - - /* read fan divs and 5-bit VID */ - i = i2c_smbus_read_byte_data(client, ADM9240_REG_VID_FAN_DIV); - data->fan_div[0] = (i >> 4) & 3; - data->fan_div[1] = (i >> 6) & 3; - data->vid = i & 0x0f; - data->vid |= (i2c_smbus_read_byte_data(client, - ADM9240_REG_VID4) & 1) << 4; - /* read analog out */ - data->aout = i2c_smbus_read_byte_data(client, - ADM9240_REG_ANALOG_OUT); - - data->last_updated_config = jiffies; - data->valid = 1; - } - mutex_unlock(&data->update_lock); - return data; -} - -/*** sysfs accessors ***/ - -/* temperature */ -static ssize_t temp1_input_show(struct device *dev, - struct device_attribute *dummy, char *buf) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", data->temp / 128 * 500); /* 9-bit value */ -} - -static ssize_t max_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", data->temp_max[attr->index] * 1000); -} - -static ssize_t max_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) + err = regmap_write(data->regmap, ADM9240_REG_VID_FAN_DIV, reg); + if (err < 0) return err; + dev_dbg(data->dev, + "fan%d clock divider changed from %lu to %lu\n", + channel + 1, BIT(old), BIT(fan_div)); - mutex_lock(&data->update_lock); - data->temp_max[attr->index] = TEMP_TO_REG(val); - i2c_smbus_write_byte_data(client, ADM9240_REG_TEMP_MAX(attr->index), - data->temp_max[attr->index]); - mutex_unlock(&data->update_lock); - return count; -} - -static DEVICE_ATTR_RO(temp1_input); -static SENSOR_DEVICE_ATTR_RW(temp1_max, max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, max, 1); - -/* voltage */ -static ssize_t in_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index], - attr->index)); -} - -static ssize_t in_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index], - attr->index)); -} - -static ssize_t in_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index], - attr->index)); -} - -static ssize_t in_min_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->in_min[attr->index] = IN_TO_REG(val, attr->index); - i2c_smbus_write_byte_data(client, ADM9240_REG_IN_MIN(attr->index), - data->in_min[attr->index]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t in_max_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->in_max[attr->index] = IN_TO_REG(val, attr->index); - i2c_smbus_write_byte_data(client, ADM9240_REG_IN_MAX(attr->index), - data->in_max[attr->index]); - mutex_unlock(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0); -static SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0); -static SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1); -static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); -static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2); -static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); -static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3); -static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); -static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4); -static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); -static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, in, 5); -static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5); -static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5); - -/* fans */ -static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index], - 1 << data->fan_div[attr->index])); -} - -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[attr->index], - 1 << data->fan_div[attr->index])); -} - -static ssize_t fan_div_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", 1 << data->fan_div[attr->index]); + return 0; } /* @@ -418,38 +164,25 @@ static ssize_t fan_div_show(struct device *dev, * - otherwise: select fan clock divider to suit fan speed low limit, * measurement code may adjust registers to ensure fan speed reading */ -static ssize_t fan_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int nr = attr->index; u8 new_div; - unsigned long val; + u8 fan_min; int err; - err = kstrtoul(buf, 10, &val); - if (err) - return err; - mutex_lock(&data->update_lock); if (!val) { - data->fan_min[nr] = 255; - new_div = data->fan_div[nr]; - - dev_dbg(&client->dev, "fan%u low limit set disabled\n", - nr + 1); + fan_min = 255; + new_div = data->fan_div[channel]; + dev_dbg(data->dev, "fan%u low limit set disabled\n", channel + 1); } else if (val < 1350000 / (8 * 254)) { new_div = 3; - data->fan_min[nr] = 254; - - dev_dbg(&client->dev, "fan%u low limit set minimum %u\n", - nr + 1, FAN_FROM_REG(254, 1 << new_div)); + fan_min = 254; + dev_dbg(data->dev, "fan%u low limit set minimum %u\n", + channel + 1, FAN_FROM_REG(254, BIT(new_div))); } else { unsigned int new_min = 1350000 / val; @@ -461,71 +194,55 @@ static ssize_t fan_min_store(struct device *dev, if (!new_min) /* keep > 0 */ new_min++; - data->fan_min[nr] = new_min; + fan_min = new_min; - dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n", - nr + 1, FAN_FROM_REG(new_min, 1 << new_div)); + dev_dbg(data->dev, "fan%u low limit set fan speed %u\n", + channel + 1, FAN_FROM_REG(new_min, BIT(new_div))); } - if (new_div != data->fan_div[nr]) { - data->fan_div[nr] = new_div; - adm9240_write_fan_div(client, nr, new_div); + if (new_div != data->fan_div[channel]) { + data->fan_div[channel] = new_div; + adm9240_write_fan_div(data, channel, new_div); } - i2c_smbus_write_byte_data(client, ADM9240_REG_FAN_MIN(nr), - data->fan_min[nr]); + err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min); mutex_unlock(&data->update_lock); - return count; -} -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RO(fan1_div, fan_div, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); -static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RO(fan2_div, fan_div, 1); - -/* alarms */ -static ssize_t alarms_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); + return err; } -static DEVICE_ATTR_RO(alarms); -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int bitnr = to_sensor_dev_attr(attr)->index; - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); -} -static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8); -static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9); -static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4); -static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7); - -/* vid */ static ssize_t cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + u8 vid; + + err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®val); + if (err < 0) + return err; + vid = regval & 0x0f; + err = regmap_read(data->regmap, ADM9240_REG_VID4, ®val); + if (err < 0) + return err; + vid |= (regval & 1) << 4; + return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm)); } static DEVICE_ATTR_RO(cpu0_vid); -/* analog output */ static ssize_t aout_output_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + err = regmap_read(data->regmap, ADM9240_REG_ANALOG_OUT, ®val); + if (err) + return err; + + return sprintf(buf, "%d\n", AOUT_FROM_REG(regval)); } static ssize_t aout_output_store(struct device *dev, @@ -533,7 +250,6 @@ static ssize_t aout_output_store(struct device *dev, const char *buf, size_t count) { struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long val; int err; @@ -541,74 +257,13 @@ static ssize_t aout_output_store(struct device *dev, if (err) return err; - mutex_lock(&data->update_lock); - data->aout = AOUT_TO_REG(val); - i2c_smbus_write_byte_data(client, ADM9240_REG_ANALOG_OUT, data->aout); - mutex_unlock(&data->update_lock); - return count; + err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, AOUT_TO_REG(val)); + return err < 0 ? err : count; } static DEVICE_ATTR_RW(aout_output); -static ssize_t alarm_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; - - if (kstrtoul(buf, 10, &val) || val != 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - i2c_smbus_write_byte_data(client, ADM9240_REG_CHASSIS_CLEAR, 0x80); - data->valid = 0; /* Force cache refresh */ - mutex_unlock(&data->update_lock); - dev_dbg(&client->dev, "chassis intrusion latch cleared\n"); - - return count; -} -static SENSOR_DEVICE_ATTR_RW(intrusion0_alarm, alarm, 12); - static struct attribute *adm9240_attrs[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in0_min.dev_attr.attr, - &sensor_dev_attr_in0_max.dev_attr.attr, - &sensor_dev_attr_in0_alarm.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in1_min.dev_attr.attr, - &sensor_dev_attr_in1_max.dev_attr.attr, - &sensor_dev_attr_in1_alarm.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in2_min.dev_attr.attr, - &sensor_dev_attr_in2_max.dev_attr.attr, - &sensor_dev_attr_in2_alarm.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in3_min.dev_attr.attr, - &sensor_dev_attr_in3_max.dev_attr.attr, - &sensor_dev_attr_in3_alarm.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in4_min.dev_attr.attr, - &sensor_dev_attr_in4_max.dev_attr.attr, - &sensor_dev_attr_in4_alarm.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in5_min.dev_attr.attr, - &sensor_dev_attr_in5_max.dev_attr.attr, - &sensor_dev_attr_in5_alarm.dev_attr.attr, - &dev_attr_temp1_input.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan1_div.dev_attr.attr, - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan2_div.dev_attr.attr, - &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, - &dev_attr_alarms.attr, &dev_attr_aout_output.attr, - &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, &dev_attr_cpu0_vid.attr, NULL }; @@ -630,26 +285,19 @@ static int adm9240_detect(struct i2c_client *new_client, return -ENODEV; /* verify chip: reg address should match i2c address */ - if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) - != address) { - dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n", - address); + if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) != address) return -ENODEV; - } /* check known chip manufacturer */ man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID); - if (man_id == 0x23) { + if (man_id == 0x23) name = "adm9240"; - } else if (man_id == 0xda) { + else if (man_id == 0xda) name = "ds1780"; - } else if (man_id == 0x01) { + else if (man_id == 0x01) name = "lm81"; - } else { - dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n", - man_id); + else return -ENODEV; - } /* successful detect, print chip info */ die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV); @@ -657,74 +305,504 @@ static int adm9240_detect(struct i2c_client *new_client, man_id == 0x23 ? "ADM9240" : man_id == 0xda ? "DS1780" : "LM81", die_rev); - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } -static void adm9240_init_client(struct i2c_client *client) +static int adm9240_init_client(struct adm9240_data *data) { - struct adm9240_data *data = i2c_get_clientdata(client); - u8 conf = i2c_smbus_read_byte_data(client, ADM9240_REG_CONFIG); - u8 mode = i2c_smbus_read_byte_data(client, ADM9240_REG_TEMP_CONF) & 3; + unsigned int regval; + u8 conf, mode; + int err; + + err = regmap_raw_read(data->regmap, ADM9240_REG_CONFIG, &conf, 1); + if (err < 0) + return err; + err = regmap_raw_read(data->regmap, ADM9240_REG_TEMP_CONF, &mode, 1); + if (err < 0) + return err; + mode &= 3; data->vrm = vid_which_vrm(); /* need this to report vid as mV */ - dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10, - data->vrm % 10); + dev_info(data->dev, "Using VRM: %d.%d\n", data->vrm / 10, + data->vrm % 10); if (conf & 1) { /* measurement cycle running: report state */ - dev_info(&client->dev, "status: config 0x%02x mode %u\n", - conf, mode); + dev_info(data->dev, "status: config 0x%02x mode %u\n", + conf, mode); } else { /* cold start: open limits before starting chip */ int i; for (i = 0; i < 6; i++) { - i2c_smbus_write_byte_data(client, - ADM9240_REG_IN_MIN(i), 0); - i2c_smbus_write_byte_data(client, - ADM9240_REG_IN_MAX(i), 255); + err = regmap_write(data->regmap, + ADM9240_REG_IN_MIN(i), 0); + if (err < 0) + return err; + err = regmap_write(data->regmap, + ADM9240_REG_IN_MAX(i), 255); + if (err < 0) + return err; + } + for (i = 0; i < 2; i++) { + err = regmap_write(data->regmap, + ADM9240_REG_FAN_MIN(i), 255); + if (err < 0) + return err; + } + for (i = 0; i < 2; i++) { + err = regmap_write(data->regmap, + ADM9240_REG_TEMP_MAX(i), 127); + if (err < 0) + return err; } - i2c_smbus_write_byte_data(client, - ADM9240_REG_FAN_MIN(0), 255); - i2c_smbus_write_byte_data(client, - ADM9240_REG_FAN_MIN(1), 255); - i2c_smbus_write_byte_data(client, - ADM9240_REG_TEMP_MAX(0), 127); - i2c_smbus_write_byte_data(client, - ADM9240_REG_TEMP_MAX(1), 127); /* start measurement cycle */ - i2c_smbus_write_byte_data(client, ADM9240_REG_CONFIG, 1); + err = regmap_write(data->regmap, ADM9240_REG_CONFIG, 1); + if (err < 0) + return err; - dev_info(&client->dev, + dev_info(data->dev, "cold start: config was 0x%02x mode %u\n", conf, mode); } + + /* read fan divs */ + err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®val); + if (err < 0) + return err; + data->fan_div[0] = (regval >> 4) & 3; + data->fan_div[1] = (regval >> 6) & 3; + return 0; +} + +static int adm9240_chip_read(struct device *dev, u32 attr, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + u8 regs[2]; + int err; + + switch (attr) { + case hwmon_chip_alarms: + err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), ®s, 2); + if (err < 0) + return err; + *val = regs[0] | regs[1] << 8; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_intrusion_read(struct device *dev, u32 attr, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_intrusion_alarm: + err = regmap_read(data->regmap, ADM9240_REG_INT(1), ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(4)); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_intrusion_write(struct device *dev, u32 attr, long val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + int err; + + switch (attr) { + case hwmon_intrusion_alarm: + if (val) + return -EINVAL; + err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80); + if (err < 0) + return err; + dev_dbg(data->dev, "chassis intrusion latch cleared\n"); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_in_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int reg; + int err; + + switch (attr) { + case hwmon_in_input: + reg = ADM9240_REG_IN(channel); + break; + case hwmon_in_min: + reg = ADM9240_REG_IN_MIN(channel); + break; + case hwmon_in_max: + reg = ADM9240_REG_IN_MAX(channel); + break; + case hwmon_in_alarm: + if (channel < 4) { + reg = ADM9240_REG_INT(0); + } else { + reg = ADM9240_REG_INT(1); + channel -= 4; + } + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(channel)); + return 0; + default: + return -EOPNOTSUPP; + } + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + *val = IN_FROM_REG(regval, channel); + return 0; } -static int adm9240_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) +static int adm9240_in_write(struct device *dev, u32 attr, int channel, long val) { - struct device *dev = &new_client->dev; + struct adm9240_data *data = dev_get_drvdata(dev); + int reg; + + switch (attr) { + case hwmon_in_min: + reg = ADM9240_REG_IN_MIN(channel); + break; + case hwmon_in_max: + reg = ADM9240_REG_IN_MAX(channel); + break; + default: + return -EOPNOTSUPP; + } + return regmap_write(data->regmap, reg, IN_TO_REG(val, channel)); +} + +static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_fan_input: + mutex_lock(&data->update_lock); + err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), ®val); + if (err < 0) { + mutex_unlock(&data->update_lock); + return err; + } + if (regval == 255 && data->fan_div[channel] < 3) { + /* adjust fan clock divider on overflow */ + err = adm9240_write_fan_div(data, channel, + ++data->fan_div[channel]); + if (err) { + mutex_unlock(&data->update_lock); + return err; + } + } + *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); + mutex_unlock(&data->update_lock); + break; + case hwmon_fan_div: + *val = BIT(data->fan_div[channel]); + break; + case hwmon_fan_min: + err = regmap_read(data->regmap, ADM9240_REG_FAN_MIN(channel), ®val); + if (err < 0) + return err; + *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); + break; + case hwmon_fan_alarm: + err = regmap_read(data->regmap, ADM9240_REG_INT(0), ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(channel + 6)); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_fan_write(struct device *dev, u32 attr, int channel, long val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + int err; + + switch (attr) { + case hwmon_fan_min: + err = adm9240_fan_min_write(data, channel, val); + if (err < 0) + return err; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_temp_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err, temp; + + switch (attr) { + case hwmon_temp_input: + err = regmap_read(data->regmap, ADM9240_REG_TEMP, ®val); + if (err < 0) + return err; + temp = regval << 1; + err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, ®val); + if (err < 0) + return err; + temp |= regval >> 7; + *val = sign_extend32(temp, 8) * 500; + break; + case hwmon_temp_max: + err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(0), ®val); + if (err < 0) + return err; + *val = (s8)regval * 1000; + break; + case hwmon_temp_max_hyst: + err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(1), ®val); + if (err < 0) + return err; + *val = (s8)regval * 1000; + break; + case hwmon_temp_alarm: + err = regmap_read(data->regmap, ADM9240_REG_INT(0), ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(4)); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_temp_write(struct device *dev, u32 attr, int channel, long val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + int reg; + + switch (attr) { + case hwmon_temp_max: + reg = ADM9240_REG_TEMP_MAX(0); + break; + case hwmon_temp_max_hyst: + reg = ADM9240_REG_TEMP_MAX(1); + break; + default: + return -EOPNOTSUPP; + } + return regmap_write(data->regmap, reg, TEMP_TO_REG(val)); +} + +static int adm9240_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return adm9240_chip_read(dev, attr, val); + case hwmon_intrusion: + return adm9240_intrusion_read(dev, attr, val); + case hwmon_in: + return adm9240_in_read(dev, attr, channel, val); + case hwmon_fan: + return adm9240_fan_read(dev, attr, channel, val); + case hwmon_temp: + return adm9240_temp_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int adm9240_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + switch (type) { + case hwmon_intrusion: + return adm9240_intrusion_write(dev, attr, val); + case hwmon_in: + return adm9240_in_write(dev, attr, channel, val); + case hwmon_fan: + return adm9240_fan_write(dev, attr, channel, val); + case hwmon_temp: + return adm9240_temp_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t adm9240_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + umode_t mode = 0; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_alarms: + mode = 0444; + break; + default: + break; + } + break; + case hwmon_intrusion: + switch (attr) { + case hwmon_intrusion_alarm: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp: + case hwmon_temp_alarm: + mode = 0444; + break; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_div: + case hwmon_fan_alarm: + mode = 0444; + break; + case hwmon_fan_min: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_alarm: + mode = 0444; + break; + case hwmon_in_min: + case hwmon_in_max: + mode = 0644; + break; + default: + break; + } + break; + default: + break; + } + return mode; +} + +static const struct hwmon_ops adm9240_hwmon_ops = { + .is_visible = adm9240_is_visible, + .read = adm9240_read, + .write = adm9240_write, +}; + +static const struct hwmon_channel_info *adm9240_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), + HWMON_CHANNEL_INFO(intrusion, HWMON_INTRUSION_ALARM), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_ALARM), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM), + NULL +}; + +static const struct hwmon_chip_info adm9240_chip_info = { + .ops = &adm9240_hwmon_ops, + .info = adm9240_info, +}; + +static bool adm9240_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADM9240_REG_IN(0) ... ADM9240_REG_IN(5): + case ADM9240_REG_FAN(0) ... ADM9240_REG_FAN(1): + case ADM9240_REG_INT(0) ... ADM9240_REG_INT(1): + case ADM9240_REG_TEMP: + case ADM9240_REG_TEMP_CONF: + case ADM9240_REG_VID_FAN_DIV: + case ADM9240_REG_VID4: + case ADM9240_REG_ANALOG_OUT: + return true; + default: + return false; + } +} + +static const struct regmap_config adm9240_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, + .volatile_reg = adm9240_volatile_reg, +}; + +static int adm9240_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; struct device *hwmon_dev; struct adm9240_data *data; + int err; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(new_client, data); - data->client = new_client; + data->dev = dev; mutex_init(&data->update_lock); + data->regmap = devm_regmap_init_i2c(client, &adm9240_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); - adm9240_init_client(new_client); + err = adm9240_init_client(data); + if (err < 0) + return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, - new_client->name, - data, - adm9240_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adm9240_chip_info, + adm9240_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } @@ -741,7 +819,7 @@ static struct i2c_driver adm9240_driver = { .driver = { .name = "adm9240", }, - .probe = adm9240_probe, + .probe_new = adm9240_probe, .id_table = adm9240_id, .detect = adm9240_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index d895b73fde6f..7246198f0901 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -99,8 +99,9 @@ static const struct regmap_config ads2830_regmap_config = { .val_bits = 8, }; -static int ads7828_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id ads7828_device_ids[]; + +static int ads7828_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ads7828_platform_data *pdata = dev_get_platdata(dev); @@ -141,7 +142,7 @@ static int ads7828_probe(struct i2c_client *client, chip = (enum ads7828_chips) of_device_get_match_data(&client->dev); else - chip = id->driver_data; + chip = i2c_match_id(ads7828_device_ids, client)->driver_data; /* Bound Vref with min/max values */ vref_mv = clamp_val(vref_mv, ADS7828_EXT_VREF_MV_MIN, @@ -207,7 +208,7 @@ static struct i2c_driver ads7828_driver = { }, .id_table = ads7828_device_ids, - .probe = ads7828_probe, + .probe_new = ads7828_probe, }; module_i2c_driver(ads7828_driver); diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c index 9fad01191620..067865f4887a 100644 --- a/drivers/hwmon/adt7310.c +++ b/drivers/hwmon/adt7310.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/regmap.h> #include <linux/spi/spi.h> #include <asm/unaligned.h> @@ -38,16 +39,13 @@ static const u8 adt7310_reg_table[] = { #define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET) -static int adt7310_spi_read_word(struct device *dev, u8 reg) +static int adt7310_spi_read_word(struct spi_device *spi, u8 reg) { - struct spi_device *spi = to_spi_device(dev); - return spi_w8r16be(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); } -static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data) +static int adt7310_spi_write_word(struct spi_device *spi, u8 reg, u16 data) { - struct spi_device *spi = to_spi_device(dev); u8 buf[3]; buf[0] = AD7310_COMMAND(reg); @@ -56,17 +54,13 @@ static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data) return spi_write(spi, buf, sizeof(buf)); } -static int adt7310_spi_read_byte(struct device *dev, u8 reg) +static int adt7310_spi_read_byte(struct spi_device *spi, u8 reg) { - struct spi_device *spi = to_spi_device(dev); - return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); } -static int adt7310_spi_write_byte(struct device *dev, u8 reg, - u8 data) +static int adt7310_spi_write_byte(struct spi_device *spi, u8 reg, u8 data) { - struct spi_device *spi = to_spi_device(dev); u8 buf[2]; buf[0] = AD7310_COMMAND(reg); @@ -75,22 +69,77 @@ static int adt7310_spi_write_byte(struct device *dev, u8 reg, return spi_write(spi, buf, sizeof(buf)); } -static const struct adt7x10_ops adt7310_spi_ops = { - .read_word = adt7310_spi_read_word, - .write_word = adt7310_spi_write_word, - .read_byte = adt7310_spi_read_byte, - .write_byte = adt7310_spi_write_byte, -}; +static bool adt7310_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADT7X10_TEMPERATURE: + case ADT7X10_STATUS: + return true; + default: + return false; + } +} -static int adt7310_spi_probe(struct spi_device *spi) +static int adt7310_reg_read(void *context, unsigned int reg, unsigned int *val) { - return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq, - &adt7310_spi_ops); + struct spi_device *spi = context; + int regval; + + switch (reg) { + case ADT7X10_TEMPERATURE: + case ADT7X10_T_ALARM_HIGH: + case ADT7X10_T_ALARM_LOW: + case ADT7X10_T_CRIT: + regval = adt7310_spi_read_word(spi, reg); + break; + default: + regval = adt7310_spi_read_byte(spi, reg); + break; + } + if (regval < 0) + return regval; + *val = regval; + return 0; +} + +static int adt7310_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct spi_device *spi = context; + int ret; + + switch (reg) { + case ADT7X10_TEMPERATURE: + case ADT7X10_T_ALARM_HIGH: + case ADT7X10_T_ALARM_LOW: + case ADT7X10_T_CRIT: + ret = adt7310_spi_write_word(spi, reg, val); + break; + default: + ret = adt7310_spi_write_byte(spi, reg, val); + break; + } + return ret; } -static int adt7310_spi_remove(struct spi_device *spi) +static const struct regmap_config adt7310_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = adt7310_regmap_is_volatile, + .reg_read = adt7310_reg_read, + .reg_write = adt7310_reg_write, +}; + +static int adt7310_spi_probe(struct spi_device *spi) { - return adt7x10_remove(&spi->dev, spi->irq); + struct regmap *regmap; + + regmap = devm_regmap_init(&spi->dev, NULL, spi, &adt7310_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq, + regmap); } static const struct spi_device_id adt7310_id[] = { @@ -103,10 +152,9 @@ MODULE_DEVICE_TABLE(spi, adt7310_id); static struct spi_driver adt7310_driver = { .driver = { .name = "adt7310", - .pm = ADT7X10_DEV_PM_OPS, + .pm = pm_sleep_ptr(&adt7x10_dev_pm_ops), }, .probe = adt7310_spi_probe, - .remove = adt7310_spi_remove, .id_table = adt7310_id, }; module_spi_driver(adt7310_driver); diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index 80f8a4673315..0cebf6777239 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -9,49 +9,82 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include "adt7x10.h" -static int adt7410_i2c_read_word(struct device *dev, u8 reg) +static bool adt7410_regmap_is_volatile(struct device *dev, unsigned int reg) { - return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg); + switch (reg) { + case ADT7X10_TEMPERATURE: + case ADT7X10_STATUS: + return true; + default: + return false; + } } -static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data) +static int adt7410_reg_read(void *context, unsigned int reg, unsigned int *val) { - return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data); -} + struct i2c_client *client = context; + int regval; -static int adt7410_i2c_read_byte(struct device *dev, u8 reg) -{ - return i2c_smbus_read_byte_data(to_i2c_client(dev), reg); + switch (reg) { + case ADT7X10_TEMPERATURE: + case ADT7X10_T_ALARM_HIGH: + case ADT7X10_T_ALARM_LOW: + case ADT7X10_T_CRIT: + regval = i2c_smbus_read_word_swapped(client, reg); + break; + default: + regval = i2c_smbus_read_byte_data(client, reg); + break; + } + if (regval < 0) + return regval; + *val = regval; + return 0; } -static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data) +static int adt7410_reg_write(void *context, unsigned int reg, unsigned int val) { - return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data); + struct i2c_client *client = context; + int ret; + + switch (reg) { + case ADT7X10_TEMPERATURE: + case ADT7X10_T_ALARM_HIGH: + case ADT7X10_T_ALARM_LOW: + case ADT7X10_T_CRIT: + ret = i2c_smbus_write_word_swapped(client, reg, val); + break; + default: + ret = i2c_smbus_write_byte_data(client, reg, val); + break; + } + return ret; } -static const struct adt7x10_ops adt7410_i2c_ops = { - .read_word = adt7410_i2c_read_word, - .write_word = adt7410_i2c_write_word, - .read_byte = adt7410_i2c_read_byte, - .write_byte = adt7410_i2c_write_byte, +static const struct regmap_config adt7410_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = ADT7X10_ID, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = adt7410_regmap_is_volatile, + .reg_read = adt7410_reg_read, + .reg_write = adt7410_reg_write, }; -static int adt7410_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adt7410_i2c_probe(struct i2c_client *client) { - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; + struct regmap *regmap; - return adt7x10_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops); -} + regmap = devm_regmap_init(&client->dev, NULL, client, + &adt7410_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); -static int adt7410_i2c_remove(struct i2c_client *client) -{ - return adt7x10_remove(&client->dev, client->irq); + return adt7x10_probe(&client->dev, client->name, client->irq, regmap); } static const struct i2c_device_id adt7410_ids[] = { @@ -65,10 +98,9 @@ static struct i2c_driver adt7410_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7410", - .pm = ADT7X10_DEV_PM_OPS, + .pm = pm_sleep_ptr(&adt7x10_dev_pm_ops), }, - .probe = adt7410_i2c_probe, - .remove = adt7410_i2c_remove, + .probe_new = adt7410_i2c_probe, .id_table = adt7410_ids, .address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b), }; diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index c7010b91bc13..bf5c5618f8d0 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -590,7 +590,7 @@ static int adt7411_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "adt7411", I2C_NAME_SIZE); + strscpy(info->type, "adt7411", I2C_NAME_SIZE); return 0; } @@ -666,8 +666,7 @@ static const struct hwmon_chip_info adt7411_chip_info = { .info = adt7411_info, }; -static int adt7411_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adt7411_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct adt7411_data *data; @@ -707,7 +706,7 @@ static struct i2c_driver adt7411_driver = { .driver = { .name = "adt7411", }, - .probe = adt7411_probe, + .probe_new = adt7411_probe, .id_table = adt7411_id, .detect = adt7411_detect, .address_list = normal_i2c, @@ -716,7 +715,6 @@ static struct i2c_driver adt7411_driver = { module_i2c_driver(adt7411_driver); -MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de> and " - "Wolfram Sang <w.sang@pengutronix.de>"); +MODULE_AUTHOR("Sascha Hauer, Wolfram Sang <kernel@pengutronix.de>"); MODULE_DESCRIPTION("ADT7411 driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 319a0519ebdb..9c0235849d4b 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -435,7 +435,7 @@ static const char *voltage_label(struct adt7462_data *data, int which) case 3: return "+1.5V"; } - /* fall through */ + fallthrough; case 2: if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) return "+12V3"; @@ -493,7 +493,7 @@ static const char *voltage_label(struct adt7462_data *data, int which) case 3: return "+1.5"; } - /* fall through */ + fallthrough; case 11: if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == ADT7462_PIN28_VOLT && @@ -531,7 +531,7 @@ static int voltage_multiplier(struct adt7462_data *data, int which) case 3: return 7800; } - /* fall through */ + fallthrough; case 2: if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) return 62500; @@ -589,7 +589,7 @@ static int voltage_multiplier(struct adt7462_data *data, int which) case 3: return 7800; } - /* fall through */ + fallthrough; case 11: case 12: if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == @@ -1782,13 +1782,12 @@ static int adt7462_detect(struct i2c_client *client, if (revision != ADT7462_REVISION) return -ENODEV; - strlcpy(info->type, "adt7462", I2C_NAME_SIZE); + strscpy(info->type, "adt7462", I2C_NAME_SIZE); return 0; } -static int adt7462_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adt7462_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct adt7462_data *data; @@ -1820,7 +1819,7 @@ static struct i2c_driver adt7462_driver = { .driver = { .name = "adt7462", }, - .probe = adt7462_probe, + .probe_new = adt7462_probe, .id_table = adt7462_id, .detect = adt7462_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index a30f34cf512c..927f8df05b7c 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -18,6 +18,8 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/kthread.h> +#include <linux/regmap.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/util_macros.h> @@ -35,7 +37,10 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_REG_PWM_MAX_BASE_ADDR 0x38 #define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B #define ADT7470_REG_CFG 0x40 +#define ADT7470_STRT_MASK 0x01 +#define ADT7470_TEST_MASK 0x02 #define ADT7470_FSPD_MASK 0x04 +#define ADT7470_T05_STB_MASK 0x80 #define ADT7470_REG_ALARM1 0x41 #define ADT7470_R1T_ALARM 0x01 #define ADT7470_R2T_ALARM 0x02 @@ -137,7 +142,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_FREQ_SHIFT 4 struct adt7470_data { - struct i2c_client *client; + struct regmap *regmap; struct mutex lock; char sensors_valid; char limits_valid; @@ -171,51 +176,76 @@ struct adt7470_data { * 16-bit registers on the ADT7470 are low-byte first. The data sheet says * that the low byte must be read before the high byte. */ -static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg) +static inline int adt7470_read_word_data(struct adt7470_data *data, unsigned int reg, + unsigned int *val) { - u16 foo; - foo = i2c_smbus_read_byte_data(client, reg); - foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); - return foo; + u8 regval[2]; + int err; + + err = regmap_bulk_read(data->regmap, reg, ®val, 2); + if (err < 0) + return err; + + *val = regval[0] | (regval[1] << 8); + + return 0; } -static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg, - u16 value) +static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg, + unsigned int val) { - return i2c_smbus_write_byte_data(client, reg, value & 0xFF) - || i2c_smbus_write_byte_data(client, reg + 1, value >> 8); + u8 regval[2]; + + regval[0] = val & 0xFF; + regval[1] = val >> 8; + + return regmap_bulk_write(data->regmap, reg, ®val, 2); } /* Probe for temperature sensors. Assumes lock is held */ -static int adt7470_read_temperatures(struct i2c_client *client, - struct adt7470_data *data) +static int adt7470_read_temperatures(struct adt7470_data *data) { unsigned long res; + unsigned int pwm_cfg[2]; + int err; int i; - u8 cfg, pwm[4], pwm_cfg[2]; + u8 pwm[ADT7470_FAN_COUNT]; /* save pwm[1-4] config register */ - pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0)); - pwm_cfg[1] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(2)); + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); + if (err < 0) + return err; + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(2), &pwm_cfg[1]); + if (err < 0) + return err; /* set manual pwm to whatever it is set to now */ - for (i = 0; i < ADT7470_FAN_COUNT; i++) - pwm[i] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM(i)); + err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) + return err; /* put pwm in manual mode */ - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), - pwm_cfg[0] & ~(ADT7470_PWM_AUTO_MASK)); - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), - pwm_cfg[1] & ~(ADT7470_PWM_AUTO_MASK)); + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(0), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) + return err; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) + return err; /* write pwm control to whatever it was */ - for (i = 0; i < ADT7470_FAN_COUNT; i++) - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(i), pwm[i]); + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) + return err; /* start reading temperature sensors */ - cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - cfg |= 0x80; - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) + return err; /* Delay is 200ms * number of temp sensors. */ res = msleep_interruptible((data->num_temp_sensors >= 0 ? @@ -223,26 +253,31 @@ static int adt7470_read_temperatures(struct i2c_client *client, TEMP_COLLECTION_TIME)); /* done reading temperature sensors */ - cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - cfg &= ~0x80; - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) + return err; /* restore pwm[1-4] config registers */ - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); - - if (res) { - pr_err("ha ha, interrupted\n"); + err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); + if (err < 0) + return err; + err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + if (err < 0) + return err; + + if (res) return -EAGAIN; - } /* Only count fans if we have to */ if (data->num_temp_sensors >= 0) return 0; + err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], + ADT7470_TEMP_COUNT); + if (err < 0) + return err; for (i = 0; i < ADT7470_TEMP_COUNT; i++) { - data->temp[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_REG(i)); if (data->temp[i]) data->num_temp_sensors = i + 1; } @@ -257,137 +292,181 @@ static int adt7470_update_thread(void *p) while (!kthread_should_stop()) { mutex_lock(&data->lock); - adt7470_read_temperatures(client, data); + adt7470_read_temperatures(data); mutex_unlock(&data->lock); - set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) break; - schedule_timeout(msecs_to_jiffies(data->auto_update_interval)); + schedule_timeout_interruptible(msecs_to_jiffies(data->auto_update_interval)); } return 0; } -static struct adt7470_data *adt7470_update_device(struct device *dev) +static int adt7470_update_sensors(struct adt7470_data *data) { - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long local_jiffies = jiffies; - u8 cfg; + unsigned int val; + int err; int i; - int need_sensors = 1; - int need_limits = 1; - - /* - * Figure out if we need to update the shadow registers. - * Lockless means that we may occasionally report out of - * date data. - */ - if (time_before(local_jiffies, data->sensors_last_updated + - SENSOR_REFRESH_INTERVAL) && - data->sensors_valid) - need_sensors = 0; - - if (time_before(local_jiffies, data->limits_last_updated + - LIMIT_REFRESH_INTERVAL) && - data->limits_valid) - need_limits = 0; - - if (!need_sensors && !need_limits) - return data; - - mutex_lock(&data->lock); - if (!need_sensors) - goto no_sensor_update; if (!data->temperatures_probed) - adt7470_read_temperatures(client, data); + err = adt7470_read_temperatures(data); else - for (i = 0; i < ADT7470_TEMP_COUNT; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_REG(i)); + err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], + ADT7470_TEMP_COUNT); + if (err < 0) + return err; - for (i = 0; i < ADT7470_FAN_COUNT; i++) - data->fan[i] = adt7470_read_word_data(client, - ADT7470_REG_FAN(i)); + for (i = 0; i < ADT7470_FAN_COUNT; i++) { + err = adt7470_read_word_data(data, ADT7470_REG_FAN(i), &val); + if (err < 0) + return err; + data->fan[i] = val; + } - for (i = 0; i < ADT7470_PWM_COUNT; i++) { - int reg; - int reg_mask; + err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &data->pwm[0], ADT7470_PWM_COUNT); + if (err < 0) + return err; - data->pwm[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM(i)); + for (i = 0; i < ADT7470_PWM_COUNT; i++) { + unsigned int mask; if (i % 2) - reg_mask = ADT7470_PWM2_AUTO_MASK; + mask = ADT7470_PWM2_AUTO_MASK; else - reg_mask = ADT7470_PWM1_AUTO_MASK; + mask = ADT7470_PWM1_AUTO_MASK; - reg = ADT7470_REG_PWM_CFG(i); - if (i2c_smbus_read_byte_data(client, reg) & reg_mask) - data->pwm_automatic[i] = 1; - else - data->pwm_automatic[i] = 0; + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(i), &val); + if (err < 0) + return err; + data->pwm_automatic[i] = !!(val & mask); - reg = ADT7470_REG_PWM_AUTO_TEMP(i); - cfg = i2c_smbus_read_byte_data(client, reg); + err = regmap_read(data->regmap, ADT7470_REG_PWM_AUTO_TEMP(i), &val); + if (err < 0) + return err; if (!(i % 2)) - data->pwm_auto_temp[i] = cfg >> 4; + data->pwm_auto_temp[i] = val >> 4; else - data->pwm_auto_temp[i] = cfg & 0xF; + data->pwm_auto_temp[i] = val & 0xF; } - if (i2c_smbus_read_byte_data(client, ADT7470_REG_CFG) & - ADT7470_FSPD_MASK) - data->force_pwm_max = 1; - else - data->force_pwm_max = 0; + err = regmap_read(data->regmap, ADT7470_REG_CFG, &val); + if (err < 0) + return err; + data->force_pwm_max = !!(val & ADT7470_FSPD_MASK); + + err = regmap_read(data->regmap, ADT7470_REG_ALARM1, &val); + if (err < 0) + return err; + data->alarm = val; + if (data->alarm & ADT7470_OOL_ALARM) { + err = regmap_read(data->regmap, ADT7470_REG_ALARM2, &val); + if (err < 0) + return err; + data->alarm |= ALARM2(val); + } - data->alarm = i2c_smbus_read_byte_data(client, ADT7470_REG_ALARM1); - if (data->alarm & ADT7470_OOL_ALARM) - data->alarm |= ALARM2(i2c_smbus_read_byte_data(client, - ADT7470_REG_ALARM2)); - data->alarms_mask = adt7470_read_word_data(client, - ADT7470_REG_ALARM1_MASK); + err = adt7470_read_word_data(data, ADT7470_REG_ALARM1_MASK, &val); + if (err < 0) + return err; + data->alarms_mask = val; - data->sensors_last_updated = local_jiffies; - data->sensors_valid = 1; + return 0; +} -no_sensor_update: - if (!need_limits) - goto out; +static int adt7470_update_limits(struct adt7470_data *data) +{ + unsigned int val; + int err; + int i; for (i = 0; i < ADT7470_TEMP_COUNT; i++) { - data->temp_min[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_MIN_REG(i)); - data->temp_max[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_MAX_REG(i)); + err = regmap_read(data->regmap, ADT7470_TEMP_MIN_REG(i), &val); + if (err < 0) + return err; + data->temp_min[i] = (s8)val; + err = regmap_read(data->regmap, ADT7470_TEMP_MAX_REG(i), &val); + if (err < 0) + return err; + data->temp_max[i] = (s8)val; } for (i = 0; i < ADT7470_FAN_COUNT; i++) { - data->fan_min[i] = adt7470_read_word_data(client, - ADT7470_REG_FAN_MIN(i)); - data->fan_max[i] = adt7470_read_word_data(client, - ADT7470_REG_FAN_MAX(i)); + err = adt7470_read_word_data(data, ADT7470_REG_FAN_MIN(i), &val); + if (err < 0) + return err; + data->fan_min[i] = val; + err = adt7470_read_word_data(data, ADT7470_REG_FAN_MAX(i), &val); + if (err < 0) + return err; + data->fan_max[i] = val; } for (i = 0; i < ADT7470_PWM_COUNT; i++) { - data->pwm_max[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM_MAX(i)); - data->pwm_min[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM_MIN(i)); - data->pwm_tmin[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM_TMIN(i)); + err = regmap_read(data->regmap, ADT7470_REG_PWM_MAX(i), &val); + if (err < 0) + return err; + data->pwm_max[i] = val; + err = regmap_read(data->regmap, ADT7470_REG_PWM_MIN(i), &val); + if (err < 0) + return err; + data->pwm_min[i] = val; + err = regmap_read(data->regmap, ADT7470_REG_PWM_TMIN(i), &val); + if (err < 0) + return err; + data->pwm_tmin[i] = (s8)val; } - data->limits_last_updated = local_jiffies; - data->limits_valid = 1; + return 0; +} +static struct adt7470_data *adt7470_update_device(struct device *dev) +{ + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned long local_jiffies = jiffies; + int need_sensors = 1; + int need_limits = 1; + int err; + + /* + * Figure out if we need to update the shadow registers. + * Lockless means that we may occasionally report out of + * date data. + */ + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) && + data->sensors_valid) + need_sensors = 0; + + if (time_before(local_jiffies, data->limits_last_updated + + LIMIT_REFRESH_INTERVAL) && + data->limits_valid) + need_limits = 0; + + if (!need_sensors && !need_limits) + return data; + + mutex_lock(&data->lock); + if (need_sensors) { + err = adt7470_update_sensors(data); + if (err < 0) + goto out; + data->sensors_last_updated = local_jiffies; + data->sensors_valid = 1; + } + + if (need_limits) { + err = adt7470_update_limits(data); + if (err < 0) + goto out; + data->limits_last_updated = local_jiffies; + data->limits_valid = 1; + } out: mutex_unlock(&data->lock); - return data; + + return err < 0 ? ERR_PTR(err) : data; } static ssize_t auto_update_interval_show(struct device *dev, @@ -395,6 +474,10 @@ static ssize_t auto_update_interval_show(struct device *dev, char *buf) { struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->auto_update_interval); } @@ -422,6 +505,10 @@ static ssize_t num_temp_sensors_show(struct device *dev, char *buf) { struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->num_temp_sensors); } @@ -446,84 +533,69 @@ static ssize_t num_temp_sensors_store(struct device *dev, return count; } -static ssize_t temp_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); - return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]); -} - -static ssize_t temp_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; - temp = clamp_val(temp, -128000, 127000); - temp = DIV_ROUND_CLOSEST(temp, 1000); - - mutex_lock(&data->lock); - data->temp_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_TEMP_MIN_REG(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_temp_input: + *val = 1000 * data->temp[channel]; + break; + case hwmon_temp_min: + *val = 1000 * data->temp_min[channel]; + break; + case hwmon_temp_max: + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: + *val = !!(data->alarm & channel); + break; + default: + return -EOPNOTSUPP; + } -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]); + return 0; } -static ssize_t temp_max_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; + int err; - temp = clamp_val(temp, -128000, 127000); - temp = DIV_ROUND_CLOSEST(temp, 1000); - - mutex_lock(&data->lock); - data->temp_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_TEMP_MAX_REG(attr->index), - temp); - mutex_unlock(&data->lock); + val = clamp_val(val, -128000, 127000); + val = DIV_ROUND_CLOSEST(val, 1000); - return count; -} + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); + data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); + data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); + mutex_unlock(&data->lock); + break; + default: + return -EOPNOTSUPP; + } -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]); + return err; } static ssize_t alarm_mask_show(struct device *dev, - struct device_attribute *devattr, - char *buf) + struct device_attribute *devattr, char *buf) { struct adt7470_data *data = adt7470_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%x\n", data->alarms_mask); } @@ -533,6 +605,7 @@ static ssize_t alarm_mask_store(struct device *dev, { struct adt7470_data *data = dev_get_drvdata(dev); long mask; + int err; if (kstrtoul(buf, 0, &mask)) return -EINVAL; @@ -542,101 +615,87 @@ static ssize_t alarm_mask_store(struct device *dev, mutex_lock(&data->lock); data->alarms_mask = mask; - adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask); + err = adt7470_write_word_data(data, ADT7470_REG_ALARM1_MASK, mask); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } -static ssize_t fan_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); - if (FAN_DATA_VALID(data->fan_max[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan_max[attr->index])); - else - return sprintf(buf, "0\n"); -} - -static ssize_t fan_max_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp) || !temp) - return -EINVAL; + if (IS_ERR(data)) + return PTR_ERR(data); - temp = FAN_RPM_TO_PERIOD(temp); - temp = clamp_val(temp, 1, 65534); - - mutex_lock(&data->lock); - data->fan_max[attr->index] = temp; - adt7470_write_word_data(client, ADT7470_REG_FAN_MAX(attr->index), temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); + switch (attr) { + case hwmon_fan_input: + if (FAN_DATA_VALID(data->fan[channel])) + *val = FAN_PERIOD_TO_RPM(data->fan[channel]); + else + *val = 0; + break; + case hwmon_fan_min: + if (FAN_DATA_VALID(data->fan_min[channel])) + *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); + else + *val = 0; + break; + case hwmon_fan_max: + if (FAN_DATA_VALID(data->fan_max[channel])) + *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); + else + *val = 0; + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & (1 << (12 + channel))); + break; + default: + return -EOPNOTSUPP; + } - if (FAN_DATA_VALID(data->fan_min[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan_min[attr->index])); - else - return sprintf(buf, "0\n"); + return 0; } -static ssize_t fan_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int adt7470_fan_write(struct device *dev, u32 attr, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; + int err; - if (kstrtol(buf, 10, &temp) || !temp) + if (val <= 0) return -EINVAL; - temp = FAN_RPM_TO_PERIOD(temp); - temp = clamp_val(temp, 1, 65534); - - mutex_lock(&data->lock); - data->fan_min[attr->index] = temp; - adt7470_write_word_data(client, ADT7470_REG_FAN_MIN(attr->index), temp); - mutex_unlock(&data->lock); + val = FAN_RPM_TO_PERIOD(val); + val = clamp_val(val, 1, 65534); - return count; -} - -static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); + switch (attr) { + case hwmon_fan_min: + mutex_lock(&data->lock); + data->fan_min[channel] = val; + err = adt7470_write_word_data(data, ADT7470_REG_FAN_MIN(channel), val); + mutex_unlock(&data->lock); + break; + case hwmon_fan_max: + mutex_lock(&data->lock); + data->fan_max[channel] = val; + err = adt7470_write_word_data(data, ADT7470_REG_FAN_MAX(channel), val); + mutex_unlock(&data->lock); + break; + default: + return -EOPNOTSUPP; + } - if (FAN_DATA_VALID(data->fan[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan[attr->index])); - else - return sprintf(buf, "0\n"); + return err; } static ssize_t force_pwm_max_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->force_pwm_max); } @@ -645,53 +704,20 @@ static ssize_t force_pwm_max_store(struct device *dev, const char *buf, size_t count) { struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; - u8 reg; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; mutex_lock(&data->lock); data->force_pwm_max = temp; - reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - if (temp) - reg |= ADT7470_FSPD_MASK; - else - reg &= ~ADT7470_FSPD_MASK; - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - return sprintf(buf, "%d\n", data->pwm[attr->index]); -} - -static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; - - temp = clamp_val(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(attr->index), temp); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_FSPD_MASK, + temp ? ADT7470_FSPD_MASK : 0); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } /* These are the valid PWM frequencies to the nearest Hz */ @@ -699,17 +725,20 @@ static const int adt7470_freq_map[] = { 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 }; -static ssize_t pwm1_freq_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int pwm1_freq_get(struct device *dev) { - struct adt7470_data *data = adt7470_update_device(dev); - unsigned char cfg_reg_1; - unsigned char cfg_reg_2; + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int cfg_reg_1, cfg_reg_2; int index; + int err; mutex_lock(&data->lock); - cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG); - cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2); + err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); + if (err < 0) + goto out; + err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); + if (err < 0) + goto out; mutex_unlock(&data->lock); index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; @@ -718,22 +747,43 @@ static ssize_t pwm1_freq_show(struct device *dev, if (index >= ARRAY_SIZE(adt7470_freq_map)) index = ARRAY_SIZE(adt7470_freq_map) - 1; - return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]); + return adt7470_freq_map[index]; + +out: + mutex_unlock(&data->lock); + return err; } -static ssize_t pwm1_freq_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_pwm_input: + *val = data->pwm[channel]; + break; + case hwmon_pwm_enable: + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: + *val = pwm1_freq_get(dev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int pwm1_freq_set(struct device *dev, long freq) { struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long freq; + unsigned int low_freq = ADT7470_CFG_LF; int index; - int low_freq = ADT7470_CFG_LF; - unsigned char val; - - if (kstrtol(buf, 10, &freq)) - return -EINVAL; + int err; /* Round the user value given to the closest available frequency */ index = find_closest(freq, adt7470_freq_map, @@ -746,16 +796,61 @@ static ssize_t pwm1_freq_store(struct device *dev, mutex_lock(&data->lock); /* Configuration Register 1 */ - val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, - (val & ~ADT7470_CFG_LF) | low_freq); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_CFG_LF, low_freq); + if (err < 0) + goto out; + /* Configuration Register 2 */ - val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2); - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2, - (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT)); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); +out: mutex_unlock(&data->lock); - return count; + return err; +} + +static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val) +{ + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int pwm_auto_reg_mask; + int err; + + switch (attr) { + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); + data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), + data->pwm[channel]); + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: + if (channel % 2) + pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; + else + pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; + + if (val != 2 && val != 1) + return -EINVAL; + val--; + + mutex_lock(&data->lock); + data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: + err = pwm1_freq_set(dev, val); + break; + default: + return -EOPNOTSUPP; + } + + return err; } static ssize_t pwm_max_show(struct device *dev, @@ -763,6 +858,10 @@ static ssize_t pwm_max_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->pwm_max[attr->index]); } @@ -772,8 +871,8 @@ static ssize_t pwm_max_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -782,11 +881,11 @@ static ssize_t pwm_max_store(struct device *dev, mutex_lock(&data->lock); data->pwm_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MAX(attr->index), - temp); + err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index), + temp); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } static ssize_t pwm_min_show(struct device *dev, @@ -794,6 +893,10 @@ static ssize_t pwm_min_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->pwm_min[attr->index]); } @@ -803,8 +906,8 @@ static ssize_t pwm_min_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -813,11 +916,11 @@ static ssize_t pwm_min_store(struct device *dev, mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MIN(attr->index), - temp); + err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index), + temp); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } static ssize_t pwm_tmax_show(struct device *dev, @@ -825,6 +928,10 @@ static ssize_t pwm_tmax_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + /* the datasheet says that tmax = tmin + 20C */ return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index])); } @@ -834,6 +941,10 @@ static ssize_t pwm_tmin_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]); } @@ -843,8 +954,8 @@ static ssize_t pwm_tmin_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -854,56 +965,11 @@ static ssize_t pwm_tmin_store(struct device *dev, mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_TMIN(attr->index), - temp); + err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index), + temp); mutex_unlock(&data->lock); - return count; -} - -static ssize_t pwm_auto_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]); -} - -static ssize_t pwm_auto_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index); - int pwm_auto_reg_mask; - long temp; - u8 reg; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; - - if (attr->index % 2) - pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; - else - pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; - - if (temp != 2 && temp != 1) - return -EINVAL; - temp--; - - mutex_lock(&data->lock); - data->pwm_automatic[attr->index] = temp; - reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); - if (temp) - reg |= pwm_auto_reg_mask; - else - reg &= ~pwm_auto_reg_mask; - i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); - mutex_unlock(&data->lock); - - return count; + return err < 0 ? err : count; } static ssize_t pwm_auto_temp_show(struct device *dev, @@ -911,8 +977,12 @@ static ssize_t pwm_auto_temp_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); - u8 ctrl = data->pwm_auto_temp[attr->index]; + u8 ctrl; + + if (IS_ERR(data)) + return PTR_ERR(data); + ctrl = data->pwm_auto_temp[attr->index]; if (ctrl) return sprintf(buf, "%d\n", 1 << (ctrl - 1)); else @@ -934,10 +1004,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); + unsigned int mask, val; long temp; - u8 reg; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -948,111 +1018,27 @@ static ssize_t pwm_auto_temp_store(struct device *dev, mutex_lock(&data->lock); data->pwm_automatic[attr->index] = temp; - reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); if (!(attr->index % 2)) { - reg &= 0xF; - reg |= (temp << 4) & 0xF0; + mask = 0xF0; + val = (temp << 4) & 0xF0; } else { - reg &= 0xF0; - reg |= temp & 0xF; + mask = 0x0F; + val = temp & 0x0F; } - i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); mutex_unlock(&data->lock); - return count; -} - -static ssize_t alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - - if (data->alarm & attr->index) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); + return err < 0 ? err : count; } static DEVICE_ATTR_RW(alarm_mask); static DEVICE_ATTR_RW(num_temp_sensors); static DEVICE_ATTR_RW(auto_update_interval); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); -static SENSOR_DEVICE_ATTR_RW(temp4_max, temp_max, 3); -static SENSOR_DEVICE_ATTR_RW(temp5_max, temp_max, 4); -static SENSOR_DEVICE_ATTR_RW(temp6_max, temp_max, 5); -static SENSOR_DEVICE_ATTR_RW(temp7_max, temp_max, 6); -static SENSOR_DEVICE_ATTR_RW(temp8_max, temp_max, 7); -static SENSOR_DEVICE_ATTR_RW(temp9_max, temp_max, 8); -static SENSOR_DEVICE_ATTR_RW(temp10_max, temp_max, 9); - -static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1); -static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2); -static SENSOR_DEVICE_ATTR_RW(temp4_min, temp_min, 3); -static SENSOR_DEVICE_ATTR_RW(temp5_min, temp_min, 4); -static SENSOR_DEVICE_ATTR_RW(temp6_min, temp_min, 5); -static SENSOR_DEVICE_ATTR_RW(temp7_min, temp_min, 6); -static SENSOR_DEVICE_ATTR_RW(temp8_min, temp_min, 7); -static SENSOR_DEVICE_ATTR_RW(temp9_min, temp_min, 8); -static SENSOR_DEVICE_ATTR_RW(temp10_min, temp_min, 9); - -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_input, temp, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_input, temp, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_input, temp, 7); -static SENSOR_DEVICE_ATTR_RO(temp9_input, temp, 8); -static SENSOR_DEVICE_ATTR_RO(temp10_input, temp, 9); - -static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, ADT7470_R1T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, ADT7470_R2T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, ADT7470_R3T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp4_alarm, alarm, ADT7470_R4T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp5_alarm, alarm, ADT7470_R5T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp6_alarm, alarm, ADT7470_R6T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp7_alarm, alarm, ADT7470_R7T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp8_alarm, alarm, ALARM2(ADT7470_R8T_ALARM)); -static SENSOR_DEVICE_ATTR_RO(temp9_alarm, alarm, ALARM2(ADT7470_R9T_ALARM)); -static SENSOR_DEVICE_ATTR_RO(temp10_alarm, alarm, ALARM2(ADT7470_R10T_ALARM)); - -static SENSOR_DEVICE_ATTR_RW(fan1_max, fan_max, 0); -static SENSOR_DEVICE_ATTR_RW(fan2_max, fan_max, 1); -static SENSOR_DEVICE_ATTR_RW(fan3_max, fan_max, 2); -static SENSOR_DEVICE_ATTR_RW(fan4_max, fan_max, 3); - -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2); -static SENSOR_DEVICE_ATTR_RW(fan4_min, fan_min, 3); - -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3); - -static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, ALARM2(ADT7470_FAN1_ALARM)); -static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, ALARM2(ADT7470_FAN2_ALARM)); -static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, ALARM2(ADT7470_FAN3_ALARM)); -static SENSOR_DEVICE_ATTR_RO(fan4_alarm, alarm, ALARM2(ADT7470_FAN4_ALARM)); - static SENSOR_DEVICE_ATTR_RW(force_pwm_max, force_pwm_max, 0); -static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2); -static SENSOR_DEVICE_ATTR_RW(pwm4, pwm, 3); - -static DEVICE_ATTR_RW(pwm1_freq); - static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm_min, 0); static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_pwm, pwm_min, 1); static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_pwm, pwm_min, 2); @@ -1073,11 +1059,6 @@ static SENSOR_DEVICE_ATTR_RO(pwm2_auto_point2_temp, pwm_tmax, 1); static SENSOR_DEVICE_ATTR_RO(pwm3_auto_point2_temp, pwm_tmax, 2); static SENSOR_DEVICE_ATTR_RO(pwm4_auto_point2_temp, pwm_tmax, 3); -static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_auto, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_auto, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_auto, 2); -static SENSOR_DEVICE_ATTR_RW(pwm4_enable, pwm_auto, 3); - static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels_temp, pwm_auto_temp, 0); static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels_temp, pwm_auto_temp, 1); static SENSOR_DEVICE_ATTR_RW(pwm3_auto_channels_temp, pwm_auto_temp, 2); @@ -1087,68 +1068,7 @@ static struct attribute *adt7470_attrs[] = { &dev_attr_alarm_mask.attr, &dev_attr_num_temp_sensors.attr, &dev_attr_auto_update_interval.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, - &sensor_dev_attr_temp6_max.dev_attr.attr, - &sensor_dev_attr_temp7_max.dev_attr.attr, - &sensor_dev_attr_temp8_max.dev_attr.attr, - &sensor_dev_attr_temp9_max.dev_attr.attr, - &sensor_dev_attr_temp10_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp4_min.dev_attr.attr, - &sensor_dev_attr_temp5_min.dev_attr.attr, - &sensor_dev_attr_temp6_min.dev_attr.attr, - &sensor_dev_attr_temp7_min.dev_attr.attr, - &sensor_dev_attr_temp8_min.dev_attr.attr, - &sensor_dev_attr_temp9_min.dev_attr.attr, - &sensor_dev_attr_temp10_min.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp6_input.dev_attr.attr, - &sensor_dev_attr_temp7_input.dev_attr.attr, - &sensor_dev_attr_temp8_input.dev_attr.attr, - &sensor_dev_attr_temp9_input.dev_attr.attr, - &sensor_dev_attr_temp10_input.dev_attr.attr, - &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_alarm.dev_attr.attr, - &sensor_dev_attr_temp6_alarm.dev_attr.attr, - &sensor_dev_attr_temp7_alarm.dev_attr.attr, - &sensor_dev_attr_temp8_alarm.dev_attr.attr, - &sensor_dev_attr_temp9_alarm.dev_attr.attr, - &sensor_dev_attr_temp10_alarm.dev_attr.attr, - &sensor_dev_attr_fan1_max.dev_attr.attr, - &sensor_dev_attr_fan2_max.dev_attr.attr, - &sensor_dev_attr_fan3_max.dev_attr.attr, - &sensor_dev_attr_fan4_max.dev_attr.attr, - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan3_min.dev_attr.attr, - &sensor_dev_attr_fan4_min.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, - &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &sensor_dev_attr_fan4_alarm.dev_attr.attr, &sensor_dev_attr_force_pwm_max.dev_attr.attr, - &sensor_dev_attr_pwm1.dev_attr.attr, - &dev_attr_pwm1_freq.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm4.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, @@ -1165,10 +1085,6 @@ static struct attribute *adt7470_attrs[] = { &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr, - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, - &sensor_dev_attr_pwm4_enable.dev_attr.attr, &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, @@ -1178,6 +1094,129 @@ static struct attribute *adt7470_attrs[] = { ATTRIBUTE_GROUPS(adt7470); +static int adt7470_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + switch (type) { + case hwmon_temp: + return adt7470_temp_read(dev, attr, channel, val); + case hwmon_fan: + return adt7470_fan_read(dev, attr, channel, val); + case hwmon_pwm: + return adt7470_pwm_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int adt7470_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + switch (type) { + case hwmon_temp: + return adt7470_temp_write(dev, attr, channel, val); + case hwmon_fan: + return adt7470_fan_write(dev, attr, channel, val); + case hwmon_pwm: + return adt7470_pwm_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + umode_t mode = 0; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp: + case hwmon_temp_alarm: + mode = 0444; + break; + case hwmon_temp_min: + case hwmon_temp_max: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_alarm: + mode = 0444; + break; + case hwmon_fan_min: + case hwmon_fan_max: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + mode = 0644; + break; + case hwmon_pwm_freq: + if (channel == 0) + mode = 0644; + else + mode = 0; + break; + default: + break; + } + break; + default: + break; + } + + return mode; +} + +static const struct hwmon_ops adt7470_hwmon_ops = { + .is_visible = adt7470_is_visible, + .read = adt7470_read, + .write = adt7470_write, +}; + +static const struct hwmon_channel_info *adt7470_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_FREQ, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_chip_info adt7470_chip_info = { + .ops = &adt7470_hwmon_ops, + .info = adt7470_info, +}; + /* Return 0 if detection is successful, -ENODEV otherwise */ static int adt7470_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -1200,29 +1239,24 @@ static int adt7470_detect(struct i2c_client *client, if (revision != ADT7470_REVISION) return -ENODEV; - strlcpy(info->type, "adt7470", I2C_NAME_SIZE); + strscpy(info->type, "adt7470", I2C_NAME_SIZE); return 0; } -static void adt7470_init_client(struct i2c_client *client) -{ - int reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - - if (reg < 0) { - dev_err(&client->dev, "cannot read configuration register\n"); - } else { - /* start monitoring (and do a self-test) */ - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3); - } -} +static const struct regmap_config adt7470_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, +}; -static int adt7470_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adt7470_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct adt7470_data *data; struct device *hwmon_dev; + int err; data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); if (!data) @@ -1230,39 +1264,43 @@ static int adt7470_probe(struct i2c_client *client, data->num_temp_sensors = -1; data->auto_update_interval = AUTO_UPDATE_INTERVAL; + data->regmap = devm_regmap_init_i2c(client, &adt7470_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); i2c_set_clientdata(client, data); - data->client = client; mutex_init(&data->lock); dev_info(&client->dev, "%s chip found\n", client->name); /* Initialize the ADT7470 chip */ - adt7470_init_client(client); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_STRT_MASK | ADT7470_TEST_MASK, + ADT7470_STRT_MASK | ADT7470_TEST_MASK); + if (err < 0) + return err; /* Register sysfs hooks */ - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - adt7470_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, + adt7470_groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); data->auto_update = kthread_run(adt7470_update_thread, client, "%s", dev_name(hwmon_dev)); - if (IS_ERR(data->auto_update)) { + if (IS_ERR(data->auto_update)) return PTR_ERR(data->auto_update); - } return 0; } -static int adt7470_remove(struct i2c_client *client) +static void adt7470_remove(struct i2c_client *client) { struct adt7470_data *data = i2c_get_clientdata(client); kthread_stop(data->auto_update); - return 0; } static const struct i2c_device_id adt7470_id[] = { @@ -1276,7 +1314,7 @@ static struct i2c_driver adt7470_driver = { .driver = { .name = "adt7470", }, - .probe = adt7470_probe, + .probe_new = adt7470_probe, .remove = adt7470_remove, .id_table = adt7470_id, .detect = adt7470_detect, diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 01c2eeb02aa9..51b3d16c3223 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -19,6 +19,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/jiffies.h> +#include <linux/of.h> #include <linux/util_macros.h> /* Indexes for the sysfs hooks */ @@ -111,6 +112,8 @@ #define CONFIG3_THERM 0x02 #define CONFIG4_PINFUNC 0x03 +#define CONFIG4_THERM 0x01 +#define CONFIG4_SMBALERT 0x02 #define CONFIG4_MAXDUTY 0x08 #define CONFIG4_ATTN_IN10 0x30 #define CONFIG4_ATTN_IN43 0xC0 @@ -193,6 +196,7 @@ struct adt7475_data { unsigned long measure_updated; bool valid; + u8 config2; u8 config4; u8 config5; u8 has_voltage; @@ -1338,7 +1342,7 @@ static int adt7475_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -1458,8 +1462,173 @@ static int adt7475_update_limits(struct i2c_client *client) return 0; } -static int adt7475_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int load_config3(const struct i2c_client *client, const char *propname) +{ + const char *function; + u8 config3; + int ret; + + ret = of_property_read_string(client->dev.of_node, propname, &function); + if (!ret) { + ret = adt7475_read(REG_CONFIG3); + if (ret < 0) + return ret; + + config3 = ret & ~CONFIG3_SMBALERT; + if (!strcmp("pwm2", function)) + ; + else if (!strcmp("smbalert#", function)) + config3 |= CONFIG3_SMBALERT; + else + return -EINVAL; + + return i2c_smbus_write_byte_data(client, REG_CONFIG3, config3); + } + + return 0; +} + +static int load_config4(const struct i2c_client *client, const char *propname) +{ + const char *function; + u8 config4; + int ret; + + ret = of_property_read_string(client->dev.of_node, propname, &function); + if (!ret) { + ret = adt7475_read(REG_CONFIG4); + if (ret < 0) + return ret; + + config4 = ret & ~CONFIG4_PINFUNC; + + if (!strcmp("tach4", function)) + ; + else if (!strcmp("therm#", function)) + config4 |= CONFIG4_THERM; + else if (!strcmp("smbalert#", function)) + config4 |= CONFIG4_SMBALERT; + else if (!strcmp("gpio", function)) + config4 |= CONFIG4_PINFUNC; + else + return -EINVAL; + + return i2c_smbus_write_byte_data(client, REG_CONFIG4, config4); + } + + return 0; +} + +static int load_config(const struct i2c_client *client, enum chips chip) +{ + int err; + const char *prop1, *prop2; + + switch (chip) { + case adt7473: + case adt7475: + prop1 = "adi,pin5-function"; + prop2 = "adi,pin9-function"; + break; + case adt7476: + case adt7490: + prop1 = "adi,pin10-function"; + prop2 = "adi,pin14-function"; + break; + } + + err = load_config3(client, prop1); + if (err) { + dev_err(&client->dev, "failed to configure %s\n", prop1); + return err; + } + + err = load_config4(client, prop2); + if (err) { + dev_err(&client->dev, "failed to configure %s\n", prop2); + return err; + } + + return 0; +} + +static int set_property_bit(const struct i2c_client *client, char *property, + u8 *config, u8 bit_index) +{ + u32 prop_value = 0; + int ret = of_property_read_u32(client->dev.of_node, property, + &prop_value); + + if (!ret) { + if (prop_value) + *config |= (1 << bit_index); + else + *config &= ~(1 << bit_index); + } + + return ret; +} + +static int load_attenuators(const struct i2c_client *client, enum chips chip, + struct adt7475_data *data) +{ + switch (chip) { + case adt7476: + case adt7490: + set_property_bit(client, "adi,bypass-attenuator-in0", + &data->config4, 4); + set_property_bit(client, "adi,bypass-attenuator-in1", + &data->config4, 5); + set_property_bit(client, "adi,bypass-attenuator-in3", + &data->config4, 6); + set_property_bit(client, "adi,bypass-attenuator-in4", + &data->config4, 7); + + return i2c_smbus_write_byte_data(client, REG_CONFIG4, + data->config4); + case adt7473: + case adt7475: + set_property_bit(client, "adi,bypass-attenuator-in1", + &data->config2, 5); + + return i2c_smbus_write_byte_data(client, REG_CONFIG2, + data->config2); + } + + return 0; +} + +static int adt7475_set_pwm_polarity(struct i2c_client *client) +{ + u32 states[ADT7475_PWM_COUNT]; + int ret, i; + u8 val; + + ret = of_property_read_u32_array(client->dev.of_node, + "adi,pwm-active-state", states, + ARRAY_SIZE(states)); + if (ret) + return ret; + + for (i = 0; i < ADT7475_PWM_COUNT; i++) { + ret = adt7475_read(PWM_CONFIG_REG(i)); + if (ret < 0) + return ret; + val = ret; + if (states[i]) + val &= ~BIT(4); + else + val |= BIT(4); + + ret = i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(i), val); + if (ret) + return ret; + } + + return 0; +} + +static int adt7475_probe(struct i2c_client *client) { enum chips chip; static const char * const names[] = { @@ -1472,7 +1641,8 @@ static int adt7475_probe(struct i2c_client *client, struct adt7475_data *data; struct device *hwmon_dev; int i, ret = 0, revision, group_num = 0; - u8 config2, config3; + u8 config3; + const struct i2c_device_id *id = i2c_match_id(adt7475_id, client); data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -1504,6 +1674,10 @@ static int adt7475_probe(struct i2c_client *client, revision = adt7475_read(REG_DEVID2) & 0x07; } + ret = load_config(client, chip); + if (ret) + return ret; + config3 = adt7475_read(REG_CONFIG3); /* Pin PWM2 may alternatively be used for ALERT output */ if (!(config3 & CONFIG3_SMBALERT)) @@ -1546,8 +1720,12 @@ static int adt7475_probe(struct i2c_client *client, } /* Voltage attenuators can be bypassed, globally or individually */ - config2 = adt7475_read(REG_CONFIG2); - if (config2 & CONFIG2_ATTN) { + data->config2 = adt7475_read(REG_CONFIG2); + ret = load_attenuators(client, chip, data); + if (ret) + dev_warn(&client->dev, "Error configuring attenuator bypass\n"); + + if (data->config2 & CONFIG2_ATTN) { data->bypass_attn = (0x3 << 3) | 0x3; } else { data->bypass_attn = ((data->config4 & CONFIG4_ATTN_IN10) >> 4) | @@ -1562,6 +1740,10 @@ static int adt7475_probe(struct i2c_client *client, for (i = 0; i < ADT7475_PWM_COUNT; i++) adt7475_read_pwm(client, i); + ret = adt7475_set_pwm_polarity(client); + if (ret && ret != -EINVAL) + dev_warn(&client->dev, "Error configuring pwm polarity\n"); + /* Start monitoring */ switch (chip) { case adt7475: @@ -1639,7 +1821,7 @@ static struct i2c_driver adt7475_driver = { .name = "adt7475", .of_match_table = of_match_ptr(adt7475_of_match), }, - .probe = adt7475_probe, + .probe_new = adt7475_probe, .id_table = adt7475_id, .detect = adt7475_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c index 3f03b4cf5858..da67734edafd 100644 --- a/drivers/hwmon/adt7x10.c +++ b/drivers/hwmon/adt7x10.c @@ -8,16 +8,17 @@ * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com> */ +#include <linux/device.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/regmap.h> #include "adt7x10.h" @@ -53,80 +54,57 @@ /* Each client has this additional data */ struct adt7x10_data { - const struct adt7x10_ops *ops; - const char *name; - struct device *hwmon_dev; + struct regmap *regmap; struct mutex update_lock; u8 config; u8 oldconfig; - bool valid; /* true if registers valid */ - unsigned long last_updated; /* In jiffies */ - s16 temp[4]; /* Register values, - 0 = input - 1 = high - 2 = low - 3 = critical */ - u8 hyst; /* hysteresis offset */ + bool valid; /* true if temperature valid */ }; -static int adt7x10_read_byte(struct device *dev, u8 reg) -{ - struct adt7x10_data *d = dev_get_drvdata(dev); - return d->ops->read_byte(dev, reg); -} - -static int adt7x10_write_byte(struct device *dev, u8 reg, u8 data) -{ - struct adt7x10_data *d = dev_get_drvdata(dev); - return d->ops->write_byte(dev, reg, data); -} - -static int adt7x10_read_word(struct device *dev, u8 reg) -{ - struct adt7x10_data *d = dev_get_drvdata(dev); - return d->ops->read_word(dev, reg); -} - -static int adt7x10_write_word(struct device *dev, u8 reg, u16 data) -{ - struct adt7x10_data *d = dev_get_drvdata(dev); - return d->ops->write_word(dev, reg, data); -} +enum { + adt7x10_temperature = 0, + adt7x10_t_alarm_high, + adt7x10_t_alarm_low, + adt7x10_t_crit, +}; -static const u8 ADT7X10_REG_TEMP[4] = { - ADT7X10_TEMPERATURE, /* input */ - ADT7X10_T_ALARM_HIGH, /* high */ - ADT7X10_T_ALARM_LOW, /* low */ - ADT7X10_T_CRIT, /* critical */ +static const u8 ADT7X10_REG_TEMP[] = { + [adt7x10_temperature] = ADT7X10_TEMPERATURE, /* input */ + [adt7x10_t_alarm_high] = ADT7X10_T_ALARM_HIGH, /* high */ + [adt7x10_t_alarm_low] = ADT7X10_T_ALARM_LOW, /* low */ + [adt7x10_t_crit] = ADT7X10_T_CRIT, /* critical */ }; static irqreturn_t adt7x10_irq_handler(int irq, void *private) { struct device *dev = private; - int status; + struct adt7x10_data *d = dev_get_drvdata(dev); + unsigned int status; + int ret; - status = adt7x10_read_byte(dev, ADT7X10_STATUS); - if (status < 0) + ret = regmap_read(d->regmap, ADT7X10_STATUS, &status); + if (ret < 0) return IRQ_HANDLED; if (status & ADT7X10_STAT_T_HIGH) - sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm"); + hwmon_notify_event(dev, hwmon_temp, hwmon_temp_max_alarm, 0); if (status & ADT7X10_STAT_T_LOW) - sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm"); + hwmon_notify_event(dev, hwmon_temp, hwmon_temp_min_alarm, 0); if (status & ADT7X10_STAT_T_CRIT) - sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm"); + hwmon_notify_event(dev, hwmon_temp, hwmon_temp_crit_alarm, 0); return IRQ_HANDLED; } -static int adt7x10_temp_ready(struct device *dev) +static int adt7x10_temp_ready(struct regmap *regmap) { - int i, status; + unsigned int status; + int i, ret; for (i = 0; i < 6; i++) { - status = adt7x10_read_byte(dev, ADT7X10_STATUS); - if (status < 0) - return status; + ret = regmap_read(regmap, ADT7X10_STATUS, &status); + if (ret < 0) + return ret; if (!(status & ADT7X10_STAT_NOT_RDY)) return 0; msleep(60); @@ -134,71 +112,10 @@ static int adt7x10_temp_ready(struct device *dev) return -ETIMEDOUT; } -static int adt7x10_update_temp(struct device *dev) -{ - struct adt7x10_data *data = dev_get_drvdata(dev); - int ret = 0; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - int temp; - - dev_dbg(dev, "Starting update\n"); - - ret = adt7x10_temp_ready(dev); /* check for new value */ - if (ret) - goto abort; - - temp = adt7x10_read_word(dev, ADT7X10_REG_TEMP[0]); - if (temp < 0) { - ret = temp; - dev_dbg(dev, "Failed to read value: reg %d, error %d\n", - ADT7X10_REG_TEMP[0], ret); - goto abort; - } - data->temp[0] = temp; - data->last_updated = jiffies; - data->valid = true; - } - -abort: - mutex_unlock(&data->update_lock); - return ret; -} - -static int adt7x10_fill_cache(struct device *dev) -{ - struct adt7x10_data *data = dev_get_drvdata(dev); - int ret; - int i; - - for (i = 1; i < ARRAY_SIZE(data->temp); i++) { - ret = adt7x10_read_word(dev, ADT7X10_REG_TEMP[i]); - if (ret < 0) { - dev_dbg(dev, "Failed to read value: reg %d, error %d\n", - ADT7X10_REG_TEMP[i], ret); - return ret; - } - data->temp[i] = ret; - } - - ret = adt7x10_read_byte(dev, ADT7X10_T_HYST); - if (ret < 0) { - dev_dbg(dev, "Failed to read value: reg %d, error %d\n", - ADT7X10_T_HYST, ret); - return ret; - } - data->hyst = ret; - - return 0; -} - static s16 ADT7X10_TEMP_TO_REG(long temp) { return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN, - ADT7X10_TEMP_MAX) * 128, 1000); + ADT7X10_TEMP_MAX) * 128, 1000); } static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg) @@ -215,170 +132,233 @@ static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg) /*-----------------------------------------------------------------------*/ -/* sysfs attributes for hwmon */ - -static ssize_t adt7x10_temp_show(struct device *dev, - struct device_attribute *da, char *buf) +static int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct adt7x10_data *data = dev_get_drvdata(dev); - - - if (attr->index == 0) { - int ret; + unsigned int regval; + int ret; - ret = adt7x10_update_temp(dev); - if (ret) + mutex_lock(&data->update_lock); + if (index == adt7x10_temperature && !data->valid) { + /* wait for valid temperature */ + ret = adt7x10_temp_ready(data->regmap); + if (ret) { + mutex_unlock(&data->update_lock); return ret; + } + data->valid = true; } + mutex_unlock(&data->update_lock); - return sprintf(buf, "%d\n", ADT7X10_REG_TO_TEMP(data, - data->temp[attr->index])); + ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], ®val); + if (ret) + return ret; + + *val = ADT7X10_REG_TO_TEMP(data, regval); + return 0; } -static ssize_t adt7x10_temp_store(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) +static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct adt7x10_data *data = dev_get_drvdata(dev); - int nr = attr->index; - long temp; int ret; - ret = kstrtol(buf, 10, &temp); - if (ret) - return ret; - mutex_lock(&data->update_lock); - data->temp[nr] = ADT7X10_TEMP_TO_REG(temp); - ret = adt7x10_write_word(dev, ADT7X10_REG_TEMP[nr], data->temp[nr]); - if (ret) - count = ret; + ret = regmap_write(data->regmap, ADT7X10_REG_TEMP[index], + ADT7X10_TEMP_TO_REG(temp)); mutex_unlock(&data->update_lock); - return count; + return ret; } -static ssize_t adt7x10_t_hyst_show(struct device *dev, - struct device_attribute *da, char *buf) +static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct adt7x10_data *data = dev_get_drvdata(dev); - int nr = attr->index; - int hyst; + int hyst, temp, ret; + + mutex_lock(&data->update_lock); + ret = regmap_read(data->regmap, ADT7X10_T_HYST, &hyst); + if (ret) { + mutex_unlock(&data->update_lock); + return ret; + } + + ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &temp); + mutex_unlock(&data->update_lock); + if (ret) + return ret; - hyst = (data->hyst & ADT7X10_T_HYST_MASK) * 1000; + hyst = (hyst & ADT7X10_T_HYST_MASK) * 1000; /* * hysteresis is stored as a 4 bit offset in the device, convert it * to an absolute value */ - if (nr == 2) /* min has positive offset, others have negative */ + /* min has positive offset, others have negative */ + if (index == adt7x10_t_alarm_low) hyst = -hyst; - return sprintf(buf, "%d\n", - ADT7X10_REG_TO_TEMP(data, data->temp[nr]) - hyst); + + *val = ADT7X10_REG_TO_TEMP(data, temp) - hyst; + return 0; } -static ssize_t adt7x10_t_hyst_store(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) +static int adt7x10_hyst_write(struct adt7x10_data *data, long hyst) { - struct adt7x10_data *data = dev_get_drvdata(dev); + unsigned int regval; int limit, ret; - long hyst; - ret = kstrtol(buf, 10, &hyst); - if (ret) - return ret; + mutex_lock(&data->update_lock); + /* convert absolute hysteresis value to a 4 bit delta value */ - limit = ADT7X10_REG_TO_TEMP(data, data->temp[1]); - hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX); - data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), - 0, ADT7X10_T_HYST_MASK); - ret = adt7x10_write_byte(dev, ADT7X10_T_HYST, data->hyst); - if (ret) - return ret; + ret = regmap_read(data->regmap, ADT7X10_T_ALARM_HIGH, ®val); + if (ret < 0) + goto abort; - return count; + limit = ADT7X10_REG_TO_TEMP(data, regval); + + hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX); + regval = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0, + ADT7X10_T_HYST_MASK); + ret = regmap_write(data->regmap, ADT7X10_T_HYST, regval); +abort: + mutex_unlock(&data->update_lock); + return ret; } -static ssize_t adt7x10_alarm_show(struct device *dev, - struct device_attribute *da, char *buf) +static int adt7x10_alarm_read(struct adt7x10_data *data, int index, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + unsigned int status; int ret; - ret = adt7x10_read_byte(dev, ADT7X10_STATUS); + ret = regmap_read(data->regmap, ADT7X10_STATUS, &status); if (ret < 0) return ret; - return sprintf(buf, "%d\n", !!(ret & attr->index)); + *val = !!(status & index); + + return 0; } -static ssize_t name_show(struct device *dev, struct device_attribute *da, - char *buf) +static umode_t adt7x10_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (attr) { + case hwmon_temp_max: + case hwmon_temp_min: + case hwmon_temp_crit: + case hwmon_temp_max_hyst: + return 0644; + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_min_hyst: + case hwmon_temp_crit_hyst: + return 0444; + default: + break; + } + + return 0; +} + +static int adt7x10_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { struct adt7x10_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->name); + switch (attr) { + case hwmon_temp_input: + return adt7x10_temp_read(data, adt7x10_temperature, val); + case hwmon_temp_max: + return adt7x10_temp_read(data, adt7x10_t_alarm_high, val); + case hwmon_temp_min: + return adt7x10_temp_read(data, adt7x10_t_alarm_low, val); + case hwmon_temp_crit: + return adt7x10_temp_read(data, adt7x10_t_crit, val); + case hwmon_temp_max_hyst: + return adt7x10_hyst_read(data, adt7x10_t_alarm_high, val); + case hwmon_temp_min_hyst: + return adt7x10_hyst_read(data, adt7x10_t_alarm_low, val); + case hwmon_temp_crit_hyst: + return adt7x10_hyst_read(data, adt7x10_t_crit, val); + case hwmon_temp_min_alarm: + return adt7x10_alarm_read(data, ADT7X10_STAT_T_LOW, val); + case hwmon_temp_max_alarm: + return adt7x10_alarm_read(data, ADT7X10_STAT_T_HIGH, val); + case hwmon_temp_crit_alarm: + return adt7x10_alarm_read(data, ADT7X10_STAT_T_CRIT, val); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR_RO(temp1_input, adt7x10_temp, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max, adt7x10_temp, 1); -static SENSOR_DEVICE_ATTR_RW(temp1_min, adt7x10_temp, 2); -static SENSOR_DEVICE_ATTR_RW(temp1_crit, adt7x10_temp, 3); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, adt7x10_t_hyst, 1); -static SENSOR_DEVICE_ATTR_RO(temp1_min_hyst, adt7x10_t_hyst, 2); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_hyst, adt7x10_t_hyst, 3); -static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, adt7x10_alarm, - ADT7X10_STAT_T_LOW); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, adt7x10_alarm, - ADT7X10_STAT_T_HIGH); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, adt7x10_alarm, - ADT7X10_STAT_T_CRIT); -static DEVICE_ATTR_RO(name); - -static struct attribute *adt7x10_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - NULL +static int adt7x10_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_max: + return adt7x10_temp_write(data, adt7x10_t_alarm_high, val); + case hwmon_temp_min: + return adt7x10_temp_write(data, adt7x10_t_alarm_low, val); + case hwmon_temp_crit: + return adt7x10_temp_write(data, adt7x10_t_crit, val); + case hwmon_temp_max_hyst: + return adt7x10_hyst_write(data, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *adt7x10_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | + HWMON_T_CRIT | HWMON_T_MAX_HYST | HWMON_T_MIN_HYST | + HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM), + NULL, }; -static const struct attribute_group adt7x10_group = { - .attrs = adt7x10_attributes, +static const struct hwmon_ops adt7x10_hwmon_ops = { + .is_visible = adt7x10_is_visible, + .read = adt7x10_read, + .write = adt7x10_write, }; +static const struct hwmon_chip_info adt7x10_chip_info = { + .ops = &adt7x10_hwmon_ops, + .info = adt7x10_info, +}; + +static void adt7x10_restore_config(void *private) +{ + struct adt7x10_data *data = private; + + regmap_write(data->regmap, ADT7X10_CONFIG, data->oldconfig); +} + int adt7x10_probe(struct device *dev, const char *name, int irq, - const struct adt7x10_ops *ops) + struct regmap *regmap) { struct adt7x10_data *data; + unsigned int config; + struct device *hdev; int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->ops = ops; - data->name = name; + data->regmap = regmap; dev_set_drvdata(dev, data); mutex_init(&data->update_lock); /* configure as specified */ - ret = adt7x10_read_byte(dev, ADT7X10_CONFIG); + ret = regmap_read(regmap, ADT7X10_CONFIG, &config); if (ret < 0) { dev_dbg(dev, "Can't read config? %d\n", ret); return ret; } - data->oldconfig = ret; + data->oldconfig = config; /* * Set to 16 bit resolution, continous conversion and comparator mode. @@ -389,99 +369,50 @@ int adt7x10_probe(struct device *dev, const char *name, int irq, data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE; if (data->config != data->oldconfig) { - ret = adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config); + ret = regmap_write(regmap, ADT7X10_CONFIG, data->config); if (ret) return ret; - } - dev_dbg(dev, "Config %02x\n", data->config); - - ret = adt7x10_fill_cache(dev); - if (ret) - goto exit_restore; - - /* Register sysfs hooks */ - ret = sysfs_create_group(&dev->kobj, &adt7x10_group); - if (ret) - goto exit_restore; - - /* - * The I2C device will already have it's own 'name' attribute, but for - * the SPI device we need to register it. name will only be non NULL if - * the device doesn't register the 'name' attribute on its own. - */ - if (name) { - ret = device_create_file(dev, &dev_attr_name); + ret = devm_add_action_or_reset(dev, adt7x10_restore_config, data); if (ret) - goto exit_remove; + return ret; } + dev_dbg(dev, "Config %02x\n", data->config); - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto exit_remove_name; - } + hdev = devm_hwmon_device_register_with_info(dev, name, data, + &adt7x10_chip_info, NULL); + if (IS_ERR(hdev)) + return PTR_ERR(hdev); if (irq > 0) { - ret = request_threaded_irq(irq, NULL, adt7x10_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), dev); + ret = devm_request_threaded_irq(dev, irq, NULL, + adt7x10_irq_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(dev), hdev); if (ret) - goto exit_hwmon_device_unregister; + return ret; } return 0; - -exit_hwmon_device_unregister: - hwmon_device_unregister(data->hwmon_dev); -exit_remove_name: - if (name) - device_remove_file(dev, &dev_attr_name); -exit_remove: - sysfs_remove_group(&dev->kobj, &adt7x10_group); -exit_restore: - adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig); - return ret; } EXPORT_SYMBOL_GPL(adt7x10_probe); -int adt7x10_remove(struct device *dev, int irq) -{ - struct adt7x10_data *data = dev_get_drvdata(dev); - - if (irq > 0) - free_irq(irq, dev); - - hwmon_device_unregister(data->hwmon_dev); - if (data->name) - device_remove_file(dev, &dev_attr_name); - sysfs_remove_group(&dev->kobj, &adt7x10_group); - if (data->oldconfig != data->config) - adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig); - return 0; -} -EXPORT_SYMBOL_GPL(adt7x10_remove); - -#ifdef CONFIG_PM_SLEEP - static int adt7x10_suspend(struct device *dev) { struct adt7x10_data *data = dev_get_drvdata(dev); - return adt7x10_write_byte(dev, ADT7X10_CONFIG, - data->config | ADT7X10_PD); + return regmap_write(data->regmap, ADT7X10_CONFIG, + data->config | ADT7X10_PD); } static int adt7x10_resume(struct device *dev) { struct adt7x10_data *data = dev_get_drvdata(dev); - return adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config); + return regmap_write(data->regmap, ADT7X10_CONFIG, data->config); } -SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume); -EXPORT_SYMBOL_GPL(adt7x10_dev_pm_ops); - -#endif /* CONFIG_PM_SLEEP */ +EXPORT_SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume); MODULE_AUTHOR("Hartmut Knaack"); MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code"); diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h index 21ad15ce3163..46caf3e21978 100644 --- a/drivers/hwmon/adt7x10.h +++ b/drivers/hwmon/adt7x10.h @@ -17,22 +17,9 @@ struct device; -struct adt7x10_ops { - int (*read_byte)(struct device *, u8 reg); - int (*write_byte)(struct device *, u8 reg, u8 data); - int (*read_word)(struct device *, u8 reg); - int (*write_word)(struct device *, u8 reg, u16 data); -}; - int adt7x10_probe(struct device *dev, const char *name, int irq, - const struct adt7x10_ops *ops); -int adt7x10_remove(struct device *dev, int irq); + struct regmap *regmap); -#ifdef CONFIG_PM_SLEEP extern const struct dev_pm_ops adt7x10_dev_pm_ops; -#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops) -#else -#define ADT7X10_DEV_PM_OPS NULL -#endif #endif diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c new file mode 100644 index 000000000000..2d9770cb4401 --- /dev/null +++ b/drivers/hwmon/aht10.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * aht10.c - Linux hwmon driver for AHT10 Temperature and Humidity sensor + * Copyright (C) 2020 Johannes Cornelis Draaijer + */ + +#include <linux/delay.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/ktime.h> +#include <linux/module.h> + +#define AHT10_MEAS_SIZE 6 + +/* + * Poll intervals (in milliseconds) + */ +#define AHT10_DEFAULT_MIN_POLL_INTERVAL 2000 +#define AHT10_MIN_POLL_INTERVAL 2000 + +/* + * I2C command delays (in microseconds) + */ +#define AHT10_MEAS_DELAY 80000 +#define AHT10_CMD_DELAY 350000 +#define AHT10_DELAY_EXTRA 100000 + +/* + * Command bytes + */ +#define AHT10_CMD_INIT 0b11100001 +#define AHT10_CMD_MEAS 0b10101100 +#define AHT10_CMD_RST 0b10111010 + +/* + * Flags in the answer byte/command + */ +#define AHT10_CAL_ENABLED BIT(3) +#define AHT10_BUSY BIT(7) +#define AHT10_MODE_NOR (BIT(5) | BIT(6)) +#define AHT10_MODE_CYC BIT(5) +#define AHT10_MODE_CMD BIT(6) + +#define AHT10_MAX_POLL_INTERVAL_LEN 30 + +/** + * struct aht10_data - All the data required to operate an AHT10 chip + * @client: the i2c client associated with the AHT10 + * @lock: a mutex that is used to prevent parallel access to the + * i2c client + * @min_poll_interval: the minimum poll interval + * While the poll rate limit is not 100% necessary, + * the datasheet recommends that a measurement + * is not performed too often to prevent + * the chip from warming up due to the heat it generates. + * If it's unwanted, it can be ignored setting it to + * it to 0. Default value is 2000 ms + * @previous_poll_time: the previous time that the AHT10 + * was polled + * @temperature: the latest temperature value received from + * the AHT10 + * @humidity: the latest humidity value received from the + * AHT10 + */ + +struct aht10_data { + struct i2c_client *client; + /* + * Prevent simultaneous access to the i2c + * client and previous_poll_time + */ + struct mutex lock; + ktime_t min_poll_interval; + ktime_t previous_poll_time; + int temperature; + int humidity; +}; + +/** + * aht10_init() - Initialize an AHT10 chip + * @client: the i2c client associated with the AHT10 + * @data: the data associated with this AHT10 chip + * Return: 0 if succesfull, 1 if not + */ +static int aht10_init(struct aht10_data *data) +{ + const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC, + 0x00}; + int res; + u8 status; + struct i2c_client *client = data->client; + + res = i2c_master_send(client, cmd_init, 3); + if (res < 0) + return res; + + usleep_range(AHT10_CMD_DELAY, AHT10_CMD_DELAY + + AHT10_DELAY_EXTRA); + + res = i2c_master_recv(client, &status, 1); + if (res != 1) + return -ENODATA; + + if (status & AHT10_BUSY) + return -EBUSY; + + return 0; +} + +/** + * aht10_polltime_expired() - check if the minimum poll interval has + * expired + * @data: the data containing the time to compare + * Return: 1 if the minimum poll interval has expired, 0 if not + */ +static int aht10_polltime_expired(struct aht10_data *data) +{ + ktime_t current_time = ktime_get_boottime(); + ktime_t difference = ktime_sub(current_time, data->previous_poll_time); + + return ktime_after(difference, data->min_poll_interval); +} + +/** + * aht10_read_values() - read and parse the raw data from the AHT10 + * @aht10_data: the struct aht10_data to use for the lock + * Return: 0 if succesfull, 1 if not + */ +static int aht10_read_values(struct aht10_data *data) +{ + const u8 cmd_meas[] = {AHT10_CMD_MEAS, 0x33, 0x00}; + u32 temp, hum; + int res; + u8 raw_data[AHT10_MEAS_SIZE]; + struct i2c_client *client = data->client; + + mutex_lock(&data->lock); + if (aht10_polltime_expired(data)) { + res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas)); + if (res < 0) { + mutex_unlock(&data->lock); + return res; + } + + usleep_range(AHT10_MEAS_DELAY, + AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA); + + res = i2c_master_recv(client, raw_data, AHT10_MEAS_SIZE); + if (res != AHT10_MEAS_SIZE) { + mutex_unlock(&data->lock); + if (res >= 0) + return -ENODATA; + else + return res; + } + + hum = ((u32)raw_data[1] << 12u) | + ((u32)raw_data[2] << 4u) | + ((raw_data[3] & 0xF0u) >> 4u); + + temp = ((u32)(raw_data[3] & 0x0Fu) << 16u) | + ((u32)raw_data[4] << 8u) | + raw_data[5]; + + temp = ((temp * 625) >> 15u) * 10; + hum = ((hum * 625) >> 16u) * 10; + + data->temperature = (int)temp - 50000; + data->humidity = hum; + data->previous_poll_time = ktime_get_boottime(); + } + mutex_unlock(&data->lock); + return 0; +} + +/** + * aht10_interval_write() - store the given minimum poll interval. + * Return: 0 on success, -EINVAL if a value lower than the + * AHT10_MIN_POLL_INTERVAL is given + */ +static ssize_t aht10_interval_write(struct aht10_data *data, + long val) +{ + data->min_poll_interval = ms_to_ktime(clamp_val(val, 2000, LONG_MAX)); + return 0; +} + +/** + * aht10_interval_read() - read the minimum poll interval + * in milliseconds + */ +static ssize_t aht10_interval_read(struct aht10_data *data, + long *val) +{ + *val = ktime_to_ms(data->min_poll_interval); + return 0; +} + +/** + * aht10_temperature1_read() - read the temperature in millidegrees + */ +static int aht10_temperature1_read(struct aht10_data *data, long *val) +{ + int res; + + res = aht10_read_values(data); + if (res < 0) + return res; + + *val = data->temperature; + return 0; +} + +/** + * aht10_humidity1_read() - read the relative humidity in millipercent + */ +static int aht10_humidity1_read(struct aht10_data *data, long *val) +{ + int res; + + res = aht10_read_values(data); + if (res < 0) + return res; + + *val = data->humidity; + return 0; +} + +static umode_t aht10_hwmon_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + case hwmon_humidity: + return 0444; + case hwmon_chip: + return 0644; + default: + return 0; + } +} + +static int aht10_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct aht10_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + return aht10_temperature1_read(data, val); + case hwmon_humidity: + return aht10_humidity1_read(data, val); + case hwmon_chip: + return aht10_interval_read(data, val); + default: + return -EOPNOTSUPP; + } +} + +static int aht10_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct aht10_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return aht10_interval_write(data, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *aht10_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), + NULL, +}; + +static const struct hwmon_ops aht10_hwmon_ops = { + .is_visible = aht10_hwmon_visible, + .read = aht10_hwmon_read, + .write = aht10_hwmon_write, +}; + +static const struct hwmon_chip_info aht10_chip_info = { + .ops = &aht10_hwmon_ops, + .info = aht10_info, +}; + +static int aht10_probe(struct i2c_client *client, + const struct i2c_device_id *aht10_id) +{ + struct device *device = &client->dev; + struct device *hwmon_dev; + struct aht10_data *data; + int res; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENOENT; + + data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->min_poll_interval = ms_to_ktime(AHT10_DEFAULT_MIN_POLL_INTERVAL); + data->client = client; + + mutex_init(&data->lock); + + res = aht10_init(data); + if (res < 0) + return res; + + res = aht10_read_values(data); + if (res < 0) + return res; + + hwmon_dev = devm_hwmon_device_register_with_info(device, + client->name, + data, + &aht10_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id aht10_id[] = { + { "aht10", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, aht10_id); + +static struct i2c_driver aht10_driver = { + .driver = { + .name = "aht10", + }, + .probe = aht10_probe, + .id_table = aht10_id, +}; + +module_i2c_driver(aht10_driver); + +MODULE_AUTHOR("Johannes Cornelis Draaijer <jcdra1@gmail.com>"); +MODULE_DESCRIPTION("AHT10 Temperature and Humidity sensor driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 013fb056b1d0..3bfd12ff4b3c 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -141,7 +141,7 @@ static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI, struct amc6821_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* register values */ @@ -258,7 +258,7 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); return data; @@ -511,7 +511,7 @@ static ssize_t temp_auto_point_temp_store(struct device *dev, } mutex_lock(&data->update_lock); - data->valid = 0; + data->valid = false; switch (ix) { case 0: @@ -584,7 +584,7 @@ static ssize_t pwm1_auto_point_pwm_store(struct device *dev, } EXIT: - data->valid = 0; + data->valid = false; mutex_unlock(&data->update_lock); return count; } @@ -809,7 +809,7 @@ static int amc6821_detect( } dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address); - strlcpy(info->type, "amc6821", I2C_NAME_SIZE); + strscpy(info->type, "amc6821", I2C_NAME_SIZE); return 0; } @@ -900,8 +900,7 @@ static int amc6821_init_client(struct i2c_client *client) return 0; } -static int amc6821_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int amc6821_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct amc6821_data *data; @@ -940,7 +939,7 @@ static struct i2c_driver amc6821_driver = { .driver = { .name = "amc6821", }, - .probe = amc6821_probe, + .probe_new = amc6821_probe, .id_table = amc6821_id, .detect = amc6821_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index ec93b8d673f5..fc6d6a9053ce 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -32,6 +32,7 @@ #include <linux/hwmon.h> #include <linux/workqueue.h> #include <linux/err.h> +#include <linux/bits.h> /* data port used by Apple SMC */ #define APPLESMC_DATA_PORT 0x300 @@ -42,10 +43,13 @@ #define APPLESMC_MAX_DATA_LENGTH 32 -/* wait up to 128 ms for a status change. */ -#define APPLESMC_MIN_WAIT 0x0010 -#define APPLESMC_RETRY_WAIT 0x0100 -#define APPLESMC_MAX_WAIT 0x20000 +/* Apple SMC status bits */ +#define SMC_STATUS_AWAITING_DATA BIT(0) /* SMC has data waiting to be read */ +#define SMC_STATUS_IB_CLOSED BIT(1) /* Will ignore any input */ +#define SMC_STATUS_BUSY BIT(2) /* Command in progress */ + +/* Initial wait is 8us */ +#define APPLESMC_MIN_WAIT 0x0008 #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 @@ -151,59 +155,84 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * wait_read - Wait for a byte to appear on SMC port. Callers must - * hold applesmc_lock. + * Wait for specific status bits with a mask on the SMC. + * Used before all transactions. + * This does 10 fast loops of 8us then exponentially backs off for a + * minimum total wait of 262ms. Depending on usleep_range this could + * run out past 500ms. */ -static int wait_read(void) + +static int wait_status(u8 val, u8 mask) { u8 status; int us; - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { - udelay(us); + int i; + + us = APPLESMC_MIN_WAIT; + for (i = 0; i < 24 ; i++) { status = inb(APPLESMC_CMD_PORT); - /* read: wait for smc to settle */ - if (status & 0x01) + if ((status & mask) == val) return 0; + usleep_range(us, us * 2); + if (i > 9) + us <<= 1; } - - pr_warn("wait_read() fail: 0x%02x\n", status); return -EIO; } -/* - * send_byte - Write to SMC port, retrying when necessary. Callers - * must hold applesmc_lock. - */ +/* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */ + static int send_byte(u8 cmd, u16 port) { - u8 status; - int us; + int status; + + status = wait_status(0, SMC_STATUS_IB_CLOSED); + if (status) + return status; + /* + * This needs to be a separate read looking for bit 0x04 + * after bit 0x02 falls. If consolidated with the wait above + * this extra read may not happen if status returns both + * simultaneously and this would appear to be required. + */ + status = wait_status(SMC_STATUS_BUSY, SMC_STATUS_BUSY); + if (status) + return status; outb(cmd, port); - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { - udelay(us); - status = inb(APPLESMC_CMD_PORT); - /* write: wait for smc to settle */ - if (status & 0x02) - continue; - /* ready: cmd accepted, return */ - if (status & 0x04) - return 0; - /* timeout: give up */ - if (us << 1 == APPLESMC_MAX_WAIT) - break; - /* busy: long wait and resend */ - udelay(APPLESMC_RETRY_WAIT); - outb(cmd, port); - } - - pr_warn("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n", cmd, port, status); - return -EIO; + return 0; } +/* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */ + static int send_command(u8 cmd) { - return send_byte(cmd, APPLESMC_CMD_PORT); + int ret; + + ret = wait_status(0, SMC_STATUS_IB_CLOSED); + if (ret) + return ret; + outb(cmd, APPLESMC_CMD_PORT); + return 0; +} + +/* + * Based on logic from the Apple driver. This is issued before any interaction + * If busy is stuck high, issue a read command to reset the SMC state machine. + * If busy is stuck high after the command then the SMC is jammed. + */ + +static int smc_sane(void) +{ + int ret; + + ret = wait_status(0, SMC_STATUS_BUSY); + if (!ret) + return ret; + ret = send_command(APPLESMC_READ_CMD); + if (ret) + return ret; + return wait_status(0, SMC_STATUS_BUSY); } static int send_argument(const char *key) @@ -220,6 +249,11 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) { u8 status, data = 0; int i; + int ret; + + ret = smc_sane(); + if (ret) + return ret; if (send_command(cmd) || send_argument(key)) { pr_warn("%.4s: read arg fail\n", key); @@ -233,7 +267,8 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) } for (i = 0; i < len; i++) { - if (wait_read()) { + if (wait_status(SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY, + SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY)) { pr_warn("%.4s: read data[%d] fail\n", key, i); return -EIO; } @@ -244,19 +279,24 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) for (i = 0; i < 16; i++) { udelay(APPLESMC_MIN_WAIT); status = inb(APPLESMC_CMD_PORT); - if (!(status & 0x01)) + if (!(status & SMC_STATUS_AWAITING_DATA)) break; data = inb(APPLESMC_DATA_PORT); } if (i) pr_warn("flushed %d bytes, last value is: %d\n", i, data); - return 0; + return wait_status(0, SMC_STATUS_BUSY); } static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) { int i; + int ret; + + ret = smc_sane(); + if (ret) + return ret; if (send_command(cmd) || send_argument(key)) { pr_warn("%s: write arg fail\n", key); @@ -275,7 +315,7 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) } } - return 0; + return wait_status(0, SMC_STATUS_BUSY); } static int read_register_count(unsigned int *count) @@ -351,7 +391,7 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) cache->len = info[0]; memcpy(cache->type, &info[1], 4); cache->flags = info[5]; - cache->valid = 1; + cache->valid = true; out: mutex_unlock(&smcreg.mutex); @@ -525,7 +565,7 @@ static int applesmc_init_index(struct applesmc_registers *s) static int applesmc_init_smcreg_try(void) { struct applesmc_registers *s = &smcreg; - bool left_light_sensor = 0, right_light_sensor = 0; + bool left_light_sensor = false, right_light_sensor = false; unsigned int count; u8 tmp[1]; int ret; @@ -701,7 +741,7 @@ static void applesmc_idev_poll(struct input_dev *idev) static ssize_t applesmc_name_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "applesmc\n"); + return sysfs_emit(buf, "applesmc\n"); } static ssize_t applesmc_position_show(struct device *dev, @@ -723,8 +763,8 @@ static ssize_t applesmc_position_show(struct device *dev, out: if (ret) return ret; - else - return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z); + + return sysfs_emit(buf, "(%d,%d,%d)\n", x, y, z); } static ssize_t applesmc_light_show(struct device *dev, @@ -747,22 +787,25 @@ static ssize_t applesmc_light_show(struct device *dev, } ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); + if (ret) + goto out; /* newer macbooks report a single 10-bit bigendian value */ if (data_length == 10) { left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2; goto out; } left = buffer[2]; + + ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length); if (ret) goto out; - ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length); right = buffer[2]; out: if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right); + + return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right); } /* Displays sensor key as label */ @@ -771,7 +814,7 @@ static ssize_t applesmc_show_sensor_label(struct device *dev, { const char *key = smcreg.index[to_index(devattr)]; - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key); + return sysfs_emit(sysfsbuf, "%s\n", key); } /* Displays degree Celsius * 1000 */ @@ -789,7 +832,7 @@ static ssize_t applesmc_show_temperature(struct device *dev, temp = 250 * (value >> 6); - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", temp); + return sysfs_emit(sysfsbuf, "%d\n", temp); } static ssize_t applesmc_show_fan_speed(struct device *dev, @@ -804,12 +847,11 @@ static ssize_t applesmc_show_fan_speed(struct device *dev, to_index(attr)); ret = applesmc_read_key(newkey, buffer, 2); - speed = ((buffer[0] << 8 | buffer[1]) >> 2); - if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed); + + speed = ((buffer[0] << 8 | buffer[1]) >> 2); + return sysfs_emit(sysfsbuf, "%u\n", speed); } static ssize_t applesmc_store_fan_speed(struct device *dev, @@ -845,12 +887,11 @@ static ssize_t applesmc_show_fan_manual(struct device *dev, u8 buffer[2]; ret = applesmc_read_key(FANS_MANUAL, buffer, 2); - manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01; - if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual); + + manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01; + return sysfs_emit(sysfsbuf, "%d\n", manual); } static ssize_t applesmc_store_fan_manual(struct device *dev, @@ -866,10 +907,11 @@ static ssize_t applesmc_store_fan_manual(struct device *dev, return -EINVAL; ret = applesmc_read_key(FANS_MANUAL, buffer, 2); - val = (buffer[0] << 8 | buffer[1]); if (ret) goto out; + val = (buffer[0] << 8 | buffer[1]); + if (input) val = val | (0x01 << to_index(attr)); else @@ -901,14 +943,14 @@ static ssize_t applesmc_show_fan_position(struct device *dev, if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4); + + return sysfs_emit(sysfsbuf, "%s\n", buffer + 4); } static ssize_t applesmc_calibrate_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y); + return sysfs_emit(sysfsbuf, "(%d,%d)\n", rest_x, rest_y); } static ssize_t applesmc_calibrate_store(struct device *dev, @@ -945,13 +987,12 @@ static ssize_t applesmc_key_count_show(struct device *dev, u32 count; ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4); - count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) + - ((u32)buffer[2]<<8) + buffer[3]; - if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count); + + count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) + + ((u32)buffer[2]<<8) + buffer[3]; + return sysfs_emit(sysfsbuf, "%d\n", count); } static ssize_t applesmc_key_at_index_read_show(struct device *dev, @@ -979,7 +1020,7 @@ static ssize_t applesmc_key_at_index_data_length_show(struct device *dev, if (IS_ERR(entry)) return PTR_ERR(entry); - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", entry->len); + return sysfs_emit(sysfsbuf, "%d\n", entry->len); } static ssize_t applesmc_key_at_index_type_show(struct device *dev, @@ -991,7 +1032,7 @@ static ssize_t applesmc_key_at_index_type_show(struct device *dev, if (IS_ERR(entry)) return PTR_ERR(entry); - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->type); + return sysfs_emit(sysfsbuf, "%s\n", entry->type); } static ssize_t applesmc_key_at_index_name_show(struct device *dev, @@ -1003,13 +1044,13 @@ static ssize_t applesmc_key_at_index_name_show(struct device *dev, if (IS_ERR(entry)) return PTR_ERR(entry); - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key); + return sysfs_emit(sysfsbuf, "%s\n", entry->key); } static ssize_t applesmc_key_at_index_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index); + return sysfs_emit(sysfsbuf, "%d\n", key_at_index); } static ssize_t applesmc_key_at_index_store(struct device *dev, @@ -1258,6 +1299,10 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "iMac") }, }, + { applesmc_dmi_match, "Apple Xserve", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "Xserve") }, + }, { .ident = NULL } }; diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c new file mode 100644 index 000000000000..c51a2678f0eb --- /dev/null +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -0,0 +1,1025 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, + * Quadro, High Flow Next) + * + * Aquacomputer devices send HID reports (with ID 0x01) every second to report + * sensor values. + * + * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com> + * Copyright 2022 Jack Doan <me@jackdoan.com> + */ + +#include <linux/crc16.h> +#include <linux/debugfs.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/seq_file.h> +#include <asm/unaligned.h> + +#define USB_VENDOR_ID_AQUACOMPUTER 0x0c70 +#define USB_PRODUCT_ID_FARBWERK 0xf00a +#define USB_PRODUCT_ID_QUADRO 0xf00d +#define USB_PRODUCT_ID_D5NEXT 0xf00e +#define USB_PRODUCT_ID_FARBWERK360 0xf010 +#define USB_PRODUCT_ID_OCTO 0xf011 +#define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 + +enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext }; + +static const char *const aqc_device_names[] = { + [d5next] = "d5next", + [farbwerk] = "farbwerk", + [farbwerk360] = "farbwerk360", + [octo] = "octo", + [quadro] = "quadro", + [highflownext] = "highflownext" +}; + +#define DRIVER_NAME "aquacomputer_d5next" + +#define STATUS_REPORT_ID 0x01 +#define STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */ +#define SERIAL_FIRST_PART 3 +#define SERIAL_SECOND_PART 5 +#define FIRMWARE_VERSION 13 + +#define CTRL_REPORT_ID 0x03 + +/* The HID report that the official software always sends + * after writing values, currently same for all devices + */ +#define SECONDARY_CTRL_REPORT_ID 0x02 +#define SECONDARY_CTRL_REPORT_SIZE 0x0B + +static u8 secondary_ctrl_report[] = { + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6 +}; + +/* Register offsets for all Aquacomputer devices */ +#define AQC_TEMP_SENSOR_SIZE 0x02 +#define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF +#define AQC_FAN_PERCENT_OFFSET 0x00 +#define AQC_FAN_VOLTAGE_OFFSET 0x02 +#define AQC_FAN_CURRENT_OFFSET 0x04 +#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 +#define D5NEXT_NUM_FANS 2 +#define D5NEXT_NUM_SENSORS 1 +#define D5NEXT_NUM_VIRTUAL_SENSORS 8 +#define D5NEXT_VIRTUAL_SENSORS_START 0x3f +#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 +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 }; + +/* Register offsets for the Farbwerk RGB controller */ +#define FARBWERK_NUM_SENSORS 4 +#define FARBWERK_SENSOR_START 0x2f + +/* Register offsets for the Farbwerk 360 RGB controller */ +#define FARBWERK360_NUM_SENSORS 4 +#define FARBWERK360_SENSOR_START 0x32 +#define FARBWERK360_NUM_VIRTUAL_SENSORS 16 +#define FARBWERK360_VIRTUAL_SENSORS_START 0x3a + +/* Register offsets for the Octo fan controller */ +#define OCTO_POWER_CYCLES 0x18 +#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 +static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; + +/* Fan speed registers in Octo control report (from 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 +#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 +#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 }; + +/* Register offsets for the High Flow Next */ +#define HIGHFLOWNEXT_NUM_SENSORS 2 +#define HIGHFLOWNEXT_SENSOR_START 85 +#define HIGHFLOWNEXT_FLOW 81 +#define HIGHFLOWNEXT_WATER_QUALITY 89 +#define HIGHFLOWNEXT_POWER 91 +#define HIGHFLOWNEXT_CONDUCTIVITY 95 +#define HIGHFLOWNEXT_5V_VOLTAGE 97 +#define HIGHFLOWNEXT_5V_VOLTAGE_USB 99 + +/* Labels for D5 Next */ +static const char *const label_d5next_temp[] = { + "Coolant temp" +}; + +static const char *const label_d5next_speeds[] = { + "Pump speed", + "Fan speed" +}; + +static const char *const label_d5next_power[] = { + "Pump power", + "Fan power" +}; + +static const char *const label_d5next_voltages[] = { + "Pump voltage", + "Fan voltage", + "+5V voltage", + "+12V voltage" +}; + +static const char *const label_d5next_current[] = { + "Pump current", + "Fan current" +}; + +/* Labels for Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */ +static const char *const label_temp_sensors[] = { + "Sensor 1", + "Sensor 2", + "Sensor 3", + "Sensor 4" +}; + +static const char *const label_virtual_temp_sensors[] = { + "Virtual sensor 1", + "Virtual sensor 2", + "Virtual sensor 3", + "Virtual sensor 4", + "Virtual sensor 5", + "Virtual sensor 6", + "Virtual sensor 7", + "Virtual sensor 8", + "Virtual sensor 9", + "Virtual sensor 10", + "Virtual sensor 11", + "Virtual sensor 12", + "Virtual sensor 13", + "Virtual sensor 14", + "Virtual sensor 15", + "Virtual sensor 16", +}; + +/* Labels for Octo and Quadro (except speed) */ +static const char *const label_fan_speed[] = { + "Fan 1 speed", + "Fan 2 speed", + "Fan 3 speed", + "Fan 4 speed", + "Fan 5 speed", + "Fan 6 speed", + "Fan 7 speed", + "Fan 8 speed" +}; + +static const char *const label_fan_power[] = { + "Fan 1 power", + "Fan 2 power", + "Fan 3 power", + "Fan 4 power", + "Fan 5 power", + "Fan 6 power", + "Fan 7 power", + "Fan 8 power" +}; + +static const char *const label_fan_voltage[] = { + "Fan 1 voltage", + "Fan 2 voltage", + "Fan 3 voltage", + "Fan 4 voltage", + "Fan 5 voltage", + "Fan 6 voltage", + "Fan 7 voltage", + "Fan 8 voltage" +}; + +static const char *const label_fan_current[] = { + "Fan 1 current", + "Fan 2 current", + "Fan 3 current", + "Fan 4 current", + "Fan 5 current", + "Fan 6 current", + "Fan 7 current", + "Fan 8 current" +}; + +/* Labels for Quadro fan speeds */ +static const char *const label_quadro_speeds[] = { + "Fan 1 speed", + "Fan 2 speed", + "Fan 3 speed", + "Fan 4 speed", + "Flow speed [dL/h]" +}; + +/* Labels for High Flow Next */ +static const char *const label_highflownext_temp_sensors[] = { + "Coolant temp", + "External sensor" +}; + +static const char *const label_highflownext_fan_speed[] = { + "Flow [dL/h]", + "Water quality [%]", + "Conductivity [nS/cm]", +}; + +static const char *const label_highflownext_power[] = { + "Dissipated power", +}; + +static const char *const label_highflownext_voltage[] = { + "+5V voltage", + "+5V USB voltage" +}; + +struct aqc_data { + struct hid_device *hdev; + struct device *hwmon_dev; + struct dentry *debugfs; + struct mutex mutex; /* Used for locking access when reading and writing PWM values */ + enum kinds kind; + const char *name; + + int buffer_size; + u8 *buffer; + int checksum_start; + int checksum_length; + int checksum_offset; + + int num_fans; + u8 *fan_sensor_offsets; + u16 *fan_ctrl_offsets; + int num_temp_sensors; + int temp_sensor_start_offset; + int num_virtual_temp_sensors; + int virtual_temp_sensor_start_offset; + u16 power_cycle_count_offset; + u8 flow_sensor_offset; + + /* General info, same across all devices */ + u32 serial_number[2]; + u16 firmware_version; + + /* How many times the device was powered on, if available */ + u32 power_cycles; + + /* Sensor values */ + s32 temp_input[20]; /* Max 4 physical and 16 virtual */ + u16 speed_input[8]; + u32 power_input[8]; + u16 voltage_input[8]; + u16 current_input[8]; + + /* Label values */ + const char *const *temp_label; + const char *const *virtual_temp_label; + const char *const *speed_label; + const char *const *power_label; + const char *const *voltage_label; + const char *const *current_label; + + unsigned long updated; +}; + +/* Converts from centi-percent */ +static int aqc_percent_to_pwm(u16 val) +{ + return DIV_ROUND_CLOSEST(val * 255, 100 * 100); +} + +/* Converts to centi-percent */ +static int aqc_pwm_to_percent(long val) +{ + if (val < 0 || val > 255) + return -EINVAL; + + return DIV_ROUND_CLOSEST(val * 100 * 100, 255); +} + +/* Expects the mutex to be locked */ +static int aqc_get_ctrl_data(struct aqc_data *priv) +{ + int ret; + + memset(priv->buffer, 0x00, priv->buffer_size); + ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) + ret = -ENODATA; + + return ret; +} + +/* Expects the mutex to be locked */ +static int aqc_send_ctrl_data(struct aqc_data *priv) +{ + int ret; + u16 checksum; + + /* Init and xorout value for CRC-16/USB is 0xffff */ + checksum = crc16(0xffff, priv->buffer + priv->checksum_start, priv->checksum_length); + checksum ^= 0xffff; + + /* Place the new checksum at the end of the report */ + put_unaligned_be16(checksum, priv->buffer + priv->checksum_offset); + + /* Send the patched up report back to the device */ + ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret < 0) + return ret; + + /* The official software sends this report after every change, so do it here as well */ + ret = hid_hw_raw_request(priv->hdev, SECONDARY_CTRL_REPORT_ID, secondary_ctrl_report, + SECONDARY_CTRL_REPORT_SIZE, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); + return ret; +} + +/* Refreshes the control buffer and returns value at offset */ +static int aqc_get_ctrl_val(struct aqc_data *priv, int offset) +{ + int ret; + + mutex_lock(&priv->mutex); + + ret = aqc_get_ctrl_data(priv); + if (ret < 0) + goto unlock_and_return; + + ret = get_unaligned_be16(priv->buffer + offset); + +unlock_and_return: + mutex_unlock(&priv->mutex); + return ret; +} + +static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val) +{ + int ret; + + mutex_lock(&priv->mutex); + + ret = aqc_get_ctrl_data(priv); + if (ret < 0) + goto unlock_and_return; + + put_unaligned_be16((u16)val, priv->buffer + offset); + + ret = aqc_send_ctrl_data(priv); + +unlock_and_return: + mutex_unlock(&priv->mutex); + return ret; +} + +static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) +{ + const struct aqc_data *priv = data; + + switch (type) { + case hwmon_temp: + if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors) + return 0444; + break; + case hwmon_pwm: + if (priv->fan_ctrl_offsets && channel < priv->num_fans) { + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + break; + } + } + break; + case hwmon_fan: + 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 hwmon_power: + switch (priv->kind) { + case highflownext: + /* Special case to support one power sensor */ + if (channel == 0) + return 0444; + break; + default: + if (channel < priv->num_fans) + return 0444; + break; + } + break; + case hwmon_curr: + if (channel < priv->num_fans) + return 0444; + break; + case hwmon_in: + switch (priv->kind) { + case d5next: + /* Special case to support +5V and +12V voltage sensors */ + if (channel < priv->num_fans + 2) + return 0444; + break; + case highflownext: + /* Special case to support two voltage sensors */ + if (channel < 2) + return 0444; + break; + default: + if (channel < priv->num_fans) + return 0444; + break; + } + break; + default: + break; + } + + return 0; +} + +static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + int ret; + struct aqc_data *priv = dev_get_drvdata(dev); + + if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL)) + return -ENODATA; + + switch (type) { + case hwmon_temp: + if (priv->temp_input[channel] == -ENODATA) + return -ENODATA; + + *val = priv->temp_input[channel]; + break; + case hwmon_fan: + *val = priv->speed_input[channel]; + 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]); + if (ret < 0) + return ret; + + *val = aqc_percent_to_pwm(ret); + } + break; + case hwmon_in: + *val = priv->voltage_input[channel]; + break; + case hwmon_curr: + *val = priv->current_input[channel]; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + struct aqc_data *priv = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + if (channel < priv->num_temp_sensors) + *str = priv->temp_label[channel]; + else + *str = priv->virtual_temp_label[channel - priv->num_temp_sensors]; + break; + case hwmon_fan: + *str = priv->speed_label[channel]; + break; + case hwmon_power: + *str = priv->power_label[channel]; + break; + case hwmon_in: + *str = priv->voltage_label[channel]; + break; + case hwmon_curr: + *str = priv->current_label[channel]; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long val) +{ + int ret, pwm_value; + struct aqc_data *priv = dev_get_drvdata(dev); + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + if (priv->fan_ctrl_offsets) { + pwm_value = aqc_pwm_to_percent(val); + if (pwm_value < 0) + return pwm_value; + + ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel], + pwm_value); + if (ret < 0) + return ret; + } + break; + default: + break; + } + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct hwmon_ops aqc_hwmon_ops = { + .is_visible = aqc_is_visible, + .read = aqc_read, + .read_string = aqc_read_string, + .write = aqc_write +}; + +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_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL), + HWMON_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_P_INPUT | HWMON_P_LABEL, + HWMON_P_INPUT | HWMON_P_LABEL, + HWMON_P_INPUT | HWMON_P_LABEL), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + 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_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_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, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + NULL +}; + +static const struct hwmon_chip_info aqc_chip_info = { + .ops = &aqc_hwmon_ops, + .info = aqc_info, +}; + +static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) +{ + int i, j, sensor_value; + struct aqc_data *priv; + + if (report->id != STATUS_REPORT_ID) + return 0; + + priv = hid_get_drvdata(hdev); + + /* Info provided with every report */ + priv->serial_number[0] = get_unaligned_be16(data + SERIAL_FIRST_PART); + priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART); + priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION); + + /* Physical temperature sensor readings */ + for (i = 0; i < priv->num_temp_sensors; i++) { + sensor_value = get_unaligned_be16(data + + priv->temp_sensor_start_offset + + i * AQC_TEMP_SENSOR_SIZE); + if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) + priv->temp_input[i] = -ENODATA; + else + priv->temp_input[i] = sensor_value * 10; + } + + /* Virtual temperature sensor readings */ + for (j = 0; j < priv->num_virtual_temp_sensors; j++) { + sensor_value = get_unaligned_be16(data + + priv->virtual_temp_sensor_start_offset + + j * AQC_TEMP_SENSOR_SIZE); + if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) + priv->temp_input[i] = -ENODATA; + else + priv->temp_input[i] = sensor_value * 10; + i++; + } + + /* Fan speed and related readings */ + for (i = 0; i < priv->num_fans; i++) { + priv->speed_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_SPEED_OFFSET); + priv->power_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + + AQC_FAN_POWER_OFFSET) * 10000; + priv->voltage_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + + AQC_FAN_VOLTAGE_OFFSET) * 10; + priv->current_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_CURRENT_OFFSET); + } + + if (priv->power_cycle_count_offset != 0) + priv->power_cycles = get_unaligned_be32(data + priv->power_cycle_count_offset); + + /* Special-case sensor readings */ + switch (priv->kind) { + case d5next: + priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; + priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; + break; + case quadro: + priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset); + break; + case highflownext: + /* If external temp sensor is not connected, its power reading is also N/A */ + if (priv->temp_input[1] == -ENODATA) + priv->power_input[0] = -ENODATA; + else + priv->power_input[0] = + get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000; + + priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10; + priv->voltage_input[1] = + get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10; + + priv->speed_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_FLOW); + priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY); + priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY); + break; + default: + break; + } + + priv->updated = jiffies; + + return 0; +} + +#ifdef CONFIG_DEBUG_FS + +static int serial_number_show(struct seq_file *seqf, void *unused) +{ + struct aqc_data *priv = seqf->private; + + seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(serial_number); + +static int firmware_version_show(struct seq_file *seqf, void *unused) +{ + struct aqc_data *priv = seqf->private; + + seq_printf(seqf, "%u\n", priv->firmware_version); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(firmware_version); + +static int power_cycles_show(struct seq_file *seqf, void *unused) +{ + struct aqc_data *priv = seqf->private; + + seq_printf(seqf, "%u\n", priv->power_cycles); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(power_cycles); + +static void aqc_debugfs_init(struct aqc_data *priv) +{ + char name[64]; + + scnprintf(name, sizeof(name), "%s_%s-%s", "aquacomputer", priv->name, + dev_name(&priv->hdev->dev)); + + priv->debugfs = debugfs_create_dir(name, NULL); + debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops); + debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops); + + if (priv->power_cycle_count_offset != 0) + debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); +} + +#else + +static void aqc_debugfs_init(struct aqc_data *priv) +{ +} + +#endif + +static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct aqc_data *priv; + int ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hdev = hdev; + hid_set_drvdata(hdev, priv); + + priv->updated = jiffies - STATUS_UPDATE_INTERVAL; + + ret = hid_parse(hdev); + if (ret) + return ret; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto fail_and_stop; + + switch (hdev->product) { + case USB_PRODUCT_ID_D5NEXT: + priv->kind = d5next; + + 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->buffer_size = D5NEXT_CTRL_REPORT_SIZE; + + priv->temp_label = label_d5next_temp; + priv->virtual_temp_label = label_virtual_temp_sensors; + priv->speed_label = label_d5next_speeds; + priv->power_label = label_d5next_power; + priv->voltage_label = label_d5next_voltages; + priv->current_label = label_d5next_current; + break; + case USB_PRODUCT_ID_FARBWERK: + 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_label = label_temp_sensors; + priv->virtual_temp_label = label_virtual_temp_sensors; + break; + case USB_PRODUCT_ID_OCTO: + priv->kind = octo; + + 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->buffer_size = OCTO_CTRL_REPORT_SIZE; + + priv->temp_label = label_temp_sensors; + priv->virtual_temp_label = label_virtual_temp_sensors; + priv->speed_label = label_fan_speed; + priv->power_label = label_fan_power; + priv->voltage_label = label_fan_voltage; + priv->current_label = label_fan_current; + break; + case USB_PRODUCT_ID_QUADRO: + priv->kind = quadro; + + 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->buffer_size = QUADRO_CTRL_REPORT_SIZE; + priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET; + + priv->temp_label = label_temp_sensors; + priv->virtual_temp_label = label_virtual_temp_sensors; + priv->speed_label = label_quadro_speeds; + priv->power_label = label_fan_power; + priv->voltage_label = label_fan_voltage; + priv->current_label = label_fan_current; + break; + case USB_PRODUCT_ID_HIGHFLOWNEXT: + 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; + priv->speed_label = label_highflownext_fan_speed; + priv->power_label = label_highflownext_power; + priv->voltage_label = label_highflownext_voltage; + break; + default: + break; + } + + if (priv->buffer_size != 0) { + priv->checksum_start = 0x01; + priv->checksum_length = priv->buffer_size - 3; + priv->checksum_offset = priv->buffer_size - 2; + } + + priv->name = aqc_device_names[priv->kind]; + + priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL); + if (!priv->buffer) { + ret = -ENOMEM; + goto fail_and_close; + } + + mutex_init(&priv->mutex); + + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv, + &aqc_chip_info, NULL); + + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + goto fail_and_close; + } + + aqc_debugfs_init(priv); + + return 0; + +fail_and_close: + hid_hw_close(hdev); +fail_and_stop: + hid_hw_stop(hdev); + return ret; +} + +static void aqc_remove(struct hid_device *hdev) +{ + struct aqc_data *priv = hid_get_drvdata(hdev); + + debugfs_remove_recursive(priv->debugfs); + hwmon_device_unregister(priv->hwmon_dev); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id aqc_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, aqc_table); + +static struct hid_driver aqc_driver = { + .name = DRIVER_NAME, + .id_table = aqc_table, + .probe = aqc_probe, + .remove = aqc_remove, + .raw_event = aqc_raw_event, +}; + +static int __init aqc_init(void) +{ + return hid_register_driver(&aqc_driver); +} + +static void __exit aqc_exit(void) +{ + hid_unregister_driver(&aqc_driver); +} + +/* Request to initialize after the HID bus to ensure it's not being loaded before */ +late_initcall(aqc_init); +module_exit(aqc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>"); +MODULE_AUTHOR("Jack Doan <me@jackdoan.com>"); +MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices"); diff --git a/drivers/hwmon/as370-hwmon.c b/drivers/hwmon/as370-hwmon.c index 464244ba8d58..63b5b2d6e593 100644 --- a/drivers/hwmon/as370-hwmon.c +++ b/drivers/hwmon/as370-hwmon.c @@ -76,18 +76,8 @@ as370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, } } -static const u32 as370_hwmon_temp_config[] = { - HWMON_T_INPUT, - 0 -}; - -static const struct hwmon_channel_info as370_hwmon_temp = { - .type = hwmon_temp, - .config = as370_hwmon_temp_config, -}; - static const struct hwmon_channel_info *as370_hwmon_info[] = { - &as370_hwmon_temp, + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), NULL }; diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 4c609e23a4ef..ce4da836765c 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -186,7 +186,7 @@ struct asb100_data { /* array of 2 pointers to subclients */ struct i2c_client *lm75[2]; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ u8 in[7]; /* Register value */ u8 in_max[7]; /* Register value */ u8 in_min[7]; /* Register value */ @@ -205,11 +205,10 @@ struct asb100_data { static int asb100_read_value(struct i2c_client *client, u16 reg); static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val); -static int asb100_probe(struct i2c_client *client, - const struct i2c_device_id *id); +static int asb100_probe(struct i2c_client *client); static int asb100_detect(struct i2c_client *client, struct i2c_board_info *info); -static int asb100_remove(struct i2c_client *client); +static void asb100_remove(struct i2c_client *client); static struct asb100_data *asb100_update_device(struct device *dev); static void asb100_init_client(struct i2c_client *client); @@ -224,7 +223,7 @@ static struct i2c_driver asb100_driver = { .driver = { .name = "asb100", }, - .probe = asb100_probe, + .probe_new = asb100_probe, .remove = asb100_remove, .id_table = asb100_id, .detect = asb100_detect, @@ -770,13 +769,12 @@ static int asb100_detect(struct i2c_client *client, if (val1 != 0x31 || val2 != 0x06) return -ENODEV; - strlcpy(info->type, "asb100", I2C_NAME_SIZE); + strscpy(info->type, "asb100", I2C_NAME_SIZE); return 0; } -static int asb100_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int asb100_probe(struct i2c_client *client) { int err; struct asb100_data *data; @@ -824,7 +822,7 @@ ERROR3: return err; } -static int asb100_remove(struct i2c_client *client) +static void asb100_remove(struct i2c_client *client) { struct asb100_data *data = i2c_get_clientdata(client); @@ -833,8 +831,6 @@ static int asb100_remove(struct i2c_client *client) i2c_unregister_device(data->lm75[1]); i2c_unregister_device(data->lm75[0]); - - return 0; } /* @@ -995,7 +991,7 @@ static struct asb100_data *asb100_update_device(struct device *dev) (asb100_read_value(client, ASB100_REG_ALARM2) << 8); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; dev_dbg(&client->dev, "... device update complete\n"); } diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 9e14e2829ee9..54595454537b 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -77,7 +77,7 @@ struct asc7621_data { struct i2c_client client; struct device *class_dev; struct mutex update_lock; - int valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_high_reading; /* In jiffies */ unsigned long last_low_reading; /* In jiffies */ /* @@ -1032,7 +1032,7 @@ static struct asc7621_data *asc7621_update_device(struct device *dev) data->last_low_reading = jiffies; } /* last_reading */ - data->valid = 1; + data->valid = true; mutex_unlock(&data->update_lock); @@ -1087,7 +1087,7 @@ static void asc7621_init_client(struct i2c_client *client) } static int -asc7621_probe(struct i2c_client *client, const struct i2c_device_id *id) +asc7621_probe(struct i2c_client *client) { struct asc7621_data *data; int i, err; @@ -1153,7 +1153,7 @@ static int asc7621_detect(struct i2c_client *client, if (company == asc7621_chips[chip_index].company_id && verstep == asc7621_chips[chip_index].verstep_id) { - strlcpy(info->type, asc7621_chips[chip_index].name, + strscpy(info->type, asc7621_chips[chip_index].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Matched %s at 0x%02x\n", @@ -1165,7 +1165,7 @@ static int asc7621_detect(struct i2c_client *client, return -ENODEV; } -static int asc7621_remove(struct i2c_client *client) +static void asc7621_remove(struct i2c_client *client) { struct asc7621_data *data = i2c_get_clientdata(client); int i; @@ -1176,8 +1176,6 @@ static int asc7621_remove(struct i2c_client *client) device_remove_file(&client->dev, &(asc7621_params[i].sda.dev_attr)); } - - return 0; } static const struct i2c_device_id asc7621_id[] = { @@ -1193,7 +1191,7 @@ static struct i2c_driver asc7621_driver = { .driver = { .name = "asc7621", }, - .probe = asc7621_probe, + .probe_new = asc7621_probe, .remove = asc7621_remove, .id_table = asc7621_id, .detect = asc7621_detect, diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 33fb54845bf6..d11f674e3dc3 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -159,7 +159,7 @@ * 11: reserved. */ #define M_TACH_MODE 0x02 /* 10b */ -#define M_TACH_UNIT 0x0210 +#define M_TACH_UNIT 0x0420 #define INIT_FAN_CTRL 0xFF /* How long we sleep in us while waiting for an RPM result. */ @@ -620,7 +620,7 @@ static ssize_t rpm_show(struct device *dev, struct device_attribute *attr, static umode_t pwm_is_visible(struct kobject *kobj, struct attribute *a, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev); if (!priv->pwm_present[index]) @@ -631,7 +631,7 @@ static umode_t pwm_is_visible(struct kobject *kobj, static umode_t fan_dev_is_visible(struct kobject *kobj, struct attribute *a, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev); if (!priv->fan_tach_present[index]) @@ -851,6 +851,8 @@ static int aspeed_create_fan(struct device *dev, ret = of_property_read_u32(child, "reg", &pwm_port); if (ret) return ret; + if (pwm_port >= ARRAY_SIZE(pwm_port_params)) + return -EINVAL; aspeed_create_pwm_port(priv, (u8)pwm_port); ret = of_property_count_u8_elems(child, "cooling-levels"); diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c new file mode 100644 index 000000000000..81e688975c6a --- /dev/null +++ b/drivers/hwmon/asus-ec-sensors.c @@ -0,0 +1,1040 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HWMON driver for ASUS motherboards that publish some sensor values + * via the embedded controller registers. + * + * Copyright (C) 2021 Eugene Shalygin <eugene.shalygin@gmail.com> + + * EC provides: + * - Chipset temperature + * - CPU temperature + * - Motherboard temperature + * - T_Sensor temperature + * - VRM temperature + * - Water In temperature + * - Water Out temperature + * - CPU Optional fan RPM + * - Chipset fan RPM + * - VRM Heat Sink fan RPM + * - Water Flow fan RPM + * - CPU current + * - CPU core voltage + */ + +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/dev_printk.h> +#include <linux/dmi.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sort.h> +#include <linux/units.h> + +#include <asm/unaligned.h> + +static char *mutex_path_override; + +/* Writing to this EC register switches EC bank */ +#define ASUS_EC_BANK_REGISTER 0xff +#define SENSOR_LABEL_LEN 16 + +/* + * Arbitrary set max. allowed bank number. Required for sorting banks and + * currently is overkill with just 2 banks used at max, but for the sake + * of alignment let's set it to a higher value. + */ +#define ASUS_EC_MAX_BANK 3 + +#define ACPI_LOCK_DELAY_MS 500 + +/* ACPI mutex for locking access to the EC for the firmware */ +#define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX" + +#define ASUS_HW_ACCESS_MUTEX_RMTW_ASMX "\\RMTW.ASMX" + +#define ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0 "\\_SB_.PCI0.SBRG.SIO1.MUT0" + +#define MAX_IDENTICAL_BOARD_VARIATIONS 3 + +/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */ +#define ACPI_GLOBAL_LOCK_PSEUDO_PATH ":GLOBAL_LOCK" + +typedef union { + u32 value; + struct { + u8 index; + u8 bank; + u8 size; + u8 dummy; + } components; +} sensor_address; + +#define MAKE_SENSOR_ADDRESS(size, bank, index) { \ + .value = (size << 16) + (bank << 8) + index \ + } + +static u32 hwmon_attributes[hwmon_max] = { + [hwmon_chip] = HWMON_C_REGISTER_TZ, + [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, + [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, + [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, + [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL, +}; + +struct ec_sensor_info { + char label[SENSOR_LABEL_LEN]; + enum hwmon_sensor_types type; + sensor_address addr; +}; + +#define EC_SENSOR(sensor_label, sensor_type, size, bank, index) { \ + .label = sensor_label, .type = sensor_type, \ + .addr = MAKE_SENSOR_ADDRESS(size, bank, index), \ + } + +enum ec_sensors { + /* chipset temperature [℃] */ + ec_sensor_temp_chipset, + /* CPU temperature [℃] */ + ec_sensor_temp_cpu, + /* motherboard temperature [℃] */ + ec_sensor_temp_mb, + /* "T_Sensor" temperature sensor reading [℃] */ + ec_sensor_temp_t_sensor, + /* VRM temperature [℃] */ + ec_sensor_temp_vrm, + /* CPU Core voltage [mV] */ + ec_sensor_in_cpu_core, + /* CPU_Opt fan [RPM] */ + ec_sensor_fan_cpu_opt, + /* VRM heat sink fan [RPM] */ + ec_sensor_fan_vrm_hs, + /* Chipset fan [RPM] */ + ec_sensor_fan_chipset, + /* Water flow sensor reading [RPM] */ + ec_sensor_fan_water_flow, + /* CPU current [A] */ + ec_sensor_curr_cpu, + /* "Water_In" temperature sensor reading [℃] */ + ec_sensor_temp_water_in, + /* "Water_Out" temperature sensor reading [℃] */ + ec_sensor_temp_water_out, + /* "Water_Block_In" temperature sensor reading [℃] */ + ec_sensor_temp_water_block_in, + /* "Water_Block_Out" temperature sensor reading [℃] */ + ec_sensor_temp_water_block_out, + /* "T_sensor_2" temperature sensor reading [℃] */ + ec_sensor_temp_t_sensor_2, + /* "Extra_1" temperature sensor reading [℃] */ + ec_sensor_temp_sensor_extra_1, + /* "Extra_2" temperature sensor reading [℃] */ + ec_sensor_temp_sensor_extra_2, + /* "Extra_3" temperature sensor reading [℃] */ + ec_sensor_temp_sensor_extra_3, +}; + +#define SENSOR_TEMP_CHIPSET BIT(ec_sensor_temp_chipset) +#define SENSOR_TEMP_CPU BIT(ec_sensor_temp_cpu) +#define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb) +#define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor) +#define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm) +#define SENSOR_IN_CPU_CORE BIT(ec_sensor_in_cpu_core) +#define SENSOR_FAN_CPU_OPT BIT(ec_sensor_fan_cpu_opt) +#define SENSOR_FAN_VRM_HS BIT(ec_sensor_fan_vrm_hs) +#define SENSOR_FAN_CHIPSET BIT(ec_sensor_fan_chipset) +#define SENSOR_FAN_WATER_FLOW BIT(ec_sensor_fan_water_flow) +#define SENSOR_CURR_CPU BIT(ec_sensor_curr_cpu) +#define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in) +#define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out) +#define SENSOR_TEMP_WATER_BLOCK_IN BIT(ec_sensor_temp_water_block_in) +#define SENSOR_TEMP_WATER_BLOCK_OUT BIT(ec_sensor_temp_water_block_out) +#define SENSOR_TEMP_T_SENSOR_2 BIT(ec_sensor_temp_t_sensor_2) +#define SENSOR_TEMP_SENSOR_EXTRA_1 BIT(ec_sensor_temp_sensor_extra_1) +#define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2) +#define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3) + +enum board_family { + family_unknown, + family_amd_400_series, + family_amd_500_series, + family_intel_300_series, + family_intel_600_series +}; + +/* All the known sensors for ASUS EC controllers */ +static const struct ec_sensor_info sensors_family_amd_400[] = { + [ec_sensor_temp_chipset] = + EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), + [ec_sensor_temp_cpu] = + EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b), + [ec_sensor_temp_mb] = + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c), + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = + EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), + [ec_sensor_in_cpu_core] = + EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2), + [ec_sensor_fan_cpu_opt] = + EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_fan_vrm_hs] = + EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2), + [ec_sensor_fan_chipset] = + /* no chipset fans in this generation */ + EC_SENSOR("Chipset", hwmon_fan, 0, 0x00, 0x00), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xb4), + [ec_sensor_curr_cpu] = + EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4), + [ec_sensor_temp_water_in] = + EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d), + [ec_sensor_temp_water_out] = + EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x0b), +}; + +static const struct ec_sensor_info sensors_family_amd_500[] = { + [ec_sensor_temp_chipset] = + EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), + [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b), + [ec_sensor_temp_mb] = + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c), + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), + [ec_sensor_in_cpu_core] = + EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2), + [ec_sensor_fan_cpu_opt] = + EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2), + [ec_sensor_fan_chipset] = + EC_SENSOR("Chipset", hwmon_fan, 2, 0x00, 0xb4), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_curr_cpu] = EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4), + [ec_sensor_temp_water_in] = + EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), + [ec_sensor_temp_water_out] = + EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), + [ec_sensor_temp_water_block_in] = + EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02), + [ec_sensor_temp_water_block_out] = + EC_SENSOR("Water_Block_Out", hwmon_temp, 1, 0x01, 0x03), + [ec_sensor_temp_sensor_extra_1] = + EC_SENSOR("Extra_1", hwmon_temp, 1, 0x01, 0x09), + [ec_sensor_temp_t_sensor_2] = + EC_SENSOR("T_sensor_2", hwmon_temp, 1, 0x01, 0x0a), + [ec_sensor_temp_sensor_extra_2] = + EC_SENSOR("Extra_2", hwmon_temp, 1, 0x01, 0x0b), + [ec_sensor_temp_sensor_extra_3] = + EC_SENSOR("Extra_3", hwmon_temp, 1, 0x01, 0x0c), +}; + +static const struct ec_sensor_info sensors_family_intel_300[] = { + [ec_sensor_temp_chipset] = + EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), + [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b), + [ec_sensor_temp_mb] = + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c), + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), + [ec_sensor_fan_cpu_opt] = + EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_temp_water_in] = + EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), + [ec_sensor_temp_water_out] = + EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), +}; + +static const struct ec_sensor_info sensors_family_intel_600[] = { + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), +}; + +/* Shortcuts for common combinations */ +#define SENSOR_SET_TEMP_CHIPSET_CPU_MB \ + (SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB) +#define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT) +#define SENSOR_SET_WATER_BLOCK \ + (SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT) + +struct ec_board_info { + unsigned long sensors; + /* + * Defines which mutex to use for guarding access to the state and the + * hardware. Can be either a full path to an AML mutex or the + * pseudo-path ACPI_GLOBAL_LOCK_PSEUDO_PATH to use the global ACPI lock, + * or left empty to use a regular mutex object, in which case access to + * the hardware is not guarded. + */ + const char *mutex_path; + enum board_family family; +}; + +static const struct ec_board_info board_info_prime_x470_pro = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_FAN_CPU_OPT | + SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, + .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH, + .family = family_amd_400_series, +}; + +static const struct ec_board_info board_info_prime_x570_pro = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_pro_art_x570_creator_wifi = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT | + SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_pro_ws_x570_ace = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET | + SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_crosshair_viii_dark_hero = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | + SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER | + SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW | + SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_crosshair_viii_hero = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | + SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER | + SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | + SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | + SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_maximus_xi_hero = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | + SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER | + SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_intel_300_series, +}; + +static const struct ec_board_info board_info_crosshair_viii_impact = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | + SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_strix_b550_e_gaming = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_strix_b550_i_gaming = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_FAN_VRM_HS | SENSOR_CURR_CPU | + SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_strix_x570_e_gaming = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | + SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_strix_x570_e_gaming_wifi_ii = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_CURR_CPU | + SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_strix_x570_f_gaming = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_strix_x570_i_gaming = { + .sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | + SENSOR_FAN_VRM_HS | SENSOR_FAN_CHIPSET | + SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_500_series, +}; + +static const struct ec_board_info board_info_strix_z690_a_gaming_wifi_d4 = { + .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM, + .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, + .family = family_intel_600_series, +}; + +static const struct ec_board_info board_info_zenith_ii_extreme = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | + SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER | + SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_VRM_HS | + SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE | + SENSOR_SET_WATER_BLOCK | + SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 | + SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_500_series, +}; + +#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name, board_info) \ + { \ + .matches = { \ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, \ + "ASUSTeK COMPUTER INC."), \ + DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ + }, \ + .driver_data = (void *)board_info, \ + } + +static const struct dmi_system_id dmi_table[] = { + DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO", + &board_info_prime_x470_pro), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO", + &board_info_prime_x570_pro), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X570-CREATOR WIFI", + &board_info_pro_art_x570_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE", + &board_info_pro_ws_x570_ace), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII DARK HERO", + &board_info_crosshair_viii_dark_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII FORMULA", + &board_info_crosshair_viii_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO", + &board_info_crosshair_viii_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO (WI-FI)", + &board_info_crosshair_viii_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO", + &board_info_maximus_xi_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)", + &board_info_maximus_xi_hero), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII IMPACT", + &board_info_crosshair_viii_impact), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", + &board_info_strix_b550_e_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", + &board_info_strix_b550_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING", + &board_info_strix_x570_e_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING WIFI II", + &board_info_strix_x570_e_gaming_wifi_ii), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-F GAMING", + &board_info_strix_x570_f_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-I GAMING", + &board_info_strix_x570_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z690-A GAMING WIFI D4", + &board_info_strix_z690_a_gaming_wifi_d4), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME", + &board_info_zenith_ii_extreme), + {}, +}; + +struct ec_sensor { + unsigned int info_index; + s32 cached_value; +}; + +struct lock_data { + union { + acpi_handle aml; + /* global lock handle */ + u32 glk; + } mutex; + bool (*lock)(struct lock_data *data); + bool (*unlock)(struct lock_data *data); +}; + +/* + * The next function pairs implement options for locking access to the + * state and the EC + */ +static bool lock_via_acpi_mutex(struct lock_data *data) +{ + /* + * ASUS DSDT does not specify that access to the EC has to be guarded, + * but firmware does access it via ACPI + */ + return ACPI_SUCCESS(acpi_acquire_mutex(data->mutex.aml, + NULL, ACPI_LOCK_DELAY_MS)); +} + +static bool unlock_acpi_mutex(struct lock_data *data) +{ + return ACPI_SUCCESS(acpi_release_mutex(data->mutex.aml, NULL)); +} + +static bool lock_via_global_acpi_lock(struct lock_data *data) +{ + return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, + &data->mutex.glk)); +} + +static bool unlock_global_acpi_lock(struct lock_data *data) +{ + return ACPI_SUCCESS(acpi_release_global_lock(data->mutex.glk)); +} + +struct ec_sensors_data { + const struct ec_board_info *board_info; + const struct ec_sensor_info *sensors_info; + struct ec_sensor *sensors; + /* EC registers to read from */ + u16 *registers; + u8 *read_buffer; + /* sorted list of unique register banks */ + u8 banks[ASUS_EC_MAX_BANK + 1]; + /* in jiffies */ + unsigned long last_updated; + struct lock_data lock_data; + /* number of board EC sensors */ + u8 nr_sensors; + /* + * number of EC registers to read + * (sensor might span more than 1 register) + */ + u8 nr_registers; + /* number of unique register banks */ + u8 nr_banks; +}; + +static u8 register_bank(u16 reg) +{ + return reg >> 8; +} + +static u8 register_index(u16 reg) +{ + return reg & 0x00ff; +} + +static bool is_sensor_data_signed(const struct ec_sensor_info *si) +{ + /* + * guessed from WMI functions in DSDT code for boards + * of the X470 generation + */ + return si->type == hwmon_temp; +} + +static const struct ec_sensor_info * +get_sensor_info(const struct ec_sensors_data *state, int index) +{ + return state->sensors_info + state->sensors[index].info_index; +} + +static int find_ec_sensor_index(const struct ec_sensors_data *ec, + enum hwmon_sensor_types type, int channel) +{ + unsigned int i; + + for (i = 0; i < ec->nr_sensors; i++) { + if (get_sensor_info(ec, i)->type == type) { + if (channel == 0) + return i; + channel--; + } + } + return -ENOENT; +} + +static int bank_compare(const void *a, const void *b) +{ + return *((const s8 *)a) - *((const s8 *)b); +} + +static void setup_sensor_data(struct ec_sensors_data *ec) +{ + struct ec_sensor *s = ec->sensors; + bool bank_found; + int i, j; + u8 bank; + + ec->nr_banks = 0; + ec->nr_registers = 0; + + for_each_set_bit(i, &ec->board_info->sensors, + BITS_PER_TYPE(ec->board_info->sensors)) { + s->info_index = i; + s->cached_value = 0; + ec->nr_registers += + ec->sensors_info[s->info_index].addr.components.size; + bank_found = false; + bank = ec->sensors_info[s->info_index].addr.components.bank; + for (j = 0; j < ec->nr_banks; j++) { + if (ec->banks[j] == bank) { + bank_found = true; + break; + } + } + if (!bank_found) { + ec->banks[ec->nr_banks++] = bank; + } + s++; + } + sort(ec->banks, ec->nr_banks, 1, bank_compare, NULL); +} + +static void fill_ec_registers(struct ec_sensors_data *ec) +{ + const struct ec_sensor_info *si; + unsigned int i, j, register_idx = 0; + + for (i = 0; i < ec->nr_sensors; ++i) { + si = get_sensor_info(ec, i); + for (j = 0; j < si->addr.components.size; ++j, ++register_idx) { + ec->registers[register_idx] = + (si->addr.components.bank << 8) + + si->addr.components.index + j; + } + } +} + +static int setup_lock_data(struct device *dev) +{ + const char *mutex_path; + int status; + struct ec_sensors_data *state = dev_get_drvdata(dev); + + mutex_path = mutex_path_override ? + mutex_path_override : state->board_info->mutex_path; + + if (!mutex_path || !strlen(mutex_path)) { + dev_err(dev, "Hardware access guard mutex name is empty"); + return -EINVAL; + } + if (!strcmp(mutex_path, ACPI_GLOBAL_LOCK_PSEUDO_PATH)) { + state->lock_data.mutex.glk = 0; + state->lock_data.lock = lock_via_global_acpi_lock; + state->lock_data.unlock = unlock_global_acpi_lock; + } else { + status = acpi_get_handle(NULL, (acpi_string)mutex_path, + &state->lock_data.mutex.aml); + if (ACPI_FAILURE(status)) { + dev_err(dev, + "Failed to get hardware access guard AML mutex '%s': error %d", + mutex_path, status); + return -ENOENT; + } + state->lock_data.lock = lock_via_acpi_mutex; + state->lock_data.unlock = unlock_acpi_mutex; + } + return 0; +} + +static int asus_ec_bank_switch(u8 bank, u8 *old) +{ + int status = 0; + + if (old) { + status = ec_read(ASUS_EC_BANK_REGISTER, old); + } + if (status || (old && (*old == bank))) + return status; + return ec_write(ASUS_EC_BANK_REGISTER, bank); +} + +static int asus_ec_block_read(const struct device *dev, + struct ec_sensors_data *ec) +{ + int ireg, ibank, status; + u8 bank, reg_bank, prev_bank; + + bank = 0; + status = asus_ec_bank_switch(bank, &prev_bank); + if (status) { + dev_warn(dev, "EC bank switch failed"); + return status; + } + + if (prev_bank) { + /* oops... somebody else is working with the EC too */ + dev_warn(dev, + "Concurrent access to the ACPI EC detected.\nRace condition possible."); + } + + /* read registers minimizing bank switches. */ + for (ibank = 0; ibank < ec->nr_banks; ibank++) { + if (bank != ec->banks[ibank]) { + bank = ec->banks[ibank]; + if (asus_ec_bank_switch(bank, NULL)) { + dev_warn(dev, "EC bank switch to %d failed", + bank); + break; + } + } + for (ireg = 0; ireg < ec->nr_registers; ireg++) { + reg_bank = register_bank(ec->registers[ireg]); + if (reg_bank < bank) { + continue; + } + ec_read(register_index(ec->registers[ireg]), + ec->read_buffer + ireg); + } + } + + status = asus_ec_bank_switch(prev_bank, NULL); + return status; +} + +static inline s32 get_sensor_value(const struct ec_sensor_info *si, u8 *data) +{ + if (is_sensor_data_signed(si)) { + switch (si->addr.components.size) { + case 1: + return (s8)*data; + case 2: + return (s16)get_unaligned_be16(data); + case 4: + return (s32)get_unaligned_be32(data); + default: + return 0; + } + } else { + switch (si->addr.components.size) { + case 1: + return *data; + case 2: + return get_unaligned_be16(data); + case 4: + return get_unaligned_be32(data); + default: + return 0; + } + } +} + +static void update_sensor_values(struct ec_sensors_data *ec, u8 *data) +{ + const struct ec_sensor_info *si; + struct ec_sensor *s, *sensor_end; + + sensor_end = ec->sensors + ec->nr_sensors; + for (s = ec->sensors; s != sensor_end; s++) { + si = ec->sensors_info + s->info_index; + s->cached_value = get_sensor_value(si, data); + data += si->addr.components.size; + } +} + +static int update_ec_sensors(const struct device *dev, + struct ec_sensors_data *ec) +{ + int status; + + if (!ec->lock_data.lock(&ec->lock_data)) { + dev_warn(dev, "Failed to acquire mutex"); + return -EBUSY; + } + + status = asus_ec_block_read(dev, ec); + + if (!status) { + update_sensor_values(ec, ec->read_buffer); + } + + if (!ec->lock_data.unlock(&ec->lock_data)) + dev_err(dev, "Failed to release mutex"); + + return status; +} + +static long scale_sensor_value(s32 value, int data_type) +{ + switch (data_type) { + case hwmon_curr: + case hwmon_temp: + return value * MILLI; + default: + return value; + } +} + +static int get_cached_value_or_update(const struct device *dev, + int sensor_index, + struct ec_sensors_data *state, s32 *value) +{ + if (time_after(jiffies, state->last_updated + HZ)) { + if (update_ec_sensors(dev, state)) { + dev_err(dev, "update_ec_sensors() failure\n"); + return -EIO; + } + + state->last_updated = jiffies; + } + + *value = state->sensors[sensor_index].cached_value; + return 0; +} + +/* + * Now follow the functions that implement the hwmon interface + */ + +static int asus_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + s32 value = 0; + + struct ec_sensors_data *state = dev_get_drvdata(dev); + int sidx = find_ec_sensor_index(state, type, channel); + + if (sidx < 0) { + return sidx; + } + + ret = get_cached_value_or_update(dev, sidx, state, &value); + if (!ret) { + *val = scale_sensor_value(value, + get_sensor_info(state, sidx)->type); + } + + return ret; +} + +static int asus_ec_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + struct ec_sensors_data *state = dev_get_drvdata(dev); + int sensor_index = find_ec_sensor_index(state, type, channel); + *str = get_sensor_info(state, sensor_index)->label; + + return 0; +} + +static umode_t asus_ec_hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct ec_sensors_data *state = drvdata; + + return find_ec_sensor_index(state, type, channel) >= 0 ? S_IRUGO : 0; +} + +static int +asus_ec_hwmon_add_chan_info(struct hwmon_channel_info *asus_ec_hwmon_chan, + struct device *dev, int num, + enum hwmon_sensor_types type, u32 config) +{ + int i; + u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); + + if (!cfg) + return -ENOMEM; + + asus_ec_hwmon_chan->type = type; + asus_ec_hwmon_chan->config = cfg; + for (i = 0; i < num; i++, cfg++) + *cfg = config; + + return 0; +} + +static const struct hwmon_ops asus_ec_hwmon_ops = { + .is_visible = asus_ec_hwmon_is_visible, + .read = asus_ec_hwmon_read, + .read_string = asus_ec_hwmon_read_string, +}; + +static struct hwmon_chip_info asus_ec_chip_info = { + .ops = &asus_ec_hwmon_ops, +}; + +static const struct ec_board_info *get_board_info(void) +{ + const struct dmi_system_id *dmi_entry; + + dmi_entry = dmi_first_match(dmi_table); + return dmi_entry ? dmi_entry->driver_data : NULL; +} + +static int asus_ec_probe(struct platform_device *pdev) +{ + const struct hwmon_channel_info **ptr_asus_ec_ci; + int nr_count[hwmon_max] = { 0 }, nr_types = 0; + struct hwmon_channel_info *asus_ec_hwmon_chan; + const struct ec_board_info *pboard_info; + const struct hwmon_chip_info *chip_info; + struct device *dev = &pdev->dev; + struct ec_sensors_data *ec_data; + const struct ec_sensor_info *si; + enum hwmon_sensor_types type; + struct device *hwdev; + unsigned int i; + int status; + + pboard_info = get_board_info(); + if (!pboard_info) + return -ENODEV; + + ec_data = devm_kzalloc(dev, sizeof(struct ec_sensors_data), + GFP_KERNEL); + if (!ec_data) + return -ENOMEM; + + dev_set_drvdata(dev, ec_data); + ec_data->board_info = pboard_info; + + switch (ec_data->board_info->family) { + case family_amd_400_series: + ec_data->sensors_info = sensors_family_amd_400; + break; + case family_amd_500_series: + ec_data->sensors_info = sensors_family_amd_500; + break; + case family_intel_300_series: + ec_data->sensors_info = sensors_family_intel_300; + break; + case family_intel_600_series: + ec_data->sensors_info = sensors_family_intel_600; + break; + default: + dev_err(dev, "Unknown board family: %d", + ec_data->board_info->family); + return -EINVAL; + } + + ec_data->nr_sensors = hweight_long(ec_data->board_info->sensors); + ec_data->sensors = devm_kcalloc(dev, ec_data->nr_sensors, + sizeof(struct ec_sensor), GFP_KERNEL); + + status = setup_lock_data(dev); + if (status) { + dev_err(dev, "Failed to setup state/EC locking: %d", status); + return status; + } + + setup_sensor_data(ec_data); + ec_data->registers = devm_kcalloc(dev, ec_data->nr_registers, + sizeof(u16), GFP_KERNEL); + ec_data->read_buffer = devm_kcalloc(dev, ec_data->nr_registers, + sizeof(u8), GFP_KERNEL); + + if (!ec_data->registers || !ec_data->read_buffer) + return -ENOMEM; + + fill_ec_registers(ec_data); + + for (i = 0; i < ec_data->nr_sensors; ++i) { + si = get_sensor_info(ec_data, i); + if (!nr_count[si->type]) + ++nr_types; + ++nr_count[si->type]; + } + + if (nr_count[hwmon_temp]) + nr_count[hwmon_chip]++, nr_types++; + + asus_ec_hwmon_chan = devm_kcalloc( + dev, nr_types, sizeof(*asus_ec_hwmon_chan), GFP_KERNEL); + if (!asus_ec_hwmon_chan) + return -ENOMEM; + + ptr_asus_ec_ci = devm_kcalloc(dev, nr_types + 1, + sizeof(*ptr_asus_ec_ci), GFP_KERNEL); + if (!ptr_asus_ec_ci) + return -ENOMEM; + + asus_ec_chip_info.info = ptr_asus_ec_ci; + chip_info = &asus_ec_chip_info; + + for (type = 0; type < hwmon_max; ++type) { + if (!nr_count[type]) + continue; + + asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); + *ptr_asus_ec_ci++ = asus_ec_hwmon_chan++; + } + + dev_info(dev, "board has %d EC sensors that span %d registers", + ec_data->nr_sensors, ec_data->nr_registers); + + hwdev = devm_hwmon_device_register_with_info(dev, "asusec", + ec_data, chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwdev); +} + +MODULE_DEVICE_TABLE(dmi, dmi_table); + +static struct platform_driver asus_ec_sensors_platform_driver = { + .driver = { + .name = "asus-ec-sensors", + }, + .probe = asus_ec_probe, +}; + +static struct platform_device *asus_ec_sensors_platform_device; + +static int __init asus_ec_init(void) +{ + asus_ec_sensors_platform_device = + platform_create_bundle(&asus_ec_sensors_platform_driver, + asus_ec_probe, NULL, 0, NULL, 0); + + if (IS_ERR(asus_ec_sensors_platform_device)) + return PTR_ERR(asus_ec_sensors_platform_device); + + return 0; +} + +static void __exit asus_ec_exit(void) +{ + platform_device_unregister(asus_ec_sensors_platform_device); + platform_driver_unregister(&asus_ec_sensors_platform_driver); +} + +module_init(asus_ec_init); +module_exit(asus_ec_exit); + +module_param_named(mutex_path, mutex_path_override, charp, 0); +MODULE_PARM_DESC(mutex_path, + "Override ACPI mutex path used to guard access to hardware"); + +MODULE_AUTHOR("Eugene Shalygin <eugene.shalygin@gmail.com>"); +MODULE_DESCRIPTION( + "HWMON driver for sensors accessible via ACPI EC in ASUS motherboards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/asus_wmi_sensors.c b/drivers/hwmon/asus_wmi_sensors.c new file mode 100644 index 000000000000..6e8a908171f0 --- /dev/null +++ b/drivers/hwmon/asus_wmi_sensors.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HWMON driver for ASUS motherboards that provides sensor readouts via WMI + * interface present in the UEFI of the X370/X470/B450/X399 Ryzen motherboards. + * + * Copyright (C) 2018-2019 Ed Brindley <kernel@maidavale.org> + * + * WMI interface provides: + * - CPU Core Voltage, + * - CPU SOC Voltage, + * - DRAM Voltage, + * - VDDP Voltage, + * - 1.8V PLL Voltage, + * - +12V Voltage, + * - +5V Voltage, + * - 3VSB Voltage, + * - VBAT Voltage, + * - AVCC3 Voltage, + * - SB 1.05V Voltage, + * - CPU Core Voltage, + * - CPU SOC Voltage, + * - DRAM Voltage, + * - CPU Fan RPM, + * - Chassis Fan 1 RPM, + * - Chassis Fan 2 RPM, + * - Chassis Fan 3 RPM, + * - HAMP Fan RPM, + * - Water Pump RPM, + * - CPU OPT RPM, + * - Water Flow RPM, + * - AIO Pump RPM, + * - CPU Temperature, + * - CPU Socket Temperature, + * - Motherboard Temperature, + * - Chipset Temperature, + * - Tsensor 1 Temperature, + * - CPU VRM Temperature, + * - Water In, + * - Water Out, + * - CPU VRM Output Current. + */ + +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/units.h> +#include <linux/wmi.h> + +#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" +#define ASUSWMI_METHODID_GET_VALUE 0x52574543 /* RWEC */ +#define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 /* QWEC */ +#define ASUSWMI_METHODID_GET_INFO 0x50574543 /* PWEC */ +#define ASUSWMI_METHODID_GET_NUMBER 0x50574572 /* PWEr */ +#define ASUSWMI_METHODID_GET_VERSION 0x50574574 /* PWEt */ + +#define ASUS_WMI_MAX_STR_SIZE 32 + +#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) { \ + .matches = { \ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \ + DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ + }, \ +} + +static const struct dmi_system_id asus_wmi_dmi_table[] = { + DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X399-A"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI EXTREME"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("CROSSHAIR VI HERO"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI HERO (WI-FI AC)"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO (WI-FI)"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-E GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING II"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-I GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X399-E GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-I GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME ALPHA"), + {} +}; +MODULE_DEVICE_TABLE(dmi, asus_wmi_dmi_table); + +enum asus_wmi_sensor_class { + VOLTAGE = 0x0, + TEMPERATURE_C = 0x1, + FAN_RPM = 0x2, + CURRENT = 0x3, + WATER_FLOW = 0x4, +}; + +enum asus_wmi_location { + CPU = 0x0, + CPU_SOC = 0x1, + DRAM = 0x2, + MOTHERBOARD = 0x3, + CHIPSET = 0x4, + AUX = 0x5, + VRM = 0x6, + COOLER = 0x7 +}; + +enum asus_wmi_type { + SIGNED_INT = 0x0, + UNSIGNED_INT = 0x1, + SCALED = 0x3, +}; + +enum asus_wmi_source { + SIO = 0x1, + EC = 0x2 +}; + +static enum hwmon_sensor_types asus_data_types[] = { + [VOLTAGE] = hwmon_in, + [TEMPERATURE_C] = hwmon_temp, + [FAN_RPM] = hwmon_fan, + [CURRENT] = hwmon_curr, + [WATER_FLOW] = hwmon_fan, +}; + +static u32 hwmon_attributes[hwmon_max] = { + [hwmon_chip] = HWMON_C_REGISTER_TZ, + [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, + [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, + [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, + [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL, +}; + +/** + * struct asus_wmi_sensor_info - sensor info. + * @id: sensor id. + * @data_type: sensor class e.g. voltage, temp etc. + * @location: sensor location. + * @name: sensor name. + * @source: sensor source. + * @type: sensor type signed, unsigned etc. + * @cached_value: cached sensor value. + */ +struct asus_wmi_sensor_info { + u32 id; + int data_type; + int location; + char name[ASUS_WMI_MAX_STR_SIZE]; + int source; + int type; + long cached_value; +}; + +struct asus_wmi_wmi_info { + unsigned long source_last_updated[3]; /* in jiffies */ + int sensor_count; + + const struct asus_wmi_sensor_info **info[hwmon_max]; + struct asus_wmi_sensor_info **info_by_id; +}; + +struct asus_wmi_sensors { + struct asus_wmi_wmi_info wmi; + /* lock access to internal cache */ + struct mutex lock; +}; + +/* + * Universal method for calling WMI method + */ +static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output) +{ + struct acpi_buffer input = {(acpi_size) sizeof(*args), args }; + acpi_status status; + + status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, + method_id, &input, output); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} + +/* + * Gets the version of the ASUS sensors interface implemented + */ +static int asus_wmi_get_version(u32 *version) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {0, 0, 0}; + union acpi_object *obj; + int err; + + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VERSION, args, &output); + if (err) + return err; + + obj = output.pointer; + if (!obj) + return -EIO; + + if (obj->type != ACPI_TYPE_INTEGER) { + err = -EIO; + goto out_free_obj; + } + + err = 0; + *version = obj->integer.value; + +out_free_obj: + ACPI_FREE(obj); + return err; +} + +/* + * Gets the number of sensor items + */ +static int asus_wmi_get_item_count(u32 *count) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {0, 0, 0}; + union acpi_object *obj; + int err; + + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_NUMBER, args, &output); + if (err) + return err; + + obj = output.pointer; + if (!obj) + return -EIO; + + if (obj->type != ACPI_TYPE_INTEGER) { + err = -EIO; + goto out_free_obj; + } + + err = 0; + *count = obj->integer.value; + +out_free_obj: + ACPI_FREE(obj); + return err; +} + +static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan, + struct device *dev, int num, + enum hwmon_sensor_types type, u32 config) +{ + u32 *cfg; + + cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + asus_wmi_hwmon_chan->type = type; + asus_wmi_hwmon_chan->config = cfg; + memset32(cfg, config, num); + + return 0; +} + +/* + * For a given sensor item returns details e.g. type (voltage/temperature/fan speed etc), bank etc + */ +static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) +{ + union acpi_object name_obj, data_type_obj, location_obj, source_obj, type_obj; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {index, 0}; + union acpi_object *obj; + int err; + + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_INFO, args, &output); + if (err) + return err; + + s->id = index; + + obj = output.pointer; + if (!obj) + return -EIO; + + if (obj->type != ACPI_TYPE_PACKAGE) { + err = -EIO; + goto out_free_obj; + } + + if (obj->package.count != 5) { + err = -EIO; + goto out_free_obj; + } + + name_obj = obj->package.elements[0]; + if (name_obj.type != ACPI_TYPE_STRING) { + err = -EIO; + goto out_free_obj; + } + + strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1); + + data_type_obj = obj->package.elements[1]; + if (data_type_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; + goto out_free_obj; + } + + s->data_type = data_type_obj.integer.value; + + location_obj = obj->package.elements[2]; + if (location_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; + goto out_free_obj; + } + + s->location = location_obj.integer.value; + + source_obj = obj->package.elements[3]; + if (source_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; + goto out_free_obj; + } + + s->source = source_obj.integer.value; + + type_obj = obj->package.elements[4]; + if (type_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; + goto out_free_obj; + } + + err = 0; + s->type = type_obj.integer.value; + +out_free_obj: + ACPI_FREE(obj); + return err; +} + +static int asus_wmi_update_buffer(int source) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {source, 0}; + + return asus_wmi_call_method(ASUSWMI_METHODID_UPDATE_BUFFER, args, &output); +} + +static int asus_wmi_get_sensor_value(u8 index, long *value) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {index, 0}; + union acpi_object *obj; + int err; + + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VALUE, args, &output); + if (err) + return err; + + obj = output.pointer; + if (!obj) + return -EIO; + + if (obj->type != ACPI_TYPE_INTEGER) { + err = -EIO; + goto out_free_obj; + } + + err = 0; + *value = obj->integer.value; + +out_free_obj: + ACPI_FREE(obj); + return err; +} + +static int asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) +{ + struct asus_wmi_sensor_info *sensor; + long value = 0; + int ret; + int i; + + for (i = 0; i < sensor_data->wmi.sensor_count; i++) { + sensor = sensor_data->wmi.info_by_id[i]; + if (sensor && sensor->source == source) { + ret = asus_wmi_get_sensor_value(sensor->id, &value); + if (ret) + return ret; + + sensor->cached_value = value; + } + } + + return 0; +} + +static int asus_wmi_scale_sensor_value(u32 value, int data_type) +{ + /* FAN_RPM and WATER_FLOW don't need scaling */ + switch (data_type) { + case VOLTAGE: + /* value in microVolts */ + return DIV_ROUND_CLOSEST(value, KILO); + case TEMPERATURE_C: + /* value in Celsius */ + return value * MILLIDEGREE_PER_DEGREE; + case CURRENT: + /* value in Amperes */ + return value * MILLI; + } + return value; +} + +static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info *sensor, + struct asus_wmi_sensors *sensor_data, + u32 *value) +{ + int ret = 0; + + mutex_lock(&sensor_data->lock); + + if (time_after(jiffies, sensor_data->wmi.source_last_updated[sensor->source] + HZ)) { + ret = asus_wmi_update_buffer(sensor->source); + if (ret) + goto unlock; + + ret = asus_wmi_update_values_for_source(sensor->source, sensor_data); + if (ret) + goto unlock; + + sensor_data->wmi.source_last_updated[sensor->source] = jiffies; + } + + *value = sensor->cached_value; + +unlock: + mutex_unlock(&sensor_data->lock); + + return ret; +} + +/* Now follow the functions that implement the hwmon interface */ +static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + const struct asus_wmi_sensor_info *sensor; + u32 value = 0; + int ret; + + struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); + + sensor = *(sensor_data->wmi.info[type] + channel); + + ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value); + if (ret) + return ret; + + *val = asus_wmi_scale_sensor_value(value, sensor->data_type); + + return ret; +} + +static int asus_wmi_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); + const struct asus_wmi_sensor_info *sensor; + + sensor = *(sensor_data->wmi.info[type] + channel); + *str = sensor->name; + + return 0; +} + +static umode_t asus_wmi_hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct asus_wmi_sensors *sensor_data = drvdata; + const struct asus_wmi_sensor_info *sensor; + + sensor = *(sensor_data->wmi.info[type] + channel); + if (sensor) + return 0444; + + return 0; +} + +static const struct hwmon_ops asus_wmi_hwmon_ops = { + .is_visible = asus_wmi_hwmon_is_visible, + .read = asus_wmi_hwmon_read, + .read_string = asus_wmi_hwmon_read_string, +}; + +static struct hwmon_chip_info asus_wmi_chip_info = { + .ops = &asus_wmi_hwmon_ops, + .info = NULL, +}; + +static int asus_wmi_configure_sensor_setup(struct device *dev, + struct asus_wmi_sensors *sensor_data) +{ + const struct hwmon_channel_info **ptr_asus_wmi_ci; + struct hwmon_channel_info *asus_wmi_hwmon_chan; + int nr_count[hwmon_max] = {}, nr_types = 0; + struct asus_wmi_sensor_info *temp_sensor; + const struct hwmon_chip_info *chip_info; + enum hwmon_sensor_types type; + struct device *hwdev; + int i, idx; + int err; + + for (i = 0; i < sensor_data->wmi.sensor_count; i++) { + struct asus_wmi_sensor_info sensor; + + err = asus_wmi_sensor_info(i, &sensor); + if (err) + return err; + + switch (sensor.data_type) { + case TEMPERATURE_C: + case VOLTAGE: + case CURRENT: + case FAN_RPM: + case WATER_FLOW: + type = asus_data_types[sensor.data_type]; + if (!nr_count[type]) + nr_types++; + nr_count[type]++; + break; + } + } + + if (nr_count[hwmon_temp]) + nr_count[hwmon_chip]++, nr_types++; + + asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, + sizeof(*asus_wmi_hwmon_chan), + GFP_KERNEL); + if (!asus_wmi_hwmon_chan) + return -ENOMEM; + + ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, + sizeof(*ptr_asus_wmi_ci), GFP_KERNEL); + if (!ptr_asus_wmi_ci) + return -ENOMEM; + + asus_wmi_chip_info.info = ptr_asus_wmi_ci; + chip_info = &asus_wmi_chip_info; + + sensor_data->wmi.info_by_id = devm_kcalloc(dev, sensor_data->wmi.sensor_count, + sizeof(*sensor_data->wmi.info_by_id), + GFP_KERNEL); + + if (!sensor_data->wmi.info_by_id) + return -ENOMEM; + + for (type = 0; type < hwmon_max; type++) { + if (!nr_count[type]) + continue; + + err = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); + if (err) + return err; + + *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++; + + sensor_data->wmi.info[type] = devm_kcalloc(dev, + nr_count[type], + sizeof(*sensor_data->wmi.info), + GFP_KERNEL); + if (!sensor_data->wmi.info[type]) + return -ENOMEM; + } + + for (i = sensor_data->wmi.sensor_count - 1; i >= 0; i--) { + temp_sensor = devm_kzalloc(dev, sizeof(*temp_sensor), GFP_KERNEL); + if (!temp_sensor) + return -ENOMEM; + + err = asus_wmi_sensor_info(i, temp_sensor); + if (err) + continue; + + switch (temp_sensor->data_type) { + case TEMPERATURE_C: + case VOLTAGE: + case CURRENT: + case FAN_RPM: + case WATER_FLOW: + type = asus_data_types[temp_sensor->data_type]; + idx = --nr_count[type]; + *(sensor_data->wmi.info[type] + idx) = temp_sensor; + sensor_data->wmi.info_by_id[i] = temp_sensor; + break; + } + } + + dev_dbg(dev, "board has %d sensors", + sensor_data->wmi.sensor_count); + + hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_sensors", + sensor_data, chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwdev); +} + +static int asus_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct asus_wmi_sensors *sensor_data; + struct device *dev = &wdev->dev; + u32 version = 0; + + if (!dmi_check_system(asus_wmi_dmi_table)) + return -ENODEV; + + sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL); + if (!sensor_data) + return -ENOMEM; + + if (asus_wmi_get_version(&version)) + return -ENODEV; + + if (asus_wmi_get_item_count(&sensor_data->wmi.sensor_count)) + return -ENODEV; + + if (sensor_data->wmi.sensor_count <= 0 || version < 2) { + dev_info(dev, "version: %u with %d sensors is unsupported\n", + version, sensor_data->wmi.sensor_count); + + return -ENODEV; + } + + mutex_init(&sensor_data->lock); + + dev_set_drvdata(dev, sensor_data); + + return asus_wmi_configure_sensor_setup(dev, sensor_data); +} + +static const struct wmi_device_id asus_wmi_id_table[] = { + { ASUSWMI_MONITORING_GUID, NULL }, + { } +}; + +static struct wmi_driver asus_sensors_wmi_driver = { + .driver = { + .name = "asus_wmi_sensors", + }, + .id_table = asus_wmi_id_table, + .probe = asus_wmi_probe, +}; +module_wmi_driver(asus_sensors_wmi_driver); + +MODULE_AUTHOR("Ed Brindley <kernel@maidavale.org>"); +MODULE_DESCRIPTION("Asus WMI Sensors Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index 79b8df258371..4fd8de8022bc 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -37,7 +37,7 @@ struct atxp1_data { struct i2c_client *client; struct mutex update_lock; unsigned long last_updated; - u8 valid; + bool valid; struct { u8 vid; /* VID output register */ u8 cpu_vid; /* VID input from CPU */ @@ -63,7 +63,7 @@ static struct atxp1_data *atxp1_update_device(struct device *dev) data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1); data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2); - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -136,7 +136,7 @@ static ssize_t cpu0_vid_store(struct device *dev, ATXP1_VID, cvid | ATXP1_VIDENA); } - data->valid = 0; + data->valid = false; return count; } @@ -180,7 +180,7 @@ static ssize_t gpio1_store(struct device *dev, struct device_attribute *attr, i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value); - data->valid = 0; + data->valid = false; } return count; @@ -224,7 +224,7 @@ static ssize_t gpio2_store(struct device *dev, struct device_attribute *attr, i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value); - data->valid = 0; + data->valid = false; } return count; @@ -244,8 +244,7 @@ static struct attribute *atxp1_attrs[] = { }; ATTRIBUTE_GROUPS(atxp1); -static int atxp1_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int atxp1_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct atxp1_data *data; @@ -288,7 +287,7 @@ static struct i2c_driver atxp1_driver = { .driver = { .name = "atxp1", }, - .probe = atxp1_probe, + .probe_new = atxp1_probe, .id_table = atxp1_id, }; diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c new file mode 100644 index 000000000000..6724e0dd3088 --- /dev/null +++ b/drivers/hwmon/axi-fan-control.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Fan Control HDL CORE driver + * + * Copyright 2019 Analog Devices Inc. + */ +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/fpga/adi-axi-common.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +/* register map */ +#define ADI_REG_RSTN 0x0080 +#define ADI_REG_PWM_WIDTH 0x0084 +#define ADI_REG_TACH_PERIOD 0x0088 +#define ADI_REG_TACH_TOLERANCE 0x008c +#define ADI_REG_PWM_PERIOD 0x00c0 +#define ADI_REG_TACH_MEASUR 0x00c4 +#define ADI_REG_TEMPERATURE 0x00c8 +#define ADI_REG_TEMP_00_H 0x0100 +#define ADI_REG_TEMP_25_L 0x0104 +#define ADI_REG_TEMP_25_H 0x0108 +#define ADI_REG_TEMP_50_L 0x010c +#define ADI_REG_TEMP_50_H 0x0110 +#define ADI_REG_TEMP_75_L 0x0114 +#define ADI_REG_TEMP_75_H 0x0118 +#define ADI_REG_TEMP_100_L 0x011c + +#define ADI_REG_IRQ_MASK 0x0040 +#define ADI_REG_IRQ_PENDING 0x0044 +#define ADI_REG_IRQ_SRC 0x0048 + +/* IRQ sources */ +#define ADI_IRQ_SRC_PWM_CHANGED BIT(0) +#define ADI_IRQ_SRC_TACH_ERR BIT(1) +#define ADI_IRQ_SRC_TEMP_INCREASE BIT(2) +#define ADI_IRQ_SRC_NEW_MEASUR BIT(3) +#define ADI_IRQ_SRC_MASK GENMASK(3, 0) +#define ADI_IRQ_MASK_OUT_ALL 0xFFFFFFFFU + +#define SYSFS_PWM_MAX 255 + +struct axi_fan_control_data { + void __iomem *base; + struct device *hdev; + unsigned long clk_rate; + int irq; + /* pulses per revolution */ + u32 ppr; + bool hw_pwm_req; + bool update_tacho_params; + u8 fan_fault; +}; + +static inline void axi_iowrite(const u32 val, const u32 reg, + const struct axi_fan_control_data *ctl) +{ + iowrite32(val, ctl->base + reg); +} + +static inline u32 axi_ioread(const u32 reg, + const struct axi_fan_control_data *ctl) +{ + return ioread32(ctl->base + reg); +} + +/* + * The core calculates the temperature as: + * T = /raw * 509.3140064 / 65535) - 280.2308787 + */ +static ssize_t axi_fan_control_show(struct device *dev, struct device_attribute *da, char *buf) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + u32 temp = axi_ioread(attr->index, ctl); + + temp = DIV_ROUND_CLOSEST_ULL(temp * 509314ULL, 65535) - 280230; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t axi_fan_control_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + u32 temp; + int ret; + + ret = kstrtou32(buf, 10, &temp); + if (ret) + return ret; + + temp = DIV_ROUND_CLOSEST_ULL((temp + 280230) * 65535ULL, 509314); + axi_iowrite(temp, attr->index, ctl); + + return count; +} + +static long axi_fan_control_get_pwm_duty(const struct axi_fan_control_data *ctl) +{ + u32 pwm_width = axi_ioread(ADI_REG_PWM_WIDTH, ctl); + u32 pwm_period = axi_ioread(ADI_REG_PWM_PERIOD, ctl); + /* + * PWM_PERIOD is a RO register set by the core. It should never be 0. + * For now we are trusting the HW... + */ + return DIV_ROUND_CLOSEST(pwm_width * SYSFS_PWM_MAX, pwm_period); +} + +static int axi_fan_control_set_pwm_duty(const long val, + struct axi_fan_control_data *ctl) +{ + u32 pwm_period = axi_ioread(ADI_REG_PWM_PERIOD, ctl); + u32 new_width; + long __val = clamp_val(val, 0, SYSFS_PWM_MAX); + + new_width = DIV_ROUND_CLOSEST(__val * pwm_period, SYSFS_PWM_MAX); + + axi_iowrite(new_width, ADI_REG_PWM_WIDTH, ctl); + + return 0; +} + +static long axi_fan_control_get_fan_rpm(const struct axi_fan_control_data *ctl) +{ + const u32 tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl); + + if (tach == 0) + /* should we return error, EAGAIN maybe? */ + return 0; + /* + * The tacho period should be: + * TACH = 60/(ppr * rpm), where rpm is revolutions per second + * and ppr is pulses per revolution. + * Given the tacho period, we can multiply it by the input clock + * so that we know how many clocks we need to have this period. + * From this, we can derive the RPM value. + */ + return DIV_ROUND_CLOSEST(60 * ctl->clk_rate, ctl->ppr * tach); +} + +static int axi_fan_control_read_temp(struct device *dev, u32 attr, long *val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + long raw_temp; + + switch (attr) { + case hwmon_temp_input: + raw_temp = axi_ioread(ADI_REG_TEMPERATURE, ctl); + /* + * The formula for the temperature is: + * T = (ADC * 501.3743 / 2^bits) - 273.6777 + * It's multiplied by 1000 to have millidegrees as + * specified by the hwmon sysfs interface. + */ + *val = ((raw_temp * 501374) >> 16) - 273677; + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read_fan(struct device *dev, u32 attr, long *val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_fan_fault: + *val = ctl->fan_fault; + /* clear it now */ + ctl->fan_fault = 0; + return 0; + case hwmon_fan_input: + *val = axi_fan_control_get_fan_rpm(ctl); + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read_pwm(struct device *dev, u32 attr, long *val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_pwm_input: + *val = axi_fan_control_get_pwm_duty(ctl); + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_write_pwm(struct device *dev, u32 attr, long val) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_pwm_input: + return axi_fan_control_set_pwm_duty(val, ctl); + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_fan: + *str = "FAN"; + return 0; + case hwmon_temp: + *str = "SYSMON4"; + return 0; + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_fan: + return axi_fan_control_read_fan(dev, attr, val); + case hwmon_pwm: + return axi_fan_control_read_pwm(dev, attr, val); + case hwmon_temp: + return axi_fan_control_read_temp(dev, attr, val); + default: + return -ENOTSUPP; + } +} + +static int axi_fan_control_write(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + return axi_fan_control_write_pwm(dev, attr, val); + default: + return -ENOTSUPP; + } +} + +static umode_t axi_fan_control_fan_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + case hwmon_fan_label: + return 0444; + default: + return 0; + } +} + +static umode_t axi_fan_control_pwm_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + return 0; + } +} + +static umode_t axi_fan_control_temp_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + default: + return 0; + } +} + +static umode_t axi_fan_control_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return axi_fan_control_fan_is_visible(attr); + case hwmon_pwm: + return axi_fan_control_pwm_is_visible(attr); + case hwmon_temp: + return axi_fan_control_temp_is_visible(attr); + default: + return 0; + } +} + +/* + * This core has two main ways of changing the PWM duty cycle. It is done, + * either by a request from userspace (writing on pwm1_input) or by the + * core itself. When the change is done by the core, it will use predefined + * parameters to evaluate the tach signal and, on that case we cannot set them. + * On the other hand, when the request is done by the user, with some arbitrary + * value that the core does not now about, we have to provide the tach + * parameters so that, the core can evaluate the signal. On the IRQ handler we + * distinguish this by using the ADI_IRQ_SRC_TEMP_INCREASE interrupt. This tell + * us that the CORE requested a new duty cycle. After this, there is 5s delay + * on which the core waits for the fan rotation speed to stabilize. After this + * we get ADI_IRQ_SRC_PWM_CHANGED irq where we will decide if we need to set + * the tach parameters or not on the next tach measurement cycle (corresponding + * already to the ney duty cycle) based on the %ctl->hw_pwm_req flag. + */ +static irqreturn_t axi_fan_control_irq_handler(int irq, void *data) +{ + struct axi_fan_control_data *ctl = (struct axi_fan_control_data *)data; + u32 irq_pending = axi_ioread(ADI_REG_IRQ_PENDING, ctl); + u32 clear_mask; + + if (irq_pending & ADI_IRQ_SRC_TEMP_INCREASE) + /* hardware requested a new pwm */ + ctl->hw_pwm_req = true; + + if (irq_pending & ADI_IRQ_SRC_PWM_CHANGED) { + /* + * if the pwm changes on behalf of software, + * we need to provide new tacho parameters to the core. + * Wait for the next measurement for that... + */ + if (!ctl->hw_pwm_req) { + ctl->update_tacho_params = true; + } else { + ctl->hw_pwm_req = false; + hwmon_notify_event(ctl->hdev, hwmon_pwm, + hwmon_pwm_input, 0); + } + } + + if (irq_pending & ADI_IRQ_SRC_NEW_MEASUR) { + if (ctl->update_tacho_params) { + u32 new_tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl); + /* get 25% tolerance */ + u32 tach_tol = DIV_ROUND_CLOSEST(new_tach * 25, 100); + + /* set new tacho parameters */ + axi_iowrite(new_tach, ADI_REG_TACH_PERIOD, ctl); + axi_iowrite(tach_tol, ADI_REG_TACH_TOLERANCE, ctl); + ctl->update_tacho_params = false; + } + } + + if (irq_pending & ADI_IRQ_SRC_TACH_ERR) + ctl->fan_fault = 1; + + /* clear all interrupts */ + clear_mask = irq_pending & ADI_IRQ_SRC_MASK; + axi_iowrite(clear_mask, ADI_REG_IRQ_PENDING, ctl); + + return IRQ_HANDLED; +} + +static int axi_fan_control_init(struct axi_fan_control_data *ctl, + const struct device_node *np) +{ + int ret; + + /* get fan pulses per revolution */ + ret = of_property_read_u32(np, "pulses-per-revolution", &ctl->ppr); + if (ret) + return ret; + + /* 1, 2 and 4 are the typical and accepted values */ + if (ctl->ppr != 1 && ctl->ppr != 2 && ctl->ppr != 4) + return -EINVAL; + /* + * Enable all IRQs + */ + axi_iowrite(ADI_IRQ_MASK_OUT_ALL & + ~(ADI_IRQ_SRC_NEW_MEASUR | ADI_IRQ_SRC_TACH_ERR | + ADI_IRQ_SRC_PWM_CHANGED | ADI_IRQ_SRC_TEMP_INCREASE), + ADI_REG_IRQ_MASK, ctl); + + /* bring the device out of reset */ + axi_iowrite(0x01, ADI_REG_RSTN, ctl); + + return ret; +} + +static const struct hwmon_channel_info *axi_fan_control_info[] = { + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_LABEL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), + NULL +}; + +static const struct hwmon_ops axi_fan_control_hwmon_ops = { + .is_visible = axi_fan_control_is_visible, + .read = axi_fan_control_read, + .write = axi_fan_control_write, + .read_string = axi_fan_control_read_labels, +}; + +static const struct hwmon_chip_info axi_chip_info = { + .ops = &axi_fan_control_hwmon_ops, + .info = axi_fan_control_info, +}; + +/* temperature threshold below which PWM should be 0% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp_hyst, axi_fan_control, ADI_REG_TEMP_00_H); +/* temperature threshold above which PWM should be 25% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, axi_fan_control, ADI_REG_TEMP_25_L); +/* temperature threshold below which PWM should be 25% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp_hyst, axi_fan_control, ADI_REG_TEMP_25_H); +/* temperature threshold above which PWM should be 50% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, axi_fan_control, ADI_REG_TEMP_50_L); +/* temperature threshold below which PWM should be 50% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp_hyst, axi_fan_control, ADI_REG_TEMP_50_H); +/* temperature threshold above which PWM should be 75% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, axi_fan_control, ADI_REG_TEMP_75_L); +/* temperature threshold below which PWM should be 75% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp_hyst, axi_fan_control, ADI_REG_TEMP_75_H); +/* temperature threshold above which PWM should be 100% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, axi_fan_control, ADI_REG_TEMP_100_L); + +static struct attribute *axi_fan_control_attrs[] = { + &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(axi_fan_control); + +static const u32 version_1_0_0 = ADI_AXI_PCORE_VER(1, 0, 'a'); + +static const struct of_device_id axi_fan_control_of_match[] = { + { .compatible = "adi,axi-fan-control-1.00.a", + .data = (void *)&version_1_0_0}, + {}, +}; +MODULE_DEVICE_TABLE(of, axi_fan_control_of_match); + +static int axi_fan_control_probe(struct platform_device *pdev) +{ + struct axi_fan_control_data *ctl; + struct clk *clk; + const struct of_device_id *id; + const char *name = "axi_fan_control"; + u32 version; + int ret; + + id = of_match_node(axi_fan_control_of_match, pdev->dev.of_node); + if (!id) + return -EINVAL; + + ctl = devm_kzalloc(&pdev->dev, sizeof(*ctl), GFP_KERNEL); + if (!ctl) + return -ENOMEM; + + ctl->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctl->base)) + return PTR_ERR(ctl->base); + + clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "clk_get failed with %ld\n", PTR_ERR(clk)); + return PTR_ERR(clk); + } + + ctl->clk_rate = clk_get_rate(clk); + if (!ctl->clk_rate) + return -EINVAL; + + version = axi_ioread(ADI_AXI_REG_VERSION, ctl); + if (ADI_AXI_PCORE_VER_MAJOR(version) != + ADI_AXI_PCORE_VER_MAJOR((*(u32 *)id->data))) { + dev_err(&pdev->dev, "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", + ADI_AXI_PCORE_VER_MAJOR((*(u32 *)id->data)), + ADI_AXI_PCORE_VER_MINOR((*(u32 *)id->data)), + ADI_AXI_PCORE_VER_PATCH((*(u32 *)id->data)), + ADI_AXI_PCORE_VER_MAJOR(version), + ADI_AXI_PCORE_VER_MINOR(version), + ADI_AXI_PCORE_VER_PATCH(version)); + return -ENODEV; + } + + ctl->irq = platform_get_irq(pdev, 0); + if (ctl->irq < 0) + return ctl->irq; + + ret = devm_request_threaded_irq(&pdev->dev, ctl->irq, NULL, + axi_fan_control_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + pdev->driver_override, ctl); + if (ret) { + dev_err(&pdev->dev, "failed to request an irq, %d", ret); + return ret; + } + + ret = axi_fan_control_init(ctl, pdev->dev.of_node); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize device\n"); + return ret; + } + + ctl->hdev = devm_hwmon_device_register_with_info(&pdev->dev, + name, + ctl, + &axi_chip_info, + axi_fan_control_groups); + + return PTR_ERR_OR_ZERO(ctl->hdev); +} + +static struct platform_driver axi_fan_control_driver = { + .driver = { + .name = "axi_fan_control_driver", + .of_match_table = axi_fan_control_of_match, + }, + .probe = axi_fan_control_probe, +}; +module_platform_driver(axi_fan_control_driver); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices Fan Control HDL CORE driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c new file mode 100644 index 000000000000..21ab172774ec --- /dev/null +++ b/drivers/hwmon/bt1-pvt.c @@ -0,0 +1,1178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Authors: + * Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru> + * Serge Semin <Sergey.Semin@baikalelectronics.ru> + * + * Baikal-T1 Process, Voltage, Temperature sensor driver + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/ktime.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/polynomial.h> +#include <linux/seqlock.h> +#include <linux/sysfs.h> +#include <linux/types.h> + +#include "bt1-pvt.h" + +/* + * For the sake of the code simplification we created the sensors info table + * with the sensor names, activation modes, threshold registers base address + * and the thresholds bit fields. + */ +static const struct pvt_sensor_info pvt_info[] = { + PVT_SENSOR_INFO(0, "CPU Core Temperature", hwmon_temp, TEMP, TTHRES), + PVT_SENSOR_INFO(0, "CPU Core Voltage", hwmon_in, VOLT, VTHRES), + PVT_SENSOR_INFO(1, "CPU Core Low-Vt", hwmon_in, LVT, LTHRES), + PVT_SENSOR_INFO(2, "CPU Core High-Vt", hwmon_in, HVT, HTHRES), + PVT_SENSOR_INFO(3, "CPU Core Standard-Vt", hwmon_in, SVT, STHRES), +}; + +/* + * The original translation formulae of the temperature (in degrees of Celsius) + * to PVT data and vice-versa are following: + * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) + + * 1.7204e2, + * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) + + * 3.1020e-1*(N^1) - 4.838e1, + * where T = [-48.380, 147.438]C and N = [0, 1023]. + * They must be accordingly altered to be suitable for the integer arithmetics. + * The technique is called 'factor redistribution', which just makes sure the + * multiplications and divisions are made so to have a result of the operations + * within the integer numbers limit. In addition we need to translate the + * formulae to accept millidegrees of Celsius. Here what they look like after + * the alterations: + * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T + + * 17204e2) / 1e4, + * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D - + * 48380, + * where T = [-48380, 147438] mC and N = [0, 1023]. + */ +static const struct polynomial __maybe_unused poly_temp_to_N = { + .total_divider = 10000, + .terms = { + {4, 18322, 10000, 10000}, + {3, 2343, 10000, 10}, + {2, 87018, 10000, 10}, + {1, 39269, 1000, 1}, + {0, 1720400, 1, 1} + } +}; + +static const struct polynomial poly_N_to_temp = { + .total_divider = 1, + .terms = { + {4, -16743, 1000, 1}, + {3, 81542, 1000, 1}, + {2, -182010, 1000, 1}, + {1, 310200, 1000, 1}, + {0, -48380, 1, 1} + } +}; + +/* + * Similar alterations are performed for the voltage conversion equations. + * The original formulae are: + * N = 1.8658e3*V - 1.1572e3, + * V = (N + 1.1572e3) / 1.8658e3, + * where V = [0.620, 1.168] V and N = [0, 1023]. + * After the optimization they looks as follows: + * N = (18658e-3*V - 11572) / 10, + * V = N * 10^5 / 18658 + 11572 * 10^4 / 18658. + */ +static const struct polynomial __maybe_unused poly_volt_to_N = { + .total_divider = 10, + .terms = { + {1, 18658, 1000, 1}, + {0, -11572, 1, 1} + } +}; + +static const struct polynomial poly_N_to_volt = { + .total_divider = 10, + .terms = { + {1, 100000, 18658, 1}, + {0, 115720000, 1, 18658} + } +}; + +static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data) +{ + u32 old; + + old = readl_relaxed(reg); + writel((old & ~mask) | (data & mask), reg); + + return old & mask; +} + +/* + * Baikal-T1 PVT mode can be updated only when the controller is disabled. + * So first we disable it, then set the new mode together with the controller + * getting back enabled. The same concerns the temperature trim and + * measurements timeout. If it is necessary the interface mutex is supposed + * to be locked at the time the operations are performed. + */ +static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode) +{ + u32 old; + + mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode); + + old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN, + mode | old); +} + +static inline u32 pvt_calc_trim(long temp) +{ + temp = clamp_val(temp, 0, PVT_TRIM_TEMP); + + return DIV_ROUND_UP(temp, PVT_TRIM_STEP); +} + +static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim) +{ + u32 old; + + trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim); + + old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN, + trim | old); +} + +static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout) +{ + u32 old; + + old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); + writel(tout, pvt->regs + PVT_TTIMEOUT); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old); +} + +/* + * This driver can optionally provide the hwmon alarms for each sensor the PVT + * controller supports. The alarms functionality is made compile-time + * configurable due to the hardware interface implementation peculiarity + * described further in this comment. So in case if alarms are unnecessary in + * your system design it's recommended to have them disabled to prevent the PVT + * IRQs being periodically raised to get the data cache/alarms status up to + * date. + * + * Baikal-T1 PVT embedded controller is based on the Analog Bits PVT sensor, + * but is equipped with a dedicated control wrapper. It exposes the PVT + * sub-block registers space via the APB3 bus. In addition the wrapper provides + * a common interrupt vector of the sensors conversion completion events and + * threshold value alarms. Alas the wrapper interface hasn't been fully thought + * through. There is only one sensor can be activated at a time, for which the + * thresholds comparator is enabled right after the data conversion is + * completed. Due to this if alarms need to be implemented for all available + * sensors we can't just set the thresholds and enable the interrupts. We need + * to enable the sensors one after another and let the controller to detect + * the alarms by itself at each conversion. This also makes pointless to handle + * the alarms interrupts, since in occasion they happen synchronously with + * data conversion completion. The best driver design would be to have the + * completion interrupts enabled only and keep the converted value in the + * driver data cache. This solution is implemented if hwmon alarms are enabled + * in this driver. In case if the alarms are disabled, the conversion is + * performed on demand at the time a sensors input file is read. + */ + +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + +#define pvt_hard_isr NULL + +static irqreturn_t pvt_soft_isr(int irq, void *data) +{ + const struct pvt_sensor_info *info; + struct pvt_hwmon *pvt = data; + struct pvt_cache *cache; + u32 val, thres_sts, old; + + /* + * DVALID bit will be cleared by reading the data. We need to save the + * status before the next conversion happens. Threshold events will be + * handled a bit later. + */ + thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT); + + /* + * Then lets recharge the PVT interface with the next sampling mode. + * Lock the interface mutex to serialize trim, timeouts and alarm + * thresholds settings. + */ + cache = &pvt->cache[pvt->sensor]; + info = &pvt_info[pvt->sensor]; + pvt->sensor = (pvt->sensor == PVT_SENSOR_LAST) ? + PVT_SENSOR_FIRST : (pvt->sensor + 1); + + /* + * For some reason we have to mask the interrupt before changing the + * mode, otherwise sometimes the temperature mode doesn't get + * activated even though the actual mode in the ctrl register + * corresponds to one. Then we read the data. By doing so we also + * recharge the data conversion. After this the mode corresponding + * to the next sensor in the row is set. Finally we enable the + * interrupts back. + */ + mutex_lock(&pvt->iface_mtx); + + old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, + PVT_INTR_DVALID); + + val = readl(pvt->regs + PVT_DATA); + + pvt_set_mode(pvt, pvt_info[pvt->sensor].mode); + + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old); + + mutex_unlock(&pvt->iface_mtx); + + /* + * We can now update the data cache with data just retrieved from the + * sensor. Lock write-seqlock to make sure the reader has a coherent + * data. + */ + write_seqlock(&cache->data_seqlock); + + cache->data = FIELD_GET(PVT_DATA_DATA_MASK, val); + + write_sequnlock(&cache->data_seqlock); + + /* + * While PVT core is doing the next mode data conversion, we'll check + * whether the alarms were triggered for the current sensor. Note that + * according to the documentation only one threshold IRQ status can be + * set at a time, that's why if-else statement is utilized. + */ + if ((thres_sts & info->thres_sts_lo) ^ cache->thres_sts_lo) { + WRITE_ONCE(cache->thres_sts_lo, thres_sts & info->thres_sts_lo); + hwmon_notify_event(pvt->hwmon, info->type, info->attr_min_alarm, + info->channel); + } else if ((thres_sts & info->thres_sts_hi) ^ cache->thres_sts_hi) { + WRITE_ONCE(cache->thres_sts_hi, thres_sts & info->thres_sts_hi); + hwmon_notify_event(pvt->hwmon, info->type, info->attr_max_alarm, + info->channel); + } + + return IRQ_HANDLED; +} + +static inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type) +{ + return 0644; +} + +static inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type) +{ + return 0444; +} + +static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + long *val) +{ + struct pvt_cache *cache = &pvt->cache[type]; + unsigned int seq; + u32 data; + + do { + seq = read_seqbegin(&cache->data_seqlock); + data = cache->data; + } while (read_seqretry(&cache->data_seqlock, seq)); + + if (type == PVT_TEMP) + *val = polynomial_calc(&poly_N_to_temp, data); + else + *val = polynomial_calc(&poly_N_to_volt, data); + + return 0; +} + +static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + bool is_low, long *val) +{ + u32 data; + + /* No need in serialization, since it is just read from MMIO. */ + data = readl(pvt->regs + pvt_info[type].thres_base); + + if (is_low) + data = FIELD_GET(PVT_THRES_LO_MASK, data); + else + data = FIELD_GET(PVT_THRES_HI_MASK, data); + + if (type == PVT_TEMP) + *val = polynomial_calc(&poly_N_to_temp, data); + else + *val = polynomial_calc(&poly_N_to_volt, data); + + return 0; +} + +static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + bool is_low, long val) +{ + u32 data, limit, mask; + int ret; + + if (type == PVT_TEMP) { + val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX); + data = polynomial_calc(&poly_temp_to_N, val); + } else { + val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX); + data = polynomial_calc(&poly_volt_to_N, val); + } + + /* Serialize limit update, since a part of the register is changed. */ + ret = mutex_lock_interruptible(&pvt->iface_mtx); + if (ret) + return ret; + + /* Make sure the upper and lower ranges don't intersect. */ + limit = readl(pvt->regs + pvt_info[type].thres_base); + if (is_low) { + limit = FIELD_GET(PVT_THRES_HI_MASK, limit); + data = clamp_val(data, PVT_DATA_MIN, limit); + data = FIELD_PREP(PVT_THRES_LO_MASK, data); + mask = PVT_THRES_LO_MASK; + } else { + limit = FIELD_GET(PVT_THRES_LO_MASK, limit); + data = clamp_val(data, limit, PVT_DATA_MAX); + data = FIELD_PREP(PVT_THRES_HI_MASK, data); + mask = PVT_THRES_HI_MASK; + } + + pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data); + + mutex_unlock(&pvt->iface_mtx); + + return 0; +} + +static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + bool is_low, long *val) +{ + if (is_low) + *val = !!READ_ONCE(pvt->cache[type].thres_sts_lo); + else + *val = !!READ_ONCE(pvt->cache[type].thres_sts_hi); + + return 0; +} + +static const struct hwmon_channel_info *pvt_channel_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL | + HWMON_T_MIN | HWMON_T_MIN_ALARM | + HWMON_T_MAX | HWMON_T_MAX_ALARM | + HWMON_T_OFFSET), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL | + HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_MAX | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LABEL | + HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_MAX | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LABEL | + HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_MAX | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LABEL | + HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_MAX | HWMON_I_MAX_ALARM), + NULL +}; + +#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ + +static irqreturn_t pvt_hard_isr(int irq, void *data) +{ + struct pvt_hwmon *pvt = data; + struct pvt_cache *cache; + u32 val; + + /* + * Mask the DVALID interrupt so after exiting from the handler a + * repeated conversion wouldn't happen. + */ + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, + PVT_INTR_DVALID); + + /* + * Nothing special for alarm-less driver. Just read the data, update + * the cache and notify a waiter of this event. + */ + val = readl(pvt->regs + PVT_DATA); + if (!(val & PVT_DATA_VALID)) { + dev_err(pvt->dev, "Got IRQ when data isn't valid\n"); + return IRQ_HANDLED; + } + + cache = &pvt->cache[pvt->sensor]; + + WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val)); + + complete(&cache->conversion); + + return IRQ_HANDLED; +} + +#define pvt_soft_isr NULL + +static inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type) +{ + return 0; +} + +static inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type) +{ + return 0; +} + +static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + long *val) +{ + struct pvt_cache *cache = &pvt->cache[type]; + unsigned long timeout; + u32 data; + int ret; + + /* + * Lock PVT conversion interface until data cache is updated. The + * data read procedure is following: set the requested PVT sensor + * mode, enable IRQ and conversion, wait until conversion is finished, + * then disable conversion and IRQ, and read the cached data. + */ + ret = mutex_lock_interruptible(&pvt->iface_mtx); + if (ret) + return ret; + + pvt->sensor = type; + pvt_set_mode(pvt, pvt_info[type].mode); + + /* + * Unmask the DVALID interrupt and enable the sensors conversions. + * Do the reverse procedure when conversion is done. + */ + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); + + /* + * Wait with timeout since in case if the sensor is suddenly powered + * down the request won't be completed and the caller will hang up on + * this procedure until the power is back up again. Multiply the + * timeout by the factor of two to prevent a false timeout. + */ + timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout)); + ret = wait_for_completion_timeout(&cache->conversion, timeout); + + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, + PVT_INTR_DVALID); + + data = READ_ONCE(cache->data); + + mutex_unlock(&pvt->iface_mtx); + + if (!ret) + return -ETIMEDOUT; + + if (type == PVT_TEMP) + *val = polynomial_calc(&poly_N_to_temp, data); + else + *val = polynomial_calc(&poly_N_to_volt, data); + + return 0; +} + +static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + bool is_low, long *val) +{ + return -EOPNOTSUPP; +} + +static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + bool is_low, long val) +{ + return -EOPNOTSUPP; +} + +static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type, + bool is_low, long *val) +{ + return -EOPNOTSUPP; +} + +static const struct hwmon_channel_info *pvt_channel_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL | + HWMON_T_OFFSET), + 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), + NULL +}; + +#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ + +static inline bool pvt_hwmon_channel_is_valid(enum hwmon_sensor_types type, + int ch) +{ + switch (type) { + case hwmon_temp: + if (ch < 0 || ch >= PVT_TEMP_CHS) + return false; + break; + case hwmon_in: + if (ch < 0 || ch >= PVT_VOLT_CHS) + return false; + break; + default: + break; + } + + /* The rest of the types are independent from the channel number. */ + return true; +} + +static umode_t pvt_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int ch) +{ + if (!pvt_hwmon_channel_is_valid(type, ch)) + return 0; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_type: + case hwmon_temp_label: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + return pvt_limit_is_visible(ch); + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + return pvt_alarm_is_visible(ch); + case hwmon_temp_offset: + return 0644; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + return 0444; + case hwmon_in_min: + case hwmon_in_max: + return pvt_limit_is_visible(PVT_VOLT + ch); + case hwmon_in_min_alarm: + case hwmon_in_max_alarm: + return pvt_alarm_is_visible(PVT_VOLT + ch); + } + break; + default: + break; + } + + return 0; +} + +static int pvt_read_trim(struct pvt_hwmon *pvt, long *val) +{ + u32 data; + + data = readl(pvt->regs + PVT_CTRL); + *val = FIELD_GET(PVT_CTRL_TRIM_MASK, data) * PVT_TRIM_STEP; + + return 0; +} + +static int pvt_write_trim(struct pvt_hwmon *pvt, long val) +{ + u32 trim; + int ret; + + /* + * Serialize trim update, since a part of the register is changed and + * the controller is supposed to be disabled during this operation. + */ + ret = mutex_lock_interruptible(&pvt->iface_mtx); + if (ret) + return ret; + + trim = pvt_calc_trim(val); + pvt_set_trim(pvt, trim); + + mutex_unlock(&pvt->iface_mtx); + + return 0; +} + +static int pvt_read_timeout(struct pvt_hwmon *pvt, long *val) +{ + int ret; + + ret = mutex_lock_interruptible(&pvt->iface_mtx); + if (ret) + return ret; + + /* Return the result in msec as hwmon sysfs interface requires. */ + *val = ktime_to_ms(pvt->timeout); + + mutex_unlock(&pvt->iface_mtx); + + return 0; +} + +static int pvt_write_timeout(struct pvt_hwmon *pvt, long val) +{ + unsigned long rate; + ktime_t kt, cache; + u32 data; + int ret; + + rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk); + if (!rate) + return -ENODEV; + + /* + * If alarms are enabled, the requested timeout must be divided + * between all available sensors to have the requested delay + * applicable to each individual sensor. + */ + cache = kt = ms_to_ktime(val); +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + kt = ktime_divns(kt, PVT_SENSORS_NUM); +#endif + + /* + * Subtract a constant lag, which always persists due to the limited + * PVT sampling rate. Make sure the timeout is not negative. + */ + kt = ktime_sub_ns(kt, PVT_TOUT_MIN); + if (ktime_to_ns(kt) < 0) + kt = ktime_set(0, 0); + + /* + * Finally recalculate the timeout in terms of the reference clock + * period. + */ + data = ktime_divns(kt * rate, NSEC_PER_SEC); + + /* + * Update the measurements delay, but lock the interface first, since + * we have to disable PVT in order to have the new delay actually + * updated. + */ + ret = mutex_lock_interruptible(&pvt->iface_mtx); + if (ret) + return ret; + + pvt_set_tout(pvt, data); + pvt->timeout = cache; + + mutex_unlock(&pvt->iface_mtx); + + return 0; +} + +static int pvt_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int ch, long *val) +{ + struct pvt_hwmon *pvt = dev_get_drvdata(dev); + + if (!pvt_hwmon_channel_is_valid(type, ch)) + return -EINVAL; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return pvt_read_timeout(pvt, val); + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return pvt_read_data(pvt, ch, val); + case hwmon_temp_type: + *val = 1; + return 0; + case hwmon_temp_min: + return pvt_read_limit(pvt, ch, true, val); + case hwmon_temp_max: + return pvt_read_limit(pvt, ch, false, val); + case hwmon_temp_min_alarm: + return pvt_read_alarm(pvt, ch, true, val); + case hwmon_temp_max_alarm: + return pvt_read_alarm(pvt, ch, false, val); + case hwmon_temp_offset: + return pvt_read_trim(pvt, val); + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + return pvt_read_data(pvt, PVT_VOLT + ch, val); + case hwmon_in_min: + return pvt_read_limit(pvt, PVT_VOLT + ch, true, val); + case hwmon_in_max: + return pvt_read_limit(pvt, PVT_VOLT + ch, false, val); + case hwmon_in_min_alarm: + return pvt_read_alarm(pvt, PVT_VOLT + ch, true, val); + case hwmon_in_max_alarm: + return pvt_read_alarm(pvt, PVT_VOLT + ch, false, val); + } + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static int pvt_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int ch, const char **str) +{ + if (!pvt_hwmon_channel_is_valid(type, ch)) + return -EINVAL; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = pvt_info[ch].label; + return 0; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_label: + *str = pvt_info[PVT_VOLT + ch].label; + return 0; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static int pvt_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int ch, long val) +{ + struct pvt_hwmon *pvt = dev_get_drvdata(dev); + + if (!pvt_hwmon_channel_is_valid(type, ch)) + return -EINVAL; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return pvt_write_timeout(pvt, val); + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_min: + return pvt_write_limit(pvt, ch, true, val); + case hwmon_temp_max: + return pvt_write_limit(pvt, ch, false, val); + case hwmon_temp_offset: + return pvt_write_trim(pvt, val); + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_min: + return pvt_write_limit(pvt, PVT_VOLT + ch, true, val); + case hwmon_in_max: + return pvt_write_limit(pvt, PVT_VOLT + ch, false, val); + } + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static const struct hwmon_ops pvt_hwmon_ops = { + .is_visible = pvt_hwmon_is_visible, + .read = pvt_hwmon_read, + .read_string = pvt_hwmon_read_string, + .write = pvt_hwmon_write +}; + +static const struct hwmon_chip_info pvt_hwmon_info = { + .ops = &pvt_hwmon_ops, + .info = pvt_channel_info +}; + +static void pvt_clear_data(void *data) +{ + struct pvt_hwmon *pvt = data; +#if !defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + int idx; + + for (idx = 0; idx < PVT_SENSORS_NUM; ++idx) + complete_all(&pvt->cache[idx].conversion); +#endif + + mutex_destroy(&pvt->iface_mtx); +} + +static struct pvt_hwmon *pvt_create_data(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pvt_hwmon *pvt; + int ret, idx; + + pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); + if (!pvt) + return ERR_PTR(-ENOMEM); + + ret = devm_add_action(dev, pvt_clear_data, pvt); + if (ret) { + dev_err(dev, "Can't add PVT data clear action\n"); + return ERR_PTR(ret); + } + + pvt->dev = dev; + pvt->sensor = PVT_SENSOR_FIRST; + mutex_init(&pvt->iface_mtx); + +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + for (idx = 0; idx < PVT_SENSORS_NUM; ++idx) + seqlock_init(&pvt->cache[idx].data_seqlock); +#else + for (idx = 0; idx < PVT_SENSORS_NUM; ++idx) + init_completion(&pvt->cache[idx].conversion); +#endif + + return pvt; +} + +static int pvt_request_regs(struct pvt_hwmon *pvt) +{ + struct platform_device *pdev = to_platform_device(pvt->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(pvt->dev, "Couldn't find PVT memresource\n"); + return -EINVAL; + } + + pvt->regs = devm_ioremap_resource(pvt->dev, res); + if (IS_ERR(pvt->regs)) + return PTR_ERR(pvt->regs); + + return 0; +} + +static void pvt_disable_clks(void *data) +{ + struct pvt_hwmon *pvt = data; + + clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks); +} + +static int pvt_request_clks(struct pvt_hwmon *pvt) +{ + int ret; + + pvt->clks[PVT_CLOCK_APB].id = "pclk"; + pvt->clks[PVT_CLOCK_REF].id = "ref"; + + ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks); + if (ret) { + dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks); + if (ret) { + dev_err(pvt->dev, "Couldn't enable the PVT clocks\n"); + return ret; + } + + ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt); + if (ret) { + dev_err(pvt->dev, "Can't add PVT clocks disable action\n"); + return ret; + } + + return 0; +} + +static int pvt_check_pwr(struct pvt_hwmon *pvt) +{ + unsigned long tout; + int ret = 0; + u32 data; + + /* + * Test out the sensor conversion functionality. If it is not done on + * time then the domain must have been unpowered and we won't be able + * to use the device later in this driver. + * Note If the power source is lost during the normal driver work the + * data read procedure will either return -ETIMEDOUT (for the + * alarm-less driver configuration) or just stop the repeated + * conversion. In the later case alas we won't be able to detect the + * problem. + */ + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); + pvt_set_tout(pvt, 0); + readl(pvt->regs + PVT_DATA); + + tout = PVT_TOUT_MIN / NSEC_PER_USEC; + usleep_range(tout, 2 * tout); + + data = readl(pvt->regs + PVT_DATA); + if (!(data & PVT_DATA_VALID)) { + ret = -ENODEV; + dev_err(pvt->dev, "Sensor is powered down\n"); + } + + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); + + return ret; +} + +static int pvt_init_iface(struct pvt_hwmon *pvt) +{ + unsigned long rate; + u32 trim, temp; + + rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk); + if (!rate) { + dev_err(pvt->dev, "Invalid reference clock rate\n"); + return -ENODEV; + } + + /* + * Make sure all interrupts and controller are disabled so not to + * accidentally have ISR executed before the driver data is fully + * initialized. Clear the IRQ status as well. + */ + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); + readl(pvt->regs + PVT_CLR_INTR); + readl(pvt->regs + PVT_DATA); + + /* Setup default sensor mode, timeout and temperature trim. */ + pvt_set_mode(pvt, pvt_info[pvt->sensor].mode); + pvt_set_tout(pvt, PVT_TOUT_DEF); + + /* + * Preserve the current ref-clock based delay (Ttotal) between the + * sensors data samples in the driver data so not to recalculate it + * each time on the data requests and timeout reads. It consists of the + * delay introduced by the internal ref-clock timer (N / Fclk) and the + * constant timeout caused by each conversion latency (Tmin): + * Ttotal = N / Fclk + Tmin + * If alarms are enabled the sensors are polled one after another and + * in order to get the next measurement of a particular sensor the + * caller will have to wait for at most until all the others are + * polled. In that case the formulae will look a bit different: + * Ttotal = 5 * (N / Fclk + Tmin) + */ +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + pvt->timeout = ktime_set(PVT_SENSORS_NUM * PVT_TOUT_DEF, 0); + pvt->timeout = ktime_divns(pvt->timeout, rate); + pvt->timeout = ktime_add_ns(pvt->timeout, PVT_SENSORS_NUM * PVT_TOUT_MIN); +#else + pvt->timeout = ktime_set(PVT_TOUT_DEF, 0); + pvt->timeout = ktime_divns(pvt->timeout, rate); + pvt->timeout = ktime_add_ns(pvt->timeout, PVT_TOUT_MIN); +#endif + + trim = PVT_TRIM_DEF; + if (!of_property_read_u32(pvt->dev->of_node, + "baikal,pvt-temp-offset-millicelsius", &temp)) + trim = pvt_calc_trim(temp); + + pvt_set_trim(pvt, trim); + + return 0; +} + +static int pvt_request_irq(struct pvt_hwmon *pvt) +{ + struct platform_device *pdev = to_platform_device(pvt->dev); + int ret; + + pvt->irq = platform_get_irq(pdev, 0); + if (pvt->irq < 0) + return pvt->irq; + + ret = devm_request_threaded_irq(pvt->dev, pvt->irq, + pvt_hard_isr, pvt_soft_isr, +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + IRQF_SHARED | IRQF_TRIGGER_HIGH | + IRQF_ONESHOT, +#else + IRQF_SHARED | IRQF_TRIGGER_HIGH, +#endif + "pvt", pvt); + if (ret) { + dev_err(pvt->dev, "Couldn't request PVT IRQ\n"); + return ret; + } + + return 0; +} + +static int pvt_create_hwmon(struct pvt_hwmon *pvt) +{ + pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", pvt, + &pvt_hwmon_info, NULL); + if (IS_ERR(pvt->hwmon)) { + dev_err(pvt->dev, "Couldn't create hwmon device\n"); + return PTR_ERR(pvt->hwmon); + } + + return 0; +} + +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + +static void pvt_disable_iface(void *data) +{ + struct pvt_hwmon *pvt = data; + + mutex_lock(&pvt->iface_mtx); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, + PVT_INTR_DVALID); + mutex_unlock(&pvt->iface_mtx); +} + +static int pvt_enable_iface(struct pvt_hwmon *pvt) +{ + int ret; + + ret = devm_add_action(pvt->dev, pvt_disable_iface, pvt); + if (ret) { + dev_err(pvt->dev, "Can't add PVT disable interface action\n"); + return ret; + } + + /* + * Enable sensors data conversion and IRQ. We need to lock the + * interface mutex since hwmon has just been created and the + * corresponding sysfs files are accessible from user-space, + * which theoretically may cause races. + */ + mutex_lock(&pvt->iface_mtx); + pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0); + pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); + mutex_unlock(&pvt->iface_mtx); + + return 0; +} + +#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ + +static int pvt_enable_iface(struct pvt_hwmon *pvt) +{ + return 0; +} + +#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */ + +static int pvt_probe(struct platform_device *pdev) +{ + struct pvt_hwmon *pvt; + int ret; + + pvt = pvt_create_data(pdev); + if (IS_ERR(pvt)) + return PTR_ERR(pvt); + + ret = pvt_request_regs(pvt); + if (ret) + return ret; + + ret = pvt_request_clks(pvt); + if (ret) + return ret; + + ret = pvt_check_pwr(pvt); + if (ret) + return ret; + + ret = pvt_init_iface(pvt); + if (ret) + return ret; + + ret = pvt_request_irq(pvt); + if (ret) + return ret; + + ret = pvt_create_hwmon(pvt); + if (ret) + return ret; + + ret = pvt_enable_iface(pvt); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id pvt_of_match[] = { + { .compatible = "baikal,bt1-pvt" }, + { } +}; +MODULE_DEVICE_TABLE(of, pvt_of_match); + +static struct platform_driver pvt_driver = { + .probe = pvt_probe, + .driver = { + .name = "bt1-pvt", + .of_match_table = pvt_of_match + } +}; +module_platform_driver(pvt_driver); + +MODULE_AUTHOR("Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>"); +MODULE_DESCRIPTION("Baikal-T1 PVT driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/bt1-pvt.h b/drivers/hwmon/bt1-pvt.h new file mode 100644 index 000000000000..93b8dd5e7c94 --- /dev/null +++ b/drivers/hwmon/bt1-pvt.h @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Baikal-T1 Process, Voltage, Temperature sensor driver + */ +#ifndef __HWMON_BT1_PVT_H__ +#define __HWMON_BT1_PVT_H__ + +#include <linux/completion.h> +#include <linux/hwmon.h> +#include <linux/kernel.h> +#include <linux/ktime.h> +#include <linux/mutex.h> +#include <linux/seqlock.h> + +/* Baikal-T1 PVT registers and their bitfields */ +#define PVT_CTRL 0x00 +#define PVT_CTRL_EN BIT(0) +#define PVT_CTRL_MODE_FLD 1 +#define PVT_CTRL_MODE_MASK GENMASK(3, PVT_CTRL_MODE_FLD) +#define PVT_CTRL_MODE_TEMP 0x0 +#define PVT_CTRL_MODE_VOLT 0x1 +#define PVT_CTRL_MODE_LVT 0x2 +#define PVT_CTRL_MODE_HVT 0x4 +#define PVT_CTRL_MODE_SVT 0x6 +#define PVT_CTRL_TRIM_FLD 4 +#define PVT_CTRL_TRIM_MASK GENMASK(8, PVT_CTRL_TRIM_FLD) +#define PVT_DATA 0x04 +#define PVT_DATA_VALID BIT(10) +#define PVT_DATA_DATA_FLD 0 +#define PVT_DATA_DATA_MASK GENMASK(9, PVT_DATA_DATA_FLD) +#define PVT_TTHRES 0x08 +#define PVT_VTHRES 0x0C +#define PVT_LTHRES 0x10 +#define PVT_HTHRES 0x14 +#define PVT_STHRES 0x18 +#define PVT_THRES_LO_FLD 0 +#define PVT_THRES_LO_MASK GENMASK(9, PVT_THRES_LO_FLD) +#define PVT_THRES_HI_FLD 10 +#define PVT_THRES_HI_MASK GENMASK(19, PVT_THRES_HI_FLD) +#define PVT_TTIMEOUT 0x1C +#define PVT_INTR_STAT 0x20 +#define PVT_INTR_MASK 0x24 +#define PVT_RAW_INTR_STAT 0x28 +#define PVT_INTR_DVALID BIT(0) +#define PVT_INTR_TTHRES_LO BIT(1) +#define PVT_INTR_TTHRES_HI BIT(2) +#define PVT_INTR_VTHRES_LO BIT(3) +#define PVT_INTR_VTHRES_HI BIT(4) +#define PVT_INTR_LTHRES_LO BIT(5) +#define PVT_INTR_LTHRES_HI BIT(6) +#define PVT_INTR_HTHRES_LO BIT(7) +#define PVT_INTR_HTHRES_HI BIT(8) +#define PVT_INTR_STHRES_LO BIT(9) +#define PVT_INTR_STHRES_HI BIT(10) +#define PVT_INTR_ALL GENMASK(10, 0) +#define PVT_CLR_INTR 0x2C + +/* + * PVT sensors-related limits and default values + * @PVT_TEMP_MIN: Minimal temperature in millidegrees of Celsius. + * @PVT_TEMP_MAX: Maximal temperature in millidegrees of Celsius. + * @PVT_TEMP_CHS: Number of temperature hwmon channels. + * @PVT_VOLT_MIN: Minimal voltage in mV. + * @PVT_VOLT_MAX: Maximal voltage in mV. + * @PVT_VOLT_CHS: Number of voltage hwmon channels. + * @PVT_DATA_MIN: Minimal PVT raw data value. + * @PVT_DATA_MAX: Maximal PVT raw data value. + * @PVT_TRIM_MIN: Minimal temperature sensor trim value. + * @PVT_TRIM_MAX: Maximal temperature sensor trim value. + * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value + * when one is determined for Baikal-T1 SoC). + * @PVT_TRIM_TEMP: Maximum temperature encoded by the trim factor. + * @PVT_TRIM_STEP: Temperature stride corresponding to the trim value. + * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds. + * @PVT_TOUT_DEF: Default data measurements timeout. In case if alarms are + * activated the PVT IRQ is enabled to be raised after each + * conversion in order to have the thresholds checked and the + * converted value cached. Too frequent conversions may cause + * the system CPU overload. Lets set the 50ms delay between + * them by default to prevent this. + */ +#define PVT_TEMP_MIN -48380L +#define PVT_TEMP_MAX 147438L +#define PVT_TEMP_CHS 1 +#define PVT_VOLT_MIN 620L +#define PVT_VOLT_MAX 1168L +#define PVT_VOLT_CHS 4 +#define PVT_DATA_MIN 0 +#define PVT_DATA_MAX (PVT_DATA_DATA_MASK >> PVT_DATA_DATA_FLD) +#define PVT_TRIM_MIN 0 +#define PVT_TRIM_MAX (PVT_CTRL_TRIM_MASK >> PVT_CTRL_TRIM_FLD) +#define PVT_TRIM_TEMP 7130 +#define PVT_TRIM_STEP (PVT_TRIM_TEMP / PVT_TRIM_MAX) +#define PVT_TRIM_DEF 0 +#define PVT_TOUT_MIN (NSEC_PER_SEC / 3000) +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) +# define PVT_TOUT_DEF 60000 +#else +# define PVT_TOUT_DEF 0 +#endif + +/* + * enum pvt_sensor_type - Baikal-T1 PVT sensor types (correspond to each PVT + * sampling mode) + * @PVT_SENSOR*: helpers to traverse the sensors in loops. + * @PVT_TEMP: PVT Temperature sensor. + * @PVT_VOLT: PVT Voltage sensor. + * @PVT_LVT: PVT Low-Voltage threshold sensor. + * @PVT_HVT: PVT High-Voltage threshold sensor. + * @PVT_SVT: PVT Standard-Voltage threshold sensor. + */ +enum pvt_sensor_type { + PVT_SENSOR_FIRST, + PVT_TEMP = PVT_SENSOR_FIRST, + PVT_VOLT, + PVT_LVT, + PVT_HVT, + PVT_SVT, + PVT_SENSOR_LAST = PVT_SVT, + PVT_SENSORS_NUM +}; + +/* + * enum pvt_clock_type - Baikal-T1 PVT clocks. + * @PVT_CLOCK_APB: APB clock. + * @PVT_CLOCK_REF: PVT reference clock. + */ +enum pvt_clock_type { + PVT_CLOCK_APB, + PVT_CLOCK_REF, + PVT_CLOCK_NUM +}; + +/* + * struct pvt_sensor_info - Baikal-T1 PVT sensor informational structure + * @channel: Sensor channel ID. + * @label: hwmon sensor label. + * @mode: PVT mode corresponding to the channel. + * @thres_base: upper and lower threshold values of the sensor. + * @thres_sts_lo: low threshold status bitfield. + * @thres_sts_hi: high threshold status bitfield. + * @type: Sensor type. + * @attr_min_alarm: Min alarm attribute ID. + * @attr_min_alarm: Max alarm attribute ID. + */ +struct pvt_sensor_info { + int channel; + const char *label; + u32 mode; + unsigned long thres_base; + u32 thres_sts_lo; + u32 thres_sts_hi; + enum hwmon_sensor_types type; + u32 attr_min_alarm; + u32 attr_max_alarm; +}; + +#define PVT_SENSOR_INFO(_ch, _label, _type, _mode, _thres) \ + { \ + .channel = _ch, \ + .label = _label, \ + .mode = PVT_CTRL_MODE_ ##_mode, \ + .thres_base = PVT_ ##_thres, \ + .thres_sts_lo = PVT_INTR_ ##_thres## _LO, \ + .thres_sts_hi = PVT_INTR_ ##_thres## _HI, \ + .type = _type, \ + .attr_min_alarm = _type## _min, \ + .attr_max_alarm = _type## _max, \ + } + +/* + * struct pvt_cache - PVT sensors data cache + * @data: data cache in raw format. + * @thres_sts_lo: low threshold status saved on the previous data conversion. + * @thres_sts_hi: high threshold status saved on the previous data conversion. + * @data_seqlock: cached data seq-lock. + * @conversion: data conversion completion. + */ +struct pvt_cache { + u32 data; +#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS) + seqlock_t data_seqlock; + u32 thres_sts_lo; + u32 thres_sts_hi; +#else + struct completion conversion; +#endif +}; + +/* + * struct pvt_hwmon - Baikal-T1 PVT private data + * @dev: device structure of the PVT platform device. + * @hwmon: hwmon device structure. + * @regs: pointer to the Baikal-T1 PVT registers region. + * @irq: PVT events IRQ number. + * @clks: Array of the PVT clocks descriptor (APB/ref clocks). + * @ref_clk: Pointer to the reference clocks descriptor. + * @iface_mtx: Generic interface mutex (used to lock the alarm registers + * when the alarms enabled, or the data conversion interface + * if alarms are disabled). + * @sensor: current PVT sensor the data conversion is being performed for. + * @cache: data cache descriptor. + * @timeout: conversion timeout cache. + */ +struct pvt_hwmon { + struct device *dev; + struct device *hwmon; + + void __iomem *regs; + int irq; + + struct clk_bulk_data clks[PVT_CLOCK_NUM]; + + struct mutex iface_mtx; + enum pvt_sensor_type sensor; + struct pvt_cache cache[PVT_SENSORS_NUM]; + ktime_t timeout; +}; + +/* + * struct pvt_poly_term - a term descriptor of the PVT data translation + * polynomial + * @deg: degree of the term. + * @coef: multiplication factor of the term. + * @divider: distributed divider per each degree. + * @divider_leftover: divider leftover, which couldn't be redistributed. + */ +struct pvt_poly_term { + unsigned int deg; + long coef; + long divider; + long divider_leftover; +}; + +/* + * struct pvt_poly - PVT data translation polynomial descriptor + * @total_divider: total data divider. + * @terms: polynomial terms up to a free one. + */ +struct pvt_poly { + long total_divider; + struct pvt_poly_term terms[]; +}; + +#endif /* __HWMON_BT1_PVT_H__ */ diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index d855c78fb8be..8bf32c6c85d9 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -46,9 +46,6 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) -#define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) -#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) - #ifdef CONFIG_SMP #define for_each_sibling(i, cpu) \ for_each_cpu(i, topology_sibling_cpumask(cpu)) @@ -91,6 +88,8 @@ struct temp_data { struct platform_data { struct device *hwmon_dev; u16 pkg_id; + u16 cpu_map[NUM_REAL_CORES]; + struct ida ida; struct cpumask cpumask; struct temp_data *core_data[MAX_CORE_DATA]; struct device_attribute name_attr; @@ -167,7 +166,7 @@ static ssize_t show_temp(struct device *dev, * really help at all. */ tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; - tdata->valid = 1; + tdata->valid = true; tdata->last_updated = jiffies; } @@ -441,7 +440,7 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) MSR_IA32_THERM_STATUS; tdata->is_pkg_data = pkg_flag; tdata->cpu = cpu; - tdata->cpu_core_id = TO_CORE_ID(cpu); + tdata->cpu_core_id = topology_core_id(cpu); tdata->attr_size = MAX_CORE_ATTRS; mutex_init(&tdata->update_lock); return tdata; @@ -454,7 +453,7 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, struct platform_data *pdata = platform_get_drvdata(pdev); struct cpuinfo_x86 *c = &cpu_data(cpu); u32 eax, edx; - int err, attr_no; + int err, index, attr_no; /* * Find attr number for sysfs: @@ -462,14 +461,26 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, * The attr number is always core id + 2 * The Pkgtemp will always show up as temp1_*, if available */ - attr_no = pkg_flag ? PKG_SYSFS_ATTR_NO : TO_ATTR_NO(cpu); + if (pkg_flag) { + attr_no = PKG_SYSFS_ATTR_NO; + } else { + index = ida_alloc(&pdata->ida, GFP_KERNEL); + if (index < 0) + return index; + pdata->cpu_map[index] = topology_core_id(cpu); + attr_no = index + BASE_SYSFS_ATTR_NO; + } - if (attr_no > MAX_CORE_DATA - 1) - return -ERANGE; + if (attr_no > MAX_CORE_DATA - 1) { + err = -ERANGE; + goto ida_free; + } tdata = init_temp_data(cpu, pkg_flag); - if (!tdata) - return -ENOMEM; + if (!tdata) { + err = -ENOMEM; + goto ida_free; + } /* Test if we can access the status register */ err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); @@ -505,6 +516,9 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, exit_free: pdata->core_data[attr_no] = NULL; kfree(tdata); +ida_free: + if (!pkg_flag) + ida_free(&pdata->ida, index); return err; } @@ -524,6 +538,9 @@ static void coretemp_remove_core(struct platform_data *pdata, int indx) kfree(pdata->core_data[indx]); pdata->core_data[indx] = NULL; + + if (indx >= BASE_SYSFS_ATTR_NO) + ida_free(&pdata->ida, indx - BASE_SYSFS_ATTR_NO); } static int coretemp_probe(struct platform_device *pdev) @@ -537,6 +554,7 @@ static int coretemp_probe(struct platform_device *pdev) return -ENOMEM; pdata->pkg_id = pdev->id; + ida_init(&pdata->ida); platform_set_drvdata(pdev, pdata); pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, @@ -553,6 +571,7 @@ static int coretemp_remove(struct platform_device *pdev) if (pdata->core_data[i]) coretemp_remove_core(pdata, i); + ida_destroy(&pdata->ida); return 0; } @@ -647,7 +666,7 @@ static int coretemp_cpu_offline(unsigned int cpu) struct platform_device *pdev = coretemp_get_pdev(cpu); struct platform_data *pd; struct temp_data *tdata; - int indx, target; + int i, indx = -1, target; /* * Don't execute this on suspend as the device remove locks @@ -660,12 +679,19 @@ static int coretemp_cpu_offline(unsigned int cpu) if (!pdev) return 0; - /* The core id is too big, just return */ - indx = TO_ATTR_NO(cpu); - if (indx > MAX_CORE_DATA - 1) + pd = platform_get_drvdata(pdev); + + for (i = 0; i < NUM_REAL_CORES; i++) { + if (pd->cpu_map[i] == topology_core_id(cpu)) { + indx = i + BASE_SYSFS_ATTR_NO; + break; + } + } + + /* Too many cores and this core is not populated, just return */ + if (indx < 0) return 0; - pd = platform_get_drvdata(pdev); tdata = pd->core_data[indx]; cpumask_clear_cpu(cpu, &pd->cpumask); @@ -709,7 +735,7 @@ static int coretemp_cpu_offline(unsigned int cpu) return 0; } static const struct x86_cpu_id __initconst coretemp_ids[] = { - { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, + X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c new file mode 100644 index 000000000000..fa6aa4fc8b52 --- /dev/null +++ b/drivers/hwmon/corsair-cpro.c @@ -0,0 +1,583 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * corsair-cpro.c - Linux driver for Corsair Commander Pro + * Copyright (C) 2020 Marius Zachmann <mail@mariuszachmann.de> + * + * This driver uses hid reports to communicate with the device to allow hidraw userspace drivers + * still being used. The device does not use report ids. When using hidraw and this driver + * simultaniously, reports could be switched. + */ + +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define USB_VENDOR_ID_CORSAIR 0x1b1c +#define USB_PRODUCT_ID_CORSAIR_COMMANDERPRO 0x0c10 +#define USB_PRODUCT_ID_CORSAIR_1000D 0x1d00 + +#define OUT_BUFFER_SIZE 63 +#define IN_BUFFER_SIZE 16 +#define LABEL_LENGTH 11 +#define REQ_TIMEOUT 300 + +#define CTL_GET_TMP_CNCT 0x10 /* + * returns in bytes 1-4 for each temp sensor: + * 0 not connected + * 1 connected + */ +#define CTL_GET_TMP 0x11 /* + * send: byte 1 is channel, rest zero + * rcv: returns temp for channel in centi-degree celsius + * in bytes 1 and 2 + * returns 0x11 in byte 0 if no sensor is connected + */ +#define CTL_GET_VOLT 0x12 /* + * send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v + * rcv: returns millivolt in bytes 1,2 + * returns error 0x10 if request is invalid + */ +#define CTL_GET_FAN_CNCT 0x20 /* + * returns in bytes 1-6 for each fan: + * 0 not connected + * 1 3pin + * 2 4pin + */ +#define CTL_GET_FAN_RPM 0x21 /* + * send: byte 1 is channel, rest zero + * rcv: returns rpm in bytes 1,2 + */ +#define CTL_GET_FAN_PWM 0x22 /* + * send: byte 1 is channel, rest zero + * rcv: returns pwm in byte 1 if it was set + * returns error 0x12 if fan is controlled via + * fan_target or fan curve + */ +#define CTL_SET_FAN_FPWM 0x23 /* + * set fixed pwm + * send: byte 1 is fan number + * send: byte 2 is percentage from 0 - 100 + */ +#define CTL_SET_FAN_TARGET 0x24 /* + * set target rpm + * send: byte 1 is fan number + * send: byte 2-3 is target + * device accepts all values from 0x00 - 0xFFFF + */ + +#define NUM_FANS 6 +#define NUM_TEMP_SENSORS 4 + +struct ccp_device { + struct hid_device *hdev; + struct device *hwmon_dev; + struct completion wait_input_report; + struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */ + u8 *buffer; + int target[6]; + DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS); + DECLARE_BITMAP(fan_cnct, NUM_FANS); + char fan_label[6][LABEL_LENGTH]; +}; + +/* converts response error in buffer to errno */ +static int ccp_get_errno(struct ccp_device *ccp) +{ + switch (ccp->buffer[0]) { + case 0x00: /* success */ + return 0; + case 0x01: /* called invalid command */ + return -EOPNOTSUPP; + case 0x10: /* called GET_VOLT / GET_TMP with invalid arguments */ + return -EINVAL; + case 0x11: /* requested temps of disconnected sensors */ + case 0x12: /* requested pwm of not pwm controlled channels */ + return -ENODATA; + default: + hid_dbg(ccp->hdev, "unknown device response error: %d", ccp->buffer[0]); + return -EIO; + } +} + +/* send command, check for error in response, response in ccp->buffer */ +static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3) +{ + unsigned long t; + int ret; + + memset(ccp->buffer, 0x00, OUT_BUFFER_SIZE); + ccp->buffer[0] = command; + ccp->buffer[1] = byte1; + ccp->buffer[2] = byte2; + ccp->buffer[3] = byte3; + + reinit_completion(&ccp->wait_input_report); + + ret = hid_hw_output_report(ccp->hdev, ccp->buffer, OUT_BUFFER_SIZE); + if (ret < 0) + return ret; + + t = wait_for_completion_timeout(&ccp->wait_input_report, msecs_to_jiffies(REQ_TIMEOUT)); + if (!t) + return -ETIMEDOUT; + + return ccp_get_errno(ccp); +} + +static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) +{ + struct ccp_device *ccp = hid_get_drvdata(hdev); + + /* only copy buffer when requested */ + if (completion_done(&ccp->wait_input_report)) + return 0; + + memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size)); + complete(&ccp->wait_input_report); + + return 0; +} + +/* requests and returns single data values depending on channel */ +static int get_data(struct ccp_device *ccp, int command, int channel, bool two_byte_data) +{ + int ret; + + mutex_lock(&ccp->mutex); + + ret = send_usb_cmd(ccp, command, channel, 0, 0); + if (ret) + goto out_unlock; + + ret = ccp->buffer[1]; + if (two_byte_data) + ret = (ret << 8) + ccp->buffer[2]; + +out_unlock: + mutex_unlock(&ccp->mutex); + return ret; +} + +static int set_pwm(struct ccp_device *ccp, int channel, long val) +{ + int ret; + + if (val < 0 || val > 255) + return -EINVAL; + + /* The Corsair Commander Pro uses values from 0-100 */ + val = DIV_ROUND_CLOSEST(val * 100, 255); + + mutex_lock(&ccp->mutex); + + ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0); + if (!ret) + ccp->target[channel] = -ENODATA; + + mutex_unlock(&ccp->mutex); + return ret; +} + +static int set_target(struct ccp_device *ccp, int channel, long val) +{ + int ret; + + val = clamp_val(val, 0, 0xFFFF); + ccp->target[channel] = val; + + mutex_lock(&ccp->mutex); + ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val); + + mutex_unlock(&ccp->mutex); + return ret; +} + +static int ccp_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct ccp_device *ccp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_label: + *str = ccp->fan_label[channel]; + return 0; + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static int ccp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct ccp_device *ccp = dev_get_drvdata(dev); + int ret; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + ret = get_data(ccp, CTL_GET_TMP, channel, true); + if (ret < 0) + return ret; + *val = ret * 10; + return 0; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + ret = get_data(ccp, CTL_GET_FAN_RPM, channel, true); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_fan_target: + /* how to read target values from the device is unknown */ + /* driver returns last set value or 0 */ + if (ccp->target[channel] < 0) + return -ENODATA; + *val = ccp->target[channel]; + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = get_data(ccp, CTL_GET_FAN_PWM, channel, false); + if (ret < 0) + return ret; + *val = DIV_ROUND_CLOSEST(ret * 255, 100); + return 0; + default: + break; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + ret = get_data(ccp, CTL_GET_VOLT, channel, true); + if (ret < 0) + return ret; + *val = ret; + return 0; + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +}; + +static int ccp_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct ccp_device *ccp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + return set_pwm(ccp, channel, val); + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_target: + return set_target(ccp, channel, val); + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +}; + +static umode_t ccp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct ccp_device *ccp = data; + + switch (type) { + case hwmon_temp: + if (!test_bit(channel, ccp->temp_cnct)) + break; + + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_label: + return 0444; + default: + break; + } + break; + case hwmon_fan: + if (!test_bit(channel, ccp->fan_cnct)) + break; + + switch (attr) { + case hwmon_fan_input: + return 0444; + case hwmon_fan_label: + return 0444; + case hwmon_fan_target: + return 0644; + default: + break; + } + break; + case hwmon_pwm: + if (!test_bit(channel, ccp->fan_cnct)) + break; + + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + break; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + return 0444; + default: + break; + } + break; + default: + break; + } + + return 0; +}; + +static const struct hwmon_ops ccp_hwmon_ops = { + .is_visible = ccp_is_visible, + .read = ccp_read, + .read_string = ccp_read_string, + .write = ccp_write, +}; + +static const struct hwmon_channel_info *ccp_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT + ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET + ), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT + ), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT + ), + NULL +}; + +static const struct hwmon_chip_info ccp_chip_info = { + .ops = &ccp_hwmon_ops, + .info = ccp_info, +}; + +/* read fan connection status and set labels */ +static int get_fan_cnct(struct ccp_device *ccp) +{ + int channel; + int mode; + int ret; + + ret = send_usb_cmd(ccp, CTL_GET_FAN_CNCT, 0, 0, 0); + if (ret) + return ret; + + for (channel = 0; channel < NUM_FANS; channel++) { + mode = ccp->buffer[channel + 1]; + if (mode == 0) + continue; + + set_bit(channel, ccp->fan_cnct); + ccp->target[channel] = -ENODATA; + + switch (mode) { + case 1: + scnprintf(ccp->fan_label[channel], LABEL_LENGTH, + "fan%d 3pin", channel + 1); + break; + case 2: + scnprintf(ccp->fan_label[channel], LABEL_LENGTH, + "fan%d 4pin", channel + 1); + break; + default: + scnprintf(ccp->fan_label[channel], LABEL_LENGTH, + "fan%d other", channel + 1); + break; + } + } + + return 0; +} + +/* read temp sensor connection status */ +static int get_temp_cnct(struct ccp_device *ccp) +{ + int channel; + int mode; + int ret; + + ret = send_usb_cmd(ccp, CTL_GET_TMP_CNCT, 0, 0, 0); + if (ret) + return ret; + + for (channel = 0; channel < NUM_TEMP_SENSORS; channel++) { + mode = ccp->buffer[channel + 1]; + if (mode == 0) + continue; + + set_bit(channel, ccp->temp_cnct); + } + + return 0; +} + +static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct ccp_device *ccp; + int ret; + + ccp = devm_kzalloc(&hdev->dev, sizeof(*ccp), GFP_KERNEL); + if (!ccp) + return -ENOMEM; + + ccp->buffer = devm_kmalloc(&hdev->dev, OUT_BUFFER_SIZE, GFP_KERNEL); + if (!ccp->buffer) + return -ENOMEM; + + ret = hid_parse(hdev); + if (ret) + return ret; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto out_hw_stop; + + ccp->hdev = hdev; + hid_set_drvdata(hdev, ccp); + mutex_init(&ccp->mutex); + init_completion(&ccp->wait_input_report); + + hid_device_io_start(hdev); + + /* temp and fan connection status only updates when device is powered on */ + ret = get_temp_cnct(ccp); + if (ret) + goto out_hw_close; + + ret = get_fan_cnct(ccp); + if (ret) + goto out_hw_close; + ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro", + ccp, &ccp_chip_info, 0); + if (IS_ERR(ccp->hwmon_dev)) { + ret = PTR_ERR(ccp->hwmon_dev); + goto out_hw_close; + } + + return 0; + +out_hw_close: + hid_hw_close(hdev); +out_hw_stop: + hid_hw_stop(hdev); + return ret; +} + +static void ccp_remove(struct hid_device *hdev) +{ + struct ccp_device *ccp = hid_get_drvdata(hdev); + + hwmon_device_unregister(ccp->hwmon_dev); + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id ccp_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_COMMANDERPRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_1000D) }, + { } +}; + +static struct hid_driver ccp_driver = { + .name = "corsair-cpro", + .id_table = ccp_devices, + .probe = ccp_probe, + .remove = ccp_remove, + .raw_event = ccp_raw_event, +}; + +MODULE_DEVICE_TABLE(hid, ccp_devices); +MODULE_LICENSE("GPL"); + +static int __init ccp_init(void) +{ + return hid_register_driver(&ccp_driver); +} + +static void __exit ccp_exit(void) +{ + hid_unregister_driver(&ccp_driver); +} + +/* + * When compiling this driver as built-in, hwmon initcalls will get called before the + * hid driver and this driver would fail to register. late_initcall solves this. + */ +late_initcall(ccp_init); +module_exit(ccp_exit); diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c new file mode 100644 index 000000000000..2210aa62e3d0 --- /dev/null +++ b/drivers/hwmon/corsair-psu.c @@ -0,0 +1,844 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * corsair-psu.c - Linux driver for Corsair power supplies with HID sensors interface + * Copyright (C) 2020 Wilken Gottwalt <wilken.gottwalt@posteo.net> + */ + +#include <linux/completion.h> +#include <linux/debugfs.h> +#include <linux/errno.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/types.h> + +/* + * Corsair protocol for PSUs + * + * message size = 64 bytes (request and response, little endian) + * request: + * [length][command][param0][param1][paramX]... + * reply: + * [echo of length][echo of command][data0][data1][dataX]... + * + * - commands are byte sized opcodes + * - length is the sum of all bytes of the commands/params + * - the micro-controller of most of these PSUs support concatenation in the request and reply, + * but it is better to not rely on this (it is also hard to parse) + * - the driver uses raw events to be accessible from userspace (though this is not really + * supported, it is just there for convenience, may be removed in the future) + * - a reply always start with the length and command in the same order the request used it + * - length of the reply data is specific to the command used + * - some of the commands work on a rail and can be switched to a specific rail (0 = 12v, + * 1 = 5v, 2 = 3.3v) + * - the format of the init command 0xFE is swapped length/command bytes + * - parameter bytes amount and values are specific to the command (rail setting is the only + * for now that uses non-zero values) + * - there are much more commands, especially for configuring the device, but they are not + * supported because a wrong command/length can lockup the micro-controller + * - the driver supports debugfs for values not fitting into the hwmon class + * - not every device class (HXi, RMi or AXi) supports all commands + * - it is a pure sensors reading driver (will not support configuring) + */ + +#define DRIVER_NAME "corsair-psu" + +#define REPLY_SIZE 16 /* max length of a reply to a single command */ +#define CMD_BUFFER_SIZE 64 +#define CMD_TIMEOUT_MS 250 +#define SECONDS_PER_HOUR (60 * 60) +#define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24) +#define RAIL_COUNT 3 /* 3v3 + 5v + 12v */ +#define TEMP_COUNT 2 +#define OCP_MULTI_RAIL 0x02 + +#define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */ +#define PSU_CMD_RAIL_VOLTS_HCRIT 0x40 /* the rest of the commands expect length 3 */ +#define PSU_CMD_RAIL_VOLTS_LCRIT 0x44 +#define PSU_CMD_RAIL_AMPS_HCRIT 0x46 +#define PSU_CMD_TEMP_HCRIT 0x4F +#define PSU_CMD_IN_VOLTS 0x88 +#define PSU_CMD_IN_AMPS 0x89 +#define PSU_CMD_RAIL_VOLTS 0x8B +#define PSU_CMD_RAIL_AMPS 0x8C +#define PSU_CMD_TEMP0 0x8D +#define PSU_CMD_TEMP1 0x8E +#define PSU_CMD_FAN 0x90 +#define PSU_CMD_RAIL_WATTS 0x96 +#define PSU_CMD_VEND_STR 0x99 +#define PSU_CMD_PROD_STR 0x9A +#define PSU_CMD_TOTAL_UPTIME 0xD1 +#define PSU_CMD_UPTIME 0xD2 +#define PSU_CMD_OCPMODE 0xD8 +#define PSU_CMD_TOTAL_WATTS 0xEE +#define PSU_CMD_INIT 0xFE + +#define L_IN_VOLTS "v_in" +#define L_OUT_VOLTS_12V "v_out +12v" +#define L_OUT_VOLTS_5V "v_out +5v" +#define L_OUT_VOLTS_3_3V "v_out +3.3v" +#define L_IN_AMPS "curr in" +#define L_AMPS_12V "curr +12v" +#define L_AMPS_5V "curr +5v" +#define L_AMPS_3_3V "curr +3.3v" +#define L_FAN "psu fan" +#define L_TEMP0 "vrm temp" +#define L_TEMP1 "case temp" +#define L_WATTS "power total" +#define L_WATTS_12V "power +12v" +#define L_WATTS_5V "power +5v" +#define L_WATTS_3_3V "power +3.3v" + +static const char *const label_watts[] = { + L_WATTS, + L_WATTS_12V, + L_WATTS_5V, + L_WATTS_3_3V +}; + +static const char *const label_volts[] = { + L_IN_VOLTS, + L_OUT_VOLTS_12V, + L_OUT_VOLTS_5V, + L_OUT_VOLTS_3_3V +}; + +static const char *const label_amps[] = { + L_IN_AMPS, + L_AMPS_12V, + L_AMPS_5V, + L_AMPS_3_3V +}; + +struct corsairpsu_data { + struct hid_device *hdev; + struct device *hwmon_dev; + struct dentry *debugfs; + struct completion wait_completion; + struct mutex lock; /* for locking access to cmd_buffer */ + u8 *cmd_buffer; + char vendor[REPLY_SIZE]; + char product[REPLY_SIZE]; + long temp_crit[TEMP_COUNT]; + long in_crit[RAIL_COUNT]; + long in_lcrit[RAIL_COUNT]; + long curr_crit[RAIL_COUNT]; + u8 temp_crit_support; + u8 in_crit_support; + u8 in_lcrit_support; + u8 curr_crit_support; + bool in_curr_cmd_support; /* not all commands are supported on every PSU */ +}; + +/* some values are SMBus LINEAR11 data which need a conversion */ +static int corsairpsu_linear11_to_int(const u16 val, const int scale) +{ + const int exp = ((s16)val) >> 11; + const int mant = (((s16)(val & 0x7ff)) << 5) >> 5; + const int result = mant * scale; + + return (exp >= 0) ? (result << exp) : (result >> -exp); +} + +static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data) +{ + unsigned long time; + int ret; + + memset(priv->cmd_buffer, 0, CMD_BUFFER_SIZE); + priv->cmd_buffer[0] = p0; + priv->cmd_buffer[1] = p1; + priv->cmd_buffer[2] = p2; + + reinit_completion(&priv->wait_completion); + + ret = hid_hw_output_report(priv->hdev, priv->cmd_buffer, CMD_BUFFER_SIZE); + if (ret < 0) + return ret; + + time = wait_for_completion_timeout(&priv->wait_completion, + msecs_to_jiffies(CMD_TIMEOUT_MS)); + if (!time) + return -ETIMEDOUT; + + /* + * at the start of the reply is an echo of the send command/length in the same order it + * was send, not every command is supported on every device class, if a command is not + * supported, the length value in the reply is okay, but the command value is set to 0 + */ + if (p0 != priv->cmd_buffer[0] || p1 != priv->cmd_buffer[1]) + return -EOPNOTSUPP; + + if (data) + memcpy(data, priv->cmd_buffer + 2, REPLY_SIZE); + + return 0; +} + +static int corsairpsu_init(struct corsairpsu_data *priv) +{ + /* + * PSU_CMD_INIT uses swapped length/command and expects 2 parameter bytes, this command + * actually generates a reply, but we don't need it + */ + return corsairpsu_usb_cmd(priv, PSU_CMD_INIT, 3, 0, NULL); +} + +static int corsairpsu_fwinfo(struct corsairpsu_data *priv) +{ + int ret; + + ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_VEND_STR, 0, priv->vendor); + if (ret < 0) + return ret; + + ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_PROD_STR, 0, priv->product); + if (ret < 0) + return ret; + + return 0; +} + +static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, void *data) +{ + int ret; + + mutex_lock(&priv->lock); + switch (cmd) { + case PSU_CMD_RAIL_VOLTS_HCRIT: + case PSU_CMD_RAIL_VOLTS_LCRIT: + case PSU_CMD_RAIL_AMPS_HCRIT: + case PSU_CMD_RAIL_VOLTS: + case PSU_CMD_RAIL_AMPS: + case PSU_CMD_RAIL_WATTS: + ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL); + if (ret < 0) + goto cmd_fail; + break; + default: + break; + } + + ret = corsairpsu_usb_cmd(priv, 3, cmd, 0, data); + +cmd_fail: + mutex_unlock(&priv->lock); + return ret; +} + +static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val) +{ + u8 data[REPLY_SIZE]; + long tmp; + int ret; + + ret = corsairpsu_request(priv, cmd, rail, data); + if (ret < 0) + return ret; + + /* + * the biggest value here comes from the uptime command and to exceed MAXINT total uptime + * needs to be about 68 years, the rest are u16 values and the biggest value coming out of + * the LINEAR11 conversion are the watts values which are about 1200 for the strongest psu + * supported (HX1200i) + */ + tmp = ((long)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0]; + switch (cmd) { + case PSU_CMD_RAIL_VOLTS_HCRIT: + case PSU_CMD_RAIL_VOLTS_LCRIT: + case PSU_CMD_RAIL_AMPS_HCRIT: + case PSU_CMD_TEMP_HCRIT: + case PSU_CMD_IN_VOLTS: + case PSU_CMD_IN_AMPS: + case PSU_CMD_RAIL_VOLTS: + case PSU_CMD_RAIL_AMPS: + case PSU_CMD_TEMP0: + case PSU_CMD_TEMP1: + *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000); + break; + case PSU_CMD_FAN: + *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1); + break; + case PSU_CMD_RAIL_WATTS: + case PSU_CMD_TOTAL_WATTS: + *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000); + break; + case PSU_CMD_TOTAL_UPTIME: + case PSU_CMD_UPTIME: + case PSU_CMD_OCPMODE: + *val = tmp; + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static void corsairpsu_get_criticals(struct corsairpsu_data *priv) +{ + long tmp; + int rail; + + for (rail = 0; rail < TEMP_COUNT; ++rail) { + if (!corsairpsu_get_value(priv, PSU_CMD_TEMP_HCRIT, rail, &tmp)) { + priv->temp_crit_support |= BIT(rail); + priv->temp_crit[rail] = tmp; + } + } + + for (rail = 0; rail < RAIL_COUNT; ++rail) { + if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_HCRIT, rail, &tmp)) { + priv->in_crit_support |= BIT(rail); + priv->in_crit[rail] = tmp; + } + + if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_LCRIT, rail, &tmp)) { + priv->in_lcrit_support |= BIT(rail); + priv->in_lcrit[rail] = tmp; + } + + if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS_HCRIT, rail, &tmp)) { + priv->curr_crit_support |= BIT(rail); + priv->curr_crit[rail] = tmp; + } + } +} + +static void corsairpsu_check_cmd_support(struct corsairpsu_data *priv) +{ + long tmp; + + priv->in_curr_cmd_support = !corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, &tmp); +} + +static umode_t corsairpsu_hwmon_temp_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + umode_t res = 0444; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + case hwmon_temp_crit: + if (channel > 0 && !(priv->temp_crit_support & BIT(channel - 1))) + res = 0; + break; + default: + break; + } + + return res; +} + +static umode_t corsairpsu_hwmon_fan_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_label: + return 0444; + default: + return 0; + } +} + +static umode_t corsairpsu_hwmon_power_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + switch (attr) { + case hwmon_power_input: + case hwmon_power_label: + return 0444; + default: + return 0; + } +} + +static umode_t corsairpsu_hwmon_in_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + umode_t res = 0444; + + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + case hwmon_in_crit: + if (channel > 0 && !(priv->in_crit_support & BIT(channel - 1))) + res = 0; + break; + case hwmon_in_lcrit: + if (channel > 0 && !(priv->in_lcrit_support & BIT(channel - 1))) + res = 0; + break; + default: + break; + } + + return res; +} + +static umode_t corsairpsu_hwmon_curr_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + umode_t res = 0444; + + switch (attr) { + case hwmon_curr_input: + if (channel == 0 && !priv->in_curr_cmd_support) + res = 0; + break; + case hwmon_curr_label: + case hwmon_curr_crit: + if (channel > 0 && !(priv->curr_crit_support & BIT(channel - 1))) + res = 0; + break; + default: + break; + } + + return res; +} + +static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct corsairpsu_data *priv = data; + + switch (type) { + case hwmon_temp: + return corsairpsu_hwmon_temp_is_visible(priv, attr, channel); + case hwmon_fan: + return corsairpsu_hwmon_fan_is_visible(priv, attr, channel); + case hwmon_power: + return corsairpsu_hwmon_power_is_visible(priv, attr, channel); + case hwmon_in: + return corsairpsu_hwmon_in_is_visible(priv, attr, channel); + case hwmon_curr: + return corsairpsu_hwmon_curr_is_visible(priv, attr, channel); + default: + return 0; + } +} + +static int corsairpsu_hwmon_temp_read(struct corsairpsu_data *priv, u32 attr, int channel, + long *val) +{ + int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_input: + return corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, + channel, val); + case hwmon_temp_crit: + *val = priv->temp_crit[channel]; + err = 0; + break; + default: + break; + } + + return err; +} + +static int corsairpsu_hwmon_power_read(struct corsairpsu_data *priv, u32 attr, int channel, + long *val) +{ + if (attr == hwmon_power_input) { + switch (channel) { + case 0: + return corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val); + case 1 ... 3: + return corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val); + default: + break; + } + } + + return -EOPNOTSUPP; +} + +static int corsairpsu_hwmon_in_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val) +{ + int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_in_input: + switch (channel) { + case 0: + return corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val); + case 1 ... 3: + return corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS, channel - 1, val); + default: + break; + } + break; + case hwmon_in_crit: + *val = priv->in_crit[channel - 1]; + err = 0; + break; + case hwmon_in_lcrit: + *val = priv->in_lcrit[channel - 1]; + err = 0; + break; + } + + return err; +} + +static int corsairpsu_hwmon_curr_read(struct corsairpsu_data *priv, u32 attr, int channel, + long *val) +{ + int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_curr_input: + switch (channel) { + case 0: + return corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val); + case 1 ... 3: + return corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val); + default: + break; + } + break; + case hwmon_curr_crit: + *val = priv->curr_crit[channel - 1]; + err = 0; + break; + default: + break; + } + + return err; +} + +static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct corsairpsu_data *priv = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + return corsairpsu_hwmon_temp_read(priv, attr, channel, val); + case hwmon_fan: + if (attr == hwmon_fan_input) + return corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val); + return -EOPNOTSUPP; + case hwmon_power: + return corsairpsu_hwmon_power_read(priv, attr, channel, val); + case hwmon_in: + return corsairpsu_hwmon_in_read(priv, attr, channel, val); + case hwmon_curr: + return corsairpsu_hwmon_curr_read(priv, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + if (type == hwmon_temp && attr == hwmon_temp_label) { + *str = channel ? L_TEMP1 : L_TEMP0; + return 0; + } else if (type == hwmon_fan && attr == hwmon_fan_label) { + *str = L_FAN; + return 0; + } else if (type == hwmon_power && attr == hwmon_power_label && channel < 4) { + *str = label_watts[channel]; + return 0; + } else if (type == hwmon_in && attr == hwmon_in_label && channel < 4) { + *str = label_volts[channel]; + return 0; + } else if (type == hwmon_curr && attr == hwmon_curr_label && channel < 4) { + *str = label_amps[channel]; + return 0; + } + + return -EOPNOTSUPP; +} + +static const struct hwmon_ops corsairpsu_hwmon_ops = { + .is_visible = corsairpsu_hwmon_ops_is_visible, + .read = corsairpsu_hwmon_ops_read, + .read_string = corsairpsu_hwmon_ops_read_string, +}; + +static const struct hwmon_channel_info *corsairpsu_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT, + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_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_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT, + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT, + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT, + HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT, + HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT), + NULL +}; + +static const struct hwmon_chip_info corsairpsu_chip_info = { + .ops = &corsairpsu_hwmon_ops, + .info = corsairpsu_info, +}; + +#ifdef CONFIG_DEBUG_FS + +static void print_uptime(struct seq_file *seqf, u8 cmd) +{ + struct corsairpsu_data *priv = seqf->private; + long val; + int ret; + + ret = corsairpsu_get_value(priv, cmd, 0, &val); + if (ret < 0) { + seq_puts(seqf, "N/A\n"); + return; + } + + if (val > SECONDS_PER_DAY) { + seq_printf(seqf, "%ld day(s), %02ld:%02ld:%02ld\n", val / SECONDS_PER_DAY, + val % SECONDS_PER_DAY / SECONDS_PER_HOUR, val % SECONDS_PER_HOUR / 60, + val % 60); + return; + } + + seq_printf(seqf, "%02ld:%02ld:%02ld\n", val % SECONDS_PER_DAY / SECONDS_PER_HOUR, + val % SECONDS_PER_HOUR / 60, val % 60); +} + +static int uptime_show(struct seq_file *seqf, void *unused) +{ + print_uptime(seqf, PSU_CMD_UPTIME); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(uptime); + +static int uptime_total_show(struct seq_file *seqf, void *unused) +{ + print_uptime(seqf, PSU_CMD_TOTAL_UPTIME); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(uptime_total); + +static int vendor_show(struct seq_file *seqf, void *unused) +{ + struct corsairpsu_data *priv = seqf->private; + + seq_printf(seqf, "%s\n", priv->vendor); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(vendor); + +static int product_show(struct seq_file *seqf, void *unused) +{ + struct corsairpsu_data *priv = seqf->private; + + seq_printf(seqf, "%s\n", priv->product); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(product); + +static int ocpmode_show(struct seq_file *seqf, void *unused) +{ + struct corsairpsu_data *priv = seqf->private; + long val; + int ret; + + /* + * The rail mode is switchable on the fly. The RAW interface can be used for this. But it + * will not be included here, because I consider it somewhat dangerous for the health of the + * PSU. The returned value can be a bogus one, if the PSU is in the process of switching and + * getting of the value itself can also fail during this. Because of this every other value + * than OCP_MULTI_RAIL can be considered as "single rail". + */ + ret = corsairpsu_get_value(priv, PSU_CMD_OCPMODE, 0, &val); + if (ret < 0) + seq_puts(seqf, "N/A\n"); + else + seq_printf(seqf, "%s\n", (val == OCP_MULTI_RAIL) ? "multi rail" : "single rail"); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ocpmode); + +static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) +{ + char name[32]; + + scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev)); + + priv->debugfs = debugfs_create_dir(name, NULL); + debugfs_create_file("uptime", 0444, priv->debugfs, priv, &uptime_fops); + debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops); + debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops); + debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops); + debugfs_create_file("ocpmode", 0444, priv->debugfs, priv, &ocpmode_fops); +} + +#else + +static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) +{ +} + +#endif + +static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct corsairpsu_data *priv; + int ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(struct corsairpsu_data), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->cmd_buffer = devm_kmalloc(&hdev->dev, CMD_BUFFER_SIZE, GFP_KERNEL); + if (!priv->cmd_buffer) + return -ENOMEM; + + ret = hid_parse(hdev); + if (ret) + return ret; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto fail_and_stop; + + priv->hdev = hdev; + hid_set_drvdata(hdev, priv); + mutex_init(&priv->lock); + init_completion(&priv->wait_completion); + + hid_device_io_start(hdev); + + ret = corsairpsu_init(priv); + if (ret < 0) { + dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret); + goto fail_and_stop; + } + + ret = corsairpsu_fwinfo(priv); + if (ret < 0) { + dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret); + goto fail_and_stop; + } + + corsairpsu_get_criticals(priv); + corsairpsu_check_cmd_support(priv); + + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv, + &corsairpsu_chip_info, NULL); + + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + goto fail_and_close; + } + + corsairpsu_debugfs_init(priv); + + return 0; + +fail_and_close: + hid_hw_close(hdev); +fail_and_stop: + hid_hw_stop(hdev); + return ret; +} + +static void corsairpsu_remove(struct hid_device *hdev) +{ + struct corsairpsu_data *priv = hid_get_drvdata(hdev); + + debugfs_remove_recursive(priv->debugfs); + hwmon_device_unregister(priv->hwmon_dev); + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static int corsairpsu_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, + int size) +{ + struct corsairpsu_data *priv = hid_get_drvdata(hdev); + + if (completion_done(&priv->wait_completion)) + return 0; + + memcpy(priv->cmd_buffer, data, min(CMD_BUFFER_SIZE, size)); + complete(&priv->wait_completion); + + return 0; +} + +#ifdef CONFIG_PM +static int corsairpsu_resume(struct hid_device *hdev) +{ + struct corsairpsu_data *priv = hid_get_drvdata(hdev); + + /* some PSUs turn off the microcontroller during standby, so a reinit is required */ + return corsairpsu_init(priv); +} +#endif + +static const struct hid_device_id corsairpsu_idtable[] = { + { HID_USB_DEVICE(0x1b1c, 0x1c03) }, /* Corsair HX550i */ + { HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */ + { HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */ + { HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */ + { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i revision 1 */ + { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */ + { HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */ + { HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */ + { HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */ + { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */ + { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */ + { HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsair HX1000i revision 2 */ + { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i */ + { }, +}; +MODULE_DEVICE_TABLE(hid, corsairpsu_idtable); + +static struct hid_driver corsairpsu_driver = { + .name = DRIVER_NAME, + .id_table = corsairpsu_idtable, + .probe = corsairpsu_probe, + .remove = corsairpsu_remove, + .raw_event = corsairpsu_raw_event, +#ifdef CONFIG_PM + .resume = corsairpsu_resume, + .reset_resume = corsairpsu_resume, +#endif +}; +module_hid_driver(corsairpsu_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wilken Gottwalt <wilken.gottwalt@posteo.net>"); +MODULE_DESCRIPTION("Linux driver for Corsair power supplies with HID sensors interface"); diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index 53b517dbe7e6..ed6c5df94fdf 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -244,9 +244,9 @@ static ssize_t da9052_tsi_show(struct device *dev, int channel = to_sensor_dev_attr(devattr)->index; int ret; - mutex_lock(&hwmon->hwmon_lock); + mutex_lock(&hwmon->da9052->auxadc_lock); ret = __da9052_read_tsi(dev, channel); - mutex_unlock(&hwmon->hwmon_lock); + mutex_unlock(&hwmon->da9052->auxadc_lock); if (ret < 0) return ret; @@ -299,7 +299,7 @@ static ssize_t label_show(struct device *dev, static umode_t da9052_channel_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct da9052_hwmon *hwmon = dev_get_drvdata(dev); struct device_attribute *dattr = container_of(attr, struct device_attribute, attr); diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index d4c83009d625..1572b5416015 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -7,28 +7,33 @@ * Hwmon integration: * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net> - * Copyright (C) 2014, 2015 Pali Rohár <pali.rohar@gmail.com> + * Copyright (C) 2014, 2015 Pali Rohár <pali@kernel.org> */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/capability.h> #include <linux/cpu.h> +#include <linux/ctype.h> #include <linux/delay.h> -#include <linux/module.h> -#include <linux/types.h> +#include <linux/dmi.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/hwmon.h> #include <linux/init.h> +#include <linux/kconfig.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> -#include <linux/dmi.h> -#include <linux/capability.h> -#include <linux/mutex.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/uaccess.h> -#include <linux/io.h> -#include <linux/sched.h> -#include <linux/ctype.h> +#include <linux/slab.h> #include <linux/smp.h> +#include <linux/string.h> +#include <linux/thermal.h> +#include <linux/types.h> +#include <linux/uaccess.h> #include <linux/i8k.h> @@ -44,8 +49,11 @@ #define I8K_SMM_GET_DELL_SIG1 0xfea3 #define I8K_SMM_GET_DELL_SIG2 0xffa3 +/* in usecs */ +#define DELL_SMM_MAX_DURATION 250000 + #define I8K_FAN_MULT 30 -#define I8K_FAN_MAX_RPM 30000 +#define I8K_FAN_RPM_THRESHOLD 1000 #define I8K_MAX_TEMP 127 #define I8K_FN_NONE 0x00 @@ -58,42 +66,40 @@ #define I8K_POWER_AC 0x05 #define I8K_POWER_BATTERY 0x01 -static DEFINE_MUTEX(i8k_mutex); -static char bios_version[4]; -static char bios_machineid[16]; -static struct device *i8k_hwmon_dev; -static u32 i8k_hwmon_flags; -static uint i8k_fan_mult = I8K_FAN_MULT; -static uint i8k_pwm_mult; -static uint i8k_fan_max = I8K_FAN_HIGH; -static bool disallow_fan_type_call; -static bool disallow_fan_support; -static unsigned int manual_fan; -static unsigned int auto_fan; - -#define I8K_HWMON_HAVE_TEMP1 (1 << 0) -#define I8K_HWMON_HAVE_TEMP2 (1 << 1) -#define I8K_HWMON_HAVE_TEMP3 (1 << 2) -#define I8K_HWMON_HAVE_TEMP4 (1 << 3) -#define I8K_HWMON_HAVE_TEMP5 (1 << 4) -#define I8K_HWMON_HAVE_TEMP6 (1 << 5) -#define I8K_HWMON_HAVE_TEMP7 (1 << 6) -#define I8K_HWMON_HAVE_TEMP8 (1 << 7) -#define I8K_HWMON_HAVE_TEMP9 (1 << 8) -#define I8K_HWMON_HAVE_TEMP10 (1 << 9) -#define I8K_HWMON_HAVE_FAN1 (1 << 10) -#define I8K_HWMON_HAVE_FAN2 (1 << 11) -#define I8K_HWMON_HAVE_FAN3 (1 << 12) +#define DELL_SMM_NO_TEMP 10 +#define DELL_SMM_NO_FANS 3 + +struct dell_smm_data { + struct mutex i8k_mutex; /* lock for sensors writes */ + char bios_version[4]; + char bios_machineid[16]; + uint i8k_fan_mult; + uint i8k_pwm_mult; + uint i8k_fan_max; + bool disallow_fan_type_call; + bool disallow_fan_support; + unsigned int manual_fan; + unsigned int auto_fan; + int temp_type[DELL_SMM_NO_TEMP]; + bool fan[DELL_SMM_NO_FANS]; + int fan_type[DELL_SMM_NO_FANS]; + int *fan_nominal_speed[DELL_SMM_NO_FANS]; +}; + +struct dell_smm_cooling_data { + u8 fan_num; + struct dell_smm_data *data; +}; MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); -MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); +MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("i8k"); static bool force; -module_param(force, bool, 0); -MODULE_PARM_DESC(force, "Force loading without checking for supported models"); +module_param_unsafe(force, bool, 0); +MODULE_PARM_DESC(force, "Force loading without checking for supported models and features"); static bool ignore_dmi; module_param(ignore_dmi, bool, 0); @@ -119,14 +125,41 @@ MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)" struct smm_regs { unsigned int eax; - unsigned int ebx __packed; - unsigned int ecx __packed; - unsigned int edx __packed; - unsigned int esi __packed; - unsigned int edi __packed; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + unsigned int esi; + unsigned int edi; +}; + +static const char * const temp_labels[] = { + "CPU", + "GPU", + "SODIMM", + "Other", + "Ambient", + "Other", }; -static inline const char *i8k_get_dmi_data(int field) +static const char * const fan_labels[] = { + "Processor Fan", + "Motherboard Fan", + "Video Fan", + "Power Supply Fan", + "Chipset Fan", + "Other Fan", +}; + +static const char * const docking_labels[] = { + "Docking Processor Fan", + "Docking Motherboard Fan", + "Docking Video Fan", + "Docking Power Supply Fan", + "Docking Chipset Fan", + "Docking Other Fan", +}; + +static inline const char __init *i8k_get_dmi_data(int field) { const char *dmi_data = dmi_get_system_info(field); @@ -138,87 +171,39 @@ static inline const char *i8k_get_dmi_data(int field) */ static int i8k_smm_func(void *par) { - int rc; + ktime_t calltime = ktime_get(); struct smm_regs *regs = par; int eax = regs->eax; - -#ifdef DEBUG int ebx = regs->ebx; - unsigned long duration; - ktime_t calltime, delta, rettime; - - calltime = ktime_get(); -#endif + unsigned char carry; + long long duration; /* SMM requires CPU 0 */ if (smp_processor_id() != 0) return -EBUSY; -#if defined(CONFIG_X86_64) - asm volatile("pushq %%rax\n\t" - "movl 0(%%rax),%%edx\n\t" - "pushq %%rdx\n\t" - "movl 4(%%rax),%%ebx\n\t" - "movl 8(%%rax),%%ecx\n\t" - "movl 12(%%rax),%%edx\n\t" - "movl 16(%%rax),%%esi\n\t" - "movl 20(%%rax),%%edi\n\t" - "popq %%rax\n\t" - "out %%al,$0xb2\n\t" - "out %%al,$0x84\n\t" - "xchgq %%rax,(%%rsp)\n\t" - "movl %%ebx,4(%%rax)\n\t" - "movl %%ecx,8(%%rax)\n\t" - "movl %%edx,12(%%rax)\n\t" - "movl %%esi,16(%%rax)\n\t" - "movl %%edi,20(%%rax)\n\t" - "popq %%rdx\n\t" - "movl %%edx,0(%%rax)\n\t" - "pushfq\n\t" - "popq %%rax\n\t" - "andl $1,%%eax\n" - : "=a"(rc) - : "a"(regs) - : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); -#else - asm volatile("pushl %%eax\n\t" - "movl 0(%%eax),%%edx\n\t" - "push %%edx\n\t" - "movl 4(%%eax),%%ebx\n\t" - "movl 8(%%eax),%%ecx\n\t" - "movl 12(%%eax),%%edx\n\t" - "movl 16(%%eax),%%esi\n\t" - "movl 20(%%eax),%%edi\n\t" - "popl %%eax\n\t" - "out %%al,$0xb2\n\t" - "out %%al,$0x84\n\t" - "xchgl %%eax,(%%esp)\n\t" - "movl %%ebx,4(%%eax)\n\t" - "movl %%ecx,8(%%eax)\n\t" - "movl %%edx,12(%%eax)\n\t" - "movl %%esi,16(%%eax)\n\t" - "movl %%edi,20(%%eax)\n\t" - "popl %%edx\n\t" - "movl %%edx,0(%%eax)\n\t" - "lahf\n\t" - "shrl $8,%%eax\n\t" - "andl $1,%%eax\n" - : "=a"(rc) - : "a"(regs) - : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); -#endif - if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) - rc = -EINVAL; - -#ifdef DEBUG - rettime = ktime_get(); - delta = ktime_sub(rettime, calltime); - duration = ktime_to_ns(delta) >> 10; - pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lu usecs)\n", eax, ebx, - (rc ? 0xffff : regs->eax & 0xffff), duration); -#endif + asm volatile("out %%al,$0xb2\n\t" + "out %%al,$0x84\n\t" + "setc %0\n" + : "=mr" (carry), + "+a" (regs->eax), + "+b" (regs->ebx), + "+c" (regs->ecx), + "+d" (regs->edx), + "+S" (regs->esi), + "+D" (regs->edi)); + + duration = ktime_us_delta(ktime_get(), calltime); + pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x carry: %d (took %7lld usecs)\n", + eax, ebx, regs->eax & 0xffff, carry, duration); + + if (duration > DELL_SMM_MAX_DURATION) + pr_warn_once("SMM call took %lld usecs!\n", duration); + + if (carry || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) + return -EINVAL; - return rc; + return 0; } /* @@ -228,9 +213,9 @@ static int i8k_smm(struct smm_regs *regs) { int ret; - get_online_cpus(); + cpus_read_lock(); ret = smp_call_on_cpu(0, i8k_smm_func, regs, true); - put_online_cpus(); + cpus_read_unlock(); return ret; } @@ -238,122 +223,130 @@ static int i8k_smm(struct smm_regs *regs) /* * Read the fan status. */ -static int i8k_get_fan_status(int fan) +static int i8k_get_fan_status(const struct dell_smm_data *data, u8 fan) { - struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; + struct smm_regs regs = { + .eax = I8K_SMM_GET_FAN, + .ebx = fan, + }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; - regs.ebx = fan & 0xff; return i8k_smm(®s) ? : regs.eax & 0xff; } /* * Read the fan speed in RPM. */ -static int i8k_get_fan_speed(int fan) +static int i8k_get_fan_speed(const struct dell_smm_data *data, u8 fan) { - struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; + struct smm_regs regs = { + .eax = I8K_SMM_GET_SPEED, + .ebx = fan, + }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; - regs.ebx = fan & 0xff; - return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; + return i8k_smm(®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; } /* * Read the fan type. */ -static int _i8k_get_fan_type(int fan) +static int _i8k_get_fan_type(const struct dell_smm_data *data, u8 fan) { - struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; + struct smm_regs regs = { + .eax = I8K_SMM_GET_FAN_TYPE, + .ebx = fan, + }; - if (disallow_fan_support || disallow_fan_type_call) + if (data->disallow_fan_support || data->disallow_fan_type_call) return -EINVAL; - regs.ebx = fan & 0xff; return i8k_smm(®s) ? : regs.eax & 0xff; } -static int i8k_get_fan_type(int fan) +static int i8k_get_fan_type(struct dell_smm_data *data, u8 fan) { /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */ - static int types[3] = { INT_MIN, INT_MIN, INT_MIN }; - - if (types[fan] == INT_MIN) - types[fan] = _i8k_get_fan_type(fan); + if (data->fan_type[fan] == INT_MIN) + data->fan_type[fan] = _i8k_get_fan_type(data, fan); - return types[fan]; + return data->fan_type[fan]; } /* * Read the fan nominal rpm for specific fan speed. */ -static int i8k_get_fan_nominal_speed(int fan, int speed) +static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, u8 fan, int speed) { - struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; + struct smm_regs regs = { + .eax = I8K_SMM_GET_NOM_SPEED, + .ebx = fan | (speed << 8), + }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; - regs.ebx = (fan & 0xff) | (speed << 8); - return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; + return i8k_smm(®s) ? : (regs.eax & 0xffff); } /* * Enable or disable automatic BIOS fan control support */ -static int i8k_enable_fan_auto_mode(bool enable) +static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable) { struct smm_regs regs = { }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; - regs.eax = enable ? auto_fan : manual_fan; + regs.eax = enable ? data->auto_fan : data->manual_fan; return i8k_smm(®s); } /* - * Set the fan speed (off, low, high). Returns the new fan status. + * Set the fan speed (off, low, high, ...). */ -static int i8k_set_fan(int fan, int speed) +static int i8k_set_fan(const struct dell_smm_data *data, u8 fan, int speed) { struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; - speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed); - regs.ebx = (fan & 0xff) | (speed << 8); + speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed); + regs.ebx = fan | (speed << 8); - return i8k_smm(®s) ? : i8k_get_fan_status(fan); + return i8k_smm(®s); } -static int i8k_get_temp_type(int sensor) +static int __init i8k_get_temp_type(u8 sensor) { - struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; + struct smm_regs regs = { + .eax = I8K_SMM_GET_TEMP_TYPE, + .ebx = sensor, + }; - regs.ebx = sensor & 0xff; return i8k_smm(®s) ? : regs.eax & 0xff; } /* * Read the cpu temperature. */ -static int _i8k_get_temp(int sensor) +static int _i8k_get_temp(u8 sensor) { struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, - .ebx = sensor & 0xff, + .ebx = sensor, }; return i8k_smm(®s) ? : regs.eax & 0xff; } -static int i8k_get_temp(int sensor) +static int i8k_get_temp(u8 sensor) { int temp = _i8k_get_temp(sensor); @@ -382,7 +375,7 @@ static int i8k_get_temp(int sensor) return temp; } -static int i8k_get_dell_signature(int req_fn) +static int __init i8k_get_dell_signature(int req_fn) { struct smm_regs regs = { .eax = req_fn, }; int rc; @@ -439,35 +432,37 @@ static int i8k_get_power_status(void) * Procfs interface */ -static int -i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) +static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { - int val = 0; - int speed; - unsigned char buff[16]; + struct dell_smm_data *data = pde_data(file_inode(fp)); int __user *argp = (int __user *)arg; + int speed, err; + int val = 0; if (!argp) return -EINVAL; switch (cmd) { case I8K_BIOS_VERSION: - if (!isdigit(bios_version[0]) || !isdigit(bios_version[1]) || - !isdigit(bios_version[2])) + if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) || + !isdigit(data->bios_version[2])) return -EINVAL; - val = (bios_version[0] << 16) | - (bios_version[1] << 8) | bios_version[2]; - break; + val = (data->bios_version[0] << 16) | + (data->bios_version[1] << 8) | data->bios_version[2]; + if (copy_to_user(argp, &val, sizeof(val))) + return -EFAULT; + + return 0; case I8K_MACHINE_ID: if (restricted && !capable(CAP_SYS_ADMIN)) return -EPERM; - memset(buff, 0, sizeof(buff)); - strlcpy(buff, bios_machineid, sizeof(buff)); - break; + if (copy_to_user(argp, data->bios_machineid, sizeof(data->bios_machineid))) + return -EFAULT; + return 0; case I8K_FN_STATUS: val = i8k_get_fn_status(); break; @@ -484,14 +479,20 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) if (copy_from_user(&val, argp, sizeof(int))) return -EFAULT; - val = i8k_get_fan_speed(val); + if (val > U8_MAX || val < 0) + return -EINVAL; + + val = i8k_get_fan_speed(data, val); break; case I8K_GET_FAN: if (copy_from_user(&val, argp, sizeof(int))) return -EFAULT; - val = i8k_get_fan_status(val); + if (val > U8_MAX || val < 0) + return -EINVAL; + + val = i8k_get_fan_status(data, val); break; case I8K_SET_FAN: @@ -501,67 +502,51 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) if (copy_from_user(&val, argp, sizeof(int))) return -EFAULT; + if (val > U8_MAX || val < 0) + return -EINVAL; + if (copy_from_user(&speed, argp + 1, sizeof(int))) return -EFAULT; - val = i8k_set_fan(val, speed); + mutex_lock(&data->i8k_mutex); + err = i8k_set_fan(data, val, speed); + if (err < 0) + val = err; + else + val = i8k_get_fan_status(data, val); + mutex_unlock(&data->i8k_mutex); break; default: - return -EINVAL; + return -ENOIOCTLCMD; } if (val < 0) return val; - switch (cmd) { - case I8K_BIOS_VERSION: - if (copy_to_user(argp, &val, 4)) - return -EFAULT; - - break; - case I8K_MACHINE_ID: - if (copy_to_user(argp, buff, 16)) - return -EFAULT; - - break; - default: - if (copy_to_user(argp, &val, sizeof(int))) - return -EFAULT; - - break; - } + if (copy_to_user(argp, &val, sizeof(int))) + return -EFAULT; return 0; } -static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -{ - long ret; - - mutex_lock(&i8k_mutex); - ret = i8k_ioctl_unlocked(fp, cmd, arg); - mutex_unlock(&i8k_mutex); - - return ret; -} - /* * Print the information for /proc/i8k. */ static int i8k_proc_show(struct seq_file *seq, void *offset) { + struct dell_smm_data *data = seq->private; int fn_key, cpu_temp, ac_power; int left_fan, right_fan, left_speed, right_speed; - cpu_temp = i8k_get_temp(0); /* 11100 µs */ - left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */ - right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */ - left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */ - right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */ - fn_key = i8k_get_fn_status(); /* 750 µs */ + cpu_temp = i8k_get_temp(0); /* 11100 µs */ + left_fan = i8k_get_fan_status(data, I8K_FAN_LEFT); /* 580 µs */ + right_fan = i8k_get_fan_status(data, I8K_FAN_RIGHT); /* 580 µs */ + left_speed = i8k_get_fan_speed(data, I8K_FAN_LEFT); /* 580 µs */ + right_speed = i8k_get_fan_speed(data, I8K_FAN_RIGHT); /* 580 µs */ + fn_key = i8k_get_fn_status(); /* 750 µs */ if (power_status) - ac_power = i8k_get_power_status(); /* 14700 µs */ + ac_power = i8k_get_power_status(); /* 14700 µs */ else ac_power = -1; @@ -581,8 +566,8 @@ static int i8k_proc_show(struct seq_file *seq, void *offset) */ seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", I8K_PROC_FMT, - bios_version, - (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid, + data->bios_version, + (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid, cpu_temp, left_fan, right_fan, left_speed, right_speed, ac_power, fn_key); @@ -592,7 +577,7 @@ static int i8k_proc_show(struct seq_file *seq, void *offset) static int i8k_open_fs(struct inode *inode, struct file *file) { - return single_open(file, i8k_proc_show, NULL); + return single_open(file, i8k_proc_show, pde_data(inode)); } static const struct proc_ops i8k_proc_ops = { @@ -603,368 +588,468 @@ static const struct proc_ops i8k_proc_ops = { .proc_ioctl = i8k_ioctl, }; -static void __init i8k_init_procfs(void) +static void i8k_exit_procfs(void *param) { - /* Register the proc entry */ - proc_create("i8k", 0, NULL, &i8k_proc_ops); + remove_proc_entry("i8k", NULL); } -static void __exit i8k_exit_procfs(void) +static void __init i8k_init_procfs(struct device *dev) { - remove_proc_entry("i8k", NULL); + struct dell_smm_data *data = dev_get_drvdata(dev); + + /* Only register exit function if creation was successful */ + if (proc_create_data("i8k", 0, NULL, &i8k_proc_ops, data)) + devm_add_action_or_reset(dev, i8k_exit_procfs, NULL); } #else -static inline void __init i8k_init_procfs(void) +static void __init i8k_init_procfs(struct device *dev) { } -static inline void __exit i8k_exit_procfs(void) +#endif + +static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state) { + struct dell_smm_cooling_data *cdata = dev->devdata; + + *state = cdata->data->i8k_fan_max; + + return 0; } -#endif +static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state) +{ + struct dell_smm_cooling_data *cdata = dev->devdata; + int ret; -/* - * Hwmon interface - */ + ret = i8k_get_fan_status(cdata->data, cdata->fan_num); + if (ret < 0) + return ret; -static ssize_t i8k_hwmon_temp_label_show(struct device *dev, - struct device_attribute *devattr, - char *buf) + *state = ret; + + return 0; +} + +static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state) { - static const char * const labels[] = { - "CPU", - "GPU", - "SODIMM", - "Other", - "Ambient", - "Other", - }; - int index = to_sensor_dev_attr(devattr)->index; - int type; + struct dell_smm_cooling_data *cdata = dev->devdata; + struct dell_smm_data *data = cdata->data; + int ret; - type = i8k_get_temp_type(index); - if (type < 0) - return type; - if (type >= ARRAY_SIZE(labels)) - type = ARRAY_SIZE(labels) - 1; - return sprintf(buf, "%s\n", labels[type]); + if (state > data->i8k_fan_max) + return -EINVAL; + + mutex_lock(&data->i8k_mutex); + ret = i8k_set_fan(data, cdata->fan_num, (int)state); + mutex_unlock(&data->i8k_mutex); + + return ret; } -static ssize_t i8k_hwmon_temp_show(struct device *dev, - struct device_attribute *devattr, - char *buf) +static const struct thermal_cooling_device_ops dell_smm_cooling_ops = { + .get_max_state = dell_smm_get_max_state, + .get_cur_state = dell_smm_get_cur_state, + .set_cur_state = dell_smm_set_cur_state, +}; + +static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, + int channel) { - int index = to_sensor_dev_attr(devattr)->index; - int temp; + const struct dell_smm_data *data = drvdata; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + /* _i8k_get_temp() is fine since we do not care about the actual value */ + if (data->temp_type[channel] >= 0 || _i8k_get_temp(channel) >= 0) + return 0444; + + break; + case hwmon_temp_label: + if (data->temp_type[channel] >= 0) + return 0444; - temp = i8k_get_temp(index); - if (temp < 0) - return temp; - return sprintf(buf, "%d\n", temp * 1000); + break; + default: + break; + } + break; + case hwmon_fan: + if (data->disallow_fan_support) + break; + + switch (attr) { + case hwmon_fan_input: + if (data->fan[channel]) + return 0444; + + break; + case hwmon_fan_label: + if (data->fan[channel] && !data->disallow_fan_type_call) + return 0444; + + break; + case hwmon_fan_min: + case hwmon_fan_max: + case hwmon_fan_target: + if (data->fan_nominal_speed[channel]) + return 0444; + + break; + default: + break; + } + break; + case hwmon_pwm: + if (data->disallow_fan_support) + break; + + switch (attr) { + case hwmon_pwm_input: + if (data->fan[channel]) + return 0644; + + break; + case hwmon_pwm_enable: + if (data->auto_fan) + /* + * There is no command for retrieve the current status + * from BIOS, and userspace/firmware itself can change + * it. + * Thus we can only provide write-only access for now. + */ + return 0200; + + break; + default: + break; + } + break; + default: + break; + } + + return 0; } -static ssize_t i8k_hwmon_fan_label_show(struct device *dev, - struct device_attribute *devattr, - char *buf) +static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) +{ + struct dell_smm_data *data = dev_get_drvdata(dev); + int mult = data->i8k_fan_mult; + int ret; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + ret = i8k_get_temp(channel); + if (ret < 0) + return ret; + + *val = ret * 1000; + + return 0; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + ret = i8k_get_fan_speed(data, channel); + if (ret < 0) + return ret; + + *val = ret; + + return 0; + case hwmon_fan_min: + *val = data->fan_nominal_speed[channel][0] * mult; + + return 0; + case hwmon_fan_max: + *val = data->fan_nominal_speed[channel][data->i8k_fan_max] * mult; + + return 0; + case hwmon_fan_target: + ret = i8k_get_fan_status(data, channel); + if (ret < 0) + return ret; + + if (ret > data->i8k_fan_max) + ret = data->i8k_fan_max; + + *val = data->fan_nominal_speed[channel][ret] * mult; + + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = i8k_get_fan_status(data, channel); + if (ret < 0) + return ret; + + *val = clamp_val(ret * data->i8k_pwm_mult, 0, 255); + + return 0; + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel) { - static const char * const labels[] = { - "Processor Fan", - "Motherboard Fan", - "Video Fan", - "Power Supply Fan", - "Chipset Fan", - "Other Fan", - }; - int index = to_sensor_dev_attr(devattr)->index; bool dock = false; - int type; + int type = i8k_get_fan_type(data, channel); - type = i8k_get_fan_type(index); if (type < 0) - return type; + return ERR_PTR(type); if (type & 0x10) { dock = true; type &= 0x0F; } - if (type >= ARRAY_SIZE(labels)) - type = (ARRAY_SIZE(labels) - 1); + if (type >= ARRAY_SIZE(fan_labels)) + type = ARRAY_SIZE(fan_labels) - 1; - return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]); + return dock ? docking_labels[type] : fan_labels[type]; } -static ssize_t i8k_hwmon_fan_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) { - int index = to_sensor_dev_attr(devattr)->index; - int fan_speed; - - fan_speed = i8k_get_fan_speed(index); - if (fan_speed < 0) - return fan_speed; - return sprintf(buf, "%d\n", fan_speed); -} - -static ssize_t i8k_hwmon_pwm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - int status; + struct dell_smm_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = temp_labels[data->temp_type[channel]]; + return 0; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_label: + *str = dell_smm_fan_label(data, channel); + return PTR_ERR_OR_ZERO(*str); + default: + break; + } + break; + default: + break; + } - status = i8k_get_fan_status(index); - if (status < 0) - return -EIO; - return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255)); + return -EOPNOTSUPP; } -static ssize_t i8k_hwmon_pwm_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long val) { - int index = to_sensor_dev_attr(attr)->index; - unsigned long val; + struct dell_smm_data *data = dev_get_drvdata(dev); + unsigned long pwm; + bool enable; int err; - err = kstrtoul(buf, 10, &val); - if (err) - return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max); + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0, + data->i8k_fan_max); - mutex_lock(&i8k_mutex); - err = i8k_set_fan(index, val); - mutex_unlock(&i8k_mutex); + mutex_lock(&data->i8k_mutex); + err = i8k_set_fan(data, channel, pwm); + mutex_unlock(&data->i8k_mutex); - return err < 0 ? -EIO : count; -} + if (err < 0) + return err; -static ssize_t i8k_hwmon_pwm_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int err; - bool enable; - unsigned long val; + return 0; + case hwmon_pwm_enable: + if (!val) + return -EINVAL; - if (!auto_fan) - return -ENODEV; + if (val == 1) + enable = false; + else + enable = true; - err = kstrtoul(buf, 10, &val); - if (err) - return err; + mutex_lock(&data->i8k_mutex); + err = i8k_enable_fan_auto_mode(data, enable); + mutex_unlock(&data->i8k_mutex); - if (val == 1) - enable = false; - else if (val == 2) - enable = true; - else - return -EINVAL; + if (err < 0) + return err; - mutex_lock(&i8k_mutex); - err = i8k_enable_fan_auto_mode(enable); - mutex_unlock(&i8k_mutex); + return 0; + default: + break; + } + break; + default: + break; + } - return err ? err : count; + return -EOPNOTSUPP; } -static SENSOR_DEVICE_ATTR_RO(temp1_input, i8k_hwmon_temp, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_label, i8k_hwmon_temp_label, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, i8k_hwmon_temp, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_label, i8k_hwmon_temp_label, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, i8k_hwmon_temp, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_label, i8k_hwmon_temp_label, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, i8k_hwmon_temp, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_label, i8k_hwmon_temp_label, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_input, i8k_hwmon_temp, 4); -static SENSOR_DEVICE_ATTR_RO(temp5_label, i8k_hwmon_temp_label, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_input, i8k_hwmon_temp, 5); -static SENSOR_DEVICE_ATTR_RO(temp6_label, i8k_hwmon_temp_label, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_input, i8k_hwmon_temp, 6); -static SENSOR_DEVICE_ATTR_RO(temp7_label, i8k_hwmon_temp_label, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_input, i8k_hwmon_temp, 7); -static SENSOR_DEVICE_ATTR_RO(temp8_label, i8k_hwmon_temp_label, 7); -static SENSOR_DEVICE_ATTR_RO(temp9_input, i8k_hwmon_temp, 8); -static SENSOR_DEVICE_ATTR_RO(temp9_label, i8k_hwmon_temp_label, 8); -static SENSOR_DEVICE_ATTR_RO(temp10_input, i8k_hwmon_temp, 9); -static SENSOR_DEVICE_ATTR_RO(temp10_label, i8k_hwmon_temp_label, 9); -static SENSOR_DEVICE_ATTR_RO(fan1_input, i8k_hwmon_fan, 0); -static SENSOR_DEVICE_ATTR_RO(fan1_label, i8k_hwmon_fan_label, 0); -static SENSOR_DEVICE_ATTR_RW(pwm1, i8k_hwmon_pwm, 0); -static SENSOR_DEVICE_ATTR_WO(pwm1_enable, i8k_hwmon_pwm_enable, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, i8k_hwmon_fan, 1); -static SENSOR_DEVICE_ATTR_RO(fan2_label, i8k_hwmon_fan_label, 1); -static SENSOR_DEVICE_ATTR_RW(pwm2, i8k_hwmon_pwm, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, i8k_hwmon_fan, 2); -static SENSOR_DEVICE_ATTR_RO(fan3_label, i8k_hwmon_fan_label, 2); -static SENSOR_DEVICE_ATTR_RW(pwm3, i8k_hwmon_pwm, 2); - -static struct attribute *i8k_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ - &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */ - &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */ - &sensor_dev_attr_temp2_label.dev_attr.attr, /* 3 */ - &sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */ - &sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */ - &sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */ - &sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */ - &sensor_dev_attr_temp5_input.dev_attr.attr, /* 8 */ - &sensor_dev_attr_temp5_label.dev_attr.attr, /* 9 */ - &sensor_dev_attr_temp6_input.dev_attr.attr, /* 10 */ - &sensor_dev_attr_temp6_label.dev_attr.attr, /* 11 */ - &sensor_dev_attr_temp7_input.dev_attr.attr, /* 12 */ - &sensor_dev_attr_temp7_label.dev_attr.attr, /* 13 */ - &sensor_dev_attr_temp8_input.dev_attr.attr, /* 14 */ - &sensor_dev_attr_temp8_label.dev_attr.attr, /* 15 */ - &sensor_dev_attr_temp9_input.dev_attr.attr, /* 16 */ - &sensor_dev_attr_temp9_label.dev_attr.attr, /* 17 */ - &sensor_dev_attr_temp10_input.dev_attr.attr, /* 18 */ - &sensor_dev_attr_temp10_label.dev_attr.attr, /* 19 */ - &sensor_dev_attr_fan1_input.dev_attr.attr, /* 20 */ - &sensor_dev_attr_fan1_label.dev_attr.attr, /* 21 */ - &sensor_dev_attr_pwm1.dev_attr.attr, /* 22 */ - &sensor_dev_attr_pwm1_enable.dev_attr.attr, /* 23 */ - &sensor_dev_attr_fan2_input.dev_attr.attr, /* 24 */ - &sensor_dev_attr_fan2_label.dev_attr.attr, /* 25 */ - &sensor_dev_attr_pwm2.dev_attr.attr, /* 26 */ - &sensor_dev_attr_fan3_input.dev_attr.attr, /* 27 */ - &sensor_dev_attr_fan3_label.dev_attr.attr, /* 28 */ - &sensor_dev_attr_pwm3.dev_attr.attr, /* 29 */ +static const struct hwmon_ops dell_smm_ops = { + .is_visible = dell_smm_is_visible, + .read = dell_smm_read, + .read_string = dell_smm_read_string, + .write = dell_smm_write, +}; + +static const struct hwmon_channel_info *dell_smm_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + 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_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_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | + HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | + HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | + HWMON_F_TARGET + ), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT + ), NULL }; -static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, - int index) -{ - if (disallow_fan_support && index >= 8) - return 0; - if (disallow_fan_type_call && - (index == 9 || index == 12 || index == 15)) - return 0; - if (index >= 0 && index <= 1 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) - return 0; - if (index >= 2 && index <= 3 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) - return 0; - if (index >= 4 && index <= 5 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) - return 0; - if (index >= 6 && index <= 7 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) - return 0; - if (index >= 8 && index <= 9 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP5)) - return 0; - if (index >= 10 && index <= 11 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP6)) - return 0; - if (index >= 12 && index <= 13 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP7)) - return 0; - if (index >= 14 && index <= 15 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP8)) - return 0; - if (index >= 16 && index <= 17 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP9)) - return 0; - if (index >= 18 && index <= 19 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP10)) - return 0; +static const struct hwmon_chip_info dell_smm_chip_info = { + .ops = &dell_smm_ops, + .info = dell_smm_info, +}; - if (index >= 20 && index <= 23 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) - return 0; - if (index >= 24 && index <= 26 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) - return 0; - if (index >= 27 && index <= 29 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN3)) - return 0; +static int __init dell_smm_init_cdev(struct device *dev, u8 fan_num) +{ + struct dell_smm_data *data = dev_get_drvdata(dev); + struct thermal_cooling_device *cdev; + struct dell_smm_cooling_data *cdata; + int ret = 0; + char *name; + + name = kasprintf(GFP_KERNEL, "dell-smm-fan%u", fan_num + 1); + if (!name) + return -ENOMEM; + + cdata = devm_kmalloc(dev, sizeof(*cdata), GFP_KERNEL); + if (cdata) { + cdata->fan_num = fan_num; + cdata->data = data; + cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata, + &dell_smm_cooling_ops); + if (IS_ERR(cdev)) { + devm_kfree(dev, cdata); + ret = PTR_ERR(cdev); + } + } else { + ret = -ENOMEM; + } - if (index == 23 && !auto_fan) - return 0; + kfree(name); - return attr->mode; + return ret; } -static const struct attribute_group i8k_group = { - .attrs = i8k_attrs, - .is_visible = i8k_is_visible, -}; -__ATTRIBUTE_GROUPS(i8k); - -static int __init i8k_init_hwmon(void) +static int __init dell_smm_init_hwmon(struct device *dev) { - int err; + struct dell_smm_data *data = dev_get_drvdata(dev); + struct device *dell_smm_hwmon_dev; + int state, err; + u8 i; + + for (i = 0; i < DELL_SMM_NO_TEMP; i++) { + data->temp_type[i] = i8k_get_temp_type(i); + if (data->temp_type[i] < 0) + continue; + + if (data->temp_type[i] >= ARRAY_SIZE(temp_labels)) + data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1; + } - i8k_hwmon_flags = 0; - - /* CPU temperature attributes, if temperature type is OK */ - err = i8k_get_temp_type(0); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1; - /* check for additional temperature sensors */ - err = i8k_get_temp_type(1); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2; - err = i8k_get_temp_type(2); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3; - err = i8k_get_temp_type(3); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; - err = i8k_get_temp_type(4); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP5; - err = i8k_get_temp_type(5); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP6; - err = i8k_get_temp_type(6); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP7; - err = i8k_get_temp_type(7); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP8; - err = i8k_get_temp_type(8); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP9; - err = i8k_get_temp_type(9); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP10; - - /* First fan attributes, if fan status or type is OK */ - err = i8k_get_fan_status(0); - if (err < 0) - err = i8k_get_fan_type(0); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1; - - /* Second fan attributes, if fan status or type is OK */ - err = i8k_get_fan_status(1); - if (err < 0) - err = i8k_get_fan_type(1); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2; - - /* Third fan attributes, if fan status or type is OK */ - err = i8k_get_fan_status(2); - if (err < 0) - err = i8k_get_fan_type(2); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN3; - - i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell_smm", - NULL, i8k_groups); - if (IS_ERR(i8k_hwmon_dev)) { - err = PTR_ERR(i8k_hwmon_dev); - i8k_hwmon_dev = NULL; - pr_err("hwmon registration failed (%d)\n", err); - return err; + for (i = 0; i < DELL_SMM_NO_FANS; i++) { + data->fan_type[i] = INT_MIN; + err = i8k_get_fan_status(data, i); + if (err < 0) + err = i8k_get_fan_type(data, i); + + if (err < 0) + continue; + + data->fan[i] = true; + + /* the cooling device is not critical, ignore failures */ + if (IS_REACHABLE(CONFIG_THERMAL)) { + err = dell_smm_init_cdev(dev, i); + if (err < 0) + dev_warn(dev, "Failed to register cooling device for fan %u\n", + i + 1); + } + + data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1, + sizeof(*data->fan_nominal_speed[i]), + GFP_KERNEL); + if (!data->fan_nominal_speed[i]) + continue; + + for (state = 0; state <= data->i8k_fan_max; state++) { + err = i8k_get_fan_nominal_speed(data, i, state); + if (err < 0) { + /* Mark nominal speed table as invalid in case of error */ + devm_kfree(dev, data->fan_nominal_speed[i]); + data->fan_nominal_speed[i] = NULL; + break; + } + data->fan_nominal_speed[i][state] = err; + /* + * Autodetect fan multiplier based on nominal rpm if multiplier + * was not specified as module param or in DMI. If fan reports + * rpm value too high then set multiplier to 1. + */ + if (!fan_mult && err > I8K_FAN_RPM_THRESHOLD) + data->i8k_fan_mult = 1; + } } - return 0; + + dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data, + &dell_smm_chip_info, NULL); + + return PTR_ERR_OR_ZERO(dell_smm_hwmon_dev); } struct i8k_config_data { @@ -979,7 +1064,12 @@ enum i8k_configs { DELL_XPS, }; -static const struct i8k_config_data i8k_config_data[] = { +/* + * Only use for machines which need some special configuration + * in order to work correctly (e.g. if autoconfig fails on this machines). + */ + +static const struct i8k_config_data i8k_config_data[] __initconst = { [DELL_LATITUDE_D520] = { .fan_mult = 1, .fan_max = I8K_FAN_TURBO, @@ -1000,6 +1090,13 @@ static const struct i8k_config_data i8k_config_data[] = { static const struct dmi_system_id i8k_dmi_table[] __initconst = { { + .ident = "Dell G5 5590", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5590"), + }, + }, + { .ident = "Dell Inspiron", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), @@ -1073,13 +1170,6 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = { }, }, { - .ident = "Dell XPS421", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"), - }, - }, - { .ident = "Dell Studio", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), @@ -1088,14 +1178,6 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = { .driver_data = (void *)&i8k_config_data[DELL_STUDIO], }, { - .ident = "Dell XPS 13", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS13"), - }, - .driver_data = (void *)&i8k_config_data[DELL_XPS], - }, - { .ident = "Dell XPS M140", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), @@ -1104,17 +1186,10 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = { .driver_data = (void *)&i8k_config_data[DELL_XPS], }, { - .ident = "Dell XPS 15 9560", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS 15 9560"), - }, - }, - { - .ident = "Dell XPS 15 9570", + .ident = "Dell XPS", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS 15 9570"), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS"), }, }, { } @@ -1150,6 +1225,13 @@ static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 580 "), }, }, + { + .ident = "Dell Inspiron 3505", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 3505"), + }, + }, { } }; @@ -1159,7 +1241,7 @@ static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst * support for affected blacklisted Dell machines stay disabled. * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751 */ -static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = { +static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = { { .ident = "Dell Inspiron 7720", .matches = { @@ -1181,6 +1263,13 @@ static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), }, }, + { + .ident = "Dell XPS 15 L502X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"), + }, + }, { } }; @@ -1193,19 +1282,19 @@ enum i8k_fan_controls { I8K_FAN_34A3_35A3, }; -static const struct i8k_fan_control_data i8k_fan_control_data[] = { +static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = { [I8K_FAN_34A3_35A3] = { .manual_fan = 0x34a3, .auto_fan = 0x35a3, }, }; -static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = { +static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { { - .ident = "Dell Precision 5530", + .ident = "Dell Latitude 5480", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"), }, .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], }, @@ -1217,57 +1306,76 @@ static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = { }, .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], }, + { + .ident = "Dell Latitude E7440", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, + { + .ident = "Dell Precision 5530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, + { + .ident = "Dell Precision 7510", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 7510"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, + { + .ident = "Dell XPS 13 7390", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 7390"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, { } }; -/* - * Probe for the presence of a supported laptop. - */ -static int __init i8k_probe(void) +static int __init dell_smm_probe(struct platform_device *pdev) { + struct dell_smm_data *data; const struct dmi_system_id *id, *fan_control; - int fan, ret; + int ret; - /* - * Get DMI information - */ - if (!dmi_check_system(i8k_dmi_table)) { - if (!ignore_dmi && !force) - return -ENODEV; + data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - pr_info("not running on a supported Dell system.\n"); - pr_info("vendor=%s, model=%s, version=%s\n", - i8k_get_dmi_data(DMI_SYS_VENDOR), - i8k_get_dmi_data(DMI_PRODUCT_NAME), - i8k_get_dmi_data(DMI_BIOS_VERSION)); - } + mutex_init(&data->i8k_mutex); + platform_set_drvdata(pdev, data); if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) { - pr_warn("broken Dell BIOS detected, disallow fan support\n"); - if (!force) - disallow_fan_support = true; + if (!force) { + dev_notice(&pdev->dev, "Disabling fan support due to BIOS bugs\n"); + data->disallow_fan_support = true; + } else { + dev_warn(&pdev->dev, "Enabling fan support despite BIOS bugs\n"); + } } if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) { - pr_warn("broken Dell BIOS detected, disallow fan type call\n"); - if (!force) - disallow_fan_type_call = true; + if (!force) { + dev_notice(&pdev->dev, "Disabling fan type call due to BIOS bugs\n"); + data->disallow_fan_type_call = true; + } else { + dev_warn(&pdev->dev, "Enabling fan type call despite BIOS bugs\n"); + } } - strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), - sizeof(bios_version)); - strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), - sizeof(bios_machineid)); - - /* - * Get SMM Dell signature - */ - 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; - } + strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), + sizeof(data->bios_version)); + strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), + sizeof(data->bios_machineid)); /* * Set fan multiplier and maximal fan speed from dmi config @@ -1276,65 +1384,84 @@ static int __init i8k_probe(void) id = dmi_first_match(i8k_dmi_table); if (id && id->driver_data) { const struct i8k_config_data *conf = id->driver_data; + if (!fan_mult && conf->fan_mult) fan_mult = conf->fan_mult; + if (!fan_max && conf->fan_max) fan_max = conf->fan_max; } - i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ - i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max); + /* All options must not be 0 */ + data->i8k_fan_mult = fan_mult ? : I8K_FAN_MULT; + data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; + data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max); fan_control = dmi_first_match(i8k_whitelist_fan_control); if (fan_control && fan_control->driver_data) { - const struct i8k_fan_control_data *data = fan_control->driver_data; + const struct i8k_fan_control_data *control = fan_control->driver_data; - manual_fan = data->manual_fan; - auto_fan = data->auto_fan; - pr_info("enabling support for setting automatic/manual fan control\n"); + data->manual_fan = control->manual_fan; + data->auto_fan = control->auto_fan; + dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n"); } - if (!fan_mult) { - /* - * Autodetect fan multiplier based on nominal rpm - * If fan reports rpm value too high then set multiplier to 1 - */ - for (fan = 0; fan < 2; ++fan) { - ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max); - if (ret < 0) - continue; - if (ret > I8K_FAN_MAX_RPM) - i8k_fan_mult = 1; - break; - } - } else { - /* Fan multiplier was specified in module param or in dmi */ - i8k_fan_mult = fan_mult; - } + ret = dell_smm_init_hwmon(&pdev->dev); + if (ret) + return ret; + + i8k_init_procfs(&pdev->dev); return 0; } +static struct platform_driver dell_smm_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, +}; + +static struct platform_device *dell_smm_device; + +/* + * Probe for the presence of a supported laptop. + */ static int __init i8k_init(void) { - int err; + /* + * Get DMI information + */ + if (!dmi_check_system(i8k_dmi_table)) { + if (!ignore_dmi && !force) + return -ENODEV; - /* Are we running on an supported laptop? */ - if (i8k_probe()) - return -ENODEV; + pr_info("not running on a supported Dell system.\n"); + pr_info("vendor=%s, model=%s, version=%s\n", + i8k_get_dmi_data(DMI_SYS_VENDOR), + i8k_get_dmi_data(DMI_PRODUCT_NAME), + i8k_get_dmi_data(DMI_BIOS_VERSION)); + } - err = i8k_init_hwmon(); - if (err) - return err; + /* + * Get SMM Dell signature + */ + 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; + } - i8k_init_procfs(); - return 0; + dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL, + 0); + + return PTR_ERR_OR_ZERO(dell_smm_device); } static void __exit i8k_exit(void) { - hwmon_device_unregister(i8k_hwmon_dev); - i8k_exit_procfs(); + platform_device_unregister(dell_smm_device); + platform_driver_unregister(&dell_smm_driver); } module_init(i8k_init); diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index c3472b73fa79..66c48f70fae7 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -203,7 +203,7 @@ struct dme1737_data { unsigned int addr; /* for ISA devices only */ struct mutex update_lock; - int valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_update; /* in jiffies */ unsigned long last_vbat; /* in jiffies */ enum chips type; @@ -778,7 +778,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) } data->last_update = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -2456,13 +2456,14 @@ static int dme1737_i2c_detect(struct i2c_client *client, dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n", verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737", client->addr, verstep); - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } -static int dme1737_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id dme1737_id[]; + +static int dme1737_i2c_probe(struct i2c_client *client) { struct dme1737_data *data; struct device *dev = &client->dev; @@ -2473,7 +2474,7 @@ static int dme1737_i2c_probe(struct i2c_client *client, return -ENOMEM; i2c_set_clientdata(client, data); - data->type = id->driver_data; + data->type = i2c_match_id(dme1737_id, client)->driver_data; data->client = client; data->name = client->name; mutex_init(&data->update_lock); @@ -2507,14 +2508,12 @@ exit_remove: return err; } -static int dme1737_i2c_remove(struct i2c_client *client) +static void dme1737_i2c_remove(struct i2c_client *client) { struct dme1737_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); dme1737_remove_files(&client->dev); - - return 0; } static const struct i2c_device_id dme1737_id[] = { @@ -2529,7 +2528,7 @@ static struct i2c_driver dme1737_i2c_driver = { .driver = { .name = "dme1737", }, - .probe = dme1737_i2c_probe, + .probe_new = dme1737_i2c_probe, .remove = dme1737_i2c_remove, .id_table = dme1737_id, .detect = dme1737_i2c_detect, diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c index 370d0c74eb01..5bac2b0fc7bb 100644 --- a/drivers/hwmon/drivetemp.c +++ b/drivers/hwmon/drivetemp.c @@ -10,7 +10,7 @@ * hwmon: Driver for SCSI/ATA temperature sensors * by Constantin Baranov <const@mimas.ru>, submitted September 2009 * - * This drive supports reporting the temperatire of SATA drives. It can be + * This drive supports reporting the temperature of SATA drives. It can be * easily extended to report the temperature of SCSI drives. * * The primary means to read drive temperatures and temperature limits @@ -264,12 +264,18 @@ static int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val) return err; switch (attr) { case hwmon_temp_input: + if (!temp_is_valid(buf[SCT_STATUS_TEMP])) + return -ENODATA; *val = temp_from_sct(buf[SCT_STATUS_TEMP]); break; case hwmon_temp_lowest: + if (!temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST])) + return -ENODATA; *val = temp_from_sct(buf[SCT_STATUS_TEMP_LOWEST]); break; case hwmon_temp_highest: + if (!temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST])) + return -ENODATA; *val = temp_from_sct(buf[SCT_STATUS_TEMP_HIGHEST]); break; default: @@ -279,6 +285,42 @@ static int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val) return err; } +static const char * const sct_avoid_models[] = { +/* + * These drives will have WRITE FPDMA QUEUED command timeouts and sometimes just + * freeze until power-cycled under heavy write loads when their temperature is + * getting polled in SCT mode. The SMART mode seems to be fine, though. + * + * While only the 3 TB model (DT01ACA3) was actually caught exhibiting the + * problem let's play safe here to avoid data corruption and ban the whole + * DT01ACAx family. + + * The models from this array are prefix-matched. + */ + "TOSHIBA DT01ACA", +}; + +static bool drivetemp_sct_avoid(struct drivetemp_data *st) +{ + struct scsi_device *sdev = st->sdev; + unsigned int ctr; + + if (!sdev->model) + return false; + + /* + * The "model" field contains just the raw SCSI INQUIRY response + * "product identification" field, which has a width of 16 bytes. + * This field is space-filled, but is NOT NULL-terminated. + */ + for (ctr = 0; ctr < ARRAY_SIZE(sct_avoid_models); ctr++) + if (!strncmp(sdev->model, sct_avoid_models[ctr], + strlen(sct_avoid_models[ctr]))) + return true; + + return false; +} + static int drivetemp_identify_sata(struct drivetemp_data *st) { struct scsi_device *sdev = st->sdev; @@ -320,6 +362,13 @@ static int drivetemp_identify_sata(struct drivetemp_data *st) /* bail out if this is not a SATA device */ if (!is_ata || !is_sata) return -ENODEV; + + if (have_sct && drivetemp_sct_avoid(st)) { + dev_notice(&sdev->sdev_gendev, + "will avoid using SCT for temperature monitoring\n"); + have_sct = false; + } + if (!have_sct) goto skip_sct; @@ -340,7 +389,7 @@ static int drivetemp_identify_sata(struct drivetemp_data *st) st->have_temp_highest = temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]); if (!have_sct_data_table) - goto skip_sct; + goto skip_sct_data; /* Request and read temperature history table */ memset(buf, '\0', sizeof(st->smartdata)); @@ -572,3 +621,4 @@ module_exit(drivetemp_exit); MODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>"); MODULE_DESCRIPTION("Hard drive temperature monitor"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:drivetemp"); diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 541bed8732b7..0886abf6ebab 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -109,7 +109,7 @@ static const u8 DS1621_REG_TEMP[3] = { struct ds1621_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ enum chips kind; /* device type */ @@ -213,7 +213,7 @@ static struct ds1621_data *ds1621_update_client(struct device *dev) new_conf); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -326,7 +326,7 @@ static struct attribute *ds1621_attributes[] = { static umode_t ds1621_attribute_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct ds1621_data *data = dev_get_drvdata(dev); if (attr == &dev_attr_update_interval.attr) @@ -342,8 +342,9 @@ static const struct attribute_group ds1621_group = { }; __ATTRIBUTE_GROUPS(ds1621); -static int ds1621_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id ds1621_id[]; + +static int ds1621_probe(struct i2c_client *client) { struct ds1621_data *data; struct device *hwmon_dev; @@ -355,7 +356,7 @@ static int ds1621_probe(struct i2c_client *client, mutex_init(&data->update_lock); - data->kind = id->driver_data; + data->kind = i2c_match_id(ds1621_id, client)->driver_data; data->client = client; /* Initialize the DS1621 chip */ @@ -383,7 +384,7 @@ static struct i2c_driver ds1621_driver = { .driver = { .name = "ds1621", }, - .probe = ds1621_probe, + .probe_new = ds1621_probe, .id_table = ds1621_id, }; diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index 8f1fc83ac37b..82d7c3d58f49 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -56,7 +56,7 @@ static const u8 DS620_REG_TEMP[3] = { struct ds620_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ s16 temp[3]; /* Register values, word */ @@ -118,7 +118,7 @@ static struct ds620_data *ds620_update_client(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } abort: mutex_unlock(&data->update_lock); @@ -211,8 +211,7 @@ static struct attribute *ds620_attrs[] = { ATTRIBUTE_GROUPS(ds620); -static int ds620_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds620_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -246,7 +245,7 @@ static struct i2c_driver ds620_driver = { .driver = { .name = "ds620", }, - .probe = ds620_probe, + .probe_new = ds620_probe, .id_table = ds620_id, }; diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index cf0962f7a020..61d59189a6d1 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -329,22 +329,22 @@ static int emc1403_detect(struct i2c_client *client, id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG); switch (id) { case 0x20: - strlcpy(info->type, "emc1402", I2C_NAME_SIZE); + strscpy(info->type, "emc1402", I2C_NAME_SIZE); break; case 0x21: - strlcpy(info->type, "emc1403", I2C_NAME_SIZE); + strscpy(info->type, "emc1403", I2C_NAME_SIZE); break; case 0x22: - strlcpy(info->type, "emc1422", I2C_NAME_SIZE); + strscpy(info->type, "emc1422", I2C_NAME_SIZE); break; case 0x23: - strlcpy(info->type, "emc1423", I2C_NAME_SIZE); + strscpy(info->type, "emc1423", I2C_NAME_SIZE); break; case 0x25: - strlcpy(info->type, "emc1404", I2C_NAME_SIZE); + strscpy(info->type, "emc1404", I2C_NAME_SIZE); break; case 0x27: - strlcpy(info->type, "emc1424", I2C_NAME_SIZE); + strscpy(info->type, "emc1424", I2C_NAME_SIZE); break; default: return -ENODEV; @@ -386,11 +386,13 @@ static const struct regmap_config emc1403_regmap_config = { .volatile_reg = emc1403_regmap_is_volatile, }; -static int emc1403_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id emc1403_idtable[]; + +static int emc1403_probe(struct i2c_client *client) { struct thermal_data *data; struct device *hwmon_dev; + const struct i2c_device_id *id = i2c_match_id(emc1403_idtable, client); data = devm_kzalloc(&client->dev, sizeof(struct thermal_data), GFP_KERNEL); @@ -406,10 +408,10 @@ static int emc1403_probe(struct i2c_client *client, switch (id->driver_data) { case emc1404: data->groups[2] = &emc1404_group; - /* fall through */ + fallthrough; case emc1403: data->groups[1] = &emc1403_group; - /* fall through */ + fallthrough; case emc1402: data->groups[0] = &emc1402_group; } @@ -452,7 +454,7 @@ static struct i2c_driver sensor_emc1403 = { .name = "emc1403", }, .detect = emc1403_detect, - .probe = emc1403_probe, + .probe_new = emc1403_probe, .id_table = emc1403_idtable, .address_list = emc1403_address_list, }; diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 491a570e8e50..361cf9292456 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -443,7 +443,7 @@ static ssize_t pwm1_enable_store(struct device *dev, } result = read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg); - if (result) { + if (result < 0) { count = result; goto err; } @@ -551,7 +551,7 @@ static const struct attribute_group emc2103_temp4_group = { }; static int -emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id) +emc2103_probe(struct i2c_client *client) { struct emc2103_data *data; struct device *hwmon_dev; @@ -643,7 +643,7 @@ emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info) if ((product != 0x24) && (product != 0x26)) return -ENODEV; - strlcpy(info->type, "emc2103", I2C_NAME_SIZE); + strscpy(info->type, "emc2103", I2C_NAME_SIZE); return 0; } @@ -653,7 +653,7 @@ static struct i2c_driver emc2103_driver = { .driver = { .name = "emc2103", }, - .probe = emc2103_probe, + .probe_new = emc2103_probe, .id_table = emc2103_ids, .detect = emc2103_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c new file mode 100644 index 000000000000..aa1f25add0b6 --- /dev/null +++ b/drivers/hwmon/emc2305.c @@ -0,0 +1,620 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for EMC2305 fan controller + * + * Copyright (C) 2022 Nvidia Technologies Ltd. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/platform_data/emc2305.h> +#include <linux/thermal.h> + +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 +#define EMC2305_FAN_MAX_STATE 10 +#define EMC2305_DEVICE 0x34 +#define EMC2305_VENDOR 0x5d +#define EMC2305_REG_PRODUCT_ID 0xfd +#define EMC2305_TACH_REGS_UNUSE_BITS 3 +#define EMC2305_TACH_CNT_MULTIPLIER 0x02 +#define EMC2305_TACH_RANGE_MIN 480 + +#define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ + DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) +#define EMC2305_PWM_STATE2DUTY(state, max_state, pwm_max) \ + DIV_ROUND_CLOSEST((state) * (pwm_max), (max_state)) + +/* + * Factor by equations [2] and [3] from data sheet; valid for fans where the number of edges + * equal (poles * 2 + 1). + */ +#define EMC2305_RPM_FACTOR 3932160 + +#define EMC2305_REG_FAN_DRIVE(n) (0x30 + 0x10 * (n)) +#define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * (n)) +#define EMC2305_REG_FAN_TACH(n) (0x3e + 0x10 * (n)) + +enum emc230x_product_id { + EMC2305 = 0x34, + EMC2303 = 0x35, + EMC2302 = 0x36, + EMC2301 = 0x37, +}; + +static const struct i2c_device_id emc2305_ids[] = { + { "emc2305", 0 }, + { "emc2303", 0 }, + { "emc2302", 0 }, + { "emc2301", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc2305_ids); + +/** + * @cdev: cooling device; + * @curr_state: cooling current state; + * @last_hwmon_state: last cooling state updated by hwmon subsystem; + * @last_thermal_state: last cooling state updated by thermal subsystem; + * + * The 'last_hwmon_state' and 'last_thermal_state' fields are provided to support fan low limit + * speed feature. The purpose of this feature is to provides ability to limit fan speed + * according to some system wise considerations, like absence of some replaceable units (PSU or + * line cards), high system ambient temperature, unreliable transceivers temperature sensing or + * some other factors which indirectly impacts system's airflow + * Fan low limit feature is supported through 'hwmon' interface: 'hwmon' 'pwm' attribute is + * used for setting low limit for fan speed in case 'thermal' subsystem is configured in + * kernel. In this case setting fan speed through 'hwmon' will never let the 'thermal' + * subsystem to select a lower duty cycle than the duty cycle selected with the 'pwm' + * attribute. + * From other side, fan speed is to be updated in hardware through 'pwm' only in case the + * requested fan speed is above last speed set by 'thermal' subsystem, otherwise requested fan + * speed will be just stored with no PWM update. + */ +struct emc2305_cdev_data { + struct thermal_cooling_device *cdev; + unsigned int cur_state; + unsigned long last_hwmon_state; + unsigned long last_thermal_state; +}; + +/** + * @client: i2c client; + * @hwmon_dev: hwmon device; + * @max_state: maximum cooling state of the cooling device; + * @pwm_num: number of PWM channels; + * @pwm_separate: separate PWM settings for every channel; + * @pwm_min: array of minimum PWM per channel; + * @cdev_data: array of cooling devices data; + */ +struct emc2305_data { + struct i2c_client *client; + struct device *hwmon_dev; + u8 max_state; + u8 pwm_num; + bool pwm_separate; + u8 pwm_min[EMC2305_PWM_MAX]; + struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; +}; + +static char *emc2305_fan_name[] = { + "emc2305_fan", + "emc2305_fan1", + "emc2305_fan2", + "emc2305_fan3", + "emc2305_fan4", + "emc2305_fan5", +}; + +static void emc2305_unset_tz(struct device *dev); + +static int emc2305_get_max_channel(const struct emc2305_data *data) +{ + return data->pwm_num; +} + +static int emc2305_get_cdev_idx(struct thermal_cooling_device *cdev) +{ + struct emc2305_data *data = cdev->devdata; + size_t len = strlen(cdev->type); + int ret; + + if (len <= 0) + return -EINVAL; + + /* + * Returns index of cooling device 0..4 in case of separate PWM setting. + * Zero index is used in case of one common PWM setting. + * If the mode is not set as pwm_separate, all PWMs are to be bound + * to the common thermal zone and should work at the same speed + * to perform cooling for the same thermal junction. + * Otherwise, return specific channel that will be used in bound + * related PWM to the thermal zone. + */ + if (!data->pwm_separate) + return 0; + + ret = cdev->type[len - 1]; + switch (ret) { + case '1' ... '5': + return ret - '1'; + default: + break; + } + return -EINVAL; +} + +static int emc2305_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + int cdev_idx; + struct emc2305_data *data = cdev->devdata; + + cdev_idx = emc2305_get_cdev_idx(cdev); + if (cdev_idx < 0) + return cdev_idx; + + *state = data->cdev_data[cdev_idx].cur_state; + return 0; +} + +static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct emc2305_data *data = cdev->devdata; + *state = data->max_state; + 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; + 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); + + data->cdev_data[cdev_idx].cur_state = state; + if (data->pwm_separate) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(cdev_idx), val); + if (ret < 0) + return ret; + } else { + /* + * Set the same PWM value in all channels + * if common PWM channel is used. + */ + for (i = 0; i < data->pwm_num; i++) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i), val); + 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, + .set_cur_state = emc2305_set_cur_state, +}; + +static int emc2305_show_fault(struct device *dev, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int status_reg; + + status_reg = i2c_smbus_read_byte_data(client, EMC2305_REG_DRIVE_FAIL_STATUS); + if (status_reg < 0) + return status_reg; + + return status_reg & (1 << channel) ? 1 : 0; +} + +static int emc2305_show_fan(struct device *dev, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret; + + ret = i2c_smbus_read_word_swapped(client, EMC2305_REG_FAN_TACH(channel)); + if (ret <= 0) + return ret; + + ret = ret >> EMC2305_TACH_REGS_UNUSE_BITS; + ret = EMC2305_RPM_FACTOR / ret; + if (ret <= EMC2305_TACH_RANGE_MIN) + return 0; + + return ret * EMC2305_TACH_CNT_MULTIPLIER; +} + +static int emc2305_show_pwm(struct device *dev, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + return i2c_smbus_read_byte_data(client, EMC2305_REG_FAN_DRIVE(channel)); +} + +static int emc2305_set_pwm(struct device *dev, long val, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret; + + if (val < data->pwm_min[channel] || val > EMC2305_FAN_MAX) + return -EINVAL; + + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(channel), val); + if (ret < 0) + return ret; + data->cdev_data[channel].cur_state = EMC2305_PWM_DUTY2STATE(val, data->max_state, + EMC2305_FAN_MAX); + return 0; +} + +static int emc2305_set_single_tz(struct device *dev, int idx) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + long pwm; + int i, cdev_idx, ret; + + cdev_idx = (idx) ? idx - 1 : 0; + pwm = data->pwm_min[cdev_idx]; + + data->cdev_data[cdev_idx].cdev = + thermal_cooling_device_register(emc2305_fan_name[idx], data, + &emc2305_cooling_ops); + + if (IS_ERR(data->cdev_data[cdev_idx].cdev)) { + dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); + return PTR_ERR(data->cdev_data[cdev_idx].cdev); + } + /* Set minimal PWM speed. */ + if (data->pwm_separate) { + ret = emc2305_set_pwm(dev, pwm, cdev_idx); + if (ret < 0) + return ret; + } else { + for (i = 0; i < data->pwm_num; i++) { + ret = emc2305_set_pwm(dev, pwm, i); + if (ret < 0) + return ret; + } + } + data->cdev_data[cdev_idx].cur_state = + EMC2305_PWM_DUTY2STATE(data->pwm_min[cdev_idx], data->max_state, + EMC2305_FAN_MAX); + data->cdev_data[cdev_idx].last_hwmon_state = + EMC2305_PWM_DUTY2STATE(data->pwm_min[cdev_idx], data->max_state, + EMC2305_FAN_MAX); + return 0; +} + +static int emc2305_set_tz(struct device *dev) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + int i, ret; + + if (!data->pwm_separate) + return emc2305_set_single_tz(dev, 0); + + for (i = 0; i < data->pwm_num; i++) { + ret = emc2305_set_single_tz(dev, i + 1); + if (ret) + goto thermal_cooling_device_register_fail; + } + return 0; + +thermal_cooling_device_register_fail: + emc2305_unset_tz(dev); + return ret; +} + +static void emc2305_unset_tz(struct device *dev) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + int i; + + /* Unregister cooling device. */ + for (i = 0; i < EMC2305_PWM_MAX; i++) + if (data->cdev_data[i].cdev) + thermal_cooling_device_unregister(data->cdev_data[i].cdev); +} + +static umode_t +emc2305_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) +{ + int max_channel = emc2305_get_max_channel(data); + + /* Don't show channels which are not physically connected. */ + if (channel >= max_channel) + return 0; + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + return 0444; + case hwmon_fan_fault: + return 0444; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + break; + } + break; + default: + break; + } + + return 0; +}; + +static int +emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + int cdev_idx; + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + /* If thermal is configured - handle PWM limit setting. */ + if (IS_REACHABLE(CONFIG_THERMAL)) { + if (data->pwm_separate) + cdev_idx = channel; + else + cdev_idx = 0; + data->cdev_data[cdev_idx].last_hwmon_state = + EMC2305_PWM_DUTY2STATE(val, data->max_state, + EMC2305_FAN_MAX); + /* + * Update PWM only in case requested state is not less than the + * last thermal state. + */ + 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, + data->cdev_data[cdev_idx].last_hwmon_state); + return 0; + } + return emc2305_set_pwm(dev, val, channel); + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +}; + +static int +emc2305_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: + ret = emc2305_show_fan(dev, channel); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_fan_fault: + ret = emc2305_show_fault(dev, channel); + if (ret < 0) + return ret; + *val = ret; + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = emc2305_show_pwm(dev, channel); + if (ret < 0) + return ret; + *val = ret; + return 0; + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +}; + +static const struct hwmon_ops emc2305_ops = { + .is_visible = emc2305_is_visible, + .read = emc2305_read, + .write = emc2305_write, +}; + +static const struct hwmon_channel_info *emc2305_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_chip_info emc2305_chip_info = { + .ops = &emc2305_ops, + .info = emc2305_info, +}; + +static int emc2305_identify(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc2305_data *data = i2c_get_clientdata(client); + int ret; + + ret = i2c_smbus_read_byte_data(client, EMC2305_REG_PRODUCT_ID); + if (ret < 0) + return ret; + + switch (ret) { + case EMC2305: + data->pwm_num = 5; + break; + case EMC2303: + data->pwm_num = 3; + break; + case EMC2302: + data->pwm_num = 2; + break; + case EMC2301: + data->pwm_num = 1; + break; + default: + return -ENODEV; + } + + return 0; +} + +static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct emc2305_data *data; + struct emc2305_platform_data *pdata; + int vendor, device; + int ret; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + vendor = i2c_smbus_read_byte_data(client, EMC2305_REG_VENDOR); + 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; + + i2c_set_clientdata(client, data); + data->client = client; + + ret = emc2305_identify(dev); + if (ret) + return ret; + + pdata = dev_get_platdata(&client->dev); + if (pdata) { + if (!pdata->max_state || pdata->max_state > EMC2305_FAN_MAX_STATE) + return -EINVAL; + data->max_state = pdata->max_state; + /* + * Validate a number of active PWM channels. Note that + * configured number can be less than the actual maximum + * supported by the device. + */ + if (!pdata->pwm_num || pdata->pwm_num > EMC2305_PWM_MAX) + return -EINVAL; + data->pwm_num = pdata->pwm_num; + data->pwm_separate = pdata->pwm_separate; + for (i = 0; i < EMC2305_PWM_MAX; i++) + data->pwm_min[i] = pdata->pwm_min[i]; + } else { + data->max_state = EMC2305_FAN_MAX_STATE; + data->pwm_separate = false; + for (i = 0; i < EMC2305_PWM_MAX; i++) + data->pwm_min[i] = EMC2305_FAN_MIN; + } + + data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "emc2305", data, + &emc2305_chip_info, NULL); + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); + + if (IS_REACHABLE(CONFIG_THERMAL)) { + ret = emc2305_set_tz(dev); + if (ret != 0) + return ret; + } + + for (i = 0; i < data->pwm_num; i++) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_MIN_DRIVE(i), + data->pwm_min[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static void emc2305_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + + if (IS_REACHABLE(CONFIG_THERMAL)) + emc2305_unset_tz(dev); +} + +static struct i2c_driver emc2305_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "emc2305", + }, + .probe = emc2305_probe, + .remove = emc2305_remove, + .id_table = emc2305_ids, + .address_list = emc2305_normal_i2c, +}; + +module_i2c_driver(emc2305_driver); + +MODULE_AUTHOR("Nvidia"); +MODULE_DESCRIPTION("Microchip EMC2305 fan controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c index df0f7292e214..bcd93f0fe982 100644 --- a/drivers/hwmon/emc6w201.c +++ b/drivers/hwmon/emc6w201.c @@ -45,7 +45,7 @@ enum subfeature { input, min, max }; struct emc6w201_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values */ @@ -162,7 +162,7 @@ static struct emc6w201_data *emc6w201_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -439,13 +439,12 @@ static int emc6w201_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "emc6w201", I2C_NAME_SIZE); + strscpy(info->type, "emc6w201", I2C_NAME_SIZE); return 0; } -static int emc6w201_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int emc6w201_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct emc6w201_data *data; @@ -475,7 +474,7 @@ static struct i2c_driver emc6w201_driver = { .driver = { .name = "emc6w201", }, - .probe = emc6w201_probe, + .probe_new = emc6w201_probe, .id_table = emc6w201_id, .detect = emc6w201_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 67b47de8263a..7f20edb0677c 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -165,7 +165,7 @@ struct f71805f_data { struct device *hwmon_dev; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ unsigned long last_limits; /* In jiffies */ @@ -404,7 +404,7 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) + (f71805f_read8(data, F71805F_REG_STATUS(2)) << 16); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index d09deb409de7..70121482a617 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -49,6 +49,8 @@ #define SIO_F81768D_ID 0x1210 /* Chipset ID */ #define SIO_F81865_ID 0x0704 /* Chipset ID */ #define SIO_F81866_ID 0x1010 /* Chipset ID */ +#define SIO_F71858AD_ID 0x0903 /* Chipset ID */ +#define SIO_F81966_ID 0x1502 /* Chipset ID */ #define REGION_LENGTH 8 #define ADDR_REG_OFFSET 5 @@ -235,13 +237,6 @@ static const char f71882fg_nr_temps[] = { static struct platform_device *f71882fg_pdev; -/* Super-I/O Function prototypes */ -static inline int superio_inb(int base, int reg); -static inline int superio_inw(int base, int reg); -static inline int superio_enter(int base); -static inline void superio_select(int base, int ld); -static inline void superio_exit(int base); - struct f71882fg_sio_data { enum chips type; }; @@ -253,7 +248,7 @@ struct f71882fg_data { struct mutex update_lock; int temp_start; /* temp numbering start (0 or 1) */ - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ char auto_point_temp_signed; unsigned long last_updated; /* In jiffies */ unsigned long last_limits; /* In jiffies */ @@ -290,108 +285,422 @@ struct f71882fg_data { s8 pwm_auto_point_temp[4][4]; }; -/* Sysfs in */ -static ssize_t show_in(struct device *dev, struct device_attribute *devattr, - char *buf); -static ssize_t show_in_max(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_in_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_in_beep(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_in_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_in_alarm(struct device *dev, struct device_attribute - *devattr, char *buf); -/* Sysfs Fan */ -static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, - char *buf); -static ssize_t show_fan_full_speed(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_fan_full_speed(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_fan_beep(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_fan_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_fan_alarm(struct device *dev, struct device_attribute - *devattr, char *buf); -/* Sysfs Temp */ -static ssize_t show_temp(struct device *dev, struct device_attribute - *devattr, char *buf); +static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg) +{ + u8 val; + + outb(reg, data->addr + ADDR_REG_OFFSET); + val = inb(data->addr + DATA_REG_OFFSET); + + return val; +} + +static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg) +{ + u16 val; + + val = f71882fg_read8(data, reg) << 8; + val |= f71882fg_read8(data, reg + 1); + + return val; +} + +static inline int fan_from_reg(u16 reg) +{ + return reg ? (1500000 / reg) : 0; +} + +static inline u16 fan_to_reg(int fan) +{ + return fan ? (1500000 / fan) : 0; +} + +static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) +{ + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val, data->addr + DATA_REG_OFFSET); +} + +static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) +{ + f71882fg_write8(data, reg, val >> 8); + f71882fg_write8(data, reg + 1, val & 0xff); +} + +static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr) +{ + if (data->type == f71858fg) + return f71882fg_read16(data, F71882FG_REG_TEMP(nr)); + else + return f71882fg_read8(data, F71882FG_REG_TEMP(nr)); +} + +static struct f71882fg_data *f71882fg_update_device(struct device *dev) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr_fans = f71882fg_nr_fans[data->type]; + int nr_temps = f71882fg_nr_temps[data->type]; + int nr, reg, point; + + mutex_lock(&data->update_lock); + + /* Update once every 60 seconds */ + if (time_after(jiffies, data->last_limits + 60 * HZ) || + !data->valid) { + if (f71882fg_has_in1_alarm[data->type]) { + if (data->type == f81866a) { + data->in1_max = + f71882fg_read8(data, + F81866_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, + F81866_REG_IN_BEEP); + } else { + data->in1_max = + f71882fg_read8(data, + F71882FG_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, + F71882FG_REG_IN_BEEP); + } + } + + /* Get High & boundary temps*/ + for (nr = data->temp_start; nr < nr_temps + data->temp_start; + nr++) { + data->temp_ovt[nr] = f71882fg_read8(data, + F71882FG_REG_TEMP_OVT(nr)); + data->temp_high[nr] = f71882fg_read8(data, + F71882FG_REG_TEMP_HIGH(nr)); + } + + if (data->type != f8000) { + data->temp_hyst[0] = f71882fg_read8(data, + F71882FG_REG_TEMP_HYST(0)); + data->temp_hyst[1] = f71882fg_read8(data, + F71882FG_REG_TEMP_HYST(1)); + } + /* All but the f71858fg / f8000 have this register */ + if ((data->type != f71858fg) && (data->type != f8000)) { + reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); + data->temp_type[1] = (reg & 0x02) ? 2 : 4; + data->temp_type[2] = (reg & 0x04) ? 2 : 4; + data->temp_type[3] = (reg & 0x08) ? 2 : 4; + } + + if (f71882fg_fan_has_beep[data->type]) + data->fan_beep = f71882fg_read8(data, + F71882FG_REG_FAN_BEEP); + + if (f71882fg_temp_has_beep[data->type]) + data->temp_beep = f71882fg_read8(data, + F71882FG_REG_TEMP_BEEP); + + data->pwm_enable = f71882fg_read8(data, + F71882FG_REG_PWM_ENABLE); + data->pwm_auto_point_hyst[0] = + f71882fg_read8(data, F71882FG_REG_FAN_HYST(0)); + data->pwm_auto_point_hyst[1] = + f71882fg_read8(data, F71882FG_REG_FAN_HYST(1)); + + for (nr = 0; nr < nr_fans; nr++) { + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, + F71882FG_REG_POINT_MAPPING(nr)); + + switch (data->type) { + default: + for (point = 0; point < 5; point++) { + data->pwm_auto_point_pwm[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, point)); + } + for (point = 0; point < 4; point++) { + data->pwm_auto_point_temp[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, point)); + } + break; + case f71808e: + case f71869: + data->pwm_auto_point_pwm[nr][0] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM(nr, 0)); + fallthrough; + case f71862fg: + data->pwm_auto_point_pwm[nr][1] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, 1)); + data->pwm_auto_point_pwm[nr][4] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, 4)); + data->pwm_auto_point_temp[nr][0] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, 0)); + data->pwm_auto_point_temp[nr][3] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, 3)); + break; + } + } + data->last_limits = jiffies; + } + + /* Update every second */ + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->temp_status = f71882fg_read8(data, + F71882FG_REG_TEMP_STATUS); + data->temp_diode_open = f71882fg_read8(data, + F71882FG_REG_TEMP_DIODE_OPEN); + for (nr = data->temp_start; nr < nr_temps + data->temp_start; + nr++) + data->temp[nr] = f71882fg_read_temp(data, nr); + + data->fan_status = f71882fg_read8(data, + F71882FG_REG_FAN_STATUS); + for (nr = 0; nr < nr_fans; nr++) { + data->fan[nr] = f71882fg_read16(data, + F71882FG_REG_FAN(nr)); + data->fan_target[nr] = + f71882fg_read16(data, F71882FG_REG_FAN_TARGET(nr)); + data->fan_full_speed[nr] = + f71882fg_read16(data, + F71882FG_REG_FAN_FULL_SPEED(nr)); + data->pwm[nr] = + f71882fg_read8(data, F71882FG_REG_PWM(nr)); + } + /* Some models have 1 more fan with limited capabilities */ + if (data->type == f71808a) { + data->fan[2] = f71882fg_read16(data, + F71882FG_REG_FAN(2)); + data->pwm[2] = f71882fg_read8(data, + F71882FG_REG_PWM(2)); + } + if (data->type == f8000) + data->fan[3] = f71882fg_read16(data, + F71882FG_REG_FAN(3)); + + if (f71882fg_has_in1_alarm[data->type]) { + if (data->type == f81866a) + data->in_status = f71882fg_read8(data, + F81866_REG_IN_STATUS); + + else + data->in_status = f71882fg_read8(data, + F71882FG_REG_IN_STATUS); + } + + for (nr = 0; nr < F71882FG_MAX_INS; nr++) + if (f71882fg_has_in[data->type][nr]) + data->in[nr] = f71882fg_read8(data, + F71882FG_REG_IN(nr)); + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static ssize_t name_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", f71882fg_names[data->type]); +} + +static DEVICE_ATTR_RO(name); + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int sign, temp; + + if (data->type == f71858fg) { + /* TEMP_TABLE_SEL 1 or 3 ? */ + if (data->temp_config & 1) { + sign = data->temp[nr] & 0x0001; + temp = (data->temp[nr] >> 5) & 0x7ff; + } else { + sign = data->temp[nr] & 0x8000; + temp = (data->temp[nr] >> 5) & 0x3ff; + } + temp *= 125; + if (sign) + temp -= 128000; + } else { + temp = ((s8)data->temp[nr]) * 1000; + } + + return sprintf(buf, "%d\n", temp); +} + static ssize_t show_temp_max(struct device *dev, struct device_attribute - *devattr, char *buf); + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + return sprintf(buf, "%d\n", data->temp_high[nr] * 1000); +} + static ssize_t store_temp_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_TEMP_HIGH(nr), val); + data->temp_high[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute - *devattr, char *buf); + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int temp_max_hyst; + + mutex_lock(&data->update_lock); + if (nr & 1) + temp_max_hyst = data->temp_hyst[nr / 2] >> 4; + else + temp_max_hyst = data->temp_hyst[nr / 2] & 0x0f; + temp_max_hyst = (data->temp_high[nr] - temp_max_hyst) * 1000; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp_max_hyst); +} + static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + ssize_t ret = count; + u8 reg; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + + mutex_lock(&data->update_lock); + + /* convert abs to relative and check */ + data->temp_high[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_HIGH(nr)); + val = clamp_val(val, data->temp_high[nr] - 15, data->temp_high[nr]); + val = data->temp_high[nr] - val; + + /* convert value to register contents */ + reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST(nr / 2)); + if (nr & 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + f71882fg_write8(data, F71882FG_REG_TEMP_HYST(nr / 2), reg); + data->temp_hyst[nr / 2] = reg; + + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_temp_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->temp_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + static ssize_t show_temp_crit(struct device *dev, struct device_attribute - *devattr, char *buf); + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + return sprintf(buf, "%d\n", data->temp_ovt[nr] * 1000); +} + static ssize_t store_temp_crit(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_TEMP_OVT(nr), val); + data->temp_ovt[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t show_temp_type(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t show_temp_beep(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_temp_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_temp_alarm(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t show_temp_fault(struct device *dev, struct device_attribute - *devattr, char *buf); -/* PWM and Auto point control */ -static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, - char *buf); -static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count); -static ssize_t show_simple_pwm(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_simple_pwm(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_enable(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_enable(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -/* Sysfs misc */ -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf); + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int temp_crit_hyst; -static int f71882fg_probe(struct platform_device *pdev); -static int f71882fg_remove(struct platform_device *pdev); + mutex_lock(&data->update_lock); + if (nr & 1) + temp_crit_hyst = data->temp_hyst[nr / 2] >> 4; + else + temp_crit_hyst = data->temp_hyst[nr / 2] & 0x0f; + temp_crit_hyst = (data->temp_ovt[nr] - temp_crit_hyst) * 1000; + mutex_unlock(&data->update_lock); -static struct platform_driver f71882fg_driver = { - .driver = { - .name = DRVNAME, - }, - .probe = f71882fg_probe, - .remove = f71882fg_remove, -}; + return sprintf(buf, "%d\n", temp_crit_hyst); +} -static DEVICE_ATTR_RO(name); +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->temp_diode_open & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} /* * Temp attr for the f71858fg, the f71858fg is special as it has its @@ -436,6 +745,15 @@ static struct sensor_device_attribute_2 f71858fg_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; +static ssize_t show_temp_type(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + return sprintf(buf, "%d\n", data->temp_type[nr]); +} + /* Temp attr for the standard models */ static struct sensor_device_attribute_2 fxxxx_temp_attr[3][9] = { { SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 1), @@ -488,6 +806,42 @@ static struct sensor_device_attribute_2 fxxxx_temp_attr[3][9] = { { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), } }; +static ssize_t show_temp_beep(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->temp_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_temp_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); + if (val) + data->temp_beep |= 1 << nr; + else + data->temp_beep &= ~(1 << nr); + + f71882fg_write8(data, F71882FG_REG_TEMP_BEEP, data->temp_beep); + mutex_unlock(&data->update_lock); + + return count; +} + /* Temp attr for models which can beep on temp alarm */ static struct sensor_device_attribute_2 fxxxx_temp_beep_attr[3][2] = { { SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, @@ -553,6 +907,15 @@ static struct sensor_device_attribute_2 f8000_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; +static ssize_t show_in(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + return sprintf(buf, "%d\n", data->in[nr] * 8); +} + /* in attr for all models */ static struct sensor_device_attribute_2 fxxxx_in_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), @@ -568,6 +931,94 @@ static struct sensor_device_attribute_2 fxxxx_in_attr[] = { SENSOR_ATTR_2(in10_input, S_IRUGO, show_in, NULL, 0, 10), }; +static ssize_t show_in_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + + return sprintf(buf, "%d\n", data->in1_max * 8); +} + +static ssize_t store_in_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 8; + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + if (data->type == f81866a) + f71882fg_write8(data, F81866_REG_IN1_HIGH, val); + else + f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); + data->in1_max = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_in_beep(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->in_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_in_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + if (data->type == f81866a) + data->in_beep = f71882fg_read8(data, F81866_REG_IN_BEEP); + else + data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); + + if (val) + data->in_beep |= 1 << nr; + else + data->in_beep &= ~(1 << nr); + + if (data->type == f81866a) + f71882fg_write8(data, F81866_REG_IN_BEEP, data->in_beep); + else + f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_in_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->in_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + /* For models with in1 alarm capability */ static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, @@ -577,6 +1028,242 @@ static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), }; +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int speed = fan_from_reg(data->fan[nr]); + + if (speed == FAN_MIN_DETECT) + speed = 0; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t show_fan_full_speed(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int speed = fan_from_reg(data->fan_full_speed[nr]); + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_full_speed(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 23, 1500000); + val = fan_to_reg(val); + + mutex_lock(&data->update_lock); + f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); + data->fan_full_speed[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->fan_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int val, nr = to_sensor_dev_attr_2(devattr)->index; + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * nr))) + /* PWM mode */ + val = data->pwm[nr]; + else { + /* RPM mode */ + val = 255 * fan_from_reg(data->fan_target[nr]) + / fan_from_reg(data->fan_full_speed[nr]); + } + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_pwm(struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + if ((data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 3) != 2) || + (data->type != f8000 && !((data->pwm_enable >> 2 * nr) & 2))) { + count = -EROFS; + goto leave; + } + if (data->pwm_enable & (1 << (2 * nr))) { + /* PWM mode */ + f71882fg_write8(data, F71882FG_REG_PWM(nr), val); + data->pwm[nr] = val; + } else { + /* RPM mode */ + int target, full_speed; + full_speed = f71882fg_read16(data, + F71882FG_REG_FAN_FULL_SPEED(nr)); + target = fan_to_reg(val * fan_from_reg(full_speed) / 255); + f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), target); + data->fan_target[nr] = target; + data->fan_full_speed[nr] = full_speed; + } +leave: + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int result = 0; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + switch ((data->pwm_enable >> 2 * nr) & 3) { + case 0: + case 1: + result = 2; /* Normal auto mode */ + break; + case 2: + result = 1; /* Manual mode */ + break; + case 3: + if (data->type == f8000) + result = 3; /* Thermostat mode */ + else + result = 1; /* Manual mode */ + break; + } + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_enable(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + /* Special case for F8000 pwm channel 3 which only does auto mode */ + if (data->type == f8000 && nr == 2 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + /* Special case for F8000 auto PWM mode / Thermostat mode */ + if (data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 1)) { + switch (val) { + case 2: + data->pwm_enable &= ~(2 << (2 * nr)); + break; /* Normal auto mode */ + case 3: + data->pwm_enable |= 2 << (2 * nr); + break; /* Thermostat mode */ + default: + count = -EINVAL; + goto leave; + } + } else { + switch (val) { + case 1: + /* The f71858fg does not support manual RPM mode */ + if (data->type == f71858fg && + ((data->pwm_enable >> (2 * nr)) & 1)) { + count = -EINVAL; + goto leave; + } + data->pwm_enable |= 2 << (2 * nr); + break; /* Manual */ + case 2: + data->pwm_enable &= ~(2 << (2 * nr)); + break; /* Normal auto mode */ + default: + count = -EINVAL; + goto leave; + } + } + f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable); +leave: + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + result = (data->pwm_auto_point_mapping[nr] >> 4) & 1; + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); + if (val) + val = data->pwm_auto_point_mapping[nr] | (1 << 4); + else + val = data->pwm_auto_point_mapping[nr] & (~(1 << 4)); + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + /* Fan / PWM attr common to all models */ static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), @@ -624,6 +1311,38 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { show_pwm_interpolate, store_pwm_interpolate, 0, 3), } }; +static ssize_t show_simple_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int val, nr = to_sensor_dev_attr_2(devattr)->index; + + val = data->pwm[nr]; + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_simple_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_PWM(nr), val); + data->pwm[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + /* Attr for the third fan of the f71808a, which only has manual pwm */ static struct sensor_device_attribute_2 f71808a_fan3_attr[] = { SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), @@ -632,6 +1351,42 @@ static struct sensor_device_attribute_2 f71808a_fan3_attr[] = { show_simple_pwm, store_simple_pwm, 0, 2), }; +static ssize_t show_fan_beep(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->fan_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_fan_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); + if (val) + data->fan_beep |= 1 << nr; + else + data->fan_beep &= ~(1 << nr); + + f71882fg_write8(data, F71882FG_REG_FAN_BEEP, data->fan_beep); + mutex_unlock(&data->update_lock); + + return count; +} + /* Attr for models which can beep on Fan alarm */ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, @@ -644,6 +1399,209 @@ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { store_fan_beep, 0, 3), }; +static ssize_t show_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - + data->temp_start); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + switch (val) { + case 1: + val = 0; + break; + case 2: + val = 1; + break; + case 4: + val = 2; + break; + default: + return -EINVAL; + } + val += data->temp_start; + mutex_lock(&data->update_lock); + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); + val = (data->pwm_auto_point_mapping[nr] & 0xfc) | val; + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + result = data->pwm_auto_point_pwm[pwm][point]; + } else { + /* RPM mode */ + result = 32 * 255 / (32 + data->pwm_auto_point_pwm[pwm][point]); + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + } else { + /* RPM mode */ + if (val < 29) /* Prevent negative numbers */ + val = 255; + else + val = (255 - val) * 32 / val; + } + f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm, point), val); + data->pwm_auto_point_pwm[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + result = data->pwm_auto_point_temp[pwm][point]; + return sprintf(buf, "%d\n", 1000 * result); +} + +static ssize_t store_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + + if (data->auto_point_temp_signed) + val = clamp_val(val, -128, 127); + else + val = clamp_val(val, 0, 127); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); + data->pwm_auto_point_temp[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result = 0; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + mutex_lock(&data->update_lock); + if (nr & 1) + result = data->pwm_auto_point_hyst[nr / 2] >> 4; + else + result = data->pwm_auto_point_hyst[nr / 2] & 0x0f; + result = 1000 * (data->pwm_auto_point_temp[nr][point] - result); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + u8 reg; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + + mutex_lock(&data->update_lock); + data->pwm_auto_point_temp[nr][point] = + f71882fg_read8(data, F71882FG_REG_POINT_TEMP(nr, point)); + val = clamp_val(val, data->pwm_auto_point_temp[nr][point] - 15, + data->pwm_auto_point_temp[nr][point]); + val = data->pwm_auto_point_temp[nr][point] - val; + + reg = f71882fg_read8(data, F71882FG_REG_FAN_HYST(nr / 2)); + if (nr & 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + + f71882fg_write8(data, F71882FG_REG_FAN_HYST(nr / 2), reg); + data->pwm_auto_point_hyst[nr / 2] = reg; + mutex_unlock(&data->update_lock); + + return count; +} + /* * PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the * standard models @@ -1142,1070 +2100,6 @@ static inline void superio_exit(int base) release_region(base, 2); } -static inline int fan_from_reg(u16 reg) -{ - return reg ? (1500000 / reg) : 0; -} - -static inline u16 fan_to_reg(int fan) -{ - return fan ? (1500000 / fan) : 0; -} - -static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg) -{ - u8 val; - - outb(reg, data->addr + ADDR_REG_OFFSET); - val = inb(data->addr + DATA_REG_OFFSET); - - return val; -} - -static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg) -{ - u16 val; - - val = f71882fg_read8(data, reg) << 8; - val |= f71882fg_read8(data, reg + 1); - - return val; -} - -static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) -{ - outb(reg, data->addr + ADDR_REG_OFFSET); - outb(val, data->addr + DATA_REG_OFFSET); -} - -static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) -{ - f71882fg_write8(data, reg, val >> 8); - f71882fg_write8(data, reg + 1, val & 0xff); -} - -static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr) -{ - if (data->type == f71858fg) - return f71882fg_read16(data, F71882FG_REG_TEMP(nr)); - else - return f71882fg_read8(data, F71882FG_REG_TEMP(nr)); -} - -static struct f71882fg_data *f71882fg_update_device(struct device *dev) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int nr_fans = f71882fg_nr_fans[data->type]; - int nr_temps = f71882fg_nr_temps[data->type]; - int nr, reg, point; - - mutex_lock(&data->update_lock); - - /* Update once every 60 seconds */ - if (time_after(jiffies, data->last_limits + 60 * HZ) || - !data->valid) { - if (f71882fg_has_in1_alarm[data->type]) { - if (data->type == f81866a) { - data->in1_max = - f71882fg_read8(data, - F81866_REG_IN1_HIGH); - data->in_beep = - f71882fg_read8(data, - F81866_REG_IN_BEEP); - } else { - data->in1_max = - f71882fg_read8(data, - F71882FG_REG_IN1_HIGH); - data->in_beep = - f71882fg_read8(data, - F71882FG_REG_IN_BEEP); - } - } - - /* Get High & boundary temps*/ - for (nr = data->temp_start; nr < nr_temps + data->temp_start; - nr++) { - data->temp_ovt[nr] = f71882fg_read8(data, - F71882FG_REG_TEMP_OVT(nr)); - data->temp_high[nr] = f71882fg_read8(data, - F71882FG_REG_TEMP_HIGH(nr)); - } - - if (data->type != f8000) { - data->temp_hyst[0] = f71882fg_read8(data, - F71882FG_REG_TEMP_HYST(0)); - data->temp_hyst[1] = f71882fg_read8(data, - F71882FG_REG_TEMP_HYST(1)); - } - /* All but the f71858fg / f8000 have this register */ - if ((data->type != f71858fg) && (data->type != f8000)) { - reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); - data->temp_type[1] = (reg & 0x02) ? 2 : 4; - data->temp_type[2] = (reg & 0x04) ? 2 : 4; - data->temp_type[3] = (reg & 0x08) ? 2 : 4; - } - - if (f71882fg_fan_has_beep[data->type]) - data->fan_beep = f71882fg_read8(data, - F71882FG_REG_FAN_BEEP); - - if (f71882fg_temp_has_beep[data->type]) - data->temp_beep = f71882fg_read8(data, - F71882FG_REG_TEMP_BEEP); - - data->pwm_enable = f71882fg_read8(data, - F71882FG_REG_PWM_ENABLE); - data->pwm_auto_point_hyst[0] = - f71882fg_read8(data, F71882FG_REG_FAN_HYST(0)); - data->pwm_auto_point_hyst[1] = - f71882fg_read8(data, F71882FG_REG_FAN_HYST(1)); - - for (nr = 0; nr < nr_fans; nr++) { - data->pwm_auto_point_mapping[nr] = - f71882fg_read8(data, - F71882FG_REG_POINT_MAPPING(nr)); - - switch (data->type) { - default: - for (point = 0; point < 5; point++) { - data->pwm_auto_point_pwm[nr][point] = - f71882fg_read8(data, - F71882FG_REG_POINT_PWM - (nr, point)); - } - for (point = 0; point < 4; point++) { - data->pwm_auto_point_temp[nr][point] = - f71882fg_read8(data, - F71882FG_REG_POINT_TEMP - (nr, point)); - } - break; - case f71808e: - case f71869: - data->pwm_auto_point_pwm[nr][0] = - f71882fg_read8(data, - F71882FG_REG_POINT_PWM(nr, 0)); - /* Fall through */ - case f71862fg: - data->pwm_auto_point_pwm[nr][1] = - f71882fg_read8(data, - F71882FG_REG_POINT_PWM - (nr, 1)); - data->pwm_auto_point_pwm[nr][4] = - f71882fg_read8(data, - F71882FG_REG_POINT_PWM - (nr, 4)); - data->pwm_auto_point_temp[nr][0] = - f71882fg_read8(data, - F71882FG_REG_POINT_TEMP - (nr, 0)); - data->pwm_auto_point_temp[nr][3] = - f71882fg_read8(data, - F71882FG_REG_POINT_TEMP - (nr, 3)); - break; - } - } - data->last_limits = jiffies; - } - - /* Update every second */ - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - data->temp_status = f71882fg_read8(data, - F71882FG_REG_TEMP_STATUS); - data->temp_diode_open = f71882fg_read8(data, - F71882FG_REG_TEMP_DIODE_OPEN); - for (nr = data->temp_start; nr < nr_temps + data->temp_start; - nr++) - data->temp[nr] = f71882fg_read_temp(data, nr); - - data->fan_status = f71882fg_read8(data, - F71882FG_REG_FAN_STATUS); - for (nr = 0; nr < nr_fans; nr++) { - data->fan[nr] = f71882fg_read16(data, - F71882FG_REG_FAN(nr)); - data->fan_target[nr] = - f71882fg_read16(data, F71882FG_REG_FAN_TARGET(nr)); - data->fan_full_speed[nr] = - f71882fg_read16(data, - F71882FG_REG_FAN_FULL_SPEED(nr)); - data->pwm[nr] = - f71882fg_read8(data, F71882FG_REG_PWM(nr)); - } - /* Some models have 1 more fan with limited capabilities */ - if (data->type == f71808a) { - data->fan[2] = f71882fg_read16(data, - F71882FG_REG_FAN(2)); - data->pwm[2] = f71882fg_read8(data, - F71882FG_REG_PWM(2)); - } - if (data->type == f8000) - data->fan[3] = f71882fg_read16(data, - F71882FG_REG_FAN(3)); - - if (f71882fg_has_in1_alarm[data->type]) { - if (data->type == f81866a) - data->in_status = f71882fg_read8(data, - F81866_REG_IN_STATUS); - - else - data->in_status = f71882fg_read8(data, - F71882FG_REG_IN_STATUS); - } - - for (nr = 0; nr < F71882FG_MAX_INS; nr++) - if (f71882fg_has_in[data->type][nr]) - data->in[nr] = f71882fg_read8(data, - F71882FG_REG_IN(nr)); - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} - -/* Sysfs Interface */ -static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int speed = fan_from_reg(data->fan[nr]); - - if (speed == FAN_MIN_DETECT) - speed = 0; - - return sprintf(buf, "%d\n", speed); -} - -static ssize_t show_fan_full_speed(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int speed = fan_from_reg(data->fan_full_speed[nr]); - return sprintf(buf, "%d\n", speed); -} - -static ssize_t store_fan_full_speed(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val = clamp_val(val, 23, 1500000); - val = fan_to_reg(val); - - mutex_lock(&data->update_lock); - f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); - data->fan_full_speed[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_fan_beep(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->fan_beep & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t store_fan_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); - if (val) - data->fan_beep |= 1 << nr; - else - data->fan_beep &= ~(1 << nr); - - f71882fg_write8(data, F71882FG_REG_FAN_BEEP, data->fan_beep); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_fan_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->fan_status & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_in(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - return sprintf(buf, "%d\n", data->in[nr] * 8); -} - -static ssize_t show_in_max(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - - return sprintf(buf, "%d\n", data->in1_max * 8); -} - -static ssize_t store_in_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 8; - val = clamp_val(val, 0, 255); - - mutex_lock(&data->update_lock); - if (data->type == f81866a) - f71882fg_write8(data, F81866_REG_IN1_HIGH, val); - else - f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); - data->in1_max = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_in_beep(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->in_beep & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t store_in_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - if (data->type == f81866a) - data->in_beep = f71882fg_read8(data, F81866_REG_IN_BEEP); - else - data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); - - if (val) - data->in_beep |= 1 << nr; - else - data->in_beep &= ~(1 << nr); - - if (data->type == f81866a) - f71882fg_write8(data, F81866_REG_IN_BEEP, data->in_beep); - else - f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_in_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->in_status & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int sign, temp; - - if (data->type == f71858fg) { - /* TEMP_TABLE_SEL 1 or 3 ? */ - if (data->temp_config & 1) { - sign = data->temp[nr] & 0x0001; - temp = (data->temp[nr] >> 5) & 0x7ff; - } else { - sign = data->temp[nr] & 0x8000; - temp = (data->temp[nr] >> 5) & 0x3ff; - } - temp *= 125; - if (sign) - temp -= 128000; - } else - temp = data->temp[nr] * 1000; - - return sprintf(buf, "%d\n", temp); -} - -static ssize_t show_temp_max(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - return sprintf(buf, "%d\n", data->temp_high[nr] * 1000); -} - -static ssize_t store_temp_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 1000; - val = clamp_val(val, 0, 255); - - mutex_lock(&data->update_lock); - f71882fg_write8(data, F71882FG_REG_TEMP_HIGH(nr), val); - data->temp_high[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int temp_max_hyst; - - mutex_lock(&data->update_lock); - if (nr & 1) - temp_max_hyst = data->temp_hyst[nr / 2] >> 4; - else - temp_max_hyst = data->temp_hyst[nr / 2] & 0x0f; - temp_max_hyst = (data->temp_high[nr] - temp_max_hyst) * 1000; - mutex_unlock(&data->update_lock); - - return sprintf(buf, "%d\n", temp_max_hyst); -} - -static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - ssize_t ret = count; - u8 reg; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 1000; - - mutex_lock(&data->update_lock); - - /* convert abs to relative and check */ - data->temp_high[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_HIGH(nr)); - val = clamp_val(val, data->temp_high[nr] - 15, data->temp_high[nr]); - val = data->temp_high[nr] - val; - - /* convert value to register contents */ - reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST(nr / 2)); - if (nr & 1) - reg = (reg & 0x0f) | (val << 4); - else - reg = (reg & 0xf0) | val; - f71882fg_write8(data, F71882FG_REG_TEMP_HYST(nr / 2), reg); - data->temp_hyst[nr / 2] = reg; - - mutex_unlock(&data->update_lock); - return ret; -} - -static ssize_t show_temp_crit(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - return sprintf(buf, "%d\n", data->temp_ovt[nr] * 1000); -} - -static ssize_t store_temp_crit(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 1000; - val = clamp_val(val, 0, 255); - - mutex_lock(&data->update_lock); - f71882fg_write8(data, F71882FG_REG_TEMP_OVT(nr), val); - data->temp_ovt[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int temp_crit_hyst; - - mutex_lock(&data->update_lock); - if (nr & 1) - temp_crit_hyst = data->temp_hyst[nr / 2] >> 4; - else - temp_crit_hyst = data->temp_hyst[nr / 2] & 0x0f; - temp_crit_hyst = (data->temp_ovt[nr] - temp_crit_hyst) * 1000; - mutex_unlock(&data->update_lock); - - return sprintf(buf, "%d\n", temp_crit_hyst); -} - -static ssize_t show_temp_type(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - return sprintf(buf, "%d\n", data->temp_type[nr]); -} - -static ssize_t show_temp_beep(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->temp_beep & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t store_temp_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); - if (val) - data->temp_beep |= 1 << nr; - else - data->temp_beep &= ~(1 << nr); - - f71882fg_write8(data, F71882FG_REG_TEMP_BEEP, data->temp_beep); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_temp_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->temp_status & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_temp_fault(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->temp_diode_open & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_pwm(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int val, nr = to_sensor_dev_attr_2(devattr)->index; - mutex_lock(&data->update_lock); - if (data->pwm_enable & (1 << (2 * nr))) - /* PWM mode */ - val = data->pwm[nr]; - else { - /* RPM mode */ - val = 255 * fan_from_reg(data->fan_target[nr]) - / fan_from_reg(data->fan_full_speed[nr]); - } - mutex_unlock(&data->update_lock); - return sprintf(buf, "%d\n", val); -} - -static ssize_t store_pwm(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val = clamp_val(val, 0, 255); - - mutex_lock(&data->update_lock); - data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - if ((data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 3) != 2) || - (data->type != f8000 && !((data->pwm_enable >> 2 * nr) & 2))) { - count = -EROFS; - goto leave; - } - if (data->pwm_enable & (1 << (2 * nr))) { - /* PWM mode */ - f71882fg_write8(data, F71882FG_REG_PWM(nr), val); - data->pwm[nr] = val; - } else { - /* RPM mode */ - int target, full_speed; - full_speed = f71882fg_read16(data, - F71882FG_REG_FAN_FULL_SPEED(nr)); - target = fan_to_reg(val * fan_from_reg(full_speed) / 255); - f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), target); - data->fan_target[nr] = target; - data->fan_full_speed[nr] = full_speed; - } -leave: - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_simple_pwm(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int val, nr = to_sensor_dev_attr_2(devattr)->index; - - val = data->pwm[nr]; - return sprintf(buf, "%d\n", val); -} - -static ssize_t store_simple_pwm(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val = clamp_val(val, 0, 255); - - mutex_lock(&data->update_lock); - f71882fg_write8(data, F71882FG_REG_PWM(nr), val); - data->pwm[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_pwm_enable(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int result = 0; - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - switch ((data->pwm_enable >> 2 * nr) & 3) { - case 0: - case 1: - result = 2; /* Normal auto mode */ - break; - case 2: - result = 1; /* Manual mode */ - break; - case 3: - if (data->type == f8000) - result = 3; /* Thermostat mode */ - else - result = 1; /* Manual mode */ - break; - } - - return sprintf(buf, "%d\n", result); -} - -static ssize_t store_pwm_enable(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - /* Special case for F8000 pwm channel 3 which only does auto mode */ - if (data->type == f8000 && nr == 2 && val != 2) - return -EINVAL; - - mutex_lock(&data->update_lock); - data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - /* Special case for F8000 auto PWM mode / Thermostat mode */ - if (data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 1)) { - switch (val) { - case 2: - data->pwm_enable &= ~(2 << (2 * nr)); - break; /* Normal auto mode */ - case 3: - data->pwm_enable |= 2 << (2 * nr); - break; /* Thermostat mode */ - default: - count = -EINVAL; - goto leave; - } - } else { - switch (val) { - case 1: - /* The f71858fg does not support manual RPM mode */ - if (data->type == f71858fg && - ((data->pwm_enable >> (2 * nr)) & 1)) { - count = -EINVAL; - goto leave; - } - data->pwm_enable |= 2 << (2 * nr); - break; /* Manual */ - case 2: - data->pwm_enable &= ~(2 << (2 * nr)); - break; /* Normal auto mode */ - default: - count = -EINVAL; - goto leave; - } - } - f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable); -leave: - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int result; - struct f71882fg_data *data = f71882fg_update_device(dev); - int pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - - mutex_lock(&data->update_lock); - if (data->pwm_enable & (1 << (2 * pwm))) { - /* PWM mode */ - result = data->pwm_auto_point_pwm[pwm][point]; - } else { - /* RPM mode */ - result = 32 * 255 / (32 + data->pwm_auto_point_pwm[pwm][point]); - } - mutex_unlock(&data->update_lock); - - return sprintf(buf, "%d\n", result); -} - -static ssize_t store_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val = clamp_val(val, 0, 255); - - mutex_lock(&data->update_lock); - data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - if (data->pwm_enable & (1 << (2 * pwm))) { - /* PWM mode */ - } else { - /* RPM mode */ - if (val < 29) /* Prevent negative numbers */ - val = 255; - else - val = (255 - val) * 32 / val; - } - f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm, point), val); - data->pwm_auto_point_pwm[pwm][point] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int result = 0; - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - - mutex_lock(&data->update_lock); - if (nr & 1) - result = data->pwm_auto_point_hyst[nr / 2] >> 4; - else - result = data->pwm_auto_point_hyst[nr / 2] & 0x0f; - result = 1000 * (data->pwm_auto_point_temp[nr][point] - result); - mutex_unlock(&data->update_lock); - - return sprintf(buf, "%d\n", result); -} - -static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - u8 reg; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 1000; - - mutex_lock(&data->update_lock); - data->pwm_auto_point_temp[nr][point] = - f71882fg_read8(data, F71882FG_REG_POINT_TEMP(nr, point)); - val = clamp_val(val, data->pwm_auto_point_temp[nr][point] - 15, - data->pwm_auto_point_temp[nr][point]); - val = data->pwm_auto_point_temp[nr][point] - val; - - reg = f71882fg_read8(data, F71882FG_REG_FAN_HYST(nr / 2)); - if (nr & 1) - reg = (reg & 0x0f) | (val << 4); - else - reg = (reg & 0xf0) | val; - - f71882fg_write8(data, F71882FG_REG_FAN_HYST(nr / 2), reg); - data->pwm_auto_point_hyst[nr / 2] = reg; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int result; - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - result = (data->pwm_auto_point_mapping[nr] >> 4) & 1; - - return sprintf(buf, "%d\n", result); -} - -static ssize_t store_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->pwm_auto_point_mapping[nr] = - f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); - if (val) - val = data->pwm_auto_point_mapping[nr] | (1 << 4); - else - val = data->pwm_auto_point_mapping[nr] & (~(1 << 4)); - f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); - data->pwm_auto_point_mapping[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int result; - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - - data->temp_start); - - return sprintf(buf, "%d\n", result); -} - -static ssize_t store_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - switch (val) { - case 1: - val = 0; - break; - case 2: - val = 1; - break; - case 4: - val = 2; - break; - default: - return -EINVAL; - } - val += data->temp_start; - mutex_lock(&data->update_lock); - data->pwm_auto_point_mapping[nr] = - f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); - val = (data->pwm_auto_point_mapping[nr] & 0xfc) | val; - f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); - data->pwm_auto_point_mapping[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int result; - struct f71882fg_data *data = f71882fg_update_device(dev); - int pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - - result = data->pwm_auto_point_temp[pwm][point]; - return sprintf(buf, "%d\n", 1000 * result); -} - -static ssize_t store_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 1000; - - if (data->auto_point_temp_signed) - val = clamp_val(val, -128, 127); - else - val = clamp_val(val, 0, 127); - - mutex_lock(&data->update_lock); - f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); - data->pwm_auto_point_temp[pwm][point] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", f71882fg_names[data->type]); -} - static int f71882fg_create_sysfs_files(struct platform_device *pdev, struct sensor_device_attribute_2 *attr, int count) { @@ -2326,6 +2220,119 @@ static int f71882fg_create_fan_sysfs_files( return err; } +static int f71882fg_remove(struct platform_device *pdev) +{ + struct f71882fg_data *data = platform_get_drvdata(pdev); + int nr_fans = f71882fg_nr_fans[data->type]; + int nr_temps = f71882fg_nr_temps[data->type]; + int i; + u8 start_reg = f71882fg_read8(data, F71882FG_REG_START); + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + device_remove_file(&pdev->dev, &dev_attr_name); + + if (start_reg & 0x01) { + switch (data->type) { + case f71858fg: + if (data->temp_config & 0x10) + f71882fg_remove_sysfs_files(pdev, + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); + else + f71882fg_remove_sysfs_files(pdev, + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); + break; + case f81866a: + f71882fg_remove_sysfs_files(pdev, + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); + break; + default: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_temp_attr[0][0], + ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps); + } + if (f71882fg_temp_has_beep[data->type]) { + if (data->type == f81866a) + f71882fg_remove_sysfs_files(pdev, + &f81866_temp_beep_attr[0][0], + ARRAY_SIZE(f81866_temp_beep_attr[0]) + * nr_temps); + else + f71882fg_remove_sysfs_files(pdev, + &fxxxx_temp_beep_attr[0][0], + ARRAY_SIZE(fxxxx_temp_beep_attr[0]) + * nr_temps); + } + + for (i = 0; i < F71882FG_MAX_INS; i++) { + if (f71882fg_has_in[data->type][i]) { + device_remove_file(&pdev->dev, + &fxxxx_in_attr[i].dev_attr); + } + } + if (f71882fg_has_in1_alarm[data->type]) { + f71882fg_remove_sysfs_files(pdev, + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); + } + } + + if (start_reg & 0x02) { + f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], + ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); + + if (f71882fg_fan_has_beep[data->type]) { + f71882fg_remove_sysfs_files(pdev, + fxxxx_fan_beep_attr, nr_fans); + } + + switch (data->type) { + case f71808a: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + f71882fg_remove_sysfs_files(pdev, + f71808a_fan3_attr, + ARRAY_SIZE(f71808a_fan3_attr)); + break; + case f71862fg: + f71882fg_remove_sysfs_files(pdev, + &f71862fg_auto_pwm_attr[0][0], + ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) * + nr_fans); + break; + case f71808e: + case f71869: + f71882fg_remove_sysfs_files(pdev, + &f71869_auto_pwm_attr[0][0], + ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_fan_attr, + ARRAY_SIZE(f8000_fan_attr)); + f71882fg_remove_sysfs_files(pdev, + &f8000_auto_pwm_attr[0][0], + ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans); + break; + default: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + } + } + return 0; +} + static int f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; @@ -2442,7 +2449,7 @@ static int f71882fg_probe(struct platform_device *pdev) case f71869a: /* These always have signed auto point temps */ data->auto_point_temp_signed = 1; - /* Fall through - to select correct fan/pwm reg bank! */ + fallthrough; /* to select correct fan/pwm reg bank! */ case f71889fg: case f71889ed: case f71889a: @@ -2499,119 +2506,6 @@ exit_unregister_sysfs: return err; /* f71882fg_remove() also frees our data */ } -static int f71882fg_remove(struct platform_device *pdev) -{ - struct f71882fg_data *data = platform_get_drvdata(pdev); - int nr_fans = f71882fg_nr_fans[data->type]; - int nr_temps = f71882fg_nr_temps[data->type]; - int i; - u8 start_reg = f71882fg_read8(data, F71882FG_REG_START); - - if (data->hwmon_dev) - hwmon_device_unregister(data->hwmon_dev); - - device_remove_file(&pdev->dev, &dev_attr_name); - - if (start_reg & 0x01) { - switch (data->type) { - case f71858fg: - if (data->temp_config & 0x10) - f71882fg_remove_sysfs_files(pdev, - f8000_temp_attr, - ARRAY_SIZE(f8000_temp_attr)); - else - f71882fg_remove_sysfs_files(pdev, - f71858fg_temp_attr, - ARRAY_SIZE(f71858fg_temp_attr)); - break; - case f8000: - f71882fg_remove_sysfs_files(pdev, - f8000_temp_attr, - ARRAY_SIZE(f8000_temp_attr)); - break; - case f81866a: - f71882fg_remove_sysfs_files(pdev, - f71858fg_temp_attr, - ARRAY_SIZE(f71858fg_temp_attr)); - break; - default: - f71882fg_remove_sysfs_files(pdev, - &fxxxx_temp_attr[0][0], - ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps); - } - if (f71882fg_temp_has_beep[data->type]) { - if (data->type == f81866a) - f71882fg_remove_sysfs_files(pdev, - &f81866_temp_beep_attr[0][0], - ARRAY_SIZE(f81866_temp_beep_attr[0]) - * nr_temps); - else - f71882fg_remove_sysfs_files(pdev, - &fxxxx_temp_beep_attr[0][0], - ARRAY_SIZE(fxxxx_temp_beep_attr[0]) - * nr_temps); - } - - for (i = 0; i < F71882FG_MAX_INS; i++) { - if (f71882fg_has_in[data->type][i]) { - device_remove_file(&pdev->dev, - &fxxxx_in_attr[i].dev_attr); - } - } - if (f71882fg_has_in1_alarm[data->type]) { - f71882fg_remove_sysfs_files(pdev, - fxxxx_in1_alarm_attr, - ARRAY_SIZE(fxxxx_in1_alarm_attr)); - } - } - - if (start_reg & 0x02) { - f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], - ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - - if (f71882fg_fan_has_beep[data->type]) { - f71882fg_remove_sysfs_files(pdev, - fxxxx_fan_beep_attr, nr_fans); - } - - switch (data->type) { - case f71808a: - f71882fg_remove_sysfs_files(pdev, - &fxxxx_auto_pwm_attr[0][0], - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); - f71882fg_remove_sysfs_files(pdev, - f71808a_fan3_attr, - ARRAY_SIZE(f71808a_fan3_attr)); - break; - case f71862fg: - f71882fg_remove_sysfs_files(pdev, - &f71862fg_auto_pwm_attr[0][0], - ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) * - nr_fans); - break; - case f71808e: - case f71869: - f71882fg_remove_sysfs_files(pdev, - &f71869_auto_pwm_attr[0][0], - ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans); - break; - case f8000: - f71882fg_remove_sysfs_files(pdev, - f8000_fan_attr, - ARRAY_SIZE(f8000_fan_attr)); - f71882fg_remove_sysfs_files(pdev, - &f8000_auto_pwm_attr[0][0], - ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans); - break; - default: - f71882fg_remove_sysfs_files(pdev, - &fxxxx_auto_pwm_attr[0][0], - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); - } - } - return 0; -} - static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) { u16 devid; @@ -2636,6 +2530,7 @@ static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) sio_data->type = f71808a; break; case SIO_F71858_ID: + case SIO_F71858AD_ID: sio_data->type = f71858fg; break; case SIO_F71862_ID: @@ -2672,6 +2567,7 @@ static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) sio_data->type = f81865f; break; case SIO_F81866_ID: + case SIO_F81966_ID: sio_data->type = f81866a; break; default: @@ -2755,6 +2651,14 @@ exit_device_put: return err; } +static struct platform_driver f71882fg_driver = { + .driver = { + .name = DRVNAME, + }, + .probe = f71882fg_probe, + .remove = f71882fg_remove, +}; + static int __init f71882fg_init(void) { int err; diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index eb847a7d6b83..64fbb8cf687c 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -85,7 +85,7 @@ struct f75375_data { const char *name; int kind; struct mutex update_lock; /* protect register access */ - char valid; + bool valid; unsigned long last_updated; /* In jiffies */ unsigned long last_limits; /* In jiffies */ @@ -113,9 +113,8 @@ struct f75375_data { static int f75375_detect(struct i2c_client *client, struct i2c_board_info *info); -static int f75375_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int f75375_remove(struct i2c_client *client); +static int f75375_probe(struct i2c_client *client); +static void f75375_remove(struct i2c_client *client); static const struct i2c_device_id f75375_id[] = { { "f75373", f75373 }, @@ -130,7 +129,7 @@ static struct i2c_driver f75375_driver = { .driver = { .name = "f75375", }, - .probe = f75375_probe, + .probe_new = f75375_probe, .remove = f75375_remove, .id_table = f75375_id, .detect = f75375_detect, @@ -229,7 +228,7 @@ static struct f75375_data *f75375_update_device(struct device *dev) f75375_read8(client, F75375_REG_VOLT(nr)); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -814,8 +813,7 @@ static void f75375_init(struct i2c_client *client, struct f75375_data *data, } -static int f75375_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int f75375_probe(struct i2c_client *client) { struct f75375_data *data; struct f75375s_platform_data *f75375s_pdata = @@ -832,7 +830,7 @@ static int f75375_probe(struct i2c_client *client, i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - data->kind = id->driver_data; + data->kind = i2c_match_id(f75375_id, client)->driver_data; err = sysfs_create_group(&client->dev.kobj, &f75375_group); if (err) @@ -866,12 +864,11 @@ exit_remove: return err; } -static int f75375_remove(struct i2c_client *client) +static void f75375_remove(struct i2c_client *client) { struct f75375_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &f75375_group); - return 0; } /* Return 0 if detection is successful, -ENODEV otherwise */ @@ -899,7 +896,7 @@ static int f75375_detect(struct i2c_client *client, version = f75375_read8(client, F75375_REG_VERSION); dev_info(&adapter->dev, "found %s version: %02X\n", name, version); - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 267eac00a3fb..521534d5c1e5 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -41,10 +41,6 @@ MODULE_LICENSE("GPL"); /* set maximum interval as 1 second */ #define MAX_INTERVAL 1000 -#define MSR_F15H_CU_PWR_ACCUMULATOR 0xc001007a -#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b -#define MSR_F15H_PTSC 0xc0010280 - #define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4 struct fam15h_power_data { @@ -170,7 +166,7 @@ static int read_registers(struct fam15h_power_data *data) memset(data->cu_on, 0, sizeof(int) * MAX_CUS); - get_online_cpus(); + cpus_read_lock(); /* * Choose the first online core of each compute unit, and then @@ -194,7 +190,7 @@ static int read_registers(struct fam15h_power_data *data) on_each_cpu_mask(mask, do_read_registers_on_cu, data, true); - put_online_cpus(); + cpus_read_unlock(); free_cpumask_var(mask); return 0; diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 4136643d8e0c..0a77d6161928 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -214,11 +214,10 @@ static const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 }; * Functions declarations */ -static int fschmd_probe(struct i2c_client *client, - const struct i2c_device_id *id); +static int fschmd_probe(struct i2c_client *client); static int fschmd_detect(struct i2c_client *client, struct i2c_board_info *info); -static int fschmd_remove(struct i2c_client *client); +static void fschmd_remove(struct i2c_client *client); static struct fschmd_data *fschmd_update_device(struct device *dev); /* @@ -242,7 +241,7 @@ static struct i2c_driver fschmd_driver = { .driver = { .name = "fschmd", }, - .probe = fschmd_probe, + .probe_new = fschmd_probe, .remove = fschmd_remove, .id_table = fschmd_id, .detect = fschmd_detect, @@ -265,7 +264,7 @@ struct fschmd_data { unsigned long watchdog_is_open; char watchdog_expect_close; char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* register values */ @@ -1076,20 +1075,19 @@ static int fschmd_detect(struct i2c_client *client, else return -ENODEV; - strlcpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE); + strscpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE); return 0; } -static int fschmd_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int fschmd_probe(struct i2c_client *client) { struct fschmd_data *data; const char * const names[7] = { "Poseidon", "Hermes", "Scylla", "Heracles", "Heimdall", "Hades", "Syleus" }; const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; int i, err; - enum chips kind = id->driver_data; + enum chips kind = i2c_match_id(fschmd_id, client)->driver_data; data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL); if (!data) @@ -1250,7 +1248,7 @@ exit_detach: return err; } -static int fschmd_remove(struct i2c_client *client) +static void fschmd_remove(struct i2c_client *client) { struct fschmd_data *data = i2c_get_clientdata(client); int i; @@ -1293,8 +1291,6 @@ static int fschmd_remove(struct i2c_client *client) mutex_lock(&watchdog_data_mutex); kref_put(&data->kref, fschmd_release_resources); mutex_unlock(&watchdog_data_mutex); - - return 0; } static struct fschmd_data *fschmd_update_device(struct device *dev) @@ -1358,7 +1354,7 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) FSCHMD_REG_VOLT[data->kind][i]); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 371ce7745f5e..f5b8e724a8ca 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -509,7 +509,7 @@ error: /* SysFS structs */ /*****************************************************************************/ -/* Temprature sensors */ +/* Temperature sensors */ static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_value, 0); static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_value, 1); static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_value, 2); @@ -713,7 +713,7 @@ static int fts_detect(struct i2c_client *client, { int val; - /* detection works with revsion greater or equal to 0x2b */ + /* detection works with revision greater or equal to 0x2b */ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG); if (val < 0x2b) return -ENODEV; @@ -739,20 +739,19 @@ static int fts_detect(struct i2c_client *client, if (val != 0x11) return -ENODEV; - strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE); + strscpy(info->type, fts_id[0].name, I2C_NAME_SIZE); info->flags = 0; return 0; } -static int fts_remove(struct i2c_client *client) +static void fts_remove(struct i2c_client *client) { struct fts_data *data = dev_get_drvdata(&client->dev); watchdog_unregister_device(&data->wdd); - return 0; } -static int fts_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int fts_probe(struct i2c_client *client) { u8 revision; struct fts_data *data; @@ -819,7 +818,7 @@ static struct i2c_driver fts_driver = { .name = "ftsteutates", }, .id_table = fts_id, - .probe = fts_probe, + .probe_new = fts_probe, .remove = fts_remove, .detect = fts_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c index 31beedcb420f..36717b524dbd 100644 --- a/drivers/hwmon/g760a.c +++ b/drivers/hwmon/g760a.c @@ -95,7 +95,7 @@ static struct g760a_data *g760a_update_client(struct device *dev) data->fan_sta = g760a_read_value(client, G760A_REG_FAN_STA); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -170,8 +170,7 @@ ATTRIBUTE_GROUPS(g760a); * new-style driver model code */ -static int g760a_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int g760a_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct g760a_data *data; @@ -207,7 +206,7 @@ static struct i2c_driver g760a_driver = { .driver = { .name = "g760a", }, - .probe = g760a_probe, + .probe_new = g760a_probe, .id_table = g760a_id, }; diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 5f0f34631580..64a0599b2da5 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -1033,7 +1033,7 @@ static inline int g762_fan_init(struct device *dev) data->fan_cmd1); } -static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int g762_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -1079,7 +1079,7 @@ static struct i2c_driver g762_driver = { .name = DRVNAME, .of_match_table = of_match_ptr(g762_dt_match), }, - .probe = g762_probe, + .probe_new = g762_probe, .id_table = g762_id, }; diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index 4964beeea542..95286c40f55a 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -107,7 +107,7 @@ struct gl518_data { enum chips type; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 voltage_in[4]; /* Register values; [0] = VDD */ @@ -211,7 +211,7 @@ static struct gl518_data *gl518_update_device(struct device *dev) gl518_read_value(client, GL518_REG_VIN3); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -586,7 +586,7 @@ static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info) if (rev != 0x00 && rev != 0x80) return -ENODEV; - strlcpy(info->type, "gl518sm", I2C_NAME_SIZE); + strscpy(info->type, "gl518sm", I2C_NAME_SIZE); return 0; } @@ -611,8 +611,7 @@ static void gl518_init_client(struct i2c_client *client) gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue); } -static int gl518_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int gl518_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -653,7 +652,7 @@ static struct i2c_driver gl518_driver = { .driver = { .name = "gl518sm", }, - .probe = gl518_probe, + .probe_new = gl518_probe, .id_table = gl518_id, .detect = gl518_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 4689e01cb56d..394da4ac977c 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -64,7 +64,7 @@ struct gl520_data { struct i2c_client *client; const struct attribute_group *groups[3]; struct mutex update_lock; - char valid; /* zero until the following fields are valid */ + bool valid; /* false until the following fields are valid */ unsigned long last_updated; /* in jiffies */ u8 vid; @@ -174,7 +174,7 @@ static struct gl520_data *gl520_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -811,7 +811,7 @@ static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, "gl520sm", I2C_NAME_SIZE); + strscpy(info->type, "gl520sm", I2C_NAME_SIZE); return 0; } @@ -854,8 +854,7 @@ static void gl520_init_client(struct i2c_client *client) gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask); } -static int gl520_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int gl520_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -896,7 +895,7 @@ static struct i2c_driver gl520_driver = { .driver = { .name = "gl520sm", }, - .probe = gl520_probe, + .probe_new = gl520_probe, .id_table = gl520_id, .detect = gl520_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 3ea4021f267c..ba408942dbe7 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -37,9 +37,7 @@ struct gpio_fan_data { int num_speed; struct gpio_fan_speed *speed; int speed_index; -#ifdef CONFIG_PM_SLEEP int resume_speed; -#endif bool pwm_enable; struct gpio_desc *alarm_gpio; struct work_struct alarm_work; @@ -299,7 +297,7 @@ static DEVICE_ATTR(fan1_target, 0644, fan1_input_show, set_rpm); static umode_t gpio_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct gpio_fan_data *data = dev_get_drvdata(dev); if (index == 0 && !data->alarm_gpio) @@ -391,6 +389,9 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev, if (!fan_data) return -EINVAL; + if (state >= fan_data->num_speed) + return -EINVAL; + set_fan_speed(fan_data, state); return 0; } @@ -554,7 +555,6 @@ static void gpio_fan_shutdown(struct platform_device *pdev) set_fan_speed(fan_data, 0); } -#ifdef CONFIG_PM_SLEEP static int gpio_fan_suspend(struct device *dev) { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); @@ -577,18 +577,14 @@ static int gpio_fan_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); -#define GPIO_FAN_PM (&gpio_fan_pm) -#else -#define GPIO_FAN_PM NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); static struct platform_driver gpio_fan_driver = { .probe = gpio_fan_probe, .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", - .pm = GPIO_FAN_PM, + .pm = pm_sleep_ptr(&gpio_fan_pm), .of_match_table = of_match_ptr(of_gpio_fan_match), }, }; diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c new file mode 100644 index 000000000000..b60ec95b5edb --- /dev/null +++ b/drivers/hwmon/gsc-hwmon.c @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Gateworks System Controller Hardware Monitor module + * + * Copyright (C) 2020 Gateworks Corporation + */ +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/mfd/gsc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <linux/platform_data/gsc_hwmon.h> + +#define GSC_HWMON_MAX_TEMP_CH 16 +#define GSC_HWMON_MAX_IN_CH 16 +#define GSC_HWMON_MAX_FAN_CH 16 + +#define GSC_HWMON_RESOLUTION 12 +#define GSC_HWMON_VREF 2500 + +struct gsc_hwmon_data { + struct gsc_dev *gsc; + struct gsc_hwmon_platform_data *pdata; + struct regmap *regmap; + const struct gsc_hwmon_channel *temp_ch[GSC_HWMON_MAX_TEMP_CH]; + const struct gsc_hwmon_channel *in_ch[GSC_HWMON_MAX_IN_CH]; + const struct gsc_hwmon_channel *fan_ch[GSC_HWMON_MAX_FAN_CH]; + u32 temp_config[GSC_HWMON_MAX_TEMP_CH + 1]; + u32 in_config[GSC_HWMON_MAX_IN_CH + 1]; + u32 fan_config[GSC_HWMON_MAX_FAN_CH + 1]; + struct hwmon_channel_info temp_info; + struct hwmon_channel_info in_info; + struct hwmon_channel_info fan_info; + const struct hwmon_channel_info *info[4]; + struct hwmon_chip_info chip; +}; + +static struct regmap_bus gsc_hwmon_regmap_bus = { + .reg_read = gsc_read, + .reg_write = gsc_write, +}; + +static const struct regmap_config gsc_hwmon_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +static ssize_t pwm_auto_point_temp_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + u8 reg = hwmon->pdata->fan_base + (2 * attr->index); + u8 regs[2]; + int ret; + + ret = regmap_bulk_read(hwmon->regmap, reg, regs, 2); + if (ret) + return ret; + + ret = regs[0] | regs[1] << 8; + return sprintf(buf, "%d\n", ret * 10); +} + +static ssize_t pwm_auto_point_temp_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + u8 reg = hwmon->pdata->fan_base + (2 * attr->index); + u8 regs[2]; + long temp; + int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + + temp = clamp_val(temp, 0, 10000); + temp = DIV_ROUND_CLOSEST(temp, 10); + + regs[0] = temp & 0xff; + regs[1] = (temp >> 8) & 0xff; + err = regmap_bulk_write(hwmon->regmap, reg, regs, 2); + if (err) + return err; + + return count; +} + +static ssize_t pwm_auto_point_pwm_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return sprintf(buf, "%d\n", 255 * (50 + (attr->index * 10)) / 100); +} + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm_auto_point_pwm, 0); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_auto_point_temp, 0); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point2_pwm, pwm_auto_point_pwm, 1); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, pwm_auto_point_temp, 1); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm_auto_point_pwm, 2); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, pwm_auto_point_temp, 2); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point4_pwm, pwm_auto_point_pwm, 3); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, pwm_auto_point_temp, 3); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point5_pwm, pwm_auto_point_pwm, 4); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point5_temp, pwm_auto_point_temp, 4); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point6_pwm, pwm_auto_point_pwm, 5); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point6_temp, pwm_auto_point_temp, 5); + +static struct attribute *gsc_hwmon_attributes[] = { + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, + NULL +}; + +static const struct attribute_group gsc_hwmon_group = { + .attrs = gsc_hwmon_attributes, +}; +__ATTRIBUTE_GROUPS(gsc_hwmon); + +static int +gsc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + const struct gsc_hwmon_channel *ch; + int sz, ret; + long tmp; + u8 buf[3]; + + switch (type) { + case hwmon_in: + ch = hwmon->in_ch[channel]; + break; + case hwmon_temp: + ch = hwmon->temp_ch[channel]; + break; + case hwmon_fan: + ch = hwmon->fan_ch[channel]; + break; + default: + return -EOPNOTSUPP; + } + + sz = (ch->mode == mode_voltage_24bit) ? 3 : 2; + ret = regmap_bulk_read(hwmon->regmap, ch->reg, buf, sz); + if (ret) + return ret; + + tmp = 0; + while (sz-- > 0) + tmp |= (buf[sz] << (8 * sz)); + + switch (ch->mode) { + case mode_temperature: + if (tmp > 0x8000) + tmp -= 0xffff; + tmp *= 100; /* convert to millidegrees celsius */ + break; + case mode_voltage_raw: + tmp = clamp_val(tmp, 0, BIT(GSC_HWMON_RESOLUTION)); + /* scale based on ref voltage and ADC resolution */ + tmp *= GSC_HWMON_VREF; + tmp >>= GSC_HWMON_RESOLUTION; + /* scale based on optional voltage divider */ + if (ch->vdiv[0] && ch->vdiv[1]) { + tmp *= (ch->vdiv[0] + ch->vdiv[1]); + tmp /= ch->vdiv[1]; + } + /* adjust by uV offset */ + tmp += ch->mvoffset; + break; + case mode_fan: + tmp *= 30; /* convert to revolutions per minute */ + break; + case mode_voltage_24bit: + case mode_voltage_16bit: + /* no adjustment needed */ + break; + } + + *val = tmp; + + return 0; +} + +static int +gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **buf) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + *buf = hwmon->in_ch[channel]->name; + break; + case hwmon_temp: + *buf = hwmon->temp_ch[channel]->name; + break; + case hwmon_fan: + *buf = hwmon->fan_ch[channel]->name; + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +static umode_t +gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, + int ch) +{ + return 0444; +} + +static const struct hwmon_ops gsc_hwmon_ops = { + .is_visible = gsc_hwmon_is_visible, + .read = gsc_hwmon_read, + .read_string = gsc_hwmon_read_string, +}; + +static struct gsc_hwmon_platform_data * +gsc_hwmon_get_devtree_pdata(struct device *dev) +{ + struct gsc_hwmon_platform_data *pdata; + struct gsc_hwmon_channel *ch; + struct fwnode_handle *child; + struct device_node *fan; + int nchannels; + + nchannels = device_get_child_node_count(dev); + if (nchannels == 0) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(dev, + sizeof(*pdata) + nchannels * sizeof(*ch), + 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 */ + of_node_get(dev->parent->of_node); + fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan"); + if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) { + of_node_put(fan); + dev_err(dev, "fan node without base\n"); + return ERR_PTR(-EINVAL); + } + + of_node_put(fan); + + /* 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)) { + dev_err(dev, "channel without label\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + if (fwnode_property_read_u32(child, "reg", &ch->reg)) { + dev_err(dev, "channel without reg\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + if (fwnode_property_read_u32(child, "gw,mode", &ch->mode)) { + dev_err(dev, "channel without mode\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + if (ch->mode > mode_max) { + dev_err(dev, "invalid channel mode\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + + if (!fwnode_property_read_u32(child, + "gw,voltage-offset-microvolt", + &ch->mvoffset)) + ch->mvoffset /= 1000; + fwnode_property_read_u32_array(child, + "gw,voltage-divider-ohms", + ch->vdiv, ARRAY_SIZE(ch->vdiv)); + ch++; + } + + return pdata; +} + +static int gsc_hwmon_probe(struct platform_device *pdev) +{ + struct gsc_dev *gsc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct device *hwmon_dev; + struct gsc_hwmon_platform_data *pdata = dev_get_platdata(dev); + struct gsc_hwmon_data *hwmon; + const struct attribute_group **groups; + int i, i_in, i_temp, i_fan; + + if (!pdata) { + pdata = gsc_hwmon_get_devtree_pdata(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + hwmon->gsc = gsc; + hwmon->pdata = pdata; + + hwmon->regmap = devm_regmap_init(dev, &gsc_hwmon_regmap_bus, + gsc->i2c_hwmon, + &gsc_hwmon_regmap_config); + if (IS_ERR(hwmon->regmap)) + return PTR_ERR(hwmon->regmap); + + for (i = 0, i_in = 0, i_temp = 0, i_fan = 0; i < hwmon->pdata->nchannels; i++) { + const struct gsc_hwmon_channel *ch = &pdata->channels[i]; + + switch (ch->mode) { + case mode_temperature: + if (i_temp == GSC_HWMON_MAX_TEMP_CH) { + dev_err(gsc->dev, "too many temp channels\n"); + return -EINVAL; + } + hwmon->temp_ch[i_temp] = ch; + hwmon->temp_config[i_temp] = HWMON_T_INPUT | + HWMON_T_LABEL; + i_temp++; + break; + case mode_fan: + if (i_fan == GSC_HWMON_MAX_FAN_CH) { + dev_err(gsc->dev, "too many fan channels\n"); + return -EINVAL; + } + hwmon->fan_ch[i_fan] = ch; + hwmon->fan_config[i_fan] = HWMON_F_INPUT | + HWMON_F_LABEL; + i_fan++; + break; + case mode_voltage_24bit: + case mode_voltage_16bit: + case mode_voltage_raw: + if (i_in == GSC_HWMON_MAX_IN_CH) { + dev_err(gsc->dev, "too many input channels\n"); + return -EINVAL; + } + hwmon->in_ch[i_in] = ch; + hwmon->in_config[i_in] = + HWMON_I_INPUT | HWMON_I_LABEL; + i_in++; + break; + default: + dev_err(gsc->dev, "invalid mode: %d\n", ch->mode); + return -EINVAL; + } + } + + /* setup config structures */ + hwmon->chip.ops = &gsc_hwmon_ops; + hwmon->chip.info = hwmon->info; + hwmon->info[0] = &hwmon->temp_info; + hwmon->info[1] = &hwmon->in_info; + hwmon->info[2] = &hwmon->fan_info; + hwmon->temp_info.type = hwmon_temp; + hwmon->temp_info.config = hwmon->temp_config; + hwmon->in_info.type = hwmon_in; + hwmon->in_info.config = hwmon->in_config; + hwmon->fan_info.type = hwmon_fan; + hwmon->fan_info.config = hwmon->fan_config; + + groups = pdata->fan_base ? gsc_hwmon_groups : NULL; + hwmon_dev = devm_hwmon_device_register_with_info(dev, + KBUILD_MODNAME, hwmon, + &hwmon->chip, groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id gsc_hwmon_of_match[] = { + { .compatible = "gw,gsc-adc", }, + {} +}; + +static struct platform_driver gsc_hwmon_driver = { + .driver = { + .name = "gsc-hwmon", + .of_match_table = gsc_hwmon_of_match, + }, + .probe = gsc_hwmon_probe, +}; + +module_platform_driver(gsc_hwmon_driver); + +MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>"); +MODULE_DESCRIPTION("GSC hardware monitor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/hih6130.c b/drivers/hwmon/hih6130.c index 018df6074f7b..d9394e19fea8 100644 --- a/drivers/hwmon/hih6130.c +++ b/drivers/hwmon/hih6130.c @@ -204,8 +204,7 @@ static struct attribute *hih6130_attrs[] = { ATTRIBUTE_GROUPS(hih6130); -static int hih6130_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int hih6130_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct hih6130 *hih6130; @@ -250,7 +249,7 @@ static struct i2c_driver hih6130_driver = { .name = "hih6130", .of_match_table = of_match_ptr(hih6130_of_match), }, - .probe = hih6130_probe, + .probe_new = hih6130_probe, .id_table = hih6130_id, }; diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index 8ae68dfa75b2..6d1175a51832 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c @@ -49,15 +49,15 @@ * The 13 specification corresponds to the Intel Pentium M series. There * doesn't seem to be any named specification for these. The conversion * tables are detailed directly in the various Pentium M datasheets: - * http://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm + * https://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm * * The 14 specification corresponds to Intel Core series. There * doesn't seem to be any named specification for these. The conversion * tables are detailed directly in the various Pentium Core datasheets: - * http://www.intel.com/design/mobile/datashts/309221.htm + * https://www.intel.com/design/mobile/datashts/309221.htm * * The 110 (VRM 11) specification corresponds to Intel Conroe based series. - * http://www.intel.com/design/processor/applnots/313214.htm + * https://www.intel.com/design/processor/applnots/313214.htm */ /* @@ -96,7 +96,7 @@ int vid_from_reg(int val, u8 vrm) val &= 0x1f; if (val == 0x1f) return 0; - /* fall through */ + fallthrough; case 25: /* AMD NPT 0Fh */ val &= 0x3f; return (val < 32) ? 1550 - 25 * val @@ -122,7 +122,7 @@ int vid_from_reg(int val, u8 vrm) case 84: /* VRM 8.4 */ val &= 0x0f; - /* fall through */ + fallthrough; case 82: /* VRM 8.2 */ val &= 0x1f; return val == 0x1f ? 0 : diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 6a30fb453f7a..4218750d5a66 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -15,8 +15,10 @@ #include <linux/gfp.h> #include <linux/hwmon.h> #include <linux/idr.h> +#include <linux/list.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/thermal.h> @@ -29,9 +31,10 @@ struct hwmon_device { const char *name; + const char *label; struct device dev; const struct hwmon_chip_info *chip; - + struct list_head tzdata; struct attribute_group group; const struct attribute_group **groups; }; @@ -55,12 +58,12 @@ struct hwmon_device_attribute { /* * Thermal zone information - * In addition to the reference to the hwmon device, - * also provides the sensor index. */ struct hwmon_thermal_data { + struct list_head node; /* hwmon tzdata list entry */ struct device *dev; /* Reference to hwmon device */ int index; /* sensor index */ + struct thermal_zone_device *tzd;/* thermal zone device */ }; static ssize_t @@ -70,17 +73,29 @@ name_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(name); +static ssize_t +label_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%s\n", to_hwmon_device(dev)->label); +} +static DEVICE_ATTR_RO(label); + static struct attribute *hwmon_dev_attrs[] = { &dev_attr_name.attr, + &dev_attr_label.attr, NULL }; -static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, +static umode_t hwmon_dev_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); + struct hwmon_device *hdev = to_hwmon_device(dev); - if (to_hwmon_device(dev)->name == NULL) + if (attr == &dev_attr_name.attr && hdev->name == NULL) + return 0; + + if (attr == &dev_attr_label.attr && hdev->label == NULL) return 0; return attr->mode; @@ -88,7 +103,7 @@ static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, static const struct attribute_group hwmon_dev_attr_group = { .attrs = hwmon_dev_attrs, - .is_visible = hwmon_dev_name_is_visible, + .is_visible = hwmon_dev_attr_is_visible, }; static const struct attribute_group *hwmon_dev_attr_groups[] = { @@ -116,6 +131,7 @@ static void hwmon_dev_release(struct device *dev) if (hwdev->group.attrs) hwmon_free_attrs(hwdev->group.attrs); kfree(hwdev->groups); + kfree(hwdev->label); kfree(hwdev); } @@ -135,9 +151,9 @@ static DEFINE_IDA(hwmon_ida); * between hwmon and thermal_sys modules. */ #ifdef CONFIG_THERMAL_OF -static int hwmon_thermal_get_temp(void *data, int *temp) +static int hwmon_thermal_get_temp(struct thermal_zone_device *tz, int *temp) { - struct hwmon_thermal_data *tdata = data; + struct hwmon_thermal_data *tdata = tz->devdata; struct hwmon_device *hwdev = to_hwmon_device(tdata->dev); int ret; long t; @@ -152,14 +168,57 @@ static int hwmon_thermal_get_temp(void *data, int *temp) return 0; } -static const struct thermal_zone_of_device_ops hwmon_thermal_ops = { +static int hwmon_thermal_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct hwmon_thermal_data *tdata = tz->devdata; + struct hwmon_device *hwdev = to_hwmon_device(tdata->dev); + const struct hwmon_chip_info *chip = hwdev->chip; + const struct hwmon_channel_info **info = chip->info; + unsigned int i; + int err; + + if (!chip->ops->write) + return 0; + + for (i = 0; info[i] && info[i]->type != hwmon_temp; i++) + continue; + + if (!info[i]) + return 0; + + if (info[i]->config[tdata->index] & HWMON_T_MIN) { + err = chip->ops->write(tdata->dev, hwmon_temp, + hwmon_temp_min, tdata->index, low); + if (err && err != -EOPNOTSUPP) + return err; + } + + if (info[i]->config[tdata->index] & HWMON_T_MAX) { + err = chip->ops->write(tdata->dev, hwmon_temp, + hwmon_temp_max, tdata->index, high); + if (err && err != -EOPNOTSUPP) + return err; + } + + return 0; +} + +static const struct thermal_zone_device_ops hwmon_thermal_ops = { .get_temp = hwmon_thermal_get_temp, + .set_trips = hwmon_thermal_set_trips, }; +static void hwmon_thermal_remove_sensor(void *data) +{ + list_del(data); +} + static int hwmon_thermal_add_sensor(struct device *dev, int index) { + struct hwmon_device *hwdev = to_hwmon_device(dev); struct hwmon_thermal_data *tdata; struct thermal_zone_device *tzd; + int err; tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL); if (!tdata) @@ -168,22 +227,79 @@ static int hwmon_thermal_add_sensor(struct device *dev, int index) tdata->dev = dev; tdata->index = index; - tzd = devm_thermal_zone_of_sensor_register(dev, index, tdata, - &hwmon_thermal_ops); - /* - * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV, - * so ignore that error but forward any other error. - */ - if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV)) - return PTR_ERR(tzd); + tzd = devm_thermal_of_zone_register(dev, index, tdata, + &hwmon_thermal_ops); + if (IS_ERR(tzd)) { + if (PTR_ERR(tzd) != -ENODEV) + return PTR_ERR(tzd); + dev_info(dev, "temp%d_input not attached to any thermal zone\n", + index + 1); + devm_kfree(dev, tdata); + return 0; + } + + err = devm_add_action(dev, hwmon_thermal_remove_sensor, &tdata->node); + if (err) + return err; + + tdata->tzd = tzd; + list_add(&tdata->node, &hwdev->tzdata); return 0; } + +static int hwmon_thermal_register_sensors(struct device *dev) +{ + struct hwmon_device *hwdev = to_hwmon_device(dev); + const struct hwmon_chip_info *chip = hwdev->chip; + const struct hwmon_channel_info **info = chip->info; + void *drvdata = dev_get_drvdata(dev); + int i; + + for (i = 1; info[i]; i++) { + int j; + + if (info[i]->type != hwmon_temp) + continue; + + for (j = 0; info[i]->config[j]; j++) { + int err; + + if (!(info[i]->config[j] & HWMON_T_INPUT) || + !chip->ops->is_visible(drvdata, hwmon_temp, + hwmon_temp_input, j)) + continue; + + err = hwmon_thermal_add_sensor(dev, j); + if (err) + return err; + } + } + + return 0; +} + +static void hwmon_thermal_notify(struct device *dev, int index) +{ + struct hwmon_device *hwdev = to_hwmon_device(dev); + struct hwmon_thermal_data *tzdata; + + list_for_each_entry(tzdata, &hwdev->tzdata, node) { + if (tzdata->index == index) { + thermal_zone_device_update(tzdata->tzd, + THERMAL_EVENT_UNSPECIFIED); + } + } +} + #else -static int hwmon_thermal_add_sensor(struct device *dev, int index) +static int hwmon_thermal_register_sensors(struct device *dev) { return 0; } + +static void hwmon_thermal_notify(struct device *dev, int index) { } + #endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */ static int hwmon_attr_base(enum hwmon_sensor_types type) @@ -368,6 +484,8 @@ static const char * const hwmon_temp_attr_templates[] = { [hwmon_temp_lowest] = "temp%d_lowest", [hwmon_temp_highest] = "temp%d_highest", [hwmon_temp_reset_history] = "temp%d_reset_history", + [hwmon_temp_rated_min] = "temp%d_rated_min", + [hwmon_temp_rated_max] = "temp%d_rated_max", }; static const char * const hwmon_in_attr_templates[] = { @@ -387,6 +505,8 @@ static const char * const hwmon_in_attr_templates[] = { [hwmon_in_max_alarm] = "in%d_max_alarm", [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm", [hwmon_in_crit_alarm] = "in%d_crit_alarm", + [hwmon_in_rated_min] = "in%d_rated_min", + [hwmon_in_rated_max] = "in%d_rated_max", }; static const char * const hwmon_curr_attr_templates[] = { @@ -406,6 +526,8 @@ static const char * const hwmon_curr_attr_templates[] = { [hwmon_curr_max_alarm] = "curr%d_max_alarm", [hwmon_curr_lcrit_alarm] = "curr%d_lcrit_alarm", [hwmon_curr_crit_alarm] = "curr%d_crit_alarm", + [hwmon_curr_rated_min] = "curr%d_rated_min", + [hwmon_curr_rated_max] = "curr%d_rated_max", }; static const char * const hwmon_power_attr_templates[] = { @@ -438,6 +560,8 @@ static const char * const hwmon_power_attr_templates[] = { [hwmon_power_max_alarm] = "power%d_max_alarm", [hwmon_power_lcrit_alarm] = "power%d_lcrit_alarm", [hwmon_power_crit_alarm] = "power%d_crit_alarm", + [hwmon_power_rated_min] = "power%d_rated_min", + [hwmon_power_rated_max] = "power%d_rated_max", }; static const char * const hwmon_energy_attr_templates[] = { @@ -456,6 +580,8 @@ static const char * const hwmon_humidity_attr_templates[] = { [hwmon_humidity_max_hyst] = "humidity%d_max_hyst", [hwmon_humidity_alarm] = "humidity%d_alarm", [hwmon_humidity_fault] = "humidity%d_fault", + [hwmon_humidity_rated_min] = "humidity%d_rated_min", + [hwmon_humidity_rated_max] = "humidity%d_rated_max", }; static const char * const hwmon_fan_attr_templates[] = { @@ -478,6 +604,7 @@ static const char * const hwmon_pwm_attr_templates[] = { [hwmon_pwm_enable] = "pwm%d_enable", [hwmon_pwm_mode] = "pwm%d_mode", [hwmon_pwm_freq] = "pwm%d_freq", + [hwmon_pwm_auto_channels_temp] = "pwm%d_auto_channels_temp", }; static const char * const hwmon_intrusion_attr_templates[] = { @@ -511,6 +638,38 @@ static const int __templates_size[] = { [hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates), }; +int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + char event[MAX_SYSFS_ATTR_NAME_LENGTH + 5]; + char sattr[MAX_SYSFS_ATTR_NAME_LENGTH]; + char *envp[] = { event, NULL }; + const char * const *templates; + const char *template; + int base; + + if (type >= ARRAY_SIZE(__templates)) + return -EINVAL; + if (attr >= __templates_size[type]) + return -EINVAL; + + templates = __templates[type]; + template = templates[attr]; + + base = hwmon_attr_base(type); + + scnprintf(sattr, MAX_SYSFS_ATTR_NAME_LENGTH, template, base + channel); + scnprintf(event, sizeof(event), "NAME=%s", sattr); + sysfs_notify(&dev->kobj, NULL, sattr); + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); + + if (type == hwmon_temp) + hwmon_thermal_notify(dev, channel); + + return 0; +} +EXPORT_SYMBOL_GPL(hwmon_notify_event); + static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) { int i, n; @@ -595,8 +754,9 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, const struct attribute_group **groups) { struct hwmon_device *hwdev; + const char *label; struct device *hdev; - int i, j, err, id; + int i, err, id; /* Complain about invalid characters in hwmon name attribute */ if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) @@ -604,7 +764,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, "hwmon: '%s' is not a valid name attribute, please fix\n", name); - id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&hwmon_ida, GFP_KERNEL); if (id < 0) return ERR_PTR(id); @@ -650,6 +810,18 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, hdev->groups = groups; } + if (dev && device_property_present(dev, "label")) { + err = device_property_read_string(dev, "label", &label); + if (err < 0) + goto free_hwmon; + + hwdev->label = kstrdup(label, GFP_KERNEL); + if (hwdev->label == NULL) { + err = -ENOMEM; + goto free_hwmon; + } + } + hwdev->name = name; hdev->class = &hwmon_class; hdev->parent = dev; @@ -658,36 +830,24 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, dev_set_drvdata(hdev, drvdata); dev_set_name(hdev, HWMON_ID_FORMAT, id); err = device_register(hdev); - if (err) - goto free_hwmon; + if (err) { + put_device(hdev); + goto ida_remove; + } + + INIT_LIST_HEAD(&hwdev->tzdata); if (dev && dev->of_node && chip && chip->ops->read && chip->info[0]->type == hwmon_chip && (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { - const struct hwmon_channel_info **info = chip->info; - - for (i = 1; info[i]; i++) { - if (info[i]->type != hwmon_temp) - continue; - - for (j = 0; info[i]->config[j]; j++) { - if (!chip->ops->is_visible(drvdata, hwmon_temp, - hwmon_temp_input, j)) - continue; - if (info[i]->config[j] & HWMON_T_INPUT) { - err = hwmon_thermal_add_sensor(hdev, j); - if (err) { - device_unregister(hdev); - /* - * Don't worry about hwdev; - * hwmon_dev_release(), called - * from device_unregister(), - * will free it. - */ - goto ida_remove; - } - } - } + err = hwmon_thermal_register_sensors(hdev); + if (err) { + device_unregister(hdev); + /* + * Don't worry about hwdev; hwmon_dev_release(), called + * from device_unregister(), will free it. + */ + goto ida_remove; } } @@ -696,7 +856,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, free_hwmon: hwmon_dev_release(hdev); ida_remove: - ida_simple_remove(&hwmon_ida, id); + ida_free(&hwmon_ida, id); return ERR_PTR(err); } @@ -726,11 +886,12 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); /** * hwmon_device_register_with_info - register w/ hwmon - * @dev: the parent device - * @name: hwmon name attribute - * @drvdata: driver data to attach to created device - * @chip: pointer to hwmon chip information + * @dev: the parent device (mandatory) + * @name: hwmon name attribute (mandatory) + * @drvdata: driver data to attach to created device (optional) + * @chip: pointer to hwmon chip information (mandatory) * @extra_groups: pointer to list of additional non-standard attribute groups + * (optional) * * hwmon_device_unregister() must be called when the device is no * longer needed. @@ -743,13 +904,10 @@ hwmon_device_register_with_info(struct device *dev, const char *name, const struct hwmon_chip_info *chip, const struct attribute_group **extra_groups) { - if (!name) - return ERR_PTR(-EINVAL); - - if (chip && (!chip->ops || !chip->ops->is_visible || !chip->info)) + if (!dev || !name || !chip) return ERR_PTR(-EINVAL); - if (chip && !dev) + if (!chip->ops || !chip->ops->is_visible || !chip->info) return ERR_PTR(-EINVAL); return __hwmon_device_register(dev, name, drvdata, chip, extra_groups); @@ -757,6 +915,31 @@ hwmon_device_register_with_info(struct device *dev, const char *name, EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); /** + * hwmon_device_register_for_thermal - register hwmon device for thermal subsystem + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * + * The use of this function is restricted. It is provided for legacy reasons + * and must only be called from the thermal subsystem. + * + * hwmon_device_unregister() must be called when the device is no + * longer needed. + * + * Returns the pointer to the new device. + */ +struct device * +hwmon_device_register_for_thermal(struct device *dev, const char *name, + void *drvdata) +{ + if (!name || !dev) + return ERR_PTR(-EINVAL); + + return __hwmon_device_register(dev, name, drvdata, NULL, NULL); +} +EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, HWMON_THERMAL); + +/** * hwmon_device_register - register w/ hwmon * @dev: the device to register * @@ -785,7 +968,7 @@ void hwmon_device_unregister(struct device *dev) if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { device_unregister(dev); - ida_simple_remove(&hwmon_ida, id); + ida_free(&hwmon_ida, id); } else dev_dbg(dev->parent, "hwmon_device_unregister() failed: bad class ID!\n"); @@ -897,6 +1080,59 @@ void devm_hwmon_device_unregister(struct device *dev) } EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); +static char *__hwmon_sanitize_name(struct device *dev, const char *old_name) +{ + char *name, *p; + + if (dev) + name = devm_kstrdup(dev, old_name, GFP_KERNEL); + else + name = kstrdup(old_name, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + + for (p = name; *p; p++) + if (hwmon_is_bad_char(*p)) + *p = '_'; + + return name; +} + +/** + * hwmon_sanitize_name - Replaces invalid characters in a hwmon name + * @name: NUL-terminated name + * + * Allocates a new string where any invalid characters will be replaced + * by an underscore. It is the responsibility of the caller to release + * the memory. + * + * Returns newly allocated name, or ERR_PTR on error. + */ +char *hwmon_sanitize_name(const char *name) +{ + return __hwmon_sanitize_name(NULL, name); +} +EXPORT_SYMBOL_GPL(hwmon_sanitize_name); + +/** + * devm_hwmon_sanitize_name - resource managed hwmon_sanitize_name() + * @dev: device to allocate memory for + * @name: NUL-terminated name + * + * Allocates a new string where any invalid characters will be replaced + * by an underscore. + * + * Returns newly allocated name, or ERR_PTR on error. + */ +char *devm_hwmon_sanitize_name(struct device *dev, const char *name) +{ + if (!dev) + return ERR_PTR(-EINVAL); + + return __hwmon_sanitize_name(dev, name); +} +EXPORT_SYMBOL_GPL(devm_hwmon_sanitize_name); + static void __init hwmon_pci_quirks(void) { #if defined CONFIG_X86 && defined CONFIG_PCI diff --git a/drivers/hwmon/i5500_temp.c b/drivers/hwmon/i5500_temp.c index 360f5aee1394..05f68e9c9477 100644 --- a/drivers/hwmon/i5500_temp.c +++ b/drivers/hwmon/i5500_temp.c @@ -5,6 +5,7 @@ * Copyright (C) 2012, 2014 Jean Delvare <jdelvare@suse.de> */ +#include <linux/bitops.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -12,7 +13,6 @@ #include <linux/device.h> #include <linux/pci.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> @@ -29,69 +29,78 @@ #define REG_CTCTRL 0xF7 #define REG_TSTIMER 0xF8 -/* - * Sysfs stuff - */ - -/* Sensor resolution : 0.5 degree C */ -static ssize_t temp1_input_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static umode_t i5500_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, + int channel) { - struct pci_dev *pdev = to_pci_dev(dev->parent); - long temp; - u16 tsthrhi; - s8 tsfsc; - - pci_read_config_word(pdev, REG_TSTHRHI, &tsthrhi); - pci_read_config_byte(pdev, REG_TSFSC, &tsfsc); - temp = ((long)tsthrhi - tsfsc) * 500; - - return sprintf(buf, "%ld\n", temp); + return 0444; } -static ssize_t thresh_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int i5500_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) { struct pci_dev *pdev = to_pci_dev(dev->parent); - int reg = to_sensor_dev_attr(devattr)->index; - long temp; u16 tsthr; + s8 tsfsc; + u8 ctsts; - pci_read_config_word(pdev, reg, &tsthr); - temp = tsthr * 500; + switch (type) { + case hwmon_temp: + switch (attr) { + /* Sensor resolution : 0.5 degree C */ + case hwmon_temp_input: + pci_read_config_word(pdev, REG_TSTHRHI, &tsthr); + pci_read_config_byte(pdev, REG_TSFSC, &tsfsc); + *val = (tsthr - tsfsc) * 500; + return 0; + case hwmon_temp_max: + pci_read_config_word(pdev, REG_TSTHRHI, &tsthr); + *val = tsthr * 500; + return 0; + case hwmon_temp_max_hyst: + pci_read_config_word(pdev, REG_TSTHRLO, &tsthr); + *val = tsthr * 500; + return 0; + case hwmon_temp_crit: + pci_read_config_word(pdev, REG_TSTHRCATA, &tsthr); + *val = tsthr * 500; + return 0; + case hwmon_temp_max_alarm: + pci_read_config_byte(pdev, REG_CTSTS, &ctsts); + *val = !!(ctsts & BIT(1)); + return 0; + case hwmon_temp_crit_alarm: + pci_read_config_byte(pdev, REG_CTSTS, &ctsts); + *val = !!(ctsts & BIT(0)); + return 0; + default: + break; + } + break; + default: + break; + } - return sprintf(buf, "%ld\n", temp); + return -EOPNOTSUPP; } -static ssize_t alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct pci_dev *pdev = to_pci_dev(dev->parent); - int nr = to_sensor_dev_attr(devattr)->index; - u8 ctsts; - - pci_read_config_byte(pdev, REG_CTSTS, &ctsts); - return sprintf(buf, "%u\n", (unsigned int)ctsts & (1 << nr)); -} +static const struct hwmon_ops i5500_ops = { + .is_visible = i5500_is_visible, + .read = i5500_read, +}; -static DEVICE_ATTR_RO(temp1_input); -static SENSOR_DEVICE_ATTR_RO(temp1_crit, thresh, 0xE2); -static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, thresh, 0xEC); -static SENSOR_DEVICE_ATTR_RO(temp1_max, thresh, 0xEE); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1); - -static struct attribute *i5500_temp_attrs[] = { - &dev_attr_temp1_input.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, +static const struct hwmon_channel_info *i5500_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM + ), NULL }; -ATTRIBUTE_GROUPS(i5500_temp); +static const struct hwmon_chip_info i5500_chip_info = { + .ops = &i5500_ops, + .info = i5500_info, +}; static const struct pci_device_id i5500_temp_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3438) }, @@ -121,9 +130,8 @@ static int i5500_temp_probe(struct pci_dev *pdev, return -ENODEV; } - hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, - "intel5500", NULL, - i5500_temp_groups); + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "intel5500", NULL, + &i5500_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index eeac4b04df27..783fa936e4d1 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -396,7 +396,7 @@ exit_remove: static int i5k_amb_add(void) { - int res = -ENODEV; + int res; /* only ever going to be one of these */ amb_pdev = platform_device_alloc(DRVNAME, 0); @@ -427,11 +427,13 @@ static int i5k_find_amb_registers(struct i5k_amb_data *data, if (!pcidev) return -ENODEV; - if (pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32)) + pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32); + if (val32 == (u32)~0) goto out; data->amb_base = val32; - if (pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32)) + pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32); + if (val32 == (u32)~0) goto out; data->amb_len = val32; @@ -458,11 +460,13 @@ static int i5k_channel_probe(u16 *amb_present, unsigned long dev_id) if (!pcidev) return -ENODEV; - if (pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16)) + pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16); + if (val16 == (u16)~0) goto out; amb_present[0] = val16; - if (pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16)) + pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16); + if (val16 == (u16)~0) goto out; amb_present[1] = val16; diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index d05ab713566d..157e232aace0 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -127,7 +127,7 @@ struct aem_data { struct device *hwmon_dev; struct platform_device *pdev; struct mutex lock; - char valid; + bool valid; unsigned long last_updated; /* In jiffies */ u8 ver_major; u8 ver_minor; @@ -219,7 +219,7 @@ struct aem_read_sensor_req { struct aem_read_sensor_resp { struct aem_iana_id id; - u8 bytes[0]; + u8 bytes[]; } __packed; /* Data structures to talk to the IPMI layer */ @@ -482,7 +482,7 @@ static void aem_delete(struct aem_data *data) ipmi_destroy_user(data->ipmi.user); platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); - ida_simple_remove(&aem_ida, data->id); + ida_free(&aem_ida, data->id); kfree(data); } @@ -539,7 +539,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + data->id = ida_alloc(&aem_ida, GFP_KERNEL); if (data->id < 0) goto id_err; @@ -550,7 +550,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) res = platform_device_add(data->pdev); if (res) - goto ipmi_err; + goto dev_add_err; platform_set_drvdata(data->pdev, data); @@ -598,9 +598,11 @@ hwmon_reg_err: ipmi_destroy_user(data->ipmi.user); ipmi_err: platform_set_drvdata(data->pdev, NULL); - platform_device_unregister(data->pdev); + platform_device_del(data->pdev); +dev_add_err: + platform_device_put(data->pdev); dev_err: - ida_simple_remove(&aem_ida, data->id); + ida_free(&aem_ida, data->id); id_err: kfree(data); @@ -679,7 +681,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + data->id = ida_alloc(&aem_ida, GFP_KERNEL); if (data->id < 0) goto id_err; @@ -690,7 +692,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, res = platform_device_add(data->pdev); if (res) - goto ipmi_err; + goto dev_add_err; platform_set_drvdata(data->pdev, data); @@ -738,9 +740,11 @@ hwmon_reg_err: ipmi_destroy_user(data->ipmi.user); ipmi_err: platform_set_drvdata(data->pdev, NULL); - platform_device_unregister(data->pdev); + platform_device_del(data->pdev); +dev_add_err: + platform_device_put(data->pdev); dev_err: - ida_simple_remove(&aem_ida, data->id); + ida_free(&aem_ida, data->id); id_err: kfree(data); diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index b2ab83c9fd9a..f6ec165c0fa8 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -66,7 +66,7 @@ struct ibmpex_bmc_data { struct device *hwmon_dev; struct device *bmc_device; struct mutex lock; - char valid; + bool valid; unsigned long last_updated; /* In jiffies */ struct ipmi_addr address; @@ -239,7 +239,7 @@ static void ibmpex_update_device(struct ibmpex_bmc_data *data) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; out: mutex_unlock(&data->lock); diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 0e525cfbdfc5..8e3724728cce 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -186,7 +186,7 @@ static void make_sensor_label(struct device_node *np, u32 id; size_t n; - n = snprintf(sdata->label, sizeof(sdata->label), "%s", label); + n = scnprintf(sdata->label, sizeof(sdata->label), "%s", label); /* * Core temp pretty print @@ -199,11 +199,11 @@ static void make_sensor_label(struct device_node *np, * The digital thermal sensors are associated * with a core. */ - n += snprintf(sdata->label + n, + n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, " %d", cpuid); else - n += snprintf(sdata->label + n, + n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, " phy%d", id); } @@ -211,7 +211,7 @@ static void make_sensor_label(struct device_node *np, * Membuffer pretty print */ if (!of_property_read_u32(np, "ibm,chip-id", &id)) - n += snprintf(sdata->label + n, sizeof(sdata->label) - n, + n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, " %d", id & 0xffff); } @@ -240,7 +240,7 @@ static int get_sensor_index_attr(const char *name, u32 *index, char *attr) if (err) return err; - strncpy(attr, dash_pos + 1, MAX_ATTR_LEN); + strscpy(attr, dash_pos + 1, MAX_ATTR_LEN); return 0; } diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index b85a125dd86f..3aa40893fc09 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -6,11 +6,13 @@ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/err.h> #include <linux/platform_device.h> +#include <linux/property.h> + #include <linux/hwmon.h> -#include <linux/of.h> #include <linux/hwmon-sysfs.h> #include <linux/iio/consumer.h> #include <linux/iio/types.h> @@ -149,8 +151,8 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->attr_group.attrs = st->attrs; st->groups[0] = &st->attr_group; - if (dev->of_node) { - sname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); + if (dev_fwnode(dev)) { + sname = devm_kasprintf(dev, GFP_KERNEL, "%pfwP", dev_fwnode(dev)); if (!sname) return -ENOMEM; strreplace(sname, '-', '_'); @@ -169,7 +171,7 @@ static const struct of_device_id iio_hwmon_of_match[] = { }; MODULE_DEVICE_TABLE(of, iio_hwmon_of_match); -static struct platform_driver __refdata iio_hwmon_driver = { +static struct platform_driver iio_hwmon_driver = { .driver = { .name = "iio_hwmon", .of_match_table = iio_hwmon_of_match, diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c index 70ad1efcb3de..9b58655d2de4 100644 --- a/drivers/hwmon/ina209.c +++ b/drivers/hwmon/ina209.c @@ -14,7 +14,7 @@ * Thanks to Jan Volkering * * Datasheet: - * http://www.ti.com/lit/gpn/ina209 + * https://www.ti.com/lit/gpn/ina209 */ #include <linux/kernel.h> @@ -259,7 +259,7 @@ static ssize_t ina209_interval_show(struct device *dev, { struct ina209_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", data->update_interval); + return sysfs_emit(buf, "%d\n", data->update_interval); } /* @@ -343,7 +343,7 @@ static ssize_t ina209_value_show(struct device *dev, return PTR_ERR(data); val = ina209_from_reg(attr->index, data->regs[attr->index]); - return snprintf(buf, PAGE_SIZE, "%ld\n", val); + return sysfs_emit(buf, "%ld\n", val); } static ssize_t ina209_alarm_show(struct device *dev, @@ -363,7 +363,7 @@ static ssize_t ina209_alarm_show(struct device *dev, * All alarms are in the INA209_STATUS register. To avoid a long * switch statement, the mask is passed in attr->index */ - return snprintf(buf, PAGE_SIZE, "%u\n", !!(status & mask)); + return sysfs_emit(buf, "%u\n", !!(status & mask)); } /* Shunt voltage, history, limits, alarms */ @@ -531,8 +531,7 @@ static int ina209_init_client(struct i2c_client *client, return 0; } -static int ina209_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ina209_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct ina209_data *data; @@ -569,13 +568,11 @@ out_restore_conf: return ret; } -static int ina209_remove(struct i2c_client *client) +static void ina209_remove(struct i2c_client *client) { struct ina209_data *data = i2c_get_clientdata(client); ina209_restore_conf(client, data); - - return 0; } static const struct i2c_device_id ina209_id[] = { @@ -597,7 +594,7 @@ static struct i2c_driver ina209_driver = { .name = "ina209", .of_match_table = of_match_ptr(ina209_of_match), }, - .probe = ina209_probe, + .probe_new = ina209_probe, .remove = ina209_remove, .id_table = ina209_id, }; diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c new file mode 100644 index 000000000000..50eb9c5e132e --- /dev/null +++ b/drivers/hwmon/ina238.c @@ -0,0 +1,644 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Texas Instruments INA238 power monitor chip + * Datasheet: https://www.ti.com/product/ina238 + * + * Copyright (C) 2021 Nathan Rossi <nathan.rossi@digi.com> + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#include <linux/platform_data/ina2xx.h> + +/* INA238 register definitions */ +#define INA238_CONFIG 0x0 +#define INA238_ADC_CONFIG 0x1 +#define INA238_SHUNT_CALIBRATION 0x2 +#define INA238_SHUNT_VOLTAGE 0x4 +#define INA238_BUS_VOLTAGE 0x5 +#define INA238_DIE_TEMP 0x6 +#define INA238_CURRENT 0x7 +#define INA238_POWER 0x8 +#define INA238_DIAG_ALERT 0xb +#define INA238_SHUNT_OVER_VOLTAGE 0xc +#define INA238_SHUNT_UNDER_VOLTAGE 0xd +#define INA238_BUS_OVER_VOLTAGE 0xe +#define INA238_BUS_UNDER_VOLTAGE 0xf +#define INA238_TEMP_LIMIT 0x10 +#define INA238_POWER_LIMIT 0x11 +#define INA238_DEVICE_ID 0x3f + +#define INA238_CONFIG_ADCRANGE BIT(4) + +#define INA238_DIAG_ALERT_TMPOL BIT(7) +#define INA238_DIAG_ALERT_SHNTOL BIT(6) +#define INA238_DIAG_ALERT_SHNTUL BIT(5) +#define INA238_DIAG_ALERT_BUSOL BIT(4) +#define INA238_DIAG_ALERT_BUSUL BIT(3) +#define INA238_DIAG_ALERT_POL BIT(2) + +#define INA238_REGISTERS 0x11 + +#define INA238_RSHUNT_DEFAULT 10000 /* uOhm */ + +/* Default configuration of device on reset. */ +#define INA238_CONFIG_DEFAULT 0 +/* 16 sample averaging, 1052us conversion time, continuous mode */ +#define INA238_ADC_CONFIG_DEFAULT 0xfb6a +/* Configure alerts to be based on averaged value (SLOWALERT) */ +#define INA238_DIAG_ALERT_DEFAULT 0x2000 +/* + * This driver uses a fixed calibration value in order to scale current/power + * based on a fixed shunt resistor value. This allows for conversion within the + * device to avoid integer limits whilst current/power accuracy is scaled + * relative to the shunt resistor value within the driver. This is similar to + * how the ina2xx driver handles current/power scaling. + * + * The end result of this is that increasing shunt values (from a fixed 20 mOhm + * shunt) increase the effective current/power accuracy whilst limiting the + * range and decreasing shunt values decrease the effective accuracy but + * increase the range. + * + * The value of the Current register is calculated given the following: + * Current (A) = (shunt voltage register * 5) * calibration / 81920 + * + * The maximum shunt voltage is 163.835 mV (0x7fff, ADC_RANGE = 0, gain = 4). + * With the maximum current value of 0x7fff and a fixed shunt value results in + * a calibration value of 16384 (0x4000). + * + * 0x7fff = (0x7fff * 5) * calibration / 81920 + * calibration = 0x4000 + * + * Equivalent calibration is applied for the Power register (maximum value for + * bus voltage is 102396.875 mV, 0x7fff), where the maximum power that can + * occur is ~16776192 uW (register value 0x147a8): + * + * This scaling means the resulting values for Current and Power registers need + * to be scaled by the difference between the fixed shunt resistor and the + * actual shunt resistor: + * + * shunt = 0x4000 / (819.2 * 10^6) / 0.001 = 20000 uOhms (with 1mA/lsb) + * + * Current (mA) = register value * 20000 / rshunt / 4 * gain + * Power (W) = 0.2 * register value * 20000 / rshunt / 4 * gain + */ +#define INA238_CALIBRATION_VALUE 16384 +#define INA238_FIXED_SHUNT 20000 + +#define INA238_SHUNT_VOLTAGE_LSB 5 /* 5 uV/lsb */ +#define INA238_BUS_VOLTAGE_LSB 3125 /* 3.125 mV/lsb */ +#define INA238_DIE_TEMP_LSB 125 /* 125 mC/lsb */ + +static struct regmap_config ina238_regmap_config = { + .max_register = INA238_REGISTERS, + .reg_bits = 8, + .val_bits = 16, +}; + +struct ina238_data { + struct i2c_client *client; + struct mutex config_lock; + struct regmap *regmap; + u32 rshunt; + int gain; +}; + +static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val) +{ + u8 data[3]; + int err; + + /* 24-bit register read */ + err = i2c_smbus_read_i2c_block_data(client, reg, 3, data); + if (err < 0) + return err; + if (err != 3) + return -EIO; + *val = (data[0] << 16) | (data[1] << 8) | data[2]; + + return 0; +} + +static int ina238_read_in(struct device *dev, u32 attr, int channel, + long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int reg, mask; + int regval; + int err; + + switch (channel) { + case 0: + switch (attr) { + case hwmon_in_input: + reg = INA238_SHUNT_VOLTAGE; + break; + case hwmon_in_max: + reg = INA238_SHUNT_OVER_VOLTAGE; + break; + case hwmon_in_min: + reg = INA238_SHUNT_UNDER_VOLTAGE; + break; + case hwmon_in_max_alarm: + reg = INA238_DIAG_ALERT; + mask = INA238_DIAG_ALERT_SHNTOL; + break; + case hwmon_in_min_alarm: + reg = INA238_DIAG_ALERT; + mask = INA238_DIAG_ALERT_SHNTUL; + break; + default: + return -EOPNOTSUPP; + } + break; + case 1: + switch (attr) { + case hwmon_in_input: + reg = INA238_BUS_VOLTAGE; + break; + case hwmon_in_max: + reg = INA238_BUS_OVER_VOLTAGE; + break; + case hwmon_in_min: + reg = INA238_BUS_UNDER_VOLTAGE; + break; + case hwmon_in_max_alarm: + reg = INA238_DIAG_ALERT; + mask = INA238_DIAG_ALERT_BUSOL; + break; + case hwmon_in_min_alarm: + reg = INA238_DIAG_ALERT; + mask = INA238_DIAG_ALERT_BUSUL; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + + switch (attr) { + case hwmon_in_input: + case hwmon_in_max: + case hwmon_in_min: + /* signed register, value in mV */ + regval = (s16)regval; + if (channel == 0) + /* gain of 1 -> LSB / 4 */ + *val = (regval * INA238_SHUNT_VOLTAGE_LSB) / + (1000 * (4 - data->gain + 1)); + else + *val = (regval * INA238_BUS_VOLTAGE_LSB) / 1000; + break; + case hwmon_in_max_alarm: + case hwmon_in_min_alarm: + *val = !!(regval & mask); + break; + } + + return 0; +} + +static int ina238_write_in(struct device *dev, u32 attr, int channel, + long val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int regval; + + if (attr != hwmon_in_max && attr != hwmon_in_min) + return -EOPNOTSUPP; + + /* convert decimal to register value */ + switch (channel) { + case 0: + /* signed value, clamp to max range +/-163 mV */ + regval = clamp_val(val, -163, 163); + regval = (regval * 1000 * (4 - data->gain + 1)) / + INA238_SHUNT_VOLTAGE_LSB; + regval = clamp_val(regval, S16_MIN, S16_MAX); + + switch (attr) { + case hwmon_in_max: + return regmap_write(data->regmap, + INA238_SHUNT_OVER_VOLTAGE, regval); + case hwmon_in_min: + return regmap_write(data->regmap, + INA238_SHUNT_UNDER_VOLTAGE, regval); + default: + return -EOPNOTSUPP; + } + case 1: + /* signed value, positive values only. Clamp to max 102.396 V */ + regval = clamp_val(val, 0, 102396); + regval = (regval * 1000) / INA238_BUS_VOLTAGE_LSB; + regval = clamp_val(regval, 0, S16_MAX); + + switch (attr) { + case hwmon_in_max: + return regmap_write(data->regmap, + INA238_BUS_OVER_VOLTAGE, regval); + case hwmon_in_min: + return regmap_write(data->regmap, + INA238_BUS_UNDER_VOLTAGE, regval); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int ina238_read_current(struct device *dev, u32 attr, long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int regval; + int err; + + switch (attr) { + case hwmon_curr_input: + err = regmap_read(data->regmap, INA238_CURRENT, ®val); + if (err < 0) + return err; + + /* Signed register, fixed 1mA current lsb. result in mA */ + *val = div_s64((s16)regval * INA238_FIXED_SHUNT * data->gain, + data->rshunt * 4); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ina238_read_power(struct device *dev, u32 attr, long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + long long power; + int regval; + int err; + + switch (attr) { + case hwmon_power_input: + err = ina238_read_reg24(data->client, INA238_POWER, ®val); + if (err) + return err; + + /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */ + power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * + data->gain, 20 * data->rshunt); + /* Clamp value to maximum value of long */ + *val = clamp_val(power, 0, LONG_MAX); + break; + case hwmon_power_max: + err = regmap_read(data->regmap, INA238_POWER_LIMIT, ®val); + if (err) + return err; + + /* + * Truncated 24-bit compare register, lower 8-bits are + * truncated. Same conversion to/from uW as POWER register. + */ + power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT * + data->gain, 20 * data->rshunt); + /* Clamp value to maximum value of long */ + *val = clamp_val(power, 0, LONG_MAX); + break; + case hwmon_power_max_alarm: + err = regmap_read(data->regmap, INA238_DIAG_ALERT, ®val); + if (err) + return err; + + *val = !!(regval & INA238_DIAG_ALERT_POL); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ina238_write_power(struct device *dev, u32 attr, long val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + long regval; + + if (attr != hwmon_power_max) + return -EOPNOTSUPP; + + /* + * Unsigned postive values. Compared against the 24-bit power register, + * lower 8-bits are truncated. Same conversion to/from uW as POWER + * register. + */ + regval = clamp_val(val, 0, LONG_MAX); + regval = div_u64(val * 20ULL * data->rshunt, + 1000ULL * INA238_FIXED_SHUNT * data->gain); + regval = clamp_val(regval >> 8, 0, U16_MAX); + + return regmap_write(data->regmap, INA238_POWER_LIMIT, regval); +} + +static int ina238_read_temp(struct device *dev, u32 attr, long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int regval; + int err; + + switch (attr) { + case hwmon_temp_input: + err = regmap_read(data->regmap, INA238_DIE_TEMP, ®val); + if (err) + return err; + + /* Signed, bits 15-4 of register, result in mC */ + *val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB; + break; + case hwmon_temp_max: + err = regmap_read(data->regmap, INA238_TEMP_LIMIT, ®val); + if (err) + return err; + + /* Signed, bits 15-4 of register, result in mC */ + *val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB; + break; + case hwmon_temp_max_alarm: + err = regmap_read(data->regmap, INA238_DIAG_ALERT, ®val); + if (err) + return err; + + *val = !!(regval & INA238_DIAG_ALERT_TMPOL); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ina238_write_temp(struct device *dev, u32 attr, long val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int regval; + + if (attr != hwmon_temp_max) + return -EOPNOTSUPP; + + /* Signed, bits 15-4 of register */ + regval = (val / INA238_DIE_TEMP_LSB) << 4; + regval = clamp_val(regval, S16_MIN, S16_MAX) & 0xfff0; + + return regmap_write(data->regmap, INA238_TEMP_LIMIT, regval); +} + +static int ina238_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_in: + return ina238_read_in(dev, attr, channel, val); + case hwmon_curr: + return ina238_read_current(dev, attr, val); + case hwmon_power: + return ina238_read_power(dev, attr, val); + case hwmon_temp: + return ina238_read_temp(dev, attr, val); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ina238_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int err; + + mutex_lock(&data->config_lock); + + switch (type) { + case hwmon_in: + err = ina238_write_in(dev, attr, channel, val); + break; + case hwmon_power: + err = ina238_write_power(dev, attr, val); + break; + case hwmon_temp: + err = ina238_write_temp(dev, attr, val); + break; + default: + err = -EOPNOTSUPP; + break; + } + + mutex_unlock(&data->config_lock); + return err; +} + +static umode_t ina238_is_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_max_alarm: + case hwmon_in_min_alarm: + return 0444; + case hwmon_in_max: + case hwmon_in_min: + return 0644; + default: + return 0; + } + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + return 0444; + default: + return 0; + } + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_max_alarm: + return 0444; + case hwmon_power_max: + return 0644; + default: + return 0; + } + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max_alarm: + return 0444; + case hwmon_temp_max: + return 0644; + default: + return 0; + } + default: + return 0; + } +} + +#define INA238_HWMON_IN_CONFIG (HWMON_I_INPUT | \ + HWMON_I_MAX | HWMON_I_MAX_ALARM | \ + HWMON_I_MIN | HWMON_I_MIN_ALARM) + +static const struct hwmon_channel_info *ina238_info[] = { + HWMON_CHANNEL_INFO(in, + /* 0: shunt voltage */ + INA238_HWMON_IN_CONFIG, + /* 1: bus voltage */ + INA238_HWMON_IN_CONFIG), + HWMON_CHANNEL_INFO(curr, + /* 0: current through shunt */ + HWMON_C_INPUT), + HWMON_CHANNEL_INFO(power, + /* 0: power */ + HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_MAX_ALARM), + HWMON_CHANNEL_INFO(temp, + /* 0: die temperature */ + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_ALARM), + NULL +}; + +static const struct hwmon_ops ina238_hwmon_ops = { + .is_visible = ina238_is_visible, + .read = ina238_read, + .write = ina238_write, +}; + +static const struct hwmon_chip_info ina238_chip_info = { + .ops = &ina238_hwmon_ops, + .info = ina238_info, +}; + +static int ina238_probe(struct i2c_client *client) +{ + struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev); + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct ina238_data *data; + int config; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->config_lock); + + data->regmap = devm_regmap_init_i2c(client, &ina238_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + /* load shunt value */ + data->rshunt = INA238_RSHUNT_DEFAULT; + if (device_property_read_u32(dev, "shunt-resistor", &data->rshunt) < 0 && pdata) + data->rshunt = pdata->shunt_uohms; + if (data->rshunt == 0) { + dev_err(dev, "invalid shunt resister value %u\n", data->rshunt); + return -EINVAL; + } + + /* load shunt gain value */ + if (device_property_read_u32(dev, "ti,shunt-gain", &data->gain) < 0) + data->gain = 4; /* Default of ADCRANGE = 0 */ + if (data->gain != 1 && data->gain != 4) { + dev_err(dev, "invalid shunt gain value %u\n", data->gain); + return -EINVAL; + } + + /* Setup CONFIG register */ + config = INA238_CONFIG_DEFAULT; + if (data->gain == 1) + config |= INA238_CONFIG_ADCRANGE; /* ADCRANGE = 1 is /1 */ + ret = regmap_write(data->regmap, INA238_CONFIG, config); + if (ret < 0) { + dev_err(dev, "error configuring the device: %d\n", ret); + return -ENODEV; + } + + /* Setup ADC_CONFIG register */ + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, + INA238_ADC_CONFIG_DEFAULT); + if (ret < 0) { + dev_err(dev, "error configuring the device: %d\n", ret); + return -ENODEV; + } + + /* Setup SHUNT_CALIBRATION register with fixed value */ + ret = regmap_write(data->regmap, INA238_SHUNT_CALIBRATION, + INA238_CALIBRATION_VALUE); + if (ret < 0) { + dev_err(dev, "error configuring the device: %d\n", ret); + return -ENODEV; + } + + /* Setup alert/alarm configuration */ + ret = regmap_write(data->regmap, INA238_DIAG_ALERT, + INA238_DIAG_ALERT_DEFAULT); + if (ret < 0) { + dev_err(dev, "error configuring the device: %d\n", ret); + return -ENODEV; + } + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &ina238_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + dev_info(dev, "power monitor %s (Rshunt = %u uOhm, gain = %u)\n", + client->name, data->rshunt, data->gain); + + return 0; +} + +static const struct i2c_device_id ina238_id[] = { + { "ina238", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ina238_id); + +static const struct of_device_id __maybe_unused ina238_of_match[] = { + { .compatible = "ti,ina238" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ina238_of_match); + +static struct i2c_driver ina238_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ina238", + .of_match_table = of_match_ptr(ina238_of_match), + }, + .probe_new = ina238_probe, + .id_table = ina238_id, +}; + +module_i2c_driver(ina238_driver); + +MODULE_AUTHOR("Nathan Rossi <nathan.rossi@digi.com>"); +MODULE_DESCRIPTION("ina238 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index e9e78c0b7212..00fc70305a89 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -4,19 +4,19 @@ * * INA219: * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface - * Datasheet: http://www.ti.com/product/ina219 + * Datasheet: https://www.ti.com/product/ina219 * * INA220: * Bi-Directional Current/Power Monitor with I2C Interface - * Datasheet: http://www.ti.com/product/ina220 + * Datasheet: https://www.ti.com/product/ina220 * * INA226: * Bi-Directional Current/Power Monitor with I2C Interface - * Datasheet: http://www.ti.com/product/ina226 + * Datasheet: https://www.ti.com/product/ina226 * * INA230: * Bi-directional Current/Power Monitor with I2C Interface - * Datasheet: http://www.ti.com/product/ina230 + * Datasheet: https://www.ti.com/product/ina230 * * Copyright (C) 2012 Lothar Felten <lothar.felten@gmail.com> * Thanks to Jan Volkering @@ -74,6 +74,17 @@ #define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9) #define INA226_SHIFT_AVG(val) ((val) << 9) +/* bit number of alert functions in Mask/Enable Register */ +#define INA226_SHUNT_OVER_VOLTAGE_BIT 15 +#define INA226_SHUNT_UNDER_VOLTAGE_BIT 14 +#define INA226_BUS_OVER_VOLTAGE_BIT 13 +#define INA226_BUS_UNDER_VOLTAGE_BIT 12 +#define INA226_POWER_OVER_LIMIT_BIT 11 + +/* bit mask for alert config bits of Mask/Enable Register */ +#define INA226_ALERT_CONFIG_MASK 0xFC00 +#define INA226_ALERT_FUNCTION_FLAG BIT(4) + /* common attrs, ina226 attrs and NULL */ #define INA2XX_MAX_ATTRIBUTE_GROUPS 3 @@ -137,7 +148,7 @@ static const struct ina2xx_config ina2xx_config[] = { * Available averaging rates for ina226. The indices correspond with * the bit values expected by the chip (according to the ina226 datasheet, * table 3 AVG bit settings, found at - * http://www.ti.com/lit/ds/symlink/ina226.pdf. + * https://www.ti.com/lit/ds/symlink/ina226.pdf. */ static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; @@ -299,8 +310,146 @@ static ssize_t ina2xx_value_show(struct device *dev, if (err < 0) return err; - return snprintf(buf, PAGE_SIZE, "%d\n", - ina2xx_get_value(data, attr->index, regval)); + return sysfs_emit(buf, "%d\n", ina2xx_get_value(data, attr->index, regval)); +} + +static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval) +{ + int reg; + + switch (bit) { + case INA226_SHUNT_OVER_VOLTAGE_BIT: + case INA226_SHUNT_UNDER_VOLTAGE_BIT: + reg = INA2XX_SHUNT_VOLTAGE; + break; + case INA226_BUS_OVER_VOLTAGE_BIT: + case INA226_BUS_UNDER_VOLTAGE_BIT: + reg = INA2XX_BUS_VOLTAGE; + break; + case INA226_POWER_OVER_LIMIT_BIT: + reg = INA2XX_POWER; + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + return 0; + } + + return ina2xx_get_value(data, reg, regval); +} + +/* + * Turns alert limit values into register values. + * Opposite of the formula in ina2xx_get_value(). + */ +static s16 ina226_alert_to_reg(struct ina2xx_data *data, u8 bit, int val) +{ + switch (bit) { + case INA226_SHUNT_OVER_VOLTAGE_BIT: + case INA226_SHUNT_UNDER_VOLTAGE_BIT: + val *= data->config->shunt_div; + return clamp_val(val, SHRT_MIN, SHRT_MAX); + case INA226_BUS_OVER_VOLTAGE_BIT: + case INA226_BUS_UNDER_VOLTAGE_BIT: + val = (val * 1000) << data->config->bus_voltage_shift; + val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); + return clamp_val(val, 0, SHRT_MAX); + case INA226_POWER_OVER_LIMIT_BIT: + val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); + return clamp_val(val, 0, USHRT_MAX); + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + return 0; + } +} + +static ssize_t ina226_alert_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = dev_get_drvdata(dev); + int regval; + int val = 0; + int ret; + + mutex_lock(&data->config_lock); + ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val); + if (ret) + goto abort; + + if (regval & BIT(attr->index)) { + ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, ®val); + if (ret) + goto abort; + val = ina226_reg_to_alert(data, attr->index, regval); + } + + ret = sysfs_emit(buf, "%d\n", val); +abort: + mutex_unlock(&data->config_lock); + return ret; +} + +static ssize_t ina226_alert_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + /* + * Clear all alerts first to avoid accidentally triggering ALERT pin + * due to register write sequence. Then, only enable the alert + * if the value is non-zero. + */ + mutex_lock(&data->config_lock); + ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE, + INA226_ALERT_CONFIG_MASK, 0); + if (ret < 0) + goto abort; + + ret = regmap_write(data->regmap, INA226_ALERT_LIMIT, + ina226_alert_to_reg(data, attr->index, val)); + if (ret < 0) + goto abort; + + if (val != 0) { + ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE, + INA226_ALERT_CONFIG_MASK, + BIT(attr->index)); + if (ret < 0) + goto abort; + } + + ret = count; +abort: + mutex_unlock(&data->config_lock); + return ret; +} + +static ssize_t ina226_alarm_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = dev_get_drvdata(dev); + int regval; + int alarm = 0; + int ret; + + ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val); + if (ret) + return ret; + + alarm = (regval & BIT(attr->index)) && + (regval & INA226_ALERT_FUNCTION_FLAG); + return sysfs_emit(buf, "%d\n", alarm); } /* @@ -331,7 +480,7 @@ static ssize_t ina2xx_shunt_show(struct device *dev, { struct ina2xx_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%li\n", data->rshunt); + return sysfs_emit(buf, "%li\n", data->rshunt); } static ssize_t ina2xx_shunt_store(struct device *dev, @@ -387,20 +536,43 @@ static ssize_t ina226_interval_show(struct device *dev, if (status) return status; - return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval)); + return sysfs_emit(buf, "%d\n", ina226_reg_to_interval(regval)); } /* shunt voltage */ static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE); +/* shunt voltage over/under voltage alert setting and alarm */ +static SENSOR_DEVICE_ATTR_RW(in0_crit, ina226_alert, + INA226_SHUNT_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RW(in0_lcrit, ina226_alert, + INA226_SHUNT_UNDER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in0_crit_alarm, ina226_alarm, + INA226_SHUNT_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in0_lcrit_alarm, ina226_alarm, + INA226_SHUNT_UNDER_VOLTAGE_BIT); /* bus voltage */ static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE); +/* bus voltage over/under voltage alert setting and alarm */ +static SENSOR_DEVICE_ATTR_RW(in1_crit, ina226_alert, + INA226_BUS_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RW(in1_lcrit, ina226_alert, + INA226_BUS_UNDER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in1_crit_alarm, ina226_alarm, + INA226_BUS_OVER_VOLTAGE_BIT); +static SENSOR_DEVICE_ATTR_RO(in1_lcrit_alarm, ina226_alarm, + INA226_BUS_UNDER_VOLTAGE_BIT); /* calculated current */ static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT); /* calculated power */ static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER); +/* over-limit power alert setting and alarm */ +static SENSOR_DEVICE_ATTR_RW(power1_crit, ina226_alert, + INA226_POWER_OVER_LIMIT_BIT); +static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina226_alarm, + INA226_POWER_OVER_LIMIT_BIT); /* shunt resistance */ static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION); @@ -423,6 +595,16 @@ static const struct attribute_group ina2xx_group = { }; static struct attribute *ina226_attrs[] = { + &sensor_dev_attr_in0_crit.dev_attr.attr, + &sensor_dev_attr_in0_lcrit.dev_attr.attr, + &sensor_dev_attr_in0_crit_alarm.dev_attr.attr, + &sensor_dev_attr_in0_lcrit_alarm.dev_attr.attr, + &sensor_dev_attr_in1_crit.dev_attr.attr, + &sensor_dev_attr_in1_lcrit.dev_attr.attr, + &sensor_dev_attr_in1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_in1_lcrit_alarm.dev_attr.attr, + &sensor_dev_attr_power1_crit.dev_attr.attr, + &sensor_dev_attr_power1_crit_alarm.dev_attr.attr, &sensor_dev_attr_update_interval.dev_attr.attr, NULL, }; @@ -431,8 +613,9 @@ static const struct attribute_group ina226_group = { .attrs = ina226_attrs, }; -static int ina2xx_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id ina2xx_id[]; + +static int ina2xx_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ina2xx_data *data; @@ -444,7 +627,7 @@ static int ina2xx_probe(struct i2c_client *client, if (client->dev.of_node) chip = (enum ina2xx_ids)of_device_get_match_data(&client->dev); else - chip = id->driver_data; + chip = i2c_match_id(ina2xx_id, client)->driver_data; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -534,7 +717,7 @@ static struct i2c_driver ina2xx_driver = { .name = "ina2xx", .of_match_table = of_match_ptr(ina2xx_of_match), }, - .probe = ina2xx_probe, + .probe_new = ina2xx_probe, .id_table = ina2xx_id, }; diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index f335d0cb0c77..2a57f4b60c29 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -2,7 +2,7 @@ /* * INA3221 Triple Current/Voltage Monitor * - * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> */ @@ -139,7 +139,7 @@ static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) (ina->reg_config & INA3221_CONFIG_CHx_EN(channel)); } -/** +/* * Helper function to return the resistor value for current summation. * * There is a condition to calculate current summation -- all the shunt @@ -196,13 +196,11 @@ static inline u32 ina3221_reg_to_interval_us(u16 config) u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK); u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config); u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config); - u32 samples_idx = INA3221_CONFIG_AVG(config); - u32 samples = ina3221_avg_samples[samples_idx]; u32 vbus_ct = ina3221_conv_time[vbus_ct_idx]; u32 vsh_ct = ina3221_conv_time[vsh_ct_idx]; /* Calculate total conversion time */ - return channels * (vbus_ct + vsh_ct) * samples; + return channels * (vbus_ct + vsh_ct); } static inline int ina3221_wait_for_data(struct ina3221_data *ina) @@ -288,13 +286,14 @@ static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) return -ENODATA; /* Write CONFIG register to trigger a single-shot measurement */ - if (ina->single_shot) + if (ina->single_shot) { regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); - ret = ina3221_wait_for_data(ina); - if (ret) - return ret; + ret = ina3221_wait_for_data(ina); + if (ret) + return ret; + } ret = ina3221_read_value(ina, reg, ®val); if (ret) @@ -344,15 +343,16 @@ static int ina3221_read_curr(struct device *dev, u32 attr, return -ENODATA; /* Write CONFIG register to trigger a single-shot measurement */ - if (ina->single_shot) + if (ina->single_shot) { regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); - ret = ina3221_wait_for_data(ina); - if (ret) - return ret; + ret = ina3221_wait_for_data(ina); + if (ret) + return ret; + } - /* fall through */ + fallthrough; case hwmon_curr_crit: case hwmon_curr_max: if (!resistance_uo) @@ -489,7 +489,7 @@ static int ina3221_write_enable(struct device *dev, int channel, bool enable) /* For enabling routine, increase refcount and resume() at first */ if (enable) { - ret = pm_runtime_get_sync(ina->pm_dev); + ret = pm_runtime_resume_and_get(ina->pm_dev); if (ret < 0) { dev_err(dev, "Failed to get PM runtime\n"); return ret; @@ -698,7 +698,7 @@ static ssize_t ina3221_shunt_show(struct device *dev, unsigned int channel = sd_attr->index; struct ina3221_input *input = &ina->inputs[channel]; - return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); + return sysfs_emit(buf, "%d\n", input->shunt_resistor); } static ssize_t ina3221_shunt_store(struct device *dev, @@ -822,8 +822,7 @@ static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) return 0; } -static int ina3221_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ina3221_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ina3221_data *ina; @@ -914,7 +913,7 @@ fail: return ret; } -static int ina3221_remove(struct i2c_client *client) +static void ina3221_remove(struct i2c_client *client) { struct ina3221_data *ina = dev_get_drvdata(&client->dev); int i; @@ -927,11 +926,9 @@ static int ina3221_remove(struct i2c_client *client) pm_runtime_put_noidle(ina->pm_dev); mutex_destroy(&ina->lock); - - return 0; } -static int __maybe_unused ina3221_suspend(struct device *dev) +static int ina3221_suspend(struct device *dev) { struct ina3221_data *ina = dev_get_drvdata(dev); int ret; @@ -954,7 +951,7 @@ static int __maybe_unused ina3221_suspend(struct device *dev) return 0; } -static int __maybe_unused ina3221_resume(struct device *dev) +static int ina3221_resume(struct device *dev) { struct ina3221_data *ina = dev_get_drvdata(dev); int ret; @@ -997,11 +994,8 @@ static int __maybe_unused ina3221_resume(struct device *dev) return 0; } -static const struct dev_pm_ops ina3221_pm = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ina3221_pm, ina3221_suspend, ina3221_resume, + NULL); static const struct of_device_id ina3221_of_match_table[] = { { .compatible = "ti,ina3221", }, @@ -1016,12 +1010,12 @@ static const struct i2c_device_id ina3221_ids[] = { MODULE_DEVICE_TABLE(i2c, ina3221_ids); static struct i2c_driver ina3221_i2c_driver = { - .probe = ina3221_probe, + .probe_new = ina3221_probe, .remove = ina3221_remove, .driver = { .name = INA3221_DRIVER_NAME, .of_match_table = ina3221_of_match_table, - .pm = &ina3221_pm, + .pm = pm_ptr(&ina3221_pm), }, .id_table = ina3221_ids, }; diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c new file mode 100644 index 000000000000..6e82f7200d1c --- /dev/null +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel MAX 10 BMC HWMON Driver + * + * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. + * + */ +#include <linux/device.h> +#include <linux/hwmon.h> +#include <linux/mfd/intel-m10-bmc.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> + +struct m10bmc_sdata { + unsigned int reg_input; + unsigned int reg_max; + unsigned int reg_crit; + unsigned int reg_hyst; + unsigned int reg_min; + unsigned int multiplier; + const char *label; +}; + +struct m10bmc_hwmon_board_data { + const struct m10bmc_sdata *tables[hwmon_max]; + const struct hwmon_channel_info **hinfo; +}; + +struct m10bmc_hwmon { + struct device *dev; + struct hwmon_chip_info chip; + char *hw_name; + struct intel_m10bmc *m10bmc; + const struct m10bmc_hwmon_board_data *bdata; +}; + +static const struct m10bmc_sdata n3000bmc_temp_tbl[] = { + { 0x100, 0x104, 0x108, 0x10c, 0x0, 500, "Board Temperature" }, + { 0x110, 0x114, 0x118, 0x0, 0x0, 500, "FPGA Die Temperature" }, + { 0x11c, 0x124, 0x120, 0x0, 0x0, 500, "QSFP0 Temperature" }, + { 0x12c, 0x134, 0x130, 0x0, 0x0, 500, "QSFP1 Temperature" }, + { 0x168, 0x0, 0x0, 0x0, 0x0, 500, "Retimer A Temperature" }, + { 0x16c, 0x0, 0x0, 0x0, 0x0, 500, "Retimer A SerDes Temperature" }, + { 0x170, 0x0, 0x0, 0x0, 0x0, 500, "Retimer B Temperature" }, + { 0x174, 0x0, 0x0, 0x0, 0x0, 500, "Retimer B SerDes Temperature" }, +}; + +static const struct m10bmc_sdata n3000bmc_in_tbl[] = { + { 0x128, 0x0, 0x0, 0x0, 0x0, 1, "QSFP0 Supply Voltage" }, + { 0x138, 0x0, 0x0, 0x0, 0x0, 1, "QSFP1 Supply Voltage" }, + { 0x13c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage" }, + { 0x144, 0x0, 0x0, 0x0, 0x0, 1, "12V Backplane Voltage" }, + { 0x14c, 0x0, 0x0, 0x0, 0x0, 1, "1.2V Voltage" }, + { 0x150, 0x0, 0x0, 0x0, 0x0, 1, "12V AUX Voltage" }, + { 0x158, 0x0, 0x0, 0x0, 0x0, 1, "1.8V Voltage" }, + { 0x15c, 0x0, 0x0, 0x0, 0x0, 1, "3.3V Voltage" }, +}; + +static const struct m10bmc_sdata n3000bmc_curr_tbl[] = { + { 0x140, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Current" }, + { 0x148, 0x0, 0x0, 0x0, 0x0, 1, "12V Backplane Current" }, + { 0x154, 0x0, 0x0, 0x0, 0x0, 1, "12V AUX Current" }, +}; + +static const struct m10bmc_sdata n3000bmc_power_tbl[] = { + { 0x160, 0x0, 0x0, 0x0, 0x0, 1000, "Board Power" }, +}; + +static const struct hwmon_channel_info *n3000bmc_hinfo[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + 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_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_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_LABEL), + NULL +}; + +static const struct m10bmc_sdata d5005bmc_temp_tbl[] = { + { 0x100, 0x104, 0x108, 0x10c, 0x0, 500, "Board Inlet Air Temperature" }, + { 0x110, 0x114, 0x118, 0x0, 0x0, 500, "FPGA Core Temperature" }, + { 0x11c, 0x120, 0x124, 0x128, 0x0, 500, "Board Exhaust Air Temperature" }, + { 0x12c, 0x130, 0x134, 0x0, 0x0, 500, "FPGA Transceiver Temperature" }, + { 0x138, 0x13c, 0x140, 0x144, 0x0, 500, "RDIMM0 Temperature" }, + { 0x148, 0x14c, 0x150, 0x154, 0x0, 500, "RDIMM1 Temperature" }, + { 0x158, 0x15c, 0x160, 0x164, 0x0, 500, "RDIMM2 Temperature" }, + { 0x168, 0x16c, 0x170, 0x174, 0x0, 500, "RDIMM3 Temperature" }, + { 0x178, 0x17c, 0x180, 0x0, 0x0, 500, "QSFP0 Temperature" }, + { 0x188, 0x18c, 0x190, 0x0, 0x0, 500, "QSFP1 Temperature" }, + { 0x1a0, 0x1a4, 0x1a8, 0x0, 0x0, 500, "3.3v Temperature" }, + { 0x1bc, 0x1c0, 0x1c4, 0x0, 0x0, 500, "VCCERAM Temperature" }, + { 0x1d8, 0x1dc, 0x1e0, 0x0, 0x0, 500, "VCCR Temperature" }, + { 0x1f4, 0x1f8, 0x1fc, 0x0, 0x0, 500, "VCCT Temperature" }, + { 0x210, 0x214, 0x218, 0x0, 0x0, 500, "1.8v Temperature" }, + { 0x22c, 0x230, 0x234, 0x0, 0x0, 500, "12v Backplane Temperature" }, + { 0x248, 0x24c, 0x250, 0x0, 0x0, 500, "12v AUX Temperature" }, +}; + +static const struct m10bmc_sdata d5005bmc_in_tbl[] = { + { 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QSFP0 Supply Voltage" }, + { 0x194, 0x0, 0x0, 0x0, 0x0, 1, "QSFP1 Supply Voltage" }, + { 0x198, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage" }, + { 0x1ac, 0x1b0, 0x1b4, 0x0, 0x0, 1, "3.3v Voltage" }, + { 0x1c8, 0x1cc, 0x1d0, 0x0, 0x0, 1, "VCCERAM Voltage" }, + { 0x1e4, 0x1e8, 0x1ec, 0x0, 0x0, 1, "VCCR Voltage" }, + { 0x200, 0x204, 0x208, 0x0, 0x0, 1, "VCCT Voltage" }, + { 0x21c, 0x220, 0x224, 0x0, 0x0, 1, "1.8v Voltage" }, + { 0x238, 0x0, 0x0, 0x0, 0x23c, 1, "12v Backplane Voltage" }, + { 0x254, 0x0, 0x0, 0x0, 0x258, 1, "12v AUX Voltage" }, +}; + +static const struct m10bmc_sdata d5005bmc_curr_tbl[] = { + { 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Current" }, + { 0x1b8, 0x0, 0x0, 0x0, 0x0, 1, "3.3v Current" }, + { 0x1d4, 0x0, 0x0, 0x0, 0x0, 1, "VCCERAM Current" }, + { 0x1f0, 0x0, 0x0, 0x0, 0x0, 1, "VCCR Current" }, + { 0x20c, 0x0, 0x0, 0x0, 0x0, 1, "VCCT Current" }, + { 0x228, 0x0, 0x0, 0x0, 0x0, 1, "1.8v Current" }, + { 0x240, 0x244, 0x0, 0x0, 0x0, 1, "12v Backplane Current" }, + { 0x25c, 0x260, 0x0, 0x0, 0x0, 1, "12v AUX Current" }, +}; + +static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = { + .tables = { + [hwmon_temp] = n3000bmc_temp_tbl, + [hwmon_in] = n3000bmc_in_tbl, + [hwmon_curr] = n3000bmc_curr_tbl, + [hwmon_power] = n3000bmc_power_tbl, + }, + + .hinfo = n3000bmc_hinfo, +}; + +static const struct hwmon_channel_info *d5005bmc_hinfo[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + 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_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_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, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL), + NULL +}; + +static const struct m10bmc_hwmon_board_data d5005bmc_hwmon_bdata = { + .tables = { + [hwmon_temp] = d5005bmc_temp_tbl, + [hwmon_in] = d5005bmc_in_tbl, + [hwmon_curr] = d5005bmc_curr_tbl, + }, + + .hinfo = d5005bmc_hinfo, +}; + +static const struct m10bmc_sdata n5010bmc_temp_tbl[] = { + { 0x100, 0x0, 0x104, 0x0, 0x0, 1000, "Board Local Temperature" }, + { 0x108, 0x0, 0x10c, 0x0, 0x0, 1000, "FPGA 1 Temperature" }, + { 0x110, 0x0, 0x114, 0x0, 0x0, 1000, "FPGA 2 Temperature" }, + { 0x118, 0x0, 0x0, 0x0, 0x0, 1000, "Card Top Temperature" }, + { 0x11c, 0x0, 0x0, 0x0, 0x0, 1000, "Card Bottom Temperature" }, + { 0x128, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 1.2V Temperature" }, + { 0x134, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 5V Temperature" }, + { 0x140, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.9V Temperature" }, + { 0x14c, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.85V Temperature" }, + { 0x158, 0x0, 0x0, 0x0, 0x0, 1000, "AUX 12V Temperature" }, + { 0x164, 0x0, 0x0, 0x0, 0x0, 1000, "Backplane 12V Temperature" }, + { 0x1a8, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-1 Temperature" }, + { 0x1ac, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-2 Temperature" }, + { 0x1b0, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-3 Temperature" }, + { 0x1b4, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-4 Temperature" }, + { 0x1b8, 0x0, 0x0, 0x0, 0x0, 1000, "CVL1 Internal Temperature" }, + { 0x1bc, 0x0, 0x0, 0x0, 0x0, 1000, "CVL2 Internal Temperature" }, +}; + +static const struct m10bmc_sdata n5010bmc_in_tbl[] = { + { 0x120, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Voltage" }, + { 0x12c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Voltage" }, + { 0x138, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Voltage" }, + { 0x144, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Voltage" }, + { 0x150, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Voltage" }, + { 0x15c, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Voltage" }, + { 0x16c, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Voltage" }, + { 0x17c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Voltage" }, + { 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Voltage" }, + { 0x18c, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Voltage" }, + { 0x194, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Voltage" }, + { 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Voltage" }, + { 0x1a4, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Voltage" }, +}; + +static const struct m10bmc_sdata n5010bmc_curr_tbl[] = { + { 0x124, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Current" }, + { 0x130, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Current" }, + { 0x13c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Current" }, + { 0x148, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Current" }, + { 0x154, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Current" }, + { 0x160, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Current" }, + { 0x168, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Current" }, + { 0x178, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Current" }, + { 0x180, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Current" }, + { 0x188, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Current" }, + { 0x190, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Current" }, + { 0x198, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Current" }, + { 0x1a0, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Current" }, +}; + +static const struct hwmon_channel_info *n5010bmc_hinfo[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + 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_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_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_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, + 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, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + NULL +}; + +static const struct m10bmc_hwmon_board_data n5010bmc_hwmon_bdata = { + .tables = { + [hwmon_temp] = n5010bmc_temp_tbl, + [hwmon_in] = n5010bmc_in_tbl, + [hwmon_curr] = n5010bmc_curr_tbl, + }, + + .hinfo = n5010bmc_hinfo, +}; + +static umode_t +m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return 0444; +} + +static const struct m10bmc_sdata * +find_sensor_data(struct m10bmc_hwmon *hw, enum hwmon_sensor_types type, + int channel) +{ + const struct m10bmc_sdata *tbl; + + tbl = hw->bdata->tables[type]; + if (!tbl) + return ERR_PTR(-EOPNOTSUPP); + + return &tbl[channel]; +} + +static int do_sensor_read(struct m10bmc_hwmon *hw, + const struct m10bmc_sdata *data, + unsigned int regoff, long *val) +{ + unsigned int regval; + int ret; + + ret = m10bmc_sys_read(hw->m10bmc, regoff, ®val); + if (ret) + return ret; + + /* + * BMC Firmware will return 0xdeadbeef if the sensor value is invalid + * at that time. This usually happens on sensor channels which connect + * to external pluggable modules, e.g. QSFP temperature and voltage. + * When the QSFP is unplugged from cage, driver will get 0xdeadbeef + * from their registers. + */ + if (regval == 0xdeadbeef) + return -ENODATA; + + *val = regval * data->multiplier; + + return 0; +} + +static int m10bmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct m10bmc_hwmon *hw = dev_get_drvdata(dev); + unsigned int reg = 0, reg_hyst = 0; + const struct m10bmc_sdata *data; + long hyst, value; + int ret; + + data = find_sensor_data(hw, type, channel); + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + reg = data->reg_input; + break; + case hwmon_temp_max_hyst: + reg_hyst = data->reg_hyst; + fallthrough; + case hwmon_temp_max: + reg = data->reg_max; + break; + case hwmon_temp_crit_hyst: + reg_hyst = data->reg_hyst; + fallthrough; + case hwmon_temp_crit: + reg = data->reg_crit; + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + reg = data->reg_input; + break; + case hwmon_in_max: + reg = data->reg_max; + break; + case hwmon_in_crit: + reg = data->reg_crit; + break; + case hwmon_in_min: + reg = data->reg_min; + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + reg = data->reg_input; + break; + case hwmon_curr_max: + reg = data->reg_max; + break; + case hwmon_curr_crit: + reg = data->reg_crit; + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + reg = data->reg_input; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + if (!reg) + return -EOPNOTSUPP; + + ret = do_sensor_read(hw, data, reg, &value); + if (ret) + return ret; + + if (reg_hyst) { + ret = do_sensor_read(hw, data, reg_hyst, &hyst); + if (ret) + return ret; + + value -= hyst; + } + + *val = value; + + return 0; +} + +static int m10bmc_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct m10bmc_hwmon *hw = dev_get_drvdata(dev); + const struct m10bmc_sdata *data; + + data = find_sensor_data(hw, type, channel); + if (IS_ERR(data)) + return PTR_ERR(data); + + *str = data->label; + + return 0; +} + +static const struct hwmon_ops m10bmc_hwmon_ops = { + .is_visible = m10bmc_hwmon_is_visible, + .read = m10bmc_hwmon_read, + .read_string = m10bmc_hwmon_read_string, +}; + +static int m10bmc_hwmon_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct intel_m10bmc *m10bmc = dev_get_drvdata(pdev->dev.parent); + struct device *hwmon_dev, *dev = &pdev->dev; + struct m10bmc_hwmon *hw; + + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + hw->dev = dev; + hw->m10bmc = m10bmc; + hw->bdata = (const struct m10bmc_hwmon_board_data *)id->driver_data; + + hw->chip.info = hw->bdata->hinfo; + hw->chip.ops = &m10bmc_hwmon_ops; + + hw->hw_name = devm_hwmon_sanitize_name(dev, id->name); + if (IS_ERR(hw->hw_name)) + return PTR_ERR(hw->hw_name); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, hw->hw_name, + hw, &hw->chip, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct platform_device_id intel_m10bmc_hwmon_ids[] = { + { + .name = "n3000bmc-hwmon", + .driver_data = (unsigned long)&n3000bmc_hwmon_bdata, + }, + { + .name = "d5005bmc-hwmon", + .driver_data = (unsigned long)&d5005bmc_hwmon_bdata, + }, + { + .name = "n5010bmc-hwmon", + .driver_data = (unsigned long)&n5010bmc_hwmon_bdata, + }, + { } +}; + +static struct platform_driver intel_m10bmc_hwmon_driver = { + .probe = m10bmc_hwmon_probe, + .driver = { + .name = "intel-m10-bmc-hwmon", + }, + .id_table = intel_m10bmc_hwmon_ids, +}; +module_platform_driver(intel_m10bmc_hwmon_driver); + +MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids); +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index fac9b5c68a6a..7bd154ba351b 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -519,7 +519,7 @@ struct it87_data { unsigned short addr; const char *name; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u16 in_scaled; /* Internal voltage sensors are scaled */ @@ -844,7 +844,7 @@ static struct it87_data *it87_update_device(struct device *dev) data->vid &= 0x3f; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -980,7 +980,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, regval |= 0x80; it87_write_value(data, IT87_REG_BEEP_ENABLE, regval); } - data->valid = 0; + data->valid = false; reg = IT87_REG_TEMP_OFFSET[nr]; break; } @@ -1079,7 +1079,7 @@ static ssize_t set_temp_type(struct device *dev, struct device_attribute *attr, it87_write_value(data, IT87_REG_TEMP_ENABLE, data->sensor); if (has_temp_old_peci(data, nr)) it87_write_value(data, IT87_REG_TEMP_EXTRA, data->extra); - data->valid = 0; /* Force cache refresh */ + data->valid = false; /* Force cache refresh */ mutex_unlock(&data->update_lock); return count; } @@ -1834,7 +1834,7 @@ static ssize_t clear_intrusion(struct device *dev, config |= BIT(5); it87_write_value(data, IT87_REG_CONFIG, config); /* Invalidate cache to force re-read */ - data->valid = 0; + data->valid = false; } mutex_unlock(&data->update_lock); @@ -1981,7 +1981,7 @@ static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 3); static umode_t it87_in_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 5; /* voltage index */ int a = index % 5; /* attribute index */ @@ -2065,7 +2065,7 @@ static const struct attribute_group it87_group_in = { static umode_t it87_temp_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 7; /* temperature index */ int a = index % 7; /* attribute index */ @@ -2126,7 +2126,7 @@ static const struct attribute_group it87_group_temp = { static umode_t it87_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); if ((index == 2 || index == 3) && !data->has_vid) @@ -2158,7 +2158,7 @@ static const struct attribute_group it87_group = { static umode_t it87_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 5; /* fan index */ int a = index % 5; /* attribute index */ @@ -2229,7 +2229,7 @@ static const struct attribute_group it87_group_fan = { static umode_t it87_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 4; /* pwm index */ int a = index % 4; /* attribute index */ @@ -2290,7 +2290,7 @@ static const struct attribute_group it87_group_pwm = { static umode_t it87_auto_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 11; /* pwm index */ int a = index % 11; /* attribute index */ @@ -3179,7 +3179,7 @@ static int it87_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(hwmon_dev); } -static void __maybe_unused it87_resume_sio(struct platform_device *pdev) +static void it87_resume_sio(struct platform_device *pdev) { struct it87_data *data = dev_get_drvdata(&pdev->dev); int err; @@ -3211,7 +3211,7 @@ static void __maybe_unused it87_resume_sio(struct platform_device *pdev) superio_exit(data->sioaddr); } -static int __maybe_unused it87_resume(struct device *dev) +static int it87_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct it87_data *data = dev_get_drvdata(dev); @@ -3229,7 +3229,7 @@ static int __maybe_unused it87_resume(struct device *dev) it87_start_monitoring(data); /* force update */ - data->valid = 0; + data->valid = false; mutex_unlock(&data->update_lock); @@ -3238,12 +3238,12 @@ static int __maybe_unused it87_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume); static struct platform_driver it87_driver = { .driver = { .name = DRVNAME, - .pm = &it87_dev_pm_ops, + .pm = pm_sleep_ptr(&it87_dev_pm_ops), }, .probe = it87_probe, }; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index f2d81b0558e5..30888feaf589 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -63,6 +63,7 @@ static const unsigned short normal_i2c[] = { #define STM_MANID 0x104a /* ST Microelectronics */ #define GT_MANID 0x1c68 /* Giantec */ #define GT_MANID2 0x132d /* Giantec, 2nd mfg ID */ +#define SI_MANID 0x1c85 /* Seiko Instruments */ /* SMBUS register */ #define SMBUS_STMOUT BIT(7) /* SMBus time-out, active low */ @@ -137,6 +138,9 @@ static const unsigned short normal_i2c[] = { #define CAT34TS04_DEVID 0x2200 #define CAT34TS04_DEVID_MASK 0xfff0 +#define N34TS04_DEVID 0x2230 +#define N34TS04_DEVID_MASK 0xfff0 + /* ST Microelectronics */ #define STTS424_DEVID 0x0101 #define STTS424_DEVID_MASK 0xffff @@ -153,6 +157,10 @@ static const unsigned short normal_i2c[] = { #define STTS3000_DEVID 0x0200 #define STTS3000_DEVID_MASK 0xffff +/* Seiko Instruments */ +#define S34TS04A_DEVID 0x2221 +#define S34TS04A_DEVID_MASK 0xffff + static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 }; struct jc42_chips { @@ -181,7 +189,9 @@ static struct jc42_chips jc42_chips[] = { { ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK }, { ONS_MANID, CAT34TS02C_DEVID, CAT34TS02C_DEVID_MASK }, { ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK }, + { ONS_MANID, N34TS04_DEVID, N34TS04_DEVID_MASK }, { NXP_MANID, SE98_DEVID, SE98_DEVID_MASK }, + { SI_MANID, S34TS04A_DEVID, S34TS04A_DEVID_MASK }, { STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK }, { STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK }, { STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK }, @@ -431,7 +441,7 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) struct jc42_chips *chip = &jc42_chips[i]; if (manid == chip->manid && (devid & chip->devid_mask) == chip->devid) { - strlcpy(info->type, "jc42", I2C_NAME_SIZE); + strscpy(info->type, "jc42", I2C_NAME_SIZE); return 0; } } @@ -439,6 +449,8 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) } static const struct hwmon_channel_info *jc42_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_HYST | @@ -458,7 +470,7 @@ static const struct hwmon_chip_info jc42_chip_info = { .info = jc42_info, }; -static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int jc42_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -506,13 +518,13 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) } data->config = config; - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + hwmon_dev = devm_hwmon_device_register_with_info(dev, "jc42", data, &jc42_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } -static int jc42_remove(struct i2c_client *client) +static void jc42_remove(struct i2c_client *client) { struct jc42_data *data = i2c_get_clientdata(client); @@ -525,7 +537,6 @@ static int jc42_remove(struct i2c_client *client) | (data->config & JC42_CFG_HYST_MASK); i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); } - return 0; } #ifdef CONFIG_PM @@ -581,7 +592,7 @@ static struct i2c_driver jc42_driver = { .pm = JC42_DEV_PM_OPS, .of_match_table = of_match_ptr(jc42_of_ids), }, - .probe = jc42_probe, + .probe_new = jc42_probe, .remove = jc42_remove, .id_table = jc42_id, .detect = jc42_detect, diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index e39354ffe973..5a9d47a229e4 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -11,17 +11,9 @@ * convert raw register values is from https://github.com/ocerman/zenpower. * The information is not confirmed from chip datasheets, but experiments * suggest that it provides reasonable temperature values. - * - Register addresses to read chip voltage and current are also from - * https://github.com/ocerman/zenpower, and not confirmed from chip - * datasheets. Current calibration is board specific and not typically - * shared by board vendors. For this reason, current values are - * normalized to report 1A/LSB for core current and and 0.25A/LSB for SoC - * current. Reported values can be adjusted using the sensors configuration - * file. */ #include <linux/bitops.h> -#include <linux/debugfs.h> #include <linux/err.h> #include <linux/hwmon.h> #include <linux/init.h> @@ -73,22 +65,16 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); #define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET 0xd8200c64 #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4 -/* F17h M01h Access througn SMN */ -#define F17H_M01H_REPORTED_TEMP_CTRL_OFFSET 0x00059800 +/* Common for Zen CPU families (Family 17h and 18h and 19h) */ +#define ZEN_REPORTED_TEMP_CTRL_BASE 0x00059800 -#define F17H_M70H_CCD_TEMP(x) (0x00059954 + ((x) * 4)) -#define F17H_M70H_CCD_TEMP_VALID BIT(11) -#define F17H_M70H_CCD_TEMP_MASK GENMASK(10, 0) +#define ZEN_CCD_TEMP(offset, x) (ZEN_REPORTED_TEMP_CTRL_BASE + \ + (offset) + ((x) * 4)) +#define ZEN_CCD_TEMP_VALID BIT(11) +#define ZEN_CCD_TEMP_MASK GENMASK(10, 0) -#define F17H_M01H_SVI 0x0005A000 -#define F17H_M01H_SVI_TEL_PLANE0 (F17H_M01H_SVI + 0xc) -#define F17H_M01H_SVI_TEL_PLANE1 (F17H_M01H_SVI + 0x10) - -#define CUR_TEMP_SHIFT 21 -#define CUR_TEMP_RANGE_SEL_MASK BIT(19) - -#define CFACTOR_ICORE 1000000 /* 1A / LSB */ -#define CFACTOR_ISOC 250000 /* 0.25A / LSB */ +#define ZEN_CUR_TEMP_SHIFT 21 +#define ZEN_CUR_TEMP_RANGE_SEL_MASK BIT(19) struct k10temp_data { struct pci_dev *pdev; @@ -96,13 +82,18 @@ struct k10temp_data { void (*read_tempreg)(struct pci_dev *pdev, u32 *regval); int temp_offset; u32 temp_adjust_mask; - bool show_tdie; - u32 show_tccd; - u32 svi_addr[2]; - bool show_current; - int cfactor[2]; + u32 show_temp; + bool is_zen; + u32 ccd_offset; }; +#define TCTL_BIT 0 +#define TDIE_BIT 1 +#define TCCD_BIT(x) ((x) + 2) + +#define HAVE_TEMP(d, channel) ((d)->show_temp & BIT(channel)) +#define HAVE_TDIE(d) HAVE_TEMP(d, TDIE_BIT) + struct tctl_offset { u8 model; char const *id; @@ -118,16 +109,6 @@ static const struct tctl_offset tctl_offset_table[] = { { 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */ }; -static bool is_threadripper(void) -{ - return strstr(boot_cpu_data.x86_model_id, "Threadripper"); -} - -static bool is_epyc(void) -{ - return strstr(boot_cpu_data.x86_model_id, "EPYC"); -} - static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval) { pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval); @@ -161,10 +142,10 @@ static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval) F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval); } -static void read_tempreg_nb_f17(struct pci_dev *pdev, u32 *regval) +static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval) { amd_smn_read(amd_pci_dev_to_node_id(pdev), - F17H_M01H_REPORTED_TEMP_CTRL_OFFSET, regval); + ZEN_REPORTED_TEMP_CTRL_BASE, regval); } static long get_raw_temp(struct k10temp_data *data) @@ -173,15 +154,15 @@ static long get_raw_temp(struct k10temp_data *data) long temp; data->read_tempreg(data->pdev, ®val); - temp = (regval >> CUR_TEMP_SHIFT) * 125; + temp = (regval >> ZEN_CUR_TEMP_SHIFT) * 125; if (regval & data->temp_adjust_mask) temp -= 49000; return temp; } -const char *k10temp_temp_label[] = { - "Tdie", +static const char *k10temp_temp_label[] = { "Tctl", + "Tdie", "Tccd1", "Tccd2", "Tccd3", @@ -190,16 +171,10 @@ const char *k10temp_temp_label[] = { "Tccd6", "Tccd7", "Tccd8", -}; - -const char *k10temp_in_label[] = { - "Vcore", - "Vsoc", -}; - -const char *k10temp_curr_label[] = { - "Icore", - "Isoc", + "Tccd9", + "Tccd10", + "Tccd11", + "Tccd12", }; static int k10temp_read_labels(struct device *dev, @@ -210,50 +185,6 @@ static int k10temp_read_labels(struct device *dev, case hwmon_temp: *str = k10temp_temp_label[channel]; break; - case hwmon_in: - *str = k10temp_in_label[channel]; - break; - case hwmon_curr: - *str = k10temp_curr_label[channel]; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -static int k10temp_read_curr(struct device *dev, u32 attr, int channel, - long *val) -{ - struct k10temp_data *data = dev_get_drvdata(dev); - u32 regval; - - switch (attr) { - case hwmon_curr_input: - amd_smn_read(amd_pci_dev_to_node_id(data->pdev), - data->svi_addr[channel], ®val); - *val = DIV_ROUND_CLOSEST(data->cfactor[channel] * - (regval & 0xff), - 1000); - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - -static int k10temp_read_in(struct device *dev, u32 attr, int channel, long *val) -{ - struct k10temp_data *data = dev_get_drvdata(dev); - u32 regval; - - switch (attr) { - case hwmon_in_input: - amd_smn_read(amd_pci_dev_to_node_id(data->pdev), - data->svi_addr[channel], ®val); - regval = (regval >> 16) & 0xff; - *val = DIV_ROUND_CLOSEST(155000 - regval * 625, 100); - break; default: return -EOPNOTSUPP; } @@ -269,20 +200,21 @@ static int k10temp_read_temp(struct device *dev, u32 attr, int channel, switch (attr) { case hwmon_temp_input: switch (channel) { - case 0: /* Tdie */ - *val = get_raw_temp(data) - data->temp_offset; + case 0: /* Tctl */ + *val = get_raw_temp(data); if (*val < 0) *val = 0; break; - case 1: /* Tctl */ - *val = get_raw_temp(data); + case 1: /* Tdie */ + *val = get_raw_temp(data) - data->temp_offset; if (*val < 0) *val = 0; break; - case 2 ... 9: /* Tccd{1-8} */ + case 2 ... 13: /* Tccd{1-12} */ amd_smn_read(amd_pci_dev_to_node_id(data->pdev), - F17H_M70H_CCD_TEMP(channel - 2), ®val); - *val = (regval & F17H_M70H_CCD_TEMP_MASK) * 125 - 49000; + ZEN_CCD_TEMP(data->ccd_offset, channel - 2), + ®val); + *val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000; break; default: return -EOPNOTSUPP; @@ -312,10 +244,6 @@ static int k10temp_read(struct device *dev, enum hwmon_sensor_types type, switch (type) { case hwmon_temp: return k10temp_read_temp(dev, attr, channel, val); - case hwmon_in: - return k10temp_read_in(dev, attr, channel, val); - case hwmon_curr: - return k10temp_read_curr(dev, attr, channel, val); default: return -EOPNOTSUPP; } @@ -333,23 +261,11 @@ static umode_t k10temp_is_visible(const void *_data, case hwmon_temp: switch (attr) { case hwmon_temp_input: - switch (channel) { - case 0: /* Tdie, or Tctl if we don't show it */ - break; - case 1: /* Tctl */ - if (!data->show_tdie) - return 0; - break; - case 2 ... 9: /* Tccd{1-8} */ - if (!(data->show_tccd & BIT(channel - 2))) - return 0; - break; - default: + if (!HAVE_TEMP(data, channel)) return 0; - } break; case hwmon_temp_max: - if (channel || data->show_tdie) + if (channel || data->is_zen) return 0; break; case hwmon_temp_crit: @@ -368,30 +284,14 @@ static umode_t k10temp_is_visible(const void *_data, return 0; break; case hwmon_temp_label: - /* No labels if we don't show the die temperature */ - if (!data->show_tdie) - return 0; - switch (channel) { - case 0: /* Tdie */ - case 1: /* Tctl */ - break; - case 2 ... 9: /* Tccd{1-8} */ - if (!(data->show_tccd & BIT(channel - 2))) - return 0; - break; - default: + /* Show temperature labels only on Zen CPUs */ + if (!data->is_zen || !HAVE_TEMP(data, channel)) return 0; - } break; default: return 0; } break; - case hwmon_in: - case hwmon_curr: - if (!data->show_current) - return 0; - break; default: return 0; } @@ -432,76 +332,6 @@ static bool has_erratum_319(struct pci_dev *pdev) (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2); } -#ifdef CONFIG_DEBUG_FS - -static void k10temp_smn_regs_show(struct seq_file *s, struct pci_dev *pdev, - u32 addr, int count) -{ - u32 reg; - int i; - - for (i = 0; i < count; i++) { - if (!(i & 3)) - seq_printf(s, "0x%06x: ", addr + i * 4); - amd_smn_read(amd_pci_dev_to_node_id(pdev), addr + i * 4, ®); - seq_printf(s, "%08x ", reg); - if ((i & 3) == 3) - seq_puts(s, "\n"); - } -} - -static int svi_show(struct seq_file *s, void *unused) -{ - struct k10temp_data *data = s->private; - - k10temp_smn_regs_show(s, data->pdev, F17H_M01H_SVI, 32); - return 0; -} -DEFINE_SHOW_ATTRIBUTE(svi); - -static int thm_show(struct seq_file *s, void *unused) -{ - struct k10temp_data *data = s->private; - - k10temp_smn_regs_show(s, data->pdev, - F17H_M01H_REPORTED_TEMP_CTRL_OFFSET, 256); - return 0; -} -DEFINE_SHOW_ATTRIBUTE(thm); - -static void k10temp_debugfs_cleanup(void *ddir) -{ - debugfs_remove_recursive(ddir); -} - -static void k10temp_init_debugfs(struct k10temp_data *data) -{ - struct dentry *debugfs; - char name[32]; - - /* Only show debugfs data for Family 17h/18h CPUs */ - if (!data->show_tdie) - return; - - scnprintf(name, sizeof(name), "k10temp-%s", pci_name(data->pdev)); - - debugfs = debugfs_create_dir(name, NULL); - if (debugfs) { - debugfs_create_file("svi", 0444, debugfs, data, &svi_fops); - debugfs_create_file("thm", 0444, debugfs, data, &thm_fops); - devm_add_action_or_reset(&data->pdev->dev, - k10temp_debugfs_cleanup, debugfs); - } -} - -#else - -static void k10temp_init_debugfs(struct k10temp_data *data) -{ -} - -#endif - static const struct hwmon_channel_info *k10temp_info[] = { HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | @@ -515,13 +345,11 @@ static const struct hwmon_channel_info *k10temp_info[] = { HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL), - HWMON_CHANNEL_INFO(in, - HWMON_I_INPUT | HWMON_I_LABEL, - HWMON_I_INPUT | HWMON_I_LABEL), - HWMON_CHANNEL_INFO(curr, - HWMON_C_INPUT | HWMON_C_LABEL, - HWMON_C_INPUT | HWMON_C_LABEL), NULL }; @@ -544,9 +372,9 @@ static void k10temp_get_ccd_support(struct pci_dev *pdev, for (i = 0; i < limit; i++) { amd_smn_read(amd_pci_dev_to_node_id(pdev), - F17H_M70H_CCD_TEMP(i), ®val); - if (regval & F17H_M70H_CCD_TEMP_VALID) - data->show_tccd |= BIT(i); + ZEN_CCD_TEMP(data->ccd_offset, i), ®val); + if (regval & ZEN_CCD_TEMP_VALID) + data->show_temp |= BIT(TCCD_BIT(i)); } } @@ -573,6 +401,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; data->pdev = pdev; + data->show_temp |= BIT(TCTL_BIT); /* Always show Tctl */ if (boot_cpu_data.x86 == 0x15 && ((boot_cpu_data.x86_model & 0xf0) == 0x60 || @@ -580,32 +409,57 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) data->read_htcreg = read_htcreg_nb_f15; data->read_tempreg = read_tempreg_nb_f15; } else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { - data->temp_adjust_mask = CUR_TEMP_RANGE_SEL_MASK; - data->read_tempreg = read_tempreg_nb_f17; - data->show_tdie = true; + data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; + data->read_tempreg = read_tempreg_nb_zen; + data->is_zen = true; switch (boot_cpu_data.x86_model) { case 0x1: /* Zen */ case 0x8: /* Zen+ */ case 0x11: /* Zen APU */ case 0x18: /* Zen+ APU */ - data->show_current = !is_threadripper() && !is_epyc(); - data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE0; - data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE1; - data->cfactor[0] = CFACTOR_ICORE; - data->cfactor[1] = CFACTOR_ISOC; + data->ccd_offset = 0x154; k10temp_get_ccd_support(pdev, data, 4); break; case 0x31: /* Zen2 Threadripper */ + case 0x60: /* Renoir */ + case 0x68: /* Lucienne */ case 0x71: /* Zen2 */ - data->show_current = !is_threadripper() && !is_epyc(); - data->cfactor[0] = CFACTOR_ICORE; - data->cfactor[1] = CFACTOR_ISOC; - data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE1; - data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE0; + data->ccd_offset = 0x154; + k10temp_get_ccd_support(pdev, data, 8); + break; + case 0xa0 ... 0xaf: + data->ccd_offset = 0x300; k10temp_get_ccd_support(pdev, data, 8); break; } + } else if (boot_cpu_data.x86 == 0x19) { + data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; + data->read_tempreg = read_tempreg_nb_zen; + data->is_zen = true; + + switch (boot_cpu_data.x86_model) { + case 0x0 ... 0x1: /* Zen3 SP3/TR */ + case 0x21: /* Zen3 Ryzen Desktop */ + case 0x50 ... 0x5f: /* Green Sardine */ + data->ccd_offset = 0x154; + k10temp_get_ccd_support(pdev, data, 8); + break; + case 0x40 ... 0x4f: /* Yellow Carp */ + data->ccd_offset = 0x300; + k10temp_get_ccd_support(pdev, data, 8); + break; + case 0x60 ... 0x6f: + case 0x70 ... 0x7f: + data->ccd_offset = 0x308; + k10temp_get_ccd_support(pdev, data, 8); + break; + case 0x10 ... 0x1f: + case 0xa0 ... 0xaf: + data->ccd_offset = 0x300; + k10temp_get_ccd_support(pdev, data, 12); + break; + } } else { data->read_htcreg = read_htcreg_pci; data->read_tempreg = read_tempreg_pci; @@ -616,6 +470,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (boot_cpu_data.x86 == entry->model && strstr(boot_cpu_data.x86_model_id, entry->id)) { + data->show_temp |= BIT(TDIE_BIT); /* show Tdie */ data->temp_offset = entry->offset; break; } @@ -624,12 +479,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data, &k10temp_chip_info, NULL); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - k10temp_init_debugfs(data); - - return 0; + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct pci_device_id k10temp_id_table[] = { @@ -646,7 +496,15 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M60H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M70H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, {} }; diff --git a/drivers/hwmon/lan966x-hwmon.c b/drivers/hwmon/lan966x-hwmon.c new file mode 100644 index 000000000000..f41df053ac31 --- /dev/null +++ b/drivers/hwmon/lan966x-hwmon.c @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/hwmon.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/polynomial.h> +#include <linux/regmap.h> + +/* + * The original translation formulae of the temperature (in degrees of Celsius) + * are as follows: + * + * T = -3.4627e-11*(N^4) + 1.1023e-7*(N^3) + -1.9165e-4*(N^2) + + * 3.0604e-1*(N^1) + -5.6197e1 + * + * where [-56.197, 136.402]C and N = [0, 1023]. + * + * They must be accordingly altered to be suitable for the integer arithmetics. + * The technique is called 'factor redistribution', which just makes sure the + * multiplications and divisions are made so to have a result of the operations + * within the integer numbers limit. In addition we need to translate the + * formulae to accept millidegrees of Celsius. Here what it looks like after + * the alterations: + * + * T = -34627e-12*(N^4) + 110230e-9*(N^3) + -191650e-6*(N^2) + + * 306040e-3*(N^1) + -56197 + * + * where T = [-56197, 136402]mC and N = [0, 1023]. + */ + +static const struct polynomial poly_N_to_temp = { + .terms = { + {4, -34627, 1000, 1}, + {3, 110230, 1000, 1}, + {2, -191650, 1000, 1}, + {1, 306040, 1000, 1}, + {0, -56197, 1, 1} + } +}; + +#define PVT_SENSOR_CTRL 0x0 /* unused */ +#define PVT_SENSOR_CFG 0x4 +#define SENSOR_CFG_CLK_CFG GENMASK(27, 20) +#define SENSOR_CFG_TRIM_VAL GENMASK(13, 9) +#define SENSOR_CFG_SAMPLE_ENA BIT(8) +#define SENSOR_CFG_START_CAPTURE BIT(7) +#define SENSOR_CFG_CONTINIOUS_MODE BIT(6) +#define SENSOR_CFG_PSAMPLE_ENA GENMASK(1, 0) +#define PVT_SENSOR_STAT 0x8 +#define SENSOR_STAT_DATA_VALID BIT(10) +#define SENSOR_STAT_DATA GENMASK(9, 0) + +#define FAN_CFG 0x0 +#define FAN_CFG_DUTY_CYCLE GENMASK(23, 16) +#define INV_POL BIT(3) +#define GATE_ENA BIT(2) +#define PWM_OPEN_COL_ENA BIT(1) +#define FAN_STAT_CFG BIT(0) +#define FAN_PWM_FREQ 0x4 +#define FAN_PWM_CYC_10US GENMASK(25, 15) +#define FAN_PWM_FREQ_FREQ GENMASK(14, 0) +#define FAN_CNT 0xc +#define FAN_CNT_DATA GENMASK(15, 0) + +#define LAN966X_PVT_CLK 1200000 /* 1.2 MHz */ + +struct lan966x_hwmon { + struct regmap *regmap_pvt; + struct regmap *regmap_fan; + struct clk *clk; + unsigned long clk_rate; +}; + +static int lan966x_hwmon_read_temp(struct device *dev, long *val) +{ + struct lan966x_hwmon *hwmon = dev_get_drvdata(dev); + unsigned int data; + int ret; + + ret = regmap_read(hwmon->regmap_pvt, PVT_SENSOR_STAT, &data); + if (ret < 0) + return ret; + + if (!(data & SENSOR_STAT_DATA_VALID)) + return -ENODATA; + + *val = polynomial_calc(&poly_N_to_temp, + FIELD_GET(SENSOR_STAT_DATA, data)); + + return 0; +} + +static int lan966x_hwmon_read_fan(struct device *dev, long *val) +{ + struct lan966x_hwmon *hwmon = dev_get_drvdata(dev); + unsigned int data; + int ret; + + ret = regmap_read(hwmon->regmap_fan, FAN_CNT, &data); + if (ret < 0) + return ret; + + /* + * Data is given in pulses per second. Assume two pulses + * per revolution. + */ + *val = FIELD_GET(FAN_CNT_DATA, data) * 60 / 2; + + return 0; +} + +static int lan966x_hwmon_read_pwm(struct device *dev, long *val) +{ + struct lan966x_hwmon *hwmon = dev_get_drvdata(dev); + unsigned int data; + int ret; + + ret = regmap_read(hwmon->regmap_fan, FAN_CFG, &data); + if (ret < 0) + return ret; + + *val = FIELD_GET(FAN_CFG_DUTY_CYCLE, data); + + return 0; +} + +static int lan966x_hwmon_read_pwm_freq(struct device *dev, long *val) +{ + struct lan966x_hwmon *hwmon = dev_get_drvdata(dev); + unsigned long tmp; + unsigned int data; + int ret; + + ret = regmap_read(hwmon->regmap_fan, FAN_PWM_FREQ, &data); + if (ret < 0) + return ret; + + /* + * Datasheet says it is sys_clk / 256 / pwm_freq. But in reality + * it is sys_clk / 256 / (pwm_freq + 1). + */ + data = FIELD_GET(FAN_PWM_FREQ_FREQ, data) + 1; + tmp = DIV_ROUND_CLOSEST(hwmon->clk_rate, 256); + *val = DIV_ROUND_CLOSEST(tmp, data); + + return 0; +} + +static int lan966x_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_temp: + return lan966x_hwmon_read_temp(dev, val); + case hwmon_fan: + return lan966x_hwmon_read_fan(dev, val); + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + return lan966x_hwmon_read_pwm(dev, val); + case hwmon_pwm_freq: + return lan966x_hwmon_read_pwm_freq(dev, val); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int lan966x_hwmon_write_pwm(struct device *dev, long val) +{ + struct lan966x_hwmon *hwmon = dev_get_drvdata(dev); + + if (val < 0 || val > 255) + return -EINVAL; + + return regmap_update_bits(hwmon->regmap_fan, FAN_CFG, + FAN_CFG_DUTY_CYCLE, + FIELD_PREP(FAN_CFG_DUTY_CYCLE, val)); +} + +static int lan966x_hwmon_write_pwm_freq(struct device *dev, long val) +{ + struct lan966x_hwmon *hwmon = dev_get_drvdata(dev); + + if (val <= 0) + return -EINVAL; + + val = DIV_ROUND_CLOSEST(hwmon->clk_rate, val); + val = DIV_ROUND_CLOSEST(val, 256) - 1; + val = clamp_val(val, 0, FAN_PWM_FREQ_FREQ); + + return regmap_update_bits(hwmon->regmap_fan, FAN_PWM_FREQ, + FAN_PWM_FREQ_FREQ, + FIELD_PREP(FAN_PWM_FREQ_FREQ, val)); +} + +static int lan966x_hwmon_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_input: + return lan966x_hwmon_write_pwm(dev, val); + case hwmon_pwm_freq: + return lan966x_hwmon_write_pwm_freq(dev, val); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static umode_t lan966x_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + umode_t mode = 0; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + mode = 0444; + break; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + mode = 0444; + break; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_freq: + mode = 0644; + break; + default: + break; + } + break; + default: + break; + } + + return mode; +} + +static const struct hwmon_channel_info *lan966x_hwmon_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_FREQ), + NULL +}; + +static const struct hwmon_ops lan966x_hwmon_ops = { + .is_visible = lan966x_hwmon_is_visible, + .read = lan966x_hwmon_read, + .write = lan966x_hwmon_write, +}; + +static const struct hwmon_chip_info lan966x_hwmon_chip_info = { + .ops = &lan966x_hwmon_ops, + .info = lan966x_hwmon_info, +}; + +static void lan966x_hwmon_disable(void *data) +{ + struct lan966x_hwmon *hwmon = data; + + regmap_update_bits(hwmon->regmap_pvt, PVT_SENSOR_CFG, + SENSOR_CFG_SAMPLE_ENA | SENSOR_CFG_CONTINIOUS_MODE, + 0); +} + +static int lan966x_hwmon_enable(struct device *dev, + struct lan966x_hwmon *hwmon) +{ + unsigned int mask = SENSOR_CFG_CLK_CFG | + SENSOR_CFG_SAMPLE_ENA | + SENSOR_CFG_START_CAPTURE | + SENSOR_CFG_CONTINIOUS_MODE | + SENSOR_CFG_PSAMPLE_ENA; + unsigned int val; + unsigned int div; + int ret; + + /* enable continuous mode */ + val = SENSOR_CFG_SAMPLE_ENA | SENSOR_CFG_CONTINIOUS_MODE; + + /* set PVT clock to be between 1.15 and 1.25 MHz */ + div = DIV_ROUND_CLOSEST(hwmon->clk_rate, LAN966X_PVT_CLK); + val |= FIELD_PREP(SENSOR_CFG_CLK_CFG, div); + + ret = regmap_update_bits(hwmon->regmap_pvt, PVT_SENSOR_CFG, + mask, val); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, lan966x_hwmon_disable, hwmon); +} + +static struct regmap *lan966x_init_regmap(struct platform_device *pdev, + const char *name) +{ + struct regmap_config regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + }; + void __iomem *base; + + base = devm_platform_ioremap_resource_byname(pdev, name); + if (IS_ERR(base)) + return ERR_CAST(base); + + regmap_config.name = name; + + return devm_regmap_init_mmio(&pdev->dev, base, ®map_config); +} + +static void lan966x_clk_disable(void *data) +{ + struct lan966x_hwmon *hwmon = data; + + clk_disable_unprepare(hwmon->clk); +} + +static int lan966x_clk_enable(struct device *dev, struct lan966x_hwmon *hwmon) +{ + int ret; + + ret = clk_prepare_enable(hwmon->clk); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, lan966x_clk_disable, hwmon); +} + +static int lan966x_hwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lan966x_hwmon *hwmon; + struct device *hwmon_dev; + int ret; + + hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->clk = devm_clk_get(dev, NULL); + if (IS_ERR(hwmon->clk)) + return dev_err_probe(dev, PTR_ERR(hwmon->clk), + "failed to get clock\n"); + + ret = lan966x_clk_enable(dev, hwmon); + if (ret) + return dev_err_probe(dev, ret, "failed to enable clock\n"); + + hwmon->clk_rate = clk_get_rate(hwmon->clk); + + hwmon->regmap_pvt = lan966x_init_regmap(pdev, "pvt"); + if (IS_ERR(hwmon->regmap_pvt)) + return dev_err_probe(dev, PTR_ERR(hwmon->regmap_pvt), + "failed to get regmap for PVT registers\n"); + + hwmon->regmap_fan = lan966x_init_regmap(pdev, "fan"); + if (IS_ERR(hwmon->regmap_fan)) + return dev_err_probe(dev, PTR_ERR(hwmon->regmap_fan), + "failed to get regmap for fan registers\n"); + + ret = lan966x_hwmon_enable(dev, hwmon); + if (ret) + return dev_err_probe(dev, ret, "failed to enable sensor\n"); + + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "lan966x_hwmon", hwmon, + &lan966x_hwmon_chip_info, NULL); + if (IS_ERR(hwmon_dev)) + return dev_err_probe(dev, PTR_ERR(hwmon_dev), + "failed to register hwmon device\n"); + + return 0; +} + +static const struct of_device_id lan966x_hwmon_of_match[] = { + { .compatible = "microchip,lan9668-hwmon" }, + {} +}; +MODULE_DEVICE_TABLE(of, lan966x_hwmon_of_match); + +static struct platform_driver lan966x_hwmon_driver = { + .probe = lan966x_hwmon_probe, + .driver = { + .name = "lan966x-hwmon", + .of_match_table = lan966x_hwmon_of_match, + }, +}; +module_platform_driver(lan966x_hwmon_driver); + +MODULE_DESCRIPTION("LAN966x Hardware Monitoring Driver"); +MODULE_AUTHOR("Michael Walle <michael@walle.cc>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c index ce5b0598524c..ef5a49cd9149 100644 --- a/drivers/hwmon/lineage-pem.c +++ b/drivers/hwmon/lineage-pem.c @@ -191,7 +191,7 @@ static struct pem_data *pem_update_device(struct device *dev) i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } abort: mutex_unlock(&data->update_lock); @@ -280,7 +280,7 @@ static ssize_t pem_bool_show(struct device *dev, struct device_attribute *da, return PTR_ERR(data); status = data->data_string[attr->nr] & attr->index; - return snprintf(buf, PAGE_SIZE, "%d\n", !!status); + return sysfs_emit(buf, "%d\n", !!status); } static ssize_t pem_data_show(struct device *dev, struct device_attribute *da, @@ -296,7 +296,7 @@ static ssize_t pem_data_show(struct device *dev, struct device_attribute *da, value = pem_get_data(data->data_string, sizeof(data->data_string), attr->index); - return snprintf(buf, PAGE_SIZE, "%ld\n", value); + return sysfs_emit(buf, "%ld\n", value); } static ssize_t pem_input_show(struct device *dev, struct device_attribute *da, @@ -312,7 +312,7 @@ static ssize_t pem_input_show(struct device *dev, struct device_attribute *da, value = pem_get_input(data->input_string, sizeof(data->input_string), attr->index); - return snprintf(buf, PAGE_SIZE, "%ld\n", value); + return sysfs_emit(buf, "%ld\n", value); } static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da, @@ -328,7 +328,7 @@ static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da, value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed), attr->index); - return snprintf(buf, PAGE_SIZE, "%ld\n", value); + return sysfs_emit(buf, "%ld\n", value); } /* Voltages */ @@ -417,8 +417,7 @@ static const struct attribute_group pem_fan_group = { .attrs = pem_fan_attributes, }; -static int pem_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pem_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; @@ -512,7 +511,7 @@ static struct i2c_driver pem_driver = { .driver = { .name = "lineage_pem", }, - .probe = pem_probe, + .probe_new = pem_probe, .id_table = pem_id, }; diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 60a817f58db9..9ab2cab4c710 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -139,7 +139,7 @@ struct lm63_data { struct i2c_client *client; struct mutex update_lock; const struct attribute_group *groups[5]; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ char lut_valid; /* zero until lut fields are valid */ unsigned long last_updated; /* in jiffies */ unsigned long lut_last_updated; /* in jiffies */ @@ -289,7 +289,7 @@ static struct lm63_data *lm63_update_device(struct device *dev) LM63_REG_ALERT_STATUS) & 0x7F; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } lm63_update_lut(data); @@ -714,7 +714,7 @@ static ssize_t temp2_type_store(struct device *dev, reg = i2c_smbus_read_byte_data(client, LM96163_REG_TRUTHERM) & ~0x02; i2c_smbus_write_byte_data(client, LM96163_REG_TRUTHERM, reg | (data->trutherm ? 0x02 : 0x00)); - data->valid = 0; + data->valid = false; mutex_unlock(&data->update_lock); return count; @@ -931,7 +931,7 @@ static const struct attribute_group lm63_group_extra_lut = { static umode_t lm63_attribute_mode(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct lm63_data *data = dev_get_drvdata(dev); if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr @@ -996,11 +996,11 @@ static int lm63_detect(struct i2c_client *client, } if (chip_id == 0x41 && address == 0x4c) - strlcpy(info->type, "lm63", I2C_NAME_SIZE); + strscpy(info->type, "lm63", I2C_NAME_SIZE); else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e)) - strlcpy(info->type, "lm64", I2C_NAME_SIZE); + strscpy(info->type, "lm64", I2C_NAME_SIZE); else if (chip_id == 0x49 && address == 0x4c) - strlcpy(info->type, "lm96163", I2C_NAME_SIZE); + strscpy(info->type, "lm96163", I2C_NAME_SIZE); else return -ENODEV; @@ -1087,8 +1087,9 @@ static void lm63_init_client(struct lm63_data *data) (data->config_fan & 0x20) ? "manual" : "auto"); } -static int lm63_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id lm63_id[]; + +static int lm63_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -1106,7 +1107,7 @@ static int lm63_probe(struct i2c_client *client, if (client->dev.of_node) data->kind = (enum chips)of_device_get_match_data(&client->dev); else - data->kind = id->driver_data; + data->kind = i2c_match_id(lm63_id, client)->driver_data; if (data->kind == lm64) data->temp2_offset = 16000; @@ -1163,7 +1164,7 @@ static struct i2c_driver lm63_driver = { .name = "lm63", .of_match_table = of_match_ptr(lm63_of_match), }, - .probe = lm63_probe, + .probe_new = lm63_probe, .id_table = lm63_id, .detect = lm63_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 4122e59f0bb4..c20a749fc7f2 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -22,10 +22,10 @@ #include <linux/hwmon.h> #include <linux/mutex.h> #include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/property.h> #include <linux/spi/spi.h> #include <linux/slab.h> -#include <linux/of_device.h> - #define DRVNAME "lm70" @@ -34,6 +34,7 @@ #define LM70_CHIP_LM71 2 /* NS LM71 */ #define LM70_CHIP_LM74 3 /* NS LM74 */ #define LM70_CHIP_TMP122 4 /* TI TMP122/TMP124 */ +#define LM70_CHIP_TMP125 5 /* TI TMP125 */ struct lm70 { struct spi_device *spi; @@ -87,6 +88,12 @@ static ssize_t temp1_input_show(struct device *dev, * LM71: * 14 bits of 2's complement data, discard LSB 2 bits, * resolution 0.0312 degrees celsius. + * + * TMP125: + * MSB/D15 is a leading zero. D14 is the sign-bit. This is + * followed by 9 temperature bits (D13..D5) in 2's complement + * data format with a resolution of 0.25 degrees celsius per unit. + * LSB 5 bits (D4..D0) share the same value as D5 and get discarded. */ switch (p_lm70->chip) { case LM70_CHIP_LM70: @@ -102,6 +109,10 @@ static ssize_t temp1_input_show(struct device *dev, case LM70_CHIP_LM71: val = ((int)raw / 4) * 3125 / 100; break; + + case LM70_CHIP_TMP125: + val = (sign_extend32(raw, 14) / 32) * 250; + break; } status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ @@ -136,6 +147,10 @@ static const struct of_device_id lm70_of_ids[] = { .data = (void *) LM70_CHIP_TMP122, }, { + .compatible = "ti,tmp125", + .data = (void *) LM70_CHIP_TMP125, + }, + { .compatible = "ti,lm71", .data = (void *) LM70_CHIP_LM71, }, @@ -150,19 +165,18 @@ MODULE_DEVICE_TABLE(of, lm70_of_ids); static int lm70_probe(struct spi_device *spi) { - const struct of_device_id *match; struct device *hwmon_dev; struct lm70 *p_lm70; int chip; - match = of_match_device(lm70_of_ids, &spi->dev); - if (match) - chip = (int)(uintptr_t)match->data; + if (dev_fwnode(&spi->dev)) + chip = (int)(uintptr_t)device_get_match_data(&spi->dev); else chip = spi_get_device_id(spi)->driver_data; + /* signaling is SPI_MODE_0 */ - if (spi->mode & (SPI_CPOL | SPI_CPHA)) + if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) return -EINVAL; /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ @@ -185,6 +199,7 @@ static const struct spi_device_id lm70_ids[] = { { "lm70", LM70_CHIP_LM70 }, { "tmp121", LM70_CHIP_TMP121 }, { "tmp122", LM70_CHIP_TMP122 }, + { "tmp125", LM70_CHIP_TMP125 }, { "lm71", LM70_CHIP_LM71 }, { "lm74", LM70_CHIP_LM74 }, { }, diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 1eeb9d7de2a0..1346b3b3f463 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -190,7 +190,7 @@ ATTRIBUTE_GROUPS(lm73); /* device probe and removal */ static int -lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) +lm73_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -257,17 +257,27 @@ static int lm73_detect(struct i2c_client *new_client, if (id < 0 || id != LM73_ID) return -ENODEV; - strlcpy(info->type, "lm73", I2C_NAME_SIZE); + strscpy(info->type, "lm73", I2C_NAME_SIZE); return 0; } +static const struct of_device_id lm73_of_match[] = { + { + .compatible = "ti,lm73", + }, + { }, +}; + +MODULE_DEVICE_TABLE(of, lm73_of_match); + static struct i2c_driver lm73_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm73", + .of_match_table = lm73_of_match, }, - .probe = lm73_probe, + .probe_new = lm73_probe, .id_table = lm73_ids, .detect = lm73_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 5e6392294c03..bcc3adcb3af1 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -17,6 +17,7 @@ #include <linux/of.h> #include <linux/regmap.h> #include <linux/util_macros.h> +#include <linux/regulator/consumer.h> #include "lm75.h" /* @@ -25,6 +26,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ adt75, + at30ts74, ds1775, ds75, ds7505, @@ -49,6 +51,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ tmp75, tmp75b, tmp75c, + tmp1075, }; /** @@ -101,6 +104,7 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, struct lm75_data { struct i2c_client *client; struct regmap *regmap; + struct regulator *vs; u8 orig_conf; u8 current_conf; u8 resolution; /* In bits, 9 to 16 */ @@ -125,6 +129,14 @@ static const struct lm75_params device_params[] = { .default_resolution = 12, .default_sample_time = MSEC_PER_SEC / 10, }, + [at30ts74] = { + .set_mask = 3 << 5, /* 12-bit mode*/ + .default_resolution = 12, + .default_sample_time = 200, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 25, 50, 100, 200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, [ds1775] = { .clr_mask = 3 << 5, .set_mask = 2 << 5, /* 11-bit mode */ @@ -291,6 +303,13 @@ static const struct lm75_params device_params[] = { .clr_mask = 1 << 5, /*not one-shot mode*/ .default_resolution = 12, .default_sample_time = MSEC_PER_SEC / 12, + }, + [tmp1075] = { /* not one-shot mode, 27.5 ms sample rate */ + .clr_mask = 1 << 5 | 1 << 6 | 1 << 7, + .default_resolution = 12, + .default_sample_time = 28, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, } }; @@ -534,6 +553,13 @@ static const struct regmap_config lm75_regmap_config = { .use_single_write = true, }; +static void lm75_disable_regulator(void *data) +{ + struct lm75_data *lm75 = data; + + regulator_disable(lm75->vs); +} + static void lm75_remove(void *data) { struct lm75_data *lm75 = data; @@ -542,8 +568,9 @@ static void lm75_remove(void *data) i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf); } -static int -lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) +static const struct i2c_device_id lm75_ids[]; + +static int lm75_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -554,7 +581,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) if (client->dev.of_node) kind = (enum lm75_type)of_device_get_match_data(&client->dev); else - kind = id->driver_data; + kind = i2c_match_id(lm75_ids, client)->driver_data; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -567,6 +594,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->client = client; data->kind = kind; + data->vs = devm_regulator_get(dev, "vs"); + if (IS_ERR(data->vs)) + return PTR_ERR(data->vs); + data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config); if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); @@ -581,6 +612,17 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->sample_time = data->params->default_sample_time; data->resolution = data->params->default_resolution; + /* Enable the power */ + err = regulator_enable(data->vs); + if (err) { + dev_err(dev, "failed to enable regulator: %d\n", err); + return err; + } + + err = devm_add_action_or_reset(dev, lm75_disable_regulator, data); + if (err) + return err; + /* Cache original configuration */ status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); if (status < 0) { @@ -612,6 +654,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) static const struct i2c_device_id lm75_ids[] = { { "adt75", adt75, }, + { "at30ts74", at30ts74, }, { "ds1775", ds1775, }, { "ds75", ds75, }, { "ds7505", ds7505, }, @@ -637,6 +680,7 @@ static const struct i2c_device_id lm75_ids[] = { { "tmp75", tmp75, }, { "tmp75b", tmp75b, }, { "tmp75c", tmp75c, }, + { "tmp1075", tmp1075, }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm75_ids); @@ -647,6 +691,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = { .data = (void *)adt75 }, { + .compatible = "atmel,at30ts74", + .data = (void *)at30ts74 + }, + { .compatible = "dallas,ds1775", .data = (void *)ds1775 }, @@ -746,6 +794,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = { .compatible = "ti,tmp75c", .data = (void *)tmp75c }, + { + .compatible = "ti,tmp1075", + .data = (void *)tmp1075 + }, { }, }; MODULE_DEVICE_TABLE(of, lm75_of_match); @@ -797,8 +849,10 @@ static int lm75_detect(struct i2c_client *new_client, /* First check for LM75A */ if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) { - /* LM75A returns 0xff on unused registers so - just to be sure we check for that too. */ + /* + * LM75A returns 0xff on unused registers so + * just to be sure we check for that too. + */ if (i2c_smbus_read_byte_data(new_client, 4) != 0xff || i2c_smbus_read_byte_data(new_client, 5) != 0xff || i2c_smbus_read_byte_data(new_client, 6) != 0xff) @@ -839,7 +893,7 @@ static int lm75_detect(struct i2c_client *new_client, return -ENODEV; } - strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); + strscpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); return 0; } @@ -849,6 +903,7 @@ static int lm75_suspend(struct device *dev) { int status; struct i2c_client *client = to_i2c_client(dev); + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); if (status < 0) { dev_dbg(&client->dev, "Can't read config? %d\n", status); @@ -863,6 +918,7 @@ static int lm75_resume(struct device *dev) { int status; struct i2c_client *client = to_i2c_client(dev); + status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); if (status < 0) { dev_dbg(&client->dev, "Can't read config? %d\n", status); @@ -889,7 +945,7 @@ static struct i2c_driver lm75_driver = { .of_match_table = of_match_ptr(lm75_of_match), .pm = LM75_DEV_PM_OPS, }, - .probe = lm75_probe, + .probe_new = lm75_probe, .id_table = lm75_ids, .detect = lm75_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h index b614e6328566..b803ada5e3c9 100644 --- a/drivers/hwmon/lm75.h +++ b/drivers/hwmon/lm75.h @@ -1,37 +1,41 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - lm75.h - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com> - -*/ + * lm75.h - Part of lm_sensors, Linux kernel modules for hardware monitoring + * Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com> + */ /* - This file contains common code for encoding/decoding LM75 type - temperature readings, which are emulated by many of the chips - we support. As the user is unlikely to load more than one driver - which contains this code, we don't worry about the wasted space. -*/ + * This file contains common code for encoding/decoding LM75 type + * temperature readings, which are emulated by many of the chips + * we support. As the user is unlikely to load more than one driver + * which contains this code, we don't worry about the wasted space. + */ -#include <linux/kernel.h> +#include <linux/minmax.h> +#include <linux/types.h> /* straight from the datasheet */ #define LM75_TEMP_MIN (-55000) #define LM75_TEMP_MAX 125000 #define LM75_SHUTDOWN 0x01 -/* TEMP: 0.001C/bit (-55C to +125C) - REG: (0.5C/bit, two's complement) << 7 */ +/* + * TEMP: 0.001C/bit (-55C to +125C) + * REG: (0.5C/bit, two's complement) << 7 + */ static inline u16 LM75_TEMP_TO_REG(long temp) { int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + ntemp += (ntemp < 0 ? -250 : 250); return (u16)((ntemp / 500) << 7); } static inline int LM75_TEMP_FROM_REG(u16 reg) { - /* use integer division instead of equivalent right shift to - guarantee arithmetic shift and preserve the sign */ + /* + * use integer division instead of equivalent right shift to + * guarantee arithmetic shift and preserve the sign + */ return ((s16)reg / 128) * 500; } diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index 671a962fde29..645cb2191abe 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -55,7 +55,7 @@ static const u8 temp_regs[t_num_temp] = { struct lm77_data { struct i2c_client *client; struct mutex update_lock; - char valid; + bool valid; unsigned long last_updated; /* In jiffies */ int temp[t_num_temp]; /* index using temp_index */ u8 alarms; @@ -118,7 +118,7 @@ static struct lm77_data *lm77_update_device(struct device *dev) data->alarms = lm77_read_value(client, LM77_REG_TEMP) & 0x0007; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -302,7 +302,7 @@ static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info) || i2c_smbus_read_word_data(client, 7) != min) return -ENODEV; - strlcpy(info->type, "lm77", I2C_NAME_SIZE); + strscpy(info->type, "lm77", I2C_NAME_SIZE); return 0; } @@ -315,7 +315,7 @@ static void lm77_init_client(struct i2c_client *client) lm77_write_value(client, LM77_REG_CONF, conf & 0xfe); } -static int lm77_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int lm77_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -348,7 +348,7 @@ static struct i2c_driver lm77_driver = { .driver = { .name = "lm77", }, - .probe = lm77_probe, + .probe_new = lm77_probe, .id_table = lm77_id, .detect = lm77_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 2119461ec43a..694e171cab7f 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -117,7 +117,7 @@ struct lm78_data { int isa_addr; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[7]; /* Register value */ @@ -617,7 +617,7 @@ static int lm78_i2c_detect(struct i2c_client *client, if (isa) mutex_unlock(&isa->update_lock); - strlcpy(info->type, client_name, I2C_NAME_SIZE); + strscpy(info->type, client_name, I2C_NAME_SIZE); return 0; @@ -627,8 +627,9 @@ static int lm78_i2c_detect(struct i2c_client *client, return -ENODEV; } -static int lm78_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id lm78_i2c_id[]; + +static int lm78_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -639,7 +640,7 @@ static int lm78_i2c_probe(struct i2c_client *client, return -ENOMEM; data->client = client; - data->type = id->driver_data; + data->type = i2c_match_id(lm78_i2c_id, client)->driver_data; /* Initialize the LM78 chip */ lm78_init_device(data); @@ -661,7 +662,7 @@ static struct i2c_driver lm78_driver = { .driver = { .name = "lm78", }, - .probe = lm78_i2c_probe, + .probe_new = lm78_i2c_probe, .id_table = lm78_i2c_id, .detect = lm78_i2c_detect, .address_list = normal_i2c, @@ -771,7 +772,7 @@ static struct lm78_data *lm78_update_device(struct device *dev) data->alarms = lm78_read_value(data, LM78_REG_ALARM1) + (lm78_read_value(data, LM78_REG_ALARM2) << 8); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; data->fan_div[2] = 1; } diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 80520cef7617..35db0b97f912 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -117,7 +117,7 @@ struct lm80_data { struct i2c_client *client; struct mutex update_lock; char error; /* !=0 if error occurred during last update */ - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[i_num_in][7]; /* Register value, 1st index is enum in_index */ @@ -236,14 +236,14 @@ static struct lm80_data *lm80_update_device(struct device *dev) data->alarms = prev_rv + (rv << 8); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; data->error = 0; } goto done; abort: ret = ERR_PTR(rv); - data->valid = 0; + data->valid = false; data->error = 1; done: @@ -586,18 +586,16 @@ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info) name = "lm80"; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } -static int lm80_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lm80_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; struct lm80_data *data; - int rv; data = devm_kzalloc(dev, sizeof(struct lm80_data), GFP_KERNEL); if (!data) @@ -610,14 +608,8 @@ static int lm80_probe(struct i2c_client *client, lm80_init_client(client); /* A few vars need to be filled upon startup */ - rv = lm80_read_value(client, LM80_REG_FAN_MIN(1)); - if (rv < 0) - return rv; - data->fan[f_min][0] = rv; - rv = lm80_read_value(client, LM80_REG_FAN_MIN(2)); - if (rv < 0) - return rv; - data->fan[f_min][1] = rv; + data->fan[f_min][0] = lm80_read_value(client, LM80_REG_FAN_MIN(1)); + data->fan[f_min][1] = lm80_read_value(client, LM80_REG_FAN_MIN(2)); hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, lm80_groups); @@ -641,7 +633,7 @@ static struct i2c_driver lm80_driver = { .driver = { .name = "lm80", }, - .probe = lm80_probe, + .probe_new = lm80_probe, .id_table = lm80_id, .detect = lm80_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index 8fefca9bbbb7..616449f2cc50 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -18,16 +18,14 @@ * http://www.national.com/pf/LM/LM82.html */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/jiffies.h> +#include <linux/bits.h> +#include <linux/err.h> #include <linux/i2c.h> -#include <linux/hwmon-sysfs.h> +#include <linux/init.h> #include <linux/hwmon.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/sysfs.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> /* * Addresses to scan @@ -66,35 +64,35 @@ enum chips { lm83, lm82 }; #define LM83_REG_R_TCRIT 0x42 #define LM83_REG_W_TCRIT 0x5A -/* - * Conversions and various macros - * The LM83 uses signed 8-bit values with LSB = 1 degree Celsius. - */ - -#define TEMP_FROM_REG(val) ((val) * 1000) -#define TEMP_TO_REG(val) ((val) <= -128000 ? -128 : \ - (val) >= 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) - -static const u8 LM83_REG_R_TEMP[] = { +static const u8 LM83_REG_TEMP[] = { LM83_REG_R_LOCAL_TEMP, LM83_REG_R_REMOTE1_TEMP, LM83_REG_R_REMOTE2_TEMP, LM83_REG_R_REMOTE3_TEMP, +}; + +static const u8 LM83_REG_MAX[] = { LM83_REG_R_LOCAL_HIGH, LM83_REG_R_REMOTE1_HIGH, LM83_REG_R_REMOTE2_HIGH, LM83_REG_R_REMOTE3_HIGH, - LM83_REG_R_TCRIT, }; -static const u8 LM83_REG_W_HIGH[] = { - LM83_REG_W_LOCAL_HIGH, - LM83_REG_W_REMOTE1_HIGH, - LM83_REG_W_REMOTE2_HIGH, - LM83_REG_W_REMOTE3_HIGH, - LM83_REG_W_TCRIT, +/* alarm and fault registers and bits, indexed by channel */ +static const u8 LM83_ALARM_REG[] = { + LM83_REG_R_STATUS1, LM83_REG_R_STATUS2, LM83_REG_R_STATUS1, LM83_REG_R_STATUS2 +}; + +static const u8 LM83_MAX_ALARM_BIT[] = { + BIT(6), BIT(7), BIT(4), BIT(4) +}; + +static const u8 LM83_CRIT_ALARM_BIT[] = { + BIT(0), BIT(0), BIT(1), BIT(1) +}; + +static const u8 LM83_FAULT_BIT[] = { + 0, BIT(5), BIT(2), BIT(2) }; /* @@ -102,180 +100,274 @@ static const u8 LM83_REG_W_HIGH[] = { */ struct lm83_data { - struct i2c_client *client; - const struct attribute_group *groups[3]; - struct mutex update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - s8 temp[9]; /* 0..3: input 1-4, - 4..7: high limit 1-4, - 8 : critical limit */ - u16 alarms; /* bitvector, combined */ + struct regmap *regmap; + enum chips type; }; -static struct lm83_data *lm83_update_device(struct device *dev) +/* regmap code */ + +static int lm83_regmap_reg_read(void *context, unsigned int reg, unsigned int *val) { - struct lm83_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; + struct i2c_client *client = context; + int ret; - mutex_lock(&data->update_lock); + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return ret; - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - int nr; + *val = ret; + return 0; +} - dev_dbg(&client->dev, "Updating lm83 data.\n"); - for (nr = 0; nr < 9; nr++) { - data->temp[nr] = - i2c_smbus_read_byte_data(client, - LM83_REG_R_TEMP[nr]); - } - data->alarms = - i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) - + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) - << 8); +/* + * The regmap write function maps read register addresses to write register + * addresses. This is necessary for regmap register caching to work. + * An alternative would be to clear the regmap cache whenever a register is + * written, but that would be much more expensive. + */ +static int lm83_regmap_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; - data->last_updated = jiffies; - data->valid = 1; + switch (reg) { + case LM83_REG_R_CONFIG: + case LM83_REG_R_LOCAL_HIGH: + case LM83_REG_R_REMOTE2_HIGH: + reg += 0x06; + break; + case LM83_REG_R_REMOTE1_HIGH: + case LM83_REG_R_REMOTE3_HIGH: + case LM83_REG_R_TCRIT: + reg += 0x18; + break; + default: + break; } - mutex_unlock(&data->update_lock); + return i2c_smbus_write_byte_data(client, reg, val); +} - return data; +static bool lm83_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LM83_REG_R_LOCAL_TEMP: + case LM83_REG_R_REMOTE1_TEMP: + case LM83_REG_R_REMOTE2_TEMP: + case LM83_REG_R_REMOTE3_TEMP: + case LM83_REG_R_STATUS1: + case LM83_REG_R_STATUS2: + return true; + default: + return false; + } } -/* - * Sysfs stuff - */ +static const struct regmap_config lm83_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = lm83_regmap_is_volatile, + .reg_read = lm83_regmap_reg_read, + .reg_write = lm83_regmap_reg_write, +}; -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) +/* hwmon API */ + +static int lm83_temp_read(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm83_data *data = lm83_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); + struct lm83_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_temp_input: + err = regmap_read(data->regmap, LM83_REG_TEMP[channel], ®val); + if (err < 0) + return err; + *val = (s8)regval * 1000; + break; + case hwmon_temp_max: + err = regmap_read(data->regmap, LM83_REG_MAX[channel], ®val); + if (err < 0) + return err; + *val = (s8)regval * 1000; + break; + case hwmon_temp_crit: + err = regmap_read(data->regmap, LM83_REG_R_TCRIT, ®val); + if (err < 0) + return err; + *val = (s8)regval * 1000; + break; + case hwmon_temp_max_alarm: + err = regmap_read(data->regmap, LM83_ALARM_REG[channel], ®val); + if (err < 0) + return err; + *val = !!(regval & LM83_MAX_ALARM_BIT[channel]); + break; + case hwmon_temp_crit_alarm: + err = regmap_read(data->regmap, LM83_ALARM_REG[channel], ®val); + if (err < 0) + return err; + *val = !!(regval & LM83_CRIT_ALARM_BIT[channel]); + break; + case hwmon_temp_fault: + err = regmap_read(data->regmap, LM83_ALARM_REG[channel], ®val); + if (err < 0) + return err; + *val = !!(regval & LM83_FAULT_BIT[channel]); + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static ssize_t temp_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) +static int lm83_temp_write(struct device *dev, u32 attr, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm83_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long val; - int nr = attr->index; + unsigned int regval; int err; - err = kstrtol(buf, 10, &val); - if (err < 0) - return err; + regval = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); - mutex_lock(&data->update_lock); - data->temp[nr] = TEMP_TO_REG(val); - i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr - 4], - data->temp[nr]); - mutex_unlock(&data->update_lock); - return count; + switch (attr) { + case hwmon_temp_max: + err = regmap_write(data->regmap, LM83_REG_MAX[channel], regval); + if (err < 0) + return err; + break; + case hwmon_temp_crit: + err = regmap_write(data->regmap, LM83_REG_R_TCRIT, regval); + if (err < 0) + return err; + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static ssize_t alarms_show(struct device *dev, struct device_attribute *dummy, - char *buf) +static int lm83_chip_read(struct device *dev, u32 attr, int channel, long *val) { - struct lm83_data *data = lm83_update_device(dev); - return sprintf(buf, "%d\n", data->alarms); + struct lm83_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_chip_alarms: + err = regmap_read(data->regmap, LM83_REG_R_STATUS1, ®val); + if (err < 0) + return err; + *val = regval; + err = regmap_read(data->regmap, LM83_REG_R_STATUS2, ®val); + if (err < 0) + return err; + *val |= regval << 8; + return 0; + default: + return -EOPNOTSUPP; + } + + return 0; } -static ssize_t alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int lm83_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm83_data *data = lm83_update_device(dev); - int bitnr = attr->index; + switch (type) { + case hwmon_chip: + return lm83_chip_read(dev, attr, channel, val); + case hwmon_temp: + return lm83_temp_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} - return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); +static int lm83_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_temp: + return lm83_temp_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 4); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, 5); -static SENSOR_DEVICE_ATTR_RW(temp3_max, temp, 6); -static SENSOR_DEVICE_ATTR_RW(temp4_max, temp, 7); -static SENSOR_DEVICE_ATTR_RO(temp1_crit, temp, 8); -static SENSOR_DEVICE_ATTR_RO(temp2_crit, temp, 8); -static SENSOR_DEVICE_ATTR_RW(temp3_crit, temp, 8); -static SENSOR_DEVICE_ATTR_RO(temp4_crit, temp, 8); - -/* Individual alarm files */ -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, 4); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 8); -static SENSOR_DEVICE_ATTR_RO(temp4_crit_alarm, alarm, 9); -static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, 10); -static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, 12); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 13); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 15); -/* Raw alarm file for compatibility */ -static DEVICE_ATTR_RO(alarms); - -static struct attribute *lm83_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &dev_attr_alarms.attr, - NULL -}; +static umode_t lm83_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct lm83_data *data = _data; -static const struct attribute_group lm83_group = { - .attrs = lm83_attributes, -}; + /* + * LM82 only supports a single external channel, modeled as channel 2. + */ + if (data->type == lm82 && (channel == 1 || channel == 3)) + return 0; + + switch (type) { + case hwmon_chip: + if (attr == hwmon_chip_alarms) + return 0444; + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + return 0444; + case hwmon_temp_fault: + if (channel) + return 0444; + break; + case hwmon_temp_max: + return 0644; + case hwmon_temp_crit: + if (channel == 2) + return 0644; + return 0444; + default: + break; + } + break; + default: + break; + } + return 0; +} -static struct attribute *lm83_attributes_opt[] = { - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp4_crit.dev_attr.attr, - - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, +static const struct hwmon_channel_info *lm83_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT + ), NULL }; -static const struct attribute_group lm83_group_opt = { - .attrs = lm83_attributes_opt, +static const struct hwmon_ops lm83_hwmon_ops = { + .is_visible = lm83_is_visible, + .read = lm83_read, + .write = lm83_write, }; -/* - * Real code - */ +static const struct hwmon_chip_info lm83_chip_info = { + .ops = &lm83_hwmon_ops, + .info = lm83_info, +}; /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm83_detect(struct i2c_client *new_client, +static int lm83_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; const char *name; u8 man_id, chip_id; @@ -283,22 +375,30 @@ static int lm83_detect(struct i2c_client *new_client, return -ENODEV; /* Detection */ - if ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) & 0xA8) || - (i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) & 0x48) || - (i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) & 0x41)) { + if ((i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) & 0xA8) || + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) & 0x48) || + (i2c_smbus_read_byte_data(client, LM83_REG_R_CONFIG) & 0x41)) { dev_dbg(&adapter->dev, "LM83 detection failed at 0x%02x\n", - new_client->addr); + client->addr); return -ENODEV; } /* Identification */ - man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID); + man_id = i2c_smbus_read_byte_data(client, LM83_REG_R_MAN_ID); if (man_id != 0x01) /* National Semiconductor */ return -ENODEV; - chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID); + chip_id = i2c_smbus_read_byte_data(client, LM83_REG_R_CHIP_ID); switch (chip_id) { case 0x03: + /* + * According to the LM82 datasheet dated March 2013, recent + * revisions of LM82 have a die revision of 0x03. This was + * confirmed with a real chip. Further details in this revision + * of the LM82 datasheet strongly suggest that LM82 is just a + * repackaged LM83. It is therefore impossible to distinguish + * those chips from LM83, and they will be misdetected as LM83. + */ name = "lm83"; break; case 0x01: @@ -306,44 +406,42 @@ static int lm83_detect(struct i2c_client *new_client, break; default: /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n", - man_id, chip_id); + dev_dbg(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n", + man_id, chip_id); return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } -static int lm83_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) +static const struct i2c_device_id lm83_id[] = { + { "lm83", lm83 }, + { "lm82", lm82 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm83_id); + +static int lm83_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct device *hwmon_dev; struct lm83_data *data; - data = devm_kzalloc(&new_client->dev, sizeof(struct lm83_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct lm83_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->client = new_client; - mutex_init(&data->update_lock); + data->regmap = devm_regmap_init(dev, NULL, client, &lm83_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); - /* - * Register sysfs hooks - * The LM82 can only monitor one external diode which is - * at the same register as the LM83 temp3 entry - so we - * declare 1 and 3 common, and then 2 and 4 only for the LM83. - */ - data->groups[0] = &lm83_group; - if (id->driver_data == lm83) - data->groups[1] = &lm83_group_opt; + data->type = i2c_match_id(lm83_id, client)->driver_data; - hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, - new_client->name, - data, data->groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &lm83_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } @@ -351,19 +449,12 @@ static int lm83_probe(struct i2c_client *new_client, * Driver data (common to all clients) */ -static const struct i2c_device_id lm83_id[] = { - { "lm83", lm83 }, - { "lm82", lm82 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm83_id); - static struct i2c_driver lm83_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm83", }, - .probe = lm83_probe, + .probe_new = lm83_probe, .id_table = lm83_id, .detect = lm83_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index cff0aa505a78..8d33c2484755 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -294,7 +294,7 @@ struct lm85_data { bool has_vid5; /* true if VID5 is configured for ADT7463 or ADT7468 */ struct mutex update_lock; - int valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_reading; /* In jiffies */ unsigned long last_config; /* In jiffies */ @@ -541,7 +541,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) data->last_config = jiffies; } /* last_config */ - data->valid = 1; + data->valid = true; mutex_unlock(&data->update_lock); @@ -1539,12 +1539,14 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) if (!type_name) return -ENODEV; - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } -static int lm85_probe(struct i2c_client *client, const struct i2c_device_id *id) +static const struct i2c_device_id lm85_id[]; + +static int lm85_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -1559,7 +1561,7 @@ static int lm85_probe(struct i2c_client *client, const struct i2c_device_id *id) if (client->dev.of_node) data->type = (enum chips)of_device_get_match_data(&client->dev); else - data->type = id->driver_data; + data->type = i2c_match_id(lm85_id, client)->driver_data; mutex_init(&data->update_lock); /* Fill in the chip specific driver values */ @@ -1696,7 +1698,7 @@ static struct i2c_driver lm85_driver = { .name = "lm85", .of_match_table = of_match_ptr(lm85_of_match), }, - .probe = lm85_probe, + .probe_new = lm85_probe, .id_table = lm85_id, .detect = lm85_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index ad501ac4a594..818fb6195245 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -40,7 +40,7 @@ * This driver also supports the ADM1024, a sensor chip made by Analog * Devices. That chip is fully compatible with the LM87. Complete * datasheet can be obtained from Analog's website at: - * http://www.analog.com/en/prod/0,2877,ADM1024,00.html + * https://www.analog.com/en/prod/0,2877,ADM1024,00.html */ #include <linux/module.h> @@ -141,7 +141,7 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; struct lm87_data { struct mutex update_lock; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 channel; /* register value */ @@ -251,7 +251,7 @@ static struct lm87_data *lm87_update_device(struct device *dev) data->aout = lm87_read_value(client, LM87_REG_AOUT); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -833,7 +833,7 @@ static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -912,7 +912,7 @@ static int lm87_init_client(struct i2c_client *client) return 0; } -static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int lm87_probe(struct i2c_client *client) { struct lm87_data *data; struct device *hwmon_dev; @@ -994,7 +994,7 @@ static struct i2c_driver lm87_driver = { .name = "lm87", .of_match_table = lm87_of_match, }, - .probe = lm87_probe, + .probe_new = lm87_probe, .id_table = lm87_id, .detect = lm87_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 9b3c9f390ef8..db595f7d01f8 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -35,6 +35,16 @@ * explicitly as max6659, or if its address is not 0x4c. * These chips lack the remote temperature offset feature. * + * This driver also supports the MAX6654 chip made by Maxim. This chip can be + * at 9 different addresses, similar to MAX6680/MAX6681. The MAX6654 is similar + * to MAX6657/MAX6658/MAX6659, but does not support critical temperature + * limits. Extended range is available by setting the configuration register + * accordingly, and is done during initialization. Extended precision is only + * available at conversion rates of 1 Hz and slower. Note that extended + * precision is not enabled by default, as this driver initializes all chips + * to 2 Hz by design. The driver also supports MAX6690, which is practically + * identical to MAX6654. + * * This driver also supports the MAX6646, MAX6647, MAX6648, MAX6649 and * MAX6692 chips made by Maxim. These are again similar to the LM86, * but they use unsigned temperature values and can report temperatures @@ -55,34 +65,53 @@ * and extended mode. They are mostly compatible with LM90 except for a data * format difference for the temperature value registers. * + * This driver also supports ADT7481, ADT7482, and ADT7483 from Analog Devices + * / ON Semiconductor. The chips are similar to ADT7461 but support two external + * temperature sensors. + * + * This driver also supports NCT72, NCT214, and NCT218 from ON Semiconductor. + * The chips are similar to ADT7461/ADT7461A but have full PEC support + * (undocumented). + * * This driver also supports the SA56004 from Philips. This device is * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. * * This driver also supports the G781 from GMT. This device is compatible * with the ADM1032. * - * This driver also supports TMP451 from Texas Instruments. This device is - * supported in both compatibility and extended mode. It's mostly compatible - * with ADT7461 except for local temperature low byte register and max - * conversion rate. + * This driver also supports TMP451 and TMP461 from Texas Instruments. + * Those devices are supported in both compatibility and extended mode. + * They are mostly compatible with ADT7461 except for local temperature + * low byte register and max conversion rate. + * + * This driver also supports MAX1617 and various clones such as G767 + * and NE1617. Such clones will be detected as MAX1617. + * + * This driver also supports NE1618 from Philips. It is similar to NE1617 + * but supports 11 bit external temperature values. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. */ -#include <linux/module.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> #include <linux/init.h> -#include <linux/slab.h> +#include <linux/interrupt.h> #include <linux/jiffies.h> -#include <linux/i2c.h> #include <linux/hwmon.h> -#include <linux/err.h> +#include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> -#include <linux/sysfs.h> -#include <linux/interrupt.h> #include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +/* The maximum number of channels currently supported */ +#define MAX_CHANNELS 3 /* * Addresses to scan @@ -94,8 +123,8 @@ * have address 0x4d. * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e. - * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, - * 0x4c, 0x4d or 0x4e. + * MAX6654, MAX6680, and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, + * 0x2a, 0x2b, 0x4c, 0x4d or 0x4e. * SA56004 can have address 0x48 through 0x4F. */ @@ -103,123 +132,154 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696, sa56004, g781, tmp451 }; +enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, + g781, lm84, lm90, lm99, + max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, + nct210, nct72, ne1618, sa56004, tmp451, tmp461, w83l771, +}; /* * The LM90 registers */ -#define LM90_REG_R_MAN_ID 0xFE -#define LM90_REG_R_CHIP_ID 0xFF -#define LM90_REG_R_CONFIG1 0x03 -#define LM90_REG_W_CONFIG1 0x09 -#define LM90_REG_R_CONFIG2 0xBF -#define LM90_REG_W_CONFIG2 0xBF -#define LM90_REG_R_CONVRATE 0x04 -#define LM90_REG_W_CONVRATE 0x0A -#define LM90_REG_R_STATUS 0x02 -#define LM90_REG_R_LOCAL_TEMP 0x00 -#define LM90_REG_R_LOCAL_HIGH 0x05 -#define LM90_REG_W_LOCAL_HIGH 0x0B -#define LM90_REG_R_LOCAL_LOW 0x06 -#define LM90_REG_W_LOCAL_LOW 0x0C -#define LM90_REG_R_LOCAL_CRIT 0x20 -#define LM90_REG_W_LOCAL_CRIT 0x20 -#define LM90_REG_R_REMOTE_TEMPH 0x01 -#define LM90_REG_R_REMOTE_TEMPL 0x10 -#define LM90_REG_R_REMOTE_OFFSH 0x11 -#define LM90_REG_W_REMOTE_OFFSH 0x11 -#define LM90_REG_R_REMOTE_OFFSL 0x12 -#define LM90_REG_W_REMOTE_OFFSL 0x12 -#define LM90_REG_R_REMOTE_HIGHH 0x07 -#define LM90_REG_W_REMOTE_HIGHH 0x0D -#define LM90_REG_R_REMOTE_HIGHL 0x13 -#define LM90_REG_W_REMOTE_HIGHL 0x13 -#define LM90_REG_R_REMOTE_LOWH 0x08 -#define LM90_REG_W_REMOTE_LOWH 0x0E -#define LM90_REG_R_REMOTE_LOWL 0x14 -#define LM90_REG_W_REMOTE_LOWL 0x14 -#define LM90_REG_R_REMOTE_CRIT 0x19 -#define LM90_REG_W_REMOTE_CRIT 0x19 -#define LM90_REG_R_TCRIT_HYST 0x21 -#define LM90_REG_W_TCRIT_HYST 0x21 - -/* MAX6646/6647/6649/6657/6658/6659/6695/6696 registers */ - -#define MAX6657_REG_R_LOCAL_TEMPL 0x11 -#define MAX6696_REG_R_STATUS2 0x12 -#define MAX6659_REG_R_REMOTE_EMERG 0x16 -#define MAX6659_REG_W_REMOTE_EMERG 0x16 -#define MAX6659_REG_R_LOCAL_EMERG 0x17 -#define MAX6659_REG_W_LOCAL_EMERG 0x17 +#define LM90_REG_MAN_ID 0xFE +#define LM90_REG_CHIP_ID 0xFF +#define LM90_REG_CONFIG1 0x03 +#define LM90_REG_CONFIG2 0xBF +#define LM90_REG_CONVRATE 0x04 +#define LM90_REG_STATUS 0x02 +#define LM90_REG_LOCAL_TEMP 0x00 +#define LM90_REG_LOCAL_HIGH 0x05 +#define LM90_REG_LOCAL_LOW 0x06 +#define LM90_REG_LOCAL_CRIT 0x20 +#define LM90_REG_REMOTE_TEMPH 0x01 +#define LM90_REG_REMOTE_TEMPL 0x10 +#define LM90_REG_REMOTE_OFFSH 0x11 +#define LM90_REG_REMOTE_OFFSL 0x12 +#define LM90_REG_REMOTE_HIGHH 0x07 +#define LM90_REG_REMOTE_HIGHL 0x13 +#define LM90_REG_REMOTE_LOWH 0x08 +#define LM90_REG_REMOTE_LOWL 0x14 +#define LM90_REG_REMOTE_CRIT 0x19 +#define LM90_REG_TCRIT_HYST 0x21 + +/* MAX6646/6647/6649/6654/6657/6658/6659/6695/6696 registers */ + +#define MAX6657_REG_LOCAL_TEMPL 0x11 +#define MAX6696_REG_STATUS2 0x12 +#define MAX6659_REG_REMOTE_EMERG 0x16 +#define MAX6659_REG_LOCAL_EMERG 0x17 /* SA56004 registers */ -#define SA56004_REG_R_LOCAL_TEMPL 0x22 +#define SA56004_REG_LOCAL_TEMPL 0x22 #define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ -/* TMP451 registers */ -#define TMP451_REG_R_LOCAL_TEMPL 0x15 +/* TMP451/TMP461 registers */ +#define TMP451_REG_LOCAL_TEMPL 0x15 +#define TMP451_REG_CONALERT 0x22 + +#define TMP461_REG_CHEN 0x16 +#define TMP461_REG_DFC 0x24 + +/* ADT7481 registers */ +#define ADT7481_REG_STATUS2 0x23 +#define ADT7481_REG_CONFIG2 0x24 + +#define ADT7481_REG_MAN_ID 0x3e +#define ADT7481_REG_CHIP_ID 0x3d -/* - * Device flags - */ -#define LM90_FLAG_ADT7461_EXT (1 << 0) /* ADT7461 extended mode */ /* Device features */ -#define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */ -#define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ -#define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ -#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ -#define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ -#define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */ -#define LM90_PAUSE_FOR_CONFIG (1 << 8) /* Pause conversion for config */ +#define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */ +#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */ +#define LM90_HAVE_UNSIGNED_TEMP BIT(2) /* temperatures are unsigned */ +#define LM90_HAVE_REM_LIMIT_EXT BIT(3) /* extended remote limit */ +#define LM90_HAVE_EMERGENCY BIT(4) /* 3rd upper (emergency) limit */ +#define LM90_HAVE_EMERGENCY_ALARM BIT(5)/* emergency alarm */ +#define LM90_HAVE_TEMP3 BIT(6) /* 3rd temperature sensor */ +#define LM90_HAVE_BROKEN_ALERT BIT(7) /* Broken alert */ +#define LM90_PAUSE_FOR_CONFIG BIT(8) /* Pause conversion for config */ +#define LM90_HAVE_CRIT BIT(9) /* Chip supports CRIT/OVERT register */ +#define LM90_HAVE_CRIT_ALRM_SWP BIT(10) /* critical alarm bits swapped */ +#define LM90_HAVE_PEC BIT(11) /* Chip supports PEC */ +#define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/ +#define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */ +#define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/ +#define LM90_HAVE_LOW BIT(15) /* low limits */ +#define LM90_HAVE_CONVRATE BIT(16) /* conversion rate */ +#define LM90_HAVE_REMOTE_EXT BIT(17) /* extended remote temperature */ +#define LM90_HAVE_FAULTQUEUE BIT(18) /* configurable samples count */ /* LM90 status */ -#define LM90_STATUS_LTHRM (1 << 0) /* local THERM limit tripped */ -#define LM90_STATUS_RTHRM (1 << 1) /* remote THERM limit tripped */ -#define LM90_STATUS_ROPEN (1 << 2) /* remote is an open circuit */ -#define LM90_STATUS_RLOW (1 << 3) /* remote low temp limit tripped */ -#define LM90_STATUS_RHIGH (1 << 4) /* remote high temp limit tripped */ -#define LM90_STATUS_LLOW (1 << 5) /* local low temp limit tripped */ -#define LM90_STATUS_LHIGH (1 << 6) /* local high temp limit tripped */ - -#define MAX6696_STATUS2_R2THRM (1 << 1) /* remote2 THERM limit tripped */ -#define MAX6696_STATUS2_R2OPEN (1 << 2) /* remote2 is an open circuit */ -#define MAX6696_STATUS2_R2LOW (1 << 3) /* remote2 low temp limit tripped */ -#define MAX6696_STATUS2_R2HIGH (1 << 4) /* remote2 high temp limit tripped */ -#define MAX6696_STATUS2_ROT2 (1 << 5) /* remote emergency limit tripped */ -#define MAX6696_STATUS2_R2OT2 (1 << 6) /* remote2 emergency limit tripped */ -#define MAX6696_STATUS2_LOT2 (1 << 7) /* local emergency limit tripped */ +#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ +#define LM90_STATUS_RTHRM BIT(1) /* remote THERM limit tripped */ +#define LM90_STATUS_ROPEN BIT(2) /* remote is an open circuit */ +#define LM90_STATUS_RLOW BIT(3) /* remote low temp limit tripped */ +#define LM90_STATUS_RHIGH BIT(4) /* remote high temp limit tripped */ +#define LM90_STATUS_LLOW BIT(5) /* local low temp limit tripped */ +#define LM90_STATUS_LHIGH BIT(6) /* local high temp limit tripped */ +#define LM90_STATUS_BUSY BIT(7) /* conversion is ongoing */ + +/* MAX6695/6696 and ADT7481 2nd status register */ +#define MAX6696_STATUS2_R2THRM BIT(1) /* remote2 THERM limit tripped */ +#define MAX6696_STATUS2_R2OPEN BIT(2) /* remote2 is an open circuit */ +#define MAX6696_STATUS2_R2LOW BIT(3) /* remote2 low temp limit tripped */ +#define MAX6696_STATUS2_R2HIGH BIT(4) /* remote2 high temp limit tripped */ +#define MAX6696_STATUS2_ROT2 BIT(5) /* remote emergency limit tripped */ +#define MAX6696_STATUS2_R2OT2 BIT(6) /* remote2 emergency limit tripped */ +#define MAX6696_STATUS2_LOT2 BIT(7) /* local emergency limit tripped */ /* * Driver data (common to all clients) */ static const struct i2c_device_id lm90_id[] = { + { "adm1020", max1617 }, + { "adm1021", max1617 }, + { "adm1023", adm1023 }, { "adm1032", adm1032 }, + { "adt7421", adt7461a }, { "adt7461", adt7461 }, - { "adt7461a", adt7461 }, + { "adt7461a", adt7461a }, + { "adt7481", adt7481 }, + { "adt7482", adt7481 }, + { "adt7483a", adt7481 }, { "g781", g781 }, + { "gl523sm", max1617 }, + { "lm84", lm84 }, + { "lm86", lm90 }, + { "lm89", lm90 }, { "lm90", lm90 }, - { "lm86", lm86 }, - { "lm89", lm86 }, { "lm99", lm99 }, + { "max1617", max1617 }, + { "max6642", max6642 }, { "max6646", max6646 }, { "max6647", max6646 }, + { "max6648", max6648 }, { "max6649", max6646 }, + { "max6654", max6654 }, { "max6657", max6657 }, { "max6658", max6657 }, { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, + { "max6690", max6654 }, + { "max6692", max6648 }, { "max6695", max6696 }, { "max6696", max6696 }, - { "nct1008", adt7461 }, + { "mc1066", max1617 }, + { "nct1008", adt7461a }, + { "nct210", nct210 }, + { "nct214", nct72 }, + { "nct218", nct72 }, + { "nct72", nct72 }, + { "ne1618", ne1618 }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, + { "thmc10", max1617 }, { "tmp451", tmp451 }, + { "tmp461", tmp461 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -235,7 +295,11 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { }, { .compatible = "adi,adt7461a", - .data = (void *)adt7461 + .data = (void *)adt7461a + }, + { + .compatible = "adi,adt7481", + .data = (void *)adt7481 }, { .compatible = "gmt,g781", @@ -247,11 +311,11 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { }, { .compatible = "national,lm86", - .data = (void *)lm86 + .data = (void *)lm90 }, { .compatible = "national,lm89", - .data = (void *)lm86 + .data = (void *)lm90 }, { .compatible = "national,lm99", @@ -270,6 +334,10 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { .data = (void *)max6646 }, { + .compatible = "dallas,max6654", + .data = (void *)max6654 + }, + { .compatible = "dallas,max6657", .data = (void *)max6657 }, @@ -299,7 +367,19 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { }, { .compatible = "onnn,nct1008", - .data = (void *)adt7461 + .data = (void *)adt7461a + }, + { + .compatible = "onnn,nct214", + .data = (void *)nct72 + }, + { + .compatible = "onnn,nct218", + .data = (void *)nct72 + }, + { + .compatible = "onnn,nct72", + .data = (void *)nct72 }, { .compatible = "winbond,w83l771", @@ -313,6 +393,10 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { .compatible = "ti,tmp451", .data = (void *)tmp451 }, + { + .compatible = "ti,tmp461", + .data = (void *)tmp461 + }, { }, }; MODULE_DEVICE_TABLE(of, lm90_of_match); @@ -325,96 +409,252 @@ struct lm90_params { u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate register value */ + u8 resolution; /* 16-bit resolution (default 11 bit) */ + u8 reg_status2; /* 2nd status register (optional) */ u8 reg_local_ext; /* Extended local temp register (optional) */ + u8 faultqueue_mask; /* fault queue bit mask */ + u8 faultqueue_depth; /* fault queue depth if mask is used */ }; static const struct lm90_params lm90_params[] = { + [adm1023] = { + .flags = LM90_HAVE_ALARMS | LM90_HAVE_OFFSET | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .resolution = 8, + .max_convrate = 7, + }, [adm1032] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT + | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 10, }, [adt7461] = { + /* + * Standard temperature range is supposed to be unsigned, + * but that does not match reality. Negative temperatures + * are always reported. + */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP + | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 10, + .resolution = 10, + }, + [adt7461a] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP + | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, + .alert_alarms = 0x7c, + .max_convrate = 10, + }, + [adt7481] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP + | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC + | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, + .alert_alarms = 0x1c7c, + .max_convrate = 11, + .resolution = 10, + .reg_status2 = ADT7481_REG_STATUS2, }, [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, - .max_convrate = 8, + .max_convrate = 7, }, - [lm86] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, - .alert_alarms = 0x7b, - .max_convrate = 9, + [lm84] = { + .flags = LM90_HAVE_ALARMS, + .resolution = 8, }, [lm90] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7b, .max_convrate = 9, + .faultqueue_mask = BIT(0), + .faultqueue_depth = 3, }, [lm99] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7b, .max_convrate = 9, + .faultqueue_mask = BIT(0), + .faultqueue_depth = 3, + }, + [max1617] = { + .flags = LM90_HAVE_CONVRATE | LM90_HAVE_BROKEN_ALERT | + LM90_HAVE_LOW | LM90_HAVE_ALARMS, + .alert_alarms = 0x78, + .resolution = 8, + .max_convrate = 7, + }, + [max6642] = { + .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, + .alert_alarms = 0x50, + .resolution = 10, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + .faultqueue_mask = BIT(4), + .faultqueue_depth = 2, }, [max6646] = { + .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 6, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + }, + [max6648] = { + .flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .max_convrate = 6, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + }, + [max6654] = { + .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .max_convrate = 7, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6657] = { - .flags = LM90_PAUSE_FOR_CONFIG, + .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 8, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6659] = { - .flags = LM90_HAVE_EMERGENCY, + .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 8, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6680] = { - .flags = LM90_HAVE_OFFSET, + /* + * Apparent temperatures of 128 degrees C or higher are reported + * and treated as negative temperatures (meaning min_alarm will + * be set). + */ + .flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT + | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 7, }, [max6696] = { .flags = LM90_HAVE_EMERGENCY - | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, + | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x1c7c, .max_convrate = 6, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_status2 = MAX6696_REG_STATUS2, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + .faultqueue_mask = BIT(5), + .faultqueue_depth = 4, + }, + [nct72] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP + | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_UNSIGNED_TEMP + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, + .alert_alarms = 0x7c, + .max_convrate = 10, + .resolution = 10, + }, + [nct210] = { + .flags = LM90_HAVE_ALARMS | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .resolution = 11, + .max_convrate = 7, + }, + [ne1618] = { + .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .resolution = 11, + .max_convrate = 7, }, [w83l771] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 8, }, [sa56004] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + /* + * Apparent temperatures of 128 degrees C or higher are reported + * and treated as negative temperatures (meaning min_alarm will + * be set). + */ + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7b, .max_convrate = 9, - .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL, + .reg_local_ext = SA56004_REG_LOCAL_TEMPL, + .faultqueue_mask = BIT(0), + .faultqueue_depth = 3, }, [tmp451] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT + | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, + .alert_alarms = 0x7c, + .max_convrate = 9, + .resolution = 12, + .reg_local_ext = TMP451_REG_LOCAL_TEMPL, + }, + [tmp461] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 9, - .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL, + .resolution = 12, + .reg_local_ext = TMP451_REG_LOCAL_TEMPL, }, }; /* - * TEMP8 register index + * temperature register index */ -enum lm90_temp8_reg_index { +enum lm90_temp_reg_index { LOCAL_LOW = 0, LOCAL_HIGH, LOCAL_CRIT, @@ -423,14 +663,8 @@ enum lm90_temp8_reg_index { REMOTE_EMERG, /* max6659 and max6695/96 */ REMOTE2_CRIT, /* max6695/96 only */ REMOTE2_EMERG, /* max6695/96 only */ - TEMP8_REG_NUM -}; -/* - * TEMP11 register index - */ -enum lm90_temp11_reg_index { - REMOTE_TEMP = 0, + REMOTE_TEMP, REMOTE_LOW, REMOTE_HIGH, REMOTE_OFFSET, /* except max6646, max6657/58/59, and max6695/96 */ @@ -438,7 +672,9 @@ enum lm90_temp11_reg_index { REMOTE2_TEMP, /* max6695/96 only */ REMOTE2_LOW, /* max6695/96 only */ REMOTE2_HIGH, /* max6695/96 only */ - TEMP11_REG_NUM + REMOTE2_OFFSET, + + TEMP_REG_NUM }; /* @@ -447,13 +683,21 @@ enum lm90_temp11_reg_index { struct lm90_data { struct i2c_client *client; - u32 channel_config[4]; + struct device *hwmon_dev; + u32 chip_config[2]; + u32 channel_config[MAX_CHANNELS + 1]; + const char *channel_label[MAX_CHANNELS]; + struct hwmon_channel_info chip_info; struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[3]; struct hwmon_chip_info chip; struct mutex update_lock; + struct delayed_work alert_work; + struct work_struct report_work; bool valid; /* true if register values are valid */ + bool alarms_valid; /* true if status register values are valid */ unsigned long last_updated; /* in jiffies */ + unsigned long alarms_updated; /* in jiffies */ int kind; u32 flags; @@ -462,16 +706,23 @@ struct lm90_data { u8 config; /* Current configuration register value */ u8 config_orig; /* Original configuration register value */ u8 convrate_orig; /* Original conversion rate register value */ + u8 resolution; /* temperature resolution in bit */ u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate */ + u8 reg_status2; /* 2nd status register (optional) */ u8 reg_local_ext; /* local extension register offset */ + u8 reg_remote_ext; /* remote temperature low byte */ + u8 faultqueue_mask; /* fault queue mask */ + u8 faultqueue_depth; /* fault queue mask */ /* registers values */ - s8 temp8[TEMP8_REG_NUM]; - s16 temp11[TEMP11_REG_NUM]; + u16 temp[TEMP_REG_NUM]; u8 temp_hyst; - u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ + u8 conalert; + u16 reported_alarms; /* alarms reported as sysfs/udev events */ + u16 current_alarms; /* current alarms, reported by chip */ + u16 alarms; /* alarms not yet reported to user */ }; /* @@ -479,10 +730,10 @@ struct lm90_data { */ /* - * The ADM1032 supports PEC but not on write byte transactions, so we need + * If the chip supports PEC but not on write byte transactions, we need * to explicitly ask for a transaction without PEC. */ -static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) +static inline s32 lm90_write_no_pec(struct i2c_client *client, u8 value) { return i2c_smbus_xfer(client->adapter, client->addr, client->flags & ~I2C_CLIENT_PEC, @@ -491,47 +742,96 @@ static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) /* * It is assumed that client->update_lock is held (unless we are in - * detection or initialization steps). This matters when PEC is enabled, - * because we don't want the address pointer to change between the write - * byte and the read byte transactions. + * detection or initialization steps). This matters when PEC is enabled + * for chips with partial PEC support, because we don't want the address + * pointer to change between the write byte and the read byte transactions. */ static int lm90_read_reg(struct i2c_client *client, u8 reg) { + struct lm90_data *data = i2c_get_clientdata(client); + bool partial_pec = (client->flags & I2C_CLIENT_PEC) && + (data->flags & LM90_HAVE_PARTIAL_PEC); int err; - if (client->flags & I2C_CLIENT_PEC) { - err = adm1032_write_byte(client, reg); - if (err >= 0) - err = i2c_smbus_read_byte(client); - } else - err = i2c_smbus_read_byte_data(client, reg); + if (partial_pec) { + err = lm90_write_no_pec(client, reg); + if (err) + return err; + return i2c_smbus_read_byte(client); + } + return i2c_smbus_read_byte_data(client, reg); +} - return err; +/* + * Return register write address + * + * The write address for registers 0x03 .. 0x08 is the read address plus 6. + * For other registers the write address matches the read address. + */ +static u8 lm90_write_reg_addr(u8 reg) +{ + if (reg >= LM90_REG_CONFIG1 && reg <= LM90_REG_REMOTE_LOWH) + return reg + 6; + return reg; +} + +/* + * Write into LM90 register. + * Convert register address to write address if needed, then execute the + * operation. + */ +static int lm90_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, lm90_write_reg_addr(reg), val); +} + +/* + * Write into 16-bit LM90 register. + * Convert register addresses to write address if needed, then execute the + * operation. + */ +static int lm90_write16(struct i2c_client *client, u8 regh, u8 regl, u16 val) +{ + int ret; + + ret = lm90_write_reg(client, regh, val >> 8); + if (ret < 0 || !regl) + return ret; + return lm90_write_reg(client, regl, val & 0xff); } -static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl) +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, + bool is_volatile) { int oldh, newh, l; - /* - * There is a trick here. We have to read two registers to have the - * sensor temperature, but we have to beware a conversion could occur - * between the readings. The datasheet says we should either use - * the one-shot conversion register, which we don't want to do - * (disables hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit at the - * exact same time). So the solution used here is to read the high - * byte once, then the low byte, then the high byte again. If the new - * high byte matches the old one, then we have a valid reading. Else - * we have to read the low byte again, and now we believe we have a - * correct reading. - */ oldh = lm90_read_reg(client, regh); if (oldh < 0) return oldh; + + if (!regl) + return oldh << 8; + l = lm90_read_reg(client, regl); if (l < 0) return l; + + if (!is_volatile) + return (oldh << 8) | l; + + /* + * For volatile registers we have to use a trick. + * We have to read two registers to have the sensor temperature, + * but we have to beware a conversion could occur between the + * readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * the high byte again. If the new high byte matches the old one, + * then we have a valid reading. Otherwise we have to read the low + * byte again, and now we believe we have a correct reading. + */ newh = lm90_read_reg(client, regh); if (newh < 0) return newh; @@ -548,9 +848,7 @@ static int lm90_update_confreg(struct lm90_data *data, u8 config) if (data->config != config) { int err; - err = i2c_smbus_write_byte_data(data->client, - LM90_REG_W_CONFIG1, - config); + err = lm90_write_reg(data->client, LM90_REG_CONFIG1, config); if (err) return err; data->config = config; @@ -566,18 +864,14 @@ static int lm90_update_confreg(struct lm90_data *data, u8 config) * various registers have different meanings as a result of selecting a * non-default remote channel. */ -static int lm90_select_remote_channel(struct lm90_data *data, int channel) +static int lm90_select_remote_channel(struct lm90_data *data, bool second) { - int err = 0; + u8 config = data->config & ~0x08; - if (data->kind == max6696) { - u8 config = data->config & ~0x08; + if (second) + config |= 0x08; - if (channel) - config |= 0x08; - err = lm90_update_confreg(data, config); - } - return err; + return lm90_update_confreg(data, config); } static int lm90_write_convrate(struct lm90_data *data, int val) @@ -593,7 +887,7 @@ static int lm90_write_convrate(struct lm90_data *data, int val) } /* Set conv rate */ - err = i2c_smbus_write_byte_data(data->client, LM90_REG_W_CONVRATE, val); + err = lm90_write_reg(data->client, LM90_REG_CONVRATE, val); /* Revert change to config */ lm90_update_confreg(data, config); @@ -626,102 +920,287 @@ static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, return err; } +static int lm90_set_faultqueue(struct i2c_client *client, + struct lm90_data *data, int val) +{ + int err; + + if (data->faultqueue_mask) { + err = lm90_update_confreg(data, val <= data->faultqueue_depth / 2 ? + data->config & ~data->faultqueue_mask : + data->config | data->faultqueue_mask); + } else { + static const u8 values[4] = {0, 2, 6, 0x0e}; + + data->conalert = (data->conalert & 0xf1) | values[val - 1]; + err = lm90_write_reg(data->client, TMP451_REG_CONALERT, + data->conalert); + } + + return err; +} + static int lm90_update_limits(struct device *dev) { struct lm90_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; int val; - val = lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT); - if (val < 0) - return val; - data->temp8[LOCAL_CRIT] = val; - - val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT); - if (val < 0) - return val; - data->temp8[REMOTE_CRIT] = val; - - val = lm90_read_reg(client, LM90_REG_R_TCRIT_HYST); - if (val < 0) - return val; - data->temp_hyst = val; + if (data->flags & LM90_HAVE_CRIT) { + val = lm90_read_reg(client, LM90_REG_LOCAL_CRIT); + if (val < 0) + return val; + data->temp[LOCAL_CRIT] = val << 8; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH); - if (val < 0) - return val; - data->temp11[REMOTE_LOW] = val << 8; + val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT); + if (val < 0) + return val; + data->temp[REMOTE_CRIT] = val << 8; - if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { - val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL); + val = lm90_read_reg(client, LM90_REG_TCRIT_HYST); if (val < 0) return val; - data->temp11[REMOTE_LOW] |= val; + data->temp_hyst = val; + } + if ((data->flags & LM90_HAVE_FAULTQUEUE) && !data->faultqueue_mask) { + val = lm90_read_reg(client, TMP451_REG_CONALERT); + if (val < 0) + return val; + data->conalert = val; } - val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH); + val = lm90_read16(client, LM90_REG_REMOTE_LOWH, + (data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_LOWL : 0, + false); if (val < 0) return val; - data->temp11[REMOTE_HIGH] = val << 8; + data->temp[REMOTE_LOW] = val; - if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { - val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL); - if (val < 0) - return val; - data->temp11[REMOTE_HIGH] |= val; - } + val = lm90_read16(client, LM90_REG_REMOTE_HIGHH, + (data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_HIGHL : 0, + false); + if (val < 0) + return val; + data->temp[REMOTE_HIGH] = val; if (data->flags & LM90_HAVE_OFFSET) { - val = lm90_read16(client, LM90_REG_R_REMOTE_OFFSH, - LM90_REG_R_REMOTE_OFFSL); + val = lm90_read16(client, LM90_REG_REMOTE_OFFSH, + LM90_REG_REMOTE_OFFSL, false); if (val < 0) return val; - data->temp11[REMOTE_OFFSET] = val; + data->temp[REMOTE_OFFSET] = val; } if (data->flags & LM90_HAVE_EMERGENCY) { - val = lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG); + val = lm90_read_reg(client, MAX6659_REG_LOCAL_EMERG); if (val < 0) return val; - data->temp8[LOCAL_EMERG] = val; + data->temp[LOCAL_EMERG] = val << 8; - val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG); + val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); if (val < 0) return val; - data->temp8[REMOTE_EMERG] = val; + data->temp[REMOTE_EMERG] = val << 8; } - if (data->kind == max6696) { - val = lm90_select_remote_channel(data, 1); + if (data->flags & LM90_HAVE_TEMP3) { + val = lm90_select_remote_channel(data, true); if (val < 0) return val; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT); + val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT); if (val < 0) return val; - data->temp8[REMOTE2_CRIT] = val; + data->temp[REMOTE2_CRIT] = val << 8; + + if (data->flags & LM90_HAVE_EMERGENCY) { + val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); + if (val < 0) + return val; + data->temp[REMOTE2_EMERG] = val << 8; + } - val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG); + val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); if (val < 0) return val; - data->temp8[REMOTE2_EMERG] = val; + data->temp[REMOTE2_LOW] = val << 8; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH); + val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH); if (val < 0) return val; - data->temp11[REMOTE2_LOW] = val << 8; + data->temp[REMOTE2_HIGH] = val << 8; + + if (data->flags & LM90_HAVE_OFFSET) { + val = lm90_read16(client, LM90_REG_REMOTE_OFFSH, + LM90_REG_REMOTE_OFFSL, false); + if (val < 0) + return val; + data->temp[REMOTE2_OFFSET] = val; + } + + lm90_select_remote_channel(data, false); + } + + return 0; +} + +static void lm90_report_alarms(struct work_struct *work) +{ + struct lm90_data *data = container_of(work, struct lm90_data, report_work); + u16 cleared_alarms, new_alarms, current_alarms; + struct device *hwmon_dev = data->hwmon_dev; + struct device *dev = &data->client->dev; + int st, st2; + + current_alarms = data->current_alarms; + cleared_alarms = data->reported_alarms & ~current_alarms; + new_alarms = current_alarms & ~data->reported_alarms; + + if (!cleared_alarms && !new_alarms) + return; + + st = new_alarms & 0xff; + st2 = new_alarms >> 8; + + if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) || + (st2 & MAX6696_STATUS2_LOT2)) + dev_dbg(dev, "temp%d out of range, please check!\n", 1); + if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) || + (st2 & MAX6696_STATUS2_ROT2)) + dev_dbg(dev, "temp%d out of range, please check!\n", 2); + if (st & LM90_STATUS_ROPEN) + dev_dbg(dev, "temp%d diode open, please check!\n", 2); + if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH | + MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2)) + dev_dbg(dev, "temp%d out of range, please check!\n", 3); + if (st2 & MAX6696_STATUS2_R2OPEN) + dev_dbg(dev, "temp%d diode open, please check!\n", 3); + + st |= cleared_alarms & 0xff; + st2 |= cleared_alarms >> 8; + + if (st & LM90_STATUS_LLOW) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 0); + if (st & LM90_STATUS_RLOW) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 1); + if (st2 & MAX6696_STATUS2_R2LOW) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 2); + + if (st & LM90_STATUS_LHIGH) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 0); + if (st & LM90_STATUS_RHIGH) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 1); + if (st2 & MAX6696_STATUS2_R2HIGH) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 2); + + if (st & LM90_STATUS_LTHRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 0); + if (st & LM90_STATUS_RTHRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 1); + if (st2 & MAX6696_STATUS2_R2THRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 2); + + if (st2 & MAX6696_STATUS2_LOT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 0); + if (st2 & MAX6696_STATUS2_ROT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 1); + if (st2 & MAX6696_STATUS2_R2OT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 2); + + data->reported_alarms = current_alarms; +} + +static int lm90_update_alarms_locked(struct lm90_data *data, bool force) +{ + if (force || !data->alarms_valid || + time_after(jiffies, data->alarms_updated + msecs_to_jiffies(data->update_interval))) { + struct i2c_client *client = data->client; + bool check_enable; + u16 alarms; + int val; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH); + data->alarms_valid = false; + + val = lm90_read_reg(client, LM90_REG_STATUS); if (val < 0) return val; - data->temp11[REMOTE2_HIGH] = val << 8; + alarms = val & ~LM90_STATUS_BUSY; - lm90_select_remote_channel(data, 0); - } + if (data->reg_status2) { + val = lm90_read_reg(client, data->reg_status2); + if (val < 0) + return val; + alarms |= val << 8; + } + /* + * If the update is forced (called from interrupt or alert + * handler) and alarm data is valid, the alarms may have been + * updated after the last update interval, and the status + * register may still be cleared. Only add additional alarms + * in this case. Alarms will be cleared later if appropriate. + */ + if (force && data->alarms_valid) + data->current_alarms |= alarms; + else + data->current_alarms = alarms; + data->alarms |= alarms; + check_enable = (client->irq || !(data->config_orig & 0x80)) && + (data->config & 0x80); + + if (force || check_enable) + schedule_work(&data->report_work); + + /* + * Re-enable ALERT# output if it was originally enabled, relevant + * alarms are all clear, and alerts are currently disabled. + * Otherwise (re)schedule worker if needed. + */ + if (check_enable) { + if (!(data->current_alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + lm90_update_confreg(data, data->config & ~0x80); + /* + * We may have been called from the update handler. + * If so, the worker, if scheduled, is no longer + * needed. Cancel it. Don't synchronize because + * it may already be running. + */ + cancel_delayed_work(&data->alert_work); + } else { + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); + } + } + data->alarms_updated = jiffies; + data->alarms_valid = true; + } return 0; } +static int lm90_update_alarms(struct lm90_data *data, bool force) +{ + int err; + + mutex_lock(&data->update_lock); + err = lm90_update_alarms_locked(data, force); + mutex_unlock(&data->update_lock); + + return err; +} + +static void lm90_alert_work(struct work_struct *__work) +{ + struct delayed_work *delayed_work = container_of(__work, struct delayed_work, work); + struct lm90_data *data = container_of(delayed_work, struct lm90_data, alert_work); + + /* Nothing to do if alerts are enabled */ + if (!(data->config & 0x80)) + return; + + lm90_update_alarms(data, true); +} + static int lm90_update_device(struct device *dev) { struct lm90_data *data = dev_get_drvdata(dev); @@ -742,71 +1221,46 @@ static int lm90_update_device(struct device *dev) data->valid = false; - val = lm90_read_reg(client, LM90_REG_R_LOCAL_LOW); + val = lm90_read_reg(client, LM90_REG_LOCAL_LOW); if (val < 0) return val; - data->temp8[LOCAL_LOW] = val; + data->temp[LOCAL_LOW] = val << 8; - val = lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH); + val = lm90_read_reg(client, LM90_REG_LOCAL_HIGH); if (val < 0) return val; - data->temp8[LOCAL_HIGH] = val; + data->temp[LOCAL_HIGH] = val << 8; - if (data->reg_local_ext) { - val = lm90_read16(client, LM90_REG_R_LOCAL_TEMP, - data->reg_local_ext); - if (val < 0) - return val; - data->temp11[LOCAL_TEMP] = val; - } else { - val = lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP); - if (val < 0) - return val; - data->temp11[LOCAL_TEMP] = val << 8; - } - val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL); + val = lm90_read16(client, LM90_REG_LOCAL_TEMP, + data->reg_local_ext, true); if (val < 0) return val; - data->temp11[REMOTE_TEMP] = val; - - val = lm90_read_reg(client, LM90_REG_R_STATUS); + data->temp[LOCAL_TEMP] = val; + val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, + data->reg_remote_ext, true); if (val < 0) return val; - data->alarms = val; /* lower 8 bit of alarms */ + data->temp[REMOTE_TEMP] = val; - if (data->kind == max6696) { - val = lm90_select_remote_channel(data, 1); + if (data->flags & LM90_HAVE_TEMP3) { + val = lm90_select_remote_channel(data, true); if (val < 0) return val; - val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL); + val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, + data->reg_remote_ext, true); if (val < 0) { - lm90_select_remote_channel(data, 0); + lm90_select_remote_channel(data, false); return val; } - data->temp11[REMOTE2_TEMP] = val; - - lm90_select_remote_channel(data, 0); + data->temp[REMOTE2_TEMP] = val; - val = lm90_read_reg(client, MAX6696_REG_R_STATUS2); - if (val < 0) - return val; - data->alarms |= val << 8; + lm90_select_remote_channel(data, false); } - /* - * Re-enable ALERT# output if it was originally enabled and - * relevant alarms are all clear - */ - if (!(data->config_orig & 0x80) && - !(data->alarms & data->alert_alarms)) { - if (data->config & 0x80) { - dev_dbg(&client->dev, "Re-enabling ALERT#\n"); - lm90_update_confreg(data, data->config & ~0x80); - } - } + val = lm90_update_alarms_locked(data, false); + if (val < 0) + return val; data->last_updated = jiffies; data->valid = true; @@ -815,130 +1269,7 @@ static int lm90_update_device(struct device *dev) return 0; } -/* - * Conversions - * For local temperatures and limits, critical limits and the hysteresis - * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. - * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some - * Maxim chips use unsigned values. - */ - -static inline int temp_from_s8(s8 val) -{ - return val * 1000; -} - -static inline int temp_from_u8(u8 val) -{ - return val * 1000; -} - -static inline int temp_from_s16(s16 val) -{ - return val / 32 * 125; -} - -static inline int temp_from_u16(u16 val) -{ - return val / 32 * 125; -} - -static s8 temp_to_s8(long val) -{ - if (val <= -128000) - return -128; - if (val >= 127000) - return 127; - if (val < 0) - return (val - 500) / 1000; - return (val + 500) / 1000; -} - -static u8 temp_to_u8(long val) -{ - if (val <= 0) - return 0; - if (val >= 255000) - return 255; - return (val + 500) / 1000; -} - -static s16 temp_to_s16(long val) -{ - if (val <= -128000) - return 0x8000; - if (val >= 127875) - return 0x7FE0; - if (val < 0) - return (val - 62) / 125 * 32; - return (val + 62) / 125 * 32; -} - -static u8 hyst_to_reg(long val) -{ - if (val <= 0) - return 0; - if (val >= 30500) - return 31; - return (val + 500) / 1000; -} - -/* - * ADT7461 in compatibility mode is almost identical to LM90 except that - * attempts to write values that are outside the range 0 < temp < 127 are - * treated as the boundary value. - * - * ADT7461 in "extended mode" operation uses unsigned integers offset by - * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. - */ -static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) - return (val - 64) * 1000; - return temp_from_s8(val); -} - -static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) - return (val - 0x4000) / 64 * 250; - return temp_from_s16(val); -} - -static u8 temp_to_u8_adt7461(struct lm90_data *data, long val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) { - if (val <= -64000) - return 0; - if (val >= 191000) - return 0xFF; - return (val + 500 + 64000) / 1000; - } - if (val <= 0) - return 0; - if (val >= 127000) - return 127; - return (val + 500) / 1000; -} - -static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) { - if (val <= -64000) - return 0; - if (val >= 191750) - return 0xFFC0; - return (val + 64000 + 125) / 250 * 64; - } - if (val <= 0) - return 0; - if (val >= 127750) - return 0x7FC0; - return (val + 125) / 250 * 64; -} - -/* pec used for ADM1032 only */ +/* pec used for devices with PEC support */ static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, char *buf) { @@ -974,186 +1305,211 @@ static ssize_t pec_store(struct device *dev, struct device_attribute *dummy, static DEVICE_ATTR_RW(pec); -static int lm90_get_temp11(struct lm90_data *data, int index) +static int lm90_temp_get_resolution(struct lm90_data *data, int index) +{ + switch (index) { + case REMOTE_TEMP: + if (data->reg_remote_ext) + return data->resolution; + return 8; + case REMOTE_OFFSET: + case REMOTE2_OFFSET: + case REMOTE2_TEMP: + return data->resolution; + case LOCAL_TEMP: + if (data->reg_local_ext) + return data->resolution; + return 8; + case REMOTE_LOW: + case REMOTE_HIGH: + case REMOTE2_LOW: + case REMOTE2_HIGH: + if (data->flags & LM90_HAVE_REM_LIMIT_EXT) + return data->resolution; + return 8; + default: + return 8; + } +} + +static int lm90_temp_from_reg(u32 flags, u16 regval, u8 resolution) { - s16 temp11 = data->temp11[index]; - int temp; + int val; - if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u16_adt7461(data, temp11); - else if (data->kind == max6646) - temp = temp_from_u16(temp11); + if (flags & LM90_HAVE_EXTENDED_TEMP) + val = regval - 0x4000; + else if (flags & (LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_EXT_UNSIGNED)) + val = regval; else - temp = temp_from_s16(temp11); + val = (s16)regval; - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index <= 2) + return ((val >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +static int lm90_get_temp(struct lm90_data *data, int index, int channel) +{ + int temp = lm90_temp_from_reg(data->flags, data->temp[index], + lm90_temp_get_resolution(data, index)); + + /* +16 degrees offset for remote temperature on LM99 */ + if (data->kind == lm99 && channel) temp += 16000; return temp; } -static int lm90_set_temp11(struct lm90_data *data, int index, long val) +static u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution) { - static struct reg { - u8 high; - u8 low; - } reg[] = { - [REMOTE_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, - [REMOTE_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }, - [REMOTE_OFFSET] = { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL }, - [REMOTE2_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, - [REMOTE2_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL } + int fraction = resolution > 8 ? + 1000 - DIV_ROUND_CLOSEST(1000, BIT(resolution - 8)) : 0; + + if (flags & LM90_HAVE_EXTENDED_TEMP) { + val = clamp_val(val, -64000, 191000 + fraction); + val += 64000; + } else if (flags & LM90_HAVE_EXT_UNSIGNED) { + val = clamp_val(val, 0, 255000 + fraction); + } else if (flags & LM90_HAVE_UNSIGNED_TEMP) { + val = clamp_val(val, 0, 127000 + fraction); + } else { + val = clamp_val(val, -128000, 127000 + fraction); + } + + return DIV_ROUND_CLOSEST(val << (resolution - 8), 1000) << (16 - resolution); +} + +static int lm90_set_temp(struct lm90_data *data, int index, int channel, long val) +{ + static const u8 regs[] = { + [LOCAL_LOW] = LM90_REG_LOCAL_LOW, + [LOCAL_HIGH] = LM90_REG_LOCAL_HIGH, + [LOCAL_CRIT] = LM90_REG_LOCAL_CRIT, + [REMOTE_CRIT] = LM90_REG_REMOTE_CRIT, + [LOCAL_EMERG] = MAX6659_REG_LOCAL_EMERG, + [REMOTE_EMERG] = MAX6659_REG_REMOTE_EMERG, + [REMOTE2_CRIT] = LM90_REG_REMOTE_CRIT, + [REMOTE2_EMERG] = MAX6659_REG_REMOTE_EMERG, + [REMOTE_LOW] = LM90_REG_REMOTE_LOWH, + [REMOTE_HIGH] = LM90_REG_REMOTE_HIGHH, + [REMOTE2_LOW] = LM90_REG_REMOTE_LOWH, + [REMOTE2_HIGH] = LM90_REG_REMOTE_HIGHH, }; struct i2c_client *client = data->client; - struct reg *regp = ®[index]; + u8 regh = regs[index]; + u8 regl = 0; int err; - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index <= 2) + if (channel && (data->flags & LM90_HAVE_REM_LIMIT_EXT)) { + if (index == REMOTE_LOW || index == REMOTE2_LOW) + regl = LM90_REG_REMOTE_LOWL; + else if (index == REMOTE_HIGH || index == REMOTE2_HIGH) + regl = LM90_REG_REMOTE_HIGHL; + } + + /* +16 degrees offset for remote temperature on LM99 */ + if (data->kind == lm99 && channel) { + /* prevent integer underflow */ + val = max(val, -128000l); val -= 16000; + } - if (data->kind == adt7461 || data->kind == tmp451) - data->temp11[index] = temp_to_u16_adt7461(data, val); - else if (data->kind == max6646) - data->temp11[index] = temp_to_u8(val) << 8; - else if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - data->temp11[index] = temp_to_s16(val); - else - data->temp11[index] = temp_to_s8(val) << 8; + data->temp[index] = lm90_temp_to_reg(data->flags, val, + lm90_temp_get_resolution(data, index)); - lm90_select_remote_channel(data, index >= 3); - err = i2c_smbus_write_byte_data(client, regp->high, - data->temp11[index] >> 8); - if (err < 0) - return err; - if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - err = i2c_smbus_write_byte_data(client, regp->low, - data->temp11[index] & 0xff); + if (channel > 1) + lm90_select_remote_channel(data, true); + + err = lm90_write16(client, regh, regl, data->temp[index]); + + if (channel > 1) + lm90_select_remote_channel(data, false); - lm90_select_remote_channel(data, 0); return err; } -static int lm90_get_temp8(struct lm90_data *data, int index) +static int lm90_get_temphyst(struct lm90_data *data, int index, int channel) { - s8 temp8 = data->temp8[index]; - int temp; - - if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u8_adt7461(data, temp8); - else if (data->kind == max6646) - temp = temp_from_u8(temp8); - else - temp = temp_from_s8(temp8); - - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index == 3) - temp += 16000; + int temp = lm90_get_temp(data, index, channel); - return temp; + return temp - data->temp_hyst * 1000; } -static int lm90_set_temp8(struct lm90_data *data, int index, long val) +static int lm90_set_temphyst(struct lm90_data *data, long val) { - static const u8 reg[TEMP8_REG_NUM] = { - LM90_REG_W_LOCAL_LOW, - LM90_REG_W_LOCAL_HIGH, - LM90_REG_W_LOCAL_CRIT, - LM90_REG_W_REMOTE_CRIT, - MAX6659_REG_W_LOCAL_EMERG, - MAX6659_REG_W_REMOTE_EMERG, - LM90_REG_W_REMOTE_CRIT, - MAX6659_REG_W_REMOTE_EMERG, - }; - struct i2c_client *client = data->client; - int err; + int temp = lm90_get_temp(data, LOCAL_CRIT, 0); - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index == 3) - val -= 16000; + /* prevent integer overflow/underflow */ + val = clamp_val(val, -128000l, 255000l); + data->temp_hyst = clamp_val(DIV_ROUND_CLOSEST(temp - val, 1000), 0, 31); - if (data->kind == adt7461 || data->kind == tmp451) - data->temp8[index] = temp_to_u8_adt7461(data, val); - else if (data->kind == max6646) - data->temp8[index] = temp_to_u8(val); - else - data->temp8[index] = temp_to_s8(val); + return lm90_write_reg(data->client, LM90_REG_TCRIT_HYST, data->temp_hyst); +} - lm90_select_remote_channel(data, index >= 6); - err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]); - lm90_select_remote_channel(data, 0); +static int lm90_get_temp_offset(struct lm90_data *data, int index) +{ + int res = lm90_temp_get_resolution(data, index); - return err; + return lm90_temp_from_reg(0, data->temp[index], res); } -static int lm90_get_temphyst(struct lm90_data *data, int index) +static int lm90_set_temp_offset(struct lm90_data *data, int index, int channel, long val) { - int temp; + int err; - if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u8_adt7461(data, data->temp8[index]); - else if (data->kind == max6646) - temp = temp_from_u8(data->temp8[index]); - else - temp = temp_from_s8(data->temp8[index]); + val = lm90_temp_to_reg(0, val, lm90_temp_get_resolution(data, index)); - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index == 3) - temp += 16000; + /* For ADT7481 we can use the same registers for remote channel 1 and 2 */ + if (channel > 1) + lm90_select_remote_channel(data, true); - return temp - temp_from_s8(data->temp_hyst); -} + err = lm90_write16(data->client, LM90_REG_REMOTE_OFFSH, LM90_REG_REMOTE_OFFSL, val); -static int lm90_set_temphyst(struct lm90_data *data, long val) -{ - struct i2c_client *client = data->client; - int temp; - int err; + if (channel > 1) + lm90_select_remote_channel(data, false); - if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); - else if (data->kind == max6646) - temp = temp_from_u8(data->temp8[LOCAL_CRIT]); - else - temp = temp_from_s8(data->temp8[LOCAL_CRIT]); + if (err) + return err; - data->temp_hyst = hyst_to_reg(temp - val); - err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, - data->temp_hyst); - return err; + data->temp[index] = val; + + return 0; } -static const u8 lm90_temp_index[3] = { +static const u8 lm90_temp_index[MAX_CHANNELS] = { LOCAL_TEMP, REMOTE_TEMP, REMOTE2_TEMP }; -static const u8 lm90_temp_min_index[3] = { +static const u8 lm90_temp_min_index[MAX_CHANNELS] = { LOCAL_LOW, REMOTE_LOW, REMOTE2_LOW }; -static const u8 lm90_temp_max_index[3] = { +static const u8 lm90_temp_max_index[MAX_CHANNELS] = { LOCAL_HIGH, REMOTE_HIGH, REMOTE2_HIGH }; -static const u8 lm90_temp_crit_index[3] = { +static const u8 lm90_temp_crit_index[MAX_CHANNELS] = { LOCAL_CRIT, REMOTE_CRIT, REMOTE2_CRIT }; -static const u8 lm90_temp_emerg_index[3] = { +static const u8 lm90_temp_emerg_index[MAX_CHANNELS] = { LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG }; -static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 }; -static const u8 lm90_max_alarm_bits[3] = { 6, 4, 12 }; -static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 }; -static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 }; -static const u8 lm90_fault_bits[3] = { 0, 2, 10 }; +static const s8 lm90_temp_offset_index[MAX_CHANNELS] = { + -1, REMOTE_OFFSET, REMOTE2_OFFSET +}; + +static const u16 lm90_min_alarm_bits[MAX_CHANNELS] = { BIT(5), BIT(3), BIT(11) }; +static const u16 lm90_max_alarm_bits[MAX_CHANNELS] = { BIT(6), BIT(4), BIT(12) }; +static const u16 lm90_crit_alarm_bits[MAX_CHANNELS] = { BIT(0), BIT(1), BIT(9) }; +static const u16 lm90_crit_alarm_bits_swapped[MAX_CHANNELS] = { BIT(1), BIT(0), BIT(9) }; +static const u16 lm90_emergency_alarm_bits[MAX_CHANNELS] = { BIT(15), BIT(13), BIT(14) }; +static const u16 lm90_fault_bits[MAX_CHANNELS] = { BIT(0), BIT(2), BIT(10) }; static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) { struct lm90_data *data = dev_get_drvdata(dev); int err; + u16 bit; mutex_lock(&data->update_lock); err = lm90_update_device(dev); @@ -1163,53 +1519,57 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) switch (attr) { case hwmon_temp_input: - *val = lm90_get_temp11(data, lm90_temp_index[channel]); + *val = lm90_get_temp(data, lm90_temp_index[channel], channel); break; case hwmon_temp_min_alarm: - *val = (data->alarms >> lm90_min_alarm_bits[channel]) & 1; - break; case hwmon_temp_max_alarm: - *val = (data->alarms >> lm90_max_alarm_bits[channel]) & 1; - break; case hwmon_temp_crit_alarm: - *val = (data->alarms >> lm90_crit_alarm_bits[channel]) & 1; - break; case hwmon_temp_emergency_alarm: - *val = (data->alarms >> lm90_emergency_alarm_bits[channel]) & 1; - break; case hwmon_temp_fault: - *val = (data->alarms >> lm90_fault_bits[channel]) & 1; + switch (attr) { + case hwmon_temp_min_alarm: + bit = lm90_min_alarm_bits[channel]; + break; + case hwmon_temp_max_alarm: + bit = lm90_max_alarm_bits[channel]; + break; + case hwmon_temp_crit_alarm: + if (data->flags & LM90_HAVE_CRIT_ALRM_SWP) + bit = lm90_crit_alarm_bits_swapped[channel]; + else + bit = lm90_crit_alarm_bits[channel]; + break; + case hwmon_temp_emergency_alarm: + bit = lm90_emergency_alarm_bits[channel]; + break; + case hwmon_temp_fault: + bit = lm90_fault_bits[channel]; + break; + } + *val = !!(data->alarms & bit); + data->alarms &= ~bit; + data->alarms |= data->current_alarms; break; case hwmon_temp_min: - if (channel == 0) - *val = lm90_get_temp8(data, - lm90_temp_min_index[channel]); - else - *val = lm90_get_temp11(data, - lm90_temp_min_index[channel]); + *val = lm90_get_temp(data, lm90_temp_min_index[channel], channel); break; case hwmon_temp_max: - if (channel == 0) - *val = lm90_get_temp8(data, - lm90_temp_max_index[channel]); - else - *val = lm90_get_temp11(data, - lm90_temp_max_index[channel]); + *val = lm90_get_temp(data, lm90_temp_max_index[channel], channel); break; case hwmon_temp_crit: - *val = lm90_get_temp8(data, lm90_temp_crit_index[channel]); + *val = lm90_get_temp(data, lm90_temp_crit_index[channel], channel); break; case hwmon_temp_crit_hyst: - *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel]); + *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel], channel); break; case hwmon_temp_emergency: - *val = lm90_get_temp8(data, lm90_temp_emerg_index[channel]); + *val = lm90_get_temp(data, lm90_temp_emerg_index[channel], channel); break; case hwmon_temp_emergency_hyst: - *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel]); + *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel], channel); break; case hwmon_temp_offset: - *val = lm90_get_temp11(data, REMOTE_OFFSET); + *val = lm90_get_temp_offset(data, lm90_temp_offset_index[channel]); break; default: return -EOPNOTSUPP; @@ -1230,36 +1590,27 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) switch (attr) { case hwmon_temp_min: - if (channel == 0) - err = lm90_set_temp8(data, - lm90_temp_min_index[channel], - val); - else - err = lm90_set_temp11(data, - lm90_temp_min_index[channel], - val); + err = lm90_set_temp(data, lm90_temp_min_index[channel], + channel, val); break; case hwmon_temp_max: - if (channel == 0) - err = lm90_set_temp8(data, - lm90_temp_max_index[channel], - val); - else - err = lm90_set_temp11(data, - lm90_temp_max_index[channel], - val); + err = lm90_set_temp(data, lm90_temp_max_index[channel], + channel, val); break; case hwmon_temp_crit: - err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val); + err = lm90_set_temp(data, lm90_temp_crit_index[channel], + channel, val); break; case hwmon_temp_crit_hyst: err = lm90_set_temphyst(data, val); break; case hwmon_temp_emergency: - err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val); + err = lm90_set_temp(data, lm90_temp_emerg_index[channel], + channel, val); break; case hwmon_temp_offset: - err = lm90_set_temp11(data, REMOTE_OFFSET, val); + err = lm90_set_temp_offset(data, lm90_temp_offset_index[channel], + channel, val); break; default: err = -EOPNOTSUPP; @@ -1281,6 +1632,7 @@ static umode_t lm90_temp_is_visible(const void *data, u32 attr, int channel) case hwmon_temp_emergency_alarm: case hwmon_temp_emergency_hyst: case hwmon_temp_fault: + case hwmon_temp_label: return 0444; case hwmon_temp_min: case hwmon_temp_max: @@ -1315,6 +1667,28 @@ static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val) case hwmon_chip_alarms: *val = data->alarms; break; + case hwmon_chip_temp_samples: + if (data->faultqueue_mask) { + *val = (data->config & data->faultqueue_mask) ? + data->faultqueue_depth : 1; + } else { + switch (data->conalert & 0x0e) { + case 0x0: + default: + *val = 1; + break; + case 0x2: + *val = 2; + break; + case 0x6: + *val = 3; + break; + case 0xe: + *val = 4; + break; + } + } + break; default: return -EOPNOTSUPP; } @@ -1339,6 +1713,9 @@ static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val) err = lm90_set_convrate(client, data, clamp_val(val, 0, 100000)); break; + case hwmon_chip_temp_samples: + err = lm90_set_faultqueue(client, data, clamp_val(val, 1, 4)); + break; default: err = -EOPNOTSUPP; break; @@ -1353,6 +1730,7 @@ static umode_t lm90_chip_is_visible(const void *data, u32 attr, int channel) { switch (attr) { case hwmon_chip_update_interval: + case hwmon_chip_temp_samples: return 0644; case hwmon_chip_alarms: return 0444; @@ -1374,6 +1752,16 @@ static int lm90_read(struct device *dev, enum hwmon_sensor_types type, } } +static int lm90_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct lm90_data *data = dev_get_drvdata(dev); + + *str = data->channel_label[channel]; + + return 0; +} + static int lm90_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { @@ -1400,126 +1788,359 @@ static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type, } } -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm90_detect(struct i2c_client *client, - struct i2c_board_info *info) +static const char *lm90_detect_lm84(struct i2c_client *client) { - struct i2c_adapter *adapter = client->adapter; + static const u8 regs[] = { + LM90_REG_STATUS, LM90_REG_LOCAL_TEMP, LM90_REG_LOCAL_HIGH, + LM90_REG_REMOTE_TEMPH, LM90_REG_REMOTE_HIGHH + }; + int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); + int reg1, reg2, reg3, reg4; + bool nonzero = false; + u8 ff = 0xff; + int i; + + if (status < 0 || (status & 0xab)) + return NULL; + + /* + * For LM84, undefined registers return the most recent value. + * Repeat several times, each time checking against a different + * (presumably) existing register. + */ + for (i = 0; i < ARRAY_SIZE(regs); i++) { + reg1 = i2c_smbus_read_byte_data(client, regs[i]); + reg2 = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL); + reg3 = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW); + reg4 = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH); + + if (reg1 < 0) + return NULL; + + /* If any register has a different value, this is not an LM84 */ + if (reg2 != reg1 || reg3 != reg1 || reg4 != reg1) + return NULL; + + nonzero |= reg1 || reg2 || reg3 || reg4; + ff &= reg1; + } + /* + * If all registers always returned 0 or 0xff, all bets are off, + * and we can not make any predictions about the chip type. + */ + return nonzero && ff != 0xff ? "lm84" : NULL; +} + +static const char *lm90_detect_max1617(struct i2c_client *client, int config1) +{ + int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); + int llo, rlo, lhi, rhi; + + if (status < 0 || (status & 0x03)) + return NULL; + + if (config1 & 0x3f) + return NULL; + + /* + * Fail if unsupported registers return anything but 0xff. + * The calling code already checked man_id and chip_id. + * A byte read operation repeats the most recent read operation + * and should also return 0xff. + */ + if (i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL) != 0xff || + i2c_smbus_read_byte_data(client, MAX6657_REG_LOCAL_TEMPL) != 0xff || + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWL) != 0xff || + i2c_smbus_read_byte(client) != 0xff) + return NULL; + + llo = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW); + rlo = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH); + + lhi = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_HIGH); + rhi = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_HIGHH); + + if (llo < 0 || rlo < 0) + return NULL; + + /* + * A byte read operation repeats the most recent read and should + * return the same value. + */ + if (i2c_smbus_read_byte(client) != rhi) + return NULL; + + /* + * The following two checks are marginal since the checked values + * are strictly speaking valid. + */ + + /* fail for negative high limits; this also catches read errors */ + if ((s8)lhi < 0 || (s8)rhi < 0) + return NULL; + + /* fail if low limits are larger than or equal to high limits */ + if ((s8)llo >= lhi || (s8)rlo >= rhi) + return NULL; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + /* + * Word read operations return 0xff in second byte + */ + if (i2c_smbus_read_word_data(client, LM90_REG_REMOTE_TEMPL) != + 0xffff) + return NULL; + if (i2c_smbus_read_word_data(client, LM90_REG_CONFIG1) != + (config1 | 0xff00)) + return NULL; + if (i2c_smbus_read_word_data(client, LM90_REG_LOCAL_HIGH) != + (lhi | 0xff00)) + return NULL; + } + + return "max1617"; +} + +static const char *lm90_detect_national(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); int address = client->addr; const char *name = NULL; - int man_id, chip_id, config1, config2, convrate; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; + if (config2 < 0) + return NULL; - /* detection and identification */ - man_id = i2c_smbus_read_byte_data(client, LM90_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, LM90_REG_R_CHIP_ID); - config1 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1); - convrate = i2c_smbus_read_byte_data(client, LM90_REG_R_CONVRATE); - if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0) - return -ENODEV; + if ((config1 & 0x2a) || (config2 & 0xf8) || convrate > 0x09) + return NULL; - if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) { - config2 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG2); - if (config2 < 0) - return -ENODEV; - } else - config2 = 0; /* Make compiler happy */ - - if ((address == 0x4C || address == 0x4D) - && man_id == 0x01) { /* National Semiconductor */ - if ((config1 & 0x2A) == 0x00 - && (config2 & 0xF8) == 0x00 - && convrate <= 0x09) { - if (address == 0x4C - && (chip_id & 0xF0) == 0x20) { /* LM90 */ - name = "lm90"; - } else - if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */ - name = "lm99"; - dev_info(&adapter->dev, - "Assuming LM99 chip at 0x%02x\n", - address); - dev_info(&adapter->dev, - "If it is an LM89, instantiate it " - "with the new_device sysfs " - "interface\n"); - } else - if (address == 0x4C - && (chip_id & 0xF0) == 0x10) { /* LM86 */ - name = "lm86"; - } - } - } else - if ((address == 0x4C || address == 0x4D) - && man_id == 0x41) { /* Analog Devices */ - if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ - && (config1 & 0x3F) == 0x00 - && convrate <= 0x0A) { + if (address != 0x4c && address != 0x4d) + return NULL; + + switch (chip_id & 0xf0) { + case 0x10: /* LM86 */ + if (address == 0x4c) + name = "lm86"; + break; + case 0x20: /* LM90 */ + if (address == 0x4c) + name = "lm90"; + break; + case 0x30: /* LM89/LM99 */ + name = "lm99"; /* detect LM89 as LM99 */ + break; + default: + break; + } + + return name; +} + +static const char *lm90_detect_on(struct i2c_client *client, int chip_id, int config1, + int convrate) +{ + int address = client->addr; + const char *name = NULL; + + switch (chip_id) { + case 0xca: /* NCT218 */ + if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "nct218"; + break; + default: + break; + } + return name; +} + +static const char *lm90_detect_analog(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) +{ + int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); + int config2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CONFIG2); + int man_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_MAN_ID); + int chip_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CHIP_ID); + int address = client->addr; + const char *name = NULL; + + if (status < 0 || config2 < 0 || man_id2 < 0 || chip_id2 < 0) + return NULL; + + /* + * The following chips should be detected by this function. Known + * register values are listed. Registers 0x3d .. 0x3e are undocumented + * for most of the chips, yet appear to return a well defined value. + * Register 0xff is undocumented for some of the chips. Register 0x3f + * is undocumented for all chips, but also returns a well defined value. + * Values are as reported from real chips unless mentioned otherwise. + * The code below checks values for registers 0x3d, 0x3e, and 0xff, + * but not for register 0x3f. + * + * Chip Register + * 3d 3e 3f fe ff Notes + * ---------------------------------------------------------- + * adm1020 00 00 00 41 39 + * adm1021 00 00 00 41 03 + * adm1021a 00 00 00 41 3c + * adm1023 00 00 00 41 3c same as adm1021a + * adm1032 00 00 00 41 42 + * + * adt7421 21 41 04 41 04 + * adt7461 00 00 00 41 51 + * adt7461a 61 41 05 41 57 + * adt7481 81 41 02 41 62 + * adt7482 - - - 41 65 datasheet + * 82 41 05 41 75 real chip + * adt7483 83 41 04 41 94 + * + * nct72 61 41 07 41 55 + * nct210 00 00 00 41 3f + * nct214 61 41 08 41 5a + * nct1008 - - - 41 57 datasheet rev. 3 + * 61 41 06 41 54 real chip + * + * nvt210 - - - 41 - datasheet + * nvt211 - - - 41 - datasheet + */ + switch (chip_id) { + case 0x00 ... 0x03: /* ADM1021 */ + case 0x05 ... 0x0f: + if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "adm1021"; + break; + case 0x04: /* ADT7421 (undocumented) */ + if (man_id2 == 0x41 && chip_id2 == 0x21 && + (address == 0x4c || address == 0x4d) && + (config1 & 0x0b) == 0x08 && convrate <= 0x0a) + name = "adt7421"; + break; + case 0x30 ... 0x38: /* ADM1021A, ADM1023 */ + case 0x3a ... 0x3e: + /* + * ADM1021A and compatible chips will be mis-detected as + * ADM1023. Chips labeled 'ADM1021A' and 'ADM1023' were both + * found to have a Chip ID of 0x3c. + * ADM1021A does not officially support low byte registers + * (0x12 .. 0x14), but a chip labeled ADM1021A does support it. + * Official support for the temperature offset high byte + * register (0x11) was added to revision F of the ADM1021A + * datasheet. + * It is currently unknown if there is a means to distinguish + * ADM1021A from ADM1023, and/or if revisions of ADM1021A exist + * which differ in functionality from ADM1023. + */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "adm1023"; + break; + case 0x39: /* ADM1020 (undocumented) */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && + (address == 0x4c || address == 0x4d || address == 0x4e) && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "adm1020"; + break; + case 0x3f: /* NCT210 */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "nct210"; + break; + case 0x40 ... 0x4f: /* ADM1032 */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && + convrate <= 0x0a) name = "adm1032"; - /* - * The ADM1032 supports PEC, but only if combined - * transactions are not used. - */ - if (i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_BYTE)) - info->flags |= I2C_CLIENT_PEC; - } else - if (chip_id == 0x51 /* ADT7461 */ - && (config1 & 0x1B) == 0x00 - && convrate <= 0x0A) { + break; + case 0x51: /* ADT7461 */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) name = "adt7461"; - } else - if (chip_id == 0x57 /* ADT7461A, NCT1008 */ - && (config1 & 0x1B) == 0x00 - && convrate <= 0x0A) { + break; + case 0x54: /* NCT1008 */ + if (man_id2 == 0x41 && chip_id2 == 0x61 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "nct1008"; + break; + case 0x55: /* NCT72 */ + if (man_id2 == 0x41 && chip_id2 == 0x61 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "nct72"; + break; + case 0x57: /* ADT7461A, NCT1008 (datasheet rev. 3) */ + if (man_id2 == 0x41 && chip_id2 == 0x61 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) name = "adt7461a"; + break; + case 0x5a: /* NCT214 */ + if (man_id2 == 0x41 && chip_id2 == 0x61 && + common_address && !(config1 & 0x1b) && convrate <= 0x0a) + name = "nct214"; + break; + case 0x62: /* ADT7481, undocumented */ + if (man_id2 == 0x41 && chip_id2 == 0x81 && + (address == 0x4b || address == 0x4c) && !(config1 & 0x10) && + !(config2 & 0x7f) && (convrate & 0x0f) <= 0x0b) { + name = "adt7481"; } - } else - if (man_id == 0x4D) { /* Maxim */ - int emerg, emerg2, status2; + break; + case 0x65: /* ADT7482, datasheet */ + case 0x75: /* ADT7482, real chip */ + if (man_id2 == 0x41 && chip_id2 == 0x82 && + address == 0x4c && !(config1 & 0x10) && !(config2 & 0x7f) && + convrate <= 0x0a) + name = "adt7482"; + break; + case 0x94: /* ADT7483 */ + if (man_id2 == 0x41 && chip_id2 == 0x83 && + common_address && + ((address >= 0x18 && address <= 0x1a) || + (address >= 0x29 && address <= 0x2b) || + (address >= 0x4c && address <= 0x4e)) && + !(config1 & 0x10) && !(config2 & 0x7f) && convrate <= 0x0a) + name = "adt7483a"; + break; + default: + break; + } + + return name; +} + +static const char *lm90_detect_maxim(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) +{ + int man_id, emerg, emerg2, status2; + int address = client->addr; + const char *name = NULL; + + switch (chip_id) { + case 0x01: + if (!common_address) + break; /* - * We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read - * LM90_REG_R_MAN_ID in between. If MAX6659_REG_R_REMOTE_EMERG + * We read MAX6659_REG_REMOTE_EMERG twice, and re-read + * LM90_REG_MAN_ID in between. If MAX6659_REG_REMOTE_EMERG * exists, both readings will reflect the same value. Otherwise, * the readings will be different. */ emerg = i2c_smbus_read_byte_data(client, - MAX6659_REG_R_REMOTE_EMERG); + MAX6659_REG_REMOTE_EMERG); man_id = i2c_smbus_read_byte_data(client, - LM90_REG_R_MAN_ID); + LM90_REG_MAN_ID); emerg2 = i2c_smbus_read_byte_data(client, - MAX6659_REG_R_REMOTE_EMERG); + MAX6659_REG_REMOTE_EMERG); status2 = i2c_smbus_read_byte_data(client, - MAX6696_REG_R_STATUS2); + MAX6696_REG_STATUS2); if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0) - return -ENODEV; + return NULL; /* - * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id - * register. Reading from that address will return the last - * read value, which in our case is those of the man_id - * register. Likewise, the config1 register seems to lack a - * low nibble, so the value will be those of the previous - * read, so in our case those of the man_id register. - * MAX6659 has a third set of upper temperature limit registers. - * Those registers also return values on MAX6657 and MAX6658, - * thus the only way to detect MAX6659 is by its address. - * For this reason it will be mis-detected as MAX6657 if its - * address is 0x4C. - */ - if (chip_id == man_id - && (address == 0x4C || address == 0x4D || address == 0x4E) - && (config1 & 0x1F) == (man_id & 0x0F) - && convrate <= 0x09) { - if (address == 0x4C) - name = "max6657"; - else - name = "max6659"; - } else - /* * Even though MAX6695 and MAX6696 do not have a chip ID * register, reading it returns 0x01. Bit 4 of the config1 * register is unused and should return zero when read. Bit 0 of @@ -1530,87 +2151,403 @@ static int lm90_detect(struct i2c_client *client, * limit registers. We can detect those chips by checking if * one of those registers exists. */ - if (chip_id == 0x01 - && (config1 & 0x10) == 0x00 - && (status2 & 0x01) == 0x00 - && emerg == emerg2 - && convrate <= 0x07) { + if (!(config1 & 0x10) && !(status2 & 0x01) && emerg == emerg2 && + convrate <= 0x07) name = "max6696"; - } else /* * The chip_id register of the MAX6680 and MAX6681 holds the * revision of the chip. The lowest bit of the config1 register * is unused and should return zero when read, so should the - * second to last bit of config1 (software reset). + * second to last bit of config1 (software reset). Register + * address 0x12 (LM90_REG_REMOTE_OFFSL) exists for this chip and + * should differ from emerg2, and emerg2 should match man_id + * since it does not exist. */ - if (chip_id == 0x01 - && (config1 & 0x03) == 0x00 - && convrate <= 0x07) { + else if (!(config1 & 0x03) && convrate <= 0x07 && + emerg2 == man_id && emerg2 != status2) name = "max6680"; - } else + /* + * MAX1617A does not have any extended registers (register + * address 0x10 or higher) except for manufacturer and + * device ID registers. Unlike other chips of this series, + * unsupported registers were observed to return a fixed value + * of 0x01. + * Note: Multiple chips with different markings labeled as + * "MAX1617" (no "A") were observed to report manufacturer ID + * 0x4d and device ID 0x01. It is unknown if other variants of + * MAX1617/MAX617A with different behavior exist. The detection + * code below works for those chips. + */ + else if (!(config1 & 0x03f) && convrate <= 0x07 && + emerg == 0x01 && emerg2 == 0x01 && status2 == 0x01) + name = "max1617"; + break; + case 0x08: + /* + * The chip_id of the MAX6654 holds the revision of the chip. + * The lowest 3 bits of the config1 register are unused and + * should return zero when read. + */ + if (common_address && !(config1 & 0x07) && convrate <= 0x07) + name = "max6654"; + break; + case 0x09: + /* + * The chip_id of the MAX6690 holds the revision of the chip. + * The lowest 3 bits of the config1 register are unused and + * should return zero when read. + * Note that MAX6654 and MAX6690 are practically the same chips. + * The only diference is the rated accuracy. Rev. 1 of the + * MAX6690 datasheet lists a chip ID of 0x08, and a chip labeled + * MAX6654 was observed to have a chip ID of 0x09. + */ + if (common_address && !(config1 & 0x07) && convrate <= 0x07) + name = "max6690"; + break; + case 0x4d: + /* + * MAX6642, MAX6657, MAX6658 and MAX6659 do NOT have a chip_id + * register. Reading from that address will return the last + * read value, which in our case is those of the man_id + * register, or 0x4d. + * MAX6642 does not have a conversion rate register, nor low + * limit registers. Reading from those registers returns the + * last read value. + * + * For MAX6657, MAX6658 and MAX6659, the config1 register lacks + * a low nibble, so the value will be those of the previous + * read, so in our case again those of the man_id register. + * MAX6659 has a third set of upper temperature limit registers. + * Those registers also return values on MAX6657 and MAX6658, + * thus the only way to detect MAX6659 is by its address. + * For this reason it will be mis-detected as MAX6657 if its + * address is 0x4c. + */ + if (address >= 0x48 && address <= 0x4f && config1 == convrate && + !(config1 & 0x0f)) { + int regval; + + /* + * We know that this is not a MAX6657/58/59 because its + * configuration register has the wrong value and it does + * not appear to have a conversion rate register. + */ + + /* re-read manufacturer ID to have a good baseline */ + if (i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID) != 0x4d) + break; + + /* check various non-existing registers */ + if (i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE) != 0x4d || + i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW) != 0x4d || + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH) != 0x4d) + break; + + /* check for unused status register bits */ + regval = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); + if (regval < 0 || (regval & 0x2b)) + break; + + /* re-check unsupported registers */ + if (i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE) != regval || + i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW) != regval || + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH) != regval) + break; + + name = "max6642"; + } else if ((address == 0x4c || address == 0x4d || address == 0x4e) && + (config1 & 0x1f) == 0x0d && convrate <= 0x09) { + if (address == 0x4c) + name = "max6657"; + else + name = "max6659"; + } + break; + case 0x59: /* * The chip_id register of the MAX6646/6647/6649 holds the * revision of the chip. The lowest 6 bits of the config1 * register are unused and should return zero when read. + * The I2C address of MAX6648/6692 is fixed at 0x4c. + * MAX6646 is at address 0x4d, MAX6647 is at address 0x4e, + * and MAX6649 is at address 0x4c. A slight difference between + * the two sets of chips is that the remote temperature register + * reports different values if the DXP pin is open or shorted. + * We can use that information to help distinguish between the + * chips. MAX6648 will be mis-detected as MAX6649 if the remote + * diode is connected, but there isn't really anything we can + * do about that. */ - if (chip_id == 0x59 - && (config1 & 0x3f) == 0x00 - && convrate <= 0x07) { - name = "max6646"; - } - } else - if (address == 0x4C - && man_id == 0x5C) { /* Winbond/Nuvoton */ - if ((config1 & 0x2A) == 0x00 - && (config2 & 0xF8) == 0x00) { - if (chip_id == 0x01 /* W83L771W/G */ - && convrate <= 0x09) { - name = "w83l771"; - } else - if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ - && convrate <= 0x08) { - name = "w83l771"; + if (!(config1 & 0x3f) && convrate <= 0x07) { + int temp; + + switch (address) { + case 0x4c: + /* + * MAX6649 reports an external temperature + * value of 0xff if DXP is open or shorted. + * MAX6648 reports 0x80 in that case. + */ + temp = i2c_smbus_read_byte_data(client, + LM90_REG_REMOTE_TEMPH); + if (temp == 0x80) + name = "max6648"; + else + name = "max6649"; + break; + case 0x4d: + name = "max6646"; + break; + case 0x4e: + name = "max6647"; + break; + default: + break; } } - } else - if (address >= 0x48 && address <= 0x4F - && man_id == 0xA1) { /* NXP Semiconductor/Philips */ - if (chip_id == 0x00 - && (config1 & 0x2A) == 0x00 - && (config2 & 0xFE) == 0x00 - && convrate <= 0x09) { - name = "sa56004"; + break; + default: + break; + } + + return name; +} + +static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); + int address = client->addr; + const char *name = NULL; + + if (config2 < 0) + return NULL; + + if (address == 0x4c && !(config1 & 0x2a) && !(config2 & 0xf8)) { + if (chip_id == 0x01 && convrate <= 0x09) { + /* W83L771W/G */ + name = "w83l771"; + } else if ((chip_id & 0xfe) == 0x10 && convrate <= 0x08) { + /* W83L771AWG/ASG */ + name = "w83l771"; } - } else - if ((address == 0x4C || address == 0x4D) - && man_id == 0x47) { /* GMT */ - if (chip_id == 0x01 /* G781 */ - && (config1 & 0x3F) == 0x00 - && convrate <= 0x08) - name = "g781"; - } else - if (address == 0x4C - && man_id == 0x55) { /* Texas Instruments */ - int local_ext; + } + return name; +} + +static const char *lm90_detect_nxp(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) +{ + int address = client->addr; + const char *name = NULL; + int config2; + + switch (chip_id) { + case 0x00: + config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); + if (config2 < 0) + return NULL; + if (address >= 0x48 && address <= 0x4f && + !(config1 & 0x2a) && !(config2 & 0xfe) && convrate <= 0x09) + name = "sa56004"; + break; + case 0x80: + if (common_address && !(config1 & 0x3f) && convrate <= 0x07) + name = "ne1618"; + break; + default: + break; + } + return name; +} + +static const char *lm90_detect_gmt(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int address = client->addr; + + /* + * According to the datasheet, G781 is supposed to be at I2C Address + * 0x4c and have a chip ID of 0x01. G781-1 is supposed to be at I2C + * address 0x4d and have a chip ID of 0x03. However, when support + * for G781 was added, chips at 0x4c and 0x4d were found to have a + * chip ID of 0x01. A G781-1 at I2C address 0x4d was now found with + * chip ID 0x03. + * To avoid detection failures, accept chip ID 0x01 and 0x03 at both + * addresses. + * G784 reports manufacturer ID 0x47 and chip ID 0x01. A public + * datasheet is not available. Extensive testing suggests that + * the chip appears to be fully compatible with G781. + * Available register dumps show that G751 also reports manufacturer + * ID 0x47 and chip ID 0x01 even though that chip does not officially + * support those registers. This makes chip detection somewhat + * vulnerable. To improve detection quality, read the offset low byte + * and alert fault queue registers and verify that only expected bits + * are set. + */ + if ((chip_id == 0x01 || chip_id == 0x03) && + (address == 0x4c || address == 0x4d) && + !(config1 & 0x3f) && convrate <= 0x08) { + int reg; + + reg = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_OFFSL); + if (reg < 0 || reg & 0x1f) + return NULL; + reg = i2c_smbus_read_byte_data(client, TMP451_REG_CONALERT); + if (reg < 0 || reg & 0xf1) + return NULL; + + return "g781"; + } + + return NULL; +} + +static const char *lm90_detect_ti49(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) +{ + if (common_address && chip_id == 0x00 && !(config1 & 0x3f) && !(convrate & 0xf8)) { + /* THMC10: Unsupported registers return 0xff */ + if (i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL) == 0xff && + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_CRIT) == 0xff) + return "thmc10"; + } + return NULL; +} + +static const char *lm90_detect_ti(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int address = client->addr; + const char *name = NULL; + + if (chip_id == 0x00 && !(config1 & 0x1b) && convrate <= 0x09) { + int local_ext, conalert, chen, dfc; local_ext = i2c_smbus_read_byte_data(client, - TMP451_REG_R_LOCAL_TEMPL); + TMP451_REG_LOCAL_TEMPL); + conalert = i2c_smbus_read_byte_data(client, + TMP451_REG_CONALERT); + chen = i2c_smbus_read_byte_data(client, TMP461_REG_CHEN); + dfc = i2c_smbus_read_byte_data(client, TMP461_REG_DFC); + + if (!(local_ext & 0x0f) && (conalert & 0xf1) == 0x01 && + (chen & 0xfc) == 0x00 && (dfc & 0xfc) == 0x00) { + if (address == 0x4c && !(chen & 0x03)) + name = "tmp451"; + else if (address >= 0x48 && address <= 0x4f) + name = "tmp461"; + } + } + + return name; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int man_id, chip_id, config1, convrate, lhigh; + const char *name = NULL; + int address = client->addr; + bool common_address = + (address >= 0x18 && address <= 0x1a) || + (address >= 0x29 && address <= 0x2b) || + (address >= 0x4c && address <= 0x4e); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* + * Get well defined register value for chips with neither man_id nor + * chip_id registers. + */ + lhigh = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_HIGH); - if (chip_id == 0x00 /* TMP451 */ - && (config1 & 0x1B) == 0x00 - && convrate <= 0x09 - && (local_ext & 0x0F) == 0x00) - name = "tmp451"; + /* detection and identification */ + man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID); + config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1); + convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE); + if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0 || lhigh < 0) + return -ENODEV; + + /* Bail out immediately if all register report the same value */ + if (lhigh == man_id && lhigh == chip_id && lhigh == config1 && lhigh == convrate) + return -ENODEV; + + /* + * If reading man_id and chip_id both return the same value as lhigh, + * the chip may not support those registers and return the most recent read + * value. Check again with a different register and handle accordingly. + */ + if (man_id == lhigh && chip_id == lhigh) { + convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE); + man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID); + if (convrate < 0 || man_id < 0 || chip_id < 0) + return -ENODEV; + if (man_id == convrate && chip_id == convrate) + man_id = -1; + } + switch (man_id) { + case -1: /* Chip does not support man_id / chip_id */ + if (common_address && !convrate && !(config1 & 0x7f)) + name = lm90_detect_lm84(client); + break; + case 0x01: /* National Semiconductor */ + name = lm90_detect_national(client, chip_id, config1, convrate); + break; + case 0x1a: /* ON */ + name = lm90_detect_on(client, chip_id, config1, convrate); + break; + case 0x23: /* Genesys Logic */ + if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "gl523sm"; + break; + case 0x41: /* Analog Devices */ + name = lm90_detect_analog(client, common_address, chip_id, config1, + convrate); + break; + case 0x47: /* GMT */ + name = lm90_detect_gmt(client, chip_id, config1, convrate); + break; + case 0x49: /* TI */ + name = lm90_detect_ti49(client, common_address, chip_id, config1, convrate); + break; + case 0x4d: /* Maxim Integrated */ + name = lm90_detect_maxim(client, common_address, chip_id, + config1, convrate); + break; + case 0x54: /* ON MC1066, Microchip TC1068, TCM1617 (originally TelCom) */ + if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "mc1066"; + break; + case 0x55: /* TI */ + name = lm90_detect_ti(client, chip_id, config1, convrate); + break; + case 0x5c: /* Winbond/Nuvoton */ + name = lm90_detect_nuvoton(client, chip_id, config1, convrate); + break; + case 0xa1: /* NXP Semiconductor/Philips */ + name = lm90_detect_nxp(client, common_address, chip_id, config1, convrate); + break; + case 0xff: /* MAX1617, G767, NE1617 */ + if (common_address && chip_id == 0xff && convrate < 8) + name = lm90_detect_max1617(client, config1); + break; + default: + break; } - if (!name) { /* identification failed */ + if (!name) { /* identification failed */ dev_dbg(&adapter->dev, - "Unsupported chip at 0x%02x (man_id=0x%02X, " - "chip_id=0x%02X)\n", address, man_id, chip_id); + "Unsupported chip at 0x%02x (man_id=0x%02X, chip_id=0x%02X)\n", + client->addr, man_id, chip_id); return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -1620,106 +2557,102 @@ static void lm90_restore_conf(void *_data) struct lm90_data *data = _data; struct i2c_client *client = data->client; + cancel_delayed_work_sync(&data->alert_work); + cancel_work_sync(&data->report_work); + /* Restore initial configuration */ - lm90_write_convrate(data, data->convrate_orig); - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, - data->config_orig); + if (data->flags & LM90_HAVE_CONVRATE) + lm90_write_convrate(data, data->convrate_orig); + lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig); } static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) { + struct device_node *np = client->dev.of_node; int config, convrate; - convrate = lm90_read_reg(client, LM90_REG_R_CONVRATE); - if (convrate < 0) - return convrate; - data->convrate_orig = convrate; + if (data->flags & LM90_HAVE_CONVRATE) { + convrate = lm90_read_reg(client, LM90_REG_CONVRATE); + if (convrate < 0) + return convrate; + data->convrate_orig = convrate; + lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ + } else { + data->update_interval = 500; + } /* * Start the conversions. */ - config = lm90_read_reg(client, LM90_REG_R_CONFIG1); + config = lm90_read_reg(client, LM90_REG_CONFIG1); if (config < 0) return config; data->config_orig = config; data->config = config; - lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ - /* Check Temperature Range Select */ - if (data->kind == adt7461 || data->kind == tmp451) { - if (config & 0x04) - data->flags |= LM90_FLAG_ADT7461_EXT; + if (data->flags & LM90_HAVE_EXTENDED_TEMP) { + if (of_property_read_bool(np, "ti,extended-range-enable")) + config |= 0x04; + if (!(config & 0x04)) + data->flags &= ~LM90_HAVE_EXTENDED_TEMP; } /* * Put MAX6680/MAX8881 into extended resolution (bit 0x10, * 0.125 degree resolution) and range (0x08, extend range * to -64 degree) mode for the remote temperature sensor. + * Note that expeciments with an actual chip do not show a difference + * if bit 3 is set or not. */ if (data->kind == max6680) config |= 0x18; /* - * Select external channel 0 for max6695/96 + * Put MAX6654 into extended range (0x20, extend minimum range from + * 0 degrees to -64 degrees). Note that extended resolution is not + * possible on the MAX6654 unless conversion rate is set to 1 Hz or + * slower, which is intentionally not done by default. + */ + if (data->kind == max6654) + config |= 0x20; + + /* + * Select external channel 0 for devices with three sensors */ - if (data->kind == max6696) + if (data->flags & LM90_HAVE_TEMP3) config &= ~0x08; + /* + * Interrupt is enabled by default on reset, but it may be disabled + * by bootloader, unmask it. + */ + if (client->irq) + config &= ~0x80; + config &= 0xBF; /* run */ lm90_update_confreg(data, config); return devm_add_action_or_reset(&client->dev, lm90_restore_conf, data); } -static bool lm90_is_tripped(struct i2c_client *client, u16 *status) +static bool lm90_is_tripped(struct i2c_client *client) { struct lm90_data *data = i2c_get_clientdata(client); - int st, st2 = 0; - - st = lm90_read_reg(client, LM90_REG_R_STATUS); - if (st < 0) - return false; - - if (data->kind == max6696) { - st2 = lm90_read_reg(client, MAX6696_REG_R_STATUS2); - if (st2 < 0) - return false; - } + int ret; - *status = st | (st2 << 8); - - if ((st & 0x7f) == 0 && (st2 & 0xfe) == 0) + ret = lm90_update_alarms(data, true); + if (ret < 0) return false; - if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) || - (st2 & MAX6696_STATUS2_LOT2)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 1); - if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) || - (st2 & MAX6696_STATUS2_ROT2)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 2); - if (st & LM90_STATUS_ROPEN) - dev_warn(&client->dev, - "temp%d diode open, please check!\n", 2); - if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH | - MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 3); - if (st2 & MAX6696_STATUS2_R2OPEN) - dev_warn(&client->dev, - "temp%d diode open, please check!\n", 3); - - return true; + return !!data->current_alarms; } static irqreturn_t lm90_irq_thread(int irq, void *dev_id) { struct i2c_client *client = dev_id; - u16 status; - if (lm90_is_tripped(client, &status)) + if (lm90_is_tripped(client)) return IRQ_HANDLED; else return IRQ_NONE; @@ -1735,15 +2668,83 @@ 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) +{ + u32 id; + s32 val; + int err; + struct device *dev = &client->dev; + + err = of_property_read_u32(child, "reg", &id); + if (err) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return err; + } + + if (id >= MAX_CHANNELS) { + dev_err(dev, "invalid reg property value %d in %pOFn\n", id, child); + return -EINVAL; + } + + err = of_property_read_string(child, "label", &data->channel_label[id]); + if (err == -ENODATA || err == -EILSEQ) { + dev_err(dev, "invalid label property in %pOFn\n", child); + return err; + } + + if (data->channel_label[id]) + data->channel_config[id] |= HWMON_T_LABEL; + + err = of_property_read_s32(child, "temperature-offset-millicelsius", &val); + if (!err) { + if (id == 0) { + dev_err(dev, "temperature-offset-millicelsius can't be set for internal channel\n"); + return -EINVAL; + } + + err = lm90_set_temp_offset(data, lm90_temp_offset_index[id], id, val); + if (err) { + dev_err(dev, "can't set temperature offset %d for channel %d (%d)\n", + val, id, err); + return err; + } + } + + return 0; +} + +static int lm90_parse_dt_channel_info(struct i2c_client *client, + struct lm90_data *data) +{ + int err; + struct device_node *child; + struct device *dev = &client->dev; + const struct device_node *np = dev->of_node; + + for_each_child_of_node(np, child) { + if (strcmp(child->name, "channel")) + continue; + + err = lm90_probe_channel_from_dt(client, child, data); + if (err) { + of_node_put(child); + return err; + } + } + + return 0; +} static const struct hwmon_ops lm90_ops = { .is_visible = lm90_is_visible, .read = lm90_read, + .read_string = lm90_read_string, .write = lm90_write, }; -static int lm90_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lm90_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct i2c_adapter *adapter = client->adapter; @@ -1774,43 +2775,68 @@ static int lm90_probe(struct i2c_client *client, data->client = client; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); + INIT_DELAYED_WORK(&data->alert_work, lm90_alert_work); + INIT_WORK(&data->report_work, lm90_report_alarms); /* Set the device type */ if (client->dev.of_node) data->kind = (enum chips)of_device_get_match_data(&client->dev); else - data->kind = id->driver_data; - if (data->kind == adm1032) { - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - client->flags &= ~I2C_CLIENT_PEC; - } + data->kind = i2c_match_id(lm90_id, client)->driver_data; /* * Different devices have different alarm bits triggering the * ALERT# output */ data->alert_alarms = lm90_params[data->kind].alert_alarms; + data->resolution = lm90_params[data->kind].resolution ? : 11; /* Set chip capabilities */ data->flags = lm90_params[data->kind].flags; + if ((data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_PEC)) + data->flags &= ~(LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC); + + if ((data->flags & LM90_HAVE_PARTIAL_PEC) && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + data->flags &= ~LM90_HAVE_PARTIAL_PEC; + data->chip.ops = &lm90_ops; data->chip.info = data->info; - data->info[0] = HWMON_CHANNEL_INFO(chip, - HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL | HWMON_C_ALARMS); + data->info[0] = &data->chip_info; + info = &data->chip_info; + info->type = hwmon_chip; + info->config = data->chip_config; + + data->chip_config[0] = HWMON_C_REGISTER_TZ; + if (data->flags & LM90_HAVE_ALARMS) + data->chip_config[0] |= HWMON_C_ALARMS; + if (data->flags & LM90_HAVE_CONVRATE) + data->chip_config[0] |= HWMON_C_UPDATE_INTERVAL; + if (data->flags & LM90_HAVE_FAULTQUEUE) + data->chip_config[0] |= HWMON_C_TEMP_SAMPLES; data->info[1] = &data->temp_info; info = &data->temp_info; info->type = hwmon_temp; info->config = data->channel_config; - data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | - HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | - HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM; - data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | - HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | - HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT; + data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_MAX_ALARM; + data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_MAX_ALARM | HWMON_T_FAULT; + + if (data->flags & LM90_HAVE_LOW) { + data->channel_config[0] |= HWMON_T_MIN | HWMON_T_MIN_ALARM; + data->channel_config[1] |= HWMON_T_MIN | HWMON_T_MIN_ALARM; + } + + if (data->flags & LM90_HAVE_CRIT) { + data->channel_config[0] |= HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_CRIT_HYST; + data->channel_config[1] |= HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_CRIT_HYST; + } if (data->flags & LM90_HAVE_OFFSET) data->channel_config[1] |= HWMON_T_OFFSET; @@ -1831,17 +2857,35 @@ static int lm90_probe(struct i2c_client *client, data->channel_config[2] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST | - HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | - HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM | - HWMON_T_FAULT; + HWMON_T_CRIT_ALARM | HWMON_T_FAULT; + if (data->flags & LM90_HAVE_EMERGENCY) { + data->channel_config[2] |= HWMON_T_EMERGENCY | + HWMON_T_EMERGENCY_HYST; + } + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) + data->channel_config[2] |= HWMON_T_EMERGENCY_ALARM; + if (data->flags & LM90_HAVE_OFFSET) + data->channel_config[2] |= HWMON_T_OFFSET; } + data->faultqueue_mask = lm90_params[data->kind].faultqueue_mask; + data->faultqueue_depth = lm90_params[data->kind].faultqueue_depth; data->reg_local_ext = lm90_params[data->kind].reg_local_ext; + if (data->flags & LM90_HAVE_REMOTE_EXT) + data->reg_remote_ext = LM90_REG_REMOTE_TEMPL; + data->reg_status2 = lm90_params[data->kind].reg_status2; /* Set maximum conversion rate */ data->max_convrate = lm90_params[data->kind].max_convrate; + /* Parse device-tree channel information */ + if (client->dev.of_node) { + err = lm90_parse_dt_channel_info(client, data); + if (err) + return err; + } + /* Initialize the LM90 chip */ err = lm90_init_client(client, data); if (err < 0) { @@ -1853,7 +2897,7 @@ static int lm90_probe(struct i2c_client *client, * The 'pec' attribute is attached to the i2c device and thus created * separately. */ - if (client->flags & I2C_CLIENT_PEC) { + if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) { err = device_create_file(dev, &dev_attr_pec); if (err) return err; @@ -1868,12 +2912,13 @@ static int lm90_probe(struct i2c_client *client, if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); + data->hwmon_dev = hwmon_dev; + if (client->irq) { dev_dbg(dev, "IRQ: %d\n", client->irq); err = devm_request_threaded_irq(dev, client->irq, NULL, lm90_irq_thread, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "lm90", client); + IRQF_ONESHOT, "lm90", client); if (err < 0) { dev_err(dev, "cannot request IRQ %d\n", client->irq); return err; @@ -1886,12 +2931,10 @@ static int lm90_probe(struct i2c_client *client, static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, unsigned int flag) { - u16 alarms; - if (type != I2C_PROTOCOL_SMBUS_ALERT) return; - if (lm90_is_tripped(client, &alarms)) { + if (lm90_is_tripped(client)) { /* * Disable ALERT# output, because these chips don't implement * SMBus alert correctly; they should only hold the alert line @@ -1900,22 +2943,51 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, struct lm90_data *data = i2c_get_clientdata(client); if ((data->flags & LM90_HAVE_BROKEN_ALERT) && - (alarms & data->alert_alarms)) { - dev_dbg(&client->dev, "Disabling ALERT#\n"); - lm90_update_confreg(data, data->config | 0x80); + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); + lm90_update_confreg(data, data->config | 0x80); + } + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } } else { - dev_info(&client->dev, "Everything OK\n"); + dev_dbg(&client->dev, "Everything OK\n"); } } +static int lm90_suspend(struct device *dev) +{ + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + if (client->irq) + disable_irq(client->irq); + + return 0; +} + +static int lm90_resume(struct device *dev) +{ + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + if (client->irq) + enable_irq(client->irq); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(lm90_pm_ops, lm90_suspend, lm90_resume); + static struct i2c_driver lm90_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm90", .of_match_table = of_match_ptr(lm90_of_match), + .pm = pm_sleep_ptr(&lm90_pm_ops), }, - .probe = lm90_probe, + .probe_new = lm90_probe, .alert = lm90_alert, .id_table = lm90_id, .detect = lm90_detect, diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 84347db5edf3..2ff3044a677d 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -99,7 +99,7 @@ static const u8 regs[t_num_regs] = { struct lm92_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values */ @@ -126,7 +126,7 @@ static struct lm92_data *lm92_update_device(struct device *dev) i2c_smbus_read_word_swapped(client, regs[i]); } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -287,13 +287,12 @@ static int lm92_detect(struct i2c_client *new_client, else return -ENODEV; - strlcpy(info->type, "lm92", I2C_NAME_SIZE); + strscpy(info->type, "lm92", I2C_NAME_SIZE); return 0; } -static int lm92_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) +static int lm92_probe(struct i2c_client *new_client) { struct device *hwmon_dev; struct lm92_data *data; @@ -331,7 +330,7 @@ static struct i2c_driver lm92_driver = { .driver = { .name = "lm92", }, - .probe = lm92_probe, + .probe_new = lm92_probe, .id_table = lm92_id, .detect = lm92_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index cea8ea323271..4cf50d5f4f59 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -202,7 +202,7 @@ struct lm93_data { /* client update function */ void (*update)(struct lm93_data *, struct i2c_client *); - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ /* register values, arranged by block read groups */ struct block1_t block1; @@ -917,7 +917,7 @@ static struct lm93_data *lm93_update_device(struct device *dev) data->update(data, client); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -2575,7 +2575,7 @@ static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); dev_dbg(&adapter->dev, "loading %s at %d, 0x%02x\n", client->name, i2c_adapter_id(client->adapter), client->addr); @@ -2583,8 +2583,7 @@ static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info) return 0; } -static int lm93_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lm93_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct lm93_data *data; @@ -2636,7 +2635,7 @@ static struct i2c_driver lm93_driver = { .driver = { .name = "lm93", }, - .probe = lm93_probe, + .probe_new = lm93_probe, .id_table = lm93_id, .detect = lm93_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 8a2a2a490496..b4a9d0c223c4 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -644,7 +644,7 @@ static int lm95234_detect(struct i2c_client *client, if (val & model_mask) return -ENODEV; - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -677,8 +677,9 @@ static int lm95234_init_client(struct i2c_client *client) return 0; } -static int lm95234_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id lm95234_id[]; + +static int lm95234_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct lm95234_data *data; @@ -698,7 +699,7 @@ static int lm95234_probe(struct i2c_client *client, return err; data->groups[0] = &lm95234_common_group; - if (id->driver_data == lm95234) + if (i2c_match_id(lm95234_id, client)->driver_data == lm95234) data->groups[1] = &lm95234_group; hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, @@ -719,7 +720,7 @@ static struct i2c_driver lm95234_driver = { .driver = { .name = DRVNAME, }, - .probe = lm95234_probe, + .probe_new = lm95234_probe, .id_table = lm95234_id, .detect = lm95234_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 8d66d6e3c0fc..f1ed777a8735 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -78,7 +78,7 @@ struct lm95241_data { struct mutex update_lock; unsigned long last_updated; /* in jiffies */ unsigned long interval; /* in milli-seconds */ - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ /* registers values */ u8 temp[ARRAY_SIZE(lm95241_reg_address)]; u8 status, config, model, trutherm; @@ -118,7 +118,7 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) data->status = i2c_smbus_read_byte_data(client, LM95241_REG_R_STATUS); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -257,7 +257,7 @@ static int lm95241_write_temp(struct device *dev, u32 attr, int channel, else data->config &= ~R2DF_MASK; } - data->valid = 0; + data->valid = false; ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); break; @@ -273,7 +273,7 @@ static int lm95241_write_temp(struct device *dev, u32 attr, int channel, else data->config &= ~R2DF_MASK; } - data->valid = 0; + data->valid = false; ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); break; @@ -389,7 +389,7 @@ static int lm95241_detect(struct i2c_client *new_client, } /* Fill the i2c board info */ - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -432,8 +432,7 @@ static const struct hwmon_chip_info lm95241_chip_info = { .info = lm95241_info, }; -static int lm95241_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lm95241_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct lm95241_data *data; @@ -469,7 +468,7 @@ static struct i2c_driver lm95241_driver = { .driver = { .name = DEVNAME, }, - .probe = lm95241_probe, + .probe_new = lm95241_probe, .id_table = lm95241_id, .detect = lm95241_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 057614e664e1..c433f0af2d31 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -461,7 +461,7 @@ static int lm95245_detect(struct i2c_client *new_client, return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -547,8 +547,7 @@ static const struct hwmon_chip_info lm95245_chip_info = { .info = lm95245_info, }; -static int lm95245_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lm95245_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct lm95245_data *data; @@ -598,7 +597,7 @@ static struct i2c_driver lm95245_driver = { .name = "lm95245", .of_match_table = of_match_ptr(lm95245_of_match), }, - .probe = lm95245_probe, + .probe_new = lm95245_probe, .id_table = lm95245_id, .detect = lm95245_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index 2818276ed3d6..9adebb59f604 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -226,7 +226,7 @@ static ssize_t ltc2945_value_show(struct device *dev, value = ltc2945_reg_to_val(dev, attr->index); if (value < 0) return value; - return snprintf(buf, PAGE_SIZE, "%lld\n", value); + return sysfs_emit(buf, "%lld\n", value); } static ssize_t ltc2945_value_store(struct device *dev, @@ -333,7 +333,7 @@ static ssize_t ltc2945_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); - return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); + return sysfs_emit(buf, "%d\n", !!fault); } /* Input voltages */ @@ -445,8 +445,7 @@ static const struct regmap_config ltc2945_regmap_config = { .max_register = LTC2945_MIN_ADIN_THRES_L, }; -static int ltc2945_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc2945_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -478,7 +477,7 @@ static struct i2c_driver ltc2945_driver = { .driver = { .name = "ltc2945", }, - .probe = ltc2945_probe, + .probe_new = ltc2945_probe, .id_table = ltc2945_id, }; diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c index bb3f7749a0b0..7404e974762f 100644 --- a/drivers/hwmon/ltc2947-core.c +++ b/drivers/hwmon/ltc2947-core.c @@ -956,13 +956,6 @@ static struct attribute *ltc2947_attrs[] = { }; ATTRIBUTE_GROUPS(ltc2947); -static void ltc2947_clk_disable(void *data) -{ - struct clk *extclk = data; - - clk_disable_unprepare(extclk); -} - static int ltc2947_setup(struct ltc2947_data *st) { int ret; @@ -989,8 +982,12 @@ static int ltc2947_setup(struct ltc2947_data *st) return ret; /* check external clock presence */ - extclk = devm_clk_get(st->dev, NULL); - if (!IS_ERR(extclk)) { + extclk = devm_clk_get_optional_enabled(st->dev, NULL); + if (IS_ERR(extclk)) + return dev_err_probe(st->dev, PTR_ERR(extclk), + "Failed to get external clock\n"); + + if (extclk) { unsigned long rate_hz; u8 pre = 0, div, tbctl; u64 aux; @@ -1003,14 +1000,6 @@ static int ltc2947_setup(struct ltc2947_data *st) return -EINVAL; } - ret = clk_prepare_enable(extclk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(st->dev, ltc2947_clk_disable, - extclk); - if (ret) - return ret; /* as in table 1 of the datasheet */ if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000) pre = 0; @@ -1131,7 +1120,7 @@ int ltc2947_core_probe(struct regmap *map, const char *name) } EXPORT_SYMBOL_GPL(ltc2947_core_probe); -static int __maybe_unused ltc2947_resume(struct device *dev) +static int ltc2947_resume(struct device *dev) { struct ltc2947_data *st = dev_get_drvdata(dev); u32 ctrl = 0; @@ -1160,7 +1149,7 @@ static int __maybe_unused ltc2947_resume(struct device *dev) LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1)); } -static int __maybe_unused ltc2947_suspend(struct device *dev) +static int ltc2947_suspend(struct device *dev) { struct ltc2947_data *st = dev_get_drvdata(dev); @@ -1168,8 +1157,7 @@ static int __maybe_unused ltc2947_suspend(struct device *dev) LTC2947_SHUTDOWN_MASK, 1); } -SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume); -EXPORT_SYMBOL_GPL(ltc2947_pm_ops); +EXPORT_SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume); const struct of_device_id ltc2947_of_match[] = { { .compatible = "adi,ltc2947" }, diff --git a/drivers/hwmon/ltc2947-i2c.c b/drivers/hwmon/ltc2947-i2c.c index cf6074b110ae..96852bc8a964 100644 --- a/drivers/hwmon/ltc2947-i2c.c +++ b/drivers/hwmon/ltc2947-i2c.c @@ -15,8 +15,7 @@ static const struct regmap_config ltc2947_regmap_config = { .val_bits = 8, }; -static int ltc2947_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ltc2947_probe(struct i2c_client *i2c) { struct regmap *map; @@ -37,9 +36,9 @@ static struct i2c_driver ltc2947_driver = { .driver = { .name = "ltc2947", .of_match_table = ltc2947_of_match, - .pm = <c2947_pm_ops, + .pm = pm_sleep_ptr(<c2947_pm_ops), }, - .probe = ltc2947_probe, + .probe_new = ltc2947_probe, .id_table = ltc2947_id, }; module_i2c_driver(ltc2947_driver); diff --git a/drivers/hwmon/ltc2947-spi.c b/drivers/hwmon/ltc2947-spi.c index c24ca569db1b..a33be110098c 100644 --- a/drivers/hwmon/ltc2947-spi.c +++ b/drivers/hwmon/ltc2947-spi.c @@ -38,7 +38,7 @@ static struct spi_driver ltc2947_driver = { .driver = { .name = "ltc2947", .of_match_table = ltc2947_of_match, - .pm = <c2947_pm_ops, + .pm = pm_sleep_ptr(<c2947_pm_ops), }, .probe = ltc2947_probe, .id_table = ltc2947_id, diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c index 53ff5051774c..689f788b8563 100644 --- a/drivers/hwmon/ltc2990.c +++ b/drivers/hwmon/ltc2990.c @@ -147,13 +147,13 @@ static ssize_t ltc2990_value_show(struct device *dev, if (unlikely(ret < 0)) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static umode_t ltc2990_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct ltc2990_data *data = dev_get_drvdata(dev); struct device_attribute *da = container_of(a, struct device_attribute, attr); @@ -200,8 +200,7 @@ static const struct attribute_group ltc2990_group = { }; __ATTRIBUTE_GROUPS(ltc2990); -static int ltc2990_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ltc2990_i2c_probe(struct i2c_client *i2c) { int ret; struct device *hwmon_dev; @@ -269,7 +268,7 @@ static struct i2c_driver ltc2990_i2c_driver = { .driver = { .name = "ltc2990", }, - .probe = ltc2990_i2c_probe, + .probe_new = ltc2990_i2c_probe, .id_table = ltc2990_i2c_id, }; diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c new file mode 100644 index 000000000000..72489d5d7eaf --- /dev/null +++ b/drivers/hwmon/ltc2992.c @@ -0,0 +1,938 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * LTC2992 - Dual Wide Range Power Monitor + * + * Copyright 2020 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#define LTC2992_CTRLB 0x01 +#define LTC2992_FAULT1 0x03 +#define LTC2992_POWER1 0x05 +#define LTC2992_POWER1_MAX 0x08 +#define LTC2992_POWER1_MIN 0x0B +#define LTC2992_POWER1_MAX_THRESH 0x0E +#define LTC2992_POWER1_MIN_THRESH 0x11 +#define LTC2992_DSENSE1 0x14 +#define LTC2992_DSENSE1_MAX 0x16 +#define LTC2992_DSENSE1_MIN 0x18 +#define LTC2992_DSENSE1_MAX_THRESH 0x1A +#define LTC2992_DSENSE1_MIN_THRESH 0x1C +#define LTC2992_SENSE1 0x1E +#define LTC2992_SENSE1_MAX 0x20 +#define LTC2992_SENSE1_MIN 0x22 +#define LTC2992_SENSE1_MAX_THRESH 0x24 +#define LTC2992_SENSE1_MIN_THRESH 0x26 +#define LTC2992_G1 0x28 +#define LTC2992_G1_MAX 0x2A +#define LTC2992_G1_MIN 0x2C +#define LTC2992_G1_MAX_THRESH 0x2E +#define LTC2992_G1_MIN_THRESH 0x30 +#define LTC2992_FAULT2 0x35 +#define LTC2992_G2 0x5A +#define LTC2992_G2_MAX 0x5C +#define LTC2992_G2_MIN 0x5E +#define LTC2992_G2_MAX_THRESH 0x60 +#define LTC2992_G2_MIN_THRESH 0x62 +#define LTC2992_G3 0x64 +#define LTC2992_G3_MAX 0x66 +#define LTC2992_G3_MIN 0x68 +#define LTC2992_G3_MAX_THRESH 0x6A +#define LTC2992_G3_MIN_THRESH 0x6C +#define LTC2992_G4 0x6E +#define LTC2992_G4_MAX 0x70 +#define LTC2992_G4_MIN 0x72 +#define LTC2992_G4_MAX_THRESH 0x74 +#define LTC2992_G4_MIN_THRESH 0x76 +#define LTC2992_FAULT3 0x92 +#define LTC2992_GPIO_STATUS 0x95 +#define LTC2992_GPIO_IO_CTRL 0x96 +#define LTC2992_GPIO_CTRL 0x97 + +#define LTC2992_POWER(x) (LTC2992_POWER1 + ((x) * 0x32)) +#define LTC2992_POWER_MAX(x) (LTC2992_POWER1_MAX + ((x) * 0x32)) +#define LTC2992_POWER_MIN(x) (LTC2992_POWER1_MIN + ((x) * 0x32)) +#define LTC2992_POWER_MAX_THRESH(x) (LTC2992_POWER1_MAX_THRESH + ((x) * 0x32)) +#define LTC2992_POWER_MIN_THRESH(x) (LTC2992_POWER1_MIN_THRESH + ((x) * 0x32)) +#define LTC2992_DSENSE(x) (LTC2992_DSENSE1 + ((x) * 0x32)) +#define LTC2992_DSENSE_MAX(x) (LTC2992_DSENSE1_MAX + ((x) * 0x32)) +#define LTC2992_DSENSE_MIN(x) (LTC2992_DSENSE1_MIN + ((x) * 0x32)) +#define LTC2992_DSENSE_MAX_THRESH(x) (LTC2992_DSENSE1_MAX_THRESH + ((x) * 0x32)) +#define LTC2992_DSENSE_MIN_THRESH(x) (LTC2992_DSENSE1_MIN_THRESH + ((x) * 0x32)) +#define LTC2992_SENSE(x) (LTC2992_SENSE1 + ((x) * 0x32)) +#define LTC2992_SENSE_MAX(x) (LTC2992_SENSE1_MAX + ((x) * 0x32)) +#define LTC2992_SENSE_MIN(x) (LTC2992_SENSE1_MIN + ((x) * 0x32)) +#define LTC2992_SENSE_MAX_THRESH(x) (LTC2992_SENSE1_MAX_THRESH + ((x) * 0x32)) +#define LTC2992_SENSE_MIN_THRESH(x) (LTC2992_SENSE1_MIN_THRESH + ((x) * 0x32)) +#define LTC2992_POWER_FAULT(x) (LTC2992_FAULT1 + ((x) * 0x32)) +#define LTC2992_SENSE_FAULT(x) (LTC2992_FAULT1 + ((x) * 0x32)) +#define LTC2992_DSENSE_FAULT(x) (LTC2992_FAULT1 + ((x) * 0x32)) + +/* CTRLB register bitfields */ +#define LTC2992_RESET_HISTORY BIT(3) + +/* FAULT1 FAULT2 registers common bitfields */ +#define LTC2992_POWER_FAULT_MSK(x) (BIT(6) << (x)) +#define LTC2992_DSENSE_FAULT_MSK(x) (BIT(4) << (x)) +#define LTC2992_SENSE_FAULT_MSK(x) (BIT(2) << (x)) + +/* FAULT1 bitfields */ +#define LTC2992_GPIO1_FAULT_MSK(x) (BIT(0) << (x)) + +/* FAULT2 bitfields */ +#define LTC2992_GPIO2_FAULT_MSK(x) (BIT(0) << (x)) + +/* FAULT3 bitfields */ +#define LTC2992_GPIO3_FAULT_MSK(x) (BIT(6) << (x)) +#define LTC2992_GPIO4_FAULT_MSK(x) (BIT(4) << (x)) + +#define LTC2992_IADC_NANOV_LSB 12500 +#define LTC2992_VADC_UV_LSB 25000 +#define LTC2992_VADC_GPIO_UV_LSB 500 + +#define LTC2992_GPIO_NR 4 +#define LTC2992_GPIO1_BIT 7 +#define LTC2992_GPIO2_BIT 6 +#define LTC2992_GPIO3_BIT 0 +#define LTC2992_GPIO4_BIT 6 +#define LTC2992_GPIO_BIT(x) (LTC2992_GPIO_NR - (x) - 1) + +struct ltc2992_state { + struct i2c_client *client; + struct gpio_chip gc; + struct mutex gpio_mutex; /* lock for gpio access */ + const char *gpio_names[LTC2992_GPIO_NR]; + struct regmap *regmap; + u32 r_sense_uohm[2]; +}; + +struct ltc2992_gpio_regs { + u8 data; + u8 max; + u8 min; + u8 max_thresh; + u8 min_thresh; + u8 alarm; + u8 min_alarm_msk; + u8 max_alarm_msk; + u8 ctrl; + u8 ctrl_bit; +}; + +static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = { + { + .data = LTC2992_G1, + .max = LTC2992_G1_MAX, + .min = LTC2992_G1_MIN, + .max_thresh = LTC2992_G1_MAX_THRESH, + .min_thresh = LTC2992_G1_MIN_THRESH, + .alarm = LTC2992_FAULT1, + .min_alarm_msk = LTC2992_GPIO1_FAULT_MSK(0), + .max_alarm_msk = LTC2992_GPIO1_FAULT_MSK(1), + .ctrl = LTC2992_GPIO_IO_CTRL, + .ctrl_bit = LTC2992_GPIO1_BIT, + }, + { + .data = LTC2992_G2, + .max = LTC2992_G2_MAX, + .min = LTC2992_G2_MIN, + .max_thresh = LTC2992_G2_MAX_THRESH, + .min_thresh = LTC2992_G2_MIN_THRESH, + .alarm = LTC2992_FAULT2, + .min_alarm_msk = LTC2992_GPIO2_FAULT_MSK(0), + .max_alarm_msk = LTC2992_GPIO2_FAULT_MSK(1), + .ctrl = LTC2992_GPIO_IO_CTRL, + .ctrl_bit = LTC2992_GPIO2_BIT, + }, + { + .data = LTC2992_G3, + .max = LTC2992_G3_MAX, + .min = LTC2992_G3_MIN, + .max_thresh = LTC2992_G3_MAX_THRESH, + .min_thresh = LTC2992_G3_MIN_THRESH, + .alarm = LTC2992_FAULT3, + .min_alarm_msk = LTC2992_GPIO3_FAULT_MSK(0), + .max_alarm_msk = LTC2992_GPIO3_FAULT_MSK(1), + .ctrl = LTC2992_GPIO_IO_CTRL, + .ctrl_bit = LTC2992_GPIO3_BIT, + }, + { + .data = LTC2992_G4, + .max = LTC2992_G4_MAX, + .min = LTC2992_G4_MIN, + .max_thresh = LTC2992_G4_MAX_THRESH, + .min_thresh = LTC2992_G4_MIN_THRESH, + .alarm = LTC2992_FAULT3, + .min_alarm_msk = LTC2992_GPIO4_FAULT_MSK(0), + .max_alarm_msk = LTC2992_GPIO4_FAULT_MSK(1), + .ctrl = LTC2992_GPIO_CTRL, + .ctrl_bit = LTC2992_GPIO4_BIT, + }, +}; + +static const char *ltc2992_gpio_names[LTC2992_GPIO_NR] = { + "GPIO1", "GPIO2", "GPIO3", "GPIO4", +}; + +static int ltc2992_read_reg(struct ltc2992_state *st, u8 addr, const u8 reg_len) +{ + u8 regvals[4]; + int val; + int ret; + int i; + + ret = regmap_bulk_read(st->regmap, addr, regvals, reg_len); + if (ret < 0) + return ret; + + val = 0; + for (i = 0; i < reg_len; i++) + val |= regvals[reg_len - i - 1] << (i * 8); + + return val; +} + +static int ltc2992_write_reg(struct ltc2992_state *st, u8 addr, const u8 reg_len, u32 val) +{ + u8 regvals[4]; + int i; + + for (i = 0; i < reg_len; i++) + regvals[reg_len - i - 1] = (val >> (i * 8)) & 0xFF; + + return regmap_bulk_write(st->regmap, addr, regvals, reg_len); +} + +static int ltc2992_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ltc2992_state *st = gpiochip_get_data(chip); + unsigned long gpio_status; + int reg; + + mutex_lock(&st->gpio_mutex); + reg = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1); + mutex_unlock(&st->gpio_mutex); + + if (reg < 0) + return reg; + + gpio_status = reg; + + return !test_bit(LTC2992_GPIO_BIT(offset), &gpio_status); +} + +static int ltc2992_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct ltc2992_state *st = gpiochip_get_data(chip); + unsigned long gpio_status; + unsigned int gpio_nr; + int reg; + + mutex_lock(&st->gpio_mutex); + reg = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1); + mutex_unlock(&st->gpio_mutex); + + if (reg < 0) + return reg; + + gpio_status = reg; + + for_each_set_bit(gpio_nr, mask, LTC2992_GPIO_NR) { + if (test_bit(LTC2992_GPIO_BIT(gpio_nr), &gpio_status)) + set_bit(gpio_nr, bits); + } + + return 0; +} + +static void ltc2992_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct ltc2992_state *st = gpiochip_get_data(chip); + unsigned long gpio_ctrl; + int reg; + + mutex_lock(&st->gpio_mutex); + reg = ltc2992_read_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1); + if (reg < 0) { + mutex_unlock(&st->gpio_mutex); + return; + } + + gpio_ctrl = reg; + assign_bit(ltc2992_gpio_addr_map[offset].ctrl_bit, &gpio_ctrl, value); + + ltc2992_write_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1, gpio_ctrl); + mutex_unlock(&st->gpio_mutex); +} + +static void ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct ltc2992_state *st = gpiochip_get_data(chip); + unsigned long gpio_ctrl_io = 0; + unsigned long gpio_ctrl = 0; + unsigned int gpio_nr; + + for_each_set_bit(gpio_nr, mask, LTC2992_GPIO_NR) { + if (gpio_nr < 3) + assign_bit(ltc2992_gpio_addr_map[gpio_nr].ctrl_bit, &gpio_ctrl_io, true); + + if (gpio_nr == 3) + assign_bit(ltc2992_gpio_addr_map[gpio_nr].ctrl_bit, &gpio_ctrl, true); + } + + mutex_lock(&st->gpio_mutex); + ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, gpio_ctrl_io); + ltc2992_write_reg(st, LTC2992_GPIO_CTRL, 1, gpio_ctrl); + mutex_unlock(&st->gpio_mutex); +} + +static int ltc2992_config_gpio(struct ltc2992_state *st) +{ + const char *name = dev_name(&st->client->dev); + char *gpio_name; + int ret; + int i; + + ret = ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, 0); + if (ret < 0) + return ret; + + mutex_init(&st->gpio_mutex); + + for (i = 0; i < ARRAY_SIZE(st->gpio_names); i++) { + gpio_name = devm_kasprintf(&st->client->dev, GFP_KERNEL, "ltc2992-%x-%s", + st->client->addr, ltc2992_gpio_names[i]); + if (!gpio_name) + return -ENOMEM; + + st->gpio_names[i] = gpio_name; + } + + st->gc.label = name; + st->gc.parent = &st->client->dev; + st->gc.owner = THIS_MODULE; + st->gc.base = -1; + st->gc.names = st->gpio_names; + st->gc.ngpio = ARRAY_SIZE(st->gpio_names); + st->gc.get = ltc2992_gpio_get; + st->gc.get_multiple = ltc2992_gpio_get_multiple; + st->gc.set = ltc2992_gpio_set; + st->gc.set_multiple = ltc2992_gpio_set_multiple; + + ret = devm_gpiochip_add_data(&st->client->dev, &st->gc, st); + if (ret) + dev_err(&st->client->dev, "GPIO registering failed (%d)\n", ret); + + return ret; +} + +static umode_t ltc2992_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct ltc2992_state *st = data; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_in_reset_history: + return 0200; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_lowest: + case hwmon_in_highest: + case hwmon_in_min_alarm: + case hwmon_in_max_alarm: + return 0444; + case hwmon_in_min: + case hwmon_in_max: + return 0644; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_lowest: + case hwmon_curr_highest: + case hwmon_curr_min_alarm: + case hwmon_curr_max_alarm: + if (st->r_sense_uohm[channel]) + return 0444; + break; + case hwmon_curr_min: + case hwmon_curr_max: + if (st->r_sense_uohm[channel]) + return 0644; + break; + } + break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_input_lowest: + case hwmon_power_input_highest: + case hwmon_power_min_alarm: + case hwmon_power_max_alarm: + if (st->r_sense_uohm[channel]) + return 0444; + break; + case hwmon_power_min: + case hwmon_power_max: + if (st->r_sense_uohm[channel]) + return 0644; + break; + } + break; + default: + break; + } + + return 0; +} + +static int ltc2992_get_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long *val) +{ + int reg_val; + + reg_val = ltc2992_read_reg(st, reg, 2); + if (reg_val < 0) + return reg_val; + + reg_val = reg_val >> 4; + *val = DIV_ROUND_CLOSEST(reg_val * scale, 1000); + + return 0; +} + +static int ltc2992_set_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long val) +{ + val = DIV_ROUND_CLOSEST(val * 1000, scale); + val = val << 4; + + return ltc2992_write_reg(st, reg, 2, val); +} + +static int ltc2992_read_gpio_alarm(struct ltc2992_state *st, int nr_gpio, u32 attr, long *val) +{ + int reg_val; + u32 mask; + + if (attr == hwmon_in_max_alarm) + mask = ltc2992_gpio_addr_map[nr_gpio].max_alarm_msk; + else + mask = ltc2992_gpio_addr_map[nr_gpio].min_alarm_msk; + + reg_val = ltc2992_read_reg(st, ltc2992_gpio_addr_map[nr_gpio].alarm, 1); + if (reg_val < 0) + return reg_val; + + *val = !!(reg_val & mask); + reg_val &= ~mask; + + return ltc2992_write_reg(st, ltc2992_gpio_addr_map[nr_gpio].alarm, 1, reg_val); +} + +static int ltc2992_read_gpios_in(struct device *dev, u32 attr, int nr_gpio, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_in_input: + reg = ltc2992_gpio_addr_map[nr_gpio].data; + break; + case hwmon_in_lowest: + reg = ltc2992_gpio_addr_map[nr_gpio].min; + break; + case hwmon_in_highest: + reg = ltc2992_gpio_addr_map[nr_gpio].max; + break; + case hwmon_in_min: + reg = ltc2992_gpio_addr_map[nr_gpio].min_thresh; + break; + case hwmon_in_max: + reg = ltc2992_gpio_addr_map[nr_gpio].max_thresh; + break; + case hwmon_in_min_alarm: + case hwmon_in_max_alarm: + return ltc2992_read_gpio_alarm(st, nr_gpio, attr, val); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_voltage(st, reg, LTC2992_VADC_GPIO_UV_LSB, val); +} + +static int ltc2992_read_in_alarm(struct ltc2992_state *st, int channel, long *val, u32 attr) +{ + int reg_val; + u32 mask; + + if (attr == hwmon_in_max_alarm) + mask = LTC2992_SENSE_FAULT_MSK(1); + else + mask = LTC2992_SENSE_FAULT_MSK(0); + + reg_val = ltc2992_read_reg(st, LTC2992_SENSE_FAULT(channel), 1); + if (reg_val < 0) + return reg_val; + + *val = !!(reg_val & mask); + reg_val &= ~mask; + + return ltc2992_write_reg(st, LTC2992_SENSE_FAULT(channel), 1, reg_val); +} + +static int ltc2992_read_in(struct device *dev, u32 attr, int channel, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + if (channel > 1) + return ltc2992_read_gpios_in(dev, attr, channel - 2, val); + + switch (attr) { + case hwmon_in_input: + reg = LTC2992_SENSE(channel); + break; + case hwmon_in_lowest: + reg = LTC2992_SENSE_MIN(channel); + break; + case hwmon_in_highest: + reg = LTC2992_SENSE_MAX(channel); + break; + case hwmon_in_min: + reg = LTC2992_SENSE_MIN_THRESH(channel); + break; + case hwmon_in_max: + reg = LTC2992_SENSE_MAX_THRESH(channel); + break; + case hwmon_in_min_alarm: + case hwmon_in_max_alarm: + return ltc2992_read_in_alarm(st, channel, val, attr); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_voltage(st, reg, LTC2992_VADC_UV_LSB, val); +} + +static int ltc2992_get_current(struct ltc2992_state *st, u32 reg, u32 channel, long *val) +{ + int reg_val; + + reg_val = ltc2992_read_reg(st, reg, 2); + if (reg_val < 0) + return reg_val; + + reg_val = reg_val >> 4; + *val = DIV_ROUND_CLOSEST(reg_val * LTC2992_IADC_NANOV_LSB, st->r_sense_uohm[channel]); + + return 0; +} + +static int ltc2992_set_current(struct ltc2992_state *st, u32 reg, u32 channel, long val) +{ + u32 reg_val; + + reg_val = DIV_ROUND_CLOSEST(val * st->r_sense_uohm[channel], LTC2992_IADC_NANOV_LSB); + reg_val = reg_val << 4; + + return ltc2992_write_reg(st, reg, 2, reg_val); +} + +static int ltc2992_read_curr_alarm(struct ltc2992_state *st, int channel, long *val, u32 attr) +{ + int reg_val; + u32 mask; + + if (attr == hwmon_curr_max_alarm) + mask = LTC2992_DSENSE_FAULT_MSK(1); + else + mask = LTC2992_DSENSE_FAULT_MSK(0); + + reg_val = ltc2992_read_reg(st, LTC2992_DSENSE_FAULT(channel), 1); + if (reg_val < 0) + return reg_val; + + *val = !!(reg_val & mask); + + reg_val &= ~mask; + return ltc2992_write_reg(st, LTC2992_DSENSE_FAULT(channel), 1, reg_val); +} + +static int ltc2992_read_curr(struct device *dev, u32 attr, int channel, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_curr_input: + reg = LTC2992_DSENSE(channel); + break; + case hwmon_curr_lowest: + reg = LTC2992_DSENSE_MIN(channel); + break; + case hwmon_curr_highest: + reg = LTC2992_DSENSE_MAX(channel); + break; + case hwmon_curr_min: + reg = LTC2992_DSENSE_MIN_THRESH(channel); + break; + case hwmon_curr_max: + reg = LTC2992_DSENSE_MAX_THRESH(channel); + break; + case hwmon_curr_min_alarm: + case hwmon_curr_max_alarm: + return ltc2992_read_curr_alarm(st, channel, val, attr); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_current(st, reg, channel, val); +} + +static int ltc2992_get_power(struct ltc2992_state *st, u32 reg, u32 channel, long *val) +{ + int reg_val; + + reg_val = ltc2992_read_reg(st, reg, 3); + if (reg_val < 0) + return reg_val; + + *val = mul_u64_u32_div(reg_val, LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB, + st->r_sense_uohm[channel] * 1000); + + return 0; +} + +static int ltc2992_set_power(struct ltc2992_state *st, u32 reg, u32 channel, long val) +{ + u32 reg_val; + + reg_val = mul_u64_u32_div(val, st->r_sense_uohm[channel] * 1000, + LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB); + + return ltc2992_write_reg(st, reg, 3, reg_val); +} + +static int ltc2992_read_power_alarm(struct ltc2992_state *st, int channel, long *val, u32 attr) +{ + int reg_val; + u32 mask; + + if (attr == hwmon_power_max_alarm) + mask = LTC2992_POWER_FAULT_MSK(1); + else + mask = LTC2992_POWER_FAULT_MSK(0); + + reg_val = ltc2992_read_reg(st, LTC2992_POWER_FAULT(channel), 1); + if (reg_val < 0) + return reg_val; + + *val = !!(reg_val & mask); + reg_val &= ~mask; + + return ltc2992_write_reg(st, LTC2992_POWER_FAULT(channel), 1, reg_val); +} + +static int ltc2992_read_power(struct device *dev, u32 attr, int channel, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_power_input: + reg = LTC2992_POWER(channel); + break; + case hwmon_power_input_lowest: + reg = LTC2992_POWER_MIN(channel); + break; + case hwmon_power_input_highest: + reg = LTC2992_POWER_MAX(channel); + break; + case hwmon_power_min: + reg = LTC2992_POWER_MIN_THRESH(channel); + break; + case hwmon_power_max: + reg = LTC2992_POWER_MAX_THRESH(channel); + break; + case hwmon_power_min_alarm: + case hwmon_power_max_alarm: + return ltc2992_read_power_alarm(st, channel, val, attr); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_power(st, reg, channel, val); +} + +static int ltc2992_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) +{ + switch (type) { + case hwmon_in: + return ltc2992_read_in(dev, attr, channel, val); + case hwmon_curr: + return ltc2992_read_curr(dev, attr, channel, val); + case hwmon_power: + return ltc2992_read_power(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc2992_write_curr(struct device *dev, u32 attr, int channel, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_curr_min: + reg = LTC2992_DSENSE_MIN_THRESH(channel); + break; + case hwmon_curr_max: + reg = LTC2992_DSENSE_MAX_THRESH(channel); + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_current(st, reg, channel, val); +} + +static int ltc2992_write_gpios_in(struct device *dev, u32 attr, int nr_gpio, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_in_min: + reg = ltc2992_gpio_addr_map[nr_gpio].min_thresh; + break; + case hwmon_in_max: + reg = ltc2992_gpio_addr_map[nr_gpio].max_thresh; + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_voltage(st, reg, LTC2992_VADC_GPIO_UV_LSB, val); +} + +static int ltc2992_write_in(struct device *dev, u32 attr, int channel, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + if (channel > 1) + return ltc2992_write_gpios_in(dev, attr, channel - 2, val); + + switch (attr) { + case hwmon_in_min: + reg = LTC2992_SENSE_MIN_THRESH(channel); + break; + case hwmon_in_max: + reg = LTC2992_SENSE_MAX_THRESH(channel); + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_voltage(st, reg, LTC2992_VADC_UV_LSB, val); +} + +static int ltc2992_write_power(struct device *dev, u32 attr, int channel, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_power_min: + reg = LTC2992_POWER_MIN_THRESH(channel); + break; + case hwmon_power_max: + reg = LTC2992_POWER_MAX_THRESH(channel); + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_power(st, reg, channel, val); +} + +static int ltc2992_write_chip(struct device *dev, u32 attr, int channel, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_in_reset_history: + return regmap_update_bits(st->regmap, LTC2992_CTRLB, LTC2992_RESET_HISTORY, + LTC2992_RESET_HISTORY); + default: + return -EOPNOTSUPP; + } +} + +static int ltc2992_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long val) +{ + switch (type) { + case hwmon_chip: + return ltc2992_write_chip(dev, attr, channel, val); + case hwmon_in: + return ltc2992_write_in(dev, attr, channel, val); + case hwmon_curr: + return ltc2992_write_curr(dev, attr, channel, val); + case hwmon_power: + return ltc2992_write_power(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops ltc2992_hwmon_ops = { + .is_visible = ltc2992_is_visible, + .read = ltc2992_read, + .write = ltc2992_write, +}; + +static const struct hwmon_channel_info *ltc2992_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_IN_RESET_HISTORY), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | + HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | + HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | + HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | + HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | + HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | + HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | + HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM, + HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | + HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | + HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM, + HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | + HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM), + NULL +}; + +static const struct hwmon_chip_info ltc2992_chip_info = { + .ops = <c2992_hwmon_ops, + .info = ltc2992_info, +}; + +static const struct regmap_config ltc2992_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xE8, +}; + +static int ltc2992_parse_dt(struct ltc2992_state *st) +{ + struct fwnode_handle *fwnode; + struct fwnode_handle *child; + u32 addr; + u32 val; + int ret; + + fwnode = dev_fwnode(&st->client->dev); + + fwnode_for_each_available_child_node(fwnode, child) { + ret = fwnode_property_read_u32(child, "reg", &addr); + if (ret < 0) { + fwnode_handle_put(child); + return ret; + } + + if (addr > 1) { + fwnode_handle_put(child); + return -EINVAL; + } + + ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val); + if (!ret) + st->r_sense_uohm[addr] = val; + } + + return 0; +} + +static int ltc2992_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *hwmon_dev; + struct ltc2992_state *st; + int ret; + + st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->client = client; + st->regmap = devm_regmap_init_i2c(client, <c2992_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + ret = ltc2992_parse_dt(st); + if (ret < 0) + return ret; + + ret = ltc2992_config_gpio(st); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, client->name, st, + <c2992_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id ltc2992_of_match[] = { + { .compatible = "adi,ltc2992" }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2992_of_match); + +static const struct i2c_device_id ltc2992_i2c_id[] = { + {"ltc2992", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltc2992_i2c_id); + +static struct i2c_driver ltc2992_i2c_driver = { + .driver = { + .name = "ltc2992", + .of_match_table = ltc2992_of_match, + }, + .probe = ltc2992_i2c_probe, + .id_table = ltc2992_i2c_id, +}; + +module_i2c_driver(ltc2992_i2c_driver); + +MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>"); +MODULE_DESCRIPTION("Hwmon driver for Linear Technology 2992"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index 67a529b7ba18..e3ac004c1ed1 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -77,7 +77,7 @@ static struct ltc4151_data *ltc4151_update_device(struct device *dev) data->regs[i] = val; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } abort: mutex_unlock(&data->update_lock); @@ -128,7 +128,7 @@ static ssize_t ltc4151_value_show(struct device *dev, return PTR_ERR(data); value = ltc4151_get_value(data, attr->index); - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } /* @@ -154,8 +154,7 @@ static struct attribute *ltc4151_attrs[] = { }; ATTRIBUTE_GROUPS(ltc4151); -static int ltc4151_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc4151_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; @@ -206,7 +205,7 @@ static struct i2c_driver ltc4151_driver = { .name = "ltc4151", .of_match_table = of_match_ptr(ltc4151_match), }, - .probe = ltc4151_probe, + .probe_new = ltc4151_probe, .id_table = ltc4151_id, }; diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index f783ac19675e..fa43d26ddd4f 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -64,7 +64,7 @@ static struct ltc4215_data *ltc4215_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -139,7 +139,7 @@ static ssize_t ltc4215_voltage_show(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); const int voltage = ltc4215_get_voltage(dev, attr->index); - return snprintf(buf, PAGE_SIZE, "%d\n", voltage); + return sysfs_emit(buf, "%d\n", voltage); } static ssize_t ltc4215_current_show(struct device *dev, @@ -147,7 +147,7 @@ static ssize_t ltc4215_current_show(struct device *dev, { const unsigned int curr = ltc4215_get_current(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", curr); + return sysfs_emit(buf, "%u\n", curr); } static ssize_t ltc4215_power_show(struct device *dev, @@ -159,7 +159,7 @@ static ssize_t ltc4215_power_show(struct device *dev, /* current in mA * voltage in mV == power in uW */ const unsigned int power = abs(output_voltage * curr); - return snprintf(buf, PAGE_SIZE, "%u\n", power); + return sysfs_emit(buf, "%u\n", power); } static ssize_t ltc4215_alarm_show(struct device *dev, @@ -170,7 +170,7 @@ static ssize_t ltc4215_alarm_show(struct device *dev, const u8 reg = data->regs[LTC4215_STATUS]; const u32 mask = attr->index; - return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask)); + return sysfs_emit(buf, "%u\n", !!(reg & mask)); } /* @@ -218,8 +218,7 @@ static struct attribute *ltc4215_attrs[] = { }; ATTRIBUTE_GROUPS(ltc4215); -static int ltc4215_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc4215_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; @@ -256,7 +255,7 @@ static struct i2c_driver ltc4215_driver = { .driver = { .name = "ltc4215", }, - .probe = ltc4215_probe, + .probe_new = ltc4215_probe, .id_table = ltc4215_id, }; diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c index d15485e93fb8..d2027ca5c925 100644 --- a/drivers/hwmon/ltc4222.c +++ b/drivers/hwmon/ltc4222.c @@ -94,7 +94,7 @@ static ssize_t ltc4222_value_show(struct device *dev, value = ltc4222_get_value(dev, attr->index); if (value < 0) return value; - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t ltc4222_bool_show(struct device *dev, @@ -112,7 +112,7 @@ static ssize_t ltc4222_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ regmap_update_bits(regmap, attr->nr, attr->index, 0); - return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); + return sysfs_emit(buf, "%d\n", !!fault); } /* Voltages */ @@ -177,8 +177,7 @@ static const struct regmap_config ltc4222_regmap_config = { .max_register = LTC4222_ADC_CONTROL, }; -static int ltc4222_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc4222_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -211,7 +210,7 @@ static struct i2c_driver ltc4222_driver = { .driver = { .name = "ltc4222", }, - .probe = ltc4222_probe, + .probe_new = ltc4222_probe, .id_table = ltc4222_id, }; diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 244a83d675cd..5088d28b3a7c 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -440,8 +440,7 @@ static bool ltc4245_use_extra_gpios(struct i2c_client *client) return false; } -static int ltc4245_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc4245_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct ltc4245_data *data; @@ -480,7 +479,7 @@ static struct i2c_driver ltc4245_driver = { .driver = { .name = "ltc4245", }, - .probe = ltc4245_probe, + .probe_new = ltc4245_probe, .id_table = ltc4245_id, }; diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c index 8b8fd4a313ee..75e89cec381e 100644 --- a/drivers/hwmon/ltc4260.c +++ b/drivers/hwmon/ltc4260.c @@ -79,7 +79,7 @@ static ssize_t ltc4260_value_show(struct device *dev, value = ltc4260_get_value(dev, attr->index); if (value < 0) return value; - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t ltc4260_bool_show(struct device *dev, @@ -98,7 +98,7 @@ static ssize_t ltc4260_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0); - return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); + return sysfs_emit(buf, "%d\n", !!fault); } /* Voltages */ @@ -141,8 +141,7 @@ static const struct regmap_config ltc4260_regmap_config = { .max_register = LTC4260_ADIN, }; -static int ltc4260_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc4260_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -174,7 +173,7 @@ static struct i2c_driver ltc4260_driver = { .driver = { .name = "ltc4260", }, - .probe = ltc4260_probe, + .probe_new = ltc4260_probe, .id_table = ltc4260_id, }; diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index c415829ffbf5..b91cc4fe84e5 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -73,13 +73,13 @@ static struct ltc4261_data *ltc4261_update_device(struct device *dev) "Failed to read ADC value: error %d\n", val); ret = ERR_PTR(val); - data->valid = 0; + data->valid = false; goto abort; } data->regs[i] = val; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } abort: mutex_unlock(&data->update_lock); @@ -130,7 +130,7 @@ static ssize_t ltc4261_value_show(struct device *dev, return PTR_ERR(data); value = ltc4261_get_value(data, attr->index); - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t ltc4261_bool_show(struct device *dev, @@ -147,7 +147,7 @@ static ssize_t ltc4261_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ i2c_smbus_write_byte_data(data->client, LTC4261_FAULT, ~fault); - return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); + return sysfs_emit(buf, "%d\n", fault ? 1 : 0); } /* @@ -190,8 +190,7 @@ static struct attribute *ltc4261_attrs[] = { }; ATTRIBUTE_GROUPS(ltc4261); -static int ltc4261_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc4261_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; @@ -234,7 +233,7 @@ static struct i2c_driver ltc4261_driver = { .driver = { .name = "ltc4261", }, - .probe = ltc4261_probe, + .probe_new = ltc4261_probe, .id_table = ltc4261_id, }; diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 5fcfd57df61e..4c5487aeb3cf 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -254,7 +254,7 @@ err_remove: return err; } -static int max1111_remove(struct spi_device *spi) +static void max1111_remove(struct spi_device *spi) { struct max1111_data *data = spi_get_drvdata(spi); @@ -265,7 +265,6 @@ static int max1111_remove(struct spi_device *spi) sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group); sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); mutex_destroy(&data->drvdata_lock); - return 0; } static const struct spi_device_id max1111_ids[] = { diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c new file mode 100644 index 000000000000..402ffdc2f425 --- /dev/null +++ b/drivers/hwmon/max127.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for MAX127. + * + * Copyright (c) 2020 Facebook Inc. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> + +/* + * MAX127 Control Byte. Refer to MAX127 datasheet, Table 1 "Control-Byte + * Format" for details. + */ +#define MAX127_CTRL_START BIT(7) +#define MAX127_CTRL_SEL_SHIFT 4 +#define MAX127_CTRL_RNG BIT(3) +#define MAX127_CTRL_BIP BIT(2) +#define MAX127_CTRL_PD1 BIT(1) +#define MAX127_CTRL_PD0 BIT(0) + +#define MAX127_NUM_CHANNELS 8 +#define MAX127_SET_CHANNEL(ch) (((ch) & 7) << MAX127_CTRL_SEL_SHIFT) + +/* + * MAX127 channel input ranges. Refer to MAX127 datasheet, Table 3 "Range + * and Polarity Selection" for details. + */ +#define MAX127_FULL_RANGE 10000 /* 10V */ +#define MAX127_HALF_RANGE 5000 /* 5V */ + +/* + * MAX127 returns 2 bytes at read: + * - the first byte contains data[11:4]. + * - the second byte contains data[3:0] (MSB) and 4 dummy 0s (LSB). + * Refer to MAX127 datasheet, "Read a Conversion (Read Cycle)" section + * for details. + */ +#define MAX127_DATA_LEN 2 +#define MAX127_DATA_SHIFT 4 + +#define MAX127_SIGN_BIT BIT(11) + +struct max127_data { + struct mutex lock; + struct i2c_client *client; + u8 ctrl_byte[MAX127_NUM_CHANNELS]; +}; + +static int max127_select_channel(struct i2c_client *client, u8 ctrl_byte) +{ + int status; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(ctrl_byte), + .buf = &ctrl_byte, + }; + + status = i2c_transfer(client->adapter, &msg, 1); + if (status < 0) + return status; + if (status != 1) + return -EIO; + + return 0; +} + +static int max127_read_channel(struct i2c_client *client, long *val) +{ + int status; + u8 i2c_data[MAX127_DATA_LEN]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(i2c_data), + .buf = i2c_data, + }; + + status = i2c_transfer(client->adapter, &msg, 1); + if (status < 0) + return status; + if (status != 1) + return -EIO; + + *val = (i2c_data[1] >> MAX127_DATA_SHIFT) | + ((u16)i2c_data[0] << MAX127_DATA_SHIFT); + return 0; +} + +static long max127_process_raw(u8 ctrl_byte, long raw) +{ + long scale, weight; + + /* + * MAX127's data coding is binary in unipolar mode with 1 LSB = + * (Full-Scale/4096) and two’s complement binary in bipolar mode + * with 1 LSB = [(2 x |FS|)/4096]. + * Refer to MAX127 datasheet, "Transfer Function" section for + * details. + */ + scale = (ctrl_byte & MAX127_CTRL_RNG) ? MAX127_FULL_RANGE : + MAX127_HALF_RANGE; + if (ctrl_byte & MAX127_CTRL_BIP) { + weight = (raw & MAX127_SIGN_BIT); + raw &= ~MAX127_SIGN_BIT; + raw -= weight; + raw *= 2; + } + + return raw * scale / 4096; +} + +static int max127_read_input(struct max127_data *data, int channel, long *val) +{ + long raw; + int status; + struct i2c_client *client = data->client; + u8 ctrl_byte = data->ctrl_byte[channel]; + + mutex_lock(&data->lock); + + status = max127_select_channel(client, ctrl_byte); + if (status) + goto exit; + + status = max127_read_channel(client, &raw); + if (status) + goto exit; + + *val = max127_process_raw(ctrl_byte, raw); + +exit: + mutex_unlock(&data->lock); + return status; +} + +static int max127_read_min(struct max127_data *data, int channel, long *val) +{ + u8 rng_bip = (data->ctrl_byte[channel] >> 2) & 3; + static const int min_input_map[4] = { + 0, /* RNG=0, BIP=0 */ + -MAX127_HALF_RANGE, /* RNG=0, BIP=1 */ + 0, /* RNG=1, BIP=0 */ + -MAX127_FULL_RANGE, /* RNG=1, BIP=1 */ + }; + + *val = min_input_map[rng_bip]; + return 0; +} + +static int max127_read_max(struct max127_data *data, int channel, long *val) +{ + u8 rng_bip = (data->ctrl_byte[channel] >> 2) & 3; + static const int max_input_map[4] = { + MAX127_HALF_RANGE, /* RNG=0, BIP=0 */ + MAX127_HALF_RANGE, /* RNG=0, BIP=1 */ + MAX127_FULL_RANGE, /* RNG=1, BIP=0 */ + MAX127_FULL_RANGE, /* RNG=1, BIP=1 */ + }; + + *val = max_input_map[rng_bip]; + return 0; +} + +static int max127_write_min(struct max127_data *data, int channel, long val) +{ + u8 ctrl; + + mutex_lock(&data->lock); + + ctrl = data->ctrl_byte[channel]; + if (val <= -MAX127_FULL_RANGE) { + ctrl |= (MAX127_CTRL_RNG | MAX127_CTRL_BIP); + } else if (val < 0) { + ctrl |= MAX127_CTRL_BIP; + ctrl &= ~MAX127_CTRL_RNG; + } else { + ctrl &= ~MAX127_CTRL_BIP; + } + data->ctrl_byte[channel] = ctrl; + + mutex_unlock(&data->lock); + + return 0; +} + +static int max127_write_max(struct max127_data *data, int channel, long val) +{ + mutex_lock(&data->lock); + + if (val >= MAX127_FULL_RANGE) + data->ctrl_byte[channel] |= MAX127_CTRL_RNG; + else + data->ctrl_byte[channel] &= ~MAX127_CTRL_RNG; + + mutex_unlock(&data->lock); + + return 0; +} + +static umode_t max127_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + return 0444; + + case hwmon_in_min: + case hwmon_in_max: + return 0644; + + default: + break; + } + } + + return 0; +} + +static int max127_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int status; + struct max127_data *data = dev_get_drvdata(dev); + + if (type != hwmon_in) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_in_input: + status = max127_read_input(data, channel, val); + break; + + case hwmon_in_min: + status = max127_read_min(data, channel, val); + break; + + case hwmon_in_max: + status = max127_read_max(data, channel, val); + break; + + default: + status = -EOPNOTSUPP; + break; + } + + return status; +} + +static int max127_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + int status; + struct max127_data *data = dev_get_drvdata(dev); + + if (type != hwmon_in) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_in_min: + status = max127_write_min(data, channel, val); + break; + + case hwmon_in_max: + status = max127_write_max(data, channel, val); + break; + + default: + status = -EOPNOTSUPP; + break; + } + + return status; +} + +static const struct hwmon_ops max127_hwmon_ops = { + .is_visible = max127_is_visible, + .read = max127_read, + .write = max127_write, +}; + +static const struct hwmon_channel_info *max127_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX), + NULL, +}; + +static const struct hwmon_chip_info max127_chip_info = { + .ops = &max127_hwmon_ops, + .info = max127_info, +}; + +static int max127_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i; + struct device *hwmon_dev; + struct max127_data *data; + struct device *dev = &client->dev; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->lock); + for (i = 0; i < ARRAY_SIZE(data->ctrl_byte); i++) + data->ctrl_byte[i] = (MAX127_CTRL_START | + MAX127_SET_CHANNEL(i)); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &max127_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max127_id[] = { + { "max127", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max127_id); + +static struct i2c_driver max127_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max127", + }, + .probe = max127_probe, + .id_table = max127_id, +}; + +module_i2c_driver(max127_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Choi <mikechoi@fb.com>"); +MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>"); +MODULE_DESCRIPTION("MAX127 Hardware Monitoring driver"); diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 49b7e0b6d1bb..daa5d8af1e69 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -166,7 +166,7 @@ static struct max16065_data *max16065_update_device(struct device *dev) = i2c_smbus_read_byte_data(client, MAX16065_FAULT(i)); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); return data; @@ -187,7 +187,7 @@ static ssize_t max16065_alarm_show(struct device *dev, i2c_smbus_write_byte_data(data->client, MAX16065_FAULT(attr2->nr), val); - return snprintf(buf, PAGE_SIZE, "%d\n", !!val); + return sysfs_emit(buf, "%d\n", !!val); } static ssize_t max16065_input_show(struct device *dev, @@ -200,8 +200,8 @@ static ssize_t max16065_input_show(struct device *dev, if (unlikely(adc < 0)) return adc; - return snprintf(buf, PAGE_SIZE, "%d\n", - ADC_TO_MV(adc, data->range[attr->index])); + return sysfs_emit(buf, "%d\n", + ADC_TO_MV(adc, data->range[attr->index])); } static ssize_t max16065_current_show(struct device *dev, @@ -212,8 +212,8 @@ static ssize_t max16065_current_show(struct device *dev, if (unlikely(data->curr_sense < 0)) return data->curr_sense; - return snprintf(buf, PAGE_SIZE, "%d\n", - ADC_TO_CURR(data->curr_sense, data->curr_gain)); + return sysfs_emit(buf, "%d\n", + ADC_TO_CURR(data->curr_sense, data->curr_gain)); } static ssize_t max16065_limit_store(struct device *dev, @@ -249,8 +249,8 @@ static ssize_t max16065_limit_show(struct device *dev, struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da); struct max16065_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", - data->limit[attr2->nr][attr2->index]); + return sysfs_emit(buf, "%d\n", + data->limit[attr2->nr][attr2->index]); } /* Construct a sensor_device_attribute structure for each register */ @@ -454,7 +454,7 @@ static struct attribute *max16065_max_attributes[] = { static umode_t max16065_basic_is_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max16065_data *data = dev_get_drvdata(dev); int index = n / 4; @@ -466,7 +466,7 @@ static umode_t max16065_basic_is_visible(struct kobject *kobj, static umode_t max16065_secondary_is_visible(struct kobject *kobj, struct attribute *a, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max16065_data *data = dev_get_drvdata(dev); if (index >= data->num_adc) @@ -493,8 +493,9 @@ static const struct attribute_group max16065_max_group = { .is_visible = max16065_secondary_is_visible, }; -static int max16065_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id max16065_id[]; + +static int max16065_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct max16065_data *data; @@ -504,6 +505,7 @@ static int max16065_probe(struct i2c_client *client, bool have_secondary; /* true if chip has secondary limits */ bool secondary_is_max = false; /* secondary limits reflect max */ int groups = 0; + const struct i2c_device_id *id = i2c_match_id(max16065_id, client); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_WORD_DATA)) @@ -598,7 +600,7 @@ static struct i2c_driver max16065_driver = { .driver = { .name = "max16065", }, - .probe = max16065_probe, + .probe_new = max16065_probe, .id_table = max16065_id, }; diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 87c6665bab3a..445c77197f69 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -79,7 +79,7 @@ enum temp_index { struct max1619_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values */ @@ -124,7 +124,7 @@ static struct max1619_data *max1619_update_device(struct device *dev) data->alarms ^= 0x02; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -241,7 +241,7 @@ static int max1619_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "max1619", I2C_NAME_SIZE); + strscpy(info->type, "max1619", I2C_NAME_SIZE); return 0; } @@ -261,8 +261,7 @@ static void max1619_init_client(struct i2c_client *client) config & 0xBF); /* run */ } -static int max1619_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) +static int max1619_probe(struct i2c_client *new_client) { struct max1619_data *data; struct device *hwmon_dev; @@ -306,7 +305,7 @@ static struct i2c_driver max1619_driver = { .name = "max1619", .of_match_table = of_match_ptr(max1619_of_match), }, - .probe = max1619_probe, + .probe_new = max1619_probe, .id_table = max1619_id, .detect = max1619_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index fb6d17287365..9f748973d6a3 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -58,7 +58,7 @@ struct max1668_data { enum chips type; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ /* 1x local and 4x remote */ @@ -120,7 +120,7 @@ static struct max1668_data *max1668_update_device(struct device *dev) data->alarms |= val; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; abort: mutex_unlock(&data->update_lock); @@ -386,13 +386,14 @@ static int max1668_detect(struct i2c_client *client, if (!type_name) return -ENODEV; - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } -static int max1668_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id max1668_id[]; + +static int max1668_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; @@ -407,7 +408,7 @@ static int max1668_probe(struct i2c_client *client, return -ENOMEM; data->client = client; - data->type = id->driver_data; + data->type = i2c_match_id(max1668_id, client)->driver_data; mutex_init(&data->update_lock); /* sysfs hooks */ @@ -434,7 +435,7 @@ static struct i2c_driver max1668_driver = { .driver = { .name = "max1668", }, - .probe = max1668_probe, + .probe_new = max1668_probe, .id_table = max1668_id, .detect = max1668_detect, .address_list = max1668_addr_list, diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c index 062eceb7be0d..9a31ef388396 100644 --- a/drivers/hwmon/max31722.c +++ b/drivers/hwmon/max31722.c @@ -6,7 +6,6 @@ * Copyright (c) 2016, Intel Corporation. */ -#include <linux/acpi.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/kernel.h> @@ -101,16 +100,20 @@ static int max31722_probe(struct spi_device *spi) return 0; } -static int max31722_remove(struct spi_device *spi) +static void max31722_remove(struct spi_device *spi) { struct max31722_data *data = spi_get_drvdata(spi); + int ret; hwmon_device_unregister(data->hwmon_dev); - return max31722_set_mode(data, MAX31722_MODE_STANDBY); + ret = max31722_set_mode(data, MAX31722_MODE_STANDBY); + if (ret) + /* There is nothing we can do about this ... */ + dev_warn(&spi->dev, "Failed to put device in stand-by mode\n"); } -static int __maybe_unused max31722_suspend(struct device *dev) +static int max31722_suspend(struct device *dev) { struct spi_device *spi_device = to_spi_device(dev); struct max31722_data *data = spi_get_drvdata(spi_device); @@ -118,7 +121,7 @@ static int __maybe_unused max31722_suspend(struct device *dev) return max31722_set_mode(data, MAX31722_MODE_STANDBY); } -static int __maybe_unused max31722_resume(struct device *dev) +static int max31722_resume(struct device *dev) { struct spi_device *spi_device = to_spi_device(dev); struct max31722_data *data = spi_get_drvdata(spi_device); @@ -126,27 +129,19 @@ static int __maybe_unused max31722_resume(struct device *dev) return max31722_set_mode(data, MAX31722_MODE_CONTINUOUS); } -static SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume); static const struct spi_device_id max31722_spi_id[] = { {"max31722", 0}, {"max31723", 0}, {} }; - -static const struct acpi_device_id __maybe_unused max31722_acpi_id[] = { - {"MAX31722", 0}, - {"MAX31723", 0}, - {} -}; - MODULE_DEVICE_TABLE(spi, max31722_spi_id); static struct spi_driver max31722_driver = { .driver = { .name = "max31722", - .pm = &max31722_pm_ops, - .acpi_match_table = ACPI_PTR(max31722_acpi_id), + .pm = pm_sleep_ptr(&max31722_pm_ops), }, .probe = max31722_probe, .remove = max31722_remove, diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c index eb22a34dc36b..746a767c9fc6 100644 --- a/drivers/hwmon/max31730.c +++ b/drivers/hwmon/max31730.c @@ -292,7 +292,7 @@ static void max31730_remove(void *data) } static int -max31730_probe(struct i2c_client *client, const struct i2c_device_id *id) +max31730_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -399,35 +399,35 @@ static int max31730_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "max31730", I2C_NAME_SIZE); + strscpy(info->type, "max31730", I2C_NAME_SIZE); return 0; } -static int __maybe_unused max31730_suspend(struct device *dev) +static int max31730_suspend(struct device *dev) { struct max31730_data *data = dev_get_drvdata(dev); return max31730_write_config(data, MAX31730_STOP, 0); } -static int __maybe_unused max31730_resume(struct device *dev) +static int max31730_resume(struct device *dev) { struct max31730_data *data = dev_get_drvdata(dev); return max31730_write_config(data, 0, MAX31730_STOP); } -static SIMPLE_DEV_PM_OPS(max31730_pm_ops, max31730_suspend, max31730_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max31730_pm_ops, max31730_suspend, max31730_resume); static struct i2c_driver max31730_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "max31730", .of_match_table = of_match_ptr(max31730_of_match), - .pm = &max31730_pm_ops, + .pm = pm_sleep_ptr(&max31730_pm_ops), }, - .probe = max31730_probe, + .probe_new = max31730_probe, .id_table = max31730_ids, .detect = max31730_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/max31760.c b/drivers/hwmon/max31760.c new file mode 100644 index 000000000000..06d5f39dc33d --- /dev/null +++ b/drivers/hwmon/max31760.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/util_macros.h> + +#define REG_CR1 0x00 +#define CR1_HYST BIT(5) +#define CR1_DRV GENMASK(4, 3) +#define CR1_TEMP_SRC GENMASK(1, 0) +#define REG_CR2 0x01 +#define CR2_STBY BIT(7) +#define CR2_ALERTS BIT(6) +#define CR2_DFC BIT(0) +#define REG_CR3 0x02 +#define REG_PWMR 0x50 +#define REG_PWMV 0x51 +#define REG_STATUS 0x5A +#define STATUS_ALARM_CRIT(ch) BIT(2 + 2 * (ch)) +#define STATUS_ALARM_MAX(ch) BIT(3 + 2 * (ch)) +#define STATUS_RDFA BIT(6) + +#define REG_TACH(ch) (0x52 + (ch) * 2) +#define REG_TEMP_INPUT(ch) (0x56 + (ch) * 2) +#define REG_TEMP_MAX(ch) (0x06 + (ch) * 2) +#define REG_TEMP_CRIT(ch) (0x0A + (ch) * 2) + +#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125) +#define TEMP11_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \ + 127875), 125) * 32) + +#define LUT_SIZE 48 + +#define REG_LUT(index) (0x20 + (index)) + +struct max31760_state { + struct regmap *regmap; + + struct lut_attribute { + char name[24]; + struct sensor_device_attribute sda; + } lut[LUT_SIZE]; + + struct attribute *attrs[LUT_SIZE + 2]; + struct attribute_group group; + const struct attribute_group *groups[2]; +}; + +static bool max31760_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg > 0x50; +} + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x5B, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = max31760_volatile_reg, +}; + +static const int max31760_pwm_freq[] = {33, 150, 1500, 25000}; + +static int tach_to_rpm(u16 tach) +{ + if (tach == 0) + tach = 1; + + return 60 * 100000 / tach / 2; +} + +static int max31760_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int regval; + unsigned int reg_temp; + s16 temp; + u8 reg[2]; + int ret; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_fault: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + *val = FIELD_GET(STATUS_RDFA, regval); + + return 0; + case hwmon_temp_max_alarm: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(STATUS_ALARM_MAX(1), regval); + else + *val = FIELD_GET(STATUS_ALARM_MAX(0), regval); + + return 0; + case hwmon_temp_crit_alarm: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(STATUS_ALARM_CRIT(1), regval); + else + *val = FIELD_GET(STATUS_ALARM_CRIT(0), regval); + + return 0; + case hwmon_temp_input: + reg_temp = REG_TEMP_INPUT(channel); + break; + case hwmon_temp_max: + reg_temp = REG_TEMP_MAX(channel); + break; + case hwmon_temp_crit: + reg_temp = REG_TEMP_CRIT(channel); + break; + default: + return -EOPNOTSUPP; + } + + ret = regmap_bulk_read(state->regmap, reg_temp, reg, 2); + if (ret) + return ret; + + temp = (reg[0] << 8) | reg[1]; + + *val = TEMP11_FROM_REG(temp); + + return 0; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + ret = regmap_bulk_read(state->regmap, REG_TACH(channel), reg, 2); + if (ret) + return ret; + + *val = tach_to_rpm(reg[0] * 256 + reg[1]); + + return 0; + case hwmon_fan_fault: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(BIT(1), regval); + else + *val = FIELD_GET(BIT(0), regval); + + return 0; + case hwmon_fan_enable: + ret = regmap_read(state->regmap, REG_CR3, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(BIT(1), regval); + else + *val = FIELD_GET(BIT(0), regval); + + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = regmap_read(state->regmap, REG_PWMV, ®val); + if (ret) + return ret; + + *val = regval; + + return 0; + case hwmon_pwm_freq: + ret = regmap_read(state->regmap, REG_CR1, ®val); + if (ret) + return ret; + + regval = FIELD_GET(CR1_DRV, regval); + if (regval >= ARRAY_SIZE(max31760_pwm_freq)) + return -EINVAL; + + *val = max31760_pwm_freq[regval]; + + return 0; + case hwmon_pwm_enable: + ret = regmap_read(state->regmap, REG_CR2, ®val); + if (ret) + return ret; + + *val = 2 - FIELD_GET(CR2_DFC, regval); + + return 0; + case hwmon_pwm_auto_channels_temp: + ret = regmap_read(state->regmap, REG_CR1, ®val); + if (ret) + return ret; + + switch (FIELD_GET(CR1_TEMP_SRC, regval)) { + case 0: + *val = 2; + break; + case 1: + *val = 1; + break; + case 2: + case 3: + *val = 3; + break; + default: + return -EINVAL; + } + + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int max31760_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int pwm_index; + unsigned int reg_temp; + int temp; + u8 reg_val[2]; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_max: + reg_temp = REG_TEMP_MAX(channel); + break; + case hwmon_temp_crit: + reg_temp = REG_TEMP_CRIT(channel); + break; + default: + return -EOPNOTSUPP; + } + + temp = TEMP11_TO_REG(val); + reg_val[0] = temp >> 8; + reg_val[1] = temp & 0xFF; + + return regmap_bulk_write(state->regmap, reg_temp, reg_val, 2); + case hwmon_fan: + switch (attr) { + case hwmon_fan_enable: + if (val == 0) + return regmap_clear_bits(state->regmap, REG_CR3, BIT(channel)); + + if (val == 1) + return regmap_set_bits(state->regmap, REG_CR3, BIT(channel)); + + return -EINVAL; + default: + return -EOPNOTSUPP; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + + return regmap_write(state->regmap, REG_PWMR, val); + case hwmon_pwm_enable: + if (val == 1) + return regmap_set_bits(state->regmap, REG_CR2, CR2_DFC); + + if (val == 2) + return regmap_clear_bits(state->regmap, REG_CR2, CR2_DFC); + + return -EINVAL; + case hwmon_pwm_freq: + pwm_index = find_closest(val, max31760_pwm_freq, + ARRAY_SIZE(max31760_pwm_freq)); + + return regmap_update_bits(state->regmap, + REG_CR1, CR1_DRV, + FIELD_PREP(CR1_DRV, pwm_index)); + case hwmon_pwm_auto_channels_temp: + switch (val) { + case 1: + break; + case 2: + val = 0; + break; + case 3: + val = 2; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(state->regmap, REG_CR1, CR1_TEMP_SRC, val); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *max31760_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_ENABLE | HWMON_PWM_FREQ | HWMON_PWM_INPUT | + HWMON_PWM_AUTO_CHANNELS_TEMP), + NULL +}; + +static umode_t max31760_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_fault: + case hwmon_temp_label: + return 0444; + case hwmon_temp_max: + case hwmon_temp_crit: + return 0644; + default: + return 0; + } + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + return 0444; + case hwmon_fan_enable: + return 0644; + default: + return 0; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + case hwmon_pwm_input: + case hwmon_pwm_freq: + case hwmon_pwm_auto_channels_temp: + return 0644; + default: + return 0; + } + default: + return 0; + } +} + +static int max31760_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_temp: + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = channel ? "local" : "remote"; + + return 0; + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops max31760_hwmon_ops = { + .is_visible = max31760_is_visible, + .read = max31760_read, + .write = max31760_write, + .read_string = max31760_read_string +}; + +static const struct hwmon_chip_info max31760_chip_info = { + .ops = &max31760_hwmon_ops, + .info = max31760_info, +}; + +static ssize_t lut_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + struct max31760_state *state = dev_get_drvdata(dev); + int ret; + unsigned int regval; + + ret = regmap_read(state->regmap, REG_LUT(sda->index), ®val); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", regval); +} + +static ssize_t lut_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + struct max31760_state *state = dev_get_drvdata(dev); + int ret; + u8 pwm; + + ret = kstrtou8(buf, 10, &pwm); + if (ret) + return ret; + + ret = regmap_write(state->regmap, REG_LUT(sda->index), pwm); + if (ret) + return ret; + + return count; +} + +static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int regval; + int ret; + + ret = regmap_read(state->regmap, REG_CR1, ®val); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", (1 + (int)FIELD_GET(CR1_HYST, regval)) * 2000); +} + +static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int hyst; + int ret; + + ret = kstrtou32(buf, 10, &hyst); + if (ret) + return ret; + + if (hyst < 3000) + ret = regmap_clear_bits(state->regmap, REG_CR1, CR1_HYST); + else + ret = regmap_set_bits(state->regmap, REG_CR1, CR1_HYST); + + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst); + +static void max31760_create_lut_nodes(struct max31760_state *state) +{ + int i; + struct sensor_device_attribute *sda; + struct lut_attribute *lut; + + for (i = 0; i < LUT_SIZE; ++i) { + lut = &state->lut[i]; + sda = &lut->sda; + + snprintf(lut->name, sizeof(lut->name), + "pwm1_auto_point%d_pwm", i + 1); + + sda->dev_attr.attr.mode = 0644; + sda->index = i; + sda->dev_attr.show = lut_show; + sda->dev_attr.store = lut_store; + sda->dev_attr.attr.name = lut->name; + + sysfs_attr_init(&sda->dev_attr.attr); + + state->attrs[i] = &sda->dev_attr.attr; + } + + state->attrs[i] = &dev_attr_pwm1_auto_point_temp_hyst.attr; + + state->group.attrs = state->attrs; + state->groups[0] = &state->group; +} + +static int max31760_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max31760_state *state; + struct device *hwmon_dev; + int ret; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(state->regmap)) + return dev_err_probe(dev, + PTR_ERR(state->regmap), + "regmap initialization failed\n"); + + dev_set_drvdata(dev, state); + + /* Set alert output to comparator mode */ + ret = regmap_set_bits(state->regmap, REG_CR2, CR2_ALERTS); + if (ret) + return dev_err_probe(dev, ret, "cannot write register\n"); + + max31760_create_lut_nodes(state); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + state, + &max31760_chip_info, + state->groups); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id max31760_of_match[] = { + {.compatible = "adi,max31760"}, + { } +}; +MODULE_DEVICE_TABLE(of, max31760_of_match); + +static const struct i2c_device_id max31760_id[] = { + {"max31760"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31760_id); + +static int max31760_suspend(struct device *dev) +{ + struct max31760_state *state = dev_get_drvdata(dev); + + return regmap_set_bits(state->regmap, REG_CR2, CR2_STBY); +} + +static int max31760_resume(struct device *dev) +{ + struct max31760_state *state = dev_get_drvdata(dev); + + return regmap_clear_bits(state->regmap, REG_CR2, CR2_STBY); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(max31760_pm_ops, max31760_suspend, + max31760_resume); + +static struct i2c_driver max31760_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max31760", + .of_match_table = max31760_of_match, + .pm = pm_ptr(&max31760_pm_ops) + }, + .probe_new = max31760_probe, + .id_table = max31760_id +}; +module_i2c_driver(max31760_driver); + +MODULE_AUTHOR("Ibrahim Tilki <Ibrahim.Tilki@analog.com>"); +MODULE_DESCRIPTION("Analog Devices MAX31760 Fan Speed Controller"); +MODULE_SOFTDEP("pre: regmap_i2c"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 117fb79ef294..20bf5ffadefe 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -27,6 +27,7 @@ /* Fan Config register bits */ #define MAX31790_FAN_CFG_RPM_MODE 0x80 +#define MAX31790_FAN_CFG_CTRL_MON 0x10 #define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08 #define MAX31790_FAN_CFG_TACH_INPUT 0x01 @@ -39,6 +40,8 @@ #define FAN_RPM_MIN 120 #define FAN_RPM_MAX 7864320 +#define FAN_COUNT_REG_MAX 0xffe0 + #define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \ ((60 * (sr) * 8192) / ((reg) >> 4)) : \ FAN_RPM_MAX) @@ -79,7 +82,7 @@ static struct max31790_data *max31790_update_device(struct device *dev) MAX31790_REG_FAN_FAULT_STATUS1); if (rv < 0) goto abort; - data->fault_status = rv & 0x3F; + data->fault_status |= rv & 0x3F; rv = i2c_smbus_read_byte_data(client, MAX31790_REG_FAN_FAULT_STATUS2); @@ -104,7 +107,7 @@ static struct max31790_data *max31790_update_device(struct device *dev) data->tach[NR_CHANNEL + i] = rv; } else { rv = i2c_smbus_read_word_swapped(client, - MAX31790_REG_PWMOUT(i)); + MAX31790_REG_PWM_DUTY_CYCLE(i)); if (rv < 0) goto abort; data->pwm[i] = rv; @@ -170,8 +173,11 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, switch (attr) { case hwmon_fan_input: - sr = get_tach_period(data->fan_dynamics[channel]); - rpm = RPM_FROM_REG(data->tach[channel], sr); + sr = get_tach_period(data->fan_dynamics[channel % NR_CHANNEL]); + if (data->tach[channel] == FAN_COUNT_REG_MAX) + rpm = 0; + else + rpm = RPM_FROM_REG(data->tach[channel], sr); *val = rpm; return 0; case hwmon_fan_target: @@ -180,7 +186,24 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, *val = rpm; return 0; case hwmon_fan_fault: + mutex_lock(&data->update_lock); *val = !!(data->fault_status & (1 << channel)); + data->fault_status &= ~(1 << channel); + /* + * If a fault bit is set, we need to write into one of the fan + * configuration registers to clear it. Note that this also + * clears the fault for the companion channel if enabled. + */ + if (*val) { + int reg = MAX31790_REG_TARGET_COUNT(channel % NR_CHANNEL); + + i2c_smbus_write_byte_data(data->client, reg, + data->target_count[channel % NR_CHANNEL] >> 8); + } + mutex_unlock(&data->update_lock); + return 0; + case hwmon_fan_enable: + *val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN); return 0; default: return -EOPNOTSUPP; @@ -194,7 +217,7 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel, struct i2c_client *client = data->client; int target_count; int err = 0; - u8 bits; + u8 bits, fan_config; int sr; mutex_lock(&data->update_lock); @@ -223,6 +246,23 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel, MAX31790_REG_TARGET_COUNT(channel), data->target_count[channel]); break; + case hwmon_fan_enable: + fan_config = data->fan_config[channel]; + if (val == 0) { + fan_config &= ~MAX31790_FAN_CFG_TACH_INPUT_EN; + } else if (val == 1) { + fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN; + } else { + err = -EINVAL; + break; + } + if (fan_config != data->fan_config[channel]) { + err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel), + fan_config); + if (!err) + data->fan_config[channel] = fan_config; + } + break; default: err = -EOPNOTSUPP; break; @@ -250,6 +290,10 @@ static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel) !(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) return 0644; return 0; + case hwmon_fan_enable: + if (channel < NR_CHANNEL) + return 0644; + return 0; default: return 0; } @@ -271,12 +315,12 @@ static int max31790_read_pwm(struct device *dev, u32 attr, int channel, *val = data->pwm[channel] >> 8; return 0; case hwmon_pwm_enable: - if (fan_config & MAX31790_FAN_CFG_RPM_MODE) + if (fan_config & MAX31790_FAN_CFG_CTRL_MON) + *val = 0; + else if (fan_config & MAX31790_FAN_CFG_RPM_MODE) *val = 2; - else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN) - *val = 1; else - *val = 0; + *val = 1; return 0; default: return -EOPNOTSUPP; @@ -299,31 +343,41 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel, err = -EINVAL; break; } - data->pwm[channel] = val << 8; + data->valid = false; err = i2c_smbus_write_word_swapped(client, MAX31790_REG_PWMOUT(channel), - data->pwm[channel]); + val << 8); break; case hwmon_pwm_enable: fan_config = data->fan_config[channel]; if (val == 0) { - fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN | - MAX31790_FAN_CFG_RPM_MODE); + fan_config |= MAX31790_FAN_CFG_CTRL_MON; + /* + * Disable RPM mode; otherwise disabling fan speed + * monitoring is not possible. + */ + fan_config &= ~MAX31790_FAN_CFG_RPM_MODE; } else if (val == 1) { - fan_config = (fan_config | - MAX31790_FAN_CFG_TACH_INPUT_EN) & - ~MAX31790_FAN_CFG_RPM_MODE; + fan_config &= ~(MAX31790_FAN_CFG_CTRL_MON | MAX31790_FAN_CFG_RPM_MODE); } else if (val == 2) { - fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN | - MAX31790_FAN_CFG_RPM_MODE; + fan_config &= ~MAX31790_FAN_CFG_CTRL_MON; + /* + * The chip sets MAX31790_FAN_CFG_TACH_INPUT_EN on its + * own if MAX31790_FAN_CFG_RPM_MODE is set. + * Do it here as well to reflect the actual register + * value in the cache. + */ + fan_config |= (MAX31790_FAN_CFG_RPM_MODE | MAX31790_FAN_CFG_TACH_INPUT_EN); } else { err = -EINVAL; break; } - data->fan_config[channel] = fan_config; - err = i2c_smbus_write_byte_data(client, - MAX31790_REG_FAN_CONFIG(channel), - fan_config); + if (fan_config != data->fan_config[channel]) { + err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel), + fan_config); + if (!err) + data->fan_config[channel] = fan_config; + } break; default: err = -EOPNOTSUPP; @@ -393,12 +447,12 @@ static umode_t max31790_is_visible(const void *data, static const struct hwmon_channel_info *max31790_info[] = { HWMON_CHANNEL_INFO(fan, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT, @@ -448,8 +502,7 @@ static int max31790_init_client(struct i2c_client *client, return 0; } -static int max31790_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max31790_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; @@ -491,7 +544,7 @@ MODULE_DEVICE_TABLE(i2c, max31790_id); static struct i2c_driver max31790_driver = { .class = I2C_CLASS_HWMON, - .probe = max31790_probe, + .probe_new = max31790_probe, .driver = { .name = "max31790", }, diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c new file mode 100644 index 000000000000..202b6438179d --- /dev/null +++ b/drivers/hwmon/max6620.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Maxim MAX6620 + * + * Originally from L. Grunenberg. + * (C) 2012 by L. Grunenberg <contact@lgrunenberg.de> + * + * Copyright (c) 2021 Dell Inc. or its subsidiaries. All Rights Reserved. + * + * based on code written by : + * 2007 by Hans J. Koch <hjk@hansjkoch.de> + * John Morris <john.morris@spirentcom.com> + * Copyright (c) 2003 Spirent Communications + * and Claus Gindhart <claus.gindhart@kontron.com> + * + * This module has only been tested with the MAX6620 chip. + * + * The datasheet was last seen at: + * + * http://pdfserv.maxim-ic.com/en/ds/MAX6620.pdf + * + */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/slab.h> + +/* + * MAX 6620 registers + */ + +#define MAX6620_REG_CONFIG 0x00 +#define MAX6620_REG_FAULT 0x01 +#define MAX6620_REG_CONF_FAN0 0x02 +#define MAX6620_REG_CONF_FAN1 0x03 +#define MAX6620_REG_CONF_FAN2 0x04 +#define MAX6620_REG_CONF_FAN3 0x05 +#define MAX6620_REG_DYN_FAN0 0x06 +#define MAX6620_REG_DYN_FAN1 0x07 +#define MAX6620_REG_DYN_FAN2 0x08 +#define MAX6620_REG_DYN_FAN3 0x09 +#define MAX6620_REG_TACH0 0x10 +#define MAX6620_REG_TACH1 0x12 +#define MAX6620_REG_TACH2 0x14 +#define MAX6620_REG_TACH3 0x16 +#define MAX6620_REG_VOLT0 0x18 +#define MAX6620_REG_VOLT1 0x1A +#define MAX6620_REG_VOLT2 0x1C +#define MAX6620_REG_VOLT3 0x1E +#define MAX6620_REG_TAR0 0x20 +#define MAX6620_REG_TAR1 0x22 +#define MAX6620_REG_TAR2 0x24 +#define MAX6620_REG_TAR3 0x26 +#define MAX6620_REG_DAC0 0x28 +#define MAX6620_REG_DAC1 0x2A +#define MAX6620_REG_DAC2 0x2C +#define MAX6620_REG_DAC3 0x2E + +/* + * Config register bits + */ + +#define MAX6620_CFG_RUN BIT(7) +#define MAX6620_CFG_POR BIT(6) +#define MAX6620_CFG_TIMEOUT BIT(5) +#define MAX6620_CFG_FULLFAN BIT(4) +#define MAX6620_CFG_OSC BIT(3) +#define MAX6620_CFG_WD_MASK (BIT(2) | BIT(1)) +#define MAX6620_CFG_WD_2 BIT(1) +#define MAX6620_CFG_WD_6 BIT(2) +#define MAX6620_CFG_WD10 (BIT(2) | BIT(1)) +#define MAX6620_CFG_WD BIT(0) + +/* + * Failure status register bits + */ + +#define MAX6620_FAIL_TACH0 BIT(4) +#define MAX6620_FAIL_TACH1 BIT(5) +#define MAX6620_FAIL_TACH2 BIT(6) +#define MAX6620_FAIL_TACH3 BIT(7) +#define MAX6620_FAIL_MASK0 BIT(0) +#define MAX6620_FAIL_MASK1 BIT(1) +#define MAX6620_FAIL_MASK2 BIT(2) +#define MAX6620_FAIL_MASK3 BIT(3) + +#define MAX6620_CLOCK_FREQ 8192 /* Clock frequency in Hz */ +#define MAX6620_PULSE_PER_REV 2 /* Tachometer pulses per revolution */ + +/* Minimum and maximum values of the FAN-RPM */ +#define FAN_RPM_MIN 240 +#define FAN_RPM_MAX 30000 + +static const u8 config_reg[] = { + MAX6620_REG_CONF_FAN0, + MAX6620_REG_CONF_FAN1, + MAX6620_REG_CONF_FAN2, + MAX6620_REG_CONF_FAN3, +}; + +static const u8 dyn_reg[] = { + MAX6620_REG_DYN_FAN0, + MAX6620_REG_DYN_FAN1, + MAX6620_REG_DYN_FAN2, + MAX6620_REG_DYN_FAN3, +}; + +static const u8 tach_reg[] = { + MAX6620_REG_TACH0, + MAX6620_REG_TACH1, + MAX6620_REG_TACH2, + MAX6620_REG_TACH3, +}; + +static const u8 target_reg[] = { + MAX6620_REG_TAR0, + MAX6620_REG_TAR1, + MAX6620_REG_TAR2, + MAX6620_REG_TAR3, +}; + +/* + * Client data (each client gets its own) + */ + +struct max6620_data { + struct i2c_client *client; + struct mutex update_lock; + bool valid; /* false until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 fancfg[4]; + u8 fandyn[4]; + u8 fault; + u16 tach[4]; + u16 target[4]; +}; + +static u8 max6620_fan_div_from_reg(u8 val) +{ + return BIT((val & 0xE0) >> 5); +} + +static u16 max6620_fan_rpm_to_tach(u8 div, int rpm) +{ + return (60 * div * MAX6620_CLOCK_FREQ) / (rpm * MAX6620_PULSE_PER_REV); +} + +static int max6620_fan_tach_to_rpm(u8 div, u16 tach) +{ + return (60 * div * MAX6620_CLOCK_FREQ) / (tach * MAX6620_PULSE_PER_REV); +} + +static int max6620_update_device(struct device *dev) +{ + struct max6620_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; + int ret = 0; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (i = 0; i < 4; i++) { + ret = i2c_smbus_read_byte_data(client, config_reg[i]); + if (ret < 0) + goto error; + data->fancfg[i] = ret; + + ret = i2c_smbus_read_byte_data(client, dyn_reg[i]); + if (ret < 0) + goto error; + data->fandyn[i] = ret; + + ret = i2c_smbus_read_byte_data(client, tach_reg[i]); + if (ret < 0) + goto error; + data->tach[i] = (ret << 3) & 0x7f8; + ret = i2c_smbus_read_byte_data(client, tach_reg[i] + 1); + if (ret < 0) + goto error; + data->tach[i] |= (ret >> 5) & 0x7; + + ret = i2c_smbus_read_byte_data(client, target_reg[i]); + if (ret < 0) + goto error; + data->target[i] = (ret << 3) & 0x7f8; + ret = i2c_smbus_read_byte_data(client, target_reg[i] + 1); + if (ret < 0) + goto error; + data->target[i] |= (ret >> 5) & 0x7; + } + + /* + * Alarms are cleared on read in case the condition that + * caused the alarm is removed. Keep the value latched here + * for providing the register through different alarm files. + */ + ret = i2c_smbus_read_byte_data(client, MAX6620_REG_FAULT); + if (ret < 0) + goto error; + data->fault |= (ret >> 4) & (ret & 0x0F); + + data->last_updated = jiffies; + data->valid = true; + } + +error: + mutex_unlock(&data->update_lock); + return ret; +} + +static umode_t +max6620_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_alarm: + case hwmon_fan_input: + return 0444; + case hwmon_fan_div: + case hwmon_fan_target: + return 0644; + default: + break; + } + break; + default: + break; + } + + return 0; +} + +static int +max6620_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct max6620_data *data; + struct i2c_client *client; + int ret; + u8 div; + u8 val1; + u8 val2; + + ret = max6620_update_device(dev); + if (ret < 0) + return ret; + data = dev_get_drvdata(dev); + client = data->client; + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_alarm: + mutex_lock(&data->update_lock); + *val = !!(data->fault & BIT(channel)); + + /* Setting TACH count to re-enable fan fault detection */ + if (*val == 1) { + val1 = (data->target[channel] >> 3) & 0xff; + val2 = (data->target[channel] << 5) & 0xe0; + ret = i2c_smbus_write_byte_data(client, + target_reg[channel], val1); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + ret = i2c_smbus_write_byte_data(client, + target_reg[channel] + 1, val2); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + + data->fault &= ~BIT(channel); + } + mutex_unlock(&data->update_lock); + + break; + case hwmon_fan_div: + *val = max6620_fan_div_from_reg(data->fandyn[channel]); + break; + case hwmon_fan_input: + if (data->tach[channel] == 0) { + *val = 0; + } else { + div = max6620_fan_div_from_reg(data->fandyn[channel]); + *val = max6620_fan_tach_to_rpm(div, data->tach[channel]); + } + break; + case hwmon_fan_target: + if (data->target[channel] == 0) { + *val = 0; + } else { + div = max6620_fan_div_from_reg(data->fandyn[channel]); + *val = max6620_fan_tach_to_rpm(div, data->target[channel]); + } + break; + default: + return -EOPNOTSUPP; + } + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int +max6620_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct max6620_data *data; + struct i2c_client *client; + int ret; + u8 div; + u16 tach; + u8 val1; + u8 val2; + + ret = max6620_update_device(dev); + if (ret < 0) + return ret; + data = dev_get_drvdata(dev); + client = data->client; + mutex_lock(&data->update_lock); + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_div: + switch (val) { + case 1: + div = 0; + break; + case 2: + div = 1; + break; + case 4: + div = 2; + break; + case 8: + div = 3; + break; + case 16: + div = 4; + break; + case 32: + div = 5; + break; + default: + ret = -EINVAL; + goto error; + } + data->fandyn[channel] &= 0x1F; + data->fandyn[channel] |= div << 5; + ret = i2c_smbus_write_byte_data(client, dyn_reg[channel], + data->fandyn[channel]); + break; + case hwmon_fan_target: + val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX); + div = max6620_fan_div_from_reg(data->fandyn[channel]); + tach = max6620_fan_rpm_to_tach(div, val); + val1 = (tach >> 3) & 0xff; + val2 = (tach << 5) & 0xe0; + ret = i2c_smbus_write_byte_data(client, target_reg[channel], val1); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(client, target_reg[channel] + 1, val2); + if (ret < 0) + break; + + /* Setting TACH count re-enables fan fault detection */ + data->fault &= ~BIT(channel); + + break; + default: + ret = -EOPNOTSUPP; + break; + } + break; + + default: + ret = -EOPNOTSUPP; + break; + } + +error: + mutex_unlock(&data->update_lock); + return ret; +} + +static const struct hwmon_channel_info *max6620_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_DIV | HWMON_F_TARGET | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_DIV | HWMON_F_TARGET | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_DIV | HWMON_F_TARGET | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_DIV | HWMON_F_TARGET | HWMON_F_ALARM), + NULL +}; + +static const struct hwmon_ops max6620_hwmon_ops = { + .read = max6620_read, + .write = max6620_write, + .is_visible = max6620_is_visible, +}; + +static const struct hwmon_chip_info max6620_chip_info = { + .ops = &max6620_hwmon_ops, + .info = max6620_info, +}; + +static int max6620_init_client(struct max6620_data *data) +{ + struct i2c_client *client = data->client; + int config; + int err; + int i; + int reg; + + config = i2c_smbus_read_byte_data(client, MAX6620_REG_CONFIG); + if (config < 0) { + dev_err(&client->dev, "Error reading config, aborting.\n"); + return config; + } + + /* + * Set bit 4, disable other fans from going full speed on a fail + * failure. + */ + err = i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config | 0x10); + if (err < 0) { + dev_err(&client->dev, "Config write error, aborting.\n"); + return err; + } + + for (i = 0; i < 4; i++) { + reg = i2c_smbus_read_byte_data(client, config_reg[i]); + if (reg < 0) + return reg; + data->fancfg[i] = reg; + + /* Enable RPM mode */ + data->fancfg[i] |= 0xa8; + err = i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]); + if (err < 0) + return err; + + /* 2 counts (001) and Rate change 100 (0.125 secs) */ + data->fandyn[i] = 0x30; + err = i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]); + if (err < 0) + return err; + } + return 0; +} + +static int max6620_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max6620_data *data; + struct device *hwmon_dev; + int err; + + data = devm_kzalloc(dev, sizeof(struct max6620_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + err = max6620_init_client(data); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &max6620_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max6620_id[] = { + { "max6620", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6620_id); + +static struct i2c_driver max6620_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max6620", + }, + .probe_new = max6620_probe, + .id_table = max6620_id, +}; + +module_i2c_driver(max6620_driver); + +MODULE_AUTHOR("Lucas Grunenberg"); +MODULE_DESCRIPTION("MAX6620 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c index a8bb5de14230..7821132e17fa 100644 --- a/drivers/hwmon/max6621.c +++ b/drivers/hwmon/max6621.c @@ -156,7 +156,7 @@ max6621_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, default: break; } - + break; default: break; } @@ -477,8 +477,7 @@ static const struct hwmon_chip_info max6621_chip_info = { .info = max6621_info, }; -static int max6621_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max6621_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct max6621_data *data; @@ -555,7 +554,7 @@ static struct i2c_driver max6621_driver = { .name = MAX6621_DRV_NAME, .of_match_table = of_match_ptr(max6621_of_match), }, - .probe = max6621_probe, + .probe_new = max6621_probe, .id_table = max6621_id, }; diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 2d56e97aa5fa..9b895402c80d 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -69,7 +69,7 @@ static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 }; struct max6639_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ /* Register values sampled regularly */ @@ -87,6 +87,9 @@ struct max6639_data { /* Register values initialized only once */ u8 ppr; /* Pulses per rotation 0..3 for 1..4 ppr */ u8 rpm_range; /* Index in above rpm_ranges table */ + + /* Optional regulator for FAN supply */ + struct regulator *reg; }; static struct max6639_data *max6639_update_device(struct device *dev) @@ -141,7 +144,7 @@ static struct max6639_data *max6639_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } abort: mutex_unlock(&data->update_lock); @@ -511,13 +514,17 @@ static int max6639_detect(struct i2c_client *client, if (dev_id != 0x58 || manu_id != 0x4D) return -ENODEV; - strlcpy(info->type, "max6639", I2C_NAME_SIZE); + strscpy(info->type, "max6639", I2C_NAME_SIZE); return 0; } -static int max6639_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static void max6639_regulator_disable(void *data) +{ + regulator_disable(data); +} + +static int max6639_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct max6639_data *data; @@ -529,6 +536,28 @@ static int max6639_probe(struct i2c_client *client, return -ENOMEM; data->client = client; + + data->reg = devm_regulator_get_optional(dev, "fan"); + if (IS_ERR(data->reg)) { + if (PTR_ERR(data->reg) != -ENODEV) + return PTR_ERR(data->reg); + + data->reg = NULL; + } else { + /* Spin up fans */ + err = regulator_enable(data->reg); + if (err) { + dev_err(dev, "Failed to enable fan supply: %d\n", err); + return err; + } + err = devm_add_action_or_reset(dev, max6639_regulator_disable, + data->reg); + if (err) { + dev_err(dev, "Failed to register action: %d\n", err); + return err; + } + } + mutex_init(&data->update_lock); /* Initialize the max6639 chip */ @@ -542,29 +571,43 @@ static int max6639_probe(struct i2c_client *client, return PTR_ERR_OR_ZERO(hwmon_dev); } -#ifdef CONFIG_PM_SLEEP static int max6639_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); - if (data < 0) - return data; + struct max6639_data *data = dev_get_drvdata(dev); + int ret = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); + + if (ret < 0) + return ret; + + if (data->reg) + regulator_disable(data->reg); return i2c_smbus_write_byte_data(client, - MAX6639_REG_GCONFIG, data | MAX6639_GCONFIG_STANDBY); + MAX6639_REG_GCONFIG, ret | MAX6639_GCONFIG_STANDBY); } static int max6639_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); - if (data < 0) - return data; + struct max6639_data *data = dev_get_drvdata(dev); + int ret; + + if (data->reg) { + ret = regulator_enable(data->reg); + if (ret) { + dev_err(dev, "Failed to enable fan supply: %d\n", ret); + return ret; + } + } + + ret = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); + if (ret < 0) + return ret; return i2c_smbus_write_byte_data(client, - MAX6639_REG_GCONFIG, data & ~MAX6639_GCONFIG_STANDBY); + MAX6639_REG_GCONFIG, ret & ~MAX6639_GCONFIG_STANDBY); } -#endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id max6639_id[] = { {"max6639", 0}, @@ -573,15 +616,15 @@ static const struct i2c_device_id max6639_id[] = { MODULE_DEVICE_TABLE(i2c, max6639_id); -static SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume); static struct i2c_driver max6639_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "max6639", - .pm = &max6639_pm_ops, + .pm = pm_sleep_ptr(&max6639_pm_ops), }, - .probe = max6639_probe, + .probe_new = max6639_probe, .id_table = max6639_id, .detect = max6639_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c index 5ab6fdb53b96..47ea34ff78f3 100644 --- a/drivers/hwmon/max6642.c +++ b/drivers/hwmon/max6642.c @@ -148,7 +148,7 @@ static int max6642_detect(struct i2c_client *client, if ((reg_status & 0x2b) != 0x00) return -ENODEV; - strlcpy(info->type, "max6642", I2C_NAME_SIZE); + strscpy(info->type, "max6642", I2C_NAME_SIZE); return 0; } @@ -181,7 +181,7 @@ static struct max6642_data *max6642_update_device(struct device *dev) MAX6642_REG_R_STATUS); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -264,8 +264,7 @@ static struct attribute *max6642_attrs[] = { }; ATTRIBUTE_GROUPS(max6642); -static int max6642_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max6642_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct max6642_data *data; @@ -302,7 +301,7 @@ static struct i2c_driver max6642_driver = { .driver = { .name = "max6642", }, - .probe = max6642_probe, + .probe_new = max6642_probe, .id_table = max6642_id, .detect = max6642_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 3d9d371c35b5..f8d4534ce172 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -321,7 +321,7 @@ static SENSOR_DEVICE_ATTR_RO(gpio2_alarm, alarm, MAX6650_ALRM_GPIO2); static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max6650_data *data = dev_get_drvdata(dev); struct device_attribute *devattr; @@ -757,8 +757,9 @@ static const struct hwmon_chip_info max6650_chip_info = { .info = max6650_info, }; -static int max6650_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id max6650_id[]; + +static int max6650_probe(struct i2c_client *client) { struct thermal_cooling_device *cooling_dev; struct device *dev = &client->dev; @@ -775,7 +776,8 @@ static int max6650_probe(struct i2c_client *client, data->client = client; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : id->driver_data; + data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : + i2c_match_id(max6650_id, client)->driver_data; /* * Initialize the max6650 chip @@ -817,7 +819,7 @@ static struct i2c_driver max6650_driver = { .name = "max6650", .of_match_table = of_match_ptr(max6650_dt_match), }, - .probe = max6650_probe, + .probe_new = max6650_probe, .id_table = max6650_id, }; diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 743752a2467a..2895cea54193 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -38,8 +38,9 @@ static const u8 MAX6697_REG_CRIT[] = { * Map device tree / platform data register bit map to chip bit map. * Applies to alert register and over-temperature register. */ -#define MAX6697_MAP_BITS(reg) ((((reg) & 0x7e) >> 1) | \ +#define MAX6697_ALERT_MAP_BITS(reg) ((((reg) & 0x7e) >> 1) | \ (((reg) & 0x01) << 6) | ((reg) & 0x80)) +#define MAX6697_OVERT_MAP_BITS(reg) (((reg) >> 1) | (((reg) & 0x01) << 7)) #define MAX6697_REG_STAT(n) (0x44 + (n)) @@ -56,6 +57,8 @@ static const u8 MAX6697_REG_CRIT[] = { #define MAX6581_REG_IDEALITY_SELECT 0x4c #define MAX6581_REG_OFFSET 0x4d #define MAX6581_REG_OFFSET_SELECT 0x4e +#define MAX6581_OFFSET_MIN -31750 +#define MAX6581_OFFSET_MAX 31750 #define MAX6697_CONV_TIME 156 /* ms per channel, worst case */ @@ -171,6 +174,11 @@ static const struct max6697_chip_data max6697_chip_data[] = { }, }; +static inline int max6581_offset_to_millic(int val) +{ + return sign_extend32(val, 7) * 250; +} + static struct max6697_data *max6697_update_device(struct device *dev) { struct max6697_data *data = dev_get_drvdata(dev); @@ -316,6 +324,70 @@ static ssize_t temp_store(struct device *dev, return ret < 0 ? ret : count; } +static ssize_t offset_store(struct device *dev, struct device_attribute *devattr, const char *buf, + size_t count) +{ + int val, ret, index, select; + struct max6697_data *data; + bool channel_enabled; + long temp; + + index = to_sensor_dev_attr(devattr)->index; + data = dev_get_drvdata(dev); + ret = kstrtol(buf, 10, &temp); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + select = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET_SELECT); + if (select < 0) { + ret = select; + goto abort; + } + channel_enabled = (select & (1 << (index - 1))); + temp = clamp_val(temp, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); + val = DIV_ROUND_CLOSEST(temp, 250); + /* disable the offset for channel if the new offset is 0 */ + if (val == 0) { + if (channel_enabled) + ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, + select & ~(1 << (index - 1))); + ret = ret < 0 ? ret : count; + goto abort; + } + if (!channel_enabled) { + ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, + select | (1 << (index - 1))); + if (ret < 0) + goto abort; + } + ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET, val); + ret = ret < 0 ? ret : count; + +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t offset_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct max6697_data *data; + int select, ret, index; + + index = to_sensor_dev_attr(devattr)->index; + data = dev_get_drvdata(dev); + mutex_lock(&data->update_lock); + select = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET_SELECT); + if (select < 0) + ret = select; + else if (select & (1 << (index - 1))) + ret = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET); + else + ret = 0; + mutex_unlock(&data->update_lock); + return ret < 0 ? ret : sprintf(buf, "%d\n", max6581_offset_to_millic(ret)); +} + static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0); static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, 0, MAX6697_TEMP_MAX); static SENSOR_DEVICE_ATTR_2_RW(temp1_crit, temp, 0, MAX6697_TEMP_CRIT); @@ -374,16 +446,25 @@ static SENSOR_DEVICE_ATTR_RO(temp6_fault, alarm, 5); static SENSOR_DEVICE_ATTR_RO(temp7_fault, alarm, 6); static SENSOR_DEVICE_ATTR_RO(temp8_fault, alarm, 7); +/* There is no offset for local temperature so starting from temp2 */ +static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 1); +static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 2); +static SENSOR_DEVICE_ATTR_RW(temp4_offset, offset, 3); +static SENSOR_DEVICE_ATTR_RW(temp5_offset, offset, 4); +static SENSOR_DEVICE_ATTR_RW(temp6_offset, offset, 5); +static SENSOR_DEVICE_ATTR_RW(temp7_offset, offset, 6); +static SENSOR_DEVICE_ATTR_RW(temp8_offset, offset, 7); + static DEVICE_ATTR(dummy, 0, NULL, NULL); static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max6697_data *data = dev_get_drvdata(dev); const struct max6697_chip_data *chip = data->chip; - int channel = index / 6; /* channel number */ - int nr = index % 6; /* attribute index within channel */ + int channel = index / 7; /* channel number */ + int nr = index % 7; /* attribute index within channel */ if (channel >= chip->channels) return 0; @@ -392,6 +473,10 @@ static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, return 0; if (nr == 5 && !(chip->have_fault & (1 << channel))) return 0; + /* offset reg is only supported on max6581 remote channels */ + if (nr == 6) + if (data->type != max6581 || channel == 0) + return 0; return attr->mode; } @@ -408,6 +493,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, &dev_attr_dummy.attr, + &dev_attr_dummy.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, @@ -415,6 +501,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp2_crit.dev_attr.attr, &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, @@ -422,6 +509,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp3_crit.dev_attr.attr, &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, &sensor_dev_attr_temp4_input.dev_attr.attr, &sensor_dev_attr_temp4_max.dev_attr.attr, @@ -429,6 +517,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp4_crit.dev_attr.attr, &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp4_offset.dev_attr.attr, &sensor_dev_attr_temp5_input.dev_attr.attr, &sensor_dev_attr_temp5_max.dev_attr.attr, @@ -436,6 +525,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp5_crit.dev_attr.attr, &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp5_offset.dev_attr.attr, &sensor_dev_attr_temp6_input.dev_attr.attr, &sensor_dev_attr_temp6_max.dev_attr.attr, @@ -443,6 +533,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp6_crit.dev_attr.attr, &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp6_fault.dev_attr.attr, + &sensor_dev_attr_temp6_offset.dev_attr.attr, &sensor_dev_attr_temp7_input.dev_attr.attr, &sensor_dev_attr_temp7_max.dev_attr.attr, @@ -450,6 +541,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp7_crit.dev_attr.attr, &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp7_fault.dev_attr.attr, + &sensor_dev_attr_temp7_offset.dev_attr.attr, &sensor_dev_attr_temp8_input.dev_attr.attr, &sensor_dev_attr_temp8_max.dev_attr.attr, @@ -457,6 +549,7 @@ static struct attribute *max6697_attributes[] = { &sensor_dev_attr_temp8_crit.dev_attr.attr, &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp8_fault.dev_attr.attr, + &sensor_dev_attr_temp8_offset.dev_attr.attr, NULL }; @@ -562,12 +655,12 @@ static int max6697_init_chip(struct max6697_data *data, return ret; ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK, - MAX6697_MAP_BITS(pdata->alert_mask)); + MAX6697_ALERT_MAP_BITS(pdata->alert_mask)); if (ret < 0) return ret; ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK, - MAX6697_MAP_BITS(pdata->over_temperature_mask)); + MAX6697_OVERT_MAP_BITS(pdata->over_temperature_mask)); if (ret < 0) return ret; @@ -592,8 +685,9 @@ done: return 0; } -static int max6697_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id max6697_id[]; + +static int max6697_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; @@ -611,7 +705,7 @@ static int max6697_probe(struct i2c_client *client, if (client->dev.of_node) data->type = (enum chips)of_device_get_match_data(&client->dev); else - data->type = id->driver_data; + data->type = i2c_match_id(max6697_id, client)->driver_data; data->chip = &max6697_chip_data[data->type]; data->client = client; mutex_init(&data->update_lock); @@ -692,7 +786,7 @@ static struct i2c_driver max6697_driver = { .name = "max6697", .of_match_table = of_match_ptr(max6697_of_match), }, - .probe = max6697_probe, + .probe_new = max6697_probe, .id_table = max6697_id, }; diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index 4e8f995dc773..e093b1998296 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -7,7 +7,7 @@ * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de> * DT support added by Clemens Gruber <clemens.gruber@pqgruber.com> * - * This driver export the value of analog input voltage to sysfs, the + * This driver exports the value of analog input voltage to sysfs, the * voltage unit is mV. Through the sysfs interface, lm-sensors tool * can also display the input voltage. */ @@ -45,19 +45,29 @@ enum chips { * Client data (each client gets its own) */ struct mcp3021_data { - struct device *hwmon_dev; + struct i2c_client *client; u32 vdd; /* supply and reference voltage in millivolt */ u16 sar_shift; u16 sar_mask; u8 output_res; }; -static int mcp3021_read16(struct i2c_client *client) +static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val) { - struct mcp3021_data *data = i2c_get_clientdata(client); - int ret; - u16 reg; + return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res); +} + +static int mcp3021_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct mcp3021_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; __be16 buf; + u16 reg; + int ret; + + if (type != hwmon_in) + return -EOPNOTSUPP; ret = i2c_master_recv(client, (char *)&buf, 2); if (ret < 0) @@ -74,38 +84,46 @@ static int mcp3021_read16(struct i2c_client *client) */ reg = (reg >> data->sar_shift) & data->sar_mask; - return reg; -} + *val = volts_from_reg(data, reg); -static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val) -{ - return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res); + return 0; } -static ssize_t in0_input_show(struct device *dev, - struct device_attribute *attr, char *buf) +static umode_t mcp3021_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) { - struct i2c_client *client = to_i2c_client(dev); - struct mcp3021_data *data = i2c_get_clientdata(client); - int reg, in_input; + if (type != hwmon_in) + return 0; - reg = mcp3021_read16(client); - if (reg < 0) - return reg; + if (attr != hwmon_in_input) + return 0; - in_input = volts_from_reg(data, reg); - - return sprintf(buf, "%d\n", in_input); + return 0444; } -static DEVICE_ATTR_RO(in0_input); +static const struct hwmon_channel_info *mcp3021_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT), + NULL +}; + +static const struct hwmon_ops mcp3021_hwmon_ops = { + .is_visible = mcp3021_is_visible, + .read = mcp3021_read, +}; + +static const struct hwmon_chip_info mcp3021_chip_info = { + .ops = &mcp3021_hwmon_ops, + .info = mcp3021_info, +}; + +static const struct i2c_device_id mcp3021_id[]; -static int mcp3021_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mcp3021_probe(struct i2c_client *client) { - int err; struct mcp3021_data *data = NULL; struct device_node *np = client->dev.of_node; + struct device *hwmon_dev; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -132,7 +150,7 @@ static int mcp3021_probe(struct i2c_client *client, data->vdd = MCP3021_VDD_REF_DEFAULT; } - switch (id->driver_data) { + switch (i2c_match_id(mcp3021_id, client)->driver_data) { case mcp3021: data->sar_shift = MCP3021_SAR_SHIFT; data->sar_mask = MCP3021_SAR_MASK; @@ -146,34 +164,17 @@ static int mcp3021_probe(struct i2c_client *client, break; } + data->client = client; + if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN) return -EINVAL; - err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); - return err; -} - -static int mcp3021_remove(struct i2c_client *client) -{ - struct mcp3021_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, + client->name, + data, + &mcp3021_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id mcp3021_id[] = { @@ -197,8 +198,7 @@ static struct i2c_driver mcp3021_driver = { .name = "mcp3021", .of_match_table = of_match_ptr(of_mcp3021_match), }, - .probe = mcp3021_probe, - .remove = mcp3021_remove, + .probe_new = mcp3021_probe, .id_table = mcp3021_id, }; diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c index ed8d59d4eecb..b48bd7c961d6 100644 --- a/drivers/hwmon/mlxreg-fan.c +++ b/drivers/hwmon/mlxreg-fan.c @@ -12,19 +12,12 @@ #include <linux/regmap.h> #include <linux/thermal.h> -#define MLXREG_FAN_MAX_TACHO 12 +#define MLXREG_FAN_MAX_TACHO 14 +#define MLXREG_FAN_MAX_PWM 4 +#define MLXREG_FAN_PWM_NOT_CONNECTED 0xff #define MLXREG_FAN_MAX_STATE 10 #define MLXREG_FAN_MIN_DUTY 51 /* 20% */ #define MLXREG_FAN_MAX_DUTY 255 /* 100% */ -/* - * Minimum and maximum FAN allowed speed in percent: from 20% to 100%. Values - * MLXREG_FAN_MAX_STATE + x, where x is between 2 and 10 are used for - * setting FAN speed dynamic minimum. For example, if value is set to 14 (40%) - * cooling levels vector will be set to 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10 to - * introduce PWM speed in percent: 40, 40, 40, 40, 40, 50, 60. 70, 80, 90, 100. - */ -#define MLXREG_FAN_SPEED_MIN (MLXREG_FAN_MAX_STATE + 2) -#define MLXREG_FAN_SPEED_MAX (MLXREG_FAN_MAX_STATE * 2) #define MLXREG_FAN_SPEED_MIN_LEVEL 2 /* 20 percent */ #define MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF 44 #define MLXREG_FAN_TACHO_DIV_MIN 283 @@ -61,28 +54,41 @@ MLXREG_FAN_MAX_DUTY, \ MLXREG_FAN_MAX_STATE)) +struct mlxreg_fan; + /* * struct mlxreg_fan_tacho - tachometer data (internal use): * * @connected: indicates if tachometer is connected; * @reg: register offset; * @mask: fault mask; + * @prsnt: present register offset; */ struct mlxreg_fan_tacho { bool connected; u32 reg; u32 mask; + u32 prsnt; }; /* * struct mlxreg_fan_pwm - PWM data (internal use): * + * @fan: private data; * @connected: indicates if PWM is connected; * @reg: register offset; + * @cooling: cooling device levels; + * @last_hwmon_state: last cooling state set by hwmon subsystem; + * @last_thermal_state: last cooling state set by thermal subsystem; + * @cdev: cooling device; */ struct mlxreg_fan_pwm { + struct mlxreg_fan *fan; bool connected; u32 reg; + unsigned long last_hwmon_state; + unsigned long last_thermal_state; + struct thermal_cooling_device *cdev; }; /* @@ -92,29 +98,31 @@ struct mlxreg_fan_pwm { * @regmap: register map of parent device; * @tacho: tachometer data; * @pwm: PWM data; + * @tachos_per_drwr - number of tachometers per drawer; * @samples: minimum allowed samples per pulse; * @divider: divider value for tachometer RPM calculation; - * @cooling: cooling device levels; - * @cdev: cooling device; */ struct mlxreg_fan { struct device *dev; void *regmap; struct mlxreg_core_platform_data *pdata; struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO]; - struct mlxreg_fan_pwm pwm; + struct mlxreg_fan_pwm pwm[MLXREG_FAN_MAX_PWM]; + int tachos_per_drwr; int samples; int divider; - u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1]; - struct thermal_cooling_device *cdev; }; +static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state); + static int mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct mlxreg_fan *fan = dev_get_drvdata(dev); struct mlxreg_fan_tacho *tacho; + struct mlxreg_fan_pwm *pwm; u32 regval; int err; @@ -123,6 +131,26 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, tacho = &fan->tacho[channel]; switch (attr) { case hwmon_fan_input: + /* + * Check FAN presence: FAN related bit in presence register is one, + * if FAN is physically connected, zero - otherwise. + */ + if (tacho->prsnt && fan->tachos_per_drwr) { + err = regmap_read(fan->regmap, tacho->prsnt, ®val); + if (err) + return err; + + /* + * Map channel to presence bit - drawer can be equipped with + * one or few FANs, while presence is indicated per drawer. + */ + if (BIT(channel / fan->tachos_per_drwr) & regval) { + /* FAN is not connected - return zero for FAN speed. */ + *val = 0; + return 0; + } + } + err = regmap_read(fan->regmap, tacho->reg, ®val); if (err) return err; @@ -145,9 +173,10 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, break; case hwmon_pwm: + pwm = &fan->pwm[channel]; switch (attr) { case hwmon_pwm_input: - err = regmap_read(fan->regmap, fan->pwm.reg, ®val); + err = regmap_read(fan->regmap, pwm->reg, ®val); if (err) return err; @@ -171,6 +200,7 @@ mlxreg_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct mlxreg_fan *fan = dev_get_drvdata(dev); + struct mlxreg_fan_pwm *pwm; switch (type) { case hwmon_pwm: @@ -179,7 +209,20 @@ mlxreg_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (val < MLXREG_FAN_MIN_DUTY || val > MLXREG_FAN_MAX_DUTY) return -EINVAL; - return regmap_write(fan->regmap, fan->pwm.reg, val); + pwm = &fan->pwm[channel]; + /* If thermal is configured - handle PWM limit setting. */ + if (IS_REACHABLE(CONFIG_THERMAL)) { + pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(val); + /* + * Update PWM only in case requested state is not less than the + * last thermal state. + */ + if (pwm->last_hwmon_state >= pwm->last_thermal_state) + return mlxreg_fan_set_cur_state(pwm->cdev, + pwm->last_hwmon_state); + return 0; + } + return regmap_write(fan->regmap, pwm->reg, val); default: return -EOPNOTSUPP; } @@ -211,7 +254,7 @@ mlxreg_fan_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, break; case hwmon_pwm: - if (!(((struct mlxreg_fan *)data)->pwm.connected)) + if (!(((struct mlxreg_fan *)data)->pwm[channel].connected)) return 0; switch (attr) { @@ -229,6 +272,13 @@ mlxreg_fan_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, return 0; } +static char *mlxreg_fan_name[] = { + "mlxreg_fan", + "mlxreg_fan1", + "mlxreg_fan2", + "mlxreg_fan3", +}; + static const struct hwmon_channel_info *mlxreg_fan_hwmon_info[] = { HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT, @@ -242,8 +292,13 @@ static const struct hwmon_channel_info *mlxreg_fan_hwmon_info[] = { HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT), HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT), NULL }; @@ -270,11 +325,12 @@ static int mlxreg_fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { - struct mlxreg_fan *fan = cdev->devdata; + struct mlxreg_fan_pwm *pwm = cdev->devdata; + struct mlxreg_fan *fan = pwm->fan; u32 regval; int err; - err = regmap_read(fan->regmap, fan->pwm.reg, ®val); + err = regmap_read(fan->regmap, pwm->reg, ®val); if (err) { dev_err(fan->dev, "Failed to query PWM duty\n"); return err; @@ -289,47 +345,18 @@ static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { - struct mlxreg_fan *fan = cdev->devdata; - unsigned long cur_state; - u32 regval; - int i; + struct mlxreg_fan_pwm *pwm = cdev->devdata; + struct mlxreg_fan *fan = pwm->fan; int err; - /* - * Verify if this request is for changing allowed FAN dynamical - * minimum. If it is - update cooling levels accordingly and update - * state, if current state is below the newly requested minimum state. - * For example, if current state is 5, and minimal state is to be - * changed from 4 to 6, fan->cooling_levels[0 to 5] will be changed all - * from 4 to 6. And state 5 (fan->cooling_levels[4]) should be - * overwritten. - */ - if (state >= MLXREG_FAN_SPEED_MIN && state <= MLXREG_FAN_SPEED_MAX) { - state -= MLXREG_FAN_MAX_STATE; - for (i = 0; i < state; i++) - fan->cooling_levels[i] = state; - for (i = state; i <= MLXREG_FAN_MAX_STATE; i++) - fan->cooling_levels[i] = i; - - err = regmap_read(fan->regmap, fan->pwm.reg, ®val); - if (err) { - dev_err(fan->dev, "Failed to query PWM duty\n"); - return err; - } - - cur_state = MLXREG_FAN_PWM_DUTY2STATE(regval); - if (state < cur_state) - return 0; - - state = cur_state; - } - if (state > MLXREG_FAN_MAX_STATE) return -EINVAL; - /* Normalize the state to the valid speed range. */ - state = fan->cooling_levels[state]; - err = regmap_write(fan->regmap, fan->pwm.reg, + /* Save thermal state. */ + pwm->last_thermal_state = state; + + state = max_t(unsigned long, state, pwm->last_hwmon_state); + err = regmap_write(fan->regmap, pwm->reg, MLXREG_FAN_PWM_STATE2DUTY(state)); if (err) { dev_err(fan->dev, "Failed to write PWM duty\n"); @@ -360,6 +387,22 @@ static int mlxreg_fan_connect_verify(struct mlxreg_fan *fan, return !!(regval & data->bit); } +static int mlxreg_pwm_connect_verify(struct mlxreg_fan *fan, + struct mlxreg_core_data *data) +{ + u32 regval; + int err; + + err = regmap_read(fan->regmap, data->reg, ®val); + if (err) { + dev_err(fan->dev, "Failed to query pwm register 0x%08x\n", + data->reg); + return err; + } + + return regval != MLXREG_FAN_PWM_NOT_CONNECTED; +} + static int mlxreg_fan_speed_divider_get(struct mlxreg_fan *fan, struct mlxreg_core_data *data) { @@ -388,9 +431,9 @@ static int mlxreg_fan_speed_divider_get(struct mlxreg_fan *fan, static int mlxreg_fan_config(struct mlxreg_fan *fan, struct mlxreg_core_platform_data *pdata) { + int tacho_num = 0, tacho_avail = 0, pwm_num = 0, i; struct mlxreg_core_data *data = pdata->data; bool configured = false; - int tacho_num = 0, i; int err; fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF; @@ -415,15 +458,28 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan, fan->tacho[tacho_num].reg = data->reg; fan->tacho[tacho_num].mask = data->mask; + fan->tacho[tacho_num].prsnt = data->reg_prsnt; fan->tacho[tacho_num++].connected = true; + tacho_avail++; } else if (strnstr(data->label, "pwm", sizeof(data->label))) { - if (fan->pwm.connected) { - dev_err(fan->dev, "duplicate pwm entry: %s\n", + if (pwm_num == MLXREG_FAN_MAX_TACHO) { + dev_err(fan->dev, "too many pwm entries: %s\n", data->label); return -EINVAL; } - fan->pwm.reg = data->reg; - fan->pwm.connected = true; + + /* Validate if more then one PWM is connected. */ + if (pwm_num) { + err = mlxreg_pwm_connect_verify(fan, data); + if (err < 0) + return err; + else if (!err) + continue; + } + + fan->pwm[pwm_num].reg = data->reg; + fan->pwm[pwm_num].connected = true; + pwm_num++; } else if (strnstr(data->label, "conf", sizeof(data->label))) { if (configured) { dev_err(fan->dev, "duplicate conf entry: %s\n", @@ -453,11 +509,52 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan, } } - /* Init cooling levels per PWM state. */ - for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++) - fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL; - for (i = MLXREG_FAN_SPEED_MIN_LEVEL; i <= MLXREG_FAN_MAX_STATE; i++) - fan->cooling_levels[i] = i; + if (pdata->capability) { + int drwr_avail; + u32 regval; + + /* Obtain the number of FAN drawers, supported by system. */ + err = regmap_read(fan->regmap, pdata->capability, ®val); + if (err) { + dev_err(fan->dev, "Failed to query capability register 0x%08x\n", + pdata->capability); + return err; + } + + drwr_avail = hweight32(regval); + if (!tacho_avail || !drwr_avail || tacho_avail < drwr_avail) { + dev_err(fan->dev, "Configuration is invalid: drawers num %d tachos num %d\n", + drwr_avail, tacho_avail); + return -EINVAL; + } + + /* Set the number of tachometers per one drawer. */ + fan->tachos_per_drwr = tacho_avail / drwr_avail; + } + + return 0; +} + +static int mlxreg_fan_cooling_config(struct device *dev, struct mlxreg_fan *fan) +{ + int i; + + for (i = 0; i < MLXREG_FAN_MAX_PWM; i++) { + struct mlxreg_fan_pwm *pwm = &fan->pwm[i]; + + if (!pwm->connected) + continue; + pwm->fan = fan; + pwm->cdev = devm_thermal_of_cooling_device_register(dev, NULL, mlxreg_fan_name[i], + pwm, &mlxreg_fan_cooling_ops); + if (IS_ERR(pwm->cdev)) { + dev_err(dev, "Failed to register cooling device\n"); + return PTR_ERR(pwm->cdev); + } + + /* Set minimal PWM speed. */ + pwm->last_hwmon_state = MLXREG_FAN_PWM_DUTY2STATE(MLXREG_FAN_MIN_DUTY); + } return 0; } @@ -496,16 +593,10 @@ static int mlxreg_fan_probe(struct platform_device *pdev) return PTR_ERR(hwm); } - if (IS_REACHABLE(CONFIG_THERMAL)) { - fan->cdev = devm_thermal_of_cooling_device_register(dev, - NULL, "mlxreg_fan", fan, &mlxreg_fan_cooling_ops); - if (IS_ERR(fan->cdev)) { - dev_err(dev, "Failed to register cooling device\n"); - return PTR_ERR(fan->cdev); - } - } + if (IS_REACHABLE(CONFIG_THERMAL)) + err = mlxreg_fan_cooling_config(dev, fan); - return 0; + return err; } static struct platform_driver mlxreg_fan_driver = { diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c new file mode 100644 index 000000000000..394a4c7e46ab --- /dev/null +++ b/drivers/hwmon/mr75203.c @@ -0,0 +1,927 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MaxLinear, Inc. + * + * This driver is a hardware monitoring driver for PVT controller + * (MR75203) which is used to configure & control Moortec embedded + * analog IP to enable multiple embedded temperature sensor(TS), + * voltage monitor(VM) & process detector(PD) modules. + */ +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/hwmon.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/slab.h> +#include <linux/units.h> + +/* PVT Common register */ +#define PVT_IP_CONFIG 0x04 +#define TS_NUM_MSK GENMASK(4, 0) +#define TS_NUM_SFT 0 +#define PD_NUM_MSK GENMASK(12, 8) +#define PD_NUM_SFT 8 +#define VM_NUM_MSK GENMASK(20, 16) +#define VM_NUM_SFT 16 +#define CH_NUM_MSK GENMASK(31, 24) +#define CH_NUM_SFT 24 + +#define VM_NUM_MAX (VM_NUM_MSK >> VM_NUM_SFT) + +/* Macro Common Register */ +#define CLK_SYNTH 0x00 +#define CLK_SYNTH_LO_SFT 0 +#define CLK_SYNTH_HI_SFT 8 +#define CLK_SYNTH_HOLD_SFT 16 +#define CLK_SYNTH_EN BIT(24) +#define CLK_SYS_CYCLES_MAX 514 +#define CLK_SYS_CYCLES_MIN 2 + +#define SDIF_DISABLE 0x04 + +#define SDIF_STAT 0x08 +#define SDIF_BUSY BIT(0) +#define SDIF_LOCK BIT(1) + +#define SDIF_W 0x0c +#define SDIF_PROG BIT(31) +#define SDIF_WRN_W BIT(27) +#define SDIF_WRN_R 0x00 +#define SDIF_ADDR_SFT 24 + +#define SDIF_HALT 0x10 +#define SDIF_CTRL 0x14 +#define SDIF_SMPL_CTRL 0x20 + +/* TS & PD Individual Macro Register */ +#define COM_REG_SIZE 0x40 + +#define SDIF_DONE(n) (COM_REG_SIZE + 0x14 + 0x40 * (n)) +#define SDIF_SMPL_DONE BIT(0) + +#define SDIF_DATA(n) (COM_REG_SIZE + 0x18 + 0x40 * (n)) +#define SAMPLE_DATA_MSK GENMASK(15, 0) + +#define HILO_RESET(n) (COM_REG_SIZE + 0x2c + 0x40 * (n)) + +/* VM Individual Macro Register */ +#define VM_COM_REG_SIZE 0x200 +#define VM_SDIF_DONE(vm) (VM_COM_REG_SIZE + 0x34 + 0x200 * (vm)) +#define VM_SDIF_DATA(vm, ch) \ + (VM_COM_REG_SIZE + 0x40 + 0x200 * (vm) + 0x4 * (ch)) + +/* SDA Slave Register */ +#define IP_CTRL 0x00 +#define IP_RST_REL BIT(1) +#define IP_RUN_CONT BIT(3) +#define IP_AUTO BIT(8) +#define IP_VM_MODE BIT(10) + +#define IP_CFG 0x01 +#define CFG0_MODE_2 BIT(0) +#define CFG0_PARALLEL_OUT 0 +#define CFG0_12_BIT 0 +#define CFG1_VOL_MEAS_MODE 0 +#define CFG1_PARALLEL_OUT 0 +#define CFG1_14_BIT 0 + +#define IP_DATA 0x03 + +#define IP_POLL 0x04 +#define VM_CH_INIT BIT(20) +#define VM_CH_REQ BIT(21) + +#define IP_TMR 0x05 +#define POWER_DELAY_CYCLE_256 0x100 +#define POWER_DELAY_CYCLE_64 0x40 + +#define PVT_POLL_DELAY_US 20 +#define PVT_POLL_TIMEOUT_US 20000 +#define PVT_CONV_BITS 10 +#define PVT_N_CONST 90 +#define PVT_R_CONST 245805 + +#define PVT_TEMP_MIN_mC -40000 +#define PVT_TEMP_MAX_mC 125000 + +/* Temperature coefficients for series 5 */ +#define PVT_SERIES5_H_CONST 200000 +#define PVT_SERIES5_G_CONST 60000 +#define PVT_SERIES5_J_CONST -100 +#define PVT_SERIES5_CAL5_CONST 4094 + +/* Temperature coefficients for series 6 */ +#define PVT_SERIES6_H_CONST 249400 +#define PVT_SERIES6_G_CONST 57400 +#define PVT_SERIES6_J_CONST 0 +#define PVT_SERIES6_CAL5_CONST 4096 + +#define TEMPERATURE_SENSOR_SERIES_5 5 +#define TEMPERATURE_SENSOR_SERIES_6 6 + +#define PRE_SCALER_X1 1 +#define PRE_SCALER_X2 2 + +/** + * struct voltage_device - VM single input parameters. + * @vm_map: Map channel number to VM index. + * @ch_map: Map channel number to channel index. + * @pre_scaler: Pre scaler value (1 or 2) used to normalize the voltage output + * result. + * + * The structure provides mapping between channel-number (0..N-1) to VM-index + * (0..num_vm-1) and channel-index (0..ch_num-1) where N = num_vm * ch_num. + * It also provides normalization factor for the VM equation. + */ +struct voltage_device { + u32 vm_map; + u32 ch_map; + u32 pre_scaler; +}; + +/** + * struct voltage_channels - VM channel count. + * @total: Total number of channels in all VMs. + * @max: Maximum number of channels among all VMs. + * + * The structure provides channel count information across all VMs. + */ +struct voltage_channels { + u32 total; + u8 max; +}; + +struct temp_coeff { + u32 h; + u32 g; + u32 cal5; + s32 j; +}; + +struct pvt_device { + struct regmap *c_map; + struct regmap *t_map; + struct regmap *p_map; + struct regmap *v_map; + struct clk *clk; + struct reset_control *rst; + struct dentry *dbgfs_dir; + struct voltage_device *vd; + struct voltage_channels vm_channels; + struct temp_coeff ts_coeff; + u32 t_num; + u32 p_num; + u32 v_num; + u32 ip_freq; +}; + +static ssize_t pvt_ts_coeff_j_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct pvt_device *pvt = file->private_data; + unsigned int len; + char buf[13]; + + len = scnprintf(buf, sizeof(buf), "%d\n", pvt->ts_coeff.j); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t pvt_ts_coeff_j_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct pvt_device *pvt = file->private_data; + int ret; + + ret = kstrtos32_from_user(user_buf, count, 0, &pvt->ts_coeff.j); + if (ret) + return ret; + + return count; +} + +static const struct file_operations pvt_ts_coeff_j_fops = { + .read = pvt_ts_coeff_j_read, + .write = pvt_ts_coeff_j_write, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static void devm_pvt_ts_dbgfs_remove(void *data) +{ + struct pvt_device *pvt = (struct pvt_device *)data; + + debugfs_remove_recursive(pvt->dbgfs_dir); + pvt->dbgfs_dir = NULL; +} + +static int pvt_ts_dbgfs_create(struct pvt_device *pvt, struct device *dev) +{ + pvt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL); + + debugfs_create_u32("ts_coeff_h", 0644, pvt->dbgfs_dir, + &pvt->ts_coeff.h); + debugfs_create_u32("ts_coeff_g", 0644, pvt->dbgfs_dir, + &pvt->ts_coeff.g); + debugfs_create_u32("ts_coeff_cal5", 0644, pvt->dbgfs_dir, + &pvt->ts_coeff.cal5); + debugfs_create_file("ts_coeff_j", 0644, pvt->dbgfs_dir, pvt, + &pvt_ts_coeff_j_fops); + + return devm_add_action_or_reset(dev, devm_pvt_ts_dbgfs_remove, pvt); +} + +static umode_t pvt_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + if (attr == hwmon_temp_input) + return 0444; + break; + case hwmon_in: + if (attr == hwmon_in_input) + return 0444; + break; + default: + break; + } + return 0; +} + +static long pvt_calc_temp(struct pvt_device *pvt, u32 nbs) +{ + /* + * Convert the register value to degrees centigrade temperature: + * T = G + H * (n / cal5 - 0.5) + J * F + */ + struct temp_coeff *ts_coeff = &pvt->ts_coeff; + + s64 tmp = ts_coeff->g + + div_s64(ts_coeff->h * (s64)nbs, ts_coeff->cal5) - + ts_coeff->h / 2 + + div_s64(ts_coeff->j * (s64)pvt->ip_freq, HZ_PER_MHZ); + + return clamp_val(tmp, PVT_TEMP_MIN_mC, PVT_TEMP_MAX_mC); +} + +static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val) +{ + struct pvt_device *pvt = dev_get_drvdata(dev); + struct regmap *t_map = pvt->t_map; + u32 stat, nbs; + int ret; + + switch (attr) { + case hwmon_temp_input: + ret = regmap_read_poll_timeout(t_map, SDIF_DONE(channel), + stat, stat & SDIF_SMPL_DONE, + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + ret = regmap_read(t_map, SDIF_DATA(channel), &nbs); + if (ret < 0) + return ret; + + nbs &= SAMPLE_DATA_MSK; + + /* + * Convert the register value to + * degrees centigrade temperature + */ + *val = pvt_calc_temp(pvt, nbs); + + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val) +{ + struct pvt_device *pvt = dev_get_drvdata(dev); + struct regmap *v_map = pvt->v_map; + u32 n, stat, pre_scaler; + u8 vm_idx, ch_idx; + int ret; + + if (channel >= pvt->vm_channels.total) + return -EINVAL; + + vm_idx = pvt->vd[channel].vm_map; + ch_idx = pvt->vd[channel].ch_map; + + switch (attr) { + case hwmon_in_input: + ret = regmap_read_poll_timeout(v_map, VM_SDIF_DONE(vm_idx), + stat, stat & SDIF_SMPL_DONE, + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + ret = regmap_read(v_map, VM_SDIF_DATA(vm_idx, ch_idx), &n); + if (ret < 0) + return ret; + + n &= SAMPLE_DATA_MSK; + pre_scaler = pvt->vd[channel].pre_scaler; + /* + * Convert the N bitstream count into voltage. + * To support negative voltage calculation for 64bit machines + * n must be cast to long, since n and *val differ both in + * signedness and in size. + * Division is used instead of right shift, because for signed + * numbers, the sign bit is used to fill the vacated bit + * positions, and if the number is negative, 1 is used. + * BIT(x) may not be used instead of (1 << x) because it's + * unsigned. + */ + *val = pre_scaler * (PVT_N_CONST * (long)n - PVT_R_CONST) / + (1 << PVT_CONV_BITS); + + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int pvt_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_temp: + return pvt_read_temp(dev, attr, channel, val); + case hwmon_in: + return pvt_read_in(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static struct hwmon_channel_info pvt_temp = { + .type = hwmon_temp, +}; + +static struct hwmon_channel_info pvt_in = { + .type = hwmon_in, +}; + +static const struct hwmon_ops pvt_hwmon_ops = { + .is_visible = pvt_is_visible, + .read = pvt_read, +}; + +static struct hwmon_chip_info pvt_chip_info = { + .ops = &pvt_hwmon_ops, +}; + +static int pvt_init(struct pvt_device *pvt) +{ + u16 sys_freq, key, middle, low = 4, high = 8; + struct regmap *t_map = pvt->t_map; + struct regmap *p_map = pvt->p_map; + struct regmap *v_map = pvt->v_map; + u32 t_num = pvt->t_num; + u32 p_num = pvt->p_num; + u32 v_num = pvt->v_num; + u32 clk_synth, val; + int ret; + + sys_freq = clk_get_rate(pvt->clk) / HZ_PER_MHZ; + while (high >= low) { + middle = (low + high + 1) / 2; + key = DIV_ROUND_CLOSEST(sys_freq, middle); + if (key > CLK_SYS_CYCLES_MAX) { + low = middle + 1; + continue; + } else if (key < CLK_SYS_CYCLES_MIN) { + high = middle - 1; + continue; + } else { + break; + } + } + + /* + * The system supports 'clk_sys' to 'clk_ip' frequency ratios + * from 2:1 to 512:1 + */ + key = clamp_val(key, CLK_SYS_CYCLES_MIN, CLK_SYS_CYCLES_MAX) - 2; + + clk_synth = ((key + 1) >> 1) << CLK_SYNTH_LO_SFT | + (key >> 1) << CLK_SYNTH_HI_SFT | + (key >> 1) << CLK_SYNTH_HOLD_SFT | CLK_SYNTH_EN; + + pvt->ip_freq = clk_get_rate(pvt->clk) / (key + 2); + + if (t_num) { + ret = regmap_write(t_map, SDIF_SMPL_CTRL, 0x0); + if (ret < 0) + return ret; + + ret = regmap_write(t_map, SDIF_HALT, 0x0); + if (ret < 0) + return ret; + + ret = regmap_write(t_map, CLK_SYNTH, clk_synth); + if (ret < 0) + return ret; + + ret = regmap_write(t_map, SDIF_DISABLE, 0x0); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(t_map, SDIF_STAT, + val, !(val & SDIF_BUSY), + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + val = CFG0_MODE_2 | CFG0_PARALLEL_OUT | CFG0_12_BIT | + IP_CFG << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; + ret = regmap_write(t_map, SDIF_W, val); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(t_map, SDIF_STAT, + val, !(val & SDIF_BUSY), + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + val = POWER_DELAY_CYCLE_256 | IP_TMR << SDIF_ADDR_SFT | + SDIF_WRN_W | SDIF_PROG; + ret = regmap_write(t_map, SDIF_W, val); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(t_map, SDIF_STAT, + val, !(val & SDIF_BUSY), + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + val = IP_RST_REL | IP_RUN_CONT | IP_AUTO | + IP_CTRL << SDIF_ADDR_SFT | + SDIF_WRN_W | SDIF_PROG; + ret = regmap_write(t_map, SDIF_W, val); + if (ret < 0) + return ret; + } + + if (p_num) { + ret = regmap_write(p_map, SDIF_HALT, 0x0); + if (ret < 0) + return ret; + + ret = regmap_write(p_map, SDIF_DISABLE, BIT(p_num) - 1); + if (ret < 0) + return ret; + + ret = regmap_write(p_map, CLK_SYNTH, clk_synth); + if (ret < 0) + return ret; + } + + if (v_num) { + ret = regmap_write(v_map, SDIF_SMPL_CTRL, 0x0); + if (ret < 0) + return ret; + + ret = regmap_write(v_map, SDIF_HALT, 0x0); + if (ret < 0) + return ret; + + ret = regmap_write(v_map, CLK_SYNTH, clk_synth); + if (ret < 0) + return ret; + + ret = regmap_write(v_map, SDIF_DISABLE, 0x0); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(v_map, SDIF_STAT, + val, !(val & SDIF_BUSY), + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + val = (BIT(pvt->vm_channels.max) - 1) | VM_CH_INIT | + IP_POLL << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; + ret = regmap_write(v_map, SDIF_W, val); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(v_map, SDIF_STAT, + val, !(val & SDIF_BUSY), + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + val = CFG1_VOL_MEAS_MODE | CFG1_PARALLEL_OUT | + CFG1_14_BIT | IP_CFG << SDIF_ADDR_SFT | + SDIF_WRN_W | SDIF_PROG; + ret = regmap_write(v_map, SDIF_W, val); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(v_map, SDIF_STAT, + val, !(val & SDIF_BUSY), + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + val = POWER_DELAY_CYCLE_64 | IP_TMR << SDIF_ADDR_SFT | + SDIF_WRN_W | SDIF_PROG; + ret = regmap_write(v_map, SDIF_W, val); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(v_map, SDIF_STAT, + val, !(val & SDIF_BUSY), + PVT_POLL_DELAY_US, + PVT_POLL_TIMEOUT_US); + if (ret) + return ret; + + val = IP_RST_REL | IP_RUN_CONT | IP_AUTO | IP_VM_MODE | + IP_CTRL << SDIF_ADDR_SFT | + SDIF_WRN_W | SDIF_PROG; + ret = regmap_write(v_map, SDIF_W, val); + if (ret < 0) + return ret; + } + + return 0; +} + +static struct regmap_config pvt_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static int pvt_get_regmap(struct platform_device *pdev, char *reg_name, + struct pvt_device *pvt) +{ + struct device *dev = &pdev->dev; + struct regmap **reg_map; + void __iomem *io_base; + + if (!strcmp(reg_name, "common")) + reg_map = &pvt->c_map; + else if (!strcmp(reg_name, "ts")) + reg_map = &pvt->t_map; + else if (!strcmp(reg_name, "pd")) + reg_map = &pvt->p_map; + else if (!strcmp(reg_name, "vm")) + reg_map = &pvt->v_map; + else + return -EINVAL; + + io_base = devm_platform_ioremap_resource_byname(pdev, reg_name); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + pvt_regmap_config.name = reg_name; + *reg_map = devm_regmap_init_mmio(dev, io_base, &pvt_regmap_config); + if (IS_ERR(*reg_map)) { + dev_err(dev, "failed to init register map\n"); + return PTR_ERR(*reg_map); + } + + return 0; +} + +static void pvt_reset_control_assert(void *data) +{ + struct pvt_device *pvt = data; + + reset_control_assert(pvt->rst); +} + +static int pvt_reset_control_deassert(struct device *dev, struct pvt_device *pvt) +{ + int ret; + + ret = reset_control_deassert(pvt->rst); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, pvt_reset_control_assert, pvt); +} + +static int pvt_get_active_channel(struct device *dev, struct pvt_device *pvt, + u32 vm_num, u32 ch_num, u8 *vm_idx) +{ + u8 vm_active_ch[VM_NUM_MAX]; + int ret, i, j, k; + + ret = device_property_read_u8_array(dev, "moortec,vm-active-channels", + vm_active_ch, vm_num); + if (ret) { + /* + * Incase "moortec,vm-active-channels" property is not defined, + * we assume each VM sensor has all of its channels active. + */ + memset(vm_active_ch, ch_num, vm_num); + pvt->vm_channels.max = ch_num; + pvt->vm_channels.total = ch_num * vm_num; + } else { + for (i = 0; i < vm_num; i++) { + if (vm_active_ch[i] > ch_num) { + dev_err(dev, "invalid active channels: %u\n", + vm_active_ch[i]); + return -EINVAL; + } + + pvt->vm_channels.total += vm_active_ch[i]; + + if (vm_active_ch[i] > pvt->vm_channels.max) + pvt->vm_channels.max = vm_active_ch[i]; + } + } + + /* + * Map between the channel-number to VM-index and channel-index. + * Example - 3 VMs, "moortec,vm_active_ch" = <5 2 4>: + * vm_map = [0 0 0 0 0 1 1 2 2 2 2] + * ch_map = [0 1 2 3 4 0 1 0 1 2 3] + */ + pvt->vd = devm_kcalloc(dev, pvt->vm_channels.total, sizeof(*pvt->vd), + GFP_KERNEL); + if (!pvt->vd) + return -ENOMEM; + + k = 0; + for (i = 0; i < vm_num; i++) { + for (j = 0; j < vm_active_ch[i]; j++) { + pvt->vd[k].vm_map = vm_idx[i]; + pvt->vd[k].ch_map = j; + k++; + } + } + + return 0; +} + +static int pvt_get_pre_scaler(struct device *dev, struct pvt_device *pvt) +{ + u8 *pre_scaler_ch_list; + int i, ret, num_ch; + u32 channel; + + /* Set default pre-scaler value to be 1. */ + for (i = 0; i < pvt->vm_channels.total; i++) + pvt->vd[i].pre_scaler = PRE_SCALER_X1; + + /* Get number of channels configured in "moortec,vm-pre-scaler-x2". */ + num_ch = device_property_count_u8(dev, "moortec,vm-pre-scaler-x2"); + if (num_ch <= 0) + return 0; + + pre_scaler_ch_list = kcalloc(num_ch, sizeof(*pre_scaler_ch_list), + GFP_KERNEL); + if (!pre_scaler_ch_list) + return -ENOMEM; + + /* Get list of all channels that have pre-scaler of 2. */ + ret = device_property_read_u8_array(dev, "moortec,vm-pre-scaler-x2", + pre_scaler_ch_list, num_ch); + if (ret) + goto out; + + for (i = 0; i < num_ch; i++) { + channel = pre_scaler_ch_list[i]; + pvt->vd[channel].pre_scaler = PRE_SCALER_X2; + } + +out: + kfree(pre_scaler_ch_list); + + return ret; +} + +static int pvt_set_temp_coeff(struct device *dev, struct pvt_device *pvt) +{ + struct temp_coeff *ts_coeff = &pvt->ts_coeff; + u32 series; + int ret; + + /* Incase ts-series property is not defined, use default 5. */ + ret = device_property_read_u32(dev, "moortec,ts-series", &series); + if (ret) + series = TEMPERATURE_SENSOR_SERIES_5; + + switch (series) { + case TEMPERATURE_SENSOR_SERIES_5: + ts_coeff->h = PVT_SERIES5_H_CONST; + ts_coeff->g = PVT_SERIES5_G_CONST; + ts_coeff->j = PVT_SERIES5_J_CONST; + ts_coeff->cal5 = PVT_SERIES5_CAL5_CONST; + break; + case TEMPERATURE_SENSOR_SERIES_6: + ts_coeff->h = PVT_SERIES6_H_CONST; + ts_coeff->g = PVT_SERIES6_G_CONST; + ts_coeff->j = PVT_SERIES6_J_CONST; + ts_coeff->cal5 = PVT_SERIES6_CAL5_CONST; + break; + default: + dev_err(dev, "invalid temperature sensor series (%u)\n", + series); + return -EINVAL; + } + + dev_dbg(dev, "temperature sensor series = %u\n", series); + + /* Override ts-coeff-h/g/j/cal5 if they are defined. */ + device_property_read_u32(dev, "moortec,ts-coeff-h", &ts_coeff->h); + device_property_read_u32(dev, "moortec,ts-coeff-g", &ts_coeff->g); + device_property_read_u32(dev, "moortec,ts-coeff-j", &ts_coeff->j); + device_property_read_u32(dev, "moortec,ts-coeff-cal5", &ts_coeff->cal5); + + dev_dbg(dev, "ts-coeff: h = %u, g = %u, j = %d, cal5 = %u\n", + ts_coeff->h, ts_coeff->g, ts_coeff->j, ts_coeff->cal5); + + return 0; +} + +static int mr75203_probe(struct platform_device *pdev) +{ + u32 ts_num, vm_num, pd_num, ch_num, val, index, i; + const struct hwmon_channel_info **pvt_info; + struct device *dev = &pdev->dev; + u32 *temp_config, *in_config; + struct device *hwmon_dev; + struct pvt_device *pvt; + int ret; + + pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); + if (!pvt) + return -ENOMEM; + + ret = pvt_get_regmap(pdev, "common", pvt); + if (ret) + return ret; + + pvt->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(pvt->clk)) + return dev_err_probe(dev, PTR_ERR(pvt->clk), "failed to get clock\n"); + + pvt->rst = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(pvt->rst)) + return dev_err_probe(dev, PTR_ERR(pvt->rst), + "failed to get reset control\n"); + + if (pvt->rst) { + ret = pvt_reset_control_deassert(dev, pvt); + if (ret) + return dev_err_probe(dev, ret, + "cannot deassert reset control\n"); + } + + ret = regmap_read(pvt->c_map, PVT_IP_CONFIG, &val); + if (ret < 0) + return ret; + + ts_num = (val & TS_NUM_MSK) >> TS_NUM_SFT; + pd_num = (val & PD_NUM_MSK) >> PD_NUM_SFT; + vm_num = (val & VM_NUM_MSK) >> VM_NUM_SFT; + ch_num = (val & CH_NUM_MSK) >> CH_NUM_SFT; + pvt->t_num = ts_num; + pvt->p_num = pd_num; + pvt->v_num = vm_num; + val = 0; + if (ts_num) + val++; + if (vm_num) + val++; + if (!val) + return -ENODEV; + + pvt_info = devm_kcalloc(dev, val + 2, sizeof(*pvt_info), GFP_KERNEL); + if (!pvt_info) + return -ENOMEM; + pvt_info[0] = HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ); + index = 1; + + if (ts_num) { + ret = pvt_get_regmap(pdev, "ts", pvt); + if (ret) + return ret; + + ret = pvt_set_temp_coeff(dev, pvt); + if (ret) + return ret; + + temp_config = devm_kcalloc(dev, ts_num + 1, + sizeof(*temp_config), GFP_KERNEL); + if (!temp_config) + return -ENOMEM; + + memset32(temp_config, HWMON_T_INPUT, ts_num); + pvt_temp.config = temp_config; + pvt_info[index++] = &pvt_temp; + + pvt_ts_dbgfs_create(pvt, dev); + } + + if (pd_num) { + ret = pvt_get_regmap(pdev, "pd", pvt); + if (ret) + return ret; + } + + if (vm_num) { + u8 vm_idx[VM_NUM_MAX]; + + ret = pvt_get_regmap(pdev, "vm", pvt); + if (ret) + return ret; + + ret = device_property_read_u8_array(dev, "intel,vm-map", vm_idx, + vm_num); + if (ret) { + /* + * Incase intel,vm-map property is not defined, we + * assume incremental channel numbers. + */ + for (i = 0; i < vm_num; i++) + vm_idx[i] = i; + } else { + for (i = 0; i < vm_num; i++) + if (vm_idx[i] >= vm_num || vm_idx[i] == 0xff) { + pvt->v_num = i; + vm_num = i; + break; + } + } + + ret = pvt_get_active_channel(dev, pvt, vm_num, ch_num, vm_idx); + if (ret) + return ret; + + ret = pvt_get_pre_scaler(dev, pvt); + if (ret) + return ret; + + in_config = devm_kcalloc(dev, pvt->vm_channels.total + 1, + sizeof(*in_config), GFP_KERNEL); + if (!in_config) + return -ENOMEM; + + memset32(in_config, HWMON_I_INPUT, pvt->vm_channels.total); + in_config[pvt->vm_channels.total] = 0; + pvt_in.config = in_config; + + pvt_info[index++] = &pvt_in; + } + + ret = pvt_init(pvt); + if (ret) { + dev_err(dev, "failed to init pvt: %d\n", ret); + return ret; + } + + pvt_chip_info.info = pvt_info; + hwmon_dev = devm_hwmon_device_register_with_info(dev, "pvt", + pvt, + &pvt_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id moortec_pvt_of_match[] = { + { .compatible = "moortec,mr75203" }, + { } +}; +MODULE_DEVICE_TABLE(of, moortec_pvt_of_match); + +static struct platform_driver moortec_pvt_driver = { + .driver = { + .name = "moortec-pvt", + .of_match_table = moortec_pvt_of_match, + }, + .probe = mr75203_probe, +}; +module_platform_driver(moortec_pvt_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index c0229152296f..a872f783e9cc 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * nct6683 - Driver for the hardware monitoring functionality of - * Nuvoton NCT6683D eSIO + * Nuvoton NCT6683D/NCT6686D/NCT6687D eSIO * * Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net> * @@ -12,6 +12,8 @@ * * Chip #vin #fan #pwm #temp chip ID * nct6683d 21(1) 16 8 32(1) 0xc730 + * nct6686d 21(1) 16 8 32(1) 0xd440 + * nct6687d 21(1) 16 8 32(1) 0xd590 * * Notes: * (1) Total number of vin and temp inputs is 32. @@ -32,7 +34,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> -enum kinds { nct6683 }; +enum kinds { nct6683, nct6686, nct6687 }; static bool force; module_param(force, bool, 0); @@ -40,10 +42,14 @@ MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors"); static const char * const nct6683_device_names[] = { "nct6683", + "nct6686", + "nct6687", }; static const char * const nct6683_chip_names[] = { "NCT6683D", + "NCT6686D", + "NCT6687D", }; #define DRVNAME "nct6683" @@ -63,6 +69,8 @@ static const char * const nct6683_chip_names[] = { #define SIO_NCT6681_ID 0xb270 /* for later */ #define SIO_NCT6683_ID 0xc730 +#define SIO_NCT6686_ID 0xd440 +#define SIO_NCT6687_ID 0xd590 #define SIO_ID_MASK 0xFFF0 static inline void @@ -164,6 +172,9 @@ superio_exit(int ioreg) #define NCT6683_REG_CUSTOMER_ID 0x602 #define NCT6683_CUSTOMER_ID_INTEL 0x805 #define NCT6683_CUSTOMER_ID_MITAC 0xa0e +#define NCT6683_CUSTOMER_ID_MSI 0x201 +#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c +#define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -401,7 +412,7 @@ nct6683_create_attr_group(struct device *dev, struct sensor_device_attr_u *su; struct attribute_group *group; struct attribute **attrs; - int i, j, count; + int i, count; if (repeat <= 0) return ERR_PTR(-EINVAL); @@ -432,7 +443,7 @@ nct6683_create_attr_group(struct device *dev, for (i = 0; i < repeat; i++) { t = tg->templates; - for (j = 0; *t != NULL; j++) { + while (*t) { snprintf(su->name, sizeof(su->name), (*t)->dev_attr.attr.name, tg->base + i); if ((*t)->s2) { @@ -482,17 +493,6 @@ static inline long in_from_reg(u16 reg, u8 src) return reg * scale; } -static inline u16 in_to_reg(u32 val, u8 src) -{ - int scale = 16; - - if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB || - src == MON_SRC_VBAT) - scale <<= 1; - - return clamp_val(DIV_ROUND_CLOSEST(val, scale), 0, 127); -} - static u16 nct6683_read(struct nct6683_data *data, u16 reg) { int res; @@ -674,7 +674,7 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) static umode_t nct6683_in_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6683_data *data = dev_get_drvdata(dev); int nr = index % 4; /* attribute */ @@ -739,7 +739,7 @@ show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf) static umode_t nct6683_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6683_data *data = dev_get_drvdata(dev); int fan = index / 3; /* fan index */ int nr = index % 3; /* attribute index */ @@ -857,7 +857,7 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) static umode_t nct6683_temp_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6683_data *data = dev_get_drvdata(dev); int temp = index / 7; /* temp index */ int nr = index % 7; /* attribute index */ @@ -944,7 +944,7 @@ SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, store_pwm, 0); static umode_t nct6683_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6683_data *data = dev_get_drvdata(dev); int pwm = index; /* pwm index */ @@ -1218,6 +1218,12 @@ static int nct6683_probe(struct platform_device *pdev) break; case NCT6683_CUSTOMER_ID_MITAC: break; + case NCT6683_CUSTOMER_ID_MSI: + break; + case NCT6683_CUSTOMER_ID_ASROCK: + break; + case NCT6683_CUSTOMER_ID_ASROCK2: + break; default: if (!force) return -ENODEV; @@ -1352,6 +1358,12 @@ static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data) case SIO_NCT6683_ID: sio_data->kind = nct6683; break; + case SIO_NCT6686_ID: + sio_data->kind = nct6686; + break; + case SIO_NCT6687_ID: + sio_data->kind = nct6687; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775-core.c index 7efa6bfef060..da9ec6983e13 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775-core.c @@ -44,23 +44,20 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> -#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> #include <linux/bitops.h> -#include <linux/dmi.h> -#include <linux/io.h> #include <linux/nospec.h> +#include <linux/regmap.h> #include "lm75.h" +#include "nct6775.h" -#define USE_ALTERNATE +#undef DEFAULT_SYMBOL_NAMESPACE +#define DEFAULT_SYMBOL_NAMESPACE HWMON_NCT6775 -enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, - nct6793, nct6795, nct6796, nct6797, nct6798 }; +#define USE_ALTERNATE /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -78,135 +75,6 @@ static const char * const nct6775_device_names[] = { "nct6798", }; -static const char * const nct6775_sio_names[] __initconst = { - "NCT6106D", - "NCT6116D", - "NCT6775F", - "NCT6776D/F", - "NCT6779D", - "NCT6791D", - "NCT6792D", - "NCT6793D", - "NCT6795D", - "NCT6796D", - "NCT6797D", - "NCT6798D", -}; - -static unsigned short force_id; -module_param(force_id, ushort, 0); -MODULE_PARM_DESC(force_id, "Override the detected device ID"); - -static unsigned short fan_debounce; -module_param(fan_debounce, ushort, 0); -MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); - -#define DRVNAME "nct6775" - -/* - * Super-I/O constants and functions - */ - -#define NCT6775_LD_ACPI 0x0a -#define NCT6775_LD_HWM 0x0b -#define NCT6775_LD_VID 0x0d -#define NCT6775_LD_12 0x12 - -#define SIO_REG_LDSEL 0x07 /* Logical device select */ -#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ -#define SIO_REG_ENABLE 0x30 /* Logical device enable */ -#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ - -#define SIO_NCT6106_ID 0xc450 -#define SIO_NCT6116_ID 0xd280 -#define SIO_NCT6775_ID 0xb470 -#define SIO_NCT6776_ID 0xc330 -#define SIO_NCT6779_ID 0xc560 -#define SIO_NCT6791_ID 0xc800 -#define SIO_NCT6792_ID 0xc910 -#define SIO_NCT6793_ID 0xd120 -#define SIO_NCT6795_ID 0xd350 -#define SIO_NCT6796_ID 0xd420 -#define SIO_NCT6797_ID 0xd450 -#define SIO_NCT6798_ID 0xd428 -#define SIO_ID_MASK 0xFFF8 - -enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; - -static inline void -superio_outb(int ioreg, int reg, int val) -{ - outb(reg, ioreg); - outb(val, ioreg + 1); -} - -static inline int -superio_inb(int ioreg, int reg) -{ - outb(reg, ioreg); - return inb(ioreg + 1); -} - -static inline void -superio_select(int ioreg, int ld) -{ - outb(SIO_REG_LDSEL, ioreg); - outb(ld, ioreg + 1); -} - -static inline int -superio_enter(int ioreg) -{ - /* - * Try to reserve <ioreg> and <ioreg + 1> for exclusive access. - */ - if (!request_muxed_region(ioreg, 2, DRVNAME)) - return -EBUSY; - - outb(0x87, ioreg); - outb(0x87, ioreg); - - return 0; -} - -static inline void -superio_exit(int ioreg) -{ - outb(0xaa, ioreg); - outb(0x02, ioreg); - outb(0x02, ioreg + 1); - release_region(ioreg, 2); -} - -/* - * ISA constants - */ - -#define IOREGION_ALIGNMENT (~7) -#define IOREGION_OFFSET 5 -#define IOREGION_LENGTH 2 -#define ADDR_REG_OFFSET 0 -#define DATA_REG_OFFSET 1 - -#define NCT6775_REG_BANK 0x4E -#define NCT6775_REG_CONFIG 0x40 - -/* - * Not currently used: - * REG_MAN_ID has the value 0x5ca3 for all supported chips. - * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. - * REG_MAN_ID is at port 0x4f - * REG_CHIP_ID is at port 0x58 - */ - -#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ -#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */ - -#define NUM_REG_ALARM 7 /* Max number of alarm registers */ -#define NUM_REG_BEEP 5 /* Max number of beep registers */ - -#define NUM_FAN 7 - /* Common and NCT6775 specific data */ /* Voltage min/max registers for nr=7..14 are in bank 5 */ @@ -225,11 +93,6 @@ static const u16 NCT6775_REG_IN[] = { #define NCT6775_REG_DIODE 0x5E #define NCT6775_DIODE_MASK 0x02 -#define NCT6775_REG_FANDIV1 0x506 -#define NCT6775_REG_FANDIV2 0x507 - -#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0 - static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; /* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */ @@ -243,10 +106,6 @@ static const s8 NCT6775_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, -1 }; /* intrusion0, intrusion1 */ -#define FAN_ALARM_BASE 16 -#define TEMP_ALARM_BASE 24 -#define INTRUSION_ALARM_BASE 30 - static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e }; /* @@ -262,11 +121,6 @@ static const s8 NCT6775_BEEP_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, -1 }; /* intrusion0, intrusion1 */ -#define BEEP_ENABLE_BASE 15 - -static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; -static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; - /* DC or PWM output fan configuration */ static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 }; static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 }; @@ -391,6 +245,8 @@ static const u16 NCT6775_REG_TEMP_CRIT[32] = { [11] = 0xa07 }; +static const u16 NCT6775_REG_TSI_TEMP[] = { 0x669 }; + /* NCT6776 specific data */ /* STEP_UP_TIME and STEP_DOWN_TIME regs are swapped for all chips but NCT6775 */ @@ -474,6 +330,9 @@ static const u16 NCT6776_REG_TEMP_CRIT[32] = { [12] = 0x70a, }; +static const u16 NCT6776_REG_TSI_TEMP[] = { + 0x409, 0x40b, 0x40d, 0x40f, 0x411, 0x413, 0x415, 0x417 }; + /* NCT6779 specific data */ static const u16 NCT6779_REG_IN[] = { @@ -577,8 +436,6 @@ static const u16 NCT6779_REG_TEMP_CRIT[32] = { /* NCT6791 specific data */ -#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28 - static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[NUM_FAN] = { 0, 0x239 }; static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[NUM_FAN] = { 0, 0x23a }; static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[NUM_FAN] = { 0, 0x23b }; @@ -757,6 +614,8 @@ static const char *const nct6796_temp_label[] = { #define NCT6796_TEMP_MASK 0xbfff0ffe #define NCT6796_VIRT_TEMP_MASK 0x80000c00 +static const u16 NCT6796_REG_TSI_TEMP[] = { 0x409, 0x40b }; + static const char *const nct6798_temp_label[] = { "", "SYSTIN", @@ -786,13 +645,13 @@ static const char *const nct6798_temp_label[] = { "Agent1 Dimm1", "BYTE_TEMP0", "BYTE_TEMP1", - "", - "", + "PECI Agent 0 Calibration", /* undocumented */ + "PECI Agent 1 Calibration", /* undocumented */ "", "Virtual_TEMP" }; -#define NCT6798_TEMP_MASK 0x8fff0ffe +#define NCT6798_TEMP_MASK 0xbfff0ffe #define NCT6798_VIRT_TEMP_MASK 0x80000c00 /* NCT6102D/NCT6106D specific data */ @@ -898,6 +757,8 @@ static const u16 NCT6106_REG_TEMP_CRIT[32] = { [12] = 0x205, }; +static const u16 NCT6106_REG_TSI_TEMP[] = { 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65, 0x67 }; + /* NCT6112D/NCT6114D/NCT6116D specific data */ static const u16 NCT6116_REG_FAN[] = { 0x20, 0x22, 0x24, 0x26, 0x28 }; @@ -962,6 +823,8 @@ static const s8 NCT6116_BEEP_BITS[] = { 34, -1 /* intrusion0, intrusion1 */ }; +static const u16 NCT6116_REG_TSI_TEMP[] = { 0x59, 0x5b }; + static enum pwm_enable reg_to_pwm_enable(int pwm, int mode) { if (mode == 0 && pwm == 255) @@ -1062,166 +925,16 @@ static inline u8 in_to_reg(u32 val, u8 nr) return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); } +/* TSI temperatures are in 8.3 format */ +static inline unsigned int tsi_temp_from_reg(unsigned int reg) +{ + return (reg >> 5) * 125; +} + /* * Data structures and manipulation thereof */ -struct nct6775_data { - int addr; /* IO base of hw monitor block */ - int sioreg; /* SIO register address */ - enum kinds kind; - const char *name; - - const struct attribute_group *groups[6]; - - u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, - * 3=temp_crit, 4=temp_lcrit - */ - u8 temp_src[NUM_TEMP]; - u16 reg_temp_config[NUM_TEMP]; - const char * const *temp_label; - u32 temp_mask; - u32 virt_temp_mask; - - u16 REG_CONFIG; - u16 REG_VBAT; - u16 REG_DIODE; - u8 DIODE_MASK; - - const s8 *ALARM_BITS; - const s8 *BEEP_BITS; - - const u16 *REG_VIN; - const u16 *REG_IN_MINMAX[2]; - - const u16 *REG_TARGET; - const u16 *REG_FAN; - const u16 *REG_FAN_MODE; - const u16 *REG_FAN_MIN; - const u16 *REG_FAN_PULSES; - const u16 *FAN_PULSE_SHIFT; - const u16 *REG_FAN_TIME[3]; - - const u16 *REG_TOLERANCE_H; - - const u8 *REG_PWM_MODE; - const u8 *PWM_MODE_MASK; - - const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, - * [3]=pwm_max, [4]=pwm_step, - * [5]=weight_duty_step, [6]=weight_duty_base - */ - const u16 *REG_PWM_READ; - - const u16 *REG_CRITICAL_PWM_ENABLE; - u8 CRITICAL_PWM_ENABLE_MASK; - const u16 *REG_CRITICAL_PWM; - - const u16 *REG_AUTO_TEMP; - const u16 *REG_AUTO_PWM; - - const u16 *REG_CRITICAL_TEMP; - const u16 *REG_CRITICAL_TEMP_TOLERANCE; - - const u16 *REG_TEMP_SOURCE; /* temp register sources */ - const u16 *REG_TEMP_SEL; - const u16 *REG_WEIGHT_TEMP_SEL; - const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */ - - const u16 *REG_TEMP_OFFSET; - - const u16 *REG_ALARM; - const u16 *REG_BEEP; - - unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); - unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); - - struct mutex update_lock; - bool valid; /* true if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - /* Register values */ - u8 bank; /* current register bank */ - u8 in_num; /* number of in inputs we have */ - u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ - unsigned int rpm[NUM_FAN]; - u16 fan_min[NUM_FAN]; - u8 fan_pulses[NUM_FAN]; - u8 fan_div[NUM_FAN]; - u8 has_pwm; - u8 has_fan; /* some fan inputs can be disabled */ - u8 has_fan_min; /* some fans don't have min register */ - bool has_fan_div; - - u8 num_temp_alarms; /* 2, 3, or 6 */ - u8 num_temp_beeps; /* 2, 3, or 6 */ - u8 temp_fixed_num; /* 3 or 6 */ - u8 temp_type[NUM_TEMP_FIXED]; - s8 temp_offset[NUM_TEMP_FIXED]; - s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, - * 3=temp_crit, 4=temp_lcrit */ - u64 alarms; - u64 beeps; - - u8 pwm_num; /* number of pwm */ - u8 pwm_mode[NUM_FAN]; /* 0->DC variable voltage, - * 1->PWM variable duty cycle - */ - enum pwm_enable pwm_enable[NUM_FAN]; - /* 0->off - * 1->manual - * 2->thermal cruise mode (also called SmartFan I) - * 3->fan speed cruise mode - * 4->SmartFan III - * 5->enhanced variable thermal cruise (SmartFan IV) - */ - u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, - * [3]=pwm_max, [4]=pwm_step, - * [5]=weight_duty_step, [6]=weight_duty_base - */ - - u8 target_temp[NUM_FAN]; - u8 target_temp_mask; - u32 target_speed[NUM_FAN]; - u32 target_speed_tolerance[NUM_FAN]; - u8 speed_tolerance_limit; - - u8 temp_tolerance[2][NUM_FAN]; - u8 tolerance_mask; - - u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */ - - /* Automatic fan speed control registers */ - int auto_pwm_num; - u8 auto_pwm[NUM_FAN][7]; - u8 auto_temp[NUM_FAN][7]; - u8 pwm_temp_sel[NUM_FAN]; - u8 pwm_weight_temp_sel[NUM_FAN]; - u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol, - * 2->temp_base - */ - - u8 vid; - u8 vrm; - - bool have_vid; - - u16 have_temp; - u16 have_temp_fixed; - u16 have_in; - - /* Remember extra register values over suspend/resume */ - u8 vbat; - u8 fandiv1; - u8 fandiv2; - u8 sio_reg_enable; -}; - -struct nct6775_sio_data { - int sioreg; - enum kinds kind; -}; - struct sensor_device_template { struct device_attribute dev_attr; union { @@ -1277,10 +990,8 @@ struct sensor_template_group { int base; }; -static struct attribute_group * -nct6775_create_attr_group(struct device *dev, - const struct sensor_template_group *tg, - int repeat) +static int nct6775_add_template_attr_group(struct device *dev, struct nct6775_data *data, + const struct sensor_template_group *tg, int repeat) { struct attribute_group *group; struct sensor_device_attr_u *su; @@ -1291,28 +1002,28 @@ nct6775_create_attr_group(struct device *dev, int i, count; if (repeat <= 0) - return ERR_PTR(-EINVAL); + return -EINVAL; t = tg->templates; for (count = 0; *t; t++, count++) ; if (count == 0) - return ERR_PTR(-EINVAL); + return -EINVAL; group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL); if (group == NULL) - return ERR_PTR(-ENOMEM); + return -ENOMEM; attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs), GFP_KERNEL); if (attrs == NULL) - return ERR_PTR(-ENOMEM); + return -ENOMEM; su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)), GFP_KERNEL); if (su == NULL) - return ERR_PTR(-ENOMEM); + return -ENOMEM; group->attrs = attrs; group->is_visible = tg->is_visible; @@ -1350,21 +1061,23 @@ nct6775_create_attr_group(struct device *dev, } } - return group; + return nct6775_add_attr_group(data, group); } -static bool is_word_sized(struct nct6775_data *data, u16 reg) +bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg) { switch (data->kind) { case nct6106: return reg == 0x20 || reg == 0x22 || reg == 0x24 || + (reg >= 0x59 && reg < 0x69 && (reg & 1)) || reg == 0xe0 || reg == 0xe2 || reg == 0xe4 || reg == 0x111 || reg == 0x121 || reg == 0x131; case nct6116: return reg == 0x20 || reg == 0x22 || reg == 0x24 || - reg == 0x26 || reg == 0x28 || reg == 0xe0 || reg == 0xe2 || - reg == 0xe4 || reg == 0xe6 || reg == 0xe8 || reg == 0x111 || - reg == 0x121 || reg == 0x131 || reg == 0x191 || reg == 0x1a1; + reg == 0x26 || reg == 0x28 || reg == 0x59 || reg == 0x5b || + reg == 0xe0 || reg == 0xe2 || reg == 0xe4 || reg == 0xe6 || + reg == 0xe8 || reg == 0x111 || reg == 0x121 || reg == 0x131 || + reg == 0x191 || reg == 0x1a1; case nct6775: return (((reg & 0xff00) == 0x100 || (reg & 0xff00) == 0x200) && @@ -1373,7 +1086,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) (reg & 0x00ff) == 0x55)) || (reg & 0xfff0) == 0x630 || reg == 0x640 || reg == 0x642 || - reg == 0x662 || + reg == 0x662 || reg == 0x669 || ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || reg == 0x73 || reg == 0x75 || reg == 0x77; case nct6776: @@ -1384,6 +1097,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) (reg & 0x00ff) == 0x55)) || (reg & 0xfff0) == 0x630 || reg == 0x402 || + (reg >= 0x409 && reg < 0x419 && (reg & 1)) || reg == 0x640 || reg == 0x642 || ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || reg == 0x73 || reg == 0x75 || reg == 0x77; @@ -1398,6 +1112,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) return reg == 0x150 || reg == 0x153 || reg == 0x155 || (reg & 0xfff0) == 0x4c0 || reg == 0x402 || + (reg >= 0x409 && reg < 0x419 && (reg & 1)) || reg == 0x63a || reg == 0x63c || reg == 0x63e || reg == 0x640 || reg == 0x642 || reg == 0x64a || reg == 0x64c || @@ -1406,132 +1121,81 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) } return false; } - -/* - * On older chips, only registers 0x50-0x5f are banked. - * On more recent chips, all registers are banked. - * Assume that is the case and set the bank number for each access. - * Cache the bank number so it only needs to be set if it changes. - */ -static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) -{ - u8 bank = reg >> 8; - - if (data->bank != bank) { - outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); - outb_p(bank, data->addr + DATA_REG_OFFSET); - data->bank = bank; - } -} - -static u16 nct6775_read_value(struct nct6775_data *data, u16 reg) -{ - int res, word_sized = is_word_sized(data, reg); - - nct6775_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); - res = inb_p(data->addr + DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - data->addr + ADDR_REG_OFFSET); - res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET); - } - return res; -} - -static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) -{ - int word_sized = is_word_sized(data, reg); - - nct6775_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, data->addr + DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - data->addr + ADDR_REG_OFFSET); - } - outb_p(value & 0xff, data->addr + DATA_REG_OFFSET); - return 0; -} +EXPORT_SYMBOL_GPL(nct6775_reg_is_word_sized); /* We left-align 8-bit temperature values to make the code simpler */ -static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg) +static int nct6775_read_temp(struct nct6775_data *data, u16 reg, u16 *val) { - u16 res; + int err; - res = nct6775_read_value(data, reg); - if (!is_word_sized(data, reg)) - res <<= 8; + err = nct6775_read_value(data, reg, val); + if (err) + return err; - return res; -} + if (!nct6775_reg_is_word_sized(data, reg)) + *val <<= 8; -static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value) -{ - if (!is_word_sized(data, reg)) - value >>= 8; - return nct6775_write_value(data, reg, value); + return 0; } /* This function assumes that the caller holds data->update_lock */ -static void nct6775_write_fan_div(struct nct6775_data *data, int nr) +static int nct6775_write_fan_div(struct nct6775_data *data, int nr) { - u8 reg; + u16 reg; + int err; + u16 fandiv_reg = nr < 2 ? NCT6775_REG_FANDIV1 : NCT6775_REG_FANDIV2; + unsigned int oddshift = (nr & 1) * 4; /* masks shift by four if nr is odd */ - switch (nr) { - case 0: - reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x70) - | (data->fan_div[0] & 0x7); - nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); - break; - case 1: - reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x7) - | ((data->fan_div[1] << 4) & 0x70); - nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); - break; - case 2: - reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x70) - | (data->fan_div[2] & 0x7); - nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); - break; - case 3: - reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x7) - | ((data->fan_div[3] << 4) & 0x70); - nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); - break; - } + err = nct6775_read_value(data, fandiv_reg, ®); + if (err) + return err; + reg &= 0x70 >> oddshift; + reg |= data->fan_div[nr] & (0x7 << oddshift); + return nct6775_write_value(data, fandiv_reg, reg); } -static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr) +static int nct6775_write_fan_div_common(struct nct6775_data *data, int nr) { if (data->kind == nct6775) - nct6775_write_fan_div(data, nr); + return nct6775_write_fan_div(data, nr); + return 0; } -static void nct6775_update_fan_div(struct nct6775_data *data) +static int nct6775_update_fan_div(struct nct6775_data *data) { - u8 i; + int err; + u16 i; - i = nct6775_read_value(data, NCT6775_REG_FANDIV1); + err = nct6775_read_value(data, NCT6775_REG_FANDIV1, &i); + if (err) + return err; data->fan_div[0] = i & 0x7; data->fan_div[1] = (i & 0x70) >> 4; - i = nct6775_read_value(data, NCT6775_REG_FANDIV2); + err = nct6775_read_value(data, NCT6775_REG_FANDIV2, &i); + if (err) + return err; data->fan_div[2] = i & 0x7; if (data->has_fan & BIT(3)) data->fan_div[3] = (i & 0x70) >> 4; + + return 0; } -static void nct6775_update_fan_div_common(struct nct6775_data *data) +static int nct6775_update_fan_div_common(struct nct6775_data *data) { if (data->kind == nct6775) - nct6775_update_fan_div(data); + return nct6775_update_fan_div(data); + return 0; } -static void nct6775_init_fan_div(struct nct6775_data *data) +static int nct6775_init_fan_div(struct nct6775_data *data) { - int i; + int i, err; + + err = nct6775_update_fan_div_common(data); + if (err) + return err; - nct6775_update_fan_div_common(data); /* * For all fans, start with highest divider value if the divider * register is not initialized. This ensures that we get a @@ -1543,19 +1207,26 @@ static void nct6775_init_fan_div(struct nct6775_data *data) continue; if (data->fan_div[i] == 0) { data->fan_div[i] = 7; - nct6775_write_fan_div_common(data, i); + err = nct6775_write_fan_div_common(data, i); + if (err) + return err; } } + + return 0; } -static void nct6775_init_fan_common(struct device *dev, - struct nct6775_data *data) +static int nct6775_init_fan_common(struct device *dev, + struct nct6775_data *data) { - int i; - u8 reg; + int i, err; + u16 reg; - if (data->has_fan_div) - nct6775_init_fan_div(data); + if (data->has_fan_div) { + err = nct6775_init_fan_div(data); + if (err) + return err; + } /* * If fan_min is not set (0), set it to 0xff to disable it. This @@ -1563,23 +1234,30 @@ static void nct6775_init_fan_common(struct device *dev, */ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { if (data->has_fan_min & BIT(i)) { - reg = nct6775_read_value(data, data->REG_FAN_MIN[i]); - if (!reg) - nct6775_write_value(data, data->REG_FAN_MIN[i], - data->has_fan_div ? 0xff - : 0xff1f); + err = nct6775_read_value(data, data->REG_FAN_MIN[i], ®); + if (err) + return err; + if (!reg) { + err = nct6775_write_value(data, data->REG_FAN_MIN[i], + data->has_fan_div ? 0xff : 0xff1f); + if (err) + return err; + } } } + + return 0; } -static void nct6775_select_fan_div(struct device *dev, - struct nct6775_data *data, int nr, u16 reg) +static int nct6775_select_fan_div(struct device *dev, + struct nct6775_data *data, int nr, u16 reg) { + int err; u8 fan_div = data->fan_div[nr]; u16 fan_min; if (!data->has_fan_div) - return; + return 0; /* * If we failed to measure the fan speed, or the reported value is not @@ -1611,37 +1289,46 @@ static void nct6775_select_fan_div(struct device *dev, } if (fan_min != data->fan_min[nr]) { data->fan_min[nr] = fan_min; - nct6775_write_value(data, data->REG_FAN_MIN[nr], - fan_min); + err = nct6775_write_value(data, data->REG_FAN_MIN[nr], fan_min); + if (err) + return err; } } data->fan_div[nr] = fan_div; - nct6775_write_fan_div_common(data, nr); + err = nct6775_write_fan_div_common(data, nr); + if (err) + return err; } + + return 0; } -static void nct6775_update_pwm(struct device *dev) +static int nct6775_update_pwm(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); - int i, j; - int fanmodecfg, reg; + int i, j, err; + u16 fanmodecfg, reg; bool duty_is_dc; for (i = 0; i < data->pwm_num; i++) { if (!(data->has_pwm & BIT(i))) continue; - duty_is_dc = data->REG_PWM_MODE[i] && - (nct6775_read_value(data, data->REG_PWM_MODE[i]) - & data->PWM_MODE_MASK[i]); + err = nct6775_read_value(data, data->REG_PWM_MODE[i], ®); + if (err) + return err; + duty_is_dc = data->REG_PWM_MODE[i] && (reg & data->PWM_MODE_MASK[i]); data->pwm_mode[i] = !duty_is_dc; - fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]); + err = nct6775_read_value(data, data->REG_FAN_MODE[i], &fanmodecfg); + if (err) + return err; for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) { if (data->REG_PWM[j] && data->REG_PWM[j][i]) { - data->pwm[j][i] - = nct6775_read_value(data, - data->REG_PWM[j][i]); + err = nct6775_read_value(data, data->REG_PWM[j][i], ®); + if (err) + return err; + data->pwm[j][i] = reg; } } @@ -1656,17 +1343,22 @@ static void nct6775_update_pwm(struct device *dev) u8 t = fanmodecfg & 0x0f; if (data->REG_TOLERANCE_H) { - t |= (nct6775_read_value(data, - data->REG_TOLERANCE_H[i]) & 0x70) >> 1; + err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], ®); + if (err) + return err; + t |= (reg & 0x70) >> 1; } data->target_speed_tolerance[i] = t; } - data->temp_tolerance[1][i] = - nct6775_read_value(data, - data->REG_CRITICAL_TEMP_TOLERANCE[i]); + err = nct6775_read_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[i], ®); + if (err) + return err; + data->temp_tolerance[1][i] = reg; - reg = nct6775_read_value(data, data->REG_TEMP_SEL[i]); + err = nct6775_read_value(data, data->REG_TEMP_SEL[i], ®); + if (err) + return err; data->pwm_temp_sel[i] = reg & 0x1f; /* If fan can stop, report floor as 0 */ if (reg & 0x80) @@ -1675,7 +1367,9 @@ static void nct6775_update_pwm(struct device *dev) if (!data->REG_WEIGHT_TEMP_SEL[i]) continue; - reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]); + err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i], ®); + if (err) + return err; data->pwm_weight_temp_sel[i] = reg & 0x1f; /* If weight is disabled, report weight source as 0 */ if (!(reg & 0x80)) @@ -1683,30 +1377,37 @@ static void nct6775_update_pwm(struct device *dev) /* Weight temp data */ for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) { - data->weight_temp[j][i] - = nct6775_read_value(data, - data->REG_WEIGHT_TEMP[j][i]); + err = nct6775_read_value(data, data->REG_WEIGHT_TEMP[j][i], ®); + if (err) + return err; + data->weight_temp[j][i] = reg; } } + + return 0; } -static void nct6775_update_pwm_limits(struct device *dev) +static int nct6775_update_pwm_limits(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); - int i, j; - u8 reg; - u16 reg_t; + int i, j, err; + u16 reg, reg_t; for (i = 0; i < data->pwm_num; i++) { if (!(data->has_pwm & BIT(i))) continue; for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) { - data->fan_time[j][i] = - nct6775_read_value(data, data->REG_FAN_TIME[j][i]); + err = nct6775_read_value(data, data->REG_FAN_TIME[j][i], ®); + if (err) + return err; + data->fan_time[j][i] = reg; } - reg_t = nct6775_read_value(data, data->REG_TARGET[i]); + err = nct6775_read_value(data, data->REG_TARGET[i], ®_t); + if (err) + return err; + /* Update only in matching mode or if never updated */ if (!data->target_temp[i] || data->pwm_enable[i] == thermal_cruise) @@ -1714,29 +1415,37 @@ static void nct6775_update_pwm_limits(struct device *dev) if (!data->target_speed[i] || data->pwm_enable[i] == speed_cruise) { if (data->REG_TOLERANCE_H) { - reg_t |= (nct6775_read_value(data, - data->REG_TOLERANCE_H[i]) & 0x0f) << 8; + err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], ®); + if (err) + return err; + reg_t |= (reg & 0x0f) << 8; } data->target_speed[i] = reg_t; } for (j = 0; j < data->auto_pwm_num; j++) { - data->auto_pwm[i][j] = - nct6775_read_value(data, - NCT6775_AUTO_PWM(data, i, j)); - data->auto_temp[i][j] = - nct6775_read_value(data, - NCT6775_AUTO_TEMP(data, i, j)); + err = nct6775_read_value(data, NCT6775_AUTO_PWM(data, i, j), ®); + if (err) + return err; + data->auto_pwm[i][j] = reg; + + err = nct6775_read_value(data, NCT6775_AUTO_TEMP(data, i, j), ®); + if (err) + return err; + data->auto_temp[i][j] = reg; } /* critical auto_pwm temperature data */ - data->auto_temp[i][data->auto_pwm_num] = - nct6775_read_value(data, data->REG_CRITICAL_TEMP[i]); + err = nct6775_read_value(data, data->REG_CRITICAL_TEMP[i], ®); + if (err) + return err; + data->auto_temp[i][data->auto_pwm_num] = reg; switch (data->kind) { case nct6775: - reg = nct6775_read_value(data, - NCT6775_REG_CRITICAL_ENAB[i]); + err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[i], ®); + if (err) + return err; data->auto_pwm[i][data->auto_pwm_num] = (reg & 0x02) ? 0xff : 0x00; break; @@ -1753,116 +1462,160 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6796: case nct6797: case nct6798: - reg = nct6775_read_value(data, - data->REG_CRITICAL_PWM_ENABLE[i]); - if (reg & data->CRITICAL_PWM_ENABLE_MASK) - reg = nct6775_read_value(data, - data->REG_CRITICAL_PWM[i]); - else + err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i], ®); + if (err) + return err; + if (reg & data->CRITICAL_PWM_ENABLE_MASK) { + err = nct6775_read_value(data, data->REG_CRITICAL_PWM[i], ®); + if (err) + return err; + } else { reg = 0xff; + } data->auto_pwm[i][data->auto_pwm_num] = reg; break; } } + + return 0; } -static struct nct6775_data *nct6775_update_device(struct device *dev) +struct nct6775_data *nct6775_update_device(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); - int i, j; + int i, j, err = 0; + u16 reg; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || !data->valid) { /* Fan clock dividers */ - nct6775_update_fan_div_common(data); + err = nct6775_update_fan_div_common(data); + if (err) + goto out; /* Measured voltages and limits */ for (i = 0; i < data->in_num; i++) { if (!(data->have_in & BIT(i))) continue; - data->in[i][0] = nct6775_read_value(data, - data->REG_VIN[i]); - data->in[i][1] = nct6775_read_value(data, - data->REG_IN_MINMAX[0][i]); - data->in[i][2] = nct6775_read_value(data, - data->REG_IN_MINMAX[1][i]); + err = nct6775_read_value(data, data->REG_VIN[i], ®); + if (err) + goto out; + data->in[i][0] = reg; + + err = nct6775_read_value(data, data->REG_IN_MINMAX[0][i], ®); + if (err) + goto out; + data->in[i][1] = reg; + + err = nct6775_read_value(data, data->REG_IN_MINMAX[1][i], ®); + if (err) + goto out; + data->in[i][2] = reg; } /* Measured fan speeds and limits */ for (i = 0; i < ARRAY_SIZE(data->rpm); i++) { - u16 reg; - if (!(data->has_fan & BIT(i))) continue; - reg = nct6775_read_value(data, data->REG_FAN[i]); + err = nct6775_read_value(data, data->REG_FAN[i], ®); + if (err) + goto out; data->rpm[i] = data->fan_from_reg(reg, data->fan_div[i]); - if (data->has_fan_min & BIT(i)) - data->fan_min[i] = nct6775_read_value(data, - data->REG_FAN_MIN[i]); + if (data->has_fan_min & BIT(i)) { + err = nct6775_read_value(data, data->REG_FAN_MIN[i], ®); + if (err) + goto out; + data->fan_min[i] = reg; + } if (data->REG_FAN_PULSES[i]) { - data->fan_pulses[i] = - (nct6775_read_value(data, - data->REG_FAN_PULSES[i]) - >> data->FAN_PULSE_SHIFT[i]) & 0x03; + err = nct6775_read_value(data, data->REG_FAN_PULSES[i], ®); + if (err) + goto out; + data->fan_pulses[i] = (reg >> data->FAN_PULSE_SHIFT[i]) & 0x03; } - nct6775_select_fan_div(dev, data, i, reg); + err = nct6775_select_fan_div(dev, data, i, reg); + if (err) + goto out; } - nct6775_update_pwm(dev); - nct6775_update_pwm_limits(dev); + err = nct6775_update_pwm(dev); + if (err) + goto out; + + err = nct6775_update_pwm_limits(dev); + if (err) + goto out; /* Measured temperatures and limits */ for (i = 0; i < NUM_TEMP; i++) { if (!(data->have_temp & BIT(i))) continue; for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) { - if (data->reg_temp[j][i]) - data->temp[j][i] - = nct6775_read_temp(data, - data->reg_temp[j][i]); + if (data->reg_temp[j][i]) { + err = nct6775_read_temp(data, data->reg_temp[j][i], ®); + if (err) + goto out; + data->temp[j][i] = reg; + } } if (i >= NUM_TEMP_FIXED || !(data->have_temp_fixed & BIT(i))) continue; - data->temp_offset[i] - = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]); + err = nct6775_read_value(data, data->REG_TEMP_OFFSET[i], ®); + if (err) + goto out; + data->temp_offset[i] = reg; + } + + for (i = 0; i < NUM_TSI_TEMP; i++) { + if (!(data->have_tsi_temp & BIT(i))) + continue; + err = nct6775_read_value(data, data->REG_TSI_TEMP[i], ®); + if (err) + goto out; + data->tsi_temp[i] = reg; } data->alarms = 0; for (i = 0; i < NUM_REG_ALARM; i++) { - u8 alarm; + u16 alarm; if (!data->REG_ALARM[i]) continue; - alarm = nct6775_read_value(data, data->REG_ALARM[i]); + err = nct6775_read_value(data, data->REG_ALARM[i], &alarm); + if (err) + goto out; data->alarms |= ((u64)alarm) << (i << 3); } data->beeps = 0; for (i = 0; i < NUM_REG_BEEP; i++) { - u8 beep; + u16 beep; if (!data->REG_BEEP[i]) continue; - beep = nct6775_read_value(data, data->REG_BEEP[i]); + err = nct6775_read_value(data, data->REG_BEEP[i], &beep); + if (err) + goto out; data->beeps |= ((u64)beep) << (i << 3); } data->last_updated = jiffies; data->valid = true; } - +out: mutex_unlock(&data->update_lock); - return data; + return err ? ERR_PTR(err) : data; } +EXPORT_SYMBOL_GPL(nct6775_update_device); /* * Sysfs callback functions @@ -1875,6 +1628,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) int index = sattr->index; int nr = sattr->nr; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); } @@ -1894,34 +1650,39 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, return err; mutex_lock(&data->update_lock); data->in[nr][index] = in_to_reg(val, nr); - nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], - data->in[nr][index]); + err = nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], data->in[nr][index]); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } -static ssize_t -show_alarm(struct device *dev, struct device_attribute *attr, char *buf) +ssize_t +nct6775_show_alarm(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); - int nr = data->ALARM_BITS[sattr->index]; + int nr; + if (IS_ERR(data)) + return PTR_ERR(data); + + nr = data->ALARM_BITS[sattr->index]; return sprintf(buf, "%u\n", (unsigned int)((data->alarms >> nr) & 0x01)); } +EXPORT_SYMBOL_GPL(nct6775_show_alarm); static int find_temp_source(struct nct6775_data *data, int index, int count) { int source = data->temp_src[index]; - int nr; + int nr, err; for (nr = 0; nr < count; nr++) { - int src; + u16 src; - src = nct6775_read_value(data, - data->REG_TEMP_SOURCE[nr]) & 0x1f; - if (src == source) + err = nct6775_read_value(data, data->REG_TEMP_SOURCE[nr], &src); + if (err) + return err; + if ((src & 0x1f) == source) return nr; } return -ENODEV; @@ -1935,6 +1696,9 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) unsigned int alarm = 0; int nr; + if (IS_ERR(data)) + return PTR_ERR(data); + /* * For temperatures, there is no fixed mapping from registers to alarm * bits. Alarm bits are determined by the temperature source mapping. @@ -1948,20 +1712,25 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%u\n", alarm); } -static ssize_t -show_beep(struct device *dev, struct device_attribute *attr, char *buf) +ssize_t +nct6775_show_beep(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); struct nct6775_data *data = nct6775_update_device(dev); - int nr = data->BEEP_BITS[sattr->index]; + int nr; + + if (IS_ERR(data)) + return PTR_ERR(data); + + nr = data->BEEP_BITS[sattr->index]; return sprintf(buf, "%u\n", (unsigned int)((data->beeps >> nr) & 0x01)); } +EXPORT_SYMBOL_GPL(nct6775_show_beep); -static ssize_t -store_beep(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) +ssize_t +nct6775_store_beep(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); struct nct6775_data *data = dev_get_drvdata(dev); @@ -1981,11 +1750,12 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf, data->beeps |= (1ULL << nr); else data->beeps &= ~(1ULL << nr); - nct6775_write_value(data, data->REG_BEEP[regindex], - (data->beeps >> (regindex << 3)) & 0xff); + err = nct6775_write_value(data, data->REG_BEEP[regindex], + (data->beeps >> (regindex << 3)) & 0xff); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } +EXPORT_SYMBOL_GPL(nct6775_store_beep); static ssize_t show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) @@ -1995,6 +1765,9 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) unsigned int beep = 0; int nr; + if (IS_ERR(data)) + return PTR_ERR(data); + /* * For temperatures, there is no fixed mapping from registers to beep * enable bits. Beep enable bits are determined by the temperature @@ -2037,34 +1810,31 @@ store_temp_beep(struct device *dev, struct device_attribute *attr, data->beeps |= (1ULL << bit); else data->beeps &= ~(1ULL << bit); - nct6775_write_value(data, data->REG_BEEP[regindex], - (data->beeps >> (regindex << 3)) & 0xff); + err = nct6775_write_value(data, data->REG_BEEP[regindex], + (data->beeps >> (regindex << 3)) & 0xff); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static umode_t nct6775_in_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6775_data *data = dev_get_drvdata(dev); int in = index / 5; /* voltage index */ if (!(data->have_in & BIT(in))) return 0; - return attr->mode; + return nct6775_attr_mode(data, attr); } -SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0); -SENSOR_TEMPLATE(in_alarm, "in%d_alarm", S_IRUGO, show_alarm, NULL, 0); -SENSOR_TEMPLATE(in_beep, "in%d_beep", S_IWUSR | S_IRUGO, show_beep, store_beep, - 0); -SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 0, 1); -SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IWUSR | S_IRUGO, show_in_reg, - store_in_reg, 0, 2); +SENSOR_TEMPLATE_2(in_input, "in%d_input", 0444, show_in_reg, NULL, 0, 0); +SENSOR_TEMPLATE(in_alarm, "in%d_alarm", 0444, nct6775_show_alarm, NULL, 0); +SENSOR_TEMPLATE(in_beep, "in%d_beep", 0644, nct6775_show_beep, nct6775_store_beep, 0); +SENSOR_TEMPLATE_2(in_min, "in%d_min", 0644, show_in_reg, store_in_reg, 0, 1); +SENSOR_TEMPLATE_2(in_max, "in%d_max", 0644, show_in_reg, store_in_reg, 0, 2); /* * nct6775_in_is_visible uses the index into the following array @@ -2092,6 +1862,9 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->rpm[nr]); } @@ -2102,6 +1875,9 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->fan_from_reg_min(data->fan_min[nr], data->fan_div[nr])); @@ -2114,6 +1890,9 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); } @@ -2199,16 +1978,18 @@ write_div: nr + 1, div_from_reg(data->fan_div[nr]), div_from_reg(new_div)); data->fan_div[nr] = new_div; - nct6775_write_fan_div_common(data, nr); + err = nct6775_write_fan_div_common(data, nr); + if (err) + goto write_min; /* Give the chip time to sample a new speed value */ data->last_updated = jiffies; } write_min: - nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); + err = nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2216,8 +1997,12 @@ show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); - int p = data->fan_pulses[sattr->index]; + int p; + + if (IS_ERR(data)) + return PTR_ERR(data); + p = data->fan_pulses[sattr->index]; return sprintf(buf, "%d\n", p ? : 4); } @@ -2230,7 +2015,7 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr, int nr = sattr->index; unsigned long val; int err; - u8 reg; + u16 reg; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -2241,19 +2026,22 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->fan_pulses[nr] = val & 3; - reg = nct6775_read_value(data, data->REG_FAN_PULSES[nr]); + err = nct6775_read_value(data, data->REG_FAN_PULSES[nr], ®); + if (err) + goto out; reg &= ~(0x03 << data->FAN_PULSE_SHIFT[nr]); reg |= (val & 3) << data->FAN_PULSE_SHIFT[nr]; - nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg); + err = nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg); +out: mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static umode_t nct6775_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6775_data *data = dev_get_drvdata(dev); int fan = index / 6; /* fan index */ int nr = index % 6; /* attribute index */ @@ -2272,19 +2060,16 @@ static umode_t nct6775_fan_is_visible(struct kobject *kobj, if (nr == 5 && data->kind != nct6775) return 0; - return attr->mode; + return nct6775_attr_mode(data, attr); } -SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0); -SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", S_IRUGO, show_alarm, NULL, - FAN_ALARM_BASE); -SENSOR_TEMPLATE(fan_beep, "fan%d_beep", S_IWUSR | S_IRUGO, show_beep, - store_beep, FAN_ALARM_BASE); -SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IWUSR | S_IRUGO, show_fan_pulses, - store_fan_pulses, 0); -SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 0); -SENSOR_TEMPLATE(fan_div, "fan%d_div", S_IRUGO, show_fan_div, NULL, 0); +SENSOR_TEMPLATE(fan_input, "fan%d_input", 0444, show_fan, NULL, 0); +SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", 0444, nct6775_show_alarm, NULL, FAN_ALARM_BASE); +SENSOR_TEMPLATE(fan_beep, "fan%d_beep", 0644, nct6775_show_beep, + nct6775_store_beep, FAN_ALARM_BASE); +SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", 0644, show_fan_pulses, store_fan_pulses, 0); +SENSOR_TEMPLATE(fan_min, "fan%d_min", 0644, show_fan_min, store_fan_min, 0); +SENSOR_TEMPLATE(fan_div, "fan%d_div", 0444, show_fan_div, NULL, 0); /* * nct6775_fan_is_visible uses the index into the following array @@ -2314,6 +2099,9 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); } @@ -2325,6 +2113,9 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf) int nr = sattr->nr; int index = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr])); } @@ -2345,10 +2136,9 @@ store_temp(struct device *dev, struct device_attribute *attr, const char *buf, mutex_lock(&data->update_lock); data->temp[index][nr] = LM75_TEMP_TO_REG(val); - nct6775_write_temp(data, data->reg_temp[index][nr], - data->temp[index][nr]); + err = nct6775_write_temp(data, data->reg_temp[index][nr], data->temp[index][nr]); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2357,6 +2147,9 @@ show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000); } @@ -2378,10 +2171,10 @@ store_temp_offset(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->temp_offset[nr] = val; - nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val); + err = nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2391,6 +2184,9 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", (int)data->temp_type[nr]); } @@ -2403,7 +2199,11 @@ store_temp_type(struct device *dev, struct device_attribute *attr, int nr = sattr->index; unsigned long val; int err; - u8 vbat, diode, vbit, dbit; + u8 vbit, dbit; + u16 vbat, diode; + + if (IS_ERR(data)) + return PTR_ERR(data); err = kstrtoul(buf, 10, &val); if (err < 0) @@ -2417,8 +2217,17 @@ store_temp_type(struct device *dev, struct device_attribute *attr, data->temp_type[nr] = val; vbit = 0x02 << nr; dbit = data->DIODE_MASK << nr; - vbat = nct6775_read_value(data, data->REG_VBAT) & ~vbit; - diode = nct6775_read_value(data, data->REG_DIODE) & ~dbit; + + err = nct6775_read_value(data, data->REG_VBAT, &vbat); + if (err) + goto out; + vbat &= ~vbit; + + err = nct6775_read_value(data, data->REG_DIODE, &diode); + if (err) + goto out; + diode &= ~dbit; + switch (val) { case 1: /* CPU diode (diode, current mode) */ vbat |= vbit; @@ -2430,17 +2239,19 @@ store_temp_type(struct device *dev, struct device_attribute *attr, case 4: /* thermistor */ break; } - nct6775_write_value(data, data->REG_VBAT, vbat); - nct6775_write_value(data, data->REG_DIODE, diode); - + err = nct6775_write_value(data, data->REG_VBAT, vbat); + if (err) + goto out; + err = nct6775_write_value(data, data->REG_DIODE, diode); +out: mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static umode_t nct6775_temp_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6775_data *data = dev_get_drvdata(dev); int temp = index / 10; /* temp index */ int nr = index % 10; /* attribute index */ @@ -2473,26 +2284,19 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj, if (nr > 7 && !(data->have_temp_fixed & BIT(temp))) return 0; - return attr->mode; + return nct6775_attr_mode(data, attr); } -SENSOR_TEMPLATE_2(temp_input, "temp%d_input", S_IRUGO, show_temp, NULL, 0, 0); -SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0); -SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO | S_IWUSR, show_temp, - store_temp, 0, 1); -SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", S_IRUGO | S_IWUSR, - show_temp, store_temp, 0, 2); -SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO | S_IWUSR, show_temp, - store_temp, 0, 3); -SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", S_IRUGO | S_IWUSR, show_temp, - store_temp, 0, 4); -SENSOR_TEMPLATE(temp_offset, "temp%d_offset", S_IRUGO | S_IWUSR, - show_temp_offset, store_temp_offset, 0); -SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO | S_IWUSR, show_temp_type, - store_temp_type, 0); -SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", S_IRUGO, show_temp_alarm, NULL, 0); -SENSOR_TEMPLATE(temp_beep, "temp%d_beep", S_IRUGO | S_IWUSR, show_temp_beep, - store_temp_beep, 0); +SENSOR_TEMPLATE_2(temp_input, "temp%d_input", 0444, show_temp, NULL, 0, 0); +SENSOR_TEMPLATE(temp_label, "temp%d_label", 0444, show_temp_label, NULL, 0); +SENSOR_TEMPLATE_2(temp_max, "temp%d_max", 0644, show_temp, store_temp, 0, 1); +SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", 0644, show_temp, store_temp, 0, 2); +SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", 0644, show_temp, store_temp, 0, 3); +SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", 0644, show_temp, store_temp, 0, 4); +SENSOR_TEMPLATE(temp_offset, "temp%d_offset", 0644, show_temp_offset, store_temp_offset, 0); +SENSOR_TEMPLATE(temp_type, "temp%d_type", 0644, show_temp_type, store_temp_type, 0); +SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", 0444, show_temp_alarm, NULL, 0); +SENSOR_TEMPLATE(temp_beep, "temp%d_beep", 0644, show_temp_beep, store_temp_beep, 0); /* * nct6775_temp_is_visible uses the index into the following array @@ -2519,12 +2323,56 @@ static const struct sensor_template_group nct6775_temp_template_group = { .base = 1, }; +static ssize_t show_tsi_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sysfs_emit(buf, "%u\n", tsi_temp_from_reg(data->tsi_temp[sattr->index])); +} + +static ssize_t show_tsi_temp_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sysfs_emit(buf, "TSI%d_TEMP\n", sattr->index); +} + +SENSOR_TEMPLATE(tsi_temp_input, "temp%d_input", 0444, show_tsi_temp, NULL, 0); +SENSOR_TEMPLATE(tsi_temp_label, "temp%d_label", 0444, show_tsi_temp_label, NULL, 0); + +static umode_t nct6775_tsi_temp_is_visible(struct kobject *kobj, struct attribute *attr, + int index) +{ + struct device *dev = kobj_to_dev(kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int temp = index / 2; + + return (data->have_tsi_temp & BIT(temp)) ? nct6775_attr_mode(data, attr) : 0; +} + +/* + * The index calculation in nct6775_tsi_temp_is_visible() must be kept in + * sync with the size of this array. + */ +static struct sensor_device_template *nct6775_tsi_temp_template[] = { + &sensor_dev_template_tsi_temp_input, + &sensor_dev_template_tsi_temp_label, + NULL +}; + static ssize_t show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->pwm_mode[sattr->index]); } @@ -2537,7 +2385,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, int nr = sattr->index; unsigned long val; int err; - u8 reg; + u16 reg; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -2555,13 +2403,16 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->pwm_mode[nr] = val; - reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]); + err = nct6775_read_value(data, data->REG_PWM_MODE[nr], ®); + if (err) + goto out; reg &= ~data->PWM_MODE_MASK[nr]; if (!val) reg |= data->PWM_MODE_MASK[nr]; - nct6775_write_value(data, data->REG_PWM_MODE[nr], reg); + err = nct6775_write_value(data, data->REG_PWM_MODE[nr], reg); +out: mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2571,16 +2422,23 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); int nr = sattr->nr; int index = sattr->index; - int pwm; + int err; + u16 pwm; + + if (IS_ERR(data)) + return PTR_ERR(data); /* * For automatic fan control modes, show current pwm readings. * Otherwise, show the configured value. */ - if (index == 0 && data->pwm_enable[nr] > manual) - pwm = nct6775_read_value(data, data->REG_PWM_READ[nr]); - else + if (index == 0 && data->pwm_enable[nr] > manual) { + err = nct6775_read_value(data, data->REG_PWM_READ[nr], &pwm); + if (err) + return err; + } else { pwm = data->pwm[index][nr]; + } return sprintf(buf, "%d\n", pwm); } @@ -2598,7 +2456,7 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, int maxval[7] = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 }; int err; - u8 reg; + u16 reg; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -2607,16 +2465,21 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, mutex_lock(&data->update_lock); data->pwm[index][nr] = val; - nct6775_write_value(data, data->REG_PWM[index][nr], val); + err = nct6775_write_value(data, data->REG_PWM[index][nr], val); + if (err) + goto out; if (index == 2) { /* floor: disable if val == 0 */ - reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); + err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], ®); + if (err) + goto out; reg &= 0x7f; if (val) reg |= 0x80; - nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); + err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); } +out: mutex_unlock(&data->update_lock); - return count; + return err ? : count; } /* Returns 0 if OK, -EINVAL otherwise */ @@ -2643,40 +2506,54 @@ static int check_trip_points(struct nct6775_data *data, int nr) return 0; } -static void pwm_update_registers(struct nct6775_data *data, int nr) +static int pwm_update_registers(struct nct6775_data *data, int nr) { - u8 reg; + u16 reg; + int err; switch (data->pwm_enable[nr]) { case off: case manual: break; case speed_cruise: - reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + err = nct6775_read_value(data, data->REG_FAN_MODE[nr], ®); + if (err) + return err; reg = (reg & ~data->tolerance_mask) | (data->target_speed_tolerance[nr] & data->tolerance_mask); - nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); - nct6775_write_value(data, data->REG_TARGET[nr], - data->target_speed[nr] & 0xff); + err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + if (err) + return err; + err = nct6775_write_value(data, data->REG_TARGET[nr], + data->target_speed[nr] & 0xff); + if (err) + return err; if (data->REG_TOLERANCE_H) { reg = (data->target_speed[nr] >> 8) & 0x0f; reg |= (data->target_speed_tolerance[nr] & 0x38) << 1; - nct6775_write_value(data, - data->REG_TOLERANCE_H[nr], - reg); + err = nct6775_write_value(data, data->REG_TOLERANCE_H[nr], reg); + if (err) + return err; } break; case thermal_cruise: - nct6775_write_value(data, data->REG_TARGET[nr], - data->target_temp[nr]); - /* fall through */ + err = nct6775_write_value(data, data->REG_TARGET[nr], data->target_temp[nr]); + if (err) + return err; + fallthrough; default: - reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + err = nct6775_read_value(data, data->REG_FAN_MODE[nr], ®); + if (err) + return err; reg = (reg & ~data->tolerance_mask) | data->temp_tolerance[0][nr]; - nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + if (err) + return err; break; } + + return 0; } static ssize_t @@ -2685,6 +2562,9 @@ show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]); } @@ -2722,15 +2602,22 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, * turn off pwm control: select manual mode, set pwm to maximum */ data->pwm[0][nr] = 255; - nct6775_write_value(data, data->REG_PWM[0][nr], 255); + err = nct6775_write_value(data, data->REG_PWM[0][nr], 255); + if (err) + goto out; } - pwm_update_registers(data, nr); - reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + err = pwm_update_registers(data, nr); + if (err) + goto out; + err = nct6775_read_value(data, data->REG_FAN_MODE[nr], ®); + if (err) + goto out; reg &= 0x0f; reg |= pwm_enable_to_reg(val) << 4; - nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); +out: mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2757,6 +2644,9 @@ show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int index = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]); } @@ -2768,7 +2658,11 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; unsigned long val; - int err, reg, src; + int err, src; + u16 reg; + + if (IS_ERR(data)) + return PTR_ERR(data); err = kstrtoul(buf, 10, &val); if (err < 0) @@ -2781,13 +2675,16 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); src = data->temp_src[val - 1]; data->pwm_temp_sel[nr] = src; - reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); + err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], ®); + if (err) + goto out; reg &= 0xe0; reg |= src; - nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); + err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); +out: mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2798,6 +2695,9 @@ show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int index = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return show_pwm_temp_sel_common(data, buf, data->pwm_weight_temp_sel[index]); } @@ -2810,7 +2710,11 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; unsigned long val; - int err, reg, src; + int err, src; + u16 reg; + + if (IS_ERR(data)) + return PTR_ERR(data); err = kstrtoul(buf, 10, &val); if (err < 0) @@ -2826,19 +2730,24 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, if (val) { src = data->temp_src[val - 1]; data->pwm_weight_temp_sel[nr] = src; - reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); + err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], ®); + if (err) + goto out; reg &= 0xe0; reg |= (src | 0x80); - nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); + err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); } else { data->pwm_weight_temp_sel[nr] = 0; - reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); + err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], ®); + if (err) + goto out; reg &= 0x7f; - nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); + err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); } +out: mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2847,6 +2756,9 @@ show_target_temp(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000); } @@ -2869,9 +2781,9 @@ store_target_temp(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->target_temp[nr] = val; - pwm_update_registers(data, nr); + err = pwm_update_registers(data, nr); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2881,6 +2793,9 @@ show_target_speed(struct device *dev, struct device_attribute *attr, char *buf) struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", fan_from_reg16(data->target_speed[nr], data->fan_div[nr])); @@ -2906,9 +2821,9 @@ store_target_speed(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->target_speed[nr] = speed; - pwm_update_registers(data, nr); + err = pwm_update_registers(data, nr); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -2920,6 +2835,9 @@ show_temp_tolerance(struct device *dev, struct device_attribute *attr, int nr = sattr->nr; int index = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000); } @@ -2944,13 +2862,11 @@ store_temp_tolerance(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->temp_tolerance[index][nr] = val; if (index) - pwm_update_registers(data, nr); + err = pwm_update_registers(data, nr); else - nct6775_write_value(data, - data->REG_CRITICAL_TEMP_TOLERANCE[nr], - val); + err = nct6775_write_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[nr], val); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } /* @@ -2967,8 +2883,12 @@ show_speed_tolerance(struct device *dev, struct device_attribute *attr, struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; - int target = data->target_speed[nr]; - int tolerance = 0; + int target, tolerance = 0; + + if (IS_ERR(data)) + return PTR_ERR(data); + + target = data->target_speed[nr]; if (target) { int low = target - data->target_speed_tolerance[nr]; @@ -3003,10 +2923,8 @@ store_speed_tolerance(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - high = fan_from_reg16(data->target_speed[nr], - data->fan_div[nr]) + val; - low = fan_from_reg16(data->target_speed[nr], - data->fan_div[nr]) - val; + high = fan_from_reg16(data->target_speed[nr], data->fan_div[nr]) + val; + low = fan_from_reg16(data->target_speed[nr], data->fan_div[nr]) - val; if (low <= 0) low = 1; if (high < low) @@ -3020,24 +2938,19 @@ store_speed_tolerance(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->target_speed_tolerance[nr] = val; - pwm_update_registers(data, nr); + err = pwm_update_registers(data, nr); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } -SENSOR_TEMPLATE_2(pwm, "pwm%d", S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0); -SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 0); -SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 0); -SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", S_IWUSR | S_IRUGO, - show_pwm_temp_sel, store_pwm_temp_sel, 0); -SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", S_IWUSR | S_IRUGO, - show_target_temp, store_target_temp, 0); -SENSOR_TEMPLATE(fan_target, "fan%d_target", S_IWUSR | S_IRUGO, - show_target_speed, store_target_speed, 0); -SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", S_IWUSR | S_IRUGO, - show_speed_tolerance, store_speed_tolerance, 0); +SENSOR_TEMPLATE_2(pwm, "pwm%d", 0644, show_pwm, store_pwm, 0, 0); +SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", 0644, show_pwm_mode, store_pwm_mode, 0); +SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", 0644, show_pwm_enable, store_pwm_enable, 0); +SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", 0644, show_pwm_temp_sel, store_pwm_temp_sel, 0); +SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", 0644, show_target_temp, store_target_temp, 0); +SENSOR_TEMPLATE(fan_target, "fan%d_target", 0644, show_target_speed, store_target_speed, 0); +SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", 0644, show_speed_tolerance, + store_speed_tolerance, 0); /* Smart Fan registers */ @@ -3049,6 +2962,9 @@ show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf) int nr = sattr->nr; int index = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000); } @@ -3071,23 +2987,21 @@ store_weight_temp(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->weight_temp[index][nr] = val; - nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val); + err = nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } -SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", S_IWUSR | S_IRUGO, - show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0); +SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", 0644, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0); SENSOR_TEMPLATE_2(pwm_weight_temp_step, "pwm%d_weight_temp_step", - S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 0); + 0644, show_weight_temp, store_weight_temp, 0, 0); SENSOR_TEMPLATE_2(pwm_weight_temp_step_tol, "pwm%d_weight_temp_step_tol", - S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 1); + 0644, show_weight_temp, store_weight_temp, 0, 1); SENSOR_TEMPLATE_2(pwm_weight_temp_step_base, "pwm%d_weight_temp_step_base", - S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 2); -SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step", - S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 5); -SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base", - S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 6); + 0644, show_weight_temp, store_weight_temp, 0, 2); +SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step", 0644, show_pwm, store_pwm, 0, 5); +SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base", 0644, show_pwm, store_pwm, 0, 6); static ssize_t show_fan_time(struct device *dev, struct device_attribute *attr, char *buf) @@ -3097,6 +3011,9 @@ show_fan_time(struct device *dev, struct device_attribute *attr, char *buf) int nr = sattr->nr; int index = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", step_time_from_reg(data->fan_time[index][nr], data->pwm_mode[nr])); @@ -3120,9 +3037,9 @@ store_fan_time(struct device *dev, struct device_attribute *attr, val = step_time_to_reg(val, data->pwm_mode[nr]); mutex_lock(&data->update_lock); data->fan_time[index][nr] = val; - nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val); + err = nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val); mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -3131,6 +3048,9 @@ show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]); } @@ -3144,7 +3064,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, int point = sattr->index; unsigned long val; int err; - u8 reg; + u16 reg; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -3162,21 +3082,20 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->auto_pwm[nr][point] = val; if (point < data->auto_pwm_num) { - nct6775_write_value(data, - NCT6775_AUTO_PWM(data, nr, point), - data->auto_pwm[nr][point]); + err = nct6775_write_value(data, NCT6775_AUTO_PWM(data, nr, point), + data->auto_pwm[nr][point]); } else { switch (data->kind) { case nct6775: /* disable if needed (pwm == 0) */ - reg = nct6775_read_value(data, - NCT6775_REG_CRITICAL_ENAB[nr]); + err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[nr], ®); + if (err) + break; if (val) reg |= 0x02; else reg &= ~0x02; - nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], - reg); + err = nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], reg); break; case nct6776: break; /* always enabled, nothing to do */ @@ -3190,22 +3109,22 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6796: case nct6797: case nct6798: - nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], - val); - reg = nct6775_read_value(data, - data->REG_CRITICAL_PWM_ENABLE[nr]); + err = nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); + if (err) + break; + err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[nr], ®); + if (err) + break; if (val == 255) reg &= ~data->CRITICAL_PWM_ENABLE_MASK; else reg |= data->CRITICAL_PWM_ENABLE_MASK; - nct6775_write_value(data, - data->REG_CRITICAL_PWM_ENABLE[nr], - reg); + err = nct6775_write_value(data, data->REG_CRITICAL_PWM_ENABLE[nr], reg); break; } } mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static ssize_t @@ -3216,6 +3135,9 @@ show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf) int nr = sattr->nr; int point = sattr->index; + if (IS_ERR(data)) + return PTR_ERR(data); + /* * We don't know for sure if the temperature is signed or unsigned. * Assume it is unsigned. @@ -3243,21 +3165,20 @@ store_auto_temp(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000); if (point < data->auto_pwm_num) { - nct6775_write_value(data, - NCT6775_AUTO_TEMP(data, nr, point), - data->auto_temp[nr][point]); + err = nct6775_write_value(data, NCT6775_AUTO_TEMP(data, nr, point), + data->auto_temp[nr][point]); } else { - nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr], - data->auto_temp[nr][point]); + err = nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr], + data->auto_temp[nr][point]); } mutex_unlock(&data->update_lock); - return count; + return err ? : count; } static umode_t nct6775_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct6775_data *data = dev_get_drvdata(dev); int pwm = index / 36; /* pwm index */ int nr = index % 36; /* attribute index */ @@ -3281,65 +3202,59 @@ static umode_t nct6775_pwm_is_visible(struct kobject *kobj, if (api > data->auto_pwm_num) return 0; } - return attr->mode; + return nct6775_attr_mode(data, attr); } -SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", S_IWUSR | S_IRUGO, - show_fan_time, store_fan_time, 0, 0); -SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", S_IWUSR | S_IRUGO, +SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", 0644, show_fan_time, store_fan_time, 0, 0); +SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", 0644, show_fan_time, store_fan_time, 0, 1); -SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", S_IWUSR | S_IRUGO, +SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", 0644, show_fan_time, store_fan_time, 0, 2); -SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 0, 1); -SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 0, 2); -SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", S_IWUSR | S_IRUGO, +SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", 0644, show_pwm, store_pwm, 0, 1); +SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", 0644, show_pwm, store_pwm, 0, 2); +SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", 0644, show_temp_tolerance, store_temp_tolerance, 0, 0); SENSOR_TEMPLATE_2(pwm_crit_temp_tolerance, "pwm%d_crit_temp_tolerance", - S_IWUSR | S_IRUGO, show_temp_tolerance, store_temp_tolerance, - 0, 1); + 0644, show_temp_tolerance, store_temp_tolerance, 0, 1); -SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", S_IWUSR | S_IRUGO, show_pwm, store_pwm, - 0, 3); +SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", 0644, show_pwm, store_pwm, 0, 3); -SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", S_IWUSR | S_IRUGO, show_pwm, - store_pwm, 0, 4); +SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", 0644, show_pwm, store_pwm, 0, 4); SENSOR_TEMPLATE_2(pwm_auto_point1_pwm, "pwm%d_auto_point1_pwm", - S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 0); + 0644, show_auto_pwm, store_auto_pwm, 0, 0); SENSOR_TEMPLATE_2(pwm_auto_point1_temp, "pwm%d_auto_point1_temp", - S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 0); + 0644, show_auto_temp, store_auto_temp, 0, 0); SENSOR_TEMPLATE_2(pwm_auto_point2_pwm, "pwm%d_auto_point2_pwm", - S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 1); + 0644, show_auto_pwm, store_auto_pwm, 0, 1); SENSOR_TEMPLATE_2(pwm_auto_point2_temp, "pwm%d_auto_point2_temp", - S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 1); + 0644, show_auto_temp, store_auto_temp, 0, 1); SENSOR_TEMPLATE_2(pwm_auto_point3_pwm, "pwm%d_auto_point3_pwm", - S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 2); + 0644, show_auto_pwm, store_auto_pwm, 0, 2); SENSOR_TEMPLATE_2(pwm_auto_point3_temp, "pwm%d_auto_point3_temp", - S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 2); + 0644, show_auto_temp, store_auto_temp, 0, 2); SENSOR_TEMPLATE_2(pwm_auto_point4_pwm, "pwm%d_auto_point4_pwm", - S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 3); + 0644, show_auto_pwm, store_auto_pwm, 0, 3); SENSOR_TEMPLATE_2(pwm_auto_point4_temp, "pwm%d_auto_point4_temp", - S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 3); + 0644, show_auto_temp, store_auto_temp, 0, 3); SENSOR_TEMPLATE_2(pwm_auto_point5_pwm, "pwm%d_auto_point5_pwm", - S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 4); + 0644, show_auto_pwm, store_auto_pwm, 0, 4); SENSOR_TEMPLATE_2(pwm_auto_point5_temp, "pwm%d_auto_point5_temp", - S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 4); + 0644, show_auto_temp, store_auto_temp, 0, 4); SENSOR_TEMPLATE_2(pwm_auto_point6_pwm, "pwm%d_auto_point6_pwm", - S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 5); + 0644, show_auto_pwm, store_auto_pwm, 0, 5); SENSOR_TEMPLATE_2(pwm_auto_point6_temp, "pwm%d_auto_point6_temp", - S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 5); + 0644, show_auto_temp, store_auto_temp, 0, 5); SENSOR_TEMPLATE_2(pwm_auto_point7_pwm, "pwm%d_auto_point7_pwm", - S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 6); + 0644, show_auto_pwm, store_auto_pwm, 0, 6); SENSOR_TEMPLATE_2(pwm_auto_point7_temp, "pwm%d_auto_point7_temp", - S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 6); + 0644, show_auto_temp, store_auto_temp, 0, 6); /* * nct6775_pwm_is_visible uses the index into the following array @@ -3393,122 +3308,21 @@ static const struct sensor_template_group nct6775_pwm_template_group = { .base = 1, }; -static ssize_t -cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct nct6775_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); -} - -static DEVICE_ATTR_RO(cpu0_vid); - -/* Case open detection */ - -static ssize_t -clear_caseopen(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct nct6775_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE; - unsigned long val; - u8 reg; - int ret; - - if (kstrtoul(buf, 10, &val) || val != 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - - /* - * Use CR registers to clear caseopen status. - * The CR registers are the same for all chips, and not all chips - * support clearing the caseopen status through "regular" registers. - */ - ret = superio_enter(data->sioreg); - if (ret) { - count = ret; - goto error; - } - - superio_select(data->sioreg, NCT6775_LD_ACPI); - reg = superio_inb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]); - reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr]; - superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); - reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr]; - superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); - superio_exit(data->sioreg); - - data->valid = false; /* Force cache refresh */ -error: - mutex_unlock(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm, - clear_caseopen, INTRUSION_ALARM_BASE); -static SENSOR_DEVICE_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm, - clear_caseopen, INTRUSION_ALARM_BASE + 1); -static SENSOR_DEVICE_ATTR(intrusion0_beep, S_IWUSR | S_IRUGO, show_beep, - store_beep, INTRUSION_ALARM_BASE); -static SENSOR_DEVICE_ATTR(intrusion1_beep, S_IWUSR | S_IRUGO, show_beep, - store_beep, INTRUSION_ALARM_BASE + 1); -static SENSOR_DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_beep, - store_beep, BEEP_ENABLE_BASE); - -static umode_t nct6775_other_is_visible(struct kobject *kobj, - struct attribute *attr, int index) +static inline int nct6775_init_device(struct nct6775_data *data) { - struct device *dev = container_of(kobj, struct device, kobj); - struct nct6775_data *data = dev_get_drvdata(dev); - - if (index == 0 && !data->have_vid) - return 0; - - if (index == 1 || index == 2) { - if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0) - return 0; - } - - if (index == 3 || index == 4) { - if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0) - return 0; - } - - return attr->mode; -} - -/* - * nct6775_other_is_visible uses the index into the following array - * to determine if attributes should be created or not. - * Any change in order or content must be matched. - */ -static struct attribute *nct6775_attributes_other[] = { - &dev_attr_cpu0_vid.attr, /* 0 */ - &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */ - &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */ - &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */ - &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */ - &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */ - - NULL -}; - -static const struct attribute_group nct6775_group_other = { - .attrs = nct6775_attributes_other, - .is_visible = nct6775_other_is_visible, -}; - -static inline void nct6775_init_device(struct nct6775_data *data) -{ - int i; - u8 tmp, diode; + int i, err; + u16 tmp, diode; /* Start monitoring if needed */ if (data->REG_CONFIG) { - tmp = nct6775_read_value(data, data->REG_CONFIG); - if (!(tmp & 0x01)) - nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01); + err = nct6775_read_value(data, data->REG_CONFIG, &tmp); + if (err) + return err; + if (!(tmp & 0x01)) { + err = nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01); + if (err) + return err; + } } /* Enable temperature sensors if needed */ @@ -3517,18 +3331,29 @@ static inline void nct6775_init_device(struct nct6775_data *data) continue; if (!data->reg_temp_config[i]) continue; - tmp = nct6775_read_value(data, data->reg_temp_config[i]); - if (tmp & 0x01) - nct6775_write_value(data, data->reg_temp_config[i], - tmp & 0xfe); + err = nct6775_read_value(data, data->reg_temp_config[i], &tmp); + if (err) + return err; + if (tmp & 0x01) { + err = nct6775_write_value(data, data->reg_temp_config[i], tmp & 0xfe); + if (err) + return err; + } } /* Enable VBAT monitoring if needed */ - tmp = nct6775_read_value(data, data->REG_VBAT); - if (!(tmp & 0x01)) - nct6775_write_value(data, data->REG_VBAT, tmp | 0x01); + err = nct6775_read_value(data, data->REG_VBAT, &tmp); + if (err) + return err; + if (!(tmp & 0x01)) { + err = nct6775_write_value(data, data->REG_VBAT, tmp | 0x01); + if (err) + return err; + } - diode = nct6775_read_value(data, data->REG_DIODE); + err = nct6775_read_value(data, data->REG_DIODE, &diode); + if (err) + return err; for (i = 0; i < data->temp_fixed_num; i++) { if (!(data->have_temp_fixed & BIT(i))) @@ -3539,242 +3364,24 @@ static inline void nct6775_init_device(struct nct6775_data *data) else /* thermistor */ data->temp_type[i] = 4; } -} - -static void -nct6775_check_fan_inputs(struct nct6775_data *data) -{ - bool fan3pin = false, fan4pin = false, fan4min = false; - bool fan5pin = false, fan6pin = false, fan7pin = false; - bool pwm3pin = false, pwm4pin = false, pwm5pin = false; - bool pwm6pin = false, pwm7pin = false; - int sioreg = data->sioreg; - - /* Store SIO_REG_ENABLE for use during resume */ - superio_select(sioreg, NCT6775_LD_HWM); - data->sio_reg_enable = superio_inb(sioreg, SIO_REG_ENABLE); - - /* fan4 and fan5 share some pins with the GPIO and serial flash */ - if (data->kind == nct6775) { - int cr2c = superio_inb(sioreg, 0x2c); - - fan3pin = cr2c & BIT(6); - pwm3pin = cr2c & BIT(7); - - /* On NCT6775, fan4 shares pins with the fdc interface */ - fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80); - } else if (data->kind == nct6776) { - bool gpok = superio_inb(sioreg, 0x27) & 0x80; - const char *board_vendor, *board_name; - - board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); - board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (board_name && board_vendor && - !strcmp(board_vendor, "ASRock")) { - /* - * Auxiliary fan monitoring is not enabled on ASRock - * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode. - * Observed with BIOS version 2.00. - */ - if (!strcmp(board_name, "Z77 Pro4-M")) { - if ((data->sio_reg_enable & 0xe0) != 0xe0) { - data->sio_reg_enable |= 0xe0; - superio_outb(sioreg, SIO_REG_ENABLE, - data->sio_reg_enable); - } - } - } - - if (data->sio_reg_enable & 0x80) - fan3pin = gpok; - else - fan3pin = !(superio_inb(sioreg, 0x24) & 0x40); - - if (data->sio_reg_enable & 0x40) - fan4pin = gpok; - else - fan4pin = superio_inb(sioreg, 0x1C) & 0x01; - - if (data->sio_reg_enable & 0x20) - fan5pin = gpok; - else - fan5pin = superio_inb(sioreg, 0x1C) & 0x02; - - fan4min = fan4pin; - pwm3pin = fan3pin; - } else if (data->kind == nct6106) { - int cr24 = superio_inb(sioreg, 0x24); - - fan3pin = !(cr24 & 0x80); - pwm3pin = cr24 & 0x08; - } else if (data->kind == nct6116) { - int cr1a = superio_inb(sioreg, 0x1a); - int cr1b = superio_inb(sioreg, 0x1b); - int cr24 = superio_inb(sioreg, 0x24); - int cr2a = superio_inb(sioreg, 0x2a); - int cr2b = superio_inb(sioreg, 0x2b); - int cr2f = superio_inb(sioreg, 0x2f); - - fan3pin = !(cr2b & 0x10); - fan4pin = (cr2b & 0x80) || // pin 1(2) - (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66) - fan5pin = (cr2b & 0x80) || // pin 126(127) - (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96) - - pwm3pin = fan3pin && (cr24 & 0x08); - pwm4pin = fan4pin; - pwm5pin = fan5pin; - } else { - /* - * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, - * NCT6797D, NCT6798D - */ - int cr1a = superio_inb(sioreg, 0x1a); - int cr1b = superio_inb(sioreg, 0x1b); - int cr1c = superio_inb(sioreg, 0x1c); - int cr1d = superio_inb(sioreg, 0x1d); - int cr2a = superio_inb(sioreg, 0x2a); - int cr2b = superio_inb(sioreg, 0x2b); - int cr2d = superio_inb(sioreg, 0x2d); - int cr2f = superio_inb(sioreg, 0x2f); - bool dsw_en = cr2f & BIT(3); - bool ddr4_en = cr2f & BIT(4); - int cre0; - int creb; - int cred; - - superio_select(sioreg, NCT6775_LD_12); - cre0 = superio_inb(sioreg, 0xe0); - creb = superio_inb(sioreg, 0xeb); - cred = superio_inb(sioreg, 0xed); - - fan3pin = !(cr1c & BIT(5)); - fan4pin = !(cr1c & BIT(6)); - fan5pin = !(cr1c & BIT(7)); - - pwm3pin = !(cr1c & BIT(0)); - pwm4pin = !(cr1c & BIT(1)); - pwm5pin = !(cr1c & BIT(2)); - - switch (data->kind) { - case nct6791: - fan6pin = cr2d & BIT(1); - pwm6pin = cr2d & BIT(0); - break; - case nct6792: - fan6pin = !dsw_en && (cr2d & BIT(1)); - pwm6pin = !dsw_en && (cr2d & BIT(0)); - break; - case nct6793: - fan5pin |= cr1b & BIT(5); - fan5pin |= creb & BIT(5); - - fan6pin = !dsw_en && (cr2d & BIT(1)); - fan6pin |= creb & BIT(3); - - pwm5pin |= cr2d & BIT(7); - pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); - - pwm6pin = !dsw_en && (cr2d & BIT(0)); - pwm6pin |= creb & BIT(2); - break; - case nct6795: - fan5pin |= cr1b & BIT(5); - fan5pin |= creb & BIT(5); - - fan6pin = (cr2a & BIT(4)) && - (!dsw_en || (cred & BIT(4))); - fan6pin |= creb & BIT(3); - - pwm5pin |= cr2d & BIT(7); - pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); - - pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); - pwm6pin |= creb & BIT(2); - break; - case nct6796: - fan5pin |= cr1b & BIT(5); - fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0)); - fan5pin |= creb & BIT(5); - - fan6pin = (cr2a & BIT(4)) && - (!dsw_en || (cred & BIT(4))); - fan6pin |= creb & BIT(3); - - fan7pin = !(cr2b & BIT(2)); - - pwm5pin |= cr2d & BIT(7); - pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0)); - pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); - - pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); - pwm6pin |= creb & BIT(2); - - pwm7pin = !(cr1d & (BIT(2) | BIT(3))); - break; - case nct6797: - fan5pin |= !ddr4_en && (cr1b & BIT(5)); - fan5pin |= creb & BIT(5); - - fan6pin = cr2a & BIT(4); - fan6pin |= creb & BIT(3); - - fan7pin = cr1a & BIT(1); - - pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); - pwm5pin |= !ddr4_en && (cr2d & BIT(7)); - - pwm6pin = creb & BIT(2); - pwm6pin |= cred & BIT(2); - - pwm7pin = cr1d & BIT(4); - break; - case nct6798: - fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3)); - fan6pin |= cr2a & BIT(4); - fan6pin |= creb & BIT(5); - - fan7pin = cr1b & BIT(5); - fan7pin |= !(cr2b & BIT(2)); - fan7pin |= creb & BIT(3); - - pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4)); - pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3)); - pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); - - pwm7pin = !(cr1d & (BIT(2) | BIT(3))); - pwm7pin |= cr2d & BIT(7); - pwm7pin |= creb & BIT(2); - break; - default: /* NCT6779D */ - break; - } - - fan4min = fan4pin; - } - /* fan 1 and 2 (0x03) are always present */ - data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) | - (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6); - data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) | - (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6); - data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | - (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6); + return 0; } -static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, - int *available, int *mask) +static int add_temp_sensors(struct nct6775_data *data, const u16 *regp, + int *available, int *mask) { - int i; - u8 src; + int i, err; + u16 src; for (i = 0; i < data->pwm_num && *available; i++) { int index; if (!regp[i]) continue; - src = nct6775_read_value(data, regp[i]); + err = nct6775_read_value(data, regp[i], &src); + if (err) + return err; src &= 0x1f; if (!src || (*mask & BIT(src))) continue; @@ -3782,46 +3389,36 @@ static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, continue; index = __ffs(*available); - nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src); + err = nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src); + if (err) + return err; *available &= ~BIT(index); *mask |= BIT(src); } + + return 0; } -static int nct6775_probe(struct platform_device *pdev) +int nct6775_probe(struct device *dev, struct nct6775_data *data, + const struct regmap_config *regmapcfg) { - struct device *dev = &pdev->dev; - struct nct6775_sio_data *sio_data = dev_get_platdata(dev); - struct nct6775_data *data; - struct resource *res; int i, s, err = 0; - int src, mask, available; + int mask, available; + u16 src; const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config; const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit; const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL; - int num_reg_temp, num_reg_temp_mon; - u8 cr2a; - struct attribute_group *group; + int num_reg_temp, num_reg_temp_mon, num_reg_tsi_temp; struct device *hwmon_dev; - int num_attr_groups = 0; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, - DRVNAME)) - return -EBUSY; + struct sensor_template_group tsi_temp_tg; - data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; + data->regmap = devm_regmap_init(dev, NULL, data, regmapcfg); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); - data->kind = sio_data->kind; - data->sioreg = sio_data->sioreg; - data->addr = res->start; mutex_init(&data->update_lock); data->name = nct6775_device_names[data->kind]; data->bank = 0xff; /* Force initial bank selection */ - platform_set_drvdata(pdev, data); switch (data->kind) { case nct6106: @@ -3883,11 +3480,13 @@ static int nct6775_probe(struct platform_device *pdev) data->ALARM_BITS = NCT6106_ALARM_BITS; data->REG_BEEP = NCT6106_REG_BEEP; data->BEEP_BITS = NCT6106_BEEP_BITS; + data->REG_TSI_TEMP = NCT6106_REG_TSI_TEMP; reg_temp = NCT6106_REG_TEMP; reg_temp_mon = NCT6106_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6106_REG_TSI_TEMP); reg_temp_over = NCT6106_REG_TEMP_OVER; reg_temp_hyst = NCT6106_REG_TEMP_HYST; reg_temp_config = NCT6106_REG_TEMP_CONFIG; @@ -3956,11 +3555,13 @@ static int nct6775_probe(struct platform_device *pdev) data->ALARM_BITS = NCT6116_ALARM_BITS; data->REG_BEEP = NCT6106_REG_BEEP; data->BEEP_BITS = NCT6116_BEEP_BITS; + data->REG_TSI_TEMP = NCT6116_REG_TSI_TEMP; reg_temp = NCT6106_REG_TEMP; reg_temp_mon = NCT6106_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); reg_temp_over = NCT6106_REG_TEMP_OVER; reg_temp_hyst = NCT6106_REG_TEMP_HYST; reg_temp_config = NCT6106_REG_TEMP_CONFIG; @@ -4031,11 +3632,13 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6775_REG_ALARM; data->REG_BEEP = NCT6775_REG_BEEP; + data->REG_TSI_TEMP = NCT6775_REG_TSI_TEMP; reg_temp = NCT6775_REG_TEMP; reg_temp_mon = NCT6775_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); num_reg_temp_mon = ARRAY_SIZE(NCT6775_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6775_REG_TSI_TEMP); reg_temp_over = NCT6775_REG_TEMP_OVER; reg_temp_hyst = NCT6775_REG_TEMP_HYST; reg_temp_config = NCT6775_REG_TEMP_CONFIG; @@ -4104,11 +3707,13 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6775_REG_ALARM; data->REG_BEEP = NCT6776_REG_BEEP; + data->REG_TSI_TEMP = NCT6776_REG_TSI_TEMP; reg_temp = NCT6775_REG_TEMP; reg_temp_mon = NCT6775_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); num_reg_temp_mon = ARRAY_SIZE(NCT6775_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6776_REG_TSI_TEMP); reg_temp_over = NCT6775_REG_TEMP_OVER; reg_temp_hyst = NCT6775_REG_TEMP_HYST; reg_temp_config = NCT6776_REG_TEMP_CONFIG; @@ -4181,11 +3786,13 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6779_REG_ALARM; data->REG_BEEP = NCT6776_REG_BEEP; + data->REG_TSI_TEMP = NCT6776_REG_TSI_TEMP; reg_temp = NCT6779_REG_TEMP; reg_temp_mon = NCT6779_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + num_reg_tsi_temp = ARRAY_SIZE(NCT6776_REG_TSI_TEMP); reg_temp_over = NCT6779_REG_TEMP_OVER; reg_temp_hyst = NCT6779_REG_TEMP_HYST; reg_temp_config = NCT6779_REG_TEMP_CONFIG; @@ -4300,6 +3907,24 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_BEEP = NCT6776_REG_BEEP; else data->REG_BEEP = NCT6792_REG_BEEP; + switch (data->kind) { + case nct6791: + case nct6792: + case nct6793: + data->REG_TSI_TEMP = NCT6776_REG_TSI_TEMP; + num_reg_tsi_temp = ARRAY_SIZE(NCT6776_REG_TSI_TEMP); + break; + case nct6795: + case nct6796: + case nct6797: + case nct6798: + data->REG_TSI_TEMP = NCT6796_REG_TSI_TEMP; + num_reg_tsi_temp = ARRAY_SIZE(NCT6796_REG_TSI_TEMP); + break; + default: + num_reg_tsi_temp = 0; + break; + } reg_temp = NCT6779_REG_TEMP; num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); @@ -4337,7 +3962,10 @@ static int nct6775_probe(struct platform_device *pdev) if (reg_temp[i] == 0) continue; - src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; + err = nct6775_read_value(data, data->REG_TEMP_SOURCE[i], &src); + if (err) + return err; + src &= 0x1f; if (!src || (mask & BIT(src))) available |= BIT(i); @@ -4348,8 +3976,12 @@ static int nct6775_probe(struct platform_device *pdev) * Now find unmonitored temperature registers and enable monitoring * if additional monitoring registers are available. */ - add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask); - add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask); + err = add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask); + if (err) + return err; + err = add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask); + if (err) + return err; mask = 0; s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */ @@ -4357,7 +3989,10 @@ static int nct6775_probe(struct platform_device *pdev) if (reg_temp[i] == 0) continue; - src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; + err = nct6775_read_value(data, data->REG_TEMP_SOURCE[i], &src); + if (err) + return err; + src &= 0x1f; if (!src || (mask & BIT(src))) continue; @@ -4417,7 +4052,10 @@ static int nct6775_probe(struct platform_device *pdev) if (reg_temp_mon[i] == 0) continue; - src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f; + err = nct6775_read_value(data, data->REG_TEMP_SEL[i], &src); + if (err) + return err; + src &= 0x1f; if (!src) continue; @@ -4499,421 +4137,70 @@ static int nct6775_probe(struct platform_device *pdev) } #endif /* USE_ALTERNATE */ - /* Initialize the chip */ - nct6775_init_device(data); - - err = superio_enter(sio_data->sioreg); - if (err) - return err; - - cr2a = superio_inb(sio_data->sioreg, 0x2a); - switch (data->kind) { - case nct6775: - data->have_vid = (cr2a & 0x40); - break; - case nct6776: - data->have_vid = (cr2a & 0x60) == 0x40; - break; - case nct6106: - case nct6116: - case nct6779: - case nct6791: - case nct6792: - case nct6793: - case nct6795: - case nct6796: - case nct6797: - case nct6798: - break; - } + /* Check which TSIx_TEMP registers are active */ + for (i = 0; i < num_reg_tsi_temp; i++) { + u16 tmp; - /* - * Read VID value - * We can get the VID input values directly at logical device D 0xe3. - */ - if (data->have_vid) { - superio_select(sio_data->sioreg, NCT6775_LD_VID); - data->vid = superio_inb(sio_data->sioreg, 0xe3); - data->vrm = vid_which_vrm(); + err = nct6775_read_value(data, data->REG_TSI_TEMP[i], &tmp); + if (err) + return err; + if (tmp) + data->have_tsi_temp |= BIT(i); } - if (fan_debounce) { - u8 tmp; + /* Initialize the chip */ + err = nct6775_init_device(data); + if (err) + return err; - superio_select(sio_data->sioreg, NCT6775_LD_HWM); - tmp = superio_inb(sio_data->sioreg, - NCT6775_REG_CR_FAN_DEBOUNCE); - switch (data->kind) { - case nct6106: - case nct6116: - tmp |= 0xe0; - break; - case nct6775: - tmp |= 0x1e; - break; - case nct6776: - case nct6779: - tmp |= 0x3e; - break; - case nct6791: - case nct6792: - case nct6793: - case nct6795: - case nct6796: - case nct6797: - case nct6798: - tmp |= 0x7e; - break; - } - superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE, - tmp); - dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n", - data->name); + if (data->driver_init) { + err = data->driver_init(data); + if (err) + return err; } - nct6775_check_fan_inputs(data); - - superio_exit(sio_data->sioreg); - /* Read fan clock dividers immediately */ - nct6775_init_fan_common(dev, data); + err = nct6775_init_fan_common(dev, data); + if (err) + return err; /* Register sysfs hooks */ - group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group, - data->pwm_num); - if (IS_ERR(group)) - return PTR_ERR(group); - - data->groups[num_attr_groups++] = group; - - group = nct6775_create_attr_group(dev, &nct6775_in_template_group, - fls(data->have_in)); - if (IS_ERR(group)) - return PTR_ERR(group); - - data->groups[num_attr_groups++] = group; - - group = nct6775_create_attr_group(dev, &nct6775_fan_template_group, - fls(data->has_fan)); - if (IS_ERR(group)) - return PTR_ERR(group); - - data->groups[num_attr_groups++] = group; - - group = nct6775_create_attr_group(dev, &nct6775_temp_template_group, - fls(data->have_temp)); - if (IS_ERR(group)) - return PTR_ERR(group); - - data->groups[num_attr_groups++] = group; - data->groups[num_attr_groups++] = &nct6775_group_other; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name, - data, data->groups); - return PTR_ERR_OR_ZERO(hwmon_dev); -} - -static void nct6791_enable_io_mapping(int sioaddr) -{ - int val; - - val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE); - if (val & 0x10) { - pr_info("Enabling hardware monitor logical device mappings.\n"); - superio_outb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE, - val & ~0x10); - } -} - -static int __maybe_unused nct6775_suspend(struct device *dev) -{ - struct nct6775_data *data = nct6775_update_device(dev); - - mutex_lock(&data->update_lock); - data->vbat = nct6775_read_value(data, data->REG_VBAT); - if (data->kind == nct6775) { - data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1); - data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2); - } - mutex_unlock(&data->update_lock); - - return 0; -} - -static int __maybe_unused nct6775_resume(struct device *dev) -{ - struct nct6775_data *data = dev_get_drvdata(dev); - int sioreg = data->sioreg; - int i, j, err = 0; - u8 reg; - - mutex_lock(&data->update_lock); - data->bank = 0xff; /* Force initial bank selection */ - - err = superio_enter(sioreg); + err = nct6775_add_template_attr_group(dev, data, &nct6775_pwm_template_group, + data->pwm_num); if (err) - goto abort; - - superio_select(sioreg, NCT6775_LD_HWM); - reg = superio_inb(sioreg, SIO_REG_ENABLE); - if (reg != data->sio_reg_enable) - superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable); - - if (data->kind == nct6791 || data->kind == nct6792 || - data->kind == nct6793 || data->kind == nct6795 || - data->kind == nct6796 || data->kind == nct6797 || - data->kind == nct6798) - nct6791_enable_io_mapping(sioreg); - - superio_exit(sioreg); - - /* Restore limits */ - for (i = 0; i < data->in_num; i++) { - if (!(data->have_in & BIT(i))) - continue; - - nct6775_write_value(data, data->REG_IN_MINMAX[0][i], - data->in[i][1]); - nct6775_write_value(data, data->REG_IN_MINMAX[1][i], - data->in[i][2]); - } - - for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { - if (!(data->has_fan_min & BIT(i))) - continue; - - nct6775_write_value(data, data->REG_FAN_MIN[i], - data->fan_min[i]); - } - - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & BIT(i))) - continue; - - for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++) - if (data->reg_temp[j][i]) - nct6775_write_temp(data, data->reg_temp[j][i], - data->temp[j][i]); - } - - /* Restore other settings */ - nct6775_write_value(data, data->REG_VBAT, data->vbat); - if (data->kind == nct6775) { - nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); - nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); - } - -abort: - /* Force re-reading all values */ - data->valid = false; - mutex_unlock(&data->update_lock); - - return err; -} - -static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); - -static struct platform_driver nct6775_driver = { - .driver = { - .name = DRVNAME, - .pm = &nct6775_dev_pm_ops, - }, - .probe = nct6775_probe, -}; - -/* nct6775_find() looks for a '627 in the Super-I/O config space */ -static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) -{ - u16 val; - int err; - int addr; + return err; - err = superio_enter(sioaddr); + err = nct6775_add_template_attr_group(dev, data, &nct6775_in_template_group, + fls(data->have_in)); if (err) return err; - val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) | - superio_inb(sioaddr, SIO_REG_DEVID + 1); - if (force_id && val != 0xffff) - val = force_id; - - switch (val & SIO_ID_MASK) { - case SIO_NCT6106_ID: - sio_data->kind = nct6106; - break; - case SIO_NCT6116_ID: - sio_data->kind = nct6116; - break; - case SIO_NCT6775_ID: - sio_data->kind = nct6775; - break; - case SIO_NCT6776_ID: - sio_data->kind = nct6776; - break; - case SIO_NCT6779_ID: - sio_data->kind = nct6779; - break; - case SIO_NCT6791_ID: - sio_data->kind = nct6791; - break; - case SIO_NCT6792_ID: - sio_data->kind = nct6792; - break; - case SIO_NCT6793_ID: - sio_data->kind = nct6793; - break; - case SIO_NCT6795_ID: - sio_data->kind = nct6795; - break; - case SIO_NCT6796_ID: - sio_data->kind = nct6796; - break; - case SIO_NCT6797_ID: - sio_data->kind = nct6797; - break; - case SIO_NCT6798_ID: - sio_data->kind = nct6798; - break; - default: - if (val != 0xffff) - pr_debug("unsupported chip ID: 0x%04x\n", val); - superio_exit(sioaddr); - return -ENODEV; - } - - /* We have a known chip, find the HWM I/O address */ - superio_select(sioaddr, NCT6775_LD_HWM); - val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8) - | superio_inb(sioaddr, SIO_REG_ADDR + 1); - addr = val & IOREGION_ALIGNMENT; - if (addr == 0) { - pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); - superio_exit(sioaddr); - return -ENODEV; - } - - /* Activate logical device if needed */ - val = superio_inb(sioaddr, SIO_REG_ENABLE); - if (!(val & 0x01)) { - pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); - superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); - } - - if (sio_data->kind == nct6791 || sio_data->kind == nct6792 || - sio_data->kind == nct6793 || sio_data->kind == nct6795 || - sio_data->kind == nct6796 || sio_data->kind == nct6797 || - sio_data->kind == nct6798) - nct6791_enable_io_mapping(sioaddr); - - superio_exit(sioaddr); - pr_info("Found %s or compatible chip at %#x:%#x\n", - nct6775_sio_names[sio_data->kind], sioaddr, addr); - sio_data->sioreg = sioaddr; - - return addr; -} - -/* - * when Super-I/O functions move to a separate file, the Super-I/O - * bus will manage the lifetime of the device and this module will only keep - * track of the nct6775 driver. But since we use platform_device_alloc(), we - * must keep track of the device - */ -static struct platform_device *pdev[2]; - -static int __init sensors_nct6775_init(void) -{ - int i, err; - bool found = false; - int address; - struct resource res; - struct nct6775_sio_data sio_data; - int sioaddr[2] = { 0x2e, 0x4e }; - - err = platform_driver_register(&nct6775_driver); + err = nct6775_add_template_attr_group(dev, data, &nct6775_fan_template_group, + fls(data->has_fan)); if (err) return err; - /* - * initialize sio_data->kind and sio_data->sioreg. - * - * when Super-I/O functions move to a separate file, the Super-I/O - * driver will probe 0x2e and 0x4e and auto-detect the presence of a - * nct6775 hardware monitor, and call probe() - */ - for (i = 0; i < ARRAY_SIZE(pdev); i++) { - address = nct6775_find(sioaddr[i], &sio_data); - if (address <= 0) - continue; - - found = true; - - pdev[i] = platform_device_alloc(DRVNAME, address); - if (!pdev[i]) { - err = -ENOMEM; - goto exit_device_unregister; - } - - err = platform_device_add_data(pdev[i], &sio_data, - sizeof(struct nct6775_sio_data)); - if (err) - goto exit_device_put; - - memset(&res, 0, sizeof(res)); - res.name = DRVNAME; - res.start = address + IOREGION_OFFSET; - res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; - res.flags = IORESOURCE_IO; - - err = acpi_check_resource_conflict(&res); - if (err) { - platform_device_put(pdev[i]); - pdev[i] = NULL; - continue; - } - - err = platform_device_add_resources(pdev[i], &res, 1); - if (err) - goto exit_device_put; + err = nct6775_add_template_attr_group(dev, data, &nct6775_temp_template_group, + fls(data->have_temp)); + if (err) + return err; - /* platform_device_add calls probe() */ - err = platform_device_add(pdev[i]); + if (data->have_tsi_temp) { + tsi_temp_tg.templates = nct6775_tsi_temp_template; + tsi_temp_tg.is_visible = nct6775_tsi_temp_is_visible; + tsi_temp_tg.base = fls(data->have_temp) + 1; + err = nct6775_add_template_attr_group(dev, data, &tsi_temp_tg, + fls(data->have_tsi_temp)); if (err) - goto exit_device_put; - } - if (!found) { - err = -ENODEV; - goto exit_unregister; - } - - return 0; - -exit_device_put: - platform_device_put(pdev[i]); -exit_device_unregister: - while (--i >= 0) { - if (pdev[i]) - platform_device_unregister(pdev[i]); + return err; } -exit_unregister: - platform_driver_unregister(&nct6775_driver); - return err; -} -static void __exit sensors_nct6775_exit(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pdev); i++) { - if (pdev[i]) - platform_device_unregister(pdev[i]); - } - platform_driver_unregister(&nct6775_driver); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } +EXPORT_SYMBOL_GPL(nct6775_probe); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("Driver for NCT6775F and compatible chips"); +MODULE_DESCRIPTION("Core driver for NCT6775F and compatible chips"); MODULE_LICENSE("GPL"); - -module_init(sensors_nct6775_init); -module_exit(sensors_nct6775_exit); diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c new file mode 100644 index 000000000000..e1bcd1146191 --- /dev/null +++ b/drivers/hwmon/nct6775-i2c.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * nct6775-i2c - I2C driver for the hardware monitoring functionality of + * Nuvoton NCT677x Super-I/O chips + * + * Copyright (C) 2022 Zev Weiss <zev@bewilderbeest.net> + * + * This driver interacts with the chip via it's "back door" i2c interface, as + * is often exposed to a BMC. Because the host may still be operating the + * chip via the ("front door") LPC interface, this driver cannot assume that + * it actually has full control of the chip, and in particular must avoid + * making any changes that could confuse the host's LPC usage of it. It thus + * operates in a strictly read-only fashion, with the only exception being the + * bank-select register (which seems, thankfully, to be replicated for the i2c + * interface so it doesn't affect the LPC interface). + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include "nct6775.h" + +static int nct6775_i2c_read(void *ctx, unsigned int reg, unsigned int *val) +{ + int ret; + u32 tmp; + u8 bank = reg >> 8; + struct nct6775_data *data = ctx; + struct i2c_client *client = data->driver_data; + + if (bank != data->bank) { + ret = i2c_smbus_write_byte_data(client, NCT6775_REG_BANK, bank); + if (ret) + return ret; + data->bank = bank; + } + + ret = i2c_smbus_read_byte_data(client, reg & 0xff); + if (ret < 0) + return ret; + tmp = ret; + + if (nct6775_reg_is_word_sized(data, reg)) { + ret = i2c_smbus_read_byte_data(client, (reg & 0xff) + 1); + if (ret < 0) + return ret; + tmp = (tmp << 8) | ret; + } + + *val = tmp; + return 0; +} + +/* + * The write operation is a dummy so as not to disturb anything being done + * with the chip via LPC. + */ +static int nct6775_i2c_write(void *ctx, unsigned int reg, unsigned int value) +{ + struct nct6775_data *data = ctx; + struct i2c_client *client = data->driver_data; + + dev_dbg(&client->dev, "skipping attempted write: %02x -> %03x\n", value, reg); + + /* + * This is a lie, but writing anything but the bank-select register is + * something this driver shouldn't be doing. + */ + return 0; +} + +static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = { + { .compatible = "nuvoton,nct6106", .data = (void *)nct6106, }, + { .compatible = "nuvoton,nct6116", .data = (void *)nct6116, }, + { .compatible = "nuvoton,nct6775", .data = (void *)nct6775, }, + { .compatible = "nuvoton,nct6776", .data = (void *)nct6776, }, + { .compatible = "nuvoton,nct6779", .data = (void *)nct6779, }, + { .compatible = "nuvoton,nct6791", .data = (void *)nct6791, }, + { .compatible = "nuvoton,nct6792", .data = (void *)nct6792, }, + { .compatible = "nuvoton,nct6793", .data = (void *)nct6793, }, + { .compatible = "nuvoton,nct6795", .data = (void *)nct6795, }, + { .compatible = "nuvoton,nct6796", .data = (void *)nct6796, }, + { .compatible = "nuvoton,nct6797", .data = (void *)nct6797, }, + { .compatible = "nuvoton,nct6798", .data = (void *)nct6798, }, + { }, +}; +MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match); + +static const struct i2c_device_id nct6775_i2c_id[] = { + { "nct6106", nct6106 }, + { "nct6116", nct6116 }, + { "nct6775", nct6775 }, + { "nct6776", nct6776 }, + { "nct6779", nct6779 }, + { "nct6791", nct6791 }, + { "nct6792", nct6792 }, + { "nct6793", nct6793 }, + { "nct6795", nct6795 }, + { "nct6796", nct6796 }, + { "nct6797", nct6797 }, + { "nct6798", nct6798 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id); + +static int nct6775_i2c_probe_init(struct nct6775_data *data) +{ + u32 tsi_channel_mask; + struct i2c_client *client = data->driver_data; + + /* + * The i2c interface doesn't provide access to the control registers + * needed to determine the presence of other fans, but fans 1 and 2 + * are (in principle) always there. + * + * In practice this is perhaps a little silly, because the system + * using this driver is mostly likely a BMC, and hence probably has + * totally separate fan tachs & pwms of its own that are actually + * controlling/monitoring the fans -- these are thus unlikely to be + * doing anything actually useful. + */ + data->has_fan = 0x03; + data->has_fan_min = 0x03; + data->has_pwm = 0x03; + + /* + * Because on a BMC this driver may be bound very shortly after power + * is first applied to the device, the automatic TSI channel detection + * in nct6775_probe() (which has already been run at this point) may + * not find anything if a channel hasn't yet produced a temperature + * reading. Augment whatever was found via autodetection (if + * anything) with the channels DT says should be active. + */ + if (!of_property_read_u32(client->dev.of_node, "nuvoton,tsi-channel-mask", + &tsi_channel_mask)) + data->have_tsi_temp |= tsi_channel_mask & GENMASK(NUM_TSI_TEMP - 1, 0); + + return 0; +} + +static const struct regmap_config nct6775_i2c_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + .reg_read = nct6775_i2c_read, + .reg_write = nct6775_i2c_write, +}; + +static int nct6775_i2c_probe(struct i2c_client *client) +{ + struct nct6775_data *data; + const struct of_device_id *of_id; + const struct i2c_device_id *i2c_id; + struct device *dev = &client->dev; + + of_id = of_match_device(nct6775_i2c_of_match, dev); + i2c_id = i2c_match_id(nct6775_i2c_id, client); + + if (of_id && (unsigned long)of_id->data != i2c_id->driver_data) + dev_notice(dev, "Device mismatch: %s in device tree, %s detected\n", + of_id->name, i2c_id->name); + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->kind = i2c_id->driver_data; + + data->read_only = true; + data->driver_data = client; + data->driver_init = nct6775_i2c_probe_init; + + return nct6775_probe(dev, data, &nct6775_i2c_regmap_config); +} + +static struct i2c_driver nct6775_i2c_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "nct6775-i2c", + .of_match_table = of_match_ptr(nct6775_i2c_of_match), + }, + .probe_new = nct6775_i2c_probe, + .id_table = nct6775_i2c_id, +}; + +module_i2c_driver(nct6775_i2c_driver); + +MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>"); +MODULE_DESCRIPTION("I2C driver for NCT6775F and compatible chips"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(HWMON_NCT6775); diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c new file mode 100644 index 000000000000..b34783784213 --- /dev/null +++ b/drivers/hwmon/nct6775-platform.c @@ -0,0 +1,1226 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * nct6775 - Platform driver for the hardware monitoring + * functionality of Nuvoton NCT677x Super-I/O chips + * + * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon-vid.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/wmi.h> + +#include "nct6775.h" + +enum sensor_access { access_direct, access_asuswmi }; + +static const char * const nct6775_sio_names[] __initconst = { + "NCT6106D", + "NCT6116D", + "NCT6775F", + "NCT6776D/F", + "NCT6779D", + "NCT6791D", + "NCT6792D", + "NCT6793D", + "NCT6795D", + "NCT6796D", + "NCT6797D", + "NCT6798D", +}; + +static unsigned short force_id; +module_param(force_id, ushort, 0); +MODULE_PARM_DESC(force_id, "Override the detected device ID"); + +static unsigned short fan_debounce; +module_param(fan_debounce, ushort, 0); +MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); + +#define DRVNAME "nct6775" + +#define NCT6775_PORT_CHIPID 0x58 + +/* + * ISA constants + */ + +#define IOREGION_ALIGNMENT (~7) +#define IOREGION_OFFSET 5 +#define IOREGION_LENGTH 2 +#define ADDR_REG_OFFSET 0 +#define DATA_REG_OFFSET 1 + +/* + * Super-I/O constants and functions + */ + +#define NCT6775_LD_ACPI 0x0a +#define NCT6775_LD_HWM 0x0b +#define NCT6775_LD_VID 0x0d +#define NCT6775_LD_12 0x12 + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define SIO_NCT6106_ID 0xc450 +#define SIO_NCT6116_ID 0xd280 +#define SIO_NCT6775_ID 0xb470 +#define SIO_NCT6776_ID 0xc330 +#define SIO_NCT6779_ID 0xc560 +#define SIO_NCT6791_ID 0xc800 +#define SIO_NCT6792_ID 0xc910 +#define SIO_NCT6793_ID 0xd120 +#define SIO_NCT6795_ID 0xd350 +#define SIO_NCT6796_ID 0xd420 +#define SIO_NCT6797_ID 0xd450 +#define SIO_NCT6798_ID 0xd428 +#define SIO_ID_MASK 0xFFF8 + +/* + * Control registers + */ +#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0 + +struct nct6775_sio_data { + int sioreg; + int ld; + enum kinds kind; + enum sensor_access access; + + /* superio_() callbacks */ + void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val); + int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg); + void (*sio_select)(struct nct6775_sio_data *sio_data, int ld); + int (*sio_enter)(struct nct6775_sio_data *sio_data); + void (*sio_exit)(struct nct6775_sio_data *sio_data); +}; + +#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" +#define ASUSWMI_METHODID_RSIO 0x5253494F +#define ASUSWMI_METHODID_WSIO 0x5753494F +#define ASUSWMI_METHODID_RHWM 0x5248574D +#define ASUSWMI_METHODID_WHWM 0x5748574D +#define ASUSWMI_UNSUPPORTED_METHOD 0xFFFFFFFE + +static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval) +{ +#if IS_ENABLED(CONFIG_ACPI_WMI) + u32 args = bank | (reg << 8) | (val << 16); + struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + union acpi_object *obj; + u32 tmp = ASUSWMI_UNSUPPORTED_METHOD; + + status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, + method_id, &input, &output); + + if (ACPI_FAILURE(status)) + return -EIO; + + obj = output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + tmp = obj->integer.value; + + if (retval) + *retval = tmp; + + kfree(obj); + + if (tmp == ASUSWMI_UNSUPPORTED_METHOD) + return -ENODEV; + return 0; +#else + return -EOPNOTSUPP; +#endif +} + +static inline int nct6775_asuswmi_write(u8 bank, u8 reg, u8 val) +{ + return nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WHWM, bank, + reg, val, NULL); +} + +static inline int nct6775_asuswmi_read(u8 bank, u8 reg, u8 *val) +{ + u32 ret, tmp = 0; + + ret = nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RHWM, bank, + reg, 0, &tmp); + *val = tmp; + return ret; +} + +static int superio_wmi_inb(struct nct6775_sio_data *sio_data, int reg) +{ + int tmp = 0; + + nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RSIO, sio_data->ld, + reg, 0, &tmp); + return tmp; +} + +static void superio_wmi_outb(struct nct6775_sio_data *sio_data, int reg, int val) +{ + nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WSIO, sio_data->ld, + reg, val, NULL); +} + +static void superio_wmi_select(struct nct6775_sio_data *sio_data, int ld) +{ + sio_data->ld = ld; +} + +static int superio_wmi_enter(struct nct6775_sio_data *sio_data) +{ + return 0; +} + +static void superio_wmi_exit(struct nct6775_sio_data *sio_data) +{ +} + +static void superio_outb(struct nct6775_sio_data *sio_data, int reg, int val) +{ + int ioreg = sio_data->sioreg; + + outb(reg, ioreg); + outb(val, ioreg + 1); +} + +static int superio_inb(struct nct6775_sio_data *sio_data, int reg) +{ + int ioreg = sio_data->sioreg; + + outb(reg, ioreg); + return inb(ioreg + 1); +} + +static void superio_select(struct nct6775_sio_data *sio_data, int ld) +{ + int ioreg = sio_data->sioreg; + + outb(SIO_REG_LDSEL, ioreg); + outb(ld, ioreg + 1); +} + +static int superio_enter(struct nct6775_sio_data *sio_data) +{ + int ioreg = sio_data->sioreg; + + /* + * Try to reserve <ioreg> and <ioreg + 1> for exclusive access. + */ + if (!request_muxed_region(ioreg, 2, DRVNAME)) + return -EBUSY; + + outb(0x87, ioreg); + outb(0x87, ioreg); + + return 0; +} + +static void superio_exit(struct nct6775_sio_data *sio_data) +{ + int ioreg = sio_data->sioreg; + + outb(0xaa, ioreg); + outb(0x02, ioreg); + outb(0x02, ioreg + 1); + release_region(ioreg, 2); +} + +static inline void nct6775_wmi_set_bank(struct nct6775_data *data, u16 reg) +{ + u8 bank = reg >> 8; + + data->bank = bank; +} + +static int nct6775_wmi_reg_read(void *ctx, unsigned int reg, unsigned int *val) +{ + struct nct6775_data *data = ctx; + int err, word_sized = nct6775_reg_is_word_sized(data, reg); + u8 tmp = 0; + u16 res; + + nct6775_wmi_set_bank(data, reg); + + err = nct6775_asuswmi_read(data->bank, reg & 0xff, &tmp); + if (err) + return err; + + res = tmp; + if (word_sized) { + err = nct6775_asuswmi_read(data->bank, (reg & 0xff) + 1, &tmp); + if (err) + return err; + + res = (res << 8) + tmp; + } + *val = res; + return 0; +} + +static int nct6775_wmi_reg_write(void *ctx, unsigned int reg, unsigned int value) +{ + struct nct6775_data *data = ctx; + int res, word_sized = nct6775_reg_is_word_sized(data, reg); + + nct6775_wmi_set_bank(data, reg); + + if (word_sized) { + res = nct6775_asuswmi_write(data->bank, reg & 0xff, value >> 8); + if (res) + return res; + + res = nct6775_asuswmi_write(data->bank, (reg & 0xff) + 1, value); + } else { + res = nct6775_asuswmi_write(data->bank, reg & 0xff, value); + } + + return res; +} + +/* + * On older chips, only registers 0x50-0x5f are banked. + * On more recent chips, all registers are banked. + * Assume that is the case and set the bank number for each access. + * Cache the bank number so it only needs to be set if it changes. + */ +static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) +{ + u8 bank = reg >> 8; + + if (data->bank != bank) { + outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); + outb_p(bank, data->addr + DATA_REG_OFFSET); + data->bank = bank; + } +} + +static int nct6775_reg_read(void *ctx, unsigned int reg, unsigned int *val) +{ + struct nct6775_data *data = ctx; + int word_sized = nct6775_reg_is_word_sized(data, reg); + + nct6775_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); + *val = inb_p(data->addr + DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + data->addr + ADDR_REG_OFFSET); + *val = (*val << 8) + inb_p(data->addr + DATA_REG_OFFSET); + } + return 0; +} + +static int nct6775_reg_write(void *ctx, unsigned int reg, unsigned int value) +{ + struct nct6775_data *data = ctx; + int word_sized = nct6775_reg_is_word_sized(data, reg); + + nct6775_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, data->addr + DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + data->addr + ADDR_REG_OFFSET); + } + outb_p(value & 0xff, data->addr + DATA_REG_OFFSET); + return 0; +} + +static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data) +{ + int val; + + val = sio_data->sio_inb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE); + if (val & 0x10) { + pr_info("Enabling hardware monitor logical device mappings.\n"); + sio_data->sio_outb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE, + val & ~0x10); + } +} + +static int nct6775_suspend(struct device *dev) +{ + int err; + u16 tmp; + struct nct6775_data *data = nct6775_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + err = nct6775_read_value(data, data->REG_VBAT, &tmp); + if (err) + goto out; + data->vbat = tmp; + if (data->kind == nct6775) { + err = nct6775_read_value(data, NCT6775_REG_FANDIV1, &tmp); + if (err) + goto out; + data->fandiv1 = tmp; + + err = nct6775_read_value(data, NCT6775_REG_FANDIV2, &tmp); + if (err) + goto out; + data->fandiv2 = tmp; + } +out: + mutex_unlock(&data->update_lock); + + return err; +} + +static int nct6775_resume(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct nct6775_sio_data *sio_data = dev_get_platdata(dev); + int i, j, err = 0; + u8 reg; + + mutex_lock(&data->update_lock); + data->bank = 0xff; /* Force initial bank selection */ + + err = sio_data->sio_enter(sio_data); + if (err) + goto abort; + + sio_data->sio_select(sio_data, NCT6775_LD_HWM); + reg = sio_data->sio_inb(sio_data, SIO_REG_ENABLE); + if (reg != data->sio_reg_enable) + sio_data->sio_outb(sio_data, SIO_REG_ENABLE, data->sio_reg_enable); + + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793 || data->kind == nct6795 || + data->kind == nct6796 || data->kind == nct6797 || + data->kind == nct6798) + nct6791_enable_io_mapping(sio_data); + + sio_data->sio_exit(sio_data); + + /* Restore limits */ + for (i = 0; i < data->in_num; i++) { + if (!(data->have_in & BIT(i))) + continue; + + err = nct6775_write_value(data, data->REG_IN_MINMAX[0][i], data->in[i][1]); + if (err) + goto abort; + err = nct6775_write_value(data, data->REG_IN_MINMAX[1][i], data->in[i][2]); + if (err) + goto abort; + } + + for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { + if (!(data->has_fan_min & BIT(i))) + continue; + + err = nct6775_write_value(data, data->REG_FAN_MIN[i], data->fan_min[i]); + if (err) + goto abort; + } + + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & BIT(i))) + continue; + + for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++) + if (data->reg_temp[j][i]) { + err = nct6775_write_temp(data, data->reg_temp[j][i], + data->temp[j][i]); + if (err) + goto abort; + } + } + + /* Restore other settings */ + err = nct6775_write_value(data, data->REG_VBAT, data->vbat); + if (err) + goto abort; + if (data->kind == nct6775) { + err = nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); + if (err) + goto abort; + err = nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); + } + +abort: + /* Force re-reading all values */ + data->valid = false; + mutex_unlock(&data->update_lock); + + return err; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); + +static void +nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data) +{ + bool fan3pin = false, fan4pin = false, fan4min = false; + bool fan5pin = false, fan6pin = false, fan7pin = false; + bool pwm3pin = false, pwm4pin = false, pwm5pin = false; + bool pwm6pin = false, pwm7pin = false; + + /* Store SIO_REG_ENABLE for use during resume */ + sio_data->sio_select(sio_data, NCT6775_LD_HWM); + data->sio_reg_enable = sio_data->sio_inb(sio_data, SIO_REG_ENABLE); + + /* fan4 and fan5 share some pins with the GPIO and serial flash */ + if (data->kind == nct6775) { + int cr2c = sio_data->sio_inb(sio_data, 0x2c); + + fan3pin = cr2c & BIT(6); + pwm3pin = cr2c & BIT(7); + + /* On NCT6775, fan4 shares pins with the fdc interface */ + fan4pin = !(sio_data->sio_inb(sio_data, 0x2A) & 0x80); + } else if (data->kind == nct6776) { + bool gpok = sio_data->sio_inb(sio_data, 0x27) & 0x80; + const char *board_vendor, *board_name; + + board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (board_name && board_vendor && + !strcmp(board_vendor, "ASRock")) { + /* + * Auxiliary fan monitoring is not enabled on ASRock + * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode. + * Observed with BIOS version 2.00. + */ + if (!strcmp(board_name, "Z77 Pro4-M")) { + if ((data->sio_reg_enable & 0xe0) != 0xe0) { + data->sio_reg_enable |= 0xe0; + sio_data->sio_outb(sio_data, SIO_REG_ENABLE, + data->sio_reg_enable); + } + } + } + + if (data->sio_reg_enable & 0x80) + fan3pin = gpok; + else + fan3pin = !(sio_data->sio_inb(sio_data, 0x24) & 0x40); + + if (data->sio_reg_enable & 0x40) + fan4pin = gpok; + else + fan4pin = sio_data->sio_inb(sio_data, 0x1C) & 0x01; + + if (data->sio_reg_enable & 0x20) + fan5pin = gpok; + else + fan5pin = sio_data->sio_inb(sio_data, 0x1C) & 0x02; + + fan4min = fan4pin; + pwm3pin = fan3pin; + } else if (data->kind == nct6106) { + int cr24 = sio_data->sio_inb(sio_data, 0x24); + + fan3pin = !(cr24 & 0x80); + pwm3pin = cr24 & 0x08; + } else if (data->kind == nct6116) { + int cr1a = sio_data->sio_inb(sio_data, 0x1a); + int cr1b = sio_data->sio_inb(sio_data, 0x1b); + int cr24 = sio_data->sio_inb(sio_data, 0x24); + int cr2a = sio_data->sio_inb(sio_data, 0x2a); + int cr2b = sio_data->sio_inb(sio_data, 0x2b); + int cr2f = sio_data->sio_inb(sio_data, 0x2f); + + fan3pin = !(cr2b & 0x10); + fan4pin = (cr2b & 0x80) || // pin 1(2) + (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66) + fan5pin = (cr2b & 0x80) || // pin 126(127) + (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96) + + pwm3pin = fan3pin && (cr24 & 0x08); + pwm4pin = fan4pin; + pwm5pin = fan5pin; + } else { + /* + * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, + * NCT6797D, NCT6798D + */ + int cr1a = sio_data->sio_inb(sio_data, 0x1a); + int cr1b = sio_data->sio_inb(sio_data, 0x1b); + int cr1c = sio_data->sio_inb(sio_data, 0x1c); + int cr1d = sio_data->sio_inb(sio_data, 0x1d); + int cr2a = sio_data->sio_inb(sio_data, 0x2a); + int cr2b = sio_data->sio_inb(sio_data, 0x2b); + int cr2d = sio_data->sio_inb(sio_data, 0x2d); + int cr2f = sio_data->sio_inb(sio_data, 0x2f); + bool dsw_en = cr2f & BIT(3); + bool ddr4_en = cr2f & BIT(4); + int cre0; + int creb; + int cred; + + sio_data->sio_select(sio_data, NCT6775_LD_12); + cre0 = sio_data->sio_inb(sio_data, 0xe0); + creb = sio_data->sio_inb(sio_data, 0xeb); + cred = sio_data->sio_inb(sio_data, 0xed); + + fan3pin = !(cr1c & BIT(5)); + fan4pin = !(cr1c & BIT(6)); + fan5pin = !(cr1c & BIT(7)); + + pwm3pin = !(cr1c & BIT(0)); + pwm4pin = !(cr1c & BIT(1)); + pwm5pin = !(cr1c & BIT(2)); + + switch (data->kind) { + case nct6791: + fan6pin = cr2d & BIT(1); + pwm6pin = cr2d & BIT(0); + break; + case nct6792: + fan6pin = !dsw_en && (cr2d & BIT(1)); + pwm6pin = !dsw_en && (cr2d & BIT(0)); + break; + case nct6793: + fan5pin |= cr1b & BIT(5); + fan5pin |= creb & BIT(5); + + fan6pin = !dsw_en && (cr2d & BIT(1)); + fan6pin |= creb & BIT(3); + + pwm5pin |= cr2d & BIT(7); + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm6pin = !dsw_en && (cr2d & BIT(0)); + pwm6pin |= creb & BIT(2); + break; + case nct6795: + fan5pin |= cr1b & BIT(5); + fan5pin |= creb & BIT(5); + + fan6pin = (cr2a & BIT(4)) && + (!dsw_en || (cred & BIT(4))); + fan6pin |= creb & BIT(3); + + pwm5pin |= cr2d & BIT(7); + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); + pwm6pin |= creb & BIT(2); + break; + case nct6796: + fan5pin |= cr1b & BIT(5); + fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0)); + fan5pin |= creb & BIT(5); + + fan6pin = (cr2a & BIT(4)) && + (!dsw_en || (cred & BIT(4))); + fan6pin |= creb & BIT(3); + + fan7pin = !(cr2b & BIT(2)); + + pwm5pin |= cr2d & BIT(7); + pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0)); + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); + pwm6pin |= creb & BIT(2); + + pwm7pin = !(cr1d & (BIT(2) | BIT(3))); + break; + case nct6797: + fan5pin |= !ddr4_en && (cr1b & BIT(5)); + fan5pin |= creb & BIT(5); + + fan6pin = cr2a & BIT(4); + fan6pin |= creb & BIT(3); + + fan7pin = cr1a & BIT(1); + + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + pwm5pin |= !ddr4_en && (cr2d & BIT(7)); + + pwm6pin = creb & BIT(2); + pwm6pin |= cred & BIT(2); + + pwm7pin = cr1d & BIT(4); + break; + case nct6798: + fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3)); + fan6pin |= cr2a & BIT(4); + fan6pin |= creb & BIT(5); + + fan7pin = cr1b & BIT(5); + fan7pin |= !(cr2b & BIT(2)); + fan7pin |= creb & BIT(3); + + pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4)); + pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3)); + pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm7pin = !(cr1d & (BIT(2) | BIT(3))); + pwm7pin |= cr2d & BIT(7); + pwm7pin |= creb & BIT(2); + break; + default: /* NCT6779D */ + break; + } + + fan4min = fan4pin; + } + + /* fan 1 and 2 (0x03) are always present */ + data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) | + (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6); + data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) | + (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6); + data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | + (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6); +} + +static ssize_t +cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + +static DEVICE_ATTR_RO(cpu0_vid); + +/* Case open detection */ + +static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; +static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; + +static ssize_t +clear_caseopen(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct nct6775_sio_data *sio_data = data->driver_data; + int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE; + unsigned long val; + u8 reg; + int ret; + + if (kstrtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + /* + * Use CR registers to clear caseopen status. + * The CR registers are the same for all chips, and not all chips + * support clearing the caseopen status through "regular" registers. + */ + ret = sio_data->sio_enter(sio_data); + if (ret) { + count = ret; + goto error; + } + + sio_data->sio_select(sio_data, NCT6775_LD_ACPI); + reg = sio_data->sio_inb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr]); + reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr]; + sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr]; + sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + sio_data->sio_exit(sio_data); + + data->valid = false; /* Force cache refresh */ +error: + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(intrusion0_alarm, 0644, nct6775_show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE); +static SENSOR_DEVICE_ATTR(intrusion1_alarm, 0644, nct6775_show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE + 1); +static SENSOR_DEVICE_ATTR(intrusion0_beep, 0644, nct6775_show_beep, + nct6775_store_beep, INTRUSION_ALARM_BASE); +static SENSOR_DEVICE_ATTR(intrusion1_beep, 0644, nct6775_show_beep, + nct6775_store_beep, INTRUSION_ALARM_BASE + 1); +static SENSOR_DEVICE_ATTR(beep_enable, 0644, nct6775_show_beep, + nct6775_store_beep, BEEP_ENABLE_BASE); + +static umode_t nct6775_other_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = kobj_to_dev(kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + + if (index == 0 && !data->have_vid) + return 0; + + if (index == 1 || index == 2) { + if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0) + return 0; + } + + if (index == 3 || index == 4) { + if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0) + return 0; + } + + return nct6775_attr_mode(data, attr); +} + +/* + * nct6775_other_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct attribute *nct6775_attributes_other[] = { + &dev_attr_cpu0_vid.attr, /* 0 */ + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */ + &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */ + &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */ + &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */ + &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */ + + NULL +}; + +static const struct attribute_group nct6775_group_other = { + .attrs = nct6775_attributes_other, + .is_visible = nct6775_other_is_visible, +}; + +static int nct6775_platform_probe_init(struct nct6775_data *data) +{ + int err; + u8 cr2a; + struct nct6775_sio_data *sio_data = data->driver_data; + + err = sio_data->sio_enter(sio_data); + if (err) + return err; + + cr2a = sio_data->sio_inb(sio_data, 0x2a); + switch (data->kind) { + case nct6775: + data->have_vid = (cr2a & 0x40); + break; + case nct6776: + data->have_vid = (cr2a & 0x60) == 0x40; + break; + case nct6106: + case nct6116: + case nct6779: + case nct6791: + case nct6792: + case nct6793: + case nct6795: + case nct6796: + case nct6797: + case nct6798: + break; + } + + /* + * Read VID value + * We can get the VID input values directly at logical device D 0xe3. + */ + if (data->have_vid) { + sio_data->sio_select(sio_data, NCT6775_LD_VID); + data->vid = sio_data->sio_inb(sio_data, 0xe3); + data->vrm = vid_which_vrm(); + } + + if (fan_debounce) { + u8 tmp; + + sio_data->sio_select(sio_data, NCT6775_LD_HWM); + tmp = sio_data->sio_inb(sio_data, + NCT6775_REG_CR_FAN_DEBOUNCE); + switch (data->kind) { + case nct6106: + case nct6116: + tmp |= 0xe0; + break; + case nct6775: + tmp |= 0x1e; + break; + case nct6776: + case nct6779: + tmp |= 0x3e; + break; + case nct6791: + case nct6792: + case nct6793: + case nct6795: + case nct6796: + case nct6797: + case nct6798: + tmp |= 0x7e; + break; + } + sio_data->sio_outb(sio_data, NCT6775_REG_CR_FAN_DEBOUNCE, + tmp); + pr_info("Enabled fan debounce for chip %s\n", data->name); + } + + nct6775_check_fan_inputs(data, sio_data); + + sio_data->sio_exit(sio_data); + + return nct6775_add_attr_group(data, &nct6775_group_other); +} + +static const struct regmap_config nct6775_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + .reg_read = nct6775_reg_read, + .reg_write = nct6775_reg_write, +}; + +static const struct regmap_config nct6775_wmi_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + .reg_read = nct6775_wmi_reg_read, + .reg_write = nct6775_wmi_reg_write, +}; + +static int nct6775_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nct6775_sio_data *sio_data = dev_get_platdata(dev); + struct nct6775_data *data; + struct resource *res; + const struct regmap_config *regmapcfg; + + if (sio_data->access == access_direct) { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, DRVNAME)) + return -EBUSY; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->kind = sio_data->kind; + data->sioreg = sio_data->sioreg; + + if (sio_data->access == access_direct) { + data->addr = res->start; + regmapcfg = &nct6775_regmap_config; + } else { + regmapcfg = &nct6775_wmi_regmap_config; + } + + platform_set_drvdata(pdev, data); + + data->driver_data = sio_data; + data->driver_init = nct6775_platform_probe_init; + + return nct6775_probe(&pdev->dev, data, regmapcfg); +} + +static struct platform_driver nct6775_driver = { + .driver = { + .name = DRVNAME, + .pm = pm_sleep_ptr(&nct6775_dev_pm_ops), + }, + .probe = nct6775_platform_probe, +}; + +/* nct6775_find() looks for a '627 in the Super-I/O config space */ +static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) +{ + u16 val; + int err; + int addr; + + sio_data->access = access_direct; + sio_data->sioreg = sioaddr; + + err = sio_data->sio_enter(sio_data); + if (err) + return err; + + val = (sio_data->sio_inb(sio_data, SIO_REG_DEVID) << 8) | + sio_data->sio_inb(sio_data, SIO_REG_DEVID + 1); + if (force_id && val != 0xffff) + val = force_id; + + switch (val & SIO_ID_MASK) { + case SIO_NCT6106_ID: + sio_data->kind = nct6106; + break; + case SIO_NCT6116_ID: + sio_data->kind = nct6116; + break; + case SIO_NCT6775_ID: + sio_data->kind = nct6775; + break; + case SIO_NCT6776_ID: + sio_data->kind = nct6776; + break; + case SIO_NCT6779_ID: + sio_data->kind = nct6779; + break; + case SIO_NCT6791_ID: + sio_data->kind = nct6791; + break; + case SIO_NCT6792_ID: + sio_data->kind = nct6792; + break; + case SIO_NCT6793_ID: + sio_data->kind = nct6793; + break; + case SIO_NCT6795_ID: + sio_data->kind = nct6795; + break; + case SIO_NCT6796_ID: + sio_data->kind = nct6796; + break; + case SIO_NCT6797_ID: + sio_data->kind = nct6797; + break; + case SIO_NCT6798_ID: + sio_data->kind = nct6798; + break; + default: + if (val != 0xffff) + pr_debug("unsupported chip ID: 0x%04x\n", val); + sio_data->sio_exit(sio_data); + return -ENODEV; + } + + /* We have a known chip, find the HWM I/O address */ + sio_data->sio_select(sio_data, NCT6775_LD_HWM); + val = (sio_data->sio_inb(sio_data, SIO_REG_ADDR) << 8) + | sio_data->sio_inb(sio_data, SIO_REG_ADDR + 1); + addr = val & IOREGION_ALIGNMENT; + if (addr == 0) { + pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); + sio_data->sio_exit(sio_data); + return -ENODEV; + } + + /* Activate logical device if needed */ + val = sio_data->sio_inb(sio_data, SIO_REG_ENABLE); + if (!(val & 0x01)) { + pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); + sio_data->sio_outb(sio_data, SIO_REG_ENABLE, val | 0x01); + } + + if (sio_data->kind == nct6791 || sio_data->kind == nct6792 || + sio_data->kind == nct6793 || sio_data->kind == nct6795 || + sio_data->kind == nct6796 || sio_data->kind == nct6797 || + sio_data->kind == nct6798) + nct6791_enable_io_mapping(sio_data); + + sio_data->sio_exit(sio_data); + pr_info("Found %s or compatible chip at %#x:%#x\n", + nct6775_sio_names[sio_data->kind], sioaddr, addr); + + return addr; +} + +/* + * when Super-I/O functions move to a separate file, the Super-I/O + * bus will manage the lifetime of the device and this module will only keep + * track of the nct6775 driver. But since we use platform_device_alloc(), we + * must keep track of the device + */ +static struct platform_device *pdev[2]; + +static const char * const asus_wmi_boards[] = { + "PRO H410T", + "ProArt X570-CREATOR WIFI", + "Pro B550M-C", + "Pro WS X570-ACE", + "PRIME B360-PLUS", + "PRIME B460-PLUS", + "PRIME B550-PLUS", + "PRIME B550M-A", + "PRIME B550M-A (WI-FI)", + "PRIME H410M-R", + "PRIME X570-P", + "PRIME X570-PRO", + "ROG CROSSHAIR VIII DARK HERO", + "ROG CROSSHAIR VIII FORMULA", + "ROG CROSSHAIR VIII HERO", + "ROG CROSSHAIR VIII IMPACT", + "ROG STRIX B550-A GAMING", + "ROG STRIX B550-E GAMING", + "ROG STRIX B550-F GAMING", + "ROG STRIX B550-F GAMING (WI-FI)", + "ROG STRIX B550-F GAMING WIFI II", + "ROG STRIX B550-I GAMING", + "ROG STRIX B550-XE GAMING (WI-FI)", + "ROG STRIX X570-E GAMING", + "ROG STRIX X570-E GAMING WIFI II", + "ROG STRIX X570-F GAMING", + "ROG STRIX X570-I GAMING", + "ROG STRIX Z390-E GAMING", + "ROG STRIX Z390-F GAMING", + "ROG STRIX Z390-H GAMING", + "ROG STRIX Z390-I GAMING", + "ROG STRIX Z490-A GAMING", + "ROG STRIX Z490-E GAMING", + "ROG STRIX Z490-F GAMING", + "ROG STRIX Z490-G GAMING", + "ROG STRIX Z490-G GAMING (WI-FI)", + "ROG STRIX Z490-H GAMING", + "ROG STRIX Z490-I GAMING", + "TUF GAMING B550M-PLUS", + "TUF GAMING B550M-PLUS (WI-FI)", + "TUF GAMING B550-PLUS", + "TUF GAMING B550-PLUS WIFI II", + "TUF GAMING B550-PRO", + "TUF GAMING X570-PLUS", + "TUF GAMING X570-PLUS (WI-FI)", + "TUF GAMING X570-PRO (WI-FI)", + "TUF GAMING Z490-PLUS", + "TUF GAMING Z490-PLUS (WI-FI)", +}; + +static int __init sensors_nct6775_platform_init(void) +{ + int i, err; + bool found = false; + int address; + struct resource res; + struct nct6775_sio_data sio_data; + int sioaddr[2] = { 0x2e, 0x4e }; + enum sensor_access access = access_direct; + const char *board_vendor, *board_name; + u8 tmp; + + err = platform_driver_register(&nct6775_driver); + if (err) + return err; + + board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (board_name && board_vendor && + !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) { + err = match_string(asus_wmi_boards, ARRAY_SIZE(asus_wmi_boards), + board_name); + if (err >= 0) { + /* if reading chip id via WMI succeeds, use WMI */ + if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) { + pr_info("Using Asus WMI to access %#x chip.\n", tmp); + access = access_asuswmi; + } else { + pr_err("Can't read ChipID by Asus WMI.\n"); + } + } + } + + /* + * initialize sio_data->kind and sio_data->sioreg. + * + * when Super-I/O functions move to a separate file, the Super-I/O + * driver will probe 0x2e and 0x4e and auto-detect the presence of a + * nct6775 hardware monitor, and call probe() + */ + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + sio_data.sio_outb = superio_outb; + sio_data.sio_inb = superio_inb; + sio_data.sio_select = superio_select; + sio_data.sio_enter = superio_enter; + sio_data.sio_exit = superio_exit; + + address = nct6775_find(sioaddr[i], &sio_data); + if (address <= 0) + continue; + + found = true; + + sio_data.access = access; + + if (access == access_asuswmi) { + sio_data.sio_outb = superio_wmi_outb; + sio_data.sio_inb = superio_wmi_inb; + sio_data.sio_select = superio_wmi_select; + sio_data.sio_enter = superio_wmi_enter; + sio_data.sio_exit = superio_wmi_exit; + } + + pdev[i] = platform_device_alloc(DRVNAME, address); + if (!pdev[i]) { + err = -ENOMEM; + goto exit_device_unregister; + } + + err = platform_device_add_data(pdev[i], &sio_data, + sizeof(struct nct6775_sio_data)); + if (err) + goto exit_device_put; + + if (sio_data.access == access_direct) { + memset(&res, 0, sizeof(res)); + res.name = DRVNAME; + res.start = address + IOREGION_OFFSET; + res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; + res.flags = IORESOURCE_IO; + + err = acpi_check_resource_conflict(&res); + if (err) { + platform_device_put(pdev[i]); + pdev[i] = NULL; + continue; + } + + err = platform_device_add_resources(pdev[i], &res, 1); + if (err) + goto exit_device_put; + } + + /* platform_device_add calls probe() */ + err = platform_device_add(pdev[i]); + if (err) + goto exit_device_put; + } + if (!found) { + err = -ENODEV; + goto exit_unregister; + } + + return 0; + +exit_device_put: + platform_device_put(pdev[i]); +exit_device_unregister: + while (i--) + platform_device_unregister(pdev[i]); +exit_unregister: + platform_driver_unregister(&nct6775_driver); + return err; +} + +static void __exit sensors_nct6775_platform_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pdev); i++) + platform_device_unregister(pdev[i]); + platform_driver_unregister(&nct6775_driver); +} + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("Platform driver for NCT6775F and compatible chips"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(HWMON_NCT6775); + +module_init(sensors_nct6775_platform_init); +module_exit(sensors_nct6775_platform_exit); diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h new file mode 100644 index 000000000000..be41848c3cd2 --- /dev/null +++ b/drivers/hwmon/nct6775.h @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __HWMON_NCT6775_H__ +#define __HWMON_NCT6775_H__ + +#include <linux/types.h> + +enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, + nct6793, nct6795, nct6796, nct6797, nct6798 }; +enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; + +#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ +#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */ +#define NUM_TSI_TEMP 8 /* Max number of TSI temp register pairs */ + +#define NUM_REG_ALARM 7 /* Max number of alarm registers */ +#define NUM_REG_BEEP 5 /* Max number of beep registers */ + +#define NUM_FAN 7 + +struct nct6775_data { + int addr; /* IO base of hw monitor block */ + int sioreg; /* SIO register address */ + enum kinds kind; + const char *name; + + const struct attribute_group *groups[7]; + u8 num_groups; + + u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit, 4=temp_lcrit + */ + u8 temp_src[NUM_TEMP]; + u16 reg_temp_config[NUM_TEMP]; + const char * const *temp_label; + u32 temp_mask; + u32 virt_temp_mask; + + u16 REG_CONFIG; + u16 REG_VBAT; + u16 REG_DIODE; + u8 DIODE_MASK; + + const s8 *ALARM_BITS; + const s8 *BEEP_BITS; + + const u16 *REG_VIN; + const u16 *REG_IN_MINMAX[2]; + + const u16 *REG_TARGET; + const u16 *REG_FAN; + const u16 *REG_FAN_MODE; + const u16 *REG_FAN_MIN; + const u16 *REG_FAN_PULSES; + const u16 *FAN_PULSE_SHIFT; + const u16 *REG_FAN_TIME[3]; + + const u16 *REG_TOLERANCE_H; + + const u8 *REG_PWM_MODE; + const u8 *PWM_MODE_MASK; + + const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, + * [3]=pwm_max, [4]=pwm_step, + * [5]=weight_duty_step, [6]=weight_duty_base + */ + const u16 *REG_PWM_READ; + + const u16 *REG_CRITICAL_PWM_ENABLE; + u8 CRITICAL_PWM_ENABLE_MASK; + const u16 *REG_CRITICAL_PWM; + + const u16 *REG_AUTO_TEMP; + const u16 *REG_AUTO_PWM; + + const u16 *REG_CRITICAL_TEMP; + const u16 *REG_CRITICAL_TEMP_TOLERANCE; + + const u16 *REG_TEMP_SOURCE; /* temp register sources */ + const u16 *REG_TEMP_SEL; + const u16 *REG_WEIGHT_TEMP_SEL; + const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */ + + const u16 *REG_TEMP_OFFSET; + + const u16 *REG_ALARM; + const u16 *REG_BEEP; + + const u16 *REG_TSI_TEMP; + + unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); + unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); + + struct mutex update_lock; + bool valid; /* true if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* Register values */ + u8 bank; /* current register bank */ + u8 in_num; /* number of in inputs we have */ + u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ + unsigned int rpm[NUM_FAN]; + u16 fan_min[NUM_FAN]; + u8 fan_pulses[NUM_FAN]; + u8 fan_div[NUM_FAN]; + u8 has_pwm; + u8 has_fan; /* some fan inputs can be disabled */ + u8 has_fan_min; /* some fans don't have min register */ + bool has_fan_div; + + u8 num_temp_alarms; /* 2, 3, or 6 */ + u8 num_temp_beeps; /* 2, 3, or 6 */ + u8 temp_fixed_num; /* 3 or 6 */ + u8 temp_type[NUM_TEMP_FIXED]; + s8 temp_offset[NUM_TEMP_FIXED]; + s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit, 4=temp_lcrit + */ + s16 tsi_temp[NUM_TSI_TEMP]; + u64 alarms; + u64 beeps; + + u8 pwm_num; /* number of pwm */ + u8 pwm_mode[NUM_FAN]; /* 0->DC variable voltage, + * 1->PWM variable duty cycle + */ + enum pwm_enable pwm_enable[NUM_FAN]; + /* 0->off + * 1->manual + * 2->thermal cruise mode (also called SmartFan I) + * 3->fan speed cruise mode + * 4->SmartFan III + * 5->enhanced variable thermal cruise (SmartFan IV) + */ + u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, + * [3]=pwm_max, [4]=pwm_step, + * [5]=weight_duty_step, [6]=weight_duty_base + */ + + u8 target_temp[NUM_FAN]; + u8 target_temp_mask; + u32 target_speed[NUM_FAN]; + u32 target_speed_tolerance[NUM_FAN]; + u8 speed_tolerance_limit; + + u8 temp_tolerance[2][NUM_FAN]; + u8 tolerance_mask; + + u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */ + + /* Automatic fan speed control registers */ + int auto_pwm_num; + u8 auto_pwm[NUM_FAN][7]; + u8 auto_temp[NUM_FAN][7]; + u8 pwm_temp_sel[NUM_FAN]; + u8 pwm_weight_temp_sel[NUM_FAN]; + u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol, + * 2->temp_base + */ + + u8 vid; + u8 vrm; + + bool have_vid; + + u16 have_temp; + u16 have_temp_fixed; + u16 have_tsi_temp; + u16 have_in; + + /* Remember extra register values over suspend/resume */ + u8 vbat; + u8 fandiv1; + u8 fandiv2; + u8 sio_reg_enable; + + struct regmap *regmap; + bool read_only; + + /* driver-specific (platform, i2c) initialization hook and data */ + int (*driver_init)(struct nct6775_data *data); + void *driver_data; +}; + +static inline int nct6775_read_value(struct nct6775_data *data, u16 reg, u16 *value) +{ + unsigned int tmp; + int ret = regmap_read(data->regmap, reg, &tmp); + + if (!ret) + *value = tmp; + return ret; +} + +static inline int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) +{ + return regmap_write(data->regmap, reg, value); +} + +struct nct6775_data *nct6775_update_device(struct device *dev); + +bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg); +int nct6775_probe(struct device *dev, struct nct6775_data *data, + const struct regmap_config *regmapcfg); + +ssize_t nct6775_show_alarm(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t nct6775_show_beep(struct device *dev, struct device_attribute *attr, char *buf); +ssize_t nct6775_store_beep(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count); + +static inline int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value) +{ + if (!nct6775_reg_is_word_sized(data, reg)) + value >>= 8; + return nct6775_write_value(data, reg, value); +} + +static inline umode_t nct6775_attr_mode(struct nct6775_data *data, struct attribute *attr) +{ + return data->read_only ? (attr->mode & ~0222) : attr->mode; +} + +static inline int +nct6775_add_attr_group(struct nct6775_data *data, const struct attribute_group *group) +{ + /* Need to leave a NULL terminator at the end of data->groups */ + if (data->num_groups == ARRAY_SIZE(data->groups) - 1) + return -ENOBUFS; + + data->groups[data->num_groups++] = group; + return 0; +} + +#define NCT6775_REG_BANK 0x4E +#define NCT6775_REG_CONFIG 0x40 + +#define NCT6775_REG_FANDIV1 0x506 +#define NCT6775_REG_FANDIV2 0x507 + +#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28 + +#define FAN_ALARM_BASE 16 +#define TEMP_ALARM_BASE 24 +#define INTRUSION_ALARM_BASE 30 +#define BEEP_ENABLE_BASE 15 + +/* + * Not currently used: + * REG_MAN_ID has the value 0x5ca3 for all supported chips. + * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. + * REG_MAN_ID is at port 0x4f + * REG_CHIP_ID is at port 0x58 + */ + +#endif /* __HWMON_NCT6775_H__ */ diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 2e97e56c72c7..a175f8283695 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -52,6 +52,23 @@ static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { #define REG_VERSION_ID 0xff /* + * Resistance temperature detector (RTD) modes according to 7.2.32 Mode + * Selection Register + */ +#define RTD_MODE_CURRENT 0x1 +#define RTD_MODE_THERMISTOR 0x2 +#define RTD_MODE_VOLTAGE 0x3 + +#define MODE_RTD_MASK 0x3 +#define MODE_LTD_EN 0x40 + +/* + * Bit offset for sensors modes in REG_MODE. + * Valid for index 0..2, indicating RTD1..3. + */ +#define MODE_BIT_OFFSET_RTD(index) ((index) * 2) + +/* * Data structures and manipulation thereof */ @@ -679,7 +696,7 @@ static struct attribute *nct7802_temp_attrs[] = { static umode_t nct7802_temp_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct7802_data *data = dev_get_drvdata(dev); unsigned int reg; int err; @@ -778,7 +795,7 @@ static struct attribute *nct7802_in_attrs[] = { static umode_t nct7802_in_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct7802_data *data = dev_get_drvdata(dev); unsigned int reg; int err; @@ -853,7 +870,7 @@ static struct attribute *nct7802_fan_attrs[] = { static umode_t nct7802_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct nct7802_data *data = dev_get_drvdata(dev); int fan = index / 4; /* 4 attributes per fan */ unsigned int reg; @@ -1021,7 +1038,7 @@ static int nct7802_detect(struct i2c_client *client, if (reg < 0 || (reg & 0x3f)) return -ENODEV; - strlcpy(info->type, "nct7802", I2C_NAME_SIZE); + strscpy(info->type, "nct7802", I2C_NAME_SIZE); return 0; } @@ -1038,7 +1055,114 @@ static const struct regmap_config nct7802_regmap_config = { .volatile_reg = nct7802_regmap_is_volatile, }; -static int nct7802_init_chip(struct nct7802_data *data) +static int nct7802_get_channel_config(struct device *dev, + struct device_node *node, u8 *mode_mask, + u8 *mode_val) +{ + u32 reg; + const char *type_str, *md_str; + u8 md; + + if (!node->name || of_node_cmp(node->name, "channel")) + return 0; + + if (of_property_read_u32(node, "reg", ®)) { + dev_err(dev, "Could not read reg value for '%s'\n", + node->full_name); + return -EINVAL; + } + + if (reg > 3) { + dev_err(dev, "Invalid reg (%u) in '%s'\n", reg, + node->full_name); + return -EINVAL; + } + + if (reg == 0) { + if (!of_device_is_available(node)) + *mode_val &= ~MODE_LTD_EN; + else + *mode_val |= MODE_LTD_EN; + *mode_mask |= MODE_LTD_EN; + return 0; + } + + /* At this point we have reg >= 1 && reg <= 3 */ + + if (!of_device_is_available(node)) { + *mode_val &= ~(MODE_RTD_MASK << MODE_BIT_OFFSET_RTD(reg - 1)); + *mode_mask |= MODE_RTD_MASK << MODE_BIT_OFFSET_RTD(reg - 1); + return 0; + } + + if (of_property_read_string(node, "sensor-type", &type_str)) { + dev_err(dev, "No type for '%s'\n", node->full_name); + return -EINVAL; + } + + if (!strcmp(type_str, "voltage")) { + *mode_val |= (RTD_MODE_VOLTAGE & MODE_RTD_MASK) + << MODE_BIT_OFFSET_RTD(reg - 1); + *mode_mask |= MODE_RTD_MASK << MODE_BIT_OFFSET_RTD(reg - 1); + return 0; + } + + if (strcmp(type_str, "temperature")) { + dev_err(dev, "Invalid type '%s' for '%s'\n", type_str, + node->full_name); + return -EINVAL; + } + + if (reg == 3) { + /* RTD3 only supports thermistor mode */ + md = RTD_MODE_THERMISTOR; + } else { + if (of_property_read_string(node, "temperature-mode", + &md_str)) { + dev_err(dev, "No mode for '%s'\n", node->full_name); + return -EINVAL; + } + + if (!strcmp(md_str, "thermal-diode")) + md = RTD_MODE_CURRENT; + else if (!strcmp(md_str, "thermistor")) + md = RTD_MODE_THERMISTOR; + else { + dev_err(dev, "Invalid mode '%s' for '%s'\n", md_str, + node->full_name); + return -EINVAL; + } + } + + *mode_val |= (md & MODE_RTD_MASK) << MODE_BIT_OFFSET_RTD(reg - 1); + *mode_mask |= MODE_RTD_MASK << MODE_BIT_OFFSET_RTD(reg - 1); + + return 0; +} + +static int nct7802_configure_channels(struct device *dev, + struct nct7802_data *data) +{ + /* Enable local temperature sensor by default */ + u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN; + struct device_node *node; + int err; + + if (dev->of_node) { + for_each_child_of_node(dev->of_node, node) { + err = nct7802_get_channel_config(dev, node, &mode_mask, + &mode_val); + if (err) { + of_node_put(node); + return err; + } + } + } + + return regmap_update_bits(data->regmap, REG_MODE, mode_mask, mode_val); +} + +static int nct7802_init_chip(struct device *dev, struct nct7802_data *data) { int err; @@ -1047,8 +1171,7 @@ static int nct7802_init_chip(struct nct7802_data *data) if (err) return err; - /* Enable local temperature sensor */ - err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40); + err = nct7802_configure_channels(dev, data); if (err) return err; @@ -1056,8 +1179,7 @@ static int nct7802_init_chip(struct nct7802_data *data) return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03); } -static int nct7802_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int nct7802_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct nct7802_data *data; @@ -1075,7 +1197,7 @@ static int nct7802_probe(struct i2c_client *client, mutex_init(&data->access_lock); mutex_init(&data->in_alarm_lock); - ret = nct7802_init_chip(data); + ret = nct7802_init_chip(dev, data); if (ret < 0) return ret; @@ -1101,7 +1223,7 @@ static struct i2c_driver nct7802_driver = { .name = DRVNAME, }, .detect = nct7802_detect, - .probe = nct7802_probe, + .probe_new = nct7802_probe, .id_table = nct7802_idtable, .address_list = nct7802_address_list, }; diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 281c81edabc6..ecc5db0011a3 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -7,6 +7,14 @@ * * Copyright (c) 2019 Advantech * Author: Amy.Shih <amy.shih@advantech.com.tw> + * + * Copyright (c) 2020 Advantech + * Author: Yuechao Zhao <yuechao.zhao@advantech.com.cn> + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp #dts chip ID + * nct7904d 20 12 4 5 8 0xc5 */ #include <linux/module.h> @@ -15,6 +23,7 @@ #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/hwmon.h> +#include <linux/watchdog.h> #define VENDOR_ID_REG 0x7A /* Any bank */ #define NUVOTON_ID 0x50 @@ -36,6 +45,7 @@ #define FANCTL_MAX 4 /* Counted from 1 */ #define TCPU_MAX 8 /* Counted from 1 */ #define TEMP_MAX 4 /* Counted from 1 */ +#define SMI_STS_MAX 10 /* Counted from 1 */ #define VT_ADC_CTRL0_REG 0x20 /* Bank 0 */ #define VT_ADC_CTRL1_REG 0x21 /* Bank 0 */ @@ -82,18 +92,42 @@ #define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */ #define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */ +#define WDT_LOCK_REG 0xE0 /* W/O Lock Watchdog Register */ +#define WDT_EN_REG 0xE1 /* R/O Watchdog Enable Register */ +#define WDT_STS_REG 0xE2 /* R/O Watchdog Status Register */ +#define WDT_TIMER_REG 0xE3 /* R/W Watchdog Timer Register */ +#define WDT_SOFT_EN 0x55 /* Enable soft watchdog timer */ +#define WDT_SOFT_DIS 0xAA /* Disable soft watchdog timer */ + #define VOLT_MONITOR_MODE 0x0 #define THERMAL_DIODE_MODE 0x1 #define THERMISTOR_MODE 0x3 #define ENABLE_TSI BIT(1) +#define WATCHDOG_TIMEOUT 1 /* 1 minute default timeout */ + +/*The timeout range is 1-255 minutes*/ +#define MIN_TIMEOUT (1 * 60) +#define MAX_TIMEOUT (255 * 60) + +static int timeout; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes. 1 <= timeout <= 255, default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) "."); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + static const unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END }; struct nct7904_data { struct i2c_client *client; + struct watchdog_device wdt; struct mutex bank_lock; int bank_sel; u32 fanin_mask; @@ -197,7 +231,7 @@ static int nct7904_read_fan(struct device *dev, u32 attr, int channel, if (ret < 0) return ret; cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); - if (cnt == 0x1fff) + if (cnt == 0 || cnt == 0x1fff) rpm = 0; else rpm = 1350000 / cnt; @@ -209,7 +243,7 @@ static int nct7904_read_fan(struct device *dev, u32 attr, int channel, if (ret < 0) return ret; cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); - if (cnt == 0x1fff) + if (cnt == 0 || cnt == 0x1fff) rpm = 0; else rpm = 1350000 / cnt; @@ -356,6 +390,7 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel, struct nct7904_data *data = dev_get_drvdata(dev); int ret, temp; unsigned int reg1, reg2, reg3; + s8 temps; switch (attr) { case hwmon_temp_input: @@ -461,7 +496,8 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel, if (ret < 0) return ret; - *val = ret * 1000; + temps = ret; + *val = temps * 1000; return 0; } @@ -762,7 +798,7 @@ static int nct7904_detect(struct i2c_client *client, (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00) return -ENODEV; - strlcpy(info->type, "nct7904", I2C_NAME_SIZE); + strscpy(info->type, "nct7904", I2C_NAME_SIZE); return 0; } @@ -820,6 +856,10 @@ static const struct hwmon_channel_info *nct7904_info[] = { HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM), HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, @@ -853,6 +893,18 @@ static const struct hwmon_channel_info *nct7904_info[] = { HWMON_T_CRIT_HYST, HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | HWMON_T_CRIT_HYST), NULL }; @@ -868,8 +920,96 @@ static const struct hwmon_chip_info nct7904_chip_info = { .info = nct7904_info, }; -static int nct7904_probe(struct i2c_client *client, - const struct i2c_device_id *id) +/* + * Watchdog Function + */ +static int nct7904_wdt_start(struct watchdog_device *wdt) +{ + struct nct7904_data *data = watchdog_get_drvdata(wdt); + + /* Enable soft watchdog timer */ + return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_EN); +} + +static int nct7904_wdt_stop(struct watchdog_device *wdt) +{ + struct nct7904_data *data = watchdog_get_drvdata(wdt); + + return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS); +} + +static int nct7904_wdt_set_timeout(struct watchdog_device *wdt, + unsigned int timeout) +{ + struct nct7904_data *data = watchdog_get_drvdata(wdt); + /* + * The NCT7904 is very special in watchdog function. + * Its minimum unit is minutes. And wdt->timeout needs + * to match the actual timeout selected. So, this needs + * to be: wdt->timeout = timeout / 60 * 60. + * For example, if the user configures a timeout of + * 119 seconds, the actual timeout will be 60 seconds. + * So, wdt->timeout must then be set to 60 seconds. + */ + wdt->timeout = timeout / 60 * 60; + + return nct7904_write_reg(data, BANK_0, WDT_TIMER_REG, + wdt->timeout / 60); +} + +static int nct7904_wdt_ping(struct watchdog_device *wdt) +{ + /* + * Note: + * NCT7904 does not support refreshing WDT_TIMER_REG register when + * the watchdog is active. Please disable watchdog before feeding + * the watchdog and enable it again. + */ + struct nct7904_data *data = watchdog_get_drvdata(wdt); + int ret; + + /* Disable soft watchdog timer */ + ret = nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS); + if (ret < 0) + return ret; + + /* feed watchdog */ + ret = nct7904_write_reg(data, BANK_0, WDT_TIMER_REG, wdt->timeout / 60); + if (ret < 0) + return ret; + + /* Enable soft watchdog timer */ + return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_EN); +} + +static unsigned int nct7904_wdt_get_timeleft(struct watchdog_device *wdt) +{ + struct nct7904_data *data = watchdog_get_drvdata(wdt); + int ret; + + ret = nct7904_read_reg(data, BANK_0, WDT_TIMER_REG); + if (ret < 0) + return 0; + + return ret * 60; +} + +static const struct watchdog_info nct7904_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "nct7904 watchdog", +}; + +static const struct watchdog_ops nct7904_wdt_ops = { + .owner = THIS_MODULE, + .start = nct7904_wdt_start, + .stop = nct7904_wdt_stop, + .ping = nct7904_wdt_ping, + .set_timeout = nct7904_wdt_set_timeout, + .get_timeleft = nct7904_wdt_get_timeleft, +}; + +static int nct7904_probe(struct i2c_client *client) { struct nct7904_data *data; struct device *hwmon_dev; @@ -988,10 +1128,36 @@ static int nct7904_probe(struct i2c_client *client, data->fan_mode[i] = ret; } + /* Read all of SMI status register to clear alarms */ + for (i = 0; i < SMI_STS_MAX; i++) { + ret = nct7904_read_reg(data, BANK_0, SMI_STS1_REG + i); + if (ret < 0) + return ret; + } + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &nct7904_chip_info, NULL); - return PTR_ERR_OR_ZERO(hwmon_dev); + ret = PTR_ERR_OR_ZERO(hwmon_dev); + if (ret) + return ret; + + /* Watchdog initialization */ + data->wdt.ops = &nct7904_wdt_ops; + data->wdt.info = &nct7904_wdt_info; + + data->wdt.timeout = WATCHDOG_TIMEOUT * 60; /* Set default timeout */ + data->wdt.min_timeout = MIN_TIMEOUT; + data->wdt.max_timeout = MAX_TIMEOUT; + data->wdt.parent = &client->dev; + + watchdog_init_timeout(&data->wdt, timeout * 60, &client->dev); + watchdog_set_nowayout(&data->wdt, nowayout); + watchdog_set_drvdata(&data->wdt, data); + + watchdog_stop_on_unregister(&data->wdt); + + return devm_watchdog_register_device(dev, &data->wdt); } static const struct i2c_device_id nct7904_id[] = { @@ -1005,7 +1171,7 @@ static struct i2c_driver nct7904_driver = { .driver = { .name = "nct7904", }, - .probe = nct7904_probe, + .probe_new = nct7904_probe, .id_table = nct7904_id, .detect = nct7904_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 3aad62a0e661..9c9e9f4ccb9e 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -8,22 +8,24 @@ #include <linux/slab.h> #include <linux/module.h> -#include <linux/pm_runtime.h> #include <linux/math64.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/err.h> -#include <linux/of.h> -#include <linux/of_device.h> - -#include <linux/platform_data/ntc_thermistor.h> - -#include <linux/iio/iio.h> -#include <linux/iio/machine.h> -#include <linux/iio/driver.h> +#include <linux/fixp-arith.h> #include <linux/iio/consumer.h> - #include <linux/hwmon.h> +enum ntc_thermistor_type { + TYPE_B57330V2103, + TYPE_B57891S0103, + TYPE_NCPXXWB473, + TYPE_NCPXXWF104, + TYPE_NCPXXWL333, + TYPE_NCPXXXH103, +}; + struct ntc_compensation { int temp_c; unsigned int ohm; @@ -43,6 +45,7 @@ enum { NTC_NCP15XH103, NTC_NCP18WB473, NTC_NCP21WB473, + NTC_SSG1404001221, NTC_LAST, }; @@ -56,6 +59,7 @@ static const struct platform_device_id ntc_thermistor_id[] = { [NTC_NCP15XH103] = { "ncp15xh103", TYPE_NCPXXXH103 }, [NTC_NCP18WB473] = { "ncp18wb473", TYPE_NCPXXWB473 }, [NTC_NCP21WB473] = { "ncp21wb473", TYPE_NCPXXWB473 }, + [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 }, [NTC_LAST] = { }, }; @@ -316,123 +320,56 @@ static const struct ntc_type ntc_type[] = { NTC_TYPE(TYPE_NCPXXXH103, ncpXXxh103), }; +/* + * pullup_uV, pullup_ohm, pulldown_ohm, and connect are required. + * + * How to setup pullup_ohm, pulldown_ohm, and connect is + * described at Documentation/hwmon/ntc_thermistor.rst + * + * pullup/down_ohm: 0 for infinite / not-connected + * + * chan: iio_channel pointer to communicate with the ADC which the + * thermistor is using for conversion of the analog values. + */ struct ntc_data { - struct ntc_thermistor_platform_data *pdata; const struct ntc_compensation *comp; int n_comp; + unsigned int pullup_uv; + unsigned int pullup_ohm; + unsigned int pulldown_ohm; + enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect; + struct iio_channel *chan; }; -#if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO) -static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata) +static int ntc_adc_iio_read(struct ntc_data *data) { - struct iio_channel *channel = pdata->chan; - int raw, uv, ret; + struct iio_channel *channel = data->chan; + int uv, ret; - ret = iio_read_channel_raw(channel, &raw); + ret = iio_read_channel_processed_scale(channel, &uv, 1000); if (ret < 0) { - pr_err("read channel() error: %d\n", ret); - return ret; - } + int raw; - ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000); - if (ret < 0) { - /* Assume 12 bit ADC with vref at pullup_uv */ - uv = (pdata->pullup_uv * (s64)raw) >> 12; + /* + * This fallback uses a raw read and then + * assumes the ADC is 12 bits, scaling with + * a factor 1000 to get to microvolts. + */ + ret = iio_read_channel_raw(channel, &raw); + if (ret < 0) { + pr_err("read channel() error: %d\n", ret); + return ret; + } + ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000); + if (ret < 0) { + /* Assume 12 bit ADC with vref at pullup_uv */ + uv = (data->pullup_uv * (s64)raw) >> 12; + } } return uv; } -static const struct of_device_id ntc_match[] = { - { .compatible = "epcos,b57330v2103", - .data = &ntc_thermistor_id[NTC_B57330V2103]}, - { .compatible = "epcos,b57891s0103", - .data = &ntc_thermistor_id[NTC_B57891S0103] }, - { .compatible = "murata,ncp03wb473", - .data = &ntc_thermistor_id[NTC_NCP03WB473] }, - { .compatible = "murata,ncp03wf104", - .data = &ntc_thermistor_id[NTC_NCP03WF104] }, - { .compatible = "murata,ncp15wb473", - .data = &ntc_thermistor_id[NTC_NCP15WB473] }, - { .compatible = "murata,ncp15wl333", - .data = &ntc_thermistor_id[NTC_NCP15WL333] }, - { .compatible = "murata,ncp15xh103", - .data = &ntc_thermistor_id[NTC_NCP15XH103] }, - { .compatible = "murata,ncp18wb473", - .data = &ntc_thermistor_id[NTC_NCP18WB473] }, - { .compatible = "murata,ncp21wb473", - .data = &ntc_thermistor_id[NTC_NCP21WB473] }, - - /* Usage of vendor name "ntc" is deprecated */ - { .compatible = "ntc,ncp03wb473", - .data = &ntc_thermistor_id[NTC_NCP03WB473] }, - { .compatible = "ntc,ncp15wb473", - .data = &ntc_thermistor_id[NTC_NCP15WB473] }, - { .compatible = "ntc,ncp15wl333", - .data = &ntc_thermistor_id[NTC_NCP15WL333] }, - { .compatible = "ntc,ncp18wb473", - .data = &ntc_thermistor_id[NTC_NCP18WB473] }, - { .compatible = "ntc,ncp21wb473", - .data = &ntc_thermistor_id[NTC_NCP21WB473] }, - { }, -}; -MODULE_DEVICE_TABLE(of, ntc_match); - -static struct ntc_thermistor_platform_data * -ntc_thermistor_parse_dt(struct device *dev) -{ - struct iio_channel *chan; - enum iio_chan_type type; - struct device_node *np = dev->of_node; - struct ntc_thermistor_platform_data *pdata; - int ret; - - if (!np) - return NULL; - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - chan = devm_iio_channel_get(dev, NULL); - if (IS_ERR(chan)) - return ERR_CAST(chan); - - ret = iio_get_channel_type(chan, &type); - if (ret < 0) - return ERR_PTR(ret); - - if (type != IIO_VOLTAGE) - return ERR_PTR(-EINVAL); - - if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uv)) - return ERR_PTR(-ENODEV); - if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm)) - return ERR_PTR(-ENODEV); - if (of_property_read_u32(np, "pulldown-ohm", &pdata->pulldown_ohm)) - return ERR_PTR(-ENODEV); - - if (of_find_property(np, "connected-positive", NULL)) - pdata->connect = NTC_CONNECTED_POSITIVE; - else /* status change should be possible if not always on. */ - pdata->connect = NTC_CONNECTED_GROUND; - - pdata->chan = chan; - pdata->read_uv = ntc_adc_iio_read; - - return pdata; -} -#else -static struct ntc_thermistor_platform_data * -ntc_thermistor_parse_dt(struct device *dev) -{ - return NULL; -} - -#define ntc_match NULL - -#endif - static inline u64 div64_u64_safe(u64 dividend, u64 divisor) { if (divisor == 0 && dividend == 0) @@ -444,24 +381,23 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor) static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv) { - struct ntc_thermistor_platform_data *pdata = data->pdata; - u32 puv = pdata->pullup_uv; + u32 puv = data->pullup_uv; u64 n, puo, pdo; - puo = pdata->pullup_ohm; - pdo = pdata->pulldown_ohm; + puo = data->pullup_ohm; + pdo = data->pulldown_ohm; if (uv == 0) - return (pdata->connect == NTC_CONNECTED_POSITIVE) ? + return (data->connect == NTC_CONNECTED_POSITIVE) ? INT_MAX : 0; if (uv >= puv) - return (pdata->connect == NTC_CONNECTED_POSITIVE) ? + return (data->connect == NTC_CONNECTED_POSITIVE) ? 0 : INT_MAX; - if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0) + if (data->connect == NTC_CONNECTED_POSITIVE && puo == 0) n = div_u64(pdo * (puv - uv), uv); - else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0) + else if (data->connect == NTC_CONNECTED_GROUND && pdo == 0) n = div_u64(puo * uv, puv - uv); - else if (pdata->connect == NTC_CONNECTED_POSITIVE) + else if (data->connect == NTC_CONNECTED_POSITIVE) n = div64_u64_safe(pdo * puo * (puv - uv), puo * uv - pdo * (puv - uv)); else @@ -544,15 +480,16 @@ static int get_temp_mc(struct ntc_data *data, unsigned int ohm) int temp; lookup_comp(data, ohm, &low, &high); - if (low == high) { - /* Unable to use linear approximation */ - temp = data->comp[low].temp_c * 1000; - } else { - temp = data->comp[low].temp_c * 1000 + - ((data->comp[high].temp_c - data->comp[low].temp_c) * - 1000 * ((int)ohm - (int)data->comp[low].ohm)) / - ((int)data->comp[high].ohm - (int)data->comp[low].ohm); - } + /* + * First multiplying the table temperatures with 1000 to get to + * millicentigrades (which is what we want) and then interpolating + * will give the best precision. + */ + temp = fixp_linear_interpolate(data->comp[low].ohm, + data->comp[low].temp_c * 1000, + data->comp[high].ohm, + data->comp[high].temp_c * 1000, + ohm); return temp; } @@ -560,16 +497,10 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data) { int read_uv; - if (data->pdata->read_ohm) - return data->pdata->read_ohm(); - - if (data->pdata->read_uv) { - read_uv = data->pdata->read_uv(data->pdata); - if (read_uv < 0) - return read_uv; - return get_ohm_of_thermistor(data, read_uv); - } - return -EINVAL; + read_uv = ntc_adc_iio_read(data); + if (read_uv < 0) + return read_uv; + return get_ohm_of_thermistor(data, read_uv); } static int ntc_read(struct device *dev, enum hwmon_sensor_types type, @@ -631,58 +562,74 @@ static const struct hwmon_chip_info ntc_chip_info = { .info = ntc_info, }; -static int ntc_thermistor_probe(struct platform_device *pdev) +static int ntc_thermistor_parse_props(struct device *dev, + struct ntc_data *data) { - struct device *dev = &pdev->dev; - const struct of_device_id *of_id = - of_match_device(of_match_ptr(ntc_match), dev); - const struct platform_device_id *pdev_id; - struct ntc_thermistor_platform_data *pdata; - struct device *hwmon_dev; - struct ntc_data *data; + struct iio_channel *chan; + enum iio_chan_type type; + int ret; - pdata = ntc_thermistor_parse_dt(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - else if (pdata == NULL) - pdata = dev_get_platdata(dev); + chan = devm_iio_channel_get(dev, NULL); + if (IS_ERR(chan)) + return PTR_ERR(chan); - if (!pdata) { - dev_err(dev, "No platform init data supplied.\n"); - return -ENODEV; - } + ret = iio_get_channel_type(chan, &type); + if (ret < 0) + return ret; - /* Either one of the two is required. */ - if (!pdata->read_uv && !pdata->read_ohm) { - dev_err(dev, - "Both read_uv and read_ohm missing. Need either one of the two.\n"); + if (type != IIO_VOLTAGE) return -EINVAL; - } - if (pdata->read_uv && pdata->read_ohm) { - dev_warn(dev, - "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n"); - pdata->read_uv = NULL; - } + ret = device_property_read_u32(dev, "pullup-uv", &data->pullup_uv); + if (ret) + return dev_err_probe(dev, ret, "pullup-uv not specified\n"); - if (pdata->read_uv && (pdata->pullup_uv == 0 || - (pdata->pullup_ohm == 0 && pdata->connect == - NTC_CONNECTED_GROUND) || - (pdata->pulldown_ohm == 0 && pdata->connect == - NTC_CONNECTED_POSITIVE) || - (pdata->connect != NTC_CONNECTED_POSITIVE && - pdata->connect != NTC_CONNECTED_GROUND))) { - dev_err(dev, "Required data to use read_uv not supplied.\n"); - return -EINVAL; - } + ret = device_property_read_u32(dev, "pullup-ohm", &data->pullup_ohm); + if (ret) + return dev_err_probe(dev, ret, "pullup-ohm not specified\n"); + + ret = device_property_read_u32(dev, "pulldown-ohm", &data->pulldown_ohm); + if (ret) + return dev_err_probe(dev, ret, "pulldown-ohm not specified\n"); + + if (device_property_read_bool(dev, "connected-positive")) + data->connect = NTC_CONNECTED_POSITIVE; + else /* status change should be possible if not always on. */ + data->connect = NTC_CONNECTED_GROUND; + + data->chan = chan; + + return 0; +} + +static int ntc_thermistor_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct platform_device_id *pdev_id; + struct device *hwmon_dev; + struct ntc_data *data; + int ret; - data = devm_kzalloc(dev, sizeof(struct ntc_data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); + ret = ntc_thermistor_parse_props(dev, data); + if (ret) + return ret; - data->pdata = pdata; + if (data->pullup_uv == 0 || + (data->pullup_ohm == 0 && data->connect == + NTC_CONNECTED_GROUND) || + (data->pulldown_ohm == 0 && data->connect == + NTC_CONNECTED_POSITIVE) || + (data->connect != NTC_CONNECTED_POSITIVE && + data->connect != NTC_CONNECTED_GROUND)) { + dev_err(dev, "Required data to use NTC driver not supplied.\n"); + return -EINVAL; + } + + pdev_id = device_get_match_data(dev); if (pdev_id->driver_data >= ARRAY_SIZE(ntc_type)) { dev_err(dev, "Unknown device type: %lu(%s)\n", @@ -707,10 +654,47 @@ static int ntc_thermistor_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id ntc_match[] = { + { .compatible = "epcos,b57330v2103", + .data = &ntc_thermistor_id[NTC_B57330V2103]}, + { .compatible = "epcos,b57891s0103", + .data = &ntc_thermistor_id[NTC_B57891S0103] }, + { .compatible = "murata,ncp03wb473", + .data = &ntc_thermistor_id[NTC_NCP03WB473] }, + { .compatible = "murata,ncp03wf104", + .data = &ntc_thermistor_id[NTC_NCP03WF104] }, + { .compatible = "murata,ncp15wb473", + .data = &ntc_thermistor_id[NTC_NCP15WB473] }, + { .compatible = "murata,ncp15wl333", + .data = &ntc_thermistor_id[NTC_NCP15WL333] }, + { .compatible = "murata,ncp15xh103", + .data = &ntc_thermistor_id[NTC_NCP15XH103] }, + { .compatible = "murata,ncp18wb473", + .data = &ntc_thermistor_id[NTC_NCP18WB473] }, + { .compatible = "murata,ncp21wb473", + .data = &ntc_thermistor_id[NTC_NCP21WB473] }, + { .compatible = "samsung,1404-001221", + .data = &ntc_thermistor_id[NTC_SSG1404001221] }, + + /* Usage of vendor name "ntc" is deprecated */ + { .compatible = "ntc,ncp03wb473", + .data = &ntc_thermistor_id[NTC_NCP03WB473] }, + { .compatible = "ntc,ncp15wb473", + .data = &ntc_thermistor_id[NTC_NCP15WB473] }, + { .compatible = "ntc,ncp15wl333", + .data = &ntc_thermistor_id[NTC_NCP15WL333] }, + { .compatible = "ntc,ncp18wb473", + .data = &ntc_thermistor_id[NTC_NCP18WB473] }, + { .compatible = "ntc,ncp21wb473", + .data = &ntc_thermistor_id[NTC_NCP21WB473] }, + { }, +}; +MODULE_DEVICE_TABLE(of, ntc_match); + static struct platform_driver ntc_thermistor_driver = { .driver = { .name = "ntc-thermistor", - .of_match_table = of_match_ptr(ntc_match), + .of_match_table = ntc_match, }, .probe = ntc_thermistor_probe, .id_table = ntc_thermistor_id, diff --git a/drivers/hwmon/nzxt-kraken2.c b/drivers/hwmon/nzxt-kraken2.c new file mode 100644 index 000000000000..89f7ea4f42d4 --- /dev/null +++ b/drivers/hwmon/nzxt-kraken2.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * nzxt-kraken2.c - hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers + * + * The device asynchronously sends HID reports (with id 0x04) twice a second to + * communicate current fan speed, pump speed and coolant temperature. The + * device does not respond to Get_Report requests for this status report. + * + * Copyright 2019-2021 Jonas Malaco <jonas@protocubo.io> + */ + +#include <asm/unaligned.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> + +#define STATUS_REPORT_ID 0x04 +#define STATUS_VALIDITY 2 /* seconds; equivalent to 4 missed updates */ + +static const char *const kraken2_temp_label[] = { + "Coolant", +}; + +static const char *const kraken2_fan_label[] = { + "Fan", + "Pump", +}; + +struct kraken2_priv_data { + struct hid_device *hid_dev; + struct device *hwmon_dev; + s32 temp_input[1]; + u16 fan_input[2]; + unsigned long updated; /* jiffies */ +}; + +static umode_t kraken2_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return 0444; +} + +static int kraken2_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct kraken2_priv_data *priv = dev_get_drvdata(dev); + + if (time_after(jiffies, priv->updated + STATUS_VALIDITY * HZ)) + return -ENODATA; + + switch (type) { + case hwmon_temp: + *val = priv->temp_input[channel]; + break; + case hwmon_fan: + *val = priv->fan_input[channel]; + break; + default: + return -EOPNOTSUPP; /* unreachable */ + } + + return 0; +} + +static int kraken2_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_temp: + *str = kraken2_temp_label[channel]; + break; + case hwmon_fan: + *str = kraken2_fan_label[channel]; + break; + default: + return -EOPNOTSUPP; /* unreachable */ + } + return 0; +} + +static const struct hwmon_ops kraken2_hwmon_ops = { + .is_visible = kraken2_is_visible, + .read = kraken2_read, + .read_string = kraken2_read_string, +}; + +static const struct hwmon_channel_info *kraken2_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL), + NULL +}; + +static const struct hwmon_chip_info kraken2_chip_info = { + .ops = &kraken2_hwmon_ops, + .info = kraken2_info, +}; + +static int kraken2_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct kraken2_priv_data *priv; + + if (size < 7 || report->id != STATUS_REPORT_ID) + return 0; + + priv = hid_get_drvdata(hdev); + + /* + * The fractional byte of the coolant temperature has been observed to + * be in the interval [1,9], but some of these steps are also + * consistently skipped for certain integer parts. + * + * For the lack of a better idea, assume that the resolution is 0.1°C, + * and that the missing steps are artifacts of how the firmware + * processes the raw sensor data. + */ + priv->temp_input[0] = data[1] * 1000 + data[2] * 100; + + priv->fan_input[0] = get_unaligned_be16(data + 3); + priv->fan_input[1] = get_unaligned_be16(data + 5); + + priv->updated = jiffies; + + return 0; +} + +static int kraken2_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct kraken2_priv_data *priv; + int ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hid_dev = hdev; + hid_set_drvdata(hdev, priv); + + /* + * Initialize ->updated to STATUS_VALIDITY seconds in the past, making + * the initial empty data invalid for kraken2_read without the need for + * a special case there. + */ + priv->updated = jiffies - STATUS_VALIDITY * HZ; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "hid parse failed with %d\n", ret); + return ret; + } + + /* + * Enable hidraw so existing user-space tools can continue to work. + */ + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "hid hw start failed with %d\n", ret); + goto fail_and_stop; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "hid hw open failed with %d\n", ret); + goto fail_and_close; + } + + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "kraken2", + priv, &kraken2_chip_info, + NULL); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + hid_err(hdev, "hwmon registration failed with %d\n", ret); + goto fail_and_close; + } + + return 0; + +fail_and_close: + hid_hw_close(hdev); +fail_and_stop: + hid_hw_stop(hdev); + return ret; +} + +static void kraken2_remove(struct hid_device *hdev) +{ + struct kraken2_priv_data *priv = hid_get_drvdata(hdev); + + hwmon_device_unregister(priv->hwmon_dev); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id kraken2_table[] = { + { HID_USB_DEVICE(0x1e71, 0x170e) }, /* NZXT Kraken X42/X52/X62/X72 */ + { } +}; + +MODULE_DEVICE_TABLE(hid, kraken2_table); + +static struct hid_driver kraken2_driver = { + .name = "nzxt-kraken2", + .id_table = kraken2_table, + .probe = kraken2_probe, + .remove = kraken2_remove, + .raw_event = kraken2_raw_event, +}; + +static int __init kraken2_init(void) +{ + return hid_register_driver(&kraken2_driver); +} + +static void __exit kraken2_exit(void) +{ + hid_unregister_driver(&kraken2_driver); +} + +/* + * When compiled into the kernel, initialize after the hid bus. + */ +late_initcall(kraken2_init); +module_exit(kraken2_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jonas Malaco <jonas@protocubo.io>"); +MODULE_DESCRIPTION("Hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers"); diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c new file mode 100644 index 000000000000..533f38b0b4e9 --- /dev/null +++ b/drivers/hwmon/nzxt-smart2.c @@ -0,0 +1,830 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Reverse-engineered NZXT RGB & Fan Controller/Smart Device v2 driver. + * + * Copyright (c) 2021 Aleksandr Mezin + */ + +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +/* + * The device has only 3 fan channels/connectors. But all HID reports have + * space reserved for up to 8 channels. + */ +#define FAN_CHANNELS 3 +#define FAN_CHANNELS_MAX 8 + +#define UPDATE_INTERVAL_DEFAULT_MS 1000 + +/* These strings match labels on the device exactly */ +static const char *const fan_label[] = { + "FAN 1", + "FAN 2", + "FAN 3", +}; + +static const char *const curr_label[] = { + "FAN 1 Current", + "FAN 2 Current", + "FAN 3 Current", +}; + +static const char *const in_label[] = { + "FAN 1 Voltage", + "FAN 2 Voltage", + "FAN 3 Voltage", +}; + +enum { + INPUT_REPORT_ID_FAN_CONFIG = 0x61, + INPUT_REPORT_ID_FAN_STATUS = 0x67, +}; + +enum { + FAN_STATUS_REPORT_SPEED = 0x02, + FAN_STATUS_REPORT_VOLTAGE = 0x04, +}; + +enum { + FAN_TYPE_NONE = 0, + FAN_TYPE_DC = 1, + FAN_TYPE_PWM = 2, +}; + +struct unknown_static_data { + /* + * Some configuration data? Stays the same after fan speed changes, + * changes in fan configuration, reboots and driver reloads. + * + * The same data in multiple report types. + * + * Byte 12 seems to be the number of fan channels, but I am not sure. + */ + u8 unknown1[14]; +} __packed; + +/* + * The device sends this input report in response to "detect fans" command: + * a 2-byte output report { 0x60, 0x03 }. + */ +struct fan_config_report { + /* report_id should be INPUT_REPORT_ID_FAN_CONFIG = 0x61 */ + u8 report_id; + /* Always 0x03 */ + u8 magic; + struct unknown_static_data unknown_data; + /* Fan type as detected by the device. See FAN_TYPE_* enum. */ + u8 fan_type[FAN_CHANNELS_MAX]; +} __packed; + +/* + * The device sends these reports at a fixed interval (update interval) - + * one report with type = FAN_STATUS_REPORT_SPEED, and one report with type = + * FAN_STATUS_REPORT_VOLTAGE per update interval. + */ +struct fan_status_report { + /* report_id should be INPUT_REPORT_ID_STATUS = 0x67 */ + u8 report_id; + /* FAN_STATUS_REPORT_SPEED = 0x02 or FAN_STATUS_REPORT_VOLTAGE = 0x04 */ + u8 type; + struct unknown_static_data unknown_data; + /* Fan type as detected by the device. See FAN_TYPE_* enum. */ + u8 fan_type[FAN_CHANNELS_MAX]; + + union { + /* When type == FAN_STATUS_REPORT_SPEED */ + struct { + /* + * Fan speed, in RPM. Zero for channels without fans + * connected. + */ + __le16 fan_rpm[FAN_CHANNELS_MAX]; + /* + * Fan duty cycle, in percent. Non-zero even for + * channels without fans connected. + */ + u8 duty_percent[FAN_CHANNELS_MAX]; + /* + * Exactly the same values as duty_percent[], non-zero + * for disconnected fans too. + */ + u8 duty_percent_dup[FAN_CHANNELS_MAX]; + /* "Case Noise" in db */ + u8 noise_db; + } __packed fan_speed; + /* When type == FAN_STATUS_REPORT_VOLTAGE */ + struct { + /* + * Voltage, in millivolts. Non-zero even when fan is + * not connected. + */ + __le16 fan_in[FAN_CHANNELS_MAX]; + /* + * Current, in milliamperes. Near-zero when + * disconnected. + */ + __le16 fan_current[FAN_CHANNELS_MAX]; + } __packed fan_voltage; + } __packed; +} __packed; + +#define OUTPUT_REPORT_SIZE 64 + +enum { + OUTPUT_REPORT_ID_INIT_COMMAND = 0x60, + OUTPUT_REPORT_ID_SET_FAN_SPEED = 0x62, +}; + +enum { + INIT_COMMAND_SET_UPDATE_INTERVAL = 0x02, + INIT_COMMAND_DETECT_FANS = 0x03, +}; + +/* + * This output report sets pwm duty cycle/target fan speed for one or more + * channels. + */ +struct set_fan_speed_report { + /* report_id should be OUTPUT_REPORT_ID_SET_FAN_SPEED = 0x62 */ + u8 report_id; + /* Should be 0x01 */ + u8 magic; + /* To change fan speed on i-th channel, set i-th bit here */ + u8 channel_bit_mask; + /* + * Fan duty cycle/target speed in percent. For voltage-controlled fans, + * the minimal voltage (duty_percent = 1) is about 9V. + * Setting duty_percent to 0 (if the channel is selected in + * channel_bit_mask) turns off the fan completely (regardless of the + * control mode). + */ + u8 duty_percent[FAN_CHANNELS_MAX]; +} __packed; + +struct drvdata { + struct hid_device *hid; + struct device *hwmon; + + u8 fan_duty_percent[FAN_CHANNELS]; + u16 fan_rpm[FAN_CHANNELS]; + bool pwm_status_received; + + u16 fan_in[FAN_CHANNELS]; + u16 fan_curr[FAN_CHANNELS]; + bool voltage_status_received; + + u8 fan_type[FAN_CHANNELS]; + bool fan_config_received; + + /* + * wq is used to wait for *_received flags to become true. + * All accesses to *_received flags and fan_* arrays are performed with + * wq.lock held. + */ + wait_queue_head_t wq; + /* + * mutex is used to: + * 1) Prevent concurrent conflicting changes to update interval and pwm + * values (after sending an output hid report, the corresponding field + * in drvdata must be updated, and only then new output reports can be + * sent). + * 2) Synchronize access to output_buffer (well, the buffer is here, + * because synchronization is necessary anyway - so why not get rid of + * a kmalloc?). + */ + struct mutex mutex; + long update_interval; + u8 output_buffer[OUTPUT_REPORT_SIZE]; +}; + +static long scale_pwm_value(long val, long orig_max, long new_max) +{ + if (val <= 0) + return 0; + + /* + * Positive values should not become zero: 0 completely turns off the + * fan. + */ + return max(1L, DIV_ROUND_CLOSEST(min(val, orig_max) * new_max, orig_max)); +} + +static void handle_fan_config_report(struct drvdata *drvdata, void *data, int size) +{ + struct fan_config_report *report = data; + int i; + + if (size < sizeof(struct fan_config_report)) + return; + + if (report->magic != 0x03) + return; + + spin_lock(&drvdata->wq.lock); + + for (i = 0; i < FAN_CHANNELS; i++) + drvdata->fan_type[i] = report->fan_type[i]; + + drvdata->fan_config_received = true; + wake_up_all_locked(&drvdata->wq); + spin_unlock(&drvdata->wq.lock); +} + +static void handle_fan_status_report(struct drvdata *drvdata, void *data, int size) +{ + struct fan_status_report *report = data; + int i; + + if (size < sizeof(struct fan_status_report)) + return; + + spin_lock(&drvdata->wq.lock); + + /* + * The device sends INPUT_REPORT_ID_FAN_CONFIG = 0x61 report in response + * to "detect fans" command. Only accept other data after getting 0x61, + * to make sure that fan detection is complete. In particular, fan + * detection resets pwm values. + */ + if (!drvdata->fan_config_received) { + spin_unlock(&drvdata->wq.lock); + return; + } + + for (i = 0; i < FAN_CHANNELS; i++) { + if (drvdata->fan_type[i] == report->fan_type[i]) + continue; + + /* + * This should not happen (if my expectations about the device + * are correct). + * + * Even if the userspace sends fan detect command through + * hidraw, fan config report should arrive first. + */ + hid_warn_once(drvdata->hid, + "Fan %d type changed unexpectedly from %d to %d", + i, drvdata->fan_type[i], report->fan_type[i]); + drvdata->fan_type[i] = report->fan_type[i]; + } + + switch (report->type) { + case FAN_STATUS_REPORT_SPEED: + for (i = 0; i < FAN_CHANNELS; i++) { + drvdata->fan_rpm[i] = + get_unaligned_le16(&report->fan_speed.fan_rpm[i]); + drvdata->fan_duty_percent[i] = + report->fan_speed.duty_percent[i]; + } + + drvdata->pwm_status_received = true; + wake_up_all_locked(&drvdata->wq); + break; + + case FAN_STATUS_REPORT_VOLTAGE: + for (i = 0; i < FAN_CHANNELS; i++) { + drvdata->fan_in[i] = + get_unaligned_le16(&report->fan_voltage.fan_in[i]); + drvdata->fan_curr[i] = + get_unaligned_le16(&report->fan_voltage.fan_current[i]); + } + + drvdata->voltage_status_received = true; + wake_up_all_locked(&drvdata->wq); + break; + } + + spin_unlock(&drvdata->wq.lock); +} + +static umode_t nzxt_smart2_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + return 0644; + + default: + return 0444; + } + + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + + default: + return 0444; + } + + default: + return 0444; + } +} + +static int nzxt_smart2_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct drvdata *drvdata = dev_get_drvdata(dev); + int res = -EINVAL; + + if (type == hwmon_chip) { + switch (attr) { + case hwmon_chip_update_interval: + *val = drvdata->update_interval; + return 0; + + default: + return -EINVAL; + } + } + + spin_lock_irq(&drvdata->wq.lock); + + switch (type) { + case hwmon_pwm: + /* + * fancontrol: + * 1) remembers pwm* values when it starts + * 2) needs pwm*_enable to be 1 on controlled fans + * So make sure we have correct data before allowing pwm* reads. + * Returning errors for pwm of fan speed read can even cause + * fancontrol to shut down. So the wait is unavoidable. + */ + switch (attr) { + case hwmon_pwm_enable: + res = wait_event_interruptible_locked_irq(drvdata->wq, + drvdata->fan_config_received); + if (res) + goto unlock; + + *val = drvdata->fan_type[channel] != FAN_TYPE_NONE; + break; + + case hwmon_pwm_mode: + res = wait_event_interruptible_locked_irq(drvdata->wq, + drvdata->fan_config_received); + if (res) + goto unlock; + + *val = drvdata->fan_type[channel] == FAN_TYPE_PWM; + break; + + case hwmon_pwm_input: + res = wait_event_interruptible_locked_irq(drvdata->wq, + drvdata->pwm_status_received); + if (res) + goto unlock; + + *val = scale_pwm_value(drvdata->fan_duty_percent[channel], + 100, 255); + break; + } + break; + + case hwmon_fan: + /* + * It's not strictly necessary to wait for *_received in the + * remaining cases (fancontrol doesn't care about them). But I'm + * doing it to have consistent behavior. + */ + if (attr == hwmon_fan_input) { + res = wait_event_interruptible_locked_irq(drvdata->wq, + drvdata->pwm_status_received); + if (res) + goto unlock; + + *val = drvdata->fan_rpm[channel]; + } + break; + + case hwmon_in: + if (attr == hwmon_in_input) { + res = wait_event_interruptible_locked_irq(drvdata->wq, + drvdata->voltage_status_received); + if (res) + goto unlock; + + *val = drvdata->fan_in[channel]; + } + break; + + case hwmon_curr: + if (attr == hwmon_curr_input) { + res = wait_event_interruptible_locked_irq(drvdata->wq, + drvdata->voltage_status_received); + if (res) + goto unlock; + + *val = drvdata->fan_curr[channel]; + } + break; + + default: + break; + } + +unlock: + spin_unlock_irq(&drvdata->wq.lock); + return res; +} + +static int send_output_report(struct drvdata *drvdata, const void *data, + size_t data_size) +{ + int ret; + + if (data_size > sizeof(drvdata->output_buffer)) + return -EINVAL; + + memcpy(drvdata->output_buffer, data, data_size); + + if (data_size < sizeof(drvdata->output_buffer)) + memset(drvdata->output_buffer + data_size, 0, + sizeof(drvdata->output_buffer) - data_size); + + ret = hid_hw_output_report(drvdata->hid, drvdata->output_buffer, + sizeof(drvdata->output_buffer)); + return ret < 0 ? ret : 0; +} + +static int set_pwm(struct drvdata *drvdata, int channel, long val) +{ + int ret; + u8 duty_percent = scale_pwm_value(val, 255, 100); + + struct set_fan_speed_report report = { + .report_id = OUTPUT_REPORT_ID_SET_FAN_SPEED, + .magic = 1, + .channel_bit_mask = 1 << channel + }; + + ret = mutex_lock_interruptible(&drvdata->mutex); + if (ret) + return ret; + + report.duty_percent[channel] = duty_percent; + ret = send_output_report(drvdata, &report, sizeof(report)); + if (ret) + goto unlock; + + /* + * pwmconfig and fancontrol scripts expect pwm writes to take effect + * immediately (i. e. read from pwm* sysfs should return the value + * written into it). The device seems to always accept pwm values - even + * when there is no fan connected - so update pwm status without waiting + * for a report, to make pwmconfig and fancontrol happy. Worst case - + * if the device didn't accept new pwm value for some reason (never seen + * this in practice) - it will be reported incorrectly only until next + * update. This avoids "fan stuck" messages from pwmconfig, and + * fancontrol setting fan speed to 100% during shutdown. + */ + spin_lock_bh(&drvdata->wq.lock); + drvdata->fan_duty_percent[channel] = duty_percent; + spin_unlock_bh(&drvdata->wq.lock); + +unlock: + mutex_unlock(&drvdata->mutex); + return ret; +} + +/* + * Workaround for fancontrol/pwmconfig trying to write to pwm*_enable even if it + * already is 1 and read-only. Otherwise, fancontrol won't restore pwm on + * shutdown properly. + */ +static int set_pwm_enable(struct drvdata *drvdata, int channel, long val) +{ + long expected_val; + int res; + + spin_lock_irq(&drvdata->wq.lock); + + res = wait_event_interruptible_locked_irq(drvdata->wq, + drvdata->fan_config_received); + if (res) { + spin_unlock_irq(&drvdata->wq.lock); + return res; + } + + expected_val = drvdata->fan_type[channel] != FAN_TYPE_NONE; + + spin_unlock_irq(&drvdata->wq.lock); + + return (val == expected_val) ? 0 : -EOPNOTSUPP; +} + +/* + * Control byte | Actual update interval in seconds + * 0xff | 65.5 + * 0xf7 | 63.46 + * 0x7f | 32.74 + * 0x3f | 16.36 + * 0x1f | 8.17 + * 0x0f | 4.07 + * 0x07 | 2.02 + * 0x03 | 1.00 + * 0x02 | 0.744 + * 0x01 | 0.488 + * 0x00 | 0.25 + */ +static u8 update_interval_to_control_byte(long interval) +{ + if (interval <= 250) + return 0; + + return clamp_val(1 + DIV_ROUND_CLOSEST(interval - 488, 256), 0, 255); +} + +static long control_byte_to_update_interval(u8 control_byte) +{ + if (control_byte == 0) + return 250; + + return 488 + (control_byte - 1) * 256; +} + +static int set_update_interval(struct drvdata *drvdata, long val) +{ + u8 control = update_interval_to_control_byte(val); + u8 report[] = { + OUTPUT_REPORT_ID_INIT_COMMAND, + INIT_COMMAND_SET_UPDATE_INTERVAL, + 0x01, + 0xe8, + control, + 0x01, + 0xe8, + control, + }; + int ret; + + ret = send_output_report(drvdata, report, sizeof(report)); + if (ret) + return ret; + + drvdata->update_interval = control_byte_to_update_interval(control); + return 0; +} + +static int init_device(struct drvdata *drvdata, long update_interval) +{ + int ret; + static const u8 detect_fans_report[] = { + OUTPUT_REPORT_ID_INIT_COMMAND, + INIT_COMMAND_DETECT_FANS, + }; + + ret = send_output_report(drvdata, detect_fans_report, + sizeof(detect_fans_report)); + if (ret) + return ret; + + return set_update_interval(drvdata, update_interval); +} + +static int nzxt_smart2_hwmon_write(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct drvdata *drvdata = dev_get_drvdata(dev); + int ret; + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + return set_pwm_enable(drvdata, channel, val); + + case hwmon_pwm_input: + return set_pwm(drvdata, channel, val); + + default: + return -EINVAL; + } + + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + ret = mutex_lock_interruptible(&drvdata->mutex); + if (ret) + return ret; + + ret = set_update_interval(drvdata, val); + + mutex_unlock(&drvdata->mutex); + return ret; + + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static int nzxt_smart2_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + switch (type) { + case hwmon_fan: + *str = fan_label[channel]; + return 0; + case hwmon_curr: + *str = curr_label[channel]; + return 0; + case hwmon_in: + *str = in_label[channel]; + return 0; + default: + return -EINVAL; + } +} + +static const struct hwmon_ops nzxt_smart2_hwmon_ops = { + .is_visible = nzxt_smart2_hwmon_is_visible, + .read = nzxt_smart2_hwmon_read, + .read_string = nzxt_smart2_hwmon_read_string, + .write = nzxt_smart2_hwmon_write, +}; + +static const struct hwmon_channel_info *nzxt_smart2_channel_info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_MODE | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_MODE | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_MODE | HWMON_PWM_ENABLE), + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + NULL +}; + +static const struct hwmon_chip_info nzxt_smart2_chip_info = { + .ops = &nzxt_smart2_hwmon_ops, + .info = nzxt_smart2_channel_info, +}; + +static int nzxt_smart2_hid_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct drvdata *drvdata = hid_get_drvdata(hdev); + u8 report_id = *data; + + switch (report_id) { + case INPUT_REPORT_ID_FAN_CONFIG: + handle_fan_config_report(drvdata, data, size); + break; + + case INPUT_REPORT_ID_FAN_STATUS: + handle_fan_status_report(drvdata, data, size); + break; + } + + return 0; +} + +static int __maybe_unused nzxt_smart2_hid_reset_resume(struct hid_device *hdev) +{ + struct drvdata *drvdata = hid_get_drvdata(hdev); + + /* + * Userspace is still frozen (so no concurrent sysfs attribute access + * is possible), but raw_event can already be called concurrently. + */ + spin_lock_bh(&drvdata->wq.lock); + drvdata->fan_config_received = false; + drvdata->pwm_status_received = false; + drvdata->voltage_status_received = false; + spin_unlock_bh(&drvdata->wq.lock); + + return init_device(drvdata, drvdata->update_interval); +} + +static int nzxt_smart2_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct drvdata *drvdata; + int ret; + + drvdata = devm_kzalloc(&hdev->dev, sizeof(struct drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->hid = hdev; + hid_set_drvdata(hdev, drvdata); + + init_waitqueue_head(&drvdata->wq); + + mutex_init(&drvdata->mutex); + devm_add_action(&hdev->dev, (void (*)(void *))mutex_destroy, + &drvdata->mutex); + + ret = hid_parse(hdev); + if (ret) + return ret; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto out_hw_stop; + + hid_device_io_start(hdev); + + init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS); + + drvdata->hwmon = + hwmon_device_register_with_info(&hdev->dev, "nzxtsmart2", drvdata, + &nzxt_smart2_chip_info, NULL); + if (IS_ERR(drvdata->hwmon)) { + ret = PTR_ERR(drvdata->hwmon); + goto out_hw_close; + } + + return 0; + +out_hw_close: + hid_hw_close(hdev); + +out_hw_stop: + hid_hw_stop(hdev); + return ret; +} + +static void nzxt_smart2_hid_remove(struct hid_device *hdev) +{ + struct drvdata *drvdata = hid_get_drvdata(hdev); + + hwmon_device_unregister(drvdata->hwmon); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id nzxt_smart2_hid_id_table[] = { + { HID_USB_DEVICE(0x1e71, 0x2006) }, /* NZXT Smart Device V2 */ + { HID_USB_DEVICE(0x1e71, 0x200d) }, /* NZXT Smart Device V2 */ + { HID_USB_DEVICE(0x1e71, 0x200f) }, /* NZXT Smart Device V2 */ + { HID_USB_DEVICE(0x1e71, 0x2009) }, /* NZXT RGB & Fan Controller */ + { HID_USB_DEVICE(0x1e71, 0x200e) }, /* NZXT RGB & Fan Controller */ + { HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */ + {}, +}; + +static struct hid_driver nzxt_smart2_hid_driver = { + .name = "nzxt-smart2", + .id_table = nzxt_smart2_hid_id_table, + .probe = nzxt_smart2_hid_probe, + .remove = nzxt_smart2_hid_remove, + .raw_event = nzxt_smart2_hid_raw_event, +#ifdef CONFIG_PM + .reset_resume = nzxt_smart2_hid_reset_resume, +#endif +}; + +static int __init nzxt_smart2_init(void) +{ + return hid_register_driver(&nzxt_smart2_hid_driver); +} + +static void __exit nzxt_smart2_exit(void) +{ + hid_unregister_driver(&nzxt_smart2_hid_driver); +} + +MODULE_DEVICE_TABLE(hid, nzxt_smart2_hid_id_table); +MODULE_AUTHOR("Aleksandr Mezin <mezin.alexander@gmail.com>"); +MODULE_DESCRIPTION("Driver for NZXT RGB & Fan Controller/Smart Device V2"); +MODULE_LICENSE("GPL"); + +/* + * With module_init()/module_hid_driver() and the driver built into the kernel: + * + * Driver 'nzxt_smart2' was unable to register with bus_type 'hid' because the + * bus was not initialized. + */ +late_initcall(nzxt_smart2_init); +module_exit(nzxt_smart2_exit); diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 30e18eb60da7..dd690f700d49 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -10,6 +10,7 @@ #include <linux/math64.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/property.h> #include <linux/sysfs.h> #include <asm/unaligned.h> @@ -41,6 +42,14 @@ struct temp_sensor_2 { u8 value; } __packed; +struct temp_sensor_10 { + u32 sensor_id; + u8 fru_type; + u8 value; + u8 throttle; + u8 reserved; +} __packed; + struct freq_sensor_1 { u16 sensor_id; u16 value; @@ -124,22 +133,20 @@ struct extended_sensor { static int occ_poll(struct occ *occ) { int rc; - u16 checksum = occ->poll_cmd_data + occ->seq_no + 1; - u8 cmd[8]; + u8 cmd[7]; struct occ_poll_response_header *header; /* big endian */ - cmd[0] = occ->seq_no++; /* sequence number */ + cmd[0] = 0; /* sequence number */ cmd[1] = 0; /* cmd type */ cmd[2] = 0; /* data length msb */ cmd[3] = 1; /* data length lsb */ cmd[4] = occ->poll_cmd_data; /* data */ - cmd[5] = checksum >> 8; /* checksum msb */ - cmd[6] = checksum & 0xFF; /* checksum lsb */ - cmd[7] = 0; + cmd[5] = 0; /* checksum msb */ + cmd[6] = 0; /* checksum lsb */ /* mutex should already be locked if necessary */ - rc = occ->send_cmd(occ, cmd); + rc = occ->send_cmd(occ, cmd, sizeof(cmd), &occ->resp, sizeof(occ->resp)); if (rc) { occ->last_error = rc; if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD) @@ -176,25 +183,24 @@ static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap) { int rc; u8 cmd[8]; - u16 checksum = 0x24; + u8 resp[8]; __be16 user_power_cap_be = cpu_to_be16(user_power_cap); - cmd[0] = 0; - cmd[1] = 0x22; - cmd[2] = 0; - cmd[3] = 2; + cmd[0] = 0; /* sequence number */ + cmd[1] = 0x22; /* cmd type */ + cmd[2] = 0; /* data length msb */ + cmd[3] = 2; /* data length lsb */ memcpy(&cmd[4], &user_power_cap_be, 2); - checksum += cmd[4] + cmd[5]; - cmd[6] = checksum >> 8; - cmd[7] = checksum & 0xFF; + cmd[6] = 0; /* checksum msb */ + cmd[7] = 0; /* checksum lsb */ rc = mutex_lock_interruptible(&occ->lock); if (rc) return rc; - rc = occ->send_cmd(occ, cmd); + rc = occ->send_cmd(occ, cmd, sizeof(cmd), resp, sizeof(resp)); mutex_unlock(&occ->lock); @@ -209,9 +215,9 @@ int occ_update_response(struct occ *occ) return rc; /* limit the maximum rate of polling the OCC */ - if (time_after(jiffies, occ->last_update + OCC_UPDATE_FREQUENCY)) { + if (time_after(jiffies, occ->next_update)) { rc = occ_poll(occ); - occ->last_update = jiffies; + occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; } else { rc = occ->last_error; } @@ -253,7 +259,7 @@ static ssize_t occ_show_temp_1(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_temp_2(struct device *dev, @@ -304,7 +310,54 @@ static ssize_t occ_show_temp_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t occ_show_temp_10(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc; + u32 val = 0; + struct temp_sensor_10 *temp; + struct occ *occ = dev_get_drvdata(dev); + struct occ_sensors *sensors = &occ->sensors; + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + + rc = occ_update_response(occ); + if (rc) + return rc; + + temp = ((struct temp_sensor_10 *)sensors->temp.data) + sattr->index; + + switch (sattr->nr) { + case 0: + val = get_unaligned_be32(&temp->sensor_id); + break; + case 1: + val = temp->value; + if (val == OCC_TEMP_SENSOR_FAULT) + return -EREMOTEIO; + + /* sensor not ready */ + if (val == 0) + return -EAGAIN; + + val *= 1000; + break; + case 2: + val = temp->fru_type; + break; + case 3: + val = temp->value == OCC_TEMP_SENSOR_FAULT; + break; + case 4: + val = temp->throttle * 1000; + break; + default: + return -EINVAL; + } + + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_freq_1(struct device *dev, @@ -334,7 +387,7 @@ static ssize_t occ_show_freq_1(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_freq_2(struct device *dev, @@ -364,7 +417,7 @@ static ssize_t occ_show_freq_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_power_1(struct device *dev, @@ -403,7 +456,7 @@ static ssize_t occ_show_power_1(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static u64 occ_get_powr_avg(u64 *accum, u32 *samples) @@ -432,9 +485,9 @@ static ssize_t occ_show_power_2(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "%u_%u_%u\n", - get_unaligned_be32(&power->sensor_id), - power->function_id, power->apss_channel); + return sysfs_emit(buf, "%u_%u_%u\n", + get_unaligned_be32(&power->sensor_id), + power->function_id, power->apss_channel); case 1: val = occ_get_powr_avg(&power->accumulator, &power->update_tag); @@ -450,7 +503,7 @@ static ssize_t occ_show_power_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_show_power_a0(struct device *dev, @@ -471,8 +524,8 @@ static ssize_t occ_show_power_a0(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "%u_system\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_system\n", + get_unaligned_be32(&power->sensor_id)); case 1: val = occ_get_powr_avg(&power->system.accumulator, &power->system.update_tag); @@ -485,8 +538,8 @@ static ssize_t occ_show_power_a0(struct device *dev, val = get_unaligned_be16(&power->system.value) * 1000000ULL; break; case 4: - return snprintf(buf, PAGE_SIZE - 1, "%u_proc\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_proc\n", + get_unaligned_be32(&power->sensor_id)); case 5: val = occ_get_powr_avg(&power->proc.accumulator, &power->proc.update_tag); @@ -499,8 +552,8 @@ static ssize_t occ_show_power_a0(struct device *dev, val = get_unaligned_be16(&power->proc.value) * 1000000ULL; break; case 8: - return snprintf(buf, PAGE_SIZE - 1, "%u_vdd\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_vdd\n", + get_unaligned_be32(&power->sensor_id)); case 9: val = occ_get_powr_avg(&power->vdd.accumulator, &power->vdd.update_tag); @@ -513,8 +566,8 @@ static ssize_t occ_show_power_a0(struct device *dev, val = get_unaligned_be16(&power->vdd.value) * 1000000ULL; break; case 12: - return snprintf(buf, PAGE_SIZE - 1, "%u_vdn\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_vdn\n", + get_unaligned_be32(&power->sensor_id)); case 13: val = occ_get_powr_avg(&power->vdn.accumulator, &power->vdn.update_tag); @@ -530,7 +583,7 @@ static ssize_t occ_show_power_a0(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_show_caps_1_2(struct device *dev, @@ -551,7 +604,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "system\n"); + return sysfs_emit(buf, "system\n"); case 1: val = get_unaligned_be16(&caps->cap) * 1000000ULL; break; @@ -580,7 +633,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_show_caps_3(struct device *dev, @@ -601,7 +654,7 @@ static ssize_t occ_show_caps_3(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "system\n"); + return sysfs_emit(buf, "system\n"); case 1: val = get_unaligned_be16(&caps->cap) * 1000000ULL; break; @@ -623,11 +676,14 @@ static ssize_t occ_show_caps_3(struct device *dev, case 7: val = caps->user_source; break; + case 8: + val = get_unaligned_be16(&caps->soft_min) * 1000000ULL; + break; default: return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_store_caps_user(struct device *dev, @@ -670,21 +726,18 @@ static ssize_t occ_show_extended(struct device *dev, switch (sattr->nr) { case 0: - if (extn->flags & EXTN_FLAG_SENSOR_ID) - rc = snprintf(buf, PAGE_SIZE - 1, "%u", - get_unaligned_be32(&extn->sensor_id)); - else - rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x\n", - extn->name[0], extn->name[1], - extn->name[2], extn->name[3]); + if (extn->flags & EXTN_FLAG_SENSOR_ID) { + rc = sysfs_emit(buf, "%u", + get_unaligned_be32(&extn->sensor_id)); + } else { + rc = sysfs_emit(buf, "%4phN\n", extn->name); + } break; case 1: - rc = snprintf(buf, PAGE_SIZE - 1, "%02x\n", extn->flags); + rc = sysfs_emit(buf, "%02x\n", extn->flags); break; case 2: - rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x%02x%02x\n", - extn->data[0], extn->data[1], extn->data[2], - extn->data[3], extn->data[4], extn->data[5]); + rc = sysfs_emit(buf, "%6phN\n", extn->data); break; default: return -EINVAL; @@ -745,6 +798,10 @@ static int occ_setup_sensor_attrs(struct occ *occ) num_attrs += (sensors->temp.num_sensors * 4); show_temp = occ_show_temp_2; break; + case 0x10: + num_attrs += (sensors->temp.num_sensors * 5); + show_temp = occ_show_temp_10; + break; default: sensors->temp.num_sensors = 0; } @@ -752,7 +809,7 @@ static int occ_setup_sensor_attrs(struct occ *occ) switch (sensors->freq.version) { case 2: show_freq = occ_show_freq_2; - /* fall through */ + fallthrough; case 1: num_attrs += (sensors->freq.num_sensors * 2); break; @@ -763,7 +820,7 @@ static int occ_setup_sensor_attrs(struct occ *occ) switch (sensors->power.version) { case 2: show_power = occ_show_power_2; - /* fall through */ + fallthrough; case 1: num_attrs += (sensors->power.num_sensors * 4); break; @@ -779,12 +836,13 @@ static int occ_setup_sensor_attrs(struct occ *occ) case 1: num_attrs += (sensors->caps.num_sensors * 7); break; - case 3: - show_caps = occ_show_caps_3; - /* fall through */ case 2: num_attrs += (sensors->caps.num_sensors * 8); break; + case 3: + show_caps = occ_show_caps_3; + num_attrs += (sensors->caps.num_sensors * 9); + break; default: sensors->caps.num_sensors = 0; } @@ -819,7 +877,7 @@ static int occ_setup_sensor_attrs(struct occ *occ) 0, i); attr++; - if (sensors->temp.version > 1 && + if (sensors->temp.version == 2 && temp->fru_type == OCC_FRU_TYPE_VRM) { snprintf(attr->name, sizeof(attr->name), "temp%d_alarm", s); @@ -844,6 +902,15 @@ static int occ_setup_sensor_attrs(struct occ *occ) attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, 3, i); attr++; + + if (sensors->temp.version == 0x10) { + snprintf(attr->name, sizeof(attr->name), + "temp%d_max", s); + attr->sensor = OCC_INIT_ATTR(attr->name, 0444, + show_temp, NULL, + 4, i); + attr++; + } } } @@ -982,6 +1049,15 @@ static int occ_setup_sensor_attrs(struct occ *occ) attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 7, 0); attr++; + + if (sensors->caps.version > 2) { + snprintf(attr->name, sizeof(attr->name), + "power%d_cap_min_soft", s); + attr->sensor = OCC_INIT_ATTR(attr->name, 0444, + show_caps, NULL, + 8, 0); + attr++; + } } } @@ -1071,50 +1147,105 @@ static void occ_parse_poll_response(struct occ *occ) sizeof(*header), size + sizeof(*header)); } -int occ_setup(struct occ *occ, const char *name) +int occ_active(struct occ *occ, bool active) { - int rc; - - mutex_init(&occ->lock); - occ->groups[0] = &occ->group; + int rc = mutex_lock_interruptible(&occ->lock); - /* no need to lock */ - rc = occ_poll(occ); - if (rc == -ESHUTDOWN) { - dev_info(occ->bus_dev, "host is not ready\n"); - return rc; - } else if (rc < 0) { - dev_err(occ->bus_dev, "failed to get OCC poll response: %d\n", - rc); + if (rc) return rc; + + if (active) { + if (occ->active) { + rc = -EALREADY; + goto unlock; + } + + occ->error_count = 0; + occ->last_safe = 0; + + rc = occ_poll(occ); + if (rc < 0) { + dev_err(occ->bus_dev, + "failed to get OCC poll response=%02x: %d\n", + occ->resp.return_status, rc); + goto unlock; + } + + occ->active = true; + occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; + occ_parse_poll_response(occ); + + rc = occ_setup_sensor_attrs(occ); + if (rc) { + dev_err(occ->bus_dev, + "failed to setup sensor attrs: %d\n", rc); + goto unlock; + } + + occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev, + "occ", occ, + occ->groups); + if (IS_ERR(occ->hwmon)) { + rc = PTR_ERR(occ->hwmon); + occ->hwmon = NULL; + dev_err(occ->bus_dev, + "failed to register hwmon device: %d\n", rc); + goto unlock; + } + } else { + if (!occ->active) { + rc = -EALREADY; + goto unlock; + } + + if (occ->hwmon) + hwmon_device_unregister(occ->hwmon); + occ->active = false; + occ->hwmon = NULL; } - occ_parse_poll_response(occ); +unlock: + mutex_unlock(&occ->lock); + return rc; +} + +int occ_setup(struct occ *occ) +{ + int rc; - rc = occ_setup_sensor_attrs(occ); + mutex_init(&occ->lock); + occ->groups[0] = &occ->group; + + rc = occ_setup_sysfs(occ); if (rc) { - dev_err(occ->bus_dev, "failed to setup sensor attrs: %d\n", - rc); + dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc); return rc; } - occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev, name, - occ, occ->groups); - if (IS_ERR(occ->hwmon)) { - rc = PTR_ERR(occ->hwmon); - dev_err(occ->bus_dev, "failed to register hwmon device: %d\n", - rc); - return rc; + if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) { + rc = occ_active(occ, true); + if (rc) + occ_shutdown_sysfs(occ); } - rc = occ_setup_sysfs(occ); - if (rc) - dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc); - return rc; } EXPORT_SYMBOL_GPL(occ_setup); +void occ_shutdown(struct occ *occ) +{ + mutex_lock(&occ->lock); + + occ_shutdown_sysfs(occ); + + if (occ->hwmon) + hwmon_device_unregister(occ->hwmon); + occ->hwmon = NULL; + + mutex_unlock(&occ->lock); +} +EXPORT_SYMBOL_GPL(occ_shutdown); + MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>"); MODULE_DESCRIPTION("Common OCC hwmon code"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h index 67e6968b8978..7ac4b2febce6 100644 --- a/drivers/hwmon/occ/common.h +++ b/drivers/hwmon/occ/common.h @@ -95,11 +95,11 @@ struct occ { struct occ_sensors sensors; int powr_sample_time_us; /* average power sample time */ - u8 seq_no; u8 poll_cmd_data; /* to perform OCC poll command */ - int (*send_cmd)(struct occ *occ, u8 *cmd); + int (*send_cmd)(struct occ *occ, u8 *cmd, size_t len, void *resp, + size_t resp_len); - unsigned long last_update; + unsigned long next_update; struct mutex lock; /* lock OCC access */ struct device *hwmon; @@ -107,6 +107,7 @@ struct occ { struct attribute_group group; const struct attribute_group *groups[2]; + bool active; int error; /* final transfer error after retry */ int last_error; /* latest transfer error */ unsigned int error_count; /* number of xfr errors observed */ @@ -120,11 +121,15 @@ struct occ { u8 prev_stat; u8 prev_ext_stat; u8 prev_occs_present; + u8 prev_ips_status; + u8 prev_mode; }; -int occ_setup(struct occ *occ, const char *name); +int occ_active(struct occ *occ, bool active); +int occ_setup(struct occ *occ); int occ_setup_sysfs(struct occ *occ); void occ_shutdown(struct occ *occ); +void occ_shutdown_sysfs(struct occ *occ); void occ_sysfs_poll_done(struct occ *occ); int occ_update_response(struct occ *occ); diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c index 76fb7870c7d3..9e1744fccb35 100644 --- a/drivers/hwmon/occ/p8_i2c.c +++ b/drivers/hwmon/occ/p8_i2c.c @@ -97,18 +97,22 @@ static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address, } static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address, - u8 *data) + u8 *data, size_t len) { - __be32 data0, data1; + __be32 data0 = 0, data1 = 0; - memcpy(&data0, data, 4); - memcpy(&data1, data + 4, 4); + memcpy(&data0, data, min_t(size_t, len, 4)); + if (len > 4) { + len -= 4; + memcpy(&data1, data + 4, min_t(size_t, len, 4)); + } return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0), be32_to_cpu(data1)); } -static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) +static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len, + void *resp, size_t resp_len) { int i, rc; unsigned long start; @@ -117,7 +121,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) const long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS); struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ); struct i2c_client *client = ctx->client; - struct occ_response *resp = &occ->resp; + struct occ_response *or = (struct occ_response *)resp; start = jiffies; @@ -127,7 +131,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) return rc; /* write command (expected to already be BE), we need bus-endian... */ - rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd); + rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd, len); if (rc) return rc; @@ -148,7 +152,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) return rc; /* wait for OCC */ - if (resp->return_status == OCC_RESP_CMD_IN_PRG) { + if (or->return_status == OCC_RESP_CMD_IN_PRG) { rc = -EALREADY; if (time_after(jiffies, start + timeout)) @@ -160,7 +164,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) } while (rc); /* check the OCC response */ - switch (resp->return_status) { + switch (or->return_status) { case OCC_RESP_CMD_IN_PRG: rc = -ETIMEDOUT; break; @@ -189,8 +193,8 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) if (rc < 0) return rc; - data_length = get_unaligned_be16(&resp->data_length); - if (data_length > OCC_RESP_DATA_BYTES) + data_length = get_unaligned_be16(&or->data_length); + if ((data_length + 7) > resp_len) return -EMSGSIZE; /* fetch the rest of the response data */ @@ -203,8 +207,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd) return 0; } -static int p8_i2c_occ_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int p8_i2c_occ_probe(struct i2c_client *client) { struct occ *occ; struct p8_i2c_occ *ctx = devm_kzalloc(&client->dev, sizeof(*ctx), @@ -221,16 +224,14 @@ static int p8_i2c_occ_probe(struct i2c_client *client, occ->poll_cmd_data = 0x10; /* P8 OCC poll data */ occ->send_cmd = p8_i2c_occ_send_cmd; - return occ_setup(occ, "p8_occ"); + return occ_setup(occ); } -static int p8_i2c_occ_remove(struct i2c_client *client) +static void p8_i2c_occ_remove(struct i2c_client *client) { struct occ *occ = dev_get_drvdata(&client->dev); occ_shutdown(occ); - - return 0; } static const struct of_device_id p8_i2c_occ_of_match[] = { @@ -245,7 +246,7 @@ static struct i2c_driver p8_i2c_occ_driver = { .name = "occ-hwmon", .of_match_table = p8_i2c_occ_of_match, }, - .probe = p8_i2c_occ_probe, + .probe_new = p8_i2c_occ_probe, .remove = p8_i2c_occ_remove, }; diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index f6387cc0b754..96521363b696 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -3,31 +3,106 @@ #include <linux/device.h> #include <linux/errno.h> +#include <linux/slab.h> #include <linux/fsi-occ.h> +#include <linux/mm.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/string.h> +#include <linux/sysfs.h> #include "common.h" +#define OCC_CHECKSUM_RETRIES 3 + struct p9_sbe_occ { struct occ occ; + bool sbe_error; + void *ffdc; + size_t ffdc_len; + size_t ffdc_size; + struct mutex sbe_error_lock; /* lock access to ffdc data */ struct device *sbe; }; #define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ) -static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd) +static ssize_t ffdc_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *battr, char *buf, loff_t pos, + size_t count) { - struct occ_response *resp = &occ->resp; + ssize_t rc = 0; + struct occ *occ = dev_get_drvdata(kobj_to_dev(kobj)); struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ); - size_t resp_len = sizeof(*resp); - int rc; - rc = fsi_occ_submit(ctx->sbe, cmd, 8, resp, &resp_len); - if (rc < 0) - return rc; + mutex_lock(&ctx->sbe_error_lock); + if (ctx->sbe_error) { + rc = memory_read_from_buffer(buf, count, &pos, ctx->ffdc, + ctx->ffdc_len); + if (pos >= ctx->ffdc_len) + ctx->sbe_error = false; + } + mutex_unlock(&ctx->sbe_error_lock); + + return rc; +} +static BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4); + +static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp, + size_t resp_len) +{ + bool notify = false; + + mutex_lock(&ctx->sbe_error_lock); + if (!ctx->sbe_error) { + if (resp_len > ctx->ffdc_size) { + kvfree(ctx->ffdc); + ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL); + if (!ctx->ffdc) { + ctx->ffdc_len = 0; + ctx->ffdc_size = 0; + goto done; + } + + ctx->ffdc_size = resp_len; + } + + notify = true; + ctx->sbe_error = true; + ctx->ffdc_len = resp_len; + memcpy(ctx->ffdc, resp, resp_len); + } + +done: + mutex_unlock(&ctx->sbe_error_lock); + return notify; +} + +static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len, + void *resp, size_t resp_len) +{ + size_t original_resp_len = resp_len; + struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ); + int rc, i; + + for (i = 0; i < OCC_CHECKSUM_RETRIES; ++i) { + rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len); + if (rc >= 0) + break; + if (resp_len) { + if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len)) + sysfs_notify(&occ->bus_dev->kobj, NULL, + bin_attr_ffdc.attr.name); + return rc; + } + if (rc != -EBADE) + return rc; + resp_len = original_resp_len; + } - switch (resp->return_status) { + switch (((struct occ_response *)resp)->return_status) { case OCC_RESP_CMD_IN_PRG: rc = -ETIMEDOUT; break; @@ -65,6 +140,8 @@ static int p9_sbe_occ_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; + mutex_init(&ctx->sbe_error_lock); + ctx->sbe = pdev->dev.parent; occ = &ctx->occ; occ->bus_dev = &pdev->dev; @@ -74,10 +151,19 @@ static int p9_sbe_occ_probe(struct platform_device *pdev) occ->poll_cmd_data = 0x20; /* P9 OCC poll data */ occ->send_cmd = p9_sbe_occ_send_cmd; - rc = occ_setup(occ, "p9_occ"); + rc = occ_setup(occ); if (rc == -ESHUTDOWN) rc = -ENODEV; /* Host is shutdown, don't spew errors */ + if (!rc) { + rc = device_create_bin_file(occ->bus_dev, &bin_attr_ffdc); + if (rc) { + dev_warn(occ->bus_dev, + "failed to create SBE error ffdc file\n"); + rc = 0; + } + } + return rc; } @@ -86,15 +172,27 @@ static int p9_sbe_occ_remove(struct platform_device *pdev) struct occ *occ = platform_get_drvdata(pdev); struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ); + device_remove_bin_file(occ->bus_dev, &bin_attr_ffdc); + ctx->sbe = NULL; occ_shutdown(occ); + kvfree(ctx->ffdc); + return 0; } +static const struct of_device_id p9_sbe_occ_of_match[] = { + { .compatible = "ibm,p9-occ-hwmon" }, + { .compatible = "ibm,p10-occ-hwmon" }, + {} +}; +MODULE_DEVICE_TABLE(of, p9_sbe_occ_of_match); + static struct platform_driver p9_sbe_occ_driver = { .driver = { .name = "occ-hwmon", + .of_match_table = p9_sbe_occ_of_match, }, .probe = p9_sbe_occ_probe, .remove = p9_sbe_occ_remove, diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c index c73be0747e66..2317301fc1e9 100644 --- a/drivers/hwmon/occ/sysfs.c +++ b/drivers/hwmon/occ/sysfs.c @@ -6,19 +6,40 @@ #include <linux/export.h> #include <linux/hwmon-sysfs.h> #include <linux/kernel.h> +#include <linux/kstrtox.h> #include <linux/sysfs.h> #include "common.h" /* OCC status register */ #define OCC_STAT_MASTER BIT(7) -#define OCC_STAT_ACTIVE BIT(0) /* OCC extended status register */ #define OCC_EXT_STAT_DVFS_OT BIT(7) #define OCC_EXT_STAT_DVFS_POWER BIT(6) #define OCC_EXT_STAT_MEM_THROTTLE BIT(5) #define OCC_EXT_STAT_QUICK_DROP BIT(4) +#define OCC_EXT_STAT_DVFS_VDD BIT(3) +#define OCC_EXT_STAT_GPU_THROTTLE GENMASK(2, 0) + +static ssize_t occ_active_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + bool active; + struct occ *occ = dev_get_drvdata(dev); + + rc = kstrtobool(buf, &active); + if (rc) + return rc; + + rc = occ_active(occ, active); + if (rc) + return rc; + + return count; +} static ssize_t occ_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -29,45 +50,67 @@ static ssize_t occ_sysfs_show(struct device *dev, struct occ_poll_response_header *header; struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); - rc = occ_update_response(occ); - if (rc) - return rc; + if (occ->active) { + rc = occ_update_response(occ); + if (rc) + return rc; - header = (struct occ_poll_response_header *)occ->resp.data; - - switch (sattr->index) { - case 0: - val = !!(header->status & OCC_STAT_MASTER); - break; - case 1: - val = !!(header->status & OCC_STAT_ACTIVE); - break; - case 2: - val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT); - break; - case 3: - val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER); - break; - case 4: - val = !!(header->ext_status & OCC_EXT_STAT_MEM_THROTTLE); - break; - case 5: - val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP); - break; - case 6: - val = header->occ_state; - break; - case 7: - if (header->status & OCC_STAT_MASTER) - val = hweight8(header->occs_present); - else + header = (struct occ_poll_response_header *)occ->resp.data; + + switch (sattr->index) { + case 0: + val = !!(header->status & OCC_STAT_MASTER); + break; + case 1: val = 1; - break; - default: - return -EINVAL; + break; + case 2: + val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT); + break; + case 3: + val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER); + break; + case 4: + val = !!(header->ext_status & + OCC_EXT_STAT_MEM_THROTTLE); + break; + case 5: + val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP); + break; + case 6: + val = header->occ_state; + break; + case 7: + if (header->status & OCC_STAT_MASTER) + val = hweight8(header->occs_present); + else + val = 1; + break; + case 8: + val = header->ips_status; + break; + case 9: + val = header->mode; + break; + case 10: + val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD); + break; + case 11: + val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE; + break; + default: + return -EINVAL; + } + } else { + if (sattr->index == 1) + val = 0; + else if (sattr->index <= 11) + val = -ENODATA; + else + return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t occ_error_show(struct device *dev, @@ -77,17 +120,22 @@ static ssize_t occ_error_show(struct device *dev, occ_update_response(occ); - return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error); + return sysfs_emit(buf, "%d\n", occ->error); } static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0); -static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_sysfs_show, NULL, 1); +static SENSOR_DEVICE_ATTR(occ_active, 0644, occ_sysfs_show, occ_active_store, + 1); static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2); static SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_sysfs_show, NULL, 3); static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4); static SENSOR_DEVICE_ATTR(occ_quick_pwr_drop, 0444, occ_sysfs_show, NULL, 5); static SENSOR_DEVICE_ATTR(occ_state, 0444, occ_sysfs_show, NULL, 6); static SENSOR_DEVICE_ATTR(occs_present, 0444, occ_sysfs_show, NULL, 7); +static SENSOR_DEVICE_ATTR(occ_ips_status, 0444, occ_sysfs_show, NULL, 8); +static SENSOR_DEVICE_ATTR(occ_mode, 0444, occ_sysfs_show, NULL, 9); +static SENSOR_DEVICE_ATTR(occ_dvfs_vdd, 0444, occ_sysfs_show, NULL, 10); +static SENSOR_DEVICE_ATTR(occ_gpu_throttle, 0444, occ_sysfs_show, NULL, 11); static DEVICE_ATTR_RO(occ_error); static struct attribute *occ_attributes[] = { @@ -99,6 +147,10 @@ static struct attribute *occ_attributes[] = { &sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr, &sensor_dev_attr_occ_state.dev_attr.attr, &sensor_dev_attr_occs_present.dev_attr.attr, + &sensor_dev_attr_occ_ips_status.dev_attr.attr, + &sensor_dev_attr_occ_mode.dev_attr.attr, + &sensor_dev_attr_occ_dvfs_vdd.dev_attr.attr, + &sensor_dev_attr_occ_gpu_throttle.dev_attr.attr, &dev_attr_occ_error.attr, NULL }; @@ -117,7 +169,7 @@ void occ_sysfs_poll_done(struct occ *occ) * On the first poll response, we haven't yet created the sysfs * attributes, so don't make any notify calls. */ - if (!occ->hwmon) + if (!occ->active) goto done; if ((header->status & OCC_STAT_MASTER) != @@ -126,12 +178,6 @@ void occ_sysfs_poll_done(struct occ *occ) sysfs_notify(&occ->bus_dev->kobj, NULL, name); } - if ((header->status & OCC_STAT_ACTIVE) != - (occ->prev_stat & OCC_STAT_ACTIVE)) { - name = sensor_dev_attr_occ_active.dev_attr.attr.name; - sysfs_notify(&occ->bus_dev->kobj, NULL, name); - } - if ((header->ext_status & OCC_EXT_STAT_DVFS_OT) != (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_OT)) { name = sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr.name; @@ -156,12 +202,34 @@ void occ_sysfs_poll_done(struct occ *occ) sysfs_notify(&occ->bus_dev->kobj, NULL, name); } + if ((header->ext_status & OCC_EXT_STAT_DVFS_VDD) != + (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_VDD)) { + name = sensor_dev_attr_occ_dvfs_vdd.dev_attr.attr.name; + sysfs_notify(&occ->bus_dev->kobj, NULL, name); + } + + if ((header->ext_status & OCC_EXT_STAT_GPU_THROTTLE) != + (occ->prev_ext_stat & OCC_EXT_STAT_GPU_THROTTLE)) { + name = sensor_dev_attr_occ_gpu_throttle.dev_attr.attr.name; + sysfs_notify(&occ->bus_dev->kobj, NULL, name); + } + if ((header->status & OCC_STAT_MASTER) && header->occs_present != occ->prev_occs_present) { name = sensor_dev_attr_occs_present.dev_attr.attr.name; sysfs_notify(&occ->bus_dev->kobj, NULL, name); } + if (header->ips_status != occ->prev_ips_status) { + name = sensor_dev_attr_occ_ips_status.dev_attr.attr.name; + sysfs_notify(&occ->bus_dev->kobj, NULL, name); + } + + if (header->mode != occ->prev_mode) { + name = sensor_dev_attr_occ_mode.dev_attr.attr.name; + sysfs_notify(&occ->bus_dev->kobj, NULL, name); + } + if (occ->error && occ->error != occ->prev_error) { name = dev_attr_occ_error.attr.name; sysfs_notify(&occ->bus_dev->kobj, NULL, name); @@ -174,6 +242,8 @@ done: occ->prev_stat = header->status; occ->prev_ext_stat = header->ext_status; occ->prev_occs_present = header->occs_present; + occ->prev_ips_status = header->ips_status; + occ->prev_mode = header->mode; } int occ_setup_sysfs(struct occ *occ) @@ -181,8 +251,7 @@ int occ_setup_sysfs(struct occ *occ) return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs); } -void occ_shutdown(struct occ *occ) +void occ_shutdown_sysfs(struct occ *occ) { sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs); } -EXPORT_SYMBOL_GPL(occ_shutdown); diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 94f4b8b4a2ba..a4adc8bd531f 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -35,6 +35,18 @@ #include <linux/acpi.h> #include <linux/io.h> +#define DRIVER_NAME "pc87360" + +/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ +#define CHAN_CNVRTD 0x80 /* new data ready */ +#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ +#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ +#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ + +#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ +#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ +#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ + static u8 devid; static struct platform_device *pdev; static unsigned short extra_isa[3]; @@ -178,7 +190,7 @@ struct pc87360_data { struct device *hwmon_dev; struct mutex lock; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ int address[3]; @@ -211,183 +223,181 @@ struct pc87360_data { }; /* - * Functions declaration + * ldi is the logical device index + * bank is for voltages and temperatures only */ - -static int pc87360_probe(struct platform_device *pdev); -static int pc87360_remove(struct platform_device *pdev); - static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg); -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value); -static void pc87360_init_device(struct platform_device *pdev, - int use_thermistors); -static struct pc87360_data *pc87360_update_device(struct device *dev); - -/* - * Driver data - */ - -static struct platform_driver pc87360_driver = { - .driver = { - .name = "pc87360", - }, - .probe = pc87360_probe, - .remove = pc87360_remove, -}; + u8 reg) +{ + int res; -/* - * Sysfs stuff - */ + mutex_lock(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + res = inb_p(data->address[ldi] + reg); + mutex_unlock(&(data->lock)); -static ssize_t fan_input_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], - FAN_DIV_FROM_REG(data->fan_status[attr->index]))); -} -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], - FAN_DIV_FROM_REG(data->fan_status[attr->index]))); -} -static ssize_t fan_div_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - FAN_DIV_FROM_REG(data->fan_status[attr->index])); + return res; } -static ssize_t fan_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, + u8 reg, u8 value) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - FAN_STATUS_FROM_REG(data->fan_status[attr->index])); + mutex_lock(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + outb_p(value, data->address[ldi] + reg); + mutex_unlock(&(data->lock)); } -static ssize_t fan_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) + +static void pc87360_autodiv(struct device *dev, int nr) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long fan_min; - int err; - - err = kstrtol(buf, 10, &fan_min); - if (err) - return err; - - mutex_lock(&data->update_lock); - fan_min = FAN_TO_REG(fan_min, - FAN_DIV_FROM_REG(data->fan_status[attr->index])); + u8 old_min = data->fan_min[nr]; - /* If it wouldn't fit, change clock divisor */ - while (fan_min > 255 - && (data->fan_status[attr->index] & 0x60) != 0x60) { - fan_min >>= 1; - data->fan[attr->index] >>= 1; - data->fan_status[attr->index] += 0x20; + /* Increase clock divider if needed and possible */ + if ((data->fan_status[nr] & 0x04) /* overflow flag */ + || (data->fan[nr] >= 224)) { /* next to overflow */ + if ((data->fan_status[nr] & 0x60) != 0x60) { + data->fan_status[nr] += 0x20; + data->fan_min[nr] >>= 1; + data->fan[nr] >>= 1; + dev_dbg(dev, + "Increasing clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); + } + } else { + /* Decrease clock divider if possible */ + while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ + && data->fan[nr] < 85 /* bad accuracy */ + && (data->fan_status[nr] & 0x60) != 0x00) { + data->fan_status[nr] -= 0x20; + data->fan_min[nr] <<= 1; + data->fan[nr] <<= 1; + dev_dbg(dev, + "Decreasing clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), + nr + 1); + } } - data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(attr->index), - data->fan_min[attr->index]); - - /* Write new divider, preserve alarm bits */ - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(attr->index), - data->fan_status[attr->index] & 0xF9); - mutex_unlock(&data->update_lock); - - return count; -} -static struct sensor_device_attribute fan_input[] = { - SENSOR_ATTR_RO(fan1_input, fan_input, 0), - SENSOR_ATTR_RO(fan2_input, fan_input, 1), - SENSOR_ATTR_RO(fan3_input, fan_input, 2), -}; -static struct sensor_device_attribute fan_status[] = { - SENSOR_ATTR_RO(fan1_status, fan_status, 0), - SENSOR_ATTR_RO(fan2_status, fan_status, 1), - SENSOR_ATTR_RO(fan3_status, fan_status, 2), -}; -static struct sensor_device_attribute fan_div[] = { - SENSOR_ATTR_RO(fan1_div, fan_div, 0), - SENSOR_ATTR_RO(fan2_div, fan_div, 1), - SENSOR_ATTR_RO(fan3_div, fan_div, 2), -}; -static struct sensor_device_attribute fan_min[] = { - SENSOR_ATTR_RW(fan1_min, fan_min, 0), - SENSOR_ATTR_RW(fan2_min, fan_min, 1), - SENSOR_ATTR_RW(fan3_min, fan_min, 2), -}; - -#define FAN_UNIT_ATTRS(X) \ -{ &fan_input[X].dev_attr.attr, \ - &fan_status[X].dev_attr.attr, \ - &fan_div[X].dev_attr.attr, \ - &fan_min[X].dev_attr.attr, \ - NULL \ + /* Write new fan min if it changed */ + if (old_min != data->fan_min[nr]) { + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(nr), + data->fan_min[nr]); + } } -static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - PWM_FROM_REG(data->pwm[attr->index], - FAN_CONFIG_INVERT(data->fan_conf, - attr->index))); -} -static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static struct pc87360_data *pc87360_update_device(struct device *dev) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; + u8 i; mutex_lock(&data->update_lock); - data->pwm[attr->index] = PWM_TO_REG(val, - FAN_CONFIG_INVERT(data->fan_conf, attr->index)); - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), - data->pwm[attr->index]); - mutex_unlock(&data->update_lock); - return count; -} -static struct sensor_device_attribute pwm[] = { - SENSOR_ATTR_RW(pwm1, pwm, 0), - SENSOR_ATTR_RW(pwm2, pwm, 1), - SENSOR_ATTR_RW(pwm3, pwm, 2), -}; + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + dev_dbg(dev, "Data update\n"); -static struct attribute *pc8736x_fan_attr[][5] = { - FAN_UNIT_ATTRS(0), - FAN_UNIT_ATTRS(1), - FAN_UNIT_ATTRS(2) -}; + /* Fans */ + for (i = 0; i < data->fannr; i++) { + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { + data->fan_status[i] = + pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN_STATUS(i)); + data->fan[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN(i)); + data->fan_min[i] = pc87360_read_value(data, + LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(i)); + /* Change clock divider if needed */ + pc87360_autodiv(dev, i); + /* Clear bits and write new divider */ + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(i), + data->fan_status[i]); + } + if (FAN_CONFIG_CONTROL(data->fan_conf, i)) + data->pwm[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_PWM(i)); + } -static const struct attribute_group pc8736x_fan_attr_group[] = { - { .attrs = pc8736x_fan_attr[0], }, - { .attrs = pc8736x_fan_attr[1], }, - { .attrs = pc8736x_fan_attr[2], }, -}; + /* Voltages */ + for (i = 0; i < data->innr; i++) { + data->in_status[i] = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_IN, i, + PC87365_REG_IN_STATUS, + data->in_status[i]); + if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { + data->in[i] = pc87360_read_value(data, LD_IN, + i, PC87365_REG_IN); + } + if (data->in_status[i] & CHAN_ENA) { + data->in_min[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MIN); + data->in_max[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MAX); + if (i >= 11) + data->in_crit[i-11] = + pc87360_read_value(data, LD_IN, + i, PC87365_REG_TEMP_CRIT); + } + } + if (data->innr) { + data->in_alarms = pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS1) + | ((pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS2) + & 0x07) << 8); + data->vid = (data->vid_conf & 0xE0) ? + pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_VID) : 0x1F; + } + + /* Temperatures */ + for (i = 0; i < data->tempnr; i++) { + data->temp_status[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS, + data->temp_status[i]); + if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { + data->temp[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP); + } + if (data->temp_status[i] & CHAN_ENA) { + data->temp_min[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MIN); + data->temp_max[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MAX); + data->temp_crit[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_CRIT); + } + } + if (data->tempnr) { + data->temp_alarms = pc87360_read_value(data, LD_TEMP, + NO_BANK, PC87365_REG_TEMP_ALARMS) + & 0x3F; + } + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} static ssize_t in_input_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -397,29 +407,52 @@ static ssize_t in_input_show(struct device *dev, return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } -static ssize_t in_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +static struct sensor_device_attribute in_input[] = { + SENSOR_ATTR_RO(in0_input, in_input, 0), + SENSOR_ATTR_RO(in1_input, in_input, 1), + SENSOR_ATTR_RO(in2_input, in_input, 2), + SENSOR_ATTR_RO(in3_input, in_input, 3), + SENSOR_ATTR_RO(in4_input, in_input, 4), + SENSOR_ATTR_RO(in5_input, in_input, 5), + SENSOR_ATTR_RO(in6_input, in_input, 6), + SENSOR_ATTR_RO(in7_input, in_input, 7), + SENSOR_ATTR_RO(in8_input, in_input, 8), + SENSOR_ATTR_RO(in9_input, in_input, 9), + SENSOR_ATTR_RO(in10_input, in_input, 10), +}; + +static ssize_t in_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], - data->in_vref)); + return sprintf(buf, "%u\n", data->in_status[attr->index]); } -static ssize_t in_max_show(struct device *dev, + +static struct sensor_device_attribute in_status[] = { + SENSOR_ATTR_RO(in0_status, in_status, 0), + SENSOR_ATTR_RO(in1_status, in_status, 1), + SENSOR_ATTR_RO(in2_status, in_status, 2), + SENSOR_ATTR_RO(in3_status, in_status, 3), + SENSOR_ATTR_RO(in4_status, in_status, 4), + SENSOR_ATTR_RO(in5_status, in_status, 5), + SENSOR_ATTR_RO(in6_status, in_status, 6), + SENSOR_ATTR_RO(in7_status, in_status, 7), + SENSOR_ATTR_RO(in8_status, in_status, 8), + SENSOR_ATTR_RO(in9_status, in_status, 9), + SENSOR_ATTR_RO(in10_status, in_status, 10), +}; + +static ssize_t in_min_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } -static ssize_t in_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->in_status[attr->index]); -} + static ssize_t in_min_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -440,6 +473,30 @@ static ssize_t in_min_store(struct device *dev, mutex_unlock(&data->update_lock); return count; } + +static struct sensor_device_attribute in_min[] = { + SENSOR_ATTR_RW(in0_min, in_min, 0), + SENSOR_ATTR_RW(in1_min, in_min, 1), + SENSOR_ATTR_RW(in2_min, in_min, 2), + SENSOR_ATTR_RW(in3_min, in_min, 3), + SENSOR_ATTR_RW(in4_min, in_min, 4), + SENSOR_ATTR_RW(in5_min, in_min, 5), + SENSOR_ATTR_RW(in6_min, in_min, 6), + SENSOR_ATTR_RW(in7_min, in_min, 7), + SENSOR_ATTR_RW(in8_min, in_min, 8), + SENSOR_ATTR_RW(in9_min, in_min, 9), + SENSOR_ATTR_RW(in10_min, in_min, 10), +}; + +static ssize_t in_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + data->in_vref)); +} + static ssize_t in_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -462,45 +519,6 @@ static ssize_t in_max_store(struct device *dev, return count; } -static struct sensor_device_attribute in_input[] = { - SENSOR_ATTR_RO(in0_input, in_input, 0), - SENSOR_ATTR_RO(in1_input, in_input, 1), - SENSOR_ATTR_RO(in2_input, in_input, 2), - SENSOR_ATTR_RO(in3_input, in_input, 3), - SENSOR_ATTR_RO(in4_input, in_input, 4), - SENSOR_ATTR_RO(in5_input, in_input, 5), - SENSOR_ATTR_RO(in6_input, in_input, 6), - SENSOR_ATTR_RO(in7_input, in_input, 7), - SENSOR_ATTR_RO(in8_input, in_input, 8), - SENSOR_ATTR_RO(in9_input, in_input, 9), - SENSOR_ATTR_RO(in10_input, in_input, 10), -}; -static struct sensor_device_attribute in_status[] = { - SENSOR_ATTR_RO(in0_status, in_status, 0), - SENSOR_ATTR_RO(in1_status, in_status, 1), - SENSOR_ATTR_RO(in2_status, in_status, 2), - SENSOR_ATTR_RO(in3_status, in_status, 3), - SENSOR_ATTR_RO(in4_status, in_status, 4), - SENSOR_ATTR_RO(in5_status, in_status, 5), - SENSOR_ATTR_RO(in6_status, in_status, 6), - SENSOR_ATTR_RO(in7_status, in_status, 7), - SENSOR_ATTR_RO(in8_status, in_status, 8), - SENSOR_ATTR_RO(in9_status, in_status, 9), - SENSOR_ATTR_RO(in10_status, in_status, 10), -}; -static struct sensor_device_attribute in_min[] = { - SENSOR_ATTR_RW(in0_min, in_min, 0), - SENSOR_ATTR_RW(in1_min, in_min, 1), - SENSOR_ATTR_RW(in2_min, in_min, 2), - SENSOR_ATTR_RW(in3_min, in_min, 3), - SENSOR_ATTR_RW(in4_min, in_min, 4), - SENSOR_ATTR_RW(in5_min, in_min, 5), - SENSOR_ATTR_RW(in6_min, in_min, 6), - SENSOR_ATTR_RW(in7_min, in_min, 7), - SENSOR_ATTR_RW(in8_min, in_min, 8), - SENSOR_ATTR_RW(in9_min, in_min, 9), - SENSOR_ATTR_RW(in10_min, in_min, 10), -}; static struct sensor_device_attribute in_max[] = { SENSOR_ATTR_RW(in0_max, in_max, 0), SENSOR_ATTR_RW(in1_max, in_max, 1), @@ -534,14 +552,6 @@ static ssize_t in_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); } -static ssize_t in_max_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - unsigned nr = to_sensor_dev_attr(devattr)->index; - - return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); -} static struct sensor_device_attribute in_min_alarm[] = { SENSOR_ATTR_RO(in0_min_alarm, in_min_alarm, 0), @@ -556,6 +566,16 @@ static struct sensor_device_attribute in_min_alarm[] = { SENSOR_ATTR_RO(in9_min_alarm, in_min_alarm, 9), SENSOR_ATTR_RO(in10_min_alarm, in_min_alarm, 10), }; + +static ssize_t in_max_alarm_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); +} + static struct sensor_device_attribute in_max_alarm[] = { SENSOR_ATTR_RO(in0_max_alarm, in_max_alarm, 0), SENSOR_ATTR_RO(in1_max_alarm, in_max_alarm, 1), @@ -592,6 +612,7 @@ static ssize_t vrm_show(struct device *dev, struct device_attribute *attr, struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->vrm); } + static ssize_t vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -648,37 +669,39 @@ static ssize_t therm_input_show(struct device *dev, return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } -static ssize_t therm_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +/* + * the +11 term below reflects the fact that VLM units 11,12,13 are + * used in the chip to measure voltage across the thermistors + */ +static struct sensor_device_attribute therm_input[] = { + SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11), + SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11), + SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11), +}; + +static ssize_t therm_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], - data->in_vref)); + return sprintf(buf, "%u\n", data->in_status[attr->index]); } -static ssize_t therm_max_show(struct device *dev, + +static struct sensor_device_attribute therm_status[] = { + SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11), + SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11), + SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11), +}; + +static ssize_t therm_min_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], - data->in_vref)); -} -static ssize_t therm_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } -static ssize_t therm_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->in_status[attr->index]); -} static ssize_t therm_min_store(struct device *dev, struct device_attribute *devattr, @@ -701,6 +724,21 @@ static ssize_t therm_min_store(struct device *dev, return count; } +static struct sensor_device_attribute therm_min[] = { + SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11), + SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11), + SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11), +}; + +static ssize_t therm_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + data->in_vref)); +} + static ssize_t therm_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -721,6 +759,22 @@ static ssize_t therm_max_store(struct device *dev, mutex_unlock(&data->update_lock); return count; } + +static struct sensor_device_attribute therm_max[] = { + SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11), + SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11), + SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11), +}; + +static ssize_t therm_crit_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], + data->in_vref)); +} + static ssize_t therm_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -742,30 +796,6 @@ static ssize_t therm_crit_store(struct device *dev, return count; } -/* - * the +11 term below reflects the fact that VLM units 11,12,13 are - * used in the chip to measure voltage across the thermistors - */ -static struct sensor_device_attribute therm_input[] = { - SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11), - SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11), - SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11), -}; -static struct sensor_device_attribute therm_status[] = { - SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11), - SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11), - SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11), -}; -static struct sensor_device_attribute therm_min[] = { - SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11), - SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11), - SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11), -}; -static struct sensor_device_attribute therm_max[] = { - SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11), - SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11), - SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11), -}; static struct sensor_device_attribute therm_crit[] = { SENSOR_ATTR_RW(temp4_crit, therm_crit, 0 + 11), SENSOR_ATTR_RW(temp5_crit, therm_crit, 1 + 11), @@ -776,7 +806,6 @@ static struct sensor_device_attribute therm_crit[] = { * show_therm_min/max_alarm() reads data from the per-channel voltage * status register (sec 11.5.12) */ - static ssize_t therm_min_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -786,6 +815,13 @@ static ssize_t therm_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); } + +static struct sensor_device_attribute therm_min_alarm[] = { + SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11), + SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11), + SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11), +}; + static ssize_t therm_max_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -795,6 +831,13 @@ static ssize_t therm_max_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); } + +static struct sensor_device_attribute therm_max_alarm[] = { + SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11), + SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11), + SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11), +}; + static ssize_t therm_crit_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -805,16 +848,6 @@ static ssize_t therm_crit_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & TEMP_ALM_CRIT)); } -static struct sensor_device_attribute therm_min_alarm[] = { - SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11), - SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11), - SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11), -}; -static struct sensor_device_attribute therm_max_alarm[] = { - SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11), - SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11), - SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11), -}; static struct sensor_device_attribute therm_crit_alarm[] = { SENSOR_ATTR_RO(temp4_crit_alarm, therm_crit_alarm, 0 + 11), SENSOR_ATTR_RO(temp5_crit_alarm, therm_crit_alarm, 1 + 11), @@ -849,37 +882,32 @@ static ssize_t temp_input_show(struct device *dev, return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); } -static ssize_t temp_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); -} +static struct sensor_device_attribute temp_input[] = { + SENSOR_ATTR_RO(temp1_input, temp_input, 0), + SENSOR_ATTR_RO(temp2_input, temp_input, 1), + SENSOR_ATTR_RO(temp3_input, temp_input, 2), +}; -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t temp_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); + return sprintf(buf, "%d\n", data->temp_status[attr->index]); } -static ssize_t temp_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", - TEMP_FROM_REG(data->temp_crit[attr->index])); -} +static struct sensor_device_attribute temp_status[] = { + SENSOR_ATTR_RO(temp1_status, temp_status, 0), + SENSOR_ATTR_RO(temp2_status, temp_status, 1), + SENSOR_ATTR_RO(temp3_status, temp_status, 2), +}; -static ssize_t temp_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t temp_min_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", data->temp_status[attr->index]); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); } static ssize_t temp_min_store(struct device *dev, @@ -903,6 +931,20 @@ static ssize_t temp_min_store(struct device *dev, return count; } +static struct sensor_device_attribute temp_min[] = { + SENSOR_ATTR_RW(temp1_min, temp_min, 0), + SENSOR_ATTR_RW(temp2_min, temp_min, 1), + SENSOR_ATTR_RW(temp3_min, temp_min, 2), +}; + +static ssize_t temp_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); +} + static ssize_t temp_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -924,6 +966,21 @@ static ssize_t temp_max_store(struct device *dev, return count; } +static struct sensor_device_attribute temp_max[] = { + SENSOR_ATTR_RW(temp1_max, temp_max, 0), + SENSOR_ATTR_RW(temp2_max, temp_max, 1), + SENSOR_ATTR_RW(temp3_max, temp_max, 2), +}; + +static ssize_t temp_crit_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%d\n", + TEMP_FROM_REG(data->temp_crit[attr->index])); +} + static ssize_t temp_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -945,47 +1002,17 @@ static ssize_t temp_crit_store(struct device *dev, return count; } -static struct sensor_device_attribute temp_input[] = { - SENSOR_ATTR_RO(temp1_input, temp_input, 0), - SENSOR_ATTR_RO(temp2_input, temp_input, 1), - SENSOR_ATTR_RO(temp3_input, temp_input, 2), -}; -static struct sensor_device_attribute temp_status[] = { - SENSOR_ATTR_RO(temp1_status, temp_status, 0), - SENSOR_ATTR_RO(temp2_status, temp_status, 1), - SENSOR_ATTR_RO(temp3_status, temp_status, 2), -}; -static struct sensor_device_attribute temp_min[] = { - SENSOR_ATTR_RW(temp1_min, temp_min, 0), - SENSOR_ATTR_RW(temp2_min, temp_min, 1), - SENSOR_ATTR_RW(temp3_min, temp_min, 2), -}; -static struct sensor_device_attribute temp_max[] = { - SENSOR_ATTR_RW(temp1_max, temp_max, 0), - SENSOR_ATTR_RW(temp2_max, temp_max, 1), - SENSOR_ATTR_RW(temp3_max, temp_max, 2), -}; static struct sensor_device_attribute temp_crit[] = { SENSOR_ATTR_RW(temp1_crit, temp_crit, 0), SENSOR_ATTR_RW(temp2_crit, temp_crit, 1), SENSOR_ATTR_RW(temp3_crit, temp_crit, 2), }; -static ssize_t alarms_temp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->temp_alarms); -} - -static DEVICE_ATTR_RO(alarms_temp); - /* - * show_temp_min/max_alarm() reads data from the per-channel status + * temp_min/max_alarm_show() reads data from the per-channel status * register (sec 12.3.7), not the temp event status registers (sec * 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) */ - static ssize_t temp_min_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -996,6 +1023,12 @@ static ssize_t temp_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN)); } +static struct sensor_device_attribute temp_min_alarm[] = { + SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0), + SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1), + SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2), +}; + static ssize_t temp_max_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1006,6 +1039,12 @@ static ssize_t temp_max_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX)); } +static struct sensor_device_attribute temp_max_alarm[] = { + SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0), + SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1), + SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2), +}; + static ssize_t temp_crit_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1016,18 +1055,6 @@ static ssize_t temp_crit_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT)); } -static struct sensor_device_attribute temp_min_alarm[] = { - SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0), - SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1), - SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2), -}; - -static struct sensor_device_attribute temp_max_alarm[] = { - SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0), - SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1), - SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2), -}; - static struct sensor_device_attribute temp_crit_alarm[] = { SENSOR_ATTR_RO(temp1_crit_alarm, temp_crit_alarm, 0), SENSOR_ATTR_RO(temp2_crit_alarm, temp_crit_alarm, 1), @@ -1043,6 +1070,7 @@ static ssize_t temp_fault_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_FAULT)); } + static struct sensor_device_attribute temp_fault[] = { SENSOR_ATTR_RO(temp1_fault, temp_fault, 0), SENSOR_ATTR_RO(temp2_fault, temp_fault, 1), @@ -1074,106 +1102,180 @@ static const struct attribute_group pc8736x_temp_attr_group[] = { { .attrs = pc8736x_temp_attr[2] } }; -static ssize_t name_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t alarms_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct pc87360_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->name); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->temp_alarms); } -static DEVICE_ATTR_RO(name); +static DEVICE_ATTR_RO(alarms_temp); -/* - * Device detection, registration and update - */ +static ssize_t fan_input_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], + FAN_DIV_FROM_REG(data->fan_status[attr->index]))); +} -static int __init pc87360_find(int sioaddr, u8 *devid, - unsigned short *addresses) +static struct sensor_device_attribute fan_input[] = { + SENSOR_ATTR_RO(fan1_input, fan_input, 0), + SENSOR_ATTR_RO(fan2_input, fan_input, 1), + SENSOR_ATTR_RO(fan3_input, fan_input, 2), +}; + +static ssize_t fan_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { - u16 val; - int i; - int nrdev; /* logical device count */ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + FAN_STATUS_FROM_REG(data->fan_status[attr->index])); +} - /* No superio_enter */ +static struct sensor_device_attribute fan_status[] = { + SENSOR_ATTR_RO(fan1_status, fan_status, 0), + SENSOR_ATTR_RO(fan2_status, fan_status, 1), + SENSOR_ATTR_RO(fan3_status, fan_status, 2), +}; - /* Identify device */ - val = force_id ? force_id : superio_inb(sioaddr, DEVID); - switch (val) { - case 0xE1: /* PC87360 */ - case 0xE8: /* PC87363 */ - case 0xE4: /* PC87364 */ - nrdev = 1; - break; - case 0xE5: /* PC87365 */ - case 0xE9: /* PC87366 */ - nrdev = 3; - break; - default: - superio_exit(sioaddr); - return -ENODEV; +static ssize_t fan_div_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + FAN_DIV_FROM_REG(data->fan_status[attr->index])); +} + +static struct sensor_device_attribute fan_div[] = { + SENSOR_ATTR_RO(fan1_div, fan_div, 0), + SENSOR_ATTR_RO(fan2_div, fan_div, 1), + SENSOR_ATTR_RO(fan3_div, fan_div, 2), +}; + +static ssize_t fan_min_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], + FAN_DIV_FROM_REG(data->fan_status[attr->index]))); +} + +static ssize_t fan_min_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = dev_get_drvdata(dev); + long fan_min; + int err; + + err = kstrtol(buf, 10, &fan_min); + if (err) + return err; + + mutex_lock(&data->update_lock); + fan_min = FAN_TO_REG(fan_min, + FAN_DIV_FROM_REG(data->fan_status[attr->index])); + + /* If it wouldn't fit, change clock divisor */ + while (fan_min > 255 + && (data->fan_status[attr->index] & 0x60) != 0x60) { + fan_min >>= 1; + data->fan[attr->index] >>= 1; + data->fan_status[attr->index] += 0x20; } - /* Remember the device id */ - *devid = val; + data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(attr->index), + data->fan_min[attr->index]); - for (i = 0; i < nrdev; i++) { - /* select logical device */ - superio_outb(sioaddr, DEV, logdev[i]); + /* Write new divider, preserve alarm bits */ + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(attr->index), + data->fan_status[attr->index] & 0xF9); + mutex_unlock(&data->update_lock); - val = superio_inb(sioaddr, ACT); - if (!(val & 0x01)) { - pr_info("Device 0x%02x not activated\n", logdev[i]); - continue; - } + return count; +} - val = (superio_inb(sioaddr, BASE) << 8) - | superio_inb(sioaddr, BASE + 1); - if (!val) { - pr_info("Base address not set for device 0x%02x\n", - logdev[i]); - continue; - } +static struct sensor_device_attribute fan_min[] = { + SENSOR_ATTR_RW(fan1_min, fan_min, 0), + SENSOR_ATTR_RW(fan2_min, fan_min, 1), + SENSOR_ATTR_RW(fan3_min, fan_min, 2), +}; - addresses[i] = val; +#define FAN_UNIT_ATTRS(X) \ +{ &fan_input[X].dev_attr.attr, \ + &fan_status[X].dev_attr.attr, \ + &fan_div[X].dev_attr.attr, \ + &fan_min[X].dev_attr.attr, \ + NULL \ +} - if (i == 0) { /* Fans */ - confreg[0] = superio_inb(sioaddr, 0xF0); - confreg[1] = superio_inb(sioaddr, 0xF1); +static struct attribute *pc8736x_fan_attr[][5] = { + FAN_UNIT_ATTRS(0), + FAN_UNIT_ATTRS(1), + FAN_UNIT_ATTRS(2) +}; - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, - (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, - (confreg[0] >> 4) & 1); - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, - (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, - (confreg[0] >> 7) & 1); - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, - confreg[1] & 1, (confreg[1] >> 1) & 1, - (confreg[1] >> 2) & 1); - } else if (i == 1) { /* Voltages */ - /* Are we using thermistors? */ - if (*devid == 0xE9) { /* PC87366 */ - /* - * These registers are not logical-device - * specific, just that we won't need them if - * we don't use the VLM device - */ - confreg[2] = superio_inb(sioaddr, 0x2B); - confreg[3] = superio_inb(sioaddr, 0x25); +static const struct attribute_group pc8736x_fan_attr_group[] = { + { .attrs = pc8736x_fan_attr[0], }, + { .attrs = pc8736x_fan_attr[1], }, + { .attrs = pc8736x_fan_attr[2], }, +}; - if (confreg[2] & 0x40) { - pr_info("Using thermistors for temperature monitoring\n"); - } - if (confreg[3] & 0xE0) { - pr_info("VID inputs routed (mode %u)\n", - confreg[3] >> 5); - } - } - } - } +static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + PWM_FROM_REG(data->pwm[attr->index], + FAN_CONFIG_INVERT(data->fan_conf, + attr->index))); +} - superio_exit(sioaddr); - return 0; +static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = dev_get_drvdata(dev); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->pwm[attr->index] = PWM_TO_REG(val, + FAN_CONFIG_INVERT(data->fan_conf, attr->index)); + pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), + data->pwm[attr->index]); + mutex_unlock(&data->update_lock); + return count; } +static struct sensor_device_attribute pwm[] = { + SENSOR_ATTR_RW(pwm1, pwm, 0), + SENSOR_ATTR_RW(pwm2, pwm, 1), + SENSOR_ATTR_RW(pwm3, pwm, 2), +}; + +static ssize_t name_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->name); +} + +static DEVICE_ATTR_RO(name); + static void pc87360_remove_files(struct device *dev) { int i; @@ -1190,6 +1292,146 @@ static void pc87360_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &pc8736x_vin_group); } +static void pc87360_init_device(struct platform_device *pdev, + int use_thermistors) +{ + struct pc87360_data *data = platform_get_drvdata(pdev); + int i, nr; + const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; + const u8 init_temp[3] = { 2, 2, 1 }; + u8 reg; + + if (init >= 2 && data->innr) { + reg = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONVRATE); + dev_info(&pdev->dev, + "VLM conversion set to 1s period, 160us delay\n"); + pc87360_write_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONVRATE, + (reg & 0xC0) | 0x11); + } + + nr = data->innr < 11 ? data->innr : 11; + for (i = 0; i < nr; i++) { + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg); + if (init >= init_in[i]) { + /* Forcibly enable voltage channel */ + if (!(reg & CHAN_ENA)) { + dev_dbg(&pdev->dev, "Forcibly enabling in%d\n", + i); + pc87360_write_value(data, LD_IN, i, + PC87365_REG_IN_STATUS, + (reg & 0x68) | 0x87); + } + } + } + + /* + * We can't blindly trust the Super-I/O space configuration bit, + * most BIOS won't set it properly + */ + dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); + for (i = 11; i < data->innr; i++) { + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_TEMP_STATUS); + use_thermistors = use_thermistors || (reg & CHAN_ENA); + /* thermistors are temp[4-6], measured on vin[11-14] */ + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg); + } + dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors); + + i = use_thermistors ? 2 : 0; + for (; i < data->tempnr; i++) { + reg = pc87360_read_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i + 1, reg); + if (init >= init_temp[i]) { + /* Forcibly enable temperature channel */ + if (!(reg & CHAN_ENA)) { + dev_dbg(&pdev->dev, + "Forcibly enabling temp%d\n", i + 1); + pc87360_write_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS, + 0xCF); + } + } + } + + if (use_thermistors) { + for (i = 11; i < data->innr; i++) { + if (init >= init_in[i]) { + /* + * The pin may already be used by thermal + * diodes + */ + reg = pc87360_read_value(data, LD_TEMP, + (i - 11) / 2, PC87365_REG_TEMP_STATUS); + if (reg & CHAN_ENA) { + dev_dbg(&pdev->dev, + "Skipping temp%d, pin already in use by temp%d\n", + i - 7, (i - 11) / 2); + continue; + } + + /* Forcibly enable thermistor channel */ + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + if (!(reg & CHAN_ENA)) { + dev_dbg(&pdev->dev, + "Forcibly enabling temp%d\n", + i - 7); + pc87360_write_value(data, LD_IN, i, + PC87365_REG_TEMP_STATUS, + (reg & 0x60) | 0x8F); + } + } + } + } + + if (data->innr) { + reg = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG); + dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); + if (reg & CHAN_ENA) { + dev_dbg(&pdev->dev, + "Forcibly enabling monitoring (VLM)\n"); + pc87360_write_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG, + reg & 0xFE); + } + } + + if (data->tempnr) { + reg = pc87360_read_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG); + dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); + if (reg & CHAN_ENA) { + dev_dbg(&pdev->dev, + "Forcibly enabling monitoring (TMS)\n"); + pc87360_write_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG, + reg & 0xFE); + } + + if (init >= 2) { + /* Chip config as documented by National Semi. */ + pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); + /* + * We voluntarily omit the bank here, in case the + * sequence itself matters. It shouldn't be a problem, + * since nobody else is supposed to access the + * device at that point. + */ + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); + } + } +} + static int pc87360_probe(struct platform_device *pdev) { int i; @@ -1239,7 +1481,7 @@ static int pc87360_probe(struct platform_device *pdev) data->address[i] = extra_isa[i]; if (data->address[i] && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, - pc87360_driver.driver.name)) { + DRIVER_NAME)) { dev_err(dev, "Region 0x%x-0x%x already in use!\n", extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); @@ -1355,330 +1597,105 @@ static int pc87360_remove(struct platform_device *pdev) } /* - * ldi is the logical device index - * bank is for voltages and temperatures only + * Driver data */ -static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg) -{ - int res; - - mutex_lock(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - res = inb_p(data->address[ldi] + reg); - mutex_unlock(&(data->lock)); +static struct platform_driver pc87360_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = pc87360_probe, + .remove = pc87360_remove, +}; - return res; -} +/* + * Device detection, registration and update + */ -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value) +static int __init pc87360_find(int sioaddr, u8 *devid, + unsigned short *addresses) { - mutex_lock(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - outb_p(value, data->address[ldi] + reg); - mutex_unlock(&(data->lock)); -} + u16 val; + int i; + int nrdev; /* logical device count */ -/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ -#define CHAN_CNVRTD 0x80 /* new data ready */ -#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ -#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ -#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ + /* No superio_enter */ -#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ -#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ -#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ + /* Identify device */ + val = force_id ? force_id : superio_inb(sioaddr, DEVID); + switch (val) { + case 0xE1: /* PC87360 */ + case 0xE8: /* PC87363 */ + case 0xE4: /* PC87364 */ + nrdev = 1; + break; + case 0xE5: /* PC87365 */ + case 0xE9: /* PC87366 */ + nrdev = 3; + break; + default: + superio_exit(sioaddr); + return -ENODEV; + } + /* Remember the device id */ + *devid = val; -static void pc87360_init_device(struct platform_device *pdev, - int use_thermistors) -{ - struct pc87360_data *data = platform_get_drvdata(pdev); - int i, nr; - const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; - const u8 init_temp[3] = { 2, 2, 1 }; - u8 reg; + for (i = 0; i < nrdev; i++) { + /* select logical device */ + superio_outb(sioaddr, DEV, logdev[i]); - if (init >= 2 && data->innr) { - reg = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONVRATE); - dev_info(&pdev->dev, - "VLM conversion set to 1s period, 160us delay\n"); - pc87360_write_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONVRATE, - (reg & 0xC0) | 0x11); - } + val = superio_inb(sioaddr, ACT); + if (!(val & 0x01)) { + pr_info("Device 0x%02x not activated\n", logdev[i]); + continue; + } - nr = data->innr < 11 ? data->innr : 11; - for (i = 0; i < nr; i++) { - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg); - if (init >= init_in[i]) { - /* Forcibly enable voltage channel */ - if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, "Forcibly enabling in%d\n", - i); - pc87360_write_value(data, LD_IN, i, - PC87365_REG_IN_STATUS, - (reg & 0x68) | 0x87); - } + val = (superio_inb(sioaddr, BASE) << 8) + | superio_inb(sioaddr, BASE + 1); + if (!val) { + pr_info("Base address not set for device 0x%02x\n", + logdev[i]); + continue; } - } - /* - * We can't blindly trust the Super-I/O space configuration bit, - * most BIOS won't set it properly - */ - dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); - for (i = 11; i < data->innr; i++) { - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_TEMP_STATUS); - use_thermistors = use_thermistors || (reg & CHAN_ENA); - /* thermistors are temp[4-6], measured on vin[11-14] */ - dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg); - } - dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors); + addresses[i] = val; - i = use_thermistors ? 2 : 0; - for (; i < data->tempnr; i++) { - reg = pc87360_read_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i + 1, reg); - if (init >= init_temp[i]) { - /* Forcibly enable temperature channel */ - if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, - "Forcibly enabling temp%d\n", i + 1); - pc87360_write_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS, - 0xCF); - } - } - } + if (i == 0) { /* Fans */ + confreg[0] = superio_inb(sioaddr, 0xF0); + confreg[1] = superio_inb(sioaddr, 0xF1); - if (use_thermistors) { - for (i = 11; i < data->innr; i++) { - if (init >= init_in[i]) { + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, + (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, + (confreg[0] >> 4) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, + (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, + (confreg[0] >> 7) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, + confreg[1] & 1, (confreg[1] >> 1) & 1, + (confreg[1] >> 2) & 1); + } else if (i == 1) { /* Voltages */ + /* Are we using thermistors? */ + if (*devid == 0xE9) { /* PC87366 */ /* - * The pin may already be used by thermal - * diodes + * These registers are not logical-device + * specific, just that we won't need them if + * we don't use the VLM device */ - reg = pc87360_read_value(data, LD_TEMP, - (i - 11) / 2, PC87365_REG_TEMP_STATUS); - if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, - "Skipping temp%d, pin already in use by temp%d\n", - i - 7, (i - 11) / 2); - continue; - } + confreg[2] = superio_inb(sioaddr, 0x2B); + confreg[3] = superio_inb(sioaddr, 0x25); - /* Forcibly enable thermistor channel */ - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, - "Forcibly enabling temp%d\n", - i - 7); - pc87360_write_value(data, LD_IN, i, - PC87365_REG_TEMP_STATUS, - (reg & 0x60) | 0x8F); + if (confreg[2] & 0x40) { + pr_info("Using thermistors for temperature monitoring\n"); + } + if (confreg[3] & 0xE0) { + pr_info("VID inputs routed (mode %u)\n", + confreg[3] >> 5); } } } } - if (data->innr) { - reg = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG); - dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); - if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, - "Forcibly enabling monitoring (VLM)\n"); - pc87360_write_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG, - reg & 0xFE); - } - } - - if (data->tempnr) { - reg = pc87360_read_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG); - dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); - if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, - "Forcibly enabling monitoring (TMS)\n"); - pc87360_write_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG, - reg & 0xFE); - } - - if (init >= 2) { - /* Chip config as documented by National Semi. */ - pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); - /* - * We voluntarily omit the bank here, in case the - * sequence itself matters. It shouldn't be a problem, - * since nobody else is supposed to access the - * device at that point. - */ - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); - } - } -} - -static void pc87360_autodiv(struct device *dev, int nr) -{ - struct pc87360_data *data = dev_get_drvdata(dev); - u8 old_min = data->fan_min[nr]; - - /* Increase clock divider if needed and possible */ - if ((data->fan_status[nr] & 0x04) /* overflow flag */ - || (data->fan[nr] >= 224)) { /* next to overflow */ - if ((data->fan_status[nr] & 0x60) != 0x60) { - data->fan_status[nr] += 0x20; - data->fan_min[nr] >>= 1; - data->fan[nr] >>= 1; - dev_dbg(dev, - "Increasing clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); - } - } else { - /* Decrease clock divider if possible */ - while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ - && data->fan[nr] < 85 /* bad accuracy */ - && (data->fan_status[nr] & 0x60) != 0x00) { - data->fan_status[nr] -= 0x20; - data->fan_min[nr] <<= 1; - data->fan[nr] <<= 1; - dev_dbg(dev, - "Decreasing clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), - nr + 1); - } - } - - /* Write new fan min if it changed */ - if (old_min != data->fan_min[nr]) { - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(nr), - data->fan_min[nr]); - } -} - -static struct pc87360_data *pc87360_update_device(struct device *dev) -{ - struct pc87360_data *data = dev_get_drvdata(dev); - u8 i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(dev, "Data update\n"); - - /* Fans */ - for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { - data->fan_status[i] = - pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN_STATUS(i)); - data->fan[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN(i)); - data->fan_min[i] = pc87360_read_value(data, - LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(i)); - /* Change clock divider if needed */ - pc87360_autodiv(dev, i); - /* Clear bits and write new divider */ - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(i), - data->fan_status[i]); - } - if (FAN_CONFIG_CONTROL(data->fan_conf, i)) - data->pwm[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_PWM(i)); - } - - /* Voltages */ - for (i = 0; i < data->innr; i++) { - data->in_status[i] = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_IN, i, - PC87365_REG_IN_STATUS, - data->in_status[i]); - if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { - data->in[i] = pc87360_read_value(data, LD_IN, - i, PC87365_REG_IN); - } - if (data->in_status[i] & CHAN_ENA) { - data->in_min[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MIN); - data->in_max[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MAX); - if (i >= 11) - data->in_crit[i-11] = - pc87360_read_value(data, LD_IN, - i, PC87365_REG_TEMP_CRIT); - } - } - if (data->innr) { - data->in_alarms = pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS1) - | ((pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS2) - & 0x07) << 8); - data->vid = (data->vid_conf & 0xE0) ? - pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_VID) : 0x1F; - } - - /* Temperatures */ - for (i = 0; i < data->tempnr; i++) { - data->temp_status[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS, - data->temp_status[i]); - if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { - data->temp[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP); - } - if (data->temp_status[i] & CHAN_ENA) { - data->temp_min[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MIN); - data->temp_max[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MAX); - data->temp_crit[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_CRIT); - } - } - if (data->tempnr) { - data->temp_alarms = pc87360_read_value(data, LD_TEMP, - NO_BANK, PC87365_REG_TEMP_ALARMS) - & 0x3F; - } - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; + superio_exit(sioaddr); + return 0; } static int __init pc87360_device_add(unsigned short address) @@ -1700,8 +1717,8 @@ static int __init pc87360_device_add(unsigned short address) continue; res[res_count].start = extra_isa[i]; res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1; - res[res_count].name = "pc87360", - res[res_count].flags = IORESOURCE_IO, + res[res_count].name = "pc87360"; + res[res_count].flags = IORESOURCE_IO; err = acpi_check_resource_conflict(&res[res_count]); if (err) @@ -1777,10 +1794,10 @@ static void __exit pc87360_exit(void) platform_driver_unregister(&pc87360_driver); } - MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("PC8736x hardware monitor"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); module_init(pc87360_init); module_exit(pc87360_exit); diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index b7a3a292123d..af9614e918a4 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -179,8 +179,7 @@ static const struct attribute_group pcf8591_attr_group_opt = { * Real code */ -static int pcf8591_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf8591_probe(struct i2c_client *client) { struct pcf8591_data *data; int err; @@ -229,14 +228,13 @@ exit_sysfs_remove: return err; } -static int pcf8591_remove(struct i2c_client *client) +static void pcf8591_remove(struct i2c_client *client) { struct pcf8591_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); - return 0; } /* Called when we have found a new PCF8591. */ @@ -295,7 +293,7 @@ static struct i2c_driver pcf8591_driver = { .driver = { .name = "pcf8591", }, - .probe = pcf8591_probe, + .probe_new = pcf8591_probe, .remove = pcf8591_remove, .id_table = pcf8591_id, }; diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig new file mode 100644 index 000000000000..9d32a57badfe --- /dev/null +++ b/drivers/hwmon/peci/Kconfig @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SENSORS_PECI_CPUTEMP + tristate "PECI CPU temperature monitoring client" + depends on PECI + select SENSORS_PECI + select PECI_CPU + help + If you say yes here you get support for the generic Intel PECI + cputemp driver which provides Digital Thermal Sensor (DTS) thermal + readings of the CPU package and CPU cores that are accessible via + the processor PECI interface. + + This driver can also be built as a module. If so, the module + will be called peci-cputemp. + +config SENSORS_PECI_DIMMTEMP + tristate "PECI DIMM temperature monitoring client" + depends on PECI + select SENSORS_PECI + select PECI_CPU + help + If you say yes here you get support for the generic Intel PECI hwmon + driver which provides Temperature Sensor on DIMM readings that are + accessible via the processor PECI interface. + + This driver can also be built as a module. If so, the module + will be called peci-dimmtemp. + +config SENSORS_PECI + tristate diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile new file mode 100644 index 000000000000..191cfa0227f3 --- /dev/null +++ b/drivers/hwmon/peci/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only + +peci-cputemp-y := cputemp.o +peci-dimmtemp-y := dimmtemp.o + +obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h new file mode 100644 index 000000000000..734506b0eca2 --- /dev/null +++ b/drivers/hwmon/peci/common.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2021 Intel Corporation */ + +#include <linux/mutex.h> +#include <linux/types.h> + +#ifndef __PECI_HWMON_COMMON_H +#define __PECI_HWMON_COMMON_H + +#define PECI_HWMON_UPDATE_INTERVAL HZ + +/** + * struct peci_sensor_state - PECI state information + * @valid: flag to indicate the sensor value is valid + * @last_updated: time of the last update in jiffies + * @lock: mutex to protect sensor access + */ +struct peci_sensor_state { + bool valid; + unsigned long last_updated; + struct mutex lock; /* protect sensor access */ +}; + +/** + * struct peci_sensor_data - PECI sensor information + * @value: sensor value in milli units + * @state: sensor update state + */ + +struct peci_sensor_data { + s32 value; + struct peci_sensor_state state; +}; + +/** + * peci_sensor_need_update() - check whether sensor update is needed or not + * @sensor: pointer to sensor data struct + * + * Return: true if update is needed, false if not. + */ + +static inline bool peci_sensor_need_update(struct peci_sensor_state *state) +{ + return !state->valid || + time_after(jiffies, state->last_updated + PECI_HWMON_UPDATE_INTERVAL); +} + +/** + * peci_sensor_mark_updated() - mark the sensor is updated + * @sensor: pointer to sensor data struct + */ +static inline void peci_sensor_mark_updated(struct peci_sensor_state *state) +{ + state->valid = true; + state->last_updated = jiffies; +} + +#endif /* __PECI_HWMON_COMMON_H */ diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c new file mode 100644 index 000000000000..57470fda5f6c --- /dev/null +++ b/drivers/hwmon/peci/cputemp.c @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/peci.h> +#include <linux/peci-cpu.h> +#include <linux/units.h> + +#include "common.h" + +#define CORE_NUMS_MAX 64 + +#define BASE_CHANNEL_NUMS 5 +#define CPUTEMP_CHANNEL_NUMS (BASE_CHANNEL_NUMS + CORE_NUMS_MAX) + +#define TEMP_TARGET_FAN_TEMP_MASK GENMASK(15, 8) +#define TEMP_TARGET_REF_TEMP_MASK GENMASK(23, 16) +#define TEMP_TARGET_TJ_OFFSET_MASK GENMASK(29, 24) + +#define DTS_MARGIN_MASK GENMASK(15, 0) +#define PCS_MODULE_TEMP_MASK GENMASK(15, 0) + +struct resolved_cores_reg { + u8 bus; + u8 dev; + u8 func; + u8 offset; +}; + +struct cpu_info { + struct resolved_cores_reg *reg; + u8 min_peci_revision; + s32 (*thermal_margin_to_millidegree)(u16 val); +}; + +struct peci_temp_target { + s32 tcontrol; + s32 tthrottle; + s32 tjmax; + struct peci_sensor_state state; +}; + +enum peci_temp_target_type { + tcontrol_type, + tthrottle_type, + tjmax_type, + crit_hyst_type, +}; + +struct peci_cputemp { + struct peci_device *peci_dev; + struct device *dev; + const char *name; + const struct cpu_info *gen_info; + struct { + struct peci_temp_target target; + struct peci_sensor_data die; + struct peci_sensor_data dts; + struct peci_sensor_data core[CORE_NUMS_MAX]; + } temp; + const char **coretemp_label; + DECLARE_BITMAP(core_mask, CORE_NUMS_MAX); +}; + +enum cputemp_channels { + channel_die, + channel_dts, + channel_tcontrol, + channel_tthrottle, + channel_tjmax, + channel_core, +}; + +static const char * const cputemp_label[BASE_CHANNEL_NUMS] = { + "Die", + "DTS", + "Tcontrol", + "Tthrottle", + "Tjmax", +}; + +static int update_temp_target(struct peci_cputemp *priv) +{ + s32 tthrottle_offset, tcontrol_margin; + u32 pcs; + int ret; + + if (!peci_sensor_need_update(&priv->temp.target.state)) + return 0; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_TEMP_TARGET, 0, &pcs); + if (ret) + return ret; + + priv->temp.target.tjmax = + FIELD_GET(TEMP_TARGET_REF_TEMP_MASK, pcs) * MILLIDEGREE_PER_DEGREE; + + tcontrol_margin = FIELD_GET(TEMP_TARGET_FAN_TEMP_MASK, pcs); + tcontrol_margin = sign_extend32(tcontrol_margin, 7) * MILLIDEGREE_PER_DEGREE; + priv->temp.target.tcontrol = priv->temp.target.tjmax - tcontrol_margin; + + tthrottle_offset = FIELD_GET(TEMP_TARGET_TJ_OFFSET_MASK, pcs) * MILLIDEGREE_PER_DEGREE; + priv->temp.target.tthrottle = priv->temp.target.tjmax - tthrottle_offset; + + peci_sensor_mark_updated(&priv->temp.target.state); + + return 0; +} + +static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type type, long *val) +{ + int ret; + + mutex_lock(&priv->temp.target.state.lock); + + ret = update_temp_target(priv); + if (ret) + goto unlock; + + switch (type) { + case tcontrol_type: + *val = priv->temp.target.tcontrol; + break; + case tthrottle_type: + *val = priv->temp.target.tthrottle; + break; + case tjmax_type: + *val = priv->temp.target.tjmax; + break; + case crit_hyst_type: + *val = priv->temp.target.tjmax - priv->temp.target.tcontrol; + break; + default: + ret = -EOPNOTSUPP; + break; + } +unlock: + mutex_unlock(&priv->temp.target.state.lock); + + return ret; +} + +/* + * Error codes: + * 0x8000: General sensor error + * 0x8001: Reserved + * 0x8002: Underflow on reading value + * 0x8003-0x81ff: Reserved + */ +static bool dts_valid(u16 val) +{ + return val < 0x8000 || val > 0x81ff; +} + +/* + * Processors return a value of DTS reading in S10.6 fixed point format + * (16 bits: 10-bit signed magnitude, 6-bit fraction). + */ +static s32 dts_ten_dot_six_to_millidegree(u16 val) +{ + return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 64; +} + +/* + * For older processors, thermal margin reading is returned in S8.8 fixed + * point format (16 bits: 8-bit signed magnitude, 8-bit fraction). + */ +static s32 dts_eight_dot_eight_to_millidegree(u16 val) +{ + return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 256; +} + +static int get_die_temp(struct peci_cputemp *priv, long *val) +{ + int ret = 0; + long tjmax; + u16 temp; + + mutex_lock(&priv->temp.die.state.lock); + if (!peci_sensor_need_update(&priv->temp.die.state)) + goto skip_update; + + ret = peci_temp_read(priv->peci_dev, &temp); + if (ret) + goto err_unlock; + + if (!dts_valid(temp)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tjmax_type, &tjmax); + if (ret) + goto err_unlock; + + priv->temp.die.value = (s32)tjmax + dts_ten_dot_six_to_millidegree(temp); + + peci_sensor_mark_updated(&priv->temp.die.state); + +skip_update: + *val = priv->temp.die.value; +err_unlock: + mutex_unlock(&priv->temp.die.state.lock); + return ret; +} + +static int get_dts(struct peci_cputemp *priv, long *val) +{ + int ret = 0; + u16 thermal_margin; + long tcontrol; + u32 pcs; + + mutex_lock(&priv->temp.dts.state.lock); + if (!peci_sensor_need_update(&priv->temp.dts.state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs); + if (ret) + goto err_unlock; + + thermal_margin = FIELD_GET(DTS_MARGIN_MASK, pcs); + if (!dts_valid(thermal_margin)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tcontrol_type, &tcontrol); + if (ret) + goto err_unlock; + + /* Note that the tcontrol should be available before calling it */ + priv->temp.dts.value = + (s32)tcontrol - priv->gen_info->thermal_margin_to_millidegree(thermal_margin); + + peci_sensor_mark_updated(&priv->temp.dts.state); + +skip_update: + *val = priv->temp.dts.value; +err_unlock: + mutex_unlock(&priv->temp.dts.state.lock); + return ret; +} + +static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val) +{ + int ret = 0; + u16 core_dts_margin; + long tjmax; + u32 pcs; + + mutex_lock(&priv->temp.core[core_index].state.lock); + if (!peci_sensor_need_update(&priv->temp.core[core_index].state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs); + if (ret) + goto err_unlock; + + core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs); + if (!dts_valid(core_dts_margin)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tjmax_type, &tjmax); + if (ret) + goto err_unlock; + + /* Note that the tjmax should be available before calling it */ + priv->temp.core[core_index].value = + (s32)tjmax + dts_ten_dot_six_to_millidegree(core_dts_margin); + + peci_sensor_mark_updated(&priv->temp.core[core_index].state); + +skip_update: + *val = priv->temp.core[core_index].value; +err_unlock: + mutex_unlock(&priv->temp.core[core_index].state.lock); + return ret; +} + +static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct peci_cputemp *priv = dev_get_drvdata(dev); + + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = channel < channel_core ? + cputemp_label[channel] : priv->coretemp_label[channel - channel_core]; + + return 0; +} + +static int cputemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct peci_cputemp *priv = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + switch (channel) { + case channel_die: + return get_die_temp(priv, val); + case channel_dts: + return get_dts(priv, val); + case channel_tcontrol: + return get_temp_target(priv, tcontrol_type, val); + case channel_tthrottle: + return get_temp_target(priv, tthrottle_type, val); + case channel_tjmax: + return get_temp_target(priv, tjmax_type, val); + default: + return get_core_temp(priv, channel - channel_core, val); + } + break; + case hwmon_temp_max: + return get_temp_target(priv, tcontrol_type, val); + case hwmon_temp_crit: + return get_temp_target(priv, tjmax_type, val); + case hwmon_temp_crit_hyst: + return get_temp_target(priv, crit_hyst_type, val); + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct peci_cputemp *priv = data; + + if (channel > CPUTEMP_CHANNEL_NUMS) + return 0; + + if (channel < channel_core) + return 0444; + + if (test_bit(channel - channel_core, priv->core_mask)) + return 0444; + + return 0; +} + +static int init_core_mask(struct peci_cputemp *priv) +{ + struct peci_device *peci_dev = priv->peci_dev; + struct resolved_cores_reg *reg = priv->gen_info->reg; + u64 core_mask; + u32 data; + int ret; + + /* Get the RESOLVED_CORES register value */ + switch (peci_dev->info.model) { + case INTEL_FAM6_ICELAKE_X: + case INTEL_FAM6_ICELAKE_D: + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, + reg->func, reg->offset + 4, &data); + if (ret) + return ret; + + core_mask = (u64)data << 32; + + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, + reg->func, reg->offset, &data); + if (ret) + return ret; + + core_mask |= data; + + break; + default: + ret = peci_pci_local_read(peci_dev, reg->bus, reg->dev, + reg->func, reg->offset, &data); + if (ret) + return ret; + + core_mask = data; + + break; + } + + if (!core_mask) + return -EIO; + + bitmap_from_u64(priv->core_mask, core_mask); + + return 0; +} + +static int create_temp_label(struct peci_cputemp *priv) +{ + unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX); + int i; + + priv->coretemp_label = devm_kzalloc(priv->dev, core_max * sizeof(char *), GFP_KERNEL); + if (!priv->coretemp_label) + return -ENOMEM; + + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) { + priv->coretemp_label[i] = devm_kasprintf(priv->dev, GFP_KERNEL, "Core %d", i); + if (!priv->coretemp_label[i]) + return -ENOMEM; + } + + return 0; +} + +static void check_resolved_cores(struct peci_cputemp *priv) +{ + /* + * Failure to resolve cores is non-critical, we're still able to + * provide other sensor data. + */ + + if (init_core_mask(priv)) + return; + + if (create_temp_label(priv)) + bitmap_zero(priv->core_mask, CORE_NUMS_MAX); +} + +static void sensor_init(struct peci_cputemp *priv) +{ + int i; + + mutex_init(&priv->temp.target.state.lock); + mutex_init(&priv->temp.die.state.lock); + mutex_init(&priv->temp.dts.state.lock); + + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) + mutex_init(&priv->temp.core[i].state.lock); +} + +static const struct hwmon_ops peci_cputemp_ops = { + .is_visible = cputemp_is_visible, + .read_string = cputemp_read_string, + .read = cputemp_read, +}; + +static const struct hwmon_channel_info *peci_cputemp_info[] = { + HWMON_CHANNEL_INFO(temp, + /* Die temperature */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST, + /* DTS margin */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST, + /* Tcontrol temperature */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT, + /* Tthrottle temperature */ + HWMON_T_LABEL | HWMON_T_INPUT, + /* Tjmax temperature */ + HWMON_T_LABEL | HWMON_T_INPUT, + /* Core temperature - for all core channels */ + [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = + HWMON_T_LABEL | HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_chip_info peci_cputemp_chip_info = { + .ops = &peci_cputemp_ops, + .info = peci_cputemp_info, +}; + +static int peci_cputemp_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct peci_device *peci_dev = to_peci_device(dev->parent); + struct peci_cputemp *priv; + struct device *hwmon_dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_cputemp.cpu%d", + peci_dev->info.socket_id); + if (!priv->name) + return -ENOMEM; + + priv->dev = dev; + priv->peci_dev = peci_dev; + priv->gen_info = (const struct cpu_info *)id->driver_data; + + /* + * This is just a sanity check. Since we're using commands that are + * guaranteed to be supported on a given platform, we should never see + * revision lower than expected. + */ + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) + dev_warn(priv->dev, + "Unexpected PECI revision %#x, some features may be unavailable\n", + peci_dev->info.peci_revision); + + check_resolved_cores(priv); + + sensor_init(priv); + + hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, + priv, &peci_cputemp_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* + * RESOLVED_CORES PCI configuration register may have different location on + * different platforms. + */ +static struct resolved_cores_reg resolved_cores_reg_hsx = { + .bus = 1, + .dev = 30, + .func = 3, + .offset = 0xb4, +}; + +static struct resolved_cores_reg resolved_cores_reg_icx = { + .bus = 14, + .dev = 30, + .func = 3, + .offset = 0xd0, +}; + +static const struct cpu_info cpu_hsx = { + .reg = &resolved_cores_reg_hsx, + .min_peci_revision = 0x33, + .thermal_margin_to_millidegree = &dts_eight_dot_eight_to_millidegree, +}; + +static const struct cpu_info cpu_icx = { + .reg = &resolved_cores_reg_icx, + .min_peci_revision = 0x40, + .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree, +}; + +static const struct auxiliary_device_id peci_cputemp_ids[] = { + { + .name = "peci_cpu.cputemp.hsx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.bdx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.bdxd", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.skx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.icx", + .driver_data = (kernel_ulong_t)&cpu_icx, + }, + { + .name = "peci_cpu.cputemp.icxd", + .driver_data = (kernel_ulong_t)&cpu_icx, + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids); + +static struct auxiliary_driver peci_cputemp_driver = { + .probe = peci_cputemp_probe, + .id_table = peci_cputemp_ids, +}; + +module_auxiliary_driver(peci_cputemp_driver); + +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); +MODULE_DESCRIPTION("PECI cputemp driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI_CPU); diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c new file mode 100644 index 000000000000..0a633bda3668 --- /dev/null +++ b/drivers/hwmon/peci/dimmtemp.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/devm-helpers.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/peci.h> +#include <linux/peci-cpu.h> +#include <linux/units.h> +#include <linux/workqueue.h> + +#include "common.h" + +#define DIMM_MASK_CHECK_DELAY_JIFFIES msecs_to_jiffies(5000) + +/* Max number of channel ranks and DIMM index per channel */ +#define CHAN_RANK_MAX_ON_HSX 8 +#define DIMM_IDX_MAX_ON_HSX 3 +#define CHAN_RANK_MAX_ON_BDX 4 +#define DIMM_IDX_MAX_ON_BDX 3 +#define CHAN_RANK_MAX_ON_BDXD 2 +#define DIMM_IDX_MAX_ON_BDXD 2 +#define CHAN_RANK_MAX_ON_SKX 6 +#define DIMM_IDX_MAX_ON_SKX 2 +#define CHAN_RANK_MAX_ON_ICX 8 +#define DIMM_IDX_MAX_ON_ICX 2 +#define CHAN_RANK_MAX_ON_ICXD 4 +#define DIMM_IDX_MAX_ON_ICXD 2 + +#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX +#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX +#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX) + +#define CPU_SEG_MASK GENMASK(23, 16) +#define GET_CPU_SEG(x) (((x) & CPU_SEG_MASK) >> 16) +#define CPU_BUS_MASK GENMASK(7, 0) +#define GET_CPU_BUS(x) ((x) & CPU_BUS_MASK) + +#define DIMM_TEMP_MAX GENMASK(15, 8) +#define DIMM_TEMP_CRIT GENMASK(23, 16) +#define GET_TEMP_MAX(x) (((x) & DIMM_TEMP_MAX) >> 8) +#define GET_TEMP_CRIT(x) (((x) & DIMM_TEMP_CRIT) >> 16) + +#define NO_DIMM_RETRY_COUNT_MAX 5 + +struct peci_dimmtemp; + +struct dimm_info { + int chan_rank_max; + int dimm_idx_max; + u8 min_peci_revision; + int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order, + int chan_rank, u32 *data); +}; + +struct peci_dimm_thresholds { + long temp_max; + long temp_crit; + struct peci_sensor_state state; +}; + +enum peci_dimm_threshold_type { + temp_max_type, + temp_crit_type, +}; + +struct peci_dimmtemp { + struct peci_device *peci_dev; + struct device *dev; + const char *name; + const struct dimm_info *gen_info; + struct delayed_work detect_work; + struct { + struct peci_sensor_data temp; + struct peci_dimm_thresholds thresholds; + } dimm[DIMM_NUMS_MAX]; + char **dimmtemp_label; + DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX); + u8 no_dimm_retry_count; +}; + +static u8 __dimm_temp(u32 reg, int dimm_order) +{ + return (reg >> (dimm_order * 8)) & 0xff; +} + +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val) +{ + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + int ret = 0; + u32 data; + + mutex_lock(&priv->dimm[dimm_no].temp.state.lock); + if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data); + if (ret) + goto unlock; + + priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE; + + peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state); + +skip_update: + *val = priv->dimm[dimm_no].temp.value; +unlock: + mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); + return ret; +} + +static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) +{ + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + u32 data; + int ret; + + if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state)) + return 0; + + ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, &data); + if (ret == -ENODATA) /* Use default or previous value */ + return 0; + if (ret) + return ret; + + priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * MILLIDEGREE_PER_DEGREE; + priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * MILLIDEGREE_PER_DEGREE; + + peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state); + + return 0; +} + +static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_threshold_type type, + int dimm_no, long *val) +{ + int ret; + + mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); + ret = update_thresholds(priv, dimm_no); + if (ret) + goto unlock; + + switch (type) { + case temp_max_type: + *val = priv->dimm[dimm_no].thresholds.temp_max; + break; + case temp_crit_type: + *val = priv->dimm[dimm_no].thresholds.temp_crit; + break; + default: + ret = -EOPNOTSUPP; + break; + } +unlock: + mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); + + return ret; +} + +static int dimmtemp_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct peci_dimmtemp *priv = dev_get_drvdata(dev); + + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = (const char *)priv->dimmtemp_label[channel]; + + return 0; +} + +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct peci_dimmtemp *priv = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + return get_dimm_temp(priv, channel, val); + case hwmon_temp_max: + return get_dimm_thresholds(priv, temp_max_type, channel, val); + case hwmon_temp_crit: + return get_dimm_thresholds(priv, temp_crit_type, channel, val); + default: + break; + } + + return -EOPNOTSUPP; +} + +static umode_t dimmtemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct peci_dimmtemp *priv = data; + + if (test_bit(channel, priv->dimm_mask)) + return 0444; + + return 0; +} + +static const struct hwmon_ops peci_dimmtemp_ops = { + .is_visible = dimmtemp_is_visible, + .read_string = dimmtemp_read_string, + .read = dimmtemp_read, +}; + +static int check_populated_dimms(struct peci_dimmtemp *priv) +{ + int chan_rank_max = priv->gen_info->chan_rank_max; + int dimm_idx_max = priv->gen_info->dimm_idx_max; + u32 chan_rank_empty = 0; + u32 dimm_mask = 0; + int chan_rank, dimm_idx, ret; + u32 pcs; + + BUILD_BUG_ON(BITS_PER_TYPE(chan_rank_empty) < CHAN_RANK_MAX); + BUILD_BUG_ON(BITS_PER_TYPE(dimm_mask) < DIMM_NUMS_MAX); + if (chan_rank_max * dimm_idx_max > DIMM_NUMS_MAX) { + WARN_ONCE(1, "Unsupported number of DIMMs - chan_rank_max: %d, dimm_idx_max: %d", + chan_rank_max, dimm_idx_max); + return -EINVAL; + } + + for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) { + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &pcs); + if (ret) { + /* + * Overall, we expect either success or -EINVAL in + * order to determine whether DIMM is populated or not. + * For anything else we fall back to deferring the + * detection to be performed at a later point in time. + */ + if (ret == -EINVAL) { + chan_rank_empty |= BIT(chan_rank); + continue; + } + + return -EAGAIN; + } + + for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++) + if (__dimm_temp(pcs, dimm_idx)) + dimm_mask |= BIT(chan_rank * dimm_idx_max + dimm_idx); + } + + /* + * If we got all -EINVALs, it means that the CPU doesn't have any + * DIMMs. Unfortunately, it may also happen at the very start of + * host platform boot. Retrying a couple of times lets us make sure + * that the state is persistent. + */ + if (chan_rank_empty == GENMASK(chan_rank_max - 1, 0)) { + if (priv->no_dimm_retry_count < NO_DIMM_RETRY_COUNT_MAX) { + priv->no_dimm_retry_count++; + + return -EAGAIN; + } + + return -ENODEV; + } + + /* + * It's possible that memory training is not done yet. In this case we + * defer the detection to be performed at a later point in time. + */ + if (!dimm_mask) { + priv->no_dimm_retry_count = 0; + return -EAGAIN; + } + + dev_dbg(priv->dev, "Scanned populated DIMMs: %#x\n", dimm_mask); + + bitmap_from_arr32(priv->dimm_mask, &dimm_mask, DIMM_NUMS_MAX); + + return 0; +} + +static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan) +{ + int rank = chan / priv->gen_info->dimm_idx_max; + int idx = chan % priv->gen_info->dimm_idx_max; + + priv->dimmtemp_label[chan] = devm_kasprintf(priv->dev, GFP_KERNEL, + "DIMM %c%d", 'A' + rank, + idx + 1); + if (!priv->dimmtemp_label[chan]) + return -ENOMEM; + + return 0; +} + +static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = { + HWMON_CHANNEL_INFO(temp, + [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT), + NULL +}; + +static const struct hwmon_chip_info peci_dimmtemp_chip_info = { + .ops = &peci_dimmtemp_ops, + .info = peci_dimmtemp_temp_info, +}; + +static int create_dimm_temp_info(struct peci_dimmtemp *priv) +{ + int ret, i, channels; + struct device *dev; + + /* + * We expect to either find populated DIMMs and carry on with creating + * sensors, or find out that there are no DIMMs populated. + * All other states mean that the platform never reached the state that + * allows to check DIMM state - causing us to retry later on. + */ + ret = check_populated_dimms(priv); + if (ret == -ENODEV) { + dev_dbg(priv->dev, "No DIMMs found\n"); + return 0; + } else if (ret) { + schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES); + dev_dbg(priv->dev, "Deferred populating DIMM temp info\n"); + return ret; + } + + channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max; + + priv->dimmtemp_label = devm_kzalloc(priv->dev, channels * sizeof(char *), GFP_KERNEL); + if (!priv->dimmtemp_label) + return -ENOMEM; + + for_each_set_bit(i, priv->dimm_mask, DIMM_NUMS_MAX) { + ret = create_dimm_temp_label(priv, i); + if (ret) + return ret; + mutex_init(&priv->dimm[i].thresholds.state.lock); + mutex_init(&priv->dimm[i].temp.state.lock); + } + + dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv, + &peci_dimmtemp_chip_info, NULL); + if (IS_ERR(dev)) { + dev_err(priv->dev, "Failed to register hwmon device\n"); + return PTR_ERR(dev); + } + + dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name); + + return 0; +} + +static void create_dimm_temp_info_delayed(struct work_struct *work) +{ + struct peci_dimmtemp *priv = container_of(to_delayed_work(work), + struct peci_dimmtemp, + detect_work); + int ret; + + ret = create_dimm_temp_info(priv); + if (ret && ret != -EAGAIN) + dev_err(priv->dev, "Failed to populate DIMM temp info\n"); +} + +static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct peci_device *peci_dev = to_peci_device(dev->parent); + struct peci_dimmtemp *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_dimmtemp.cpu%d", + peci_dev->info.socket_id); + if (!priv->name) + return -ENOMEM; + + priv->dev = dev; + priv->peci_dev = peci_dev; + priv->gen_info = (const struct dimm_info *)id->driver_data; + + /* + * This is just a sanity check. Since we're using commands that are + * guaranteed to be supported on a given platform, we should never see + * revision lower than expected. + */ + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) + dev_warn(priv->dev, + "Unexpected PECI revision %#x, some features may be unavailable\n", + peci_dev->info.peci_revision); + + ret = devm_delayed_work_autocancel(priv->dev, &priv->detect_work, + create_dimm_temp_info_delayed); + if (ret) + return ret; + + ret = create_dimm_temp_info(priv); + if (ret && ret != -EAGAIN) { + dev_err(dev, "Failed to populate DIMM temp info\n"); + return ret; + } + + return 0; +} + +static int +read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 20, Function 0: IMC 0 channel 0 -> rank 0 + * Device 20, Function 1: IMC 0 channel 1 -> rank 1 + * Device 21, Function 0: IMC 0 channel 2 -> rank 2 + * Device 21, Function 1: IMC 0 channel 3 -> rank 3 + * Device 23, Function 0: IMC 1 channel 0 -> rank 4 + * Device 23, Function 1: IMC 1 channel 1 -> rank 5 + * Device 24, Function 0: IMC 1 channel 2 -> rank 6 + * Device 24, Function 1: IMC 1 channel 3 -> rank 7 + */ + dev = 20 + chan_rank / 2 + chan_rank / 4; + func = chan_rank % 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 1, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_bdxd(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 12, Function 2: IMC 1 channel 0 -> rank 2 + * Device 12, Function 6: IMC 1 channel 1 -> rank 3 + */ + dev = 10 + chan_rank / 2 * 2; + func = (chan_rank % 2) ? 6 : 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_skx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 11, Function 2: IMC 0 channel 2 -> rank 2 + * Device 12, Function 2: IMC 1 channel 0 -> rank 3 + * Device 12, Function 6: IMC 1 channel 1 -> rank 4 + * Device 13, Function 2: IMC 1 channel 2 -> rank 5 + */ + dev = 10 + chan_rank / 3 * 2 + (chan_rank % 3 == 2 ? 1 : 0); + func = chan_rank % 3 == 1 ? 6 : 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u32 reg_val; + u64 offset; + int ret; + u8 dev; + + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, ®_val); + if (ret || !(reg_val & BIT(31))) + return -ENODATA; /* Use default or previous value */ + + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, ®_val); + if (ret) + return -ENODATA; /* Use default or previous value */ + + /* + * Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0 + * Device 26, Offset 264e0: IMC 0 channel 1 -> rank 1 + * Device 27, Offset 224e0: IMC 1 channel 0 -> rank 2 + * Device 27, Offset 264e0: IMC 1 channel 1 -> rank 3 + * Device 28, Offset 224e0: IMC 2 channel 0 -> rank 4 + * Device 28, Offset 264e0: IMC 2 channel 1 -> rank 5 + * Device 29, Offset 224e0: IMC 3 channel 0 -> rank 6 + * Device 29, Offset 264e0: IMC 3 channel 1 -> rank 7 + */ + dev = 26 + chan_rank / 2; + offset = 0x224e0 + dimm_order * 4 + (chan_rank % 2) * 0x4000; + + ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val), + dev, 0, offset, data); + if (ret) + return ret; + + return 0; +} + +static const struct dimm_info dimm_hsx = { + .chan_rank_max = CHAN_RANK_MAX_ON_HSX, + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_hsx, +}; + +static const struct dimm_info dimm_bdx = { + .chan_rank_max = CHAN_RANK_MAX_ON_BDX, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_hsx, +}; + +static const struct dimm_info dimm_bdxd = { + .chan_rank_max = CHAN_RANK_MAX_ON_BDXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDXD, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_bdxd, +}; + +static const struct dimm_info dimm_skx = { + .chan_rank_max = CHAN_RANK_MAX_ON_SKX, + .dimm_idx_max = DIMM_IDX_MAX_ON_SKX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_skx, +}; + +static const struct dimm_info dimm_icx = { + .chan_rank_max = CHAN_RANK_MAX_ON_ICX, + .dimm_idx_max = DIMM_IDX_MAX_ON_ICX, + .min_peci_revision = 0x40, + .read_thresholds = &read_thresholds_icx, +}; + +static const struct dimm_info dimm_icxd = { + .chan_rank_max = CHAN_RANK_MAX_ON_ICXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_ICXD, + .min_peci_revision = 0x40, + .read_thresholds = &read_thresholds_icx, +}; + +static const struct auxiliary_device_id peci_dimmtemp_ids[] = { + { + .name = "peci_cpu.dimmtemp.hsx", + .driver_data = (kernel_ulong_t)&dimm_hsx, + }, + { + .name = "peci_cpu.dimmtemp.bdx", + .driver_data = (kernel_ulong_t)&dimm_bdx, + }, + { + .name = "peci_cpu.dimmtemp.bdxd", + .driver_data = (kernel_ulong_t)&dimm_bdxd, + }, + { + .name = "peci_cpu.dimmtemp.skx", + .driver_data = (kernel_ulong_t)&dimm_skx, + }, + { + .name = "peci_cpu.dimmtemp.icx", + .driver_data = (kernel_ulong_t)&dimm_icx, + }, + { + .name = "peci_cpu.dimmtemp.icxd", + .driver_data = (kernel_ulong_t)&dimm_icxd, + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids); + +static struct auxiliary_driver peci_dimmtemp_driver = { + .probe = peci_dimmtemp_probe, + .id_table = peci_dimmtemp_ids, +}; + +module_auxiliary_driver(peci_dimmtemp_driver); + +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); +MODULE_DESCRIPTION("PECI dimmtemp driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI_CPU); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index a9ea06204767..89668af67206 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -19,13 +19,25 @@ config SENSORS_PMBUS default y help If you say yes here you get hardware monitoring support for generic - PMBus devices, including but not limited to ADP4000, BMR453, BMR454, - MAX20796, MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, TPS40400, - TPS544B20, TPS544B25, TPS544C20, TPS544C25, and UDT020. + PMBus devices, including but not limited to ADP4000, BMR310, BMR453, + BMR454, BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, BMR492, + MAX20796, MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, + TPS40400, TPS544B20, TPS544B25, TPS544C20, TPS544C25, and UDT020. This driver can also be built as a module. If so, the module will be called pmbus. +config SENSORS_ADM1266 + tristate "Analog Devices ADM1266 Sequencer" + select CRC8 + depends on GPIOLIB + help + If you say yes here you get hardware monitoring support for Analog + Devices ADM1266 Cascadable Super Sequencer. + + This driver can also be built as a module. If so, the module will + be called adm1266. + config SENSORS_ADM1275 tristate "Analog Devices ADM1275 and compatibles" help @@ -45,6 +57,35 @@ config SENSORS_BEL_PFE This driver can also be built as a module. If so, the module will be called bel-pfe. +config SENSORS_BPA_RS600 + tristate "BluTek BPA-RS600 Power Supplies" + help + If you say yes here you get hardware monitoring support for BluTek + BPA-RS600 Power Supplies. + + This driver can also be built as a module. If so, the module will + be called bpa-rs600. + +config SENSORS_DELTA_AHE50DC_FAN + tristate "Delta AHE-50DC fan control module" + help + If you say yes here you get hardware monitoring support for + the integrated fan control module of the Delta AHE-50DC + Open19 power shelf. + + This driver can also be built as a module. If so, the module + will be called delta-ahe50dc-fan. + +config SENSORS_FSP_3Y + tristate "FSP/3Y-Power power supplies" + help + If you say yes here you get hardware monitoring support for + FSP/3Y-Power hot-swap power supplies. + Supported models: YH-5151E, YM-2151E + + This driver can also be built as a module. If so, the module will + be called fsp-3y. + config SENSORS_IBM_CFFPS tristate "IBM Common Form Factor Power Supply" depends on LEDS_CLASS @@ -55,6 +96,15 @@ config SENSORS_IBM_CFFPS This driver can also be built as a module. If so, the module will be called ibm-cffps. +config SENSORS_DPS920AB + tristate "Delta DPS920AB Power Supply" + help + If you say yes here you get hardware monitoring support for Delta + DPS920AB Power Supplies. + + This driver can also be built as a module. If so, the module will + be called dps920ab. + config SENSORS_INSPUR_IPSPS tristate "INSPUR Power System Power Supply" help @@ -71,17 +121,32 @@ config SENSORS_IR35221 Infineon IR35221 controller. This driver can also be built as a module. If so, the module will - be called ir35521. + be called ir35221. + +config SENSORS_IR36021 + tristate "Infineon IR36021" + help + If you say yes here you get hardware monitoring support for Infineon + IR36021. + + This driver can also be built as a module. If so, the module will + be called ir36021. config SENSORS_IR38064 - tristate "Infineon IR38064" + tristate "Infineon IR38064 and compatibles" help If you say yes here you get hardware monitoring support for Infineon - IR38064. + IR38060, IR38064, IR38164 and IR38263. This driver can also be built as a module. If so, the module will be called ir38064. +config SENSORS_IR38064_REGULATOR + bool "Regulator support for IR38064 and compatibles" + depends on SENSORS_IR38064 && REGULATOR + help + Uses the IR38064 or compatible as regulator. + config SENSORS_IRPS5401 tristate "Infineon IRPS5401" help @@ -92,10 +157,10 @@ config SENSORS_IRPS5401 be called irps5401. config SENSORS_ISL68137 - tristate "Intersil ISL68137" + tristate "Renesas Digital Multiphase Voltage Regulators" help - If you say yes here you get hardware monitoring support for Intersil - ISL68137. + If you say yes here you get hardware monitoring support for Renesas + digital multiphase voltage regulators. This driver can also be built as a module. If so, the module will be called isl68137. @@ -109,12 +174,28 @@ config SENSORS_LM25066 This driver can also be built as a module. If so, the module will be called lm25066. +config SENSORS_LM25066_REGULATOR + bool "Regulator support for LM25066 and compatibles" + depends on SENSORS_LM25066 && REGULATOR + help + If you say yes here you get regulator support for National + Semiconductor LM25066, LM5064, and LM5066. + +config SENSORS_LT7182S + tristate "Analog Devices LT7182S" + help + If you say yes here you get hardware monitoring support for Analog + Devices LT7182S. + + This driver can also be built as a module. If so, the module will + be called lt7182s. + config SENSORS_LTC2978 tristate "Linear Technologies LTC2978 and compatibles" help If you say yes here you get hardware monitoring support for Linear - Technology LTC2974, LTC2975, LTC2977, LTC2978, LTC2980, LTC3880, - LTC3883, LTC3886, LTC3887, LTCM2987, LTM4675, and LTM4676. + Technology LTC2972, LTC2974, LTC2975, LTC2977, LTC2978, LTC2979, + LTC2980, and LTM2987. This driver can also be built as a module. If so, the module will be called ltc2978. @@ -123,9 +204,10 @@ config SENSORS_LTC2978_REGULATOR bool "Regulator support for LTC2978 and compatibles" depends on SENSORS_LTC2978 && REGULATOR help - If you say yes here you get regulator support for Linear - Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, LTM4676 - and LTM4686. + If you say yes here you get regulator support for Linear Technology + LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7880, + LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4686, + and LTM4700. config SENSORS_LTC3815 tristate "Linear Technologies LTC3815" @@ -136,6 +218,15 @@ config SENSORS_LTC3815 This driver can also be built as a module. If so, the module will be called ltc3815. +config SENSORS_MAX15301 + tristate "Maxim MAX15301" + help + If you say yes here you get hardware monitoring support for Maxim + MAX15301, as well as for Flex BMR461. + + This driver can also be built as a module. If so, the module will + be called max15301. + config SENSORS_MAX16064 tristate "Maxim MAX16064" help @@ -145,11 +236,20 @@ config SENSORS_MAX16064 This driver can also be built as a module. If so, the module will be called max16064. +config SENSORS_MAX16601 + tristate "Maxim MAX16508, MAX16601, MAX16602" + help + If you say yes here you get hardware monitoring support for Maxim + MAX16508, MAX16601 and MAX16602. + + This driver can also be built as a module. If so, the module will + be called max16601. + config SENSORS_MAX20730 - tristate "Maxim MAX20730, MAX20734, MAX20743" + tristate "Maxim MAX20710, MAX20730, MAX20734, MAX20743" help If you say yes here you get hardware monitoring support for Maxim - MAX20730, MAX20734, and MAX20743. + MAX20710, MAX20730, MAX20734, and MAX20743. This driver can also be built as a module. If so, the module will be called max20730. @@ -190,6 +290,67 @@ config SENSORS_MAX8688 This driver can also be built as a module. If so, the module will be called max8688. +config SENSORS_MP2888 + tristate "MPS MP2888" + help + If you say yes here you get hardware monitoring support for MPS + MP2888 Digital, Multi-Phase, Pulse-Width Modulation Controller. + + This driver can also be built as a module. If so, the module will + be called mp2888. + +config SENSORS_MP2975 + tristate "MPS MP2975" + help + If you say yes here you get hardware monitoring support for MPS + MP2975 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2975. + +config SENSORS_MP5023 + tristate "MPS MP5023" + help + If you say yes here you get hardware monitoring support for MPS + MP5023. + + This driver can also be built as a module. If so, the module will + be called mp5023. + +config SENSORS_PIM4328 + tristate "Flex PIM4328 and compatibles" + help + If you say yes here you get hardware monitoring support for Flex + PIM4328, PIM4820 and PIM4006 Power Interface Modules. + + This driver can also be built as a module. If so, the module will + be called pim4328. + +config SENSORS_PLI1209BC + tristate "Vicor PLI1209BC" + help + If you say yes here you get hardware monitoring support for Vicor + PLI1209BC Digital Supervisor. + + This driver can also be built as a module. If so, the module will + be called pli1209bc. + +config SENSORS_PLI1209BC_REGULATOR + bool "Regulator support for PLI1209BC" + depends on SENSORS_PLI1209BC && REGULATOR + help + If you say yes here you get regulator support for Vicor PLI1209BC + Digital Supervisor. + +config SENSORS_PM6764TR + tristate "ST PM6764TR" + help + If you say yes here you get hardware monitoring support for ST + PM6764TR. + + This driver can also be built as a module. If so, the module will + be called pm6764tr. + config SENSORS_PXE1610 tristate "Infineon PXE1610" help @@ -199,6 +360,25 @@ config SENSORS_PXE1610 This driver can also be built as a module. If so, the module will be called pxe1610. +config SENSORS_Q54SJ108A2 + tristate "Delta Power Supplies Q54SJ108A2" + help + If you say yes here you get hardware monitoring support for Delta + Q54SJ108A2 series Power Supplies. + + This driver can also be built as a module. If so, the module will + be called q54sj108a2. + +config SENSORS_STPDDC60 + tristate "ST STPDDC60" + help + If you say yes here you get hardware monitoring support for ST + STPDDC60 Universal Digital Multicell Controller, as well as for + Flex BMR481. + + This driver can also be built as a module. If so, the module will + be called stpddc60. + config SENSORS_TPS40422 tristate "TI TPS40422" help @@ -209,14 +389,23 @@ config SENSORS_TPS40422 be called tps40422. config SENSORS_TPS53679 - tristate "TI TPS53679, TPS53688" + tristate "TI TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, TPS53688" help If you say yes here you get hardware monitoring support for TI - TPS53679, TPS53688 + TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, and TPS53688. This driver can also be built as a module. If so, the module will be called tps53679. +config SENSORS_TPS546D24 + tristate "TPS546D24" + help + If you say yes here you get hardware monitoring support for TEXAS + TPS546D24. + + This driver can also be built as a module. If so, the module will + be called tps546d24 + config SENSORS_UCD9000 tristate "TI UCD90120, UCD90124, UCD90160, UCD90320, UCD9090, UCD90910" help @@ -237,6 +426,15 @@ config SENSORS_UCD9200 This driver can also be built as a module. If so, the module will be called ucd9200. +config SENSORS_XDPE152 + tristate "Infineon XDPE152 family" + help + If you say yes here you get hardware monitoring support for Infineon + XDPE15284, XDPE152C4, device. + + This driver can also be built as a module. If so, the module will + be called xdpe152c4. + config SENSORS_XDPE122 tristate "Infineon XDPE122 family" help @@ -246,6 +444,12 @@ config SENSORS_XDPE122 This driver can also be built as a module. If so, the module will be called xdpe12284. +config SENSORS_XDPE122_REGULATOR + bool "Regulator support for XDPE122 and compatibles" + depends on SENSORS_XDPE122 && REGULATOR + help + Uses the xdpe12284 or compatible as regulator. + config SENSORS_ZL6100 tristate "Intersil ZL6100 and compatibles" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 5feb45806123..0002dbe22d52 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -5,27 +5,46 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o +obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o +obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o +obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o +obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o +obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o +obj-$(CONFIG_SENSORS_IR36021) += ir36021.o obj-$(CONFIG_SENSORS_IR38064) += ir38064.o obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o +obj-$(CONFIG_SENSORS_MAX15301) += max15301.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o +obj-$(CONFIG_SENSORS_MAX16601) += max16601.o obj-$(CONFIG_SENSORS_MAX20730) += max20730.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o +obj-$(CONFIG_SENSORS_MP2888) += mp2888.o +obj-$(CONFIG_SENSORS_MP2975) += mp2975.o +obj-$(CONFIG_SENSORS_MP5023) += mp5023.o +obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o +obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o +obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o +obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o +obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o +obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o +obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c new file mode 100644 index 000000000000..ec5f932fc6f0 --- /dev/null +++ b/drivers/hwmon/pmbus/adm1266.c @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADM1266 - Cascadable Super Sequencer with Margin + * Control and Fault Recording + * + * Copyright 2020 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/crc8.h> +#include <linux/debugfs.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/i2c-smbus.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> +#include "pmbus.h" +#include <linux/slab.h> +#include <linux/timekeeping.h> + +#define ADM1266_BLACKBOX_CONFIG 0xD3 +#define ADM1266_PDIO_CONFIG 0xD4 +#define ADM1266_READ_STATE 0xD9 +#define ADM1266_READ_BLACKBOX 0xDE +#define ADM1266_SET_RTC 0xDF +#define ADM1266_GPIO_CONFIG 0xE1 +#define ADM1266_BLACKBOX_INFO 0xE6 +#define ADM1266_PDIO_STATUS 0xE9 +#define ADM1266_GPIO_STATUS 0xEA + +/* ADM1266 GPIO defines */ +#define ADM1266_GPIO_NR 9 +#define ADM1266_GPIO_FUNCTIONS(x) FIELD_GET(BIT(0), x) +#define ADM1266_GPIO_INPUT_EN(x) FIELD_GET(BIT(2), x) +#define ADM1266_GPIO_OUTPUT_EN(x) FIELD_GET(BIT(3), x) +#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x) + +/* ADM1266 PDIO defines */ +#define ADM1266_PDIO_NR 16 +#define ADM1266_PDIO_PIN_CFG(x) FIELD_GET(GENMASK(15, 13), x) +#define ADM1266_PDIO_GLITCH_FILT(x) FIELD_GET(GENMASK(12, 9), x) +#define ADM1266_PDIO_OUT_CFG(x) FIELD_GET(GENMASK(2, 0), x) + +#define ADM1266_BLACKBOX_OFFSET 0 +#define ADM1266_BLACKBOX_SIZE 64 + +#define ADM1266_PMBUS_BLOCK_MAX 255 + +struct adm1266_data { + struct pmbus_driver_info info; + struct gpio_chip gc; + const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR]; + struct i2c_client *client; + struct dentry *debugfs_dir; + struct nvmem_config nvmem_config; + struct nvmem_device *nvmem; + u8 *dev_mem; + struct mutex buf_mutex; + u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned; + u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned; +}; + +static const struct nvmem_cell_info adm1266_nvmem_cells[] = { + { + .name = "blackbox", + .offset = ADM1266_BLACKBOX_OFFSET, + .bytes = 2048, + }, +}; + +DECLARE_CRC8_TABLE(pmbus_crc_table); + +/* + * Different from Block Read as it sends data and waits for the slave to + * return a value dependent on that data. The protocol is simply a Write Block + * followed by a Read Block without the Read-Block command field and the + * Write-Block STOP bit. + */ +static int adm1266_pmbus_block_xfer(struct adm1266_data *data, u8 cmd, u8 w_len, u8 *data_w, + u8 *data_r) +{ + struct i2c_client *client = data->client; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags = I2C_M_DMA_SAFE, + .buf = data->write_buf, + .len = w_len + 2, + }, + { + .addr = client->addr, + .flags = I2C_M_RD | I2C_M_DMA_SAFE, + .buf = data->read_buf, + .len = ADM1266_PMBUS_BLOCK_MAX + 2, + } + }; + u8 addr; + u8 crc; + int ret; + + mutex_lock(&data->buf_mutex); + + msgs[0].buf[0] = cmd; + msgs[0].buf[1] = w_len; + memcpy(&msgs[0].buf[2], data_w, w_len); + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret != 2) { + if (ret >= 0) + ret = -EPROTO; + + mutex_unlock(&data->buf_mutex); + + return ret; + } + + if (client->flags & I2C_CLIENT_PEC) { + addr = i2c_8bit_addr_from_msg(&msgs[0]); + crc = crc8(pmbus_crc_table, &addr, 1, 0); + crc = crc8(pmbus_crc_table, msgs[0].buf, msgs[0].len, crc); + + addr = i2c_8bit_addr_from_msg(&msgs[1]); + crc = crc8(pmbus_crc_table, &addr, 1, crc); + crc = crc8(pmbus_crc_table, msgs[1].buf, msgs[1].buf[0] + 1, crc); + + if (crc != msgs[1].buf[msgs[1].buf[0] + 1]) { + mutex_unlock(&data->buf_mutex); + return -EBADMSG; + } + } + + memcpy(data_r, &msgs[1].buf[1], msgs[1].buf[0]); + + ret = msgs[1].buf[0]; + mutex_unlock(&data->buf_mutex); + + return ret; +} + +static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = { + {1, 0}, + {2, 1}, + {3, 2}, + {4, 8}, + {5, 9}, + {6, 10}, + {7, 11}, + {8, 6}, + {9, 7}, +}; + +static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = { + "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8", + "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6", + "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13", + "PDIO14", "PDIO15", "PDIO16", +}; + +static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct adm1266_data *data = gpiochip_get_data(chip); + u8 read_buf[I2C_SMBUS_BLOCK_MAX + 1]; + unsigned long pins_status; + unsigned int pmbus_cmd; + int ret; + + if (offset < ADM1266_GPIO_NR) + pmbus_cmd = ADM1266_GPIO_STATUS; + else + pmbus_cmd = ADM1266_PDIO_STATUS; + + ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf); + if (ret < 0) + return ret; + + pins_status = read_buf[0] + (read_buf[1] << 8); + if (offset < ADM1266_GPIO_NR) + return test_bit(adm1266_gpio_mapping[offset][1], &pins_status); + + return test_bit(offset - ADM1266_GPIO_NR, &pins_status); +} + +static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct adm1266_data *data = gpiochip_get_data(chip); + u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1]; + unsigned long status; + unsigned int gpio_nr; + int ret; + + ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf); + if (ret < 0) + return ret; + + status = read_buf[0] + (read_buf[1] << 8); + + *bits = 0; + for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) { + if (test_bit(adm1266_gpio_mapping[gpio_nr][1], &status)) + set_bit(gpio_nr, bits); + } + + ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, read_buf); + if (ret < 0) + return ret; + + status = read_buf[0] + (read_buf[1] << 8); + + *bits = 0; + for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) { + if (test_bit(gpio_nr - ADM1266_GPIO_NR, &status)) + set_bit(gpio_nr, bits); + } + + return 0; +} + +static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct adm1266_data *data = gpiochip_get_data(chip); + u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1]; + unsigned long gpio_config; + unsigned long pdio_config; + unsigned long pin_cfg; + u8 write_cmd; + int ret; + int i; + + for (i = 0; i < ADM1266_GPIO_NR; i++) { + write_cmd = adm1266_gpio_mapping[i][1]; + ret = adm1266_pmbus_block_xfer(data, ADM1266_GPIO_CONFIG, 1, &write_cmd, read_buf); + if (ret != 2) + return; + + gpio_config = read_buf[0]; + seq_puts(s, adm1266_names[i]); + + seq_puts(s, " ( "); + if (!ADM1266_GPIO_FUNCTIONS(gpio_config)) { + seq_puts(s, "high-Z )\n"); + continue; + } + if (ADM1266_GPIO_INPUT_EN(gpio_config)) + seq_puts(s, "input "); + if (ADM1266_GPIO_OUTPUT_EN(gpio_config)) + seq_puts(s, "output "); + if (ADM1266_GPIO_OPEN_DRAIN(gpio_config)) + seq_puts(s, "open-drain )\n"); + else + seq_puts(s, "push-pull )\n"); + } + + write_cmd = 0xFF; + ret = adm1266_pmbus_block_xfer(data, ADM1266_PDIO_CONFIG, 1, &write_cmd, read_buf); + if (ret != 32) + return; + + for (i = 0; i < ADM1266_PDIO_NR; i++) { + seq_puts(s, adm1266_names[ADM1266_GPIO_NR + i]); + + pdio_config = read_buf[2 * i]; + pdio_config += (read_buf[2 * i + 1] << 8); + pin_cfg = ADM1266_PDIO_PIN_CFG(pdio_config); + + seq_puts(s, " ( "); + if (!pin_cfg || pin_cfg > 5) { + seq_puts(s, "high-Z )\n"); + continue; + } + + if (pin_cfg & BIT(0)) + seq_puts(s, "output "); + + if (pin_cfg & BIT(1)) + seq_puts(s, "input "); + + seq_puts(s, ")\n"); + } +} + +static int adm1266_config_gpio(struct adm1266_data *data) +{ + const char *name = dev_name(&data->client->dev); + char *gpio_name; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(data->gpio_names); i++) { + gpio_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "adm1266-%x-%s", + data->client->addr, adm1266_names[i]); + if (!gpio_name) + return -ENOMEM; + + data->gpio_names[i] = gpio_name; + } + + data->gc.label = name; + data->gc.parent = &data->client->dev; + data->gc.owner = THIS_MODULE; + data->gc.base = -1; + data->gc.names = data->gpio_names; + data->gc.ngpio = ARRAY_SIZE(data->gpio_names); + data->gc.get = adm1266_gpio_get; + data->gc.get_multiple = adm1266_gpio_get_multiple; + data->gc.dbg_show = adm1266_gpio_dbg_show; + + ret = devm_gpiochip_add_data(&data->client->dev, &data->gc, data); + if (ret) + dev_err(&data->client->dev, "GPIO registering failed (%d)\n", ret); + + return ret; +} + +static int adm1266_state_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE); + if (ret < 0) + return ret; + + seq_printf(s, "%d\n", ret); + + return 0; +} + +static void adm1266_init_debugfs(struct adm1266_data *data) +{ + struct dentry *root; + + root = pmbus_get_debugfs_dir(data->client); + if (!root) + return; + + data->debugfs_dir = debugfs_create_dir(data->client->name, root); + if (!data->debugfs_dir) + return; + + debugfs_create_devm_seqfile(&data->client->dev, "sequencer_state", data->debugfs_dir, + adm1266_state_read); +} + +static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) +{ + int record_count; + char index; + u8 buf[5]; + int ret; + + ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, buf); + if (ret < 0) + return ret; + + if (ret != 4) + return -EIO; + + record_count = buf[3]; + + for (index = 0; index < record_count; index++) { + ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, read_buff); + if (ret < 0) + return ret; + + if (ret != ADM1266_BLACKBOX_SIZE) + return -EIO; + + read_buff += ADM1266_BLACKBOX_SIZE; + } + + return 0; +} + +static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val, size_t bytes) +{ + struct adm1266_data *data = priv; + int ret; + + if (offset + bytes > data->nvmem_config.size) + return -EINVAL; + + if (offset == 0) { + memset(data->dev_mem, 0, data->nvmem_config.size); + + ret = adm1266_nvmem_read_blackbox(data, data->dev_mem); + if (ret) { + dev_err(&data->client->dev, "Could not read blackbox!"); + return ret; + } + } + + memcpy(val, data->dev_mem + offset, bytes); + + return 0; +} + +static int adm1266_config_nvmem(struct adm1266_data *data) +{ + data->nvmem_config.name = dev_name(&data->client->dev); + data->nvmem_config.dev = &data->client->dev; + data->nvmem_config.root_only = true; + data->nvmem_config.read_only = true; + data->nvmem_config.owner = THIS_MODULE; + data->nvmem_config.reg_read = adm1266_nvmem_read; + data->nvmem_config.cells = adm1266_nvmem_cells; + data->nvmem_config.ncells = ARRAY_SIZE(adm1266_nvmem_cells); + data->nvmem_config.priv = data; + data->nvmem_config.stride = 1; + data->nvmem_config.word_size = 1; + data->nvmem_config.size = adm1266_nvmem_cells[0].bytes; + + data->dev_mem = devm_kzalloc(&data->client->dev, data->nvmem_config.size, GFP_KERNEL); + if (!data->dev_mem) + return -ENOMEM; + + data->nvmem = devm_nvmem_register(&data->client->dev, &data->nvmem_config); + if (IS_ERR(data->nvmem)) { + dev_err(&data->client->dev, "Could not register nvmem!"); + return PTR_ERR(data->nvmem); + } + + return 0; +} + +static int adm1266_set_rtc(struct adm1266_data *data) +{ + time64_t kt; + char write_buf[6]; + int i; + + kt = ktime_get_seconds(); + + memset(write_buf, 0, sizeof(write_buf)); + + for (i = 0; i < 4; i++) + write_buf[2 + i] = (kt >> (i * 8)) & 0xFF; + + return i2c_smbus_write_block_data(data->client, ADM1266_SET_RTC, sizeof(write_buf), + write_buf); +} + +static int adm1266_probe(struct i2c_client *client) +{ + struct adm1266_data *data; + int ret; + int i; + + data = devm_kzalloc(&client->dev, sizeof(struct adm1266_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->info.pages = 17; + data->info.format[PSC_VOLTAGE_OUT] = linear; + for (i = 0; i < data->info.pages; i++) + data->info.func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + + crc8_populate_msb(pmbus_crc_table, 0x7); + mutex_init(&data->buf_mutex); + + ret = adm1266_config_gpio(data); + if (ret < 0) + return ret; + + ret = adm1266_set_rtc(data); + if (ret < 0) + return ret; + + ret = adm1266_config_nvmem(data); + if (ret < 0) + return ret; + + ret = pmbus_do_probe(client, &data->info); + if (ret) + return ret; + + adm1266_init_debugfs(data); + + return 0; +} + +static const struct of_device_id adm1266_of_match[] = { + { .compatible = "adi,adm1266" }, + { } +}; +MODULE_DEVICE_TABLE(of, adm1266_of_match); + +static const struct i2c_device_id adm1266_id[] = { + { "adm1266", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adm1266_id); + +static struct i2c_driver adm1266_driver = { + .driver = { + .name = "adm1266", + .of_match_table = adm1266_of_match, + }, + .probe_new = adm1266_probe, + .id_table = adm1266_id, +}; + +module_i2c_driver(adm1266_driver); + +MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 5caa37fbfc18..3b07bfb43e93 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -226,7 +226,8 @@ static int adm1275_write_pmon_config(const struct adm1275_data *data, return ret; } -static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) +static int adm1275_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct adm1275_data *data = to_adm1275_data(info); @@ -239,58 +240,68 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) case PMBUS_IOUT_UC_FAULT_LIMIT: if (!data->have_uc_fault) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_IOUT_WARN2_LIMIT); break; case PMBUS_IOUT_OC_FAULT_LIMIT: if (!data->have_oc_fault) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_IOUT_WARN2_LIMIT); break; case PMBUS_VOUT_OV_WARN_LIMIT: if (data->have_vout) return -ENODATA; - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, ADM1075_VAUX_OV_WARN_LIMIT); break; case PMBUS_VOUT_UV_WARN_LIMIT: if (data->have_vout) return -ENODATA; - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, ADM1075_VAUX_UV_WARN_LIMIT); break; case PMBUS_READ_VOUT: if (data->have_vout) return -ENODATA; - ret = pmbus_read_word_data(client, 0, ADM1075_READ_VAUX); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1075_READ_VAUX); break; case PMBUS_VIRT_READ_IOUT_MIN: if (!data->have_iout_min) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1293_IOUT_MIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1293_IOUT_MIN); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_PEAK_IOUT); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_PEAK_VOUT); break; case PMBUS_VIRT_READ_VIN_MAX: - ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1275_PEAK_VIN); break; case PMBUS_VIRT_READ_PIN_MIN: if (!data->have_pin_min) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1293_PIN_MIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1293_PIN_MIN); break; case PMBUS_VIRT_READ_PIN_MAX: if (!data->have_pin_max) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1276_PEAK_PIN); break; case PMBUS_VIRT_READ_TEMP_MAX: if (!data->have_temp_max) return -ENXIO; - ret = pmbus_read_word_data(client, 0, ADM1278_PEAK_TEMP); + ret = pmbus_read_word_data(client, 0, 0xff, + ADM1278_PEAK_TEMP); break; case PMBUS_VIRT_RESET_IOUT_HISTORY: case PMBUS_VIRT_RESET_VOUT_HISTORY: @@ -451,9 +462,9 @@ static const struct i2c_device_id adm1275_id[] = { }; MODULE_DEVICE_TABLE(i2c, adm1275_id); -static int adm1275_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adm1275_probe(struct i2c_client *client) { + s32 (*config_read_fn)(const struct i2c_client *client, u8 reg); u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; int config, device_config; int ret; @@ -464,6 +475,7 @@ static int adm1275_probe(struct i2c_client *client, int vindex = -1, voindex = -1, cindex = -1, pindex = -1; int tindex = -1; u32 shunt; + u32 avg; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA @@ -494,16 +506,21 @@ static int adm1275_probe(struct i2c_client *client, return -ENODEV; } - if (id->driver_data != mid->driver_data) + if (strcmp(client->name, mid->name) != 0) dev_notice(&client->dev, "Device mismatch: Configured %s, detected %s\n", - id->name, mid->name); + client->name, mid->name); - config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); + if (mid->driver_data == adm1272 || mid->driver_data == adm1278 || + mid->driver_data == adm1293 || mid->driver_data == adm1294) + config_read_fn = i2c_smbus_read_word_data; + else + config_read_fn = i2c_smbus_read_byte_data; + config = config_read_fn(client, ADM1275_PMON_CONFIG); if (config < 0) return config; - device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG); + device_config = config_read_fn(client, ADM1275_DEVICE_CONFIG); if (device_config < 0) return device_config; @@ -595,11 +612,13 @@ static int adm1275_probe(struct i2c_client *client, tindex = 8; info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | - PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - /* Enable VOUT if not enabled (it is disabled by default) */ - if (!(config & ADM1278_VOUT_EN)) { - config |= ADM1278_VOUT_EN; + /* Enable VOUT & TEMP1 if not enabled (disabled by default) */ + if ((config & (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) != + (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) { + config |= ADM1278_VOUT_EN | ADM1278_TEMP1_EN; ret = i2c_smbus_write_byte_data(client, ADM1275_PMON_CONFIG, config); @@ -609,10 +628,6 @@ static int adm1275_probe(struct i2c_client *client, return -ENODEV; } } - - if (config & ADM1278_TEMP1_EN) - info->func[0] |= - PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; if (config & ADM1278_VIN_EN) info->func[0] |= PMBUS_HAVE_VIN; break; @@ -666,12 +681,14 @@ static int adm1275_probe(struct i2c_client *client, tindex = 3; info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | - PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; - - /* Enable VOUT if not enabled (it is disabled by default) */ - if (!(config & ADM1278_VOUT_EN)) { - config |= ADM1278_VOUT_EN; - ret = i2c_smbus_write_byte_data(client, + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + + /* Enable VOUT & TEMP1 if not enabled (disabled by default) */ + if ((config & (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) != + (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) { + config |= ADM1278_VOUT_EN | ADM1278_TEMP1_EN; + ret = i2c_smbus_write_word_data(client, ADM1275_PMON_CONFIG, config); if (ret < 0) { @@ -681,9 +698,6 @@ static int adm1275_probe(struct i2c_client *client, } } - if (config & ADM1278_TEMP1_EN) - info->func[0] |= - PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; if (config & ADM1278_VIN_EN) info->func[0] |= PMBUS_HAVE_VIN; break; @@ -743,6 +757,43 @@ static int adm1275_probe(struct i2c_client *client, return -ENODEV; } + if (data->have_power_sampling && + of_property_read_u32(client->dev.of_node, + "adi,power-sample-average", &avg) == 0) { + if (!avg || avg > ADM1275_SAMPLES_AVG_MAX || + BIT(__fls(avg)) != avg) { + dev_err(&client->dev, + "Invalid number of power samples"); + return -EINVAL; + } + ret = adm1275_write_pmon_config(data, client, true, + ilog2(avg)); + if (ret < 0) { + dev_err(&client->dev, + "Setting power sample averaging failed with error %d", + ret); + return ret; + } + } + + if (of_property_read_u32(client->dev.of_node, + "adi,volt-curr-sample-average", &avg) == 0) { + if (!avg || avg > ADM1275_SAMPLES_AVG_MAX || + BIT(__fls(avg)) != avg) { + dev_err(&client->dev, + "Invalid number of voltage/current samples"); + return -EINVAL; + } + ret = adm1275_write_pmon_config(data, client, false, + ilog2(avg)); + if (ret < 0) { + dev_err(&client->dev, + "Setting voltage and current sample averaging failed with error %d", + ret); + return ret; + } + } + if (voindex < 0) voindex = vindex; if (vindex >= 0) { @@ -774,15 +825,14 @@ static int adm1275_probe(struct i2c_client *client, info->R[PSC_TEMPERATURE] = coefficients[tindex].R; } - return pmbus_do_probe(client, id, info); + return pmbus_do_probe(client, info); } static struct i2c_driver adm1275_driver = { .driver = { .name = "adm1275", }, - .probe = adm1275_probe, - .remove = pmbus_do_remove, + .probe_new = adm1275_probe, .id_table = adm1275_id, }; @@ -791,3 +841,4 @@ module_i2c_driver(adm1275_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index f236e18f45a5..4100eefb7ac3 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -87,12 +87,13 @@ static struct pmbus_driver_info pfe_driver_info[] = { }, }; -static int pfe_pmbus_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id pfe_device_id[]; + +static int pfe_pmbus_probe(struct i2c_client *client) { int model; - model = (int)id->driver_data; + model = (int)i2c_match_id(pfe_device_id, client)->driver_data; /* * PFE3000-12-069RA devices may not stay in page 0 during device @@ -104,7 +105,7 @@ static int pfe_pmbus_probe(struct i2c_client *client, i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); } - return pmbus_do_probe(client, id, &pfe_driver_info[model]); + return pmbus_do_probe(client, &pfe_driver_info[model]); } static const struct i2c_device_id pfe_device_id[] = { @@ -119,8 +120,7 @@ static struct i2c_driver pfe_pmbus_driver = { .driver = { .name = "bel-pfe", }, - .probe = pfe_pmbus_probe, - .remove = pmbus_do_remove, + .probe_new = pfe_pmbus_probe, .id_table = pfe_device_id, }; @@ -129,3 +129,4 @@ module_i2c_driver(pfe_pmbus_driver); MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c new file mode 100644 index 000000000000..f2d4e378a775 --- /dev/null +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for BluTek BPA-RS600 Power Supplies + * + * Copyright 2021 Allied Telesis Labs + */ + +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +enum chips { bpa_rs600, bpd_rs600 }; + +static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_FAN_CONFIG_12: + /* + * Two fans are reported in PMBUS_FAN_CONFIG_12 but there is + * only one fan in the module. Mask out the FAN2 bits. + */ + ret = pmbus_read_byte_data(client, 0, PMBUS_FAN_CONFIG_12); + if (ret >= 0) + ret &= ~(PB_FAN_2_INSTALLED | PB_FAN_2_PULSE_MASK); + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +/* + * The BPA-RS600 violates the PMBus spec. Specifically it treats the + * mantissa as unsigned. Deal with this here to allow the PMBus core + * to work with correctly encoded data. + */ +static int bpa_rs600_read_vin(struct i2c_client *client) +{ + int ret, exponent, mantissa; + + ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_READ_VIN); + if (ret < 0) + return ret; + + if (ret & BIT(10)) { + exponent = ret >> 11; + mantissa = ret & 0x7ff; + + exponent++; + mantissa >>= 1; + + ret = (exponent << 11) | mantissa; + } + + return ret; +} + +/* + * Firmware V5.70 incorrectly reports 1640W for MFR_PIN_MAX. + * Deal with this by returning a sensible value. + */ +static int bpa_rs600_read_pin_max(struct i2c_client *client) +{ + int ret; + + ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_MFR_PIN_MAX); + if (ret < 0) + return ret; + + /* Detect invalid 1640W (linear encoding) */ + if (ret == 0x0b34) + /* Report 700W (linear encoding) */ + return 0x095e; + + return ret; +} + +static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_VOUT_OV_FAULT_LIMIT: + /* These commands return data but it is invalid/un-documented */ + ret = -ENXIO; + break; + case PMBUS_READ_VIN: + ret = bpa_rs600_read_vin(client); + break; + case PMBUS_MFR_PIN_MAX: + ret = bpa_rs600_read_pin_max(client); + break; + default: + if (reg >= PMBUS_VIRT_BASE) + ret = -ENXIO; + else + ret = -ENODATA; + break; + } + + return ret; +} + +static struct pmbus_driver_info bpa_rs600_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_FAN] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_FAN12 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_FAN12, + .read_byte_data = bpa_rs600_read_byte_data, + .read_word_data = bpa_rs600_read_word_data, +}; + +static const struct i2c_device_id bpa_rs600_id[] = { + { "bpa-rs600", bpa_rs600 }, + { "bpd-rs600", bpd_rs600 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bpa_rs600_id); + +static int bpa_rs600_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + int ret; + const struct i2c_device_id *mid; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) { + dev_err(dev, "Failed to read Manufacturer Model\n"); + return ret; + } + + for (mid = bpa_rs600_id; mid->name[0]; mid++) { + if (!strncasecmp(buf, mid->name, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf); + return -ENODEV; + } + + return pmbus_do_probe(client, &bpa_rs600_info); +} + +static const struct of_device_id __maybe_unused bpa_rs600_of_match[] = { + { .compatible = "blutek,bpa-rs600" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bpa_rs600_of_match); + +static struct i2c_driver bpa_rs600_driver = { + .driver = { + .name = "bpa-rs600", + .of_match_table = of_match_ptr(bpa_rs600_of_match), + }, + .probe_new = bpa_rs600_probe, + .id_table = bpa_rs600_id, +}; + +module_i2c_driver(bpa_rs600_driver); + +MODULE_AUTHOR("Chris Packham"); +MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c new file mode 100644 index 000000000000..f546f0c12497 --- /dev/null +++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Delta AHE-50DC power shelf fan control module driver + * + * Copyright 2021 Zev Weiss <zev@bewilderbeest.net> + */ + +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pmbus.h> + +#include "pmbus.h" + +#define AHE50DC_PMBUS_READ_TEMP4 0xd0 + +static int ahe50dc_fan_write_byte(struct i2c_client *client, int page, u8 value) +{ + /* + * The CLEAR_FAULTS operation seems to sometimes (unpredictably, perhaps + * 5% of the time or so) trigger a problematic phenomenon in which the + * fan speeds surge momentarily and at least some (perhaps all?) of the + * system's power outputs experience a glitch. + * + * However, according to Delta it should be OK to simply not send any + * CLEAR_FAULTS commands (the device doesn't seem to be capable of + * reporting any faults anyway), so just blackhole them unconditionally. + */ + return value == PMBUS_CLEAR_FAULTS ? -EOPNOTSUPP : -ENODATA; +} + +static int ahe50dc_fan_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + /* temp1 in (virtual) page 1 is remapped to mfr-specific temp4 */ + if (page == 1) { + if (reg == PMBUS_READ_TEMPERATURE_1) + return i2c_smbus_read_word_data(client, AHE50DC_PMBUS_READ_TEMP4); + return -EOPNOTSUPP; + } + + /* + * There's a fairly limited set of commands this device actually + * supports, so here we block attempts to read anything else (which + * return 0xffff and would cause confusion elsewhere). + */ + switch (reg) { + case PMBUS_STATUS_WORD: + case PMBUS_FAN_COMMAND_1: + case PMBUS_FAN_COMMAND_2: + case PMBUS_FAN_COMMAND_3: + case PMBUS_FAN_COMMAND_4: + case PMBUS_STATUS_FAN_12: + case PMBUS_STATUS_FAN_34: + case PMBUS_READ_VIN: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_TEMPERATURE_2: + case PMBUS_READ_TEMPERATURE_3: + case PMBUS_READ_FAN_SPEED_1: + case PMBUS_READ_FAN_SPEED_2: + case PMBUS_READ_FAN_SPEED_3: + case PMBUS_READ_FAN_SPEED_4: + return -ENODATA; + default: + return -EOPNOTSUPP; + } +} + +static struct pmbus_driver_info ahe50dc_fan_info = { + .pages = 2, + .format[PSC_FAN] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_FAN] = 1, + .b[PSC_FAN] = 0, + .R[PSC_FAN] = 0, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 1, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 3, + .func[0] = PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_VIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_FAN34 | + PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_STATUS_FAN34 | PMBUS_PAGE_VIRTUAL, + .func[1] = PMBUS_HAVE_TEMP | PMBUS_PAGE_VIRTUAL, + .write_byte = ahe50dc_fan_write_byte, + .read_word_data = ahe50dc_fan_read_word_data, +}; + +/* + * CAPABILITY returns 0xff, which appears to be this device's way indicating + * it doesn't support something (and if we enable I2C_CLIENT_PEC on seeing bit + * 7 being set it generates bad PECs, so let's not go there). + */ +static struct pmbus_platform_data ahe50dc_fan_data = { + .flags = PMBUS_NO_CAPABILITY, +}; + +static int ahe50dc_fan_probe(struct i2c_client *client) +{ + client->dev.platform_data = &ahe50dc_fan_data; + return pmbus_do_probe(client, &ahe50dc_fan_info); +} + +static const struct i2c_device_id ahe50dc_fan_id[] = { + { "ahe50dc_fan" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ahe50dc_fan_id); + +static const struct of_device_id __maybe_unused ahe50dc_fan_of_match[] = { + { .compatible = "delta,ahe50dc-fan" }, + { } +}; +MODULE_DEVICE_TABLE(of, ahe50dc_fan_of_match); + +static struct i2c_driver ahe50dc_fan_driver = { + .driver = { + .name = "ahe50dc_fan", + .of_match_table = of_match_ptr(ahe50dc_fan_of_match), + }, + .probe_new = ahe50dc_fan_probe, + .id_table = ahe50dc_fan_id, +}; +module_i2c_driver(ahe50dc_fan_driver); + +MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>"); +MODULE_DESCRIPTION("Driver for Delta AHE-50DC power shelf fan control module"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c new file mode 100644 index 000000000000..d3941f6eb29a --- /dev/null +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Delta DPS920AB PSU + * + * Copyright (C) 2021 Delta Networks, Inc. + * Copyright (C) 2021 Sartura Ltd. + */ + +#include <linux/debugfs.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +struct dps920ab_data { + char *mfr_model; + char *mfr_id; +}; + +static int dps920ab_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + /* + * This masks commands which are not supported. + * PSU advertises that all features are supported, + * in reality that unfortunately is not true. + * So enable only those that the datasheet confirms. + */ + switch (reg) { + case PMBUS_FAN_COMMAND_1: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_STATUS_WORD: + case PMBUS_READ_VIN: + case PMBUS_READ_IIN: + case PMBUS_READ_VOUT: + case PMBUS_READ_IOUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_TEMPERATURE_2: + case PMBUS_READ_TEMPERATURE_3: + case PMBUS_READ_FAN_SPEED_1: + case PMBUS_READ_POUT: + case PMBUS_READ_PIN: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_POUT_MAX: + return pmbus_read_word_data(client, page, phase, reg); + default: + return -ENXIO; + } +} + +static int dps920ab_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + /* + * This masks commands which are not supported. + * PSU only has one R/W register and that is + * for the fan. + */ + switch (reg) { + case PMBUS_FAN_COMMAND_1: + return pmbus_write_word_data(client, page, reg, word); + default: + return -EACCES; + } +} + +static struct pmbus_driver_info dps920ab_info = { + .pages = 1, + + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_FAN] = linear, + .format[PSC_TEMPERATURE] = linear, + + .func[0] = + PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = dps920ab_read_word_data, + .write_word_data = dps920ab_write_word_data, +}; + +static int dps920ab_mfr_id_show(struct seq_file *s, void *data) +{ + struct dps920ab_data *priv = s->private; + + seq_printf(s, "%s\n", priv->mfr_id); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(dps920ab_mfr_id); + +static int dps920ab_mfr_model_show(struct seq_file *s, void *data) +{ + struct dps920ab_data *priv = s->private; + + seq_printf(s, "%s\n", priv->mfr_model); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(dps920ab_mfr_model); + +static void dps920ab_init_debugfs(struct dps920ab_data *data, struct i2c_client *client) +{ + struct dentry *debugfs_dir; + struct dentry *root; + + root = pmbus_get_debugfs_dir(client); + if (!root) + return; + + debugfs_dir = debugfs_create_dir(client->name, root); + + debugfs_create_file("mfr_id", + 0400, + debugfs_dir, + data, + &dps920ab_mfr_id_fops); + + debugfs_create_file("mfr_model", + 0400, + debugfs_dir, + data, + &dps920ab_mfr_model_fops); +} + +static int dps920ab_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + struct dps920ab_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + + buf[ret] = '\0'; + if (ret != 5 || strncmp(buf, "DELTA", 5)) { + buf[ret] = '\0'; + dev_err(&client->dev, "Unsupported Manufacturer ID '%s'\n", buf); + return -ENODEV; + } + data->mfr_id = devm_kstrdup(&client->dev, buf, GFP_KERNEL); + if (!data->mfr_id) + return -ENOMEM; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return ret; + } + + buf[ret] = '\0'; + if (ret != 11 || strncmp(buf, "DPS-920AB", 9)) { + dev_err(&client->dev, "Unsupported Manufacturer Model '%s'\n", buf); + return -ENODEV; + } + data->mfr_model = devm_kstrdup(&client->dev, buf, GFP_KERNEL); + if (!data->mfr_model) + return -ENOMEM; + + ret = pmbus_do_probe(client, &dps920ab_info); + if (ret) + return ret; + + dps920ab_init_debugfs(data, client); + + return 0; +} + +static const struct of_device_id __maybe_unused dps920ab_of_match[] = { + { .compatible = "delta,dps920ab", }, + {} +}; + +MODULE_DEVICE_TABLE(of, dps920ab_of_match); + +static struct i2c_driver dps920ab_driver = { + .driver = { + .name = "dps920ab", + .of_match_table = of_match_ptr(dps920ab_of_match), + }, + .probe_new = dps920ab_probe, +}; + +module_i2c_driver(dps920ab_driver); + +MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); +MODULE_DESCRIPTION("PMBus driver for Delta DPS920AB PSU"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c new file mode 100644 index 000000000000..aec294cc72d1 --- /dev/null +++ b/drivers/hwmon/pmbus/fsp-3y.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for FSP 3Y-Power PSUs + * + * Copyright (c) 2021 Václav Kubernát, CESNET + * + * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by + * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue + * when switching pages, details are explained in the code. The driver support is limited. It + * exposes only the values, that have been tested to work correctly. Unsupported values either + * aren't supported by the devices or their encondings are unknown. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define YM2151_PAGE_12V_LOG 0x00 +#define YM2151_PAGE_12V_REAL 0x00 +#define YM2151_PAGE_5VSB_LOG 0x01 +#define YM2151_PAGE_5VSB_REAL 0x20 +#define YH5151E_PAGE_12V_LOG 0x00 +#define YH5151E_PAGE_12V_REAL 0x00 +#define YH5151E_PAGE_5V_LOG 0x01 +#define YH5151E_PAGE_5V_REAL 0x10 +#define YH5151E_PAGE_3V3_LOG 0x02 +#define YH5151E_PAGE_3V3_REAL 0x11 + +enum chips { + ym2151e, + yh5151e +}; + +struct fsp3y_data { + struct pmbus_driver_info info; + int chip; + int page; + + bool vout_linear_11; +}; + +#define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info) + +static int page_log_to_page_real(int page_log, enum chips chip) +{ + switch (chip) { + case ym2151e: + switch (page_log) { + case YM2151_PAGE_12V_LOG: + return YM2151_PAGE_12V_REAL; + case YM2151_PAGE_5VSB_LOG: + return YM2151_PAGE_5VSB_REAL; + } + return -EINVAL; + case yh5151e: + switch (page_log) { + case YH5151E_PAGE_12V_LOG: + return YH5151E_PAGE_12V_REAL; + case YH5151E_PAGE_5V_LOG: + return YH5151E_PAGE_5V_REAL; + case YH5151E_PAGE_3V3_LOG: + return YH5151E_PAGE_3V3_REAL; + } + return -EINVAL; + } + + return -EINVAL; +} + +static int set_page(struct i2c_client *client, int page_log) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct fsp3y_data *data = to_fsp3y_data(info); + int rv; + int page_real; + + if (page_log < 0) + return 0; + + page_real = page_log_to_page_real(page_log, data->chip); + if (page_real < 0) + return page_real; + + if (data->page != page_real) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page_real); + if (rv < 0) + return rv; + + data->page = page_real; + + /* + * Testing showed that the device has a timing issue. After + * setting a page, it takes a while, before the device actually + * gives the correct values from the correct page. 20 ms was + * tested to be enough to not give wrong values (15 ms wasn't + * enough). + */ + usleep_range(20000, 30000); + } + + return 0; +} + +static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct fsp3y_data *data = to_fsp3y_data(info); + int rv; + + /* + * Inject an exponent for non-compliant YH5151-E. + */ + if (data->vout_linear_11 && reg == PMBUS_VOUT_MODE) + return 0x1A; + + rv = set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_read_byte_data(client, reg); +} + +static int fsp3y_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct fsp3y_data *data = to_fsp3y_data(info); + int rv; + + /* + * This masks commands which weren't tested to work correctly. Some of + * the masked commands return 0xFFFF. These would probably get tagged as + * invalid by pmbus_core. Other ones do return values which might be + * useful (that is, they are not 0xFFFF), but their encoding is unknown, + * and so they are unsupported. + */ + switch (reg) { + case PMBUS_READ_FAN_SPEED_1: + case PMBUS_READ_IIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_PIN: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_TEMPERATURE_2: + case PMBUS_READ_TEMPERATURE_3: + case PMBUS_READ_VIN: + case PMBUS_READ_VOUT: + case PMBUS_STATUS_WORD: + break; + default: + return -ENXIO; + } + + rv = set_page(client, page); + if (rv < 0) + return rv; + + rv = i2c_smbus_read_word_data(client, reg); + if (rv < 0) + return rv; + + /* + * Handle YH-5151E non-compliant linear11 vout voltage. + */ + if (data->vout_linear_11 && reg == PMBUS_READ_VOUT) + rv = sign_extend32(rv, 10) & 0xffff; + + return rv; +} + +static struct pmbus_driver_info fsp3y_info[] = { + [ym2151e] = { + .pages = 2, + .func[YM2151_PAGE_12V_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | + PMBUS_HAVE_FAN12, + .func[YM2151_PAGE_5VSB_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT, + PMBUS_HAVE_IIN, + .read_word_data = fsp3y_read_word_data, + .read_byte_data = fsp3y_read_byte_data, + }, + [yh5151e] = { + .pages = 3, + .func[YH5151E_PAGE_12V_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3, + .func[YH5151E_PAGE_5V_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_POUT, + .func[YH5151E_PAGE_3V3_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_POUT, + .read_word_data = fsp3y_read_word_data, + .read_byte_data = fsp3y_read_byte_data, + } +}; + +static int fsp3y_detect(struct i2c_client *client) +{ + int rv; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + + rv = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rv < 0) + return rv; + + buf[rv] = '\0'; + + if (rv == 8) { + if (!strcmp(buf, "YM-2151E")) + return ym2151e; + else if (!strcmp(buf, "YH-5151E")) + return yh5151e; + } + + dev_err(&client->dev, "Unsupported model %.*s\n", rv, buf); + return -ENODEV; +} + +static const struct i2c_device_id fsp3y_id[] = { + {"ym2151e", ym2151e}, + {"yh5151e", yh5151e}, + { } +}; + +static int fsp3y_probe(struct i2c_client *client) +{ + struct fsp3y_data *data; + const struct i2c_device_id *id; + int rv; + + data = devm_kzalloc(&client->dev, sizeof(struct fsp3y_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->chip = fsp3y_detect(client); + if (data->chip < 0) + return data->chip; + + id = i2c_match_id(fsp3y_id, client); + if (data->chip != id->driver_data) + dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n", + id->name, (int)id->driver_data, data->chip); + + rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + if (rv < 0) + return rv; + data->page = rv; + + data->info = fsp3y_info[data->chip]; + + /* + * YH-5151E sometimes reports vout in linear11 and sometimes in + * linear16. This depends on the exact individual piece of hardware. One + * YH-5151E can use linear16 and another might use linear11 instead. + * + * The format can be recognized by reading VOUT_MODE - if it doesn't + * report a valid exponent, then vout uses linear11. Otherwise, the + * device is compliant and uses linear16. + */ + data->vout_linear_11 = false; + if (data->chip == yh5151e) { + rv = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); + if (rv < 0) + return rv; + + if (rv == 0xFF) + data->vout_linear_11 = true; + } + + return pmbus_do_probe(client, &data->info); +} + +MODULE_DEVICE_TABLE(i2c, fsp3y_id); + +static struct i2c_driver fsp3y_driver = { + .driver = { + .name = "fsp3y", + }, + .probe_new = fsp3y_probe, + .id_table = fsp3y_id +}; + +module_i2c_driver(fsp3y_driver); + +MODULE_AUTHOR("Václav Kubernát"); +MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 3795fe55b84f..e3294a1a54bb 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -18,6 +18,7 @@ #include "pmbus.h" +#define CFFPS_MFG_ID_CMD 0x99 #define CFFPS_FRU_CMD 0x9A #define CFFPS_PN_CMD 0x9B #define CFFPS_HEADER_CMD 0x9C @@ -33,9 +34,12 @@ #define CFFPS_INPUT_HISTORY_CMD 0xD6 #define CFFPS_INPUT_HISTORY_SIZE 100 +#define CFFPS_CCIN_REVISION GENMASK(7, 0) +#define CFFPS_CCIN_REVISION_LEGACY 0xde #define CFFPS_CCIN_VERSION GENMASK(15, 8) #define CFFPS_CCIN_VERSION_1 0x2b #define CFFPS_CCIN_VERSION_2 0x2e +#define CFFPS_CCIN_VERSION_3 0x51 /* STATUS_MFR_SPECIFIC bits */ #define CFFPS_MFR_FAN_FAULT BIT(0) @@ -47,13 +51,14 @@ #define CFFPS_MFR_VAUX_FAULT BIT(6) #define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7) -#define CFFPS_LED_BLINK BIT(0) -#define CFFPS_LED_ON BIT(1) -#define CFFPS_LED_OFF BIT(2) +#define CFFPS_LED_BLINK (BIT(0) | BIT(6)) +#define CFFPS_LED_ON (BIT(1) | BIT(6)) +#define CFFPS_LED_OFF (BIT(2) | BIT(6)) #define CFFPS_BLINK_RATE_MS 250 enum { CFFPS_DEBUGFS_INPUT_HISTORY = 0, + CFFPS_DEBUGFS_MFG_ID, CFFPS_DEBUGFS_FRU, CFFPS_DEBUGFS_PN, CFFPS_DEBUGFS_HEADER, @@ -88,6 +93,8 @@ struct ibm_cffps { struct led_classdev led; }; +static const struct i2c_device_id ibm_cffps_id[]; + #define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)]) static ssize_t ibm_cffps_read_input_history(struct ibm_cffps *psu, @@ -148,11 +155,14 @@ static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf, struct ibm_cffps *psu = to_psu(idxp, idx); char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); switch (idx) { case CFFPS_DEBUGFS_INPUT_HISTORY: return ibm_cffps_read_input_history(psu, buf, count, ppos); + case CFFPS_DEBUGFS_MFG_ID: + cmd = CFFPS_MFG_ID_CMD; + break; case CFFPS_DEBUGFS_FRU: cmd = CFFPS_FRU_CMD; break; @@ -166,8 +176,14 @@ static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf, cmd = CFFPS_SN_CMD; break; case CFFPS_DEBUGFS_MAX_POWER_OUT: - rc = i2c_smbus_read_word_swapped(psu->client, - CFFPS_MAX_POWER_OUT_CMD); + if (psu->version == cffps1) { + rc = i2c_smbus_read_word_swapped(psu->client, + CFFPS_MAX_POWER_OUT_CMD); + } else { + rc = i2c_smbus_read_word_data(psu->client, + CFFPS_MAX_POWER_OUT_CMD); + } + if (rc < 0) return rc; @@ -247,7 +263,7 @@ static ssize_t ibm_cffps_debugfs_write(struct file *file, switch (idx) { case CFFPS_DEBUGFS_ON_OFF_CONFIG: - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); rc = simple_write_to_buffer(&data, 1, ppos, buf, count); if (rc <= 0) @@ -325,13 +341,13 @@ static int ibm_cffps_read_byte_data(struct i2c_client *client, int page, } static int ibm_cffps_read_word_data(struct i2c_client *client, int page, - int reg) + int phase, int reg) { int rc, mfr; switch (reg) { case PMBUS_STATUS_WORD: - rc = pmbus_read_word_data(client, page, reg); + rc = pmbus_read_word_data(client, page, phase, reg); if (rc < 0) return rc; @@ -348,7 +364,8 @@ static int ibm_cffps_read_word_data(struct i2c_client *client, int page, rc |= PB_STATUS_OFF; break; case PMBUS_VIRT_READ_VMON: - rc = pmbus_read_word_data(client, page, CFFPS_12VCS_VOUT_CMD); + rc = pmbus_read_word_data(client, page, phase, + CFFPS_12VCS_VOUT_CMD); break; default: rc = -ENODATA; @@ -379,7 +396,7 @@ static int ibm_cffps_led_brightness_set(struct led_classdev *led_cdev, dev_dbg(&psu->client->dev, "LED brightness set: %d. Command: %d.\n", brightness, next_led_state); - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, next_led_state); @@ -401,7 +418,7 @@ static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev, dev_dbg(&psu->client->dev, "LED blink set.\n"); - pmbus_set_page(psu->client, 0); + pmbus_set_page(psu->client, 0, 0xff); rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, CFFPS_LED_BLINK); @@ -466,11 +483,10 @@ static struct pmbus_driver_info ibm_cffps_info[] = { }; static struct pmbus_platform_data ibm_cffps_pdata = { - .flags = PMBUS_SKIP_STATUS_CHECK, + .flags = PMBUS_SKIP_STATUS_CHECK | PMBUS_NO_CAPABILITY, }; -static int ibm_cffps_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ibm_cffps_probe(struct i2c_client *client) { int i, rc; enum versions vs = cffps_unknown; @@ -478,27 +494,51 @@ static int ibm_cffps_probe(struct i2c_client *client, struct dentry *ibm_cffps_dir; struct ibm_cffps *psu; const void *md = of_device_get_match_data(&client->dev); + const struct i2c_device_id *id; - if (md) + if (md) { vs = (enum versions)md; - else if (id) - vs = (enum versions)id->driver_data; + } else { + id = i2c_match_id(ibm_cffps_id, client); + if (id) + vs = (enum versions)id->driver_data; + } if (vs == cffps_unknown) { + u16 ccin_revision = 0; u16 ccin_version = CFFPS_CCIN_VERSION_1; int ccin = i2c_smbus_read_word_swapped(client, CFFPS_CCIN_CMD); + char mfg_id[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; - if (ccin > 0) + if (ccin > 0) { + ccin_revision = FIELD_GET(CFFPS_CCIN_REVISION, ccin); ccin_version = FIELD_GET(CFFPS_CCIN_VERSION, ccin); + } + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, mfg_id); + if (rc < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return rc; + } switch (ccin_version) { default: case CFFPS_CCIN_VERSION_1: - vs = cffps1; + if ((strncmp(mfg_id, "ACBE", 4) == 0) || + (strncmp(mfg_id, "ARTE", 4) == 0)) + vs = cffps1; + else + vs = cffps2; break; case CFFPS_CCIN_VERSION_2: vs = cffps2; break; + case CFFPS_CCIN_VERSION_3: + if (ccin_revision == CFFPS_CCIN_REVISION_LEGACY) + vs = cffps1; + else + vs = cffps2; + break; } /* Set the client name to include the version number. */ @@ -506,7 +546,7 @@ static int ibm_cffps_probe(struct i2c_client *client, } client->dev.platform_data = &ibm_cffps_pdata; - rc = pmbus_do_probe(client, id, &ibm_cffps_info[vs]); + rc = pmbus_do_probe(client, &ibm_cffps_info[vs]); if (rc) return rc; @@ -540,6 +580,9 @@ static int ibm_cffps_probe(struct i2c_client *client, debugfs_create_file("input_history", 0444, ibm_cffps_dir, &psu->debugfs_entries[CFFPS_DEBUGFS_INPUT_HISTORY], &ibm_cffps_fops); + debugfs_create_file("mfg_id", 0444, ibm_cffps_dir, + &psu->debugfs_entries[CFFPS_DEBUGFS_MFG_ID], + &ibm_cffps_fops); debugfs_create_file("fru", 0444, ibm_cffps_dir, &psu->debugfs_entries[CFFPS_DEBUGFS_FRU], &ibm_cffps_fops); @@ -598,8 +641,7 @@ static struct i2c_driver ibm_cffps_driver = { .name = "ibm-cffps", .of_match_table = ibm_cffps_of_match, }, - .probe = ibm_cffps_probe, - .remove = pmbus_do_remove, + .probe_new = ibm_cffps_probe, .id_table = ibm_cffps_id, }; @@ -608,3 +650,4 @@ module_i2c_driver(ibm_cffps_driver); MODULE_AUTHOR("Eddie James"); MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c index 42e01549184a..0f614e8d95f6 100644 --- a/drivers/hwmon/pmbus/inspur-ipsps.c +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -70,7 +70,7 @@ static ssize_t ipsps_string_show(struct device *dev, p = memscan(data, '#', rc); *p = '\0'; - return snprintf(buf, PAGE_SIZE, "%s\n", data); + return sysfs_emit(buf, "%s\n", data); } static ssize_t ipsps_fw_version_show(struct device *dev, @@ -91,9 +91,9 @@ static ssize_t ipsps_fw_version_show(struct device *dev, if (rc != 6) return -EPROTO; - return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n", - data[1], data[2]/* < 100 */, data[3]/*< 10*/, - data[4], data[5]/* < 100 */); + return sysfs_emit(buf, "%u.%02u%u-%u.%02u\n", + data[1], data[2]/* < 100 */, data[3]/*< 10*/, + data[4], data[5]/* < 100 */); } static ssize_t ipsps_mode_show(struct device *dev, @@ -111,19 +111,19 @@ static ssize_t ipsps_mode_show(struct device *dev, switch (rc) { case MODE_ACTIVE: - return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n", - MODE_ACTIVE_STRING, - MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + return sysfs_emit(buf, "[%s] %s %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); case MODE_STANDBY: - return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n", - MODE_ACTIVE_STRING, - MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + return sysfs_emit(buf, "%s [%s] %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); case MODE_REDUNDANCY: - return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n", - MODE_ACTIVE_STRING, - MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + return sysfs_emit(buf, "%s %s [%s]\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); default: - return snprintf(buf, PAGE_SIZE, "unspecified\n"); + return sysfs_emit(buf, "unspecified\n"); } } @@ -190,11 +190,10 @@ static struct pmbus_platform_data ipsps_pdata = { .flags = PMBUS_SKIP_STATUS_CHECK, }; -static int ipsps_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ipsps_probe(struct i2c_client *client) { client->dev.platform_data = &ipsps_pdata; - return pmbus_do_probe(client, id, &ipsps_info); + return pmbus_do_probe(client, &ipsps_info); } static const struct i2c_device_id ipsps_id[] = { @@ -216,8 +215,7 @@ static struct i2c_driver ipsps_driver = { .name = "inspur-ipsps", .of_match_table = of_match_ptr(ipsps_of_match), }, - .probe = ipsps_probe, - .remove = pmbus_do_remove, + .probe_new = ipsps_probe, .id_table = ipsps_id, }; @@ -226,3 +224,4 @@ module_i2c_driver(ipsps_driver); MODULE_AUTHOR("John Wang"); MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index 0d878bcd6d26..a6cf98e49666 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -21,37 +21,42 @@ #define IR35221_MFR_IOUT_VALLEY 0xcb #define IR35221_MFR_TEMP_VALLEY 0xcc -static int ir35221_read_word_data(struct i2c_client *client, int page, int reg) +static int ir35221_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_READ_VIN_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_VIN_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_VIN_PEAK); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_VOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_IOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, IR35221_MFR_TEMP_PEAK); + ret = pmbus_read_word_data(client, page, phase, + IR35221_MFR_TEMP_PEAK); break; case PMBUS_VIRT_READ_VIN_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_VIN_VALLEY); break; case PMBUS_VIRT_READ_VOUT_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_VOUT_VALLEY); break; case PMBUS_VIRT_READ_IOUT_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_IOUT_VALLEY); break; case PMBUS_VIRT_READ_TEMP_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, IR35221_MFR_TEMP_VALLEY); break; default: @@ -62,8 +67,7 @@ static int ir35221_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int ir35221_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ir35221_probe(struct i2c_client *client) { struct pmbus_driver_info *info; u8 buf[I2C_SMBUS_BLOCK_MAX]; @@ -118,7 +122,7 @@ static int ir35221_probe(struct i2c_client *client, | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP; info->func[1] = info->func[0]; - return pmbus_do_probe(client, id, info); + return pmbus_do_probe(client, info); } static const struct i2c_device_id ir35221_id[] = { @@ -132,8 +136,7 @@ static struct i2c_driver ir35221_driver = { .driver = { .name = "ir35221", }, - .probe = ir35221_probe, - .remove = pmbus_do_remove, + .probe_new = ir35221_probe, .id_table = ir35221_id, }; @@ -142,3 +145,4 @@ module_i2c_driver(ir35221_driver); MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com"); MODULE_DESCRIPTION("PMBus driver for IR35221"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c new file mode 100644 index 000000000000..4dca4767f571 --- /dev/null +++ b/drivers/hwmon/pmbus/ir36021.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for Infineon IR36021 + * + * Copyright (c) 2021 Allied Telesis + */ +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +static struct pmbus_driver_info ir36021_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_STATUS_TEMP, +}; + +static int ir36021_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_i2c_block_data(client, PMBUS_MFR_MODEL, 2, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n"); + return ret; + } + if (ret != 2 || buf[0] != 0x01 || buf[1] != 0x2d) { + dev_err(&client->dev, "MFR_MODEL unrecognised\n"); + return -ENODEV; + } + + return pmbus_do_probe(client, &ir36021_info); +} + +static const struct i2c_device_id ir36021_id[] = { + { "ir36021", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ir36021_id); + +static const struct of_device_id __maybe_unused ir36021_of_id[] = { + { .compatible = "infineon,ir36021" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ir36021_of_id); + +static struct i2c_driver ir36021_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ir36021", + .of_match_table = of_match_ptr(ir36021_of_id), + }, + .probe_new = ir36021_probe, + .id_table = ir36021_id, +}; + +module_i2c_driver(ir36021_driver); + +MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>"); +MODULE_DESCRIPTION("PMBus driver for Infineon IR36021"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c index 1820f5077f66..09276e397194 100644 --- a/drivers/hwmon/pmbus/ir38064.c +++ b/drivers/hwmon/pmbus/ir38064.c @@ -16,8 +16,16 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regulator/driver.h> #include "pmbus.h" +#if IS_ENABLED(CONFIG_SENSORS_IR38064_REGULATOR) +static const struct regulator_desc ir38064_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), +}; +#endif /* CONFIG_SENSORS_IR38064_REGULATOR */ + static struct pmbus_driver_info ir38064_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = linear, @@ -33,33 +41,50 @@ static struct pmbus_driver_info ir38064_info = { | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, +#if IS_ENABLED(CONFIG_SENSORS_IR38064_REGULATOR) + .num_regulators = 1, + .reg_desc = ir38064_reg_desc, +#endif }; -static int ir38064_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ir38064_probe(struct i2c_client *client) { - return pmbus_do_probe(client, id, &ir38064_info); + return pmbus_do_probe(client, &ir38064_info); } static const struct i2c_device_id ir38064_id[] = { + {"ir38060", 0}, {"ir38064", 0}, + {"ir38164", 0}, + {"ir38263", 0}, {} }; MODULE_DEVICE_TABLE(i2c, ir38064_id); +static const struct of_device_id __maybe_unused ir38064_of_match[] = { + { .compatible = "infineon,ir38060" }, + { .compatible = "infineon,ir38064" }, + { .compatible = "infineon,ir38164" }, + { .compatible = "infineon,ir38263" }, + {} +}; + +MODULE_DEVICE_TABLE(of, ir38064_of_match); + /* This is the driver that will be inserted */ static struct i2c_driver ir38064_driver = { .driver = { .name = "ir38064", + .of_match_table = of_match_ptr(ir38064_of_match), }, - .probe = ir38064_probe, - .remove = pmbus_do_remove, + .probe_new = ir38064_probe, .id_table = ir38064_id, }; module_i2c_driver(ir38064_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); -MODULE_DESCRIPTION("PMBus driver for Infineon IR38064"); +MODULE_DESCRIPTION("PMBus driver for Infineon IR38064 and compatible chips"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c index d37daa001fb3..de3449e4d77a 100644 --- a/drivers/hwmon/pmbus/irps5401.c +++ b/drivers/hwmon/pmbus/irps5401.c @@ -38,10 +38,9 @@ static struct pmbus_driver_info irps5401_info = { .func[4] = IRPS5401_LDO_FUNC, }; -static int irps5401_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int irps5401_probe(struct i2c_client *client) { - return pmbus_do_probe(client, id, &irps5401_info); + return pmbus_do_probe(client, &irps5401_info); } static const struct i2c_device_id irps5401_id[] = { @@ -55,8 +54,7 @@ static struct i2c_driver irps5401_driver = { .driver = { .name = "irps5401", }, - .probe = irps5401_probe, - .remove = pmbus_do_remove, + .probe_new = irps5401_probe, .id_table = irps5401_id, }; @@ -65,3 +63,4 @@ module_i2c_driver(irps5401_driver); MODULE_AUTHOR("Robert Hancock"); MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 515596c92fe1..1a8caff1ac5f 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Hardware monitoring driver for Intersil ISL68137 + * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators * * Copyright (c) 2017 Google Inc + * Copyright (c) 2020 Renesas Electronics America * */ @@ -14,9 +15,64 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/sysfs.h> + #include "pmbus.h" #define ISL68137_VOUT_AVS 0x30 +#define RAA_DMPVR2_READ_VMON 0xc8 + +enum chips { + isl68137, + isl68220, + isl68221, + isl68222, + isl68223, + isl68224, + isl68225, + isl68226, + isl68227, + isl68229, + isl68233, + isl68239, + isl69222, + isl69223, + isl69224, + isl69225, + isl69227, + isl69228, + isl69234, + isl69236, + isl69239, + isl69242, + isl69243, + isl69247, + isl69248, + isl69254, + isl69255, + isl69256, + isl69259, + isl69260, + isl69268, + isl69269, + isl69298, + raa228000, + raa228004, + raa228006, + raa228228, + raa229001, + raa229004, +}; + +enum variants { + raa_dmpvr1_2rail, + raa_dmpvr2_1rail, + raa_dmpvr2_2rail, + raa_dmpvr2_2rail_nontc, + raa_dmpvr2_3rail, + raa_dmpvr2_hv, +}; + +static const struct i2c_device_id raa_dmpvr_id[]; static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client, int page, @@ -49,7 +105,8 @@ static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client, * enabling AVS control is the workaround. */ if (op_val == ISL68137_VOUT_AVS) { - rc = pmbus_read_word_data(client, page, PMBUS_VOUT_COMMAND); + rc = pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_COMMAND); if (rc < 0) return rc; @@ -98,13 +155,31 @@ static const struct attribute_group enable_group = { .attrs = enable_attrs, }; -static const struct attribute_group *attribute_groups[] = { +static const struct attribute_group *isl68137_attribute_groups[] = { &enable_group, NULL, }; -static struct pmbus_driver_info isl68137_info = { - .pages = 2, +static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, page, phase, + RAA_DMPVR2_READ_VMON); + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static struct pmbus_driver_info raa_dmpvr_info = { + .pages = 3, .format[PSC_VOLTAGE_IN] = direct, .format[PSC_VOLTAGE_OUT] = direct, .format[PSC_CURRENT_IN] = direct, @@ -113,7 +188,7 @@ static struct pmbus_driver_info isl68137_info = { .format[PSC_TEMPERATURE] = direct, .m[PSC_VOLTAGE_IN] = 1, .b[PSC_VOLTAGE_IN] = 0, - .R[PSC_VOLTAGE_IN] = 3, + .R[PSC_VOLTAGE_IN] = 2, .m[PSC_VOLTAGE_OUT] = 1, .b[PSC_VOLTAGE_OUT] = 0, .R[PSC_VOLTAGE_OUT] = 3, @@ -133,37 +208,128 @@ static struct pmbus_driver_info isl68137_info = { | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT - | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, - .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT - | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, - .groups = attribute_groups, + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_VMON, + .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT + | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, + .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT + | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, }; -static int isl68137_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int isl68137_probe(struct i2c_client *client) { - return pmbus_do_probe(client, id, &isl68137_info); + struct pmbus_driver_info *info; + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + memcpy(info, &raa_dmpvr_info, sizeof(*info)); + + switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) { + case raa_dmpvr1_2rail: + info->pages = 2; + info->R[PSC_VOLTAGE_IN] = 3; + info->func[0] &= ~PMBUS_HAVE_VMON; + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT; + info->groups = isl68137_attribute_groups; + break; + case raa_dmpvr2_1rail: + info->pages = 1; + info->read_word_data = raa_dmpvr2_read_word_data; + break; + case raa_dmpvr2_2rail_nontc: + info->func[0] &= ~PMBUS_HAVE_TEMP3; + info->func[1] &= ~PMBUS_HAVE_TEMP3; + fallthrough; + case raa_dmpvr2_2rail: + info->pages = 2; + info->read_word_data = raa_dmpvr2_read_word_data; + break; + case raa_dmpvr2_3rail: + info->read_word_data = raa_dmpvr2_read_word_data; + break; + case raa_dmpvr2_hv: + info->pages = 1; + info->R[PSC_VOLTAGE_IN] = 1; + info->m[PSC_VOLTAGE_OUT] = 2; + info->R[PSC_VOLTAGE_OUT] = 2; + info->m[PSC_CURRENT_IN] = 2; + info->m[PSC_POWER] = 2; + info->R[PSC_POWER] = -1; + info->read_word_data = raa_dmpvr2_read_word_data; + break; + default: + return -ENODEV; + } + + return pmbus_do_probe(client, info); } -static const struct i2c_device_id isl68137_id[] = { - {"isl68137", 0}, +static const struct i2c_device_id raa_dmpvr_id[] = { + {"isl68137", raa_dmpvr1_2rail}, + {"isl68220", raa_dmpvr2_2rail}, + {"isl68221", raa_dmpvr2_3rail}, + {"isl68222", raa_dmpvr2_2rail}, + {"isl68223", raa_dmpvr2_2rail}, + {"isl68224", raa_dmpvr2_3rail}, + {"isl68225", raa_dmpvr2_2rail}, + {"isl68226", raa_dmpvr2_3rail}, + {"isl68227", raa_dmpvr2_1rail}, + {"isl68229", raa_dmpvr2_3rail}, + {"isl68233", raa_dmpvr2_2rail}, + {"isl68239", raa_dmpvr2_3rail}, + + {"isl69222", raa_dmpvr2_2rail}, + {"isl69223", raa_dmpvr2_3rail}, + {"isl69224", raa_dmpvr2_2rail}, + {"isl69225", raa_dmpvr2_2rail}, + {"isl69227", raa_dmpvr2_3rail}, + {"isl69228", raa_dmpvr2_3rail}, + {"isl69234", raa_dmpvr2_2rail}, + {"isl69236", raa_dmpvr2_2rail}, + {"isl69239", raa_dmpvr2_3rail}, + {"isl69242", raa_dmpvr2_2rail}, + {"isl69243", raa_dmpvr2_1rail}, + {"isl69247", raa_dmpvr2_2rail}, + {"isl69248", raa_dmpvr2_2rail}, + {"isl69254", raa_dmpvr2_2rail}, + {"isl69255", raa_dmpvr2_2rail}, + {"isl69256", raa_dmpvr2_2rail}, + {"isl69259", raa_dmpvr2_2rail}, + {"isl69260", raa_dmpvr2_2rail}, + {"isl69268", raa_dmpvr2_2rail}, + {"isl69269", raa_dmpvr2_3rail}, + {"isl69298", raa_dmpvr2_2rail}, + + {"raa228000", raa_dmpvr2_hv}, + {"raa228004", raa_dmpvr2_hv}, + {"raa228006", raa_dmpvr2_hv}, + {"raa228228", raa_dmpvr2_2rail_nontc}, + {"raa229001", raa_dmpvr2_2rail}, + {"raa229004", raa_dmpvr2_2rail}, {} }; -MODULE_DEVICE_TABLE(i2c, isl68137_id); +MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); /* This is the driver that will be inserted */ static struct i2c_driver isl68137_driver = { .driver = { .name = "isl68137", }, - .probe = isl68137_probe, - .remove = pmbus_do_remove, - .id_table = isl68137_id, + .probe_new = isl68137_probe, + .id_table = raa_dmpvr_id, }; module_i2c_driver(isl68137_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); -MODULE_DESCRIPTION("PMBus driver for Intersil ISL68137"); +MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 05fce86f1f81..09792cd03d9f 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/log2.h> +#include <linux/of_device.h> #include "pmbus.h" enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i }; @@ -51,26 +52,31 @@ struct __coeff { #define PSC_CURRENT_IN_L (PSC_NUM_CLASSES) #define PSC_POWER_L (PSC_NUM_CLASSES + 1) -static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = { +static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { [lm25056] = { [PSC_VOLTAGE_IN] = { .m = 16296, + .b = 1343, .R = -2, }, [PSC_CURRENT_IN] = { .m = 13797, + .b = -1833, .R = -2, }, [PSC_CURRENT_IN_L] = { .m = 6726, + .b = -537, .R = -2, }, [PSC_POWER] = { .m = 5501, + .b = -2908, .R = -3, }, [PSC_POWER_L] = { .m = 26882, + .b = -5646, .R = -4, }, [PSC_TEMPERATURE] = { @@ -82,26 +88,32 @@ static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = { [lm25066] = { [PSC_VOLTAGE_IN] = { .m = 22070, + .b = -1800, .R = -2, }, [PSC_VOLTAGE_OUT] = { .m = 22070, + .b = -1800, .R = -2, }, [PSC_CURRENT_IN] = { .m = 13661, + .b = -5200, .R = -2, }, [PSC_CURRENT_IN_L] = { - .m = 6852, + .m = 6854, + .b = -3100, .R = -2, }, [PSC_POWER] = { .m = 736, + .b = -3300, .R = -2, }, [PSC_POWER_L] = { .m = 369, + .b = -1900, .R = -2, }, [PSC_TEMPERATURE] = { @@ -111,26 +123,32 @@ static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = { [lm5064] = { [PSC_VOLTAGE_IN] = { .m = 4611, + .b = -642, .R = -2, }, [PSC_VOLTAGE_OUT] = { .m = 4621, + .b = 423, .R = -2, }, [PSC_CURRENT_IN] = { .m = 10742, + .b = 1552, .R = -2, }, [PSC_CURRENT_IN_L] = { .m = 5456, + .b = 2118, .R = -2, }, [PSC_POWER] = { .m = 1204, + .b = 8524, .R = -3, }, [PSC_POWER_L] = { .m = 612, + .b = 11202, .R = -3, }, [PSC_TEMPERATURE] = { @@ -140,26 +158,32 @@ static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = { [lm5066] = { [PSC_VOLTAGE_IN] = { .m = 4587, + .b = -1200, .R = -2, }, [PSC_VOLTAGE_OUT] = { .m = 4587, + .b = -2400, .R = -2, }, [PSC_CURRENT_IN] = { .m = 10753, + .b = -1200, .R = -2, }, [PSC_CURRENT_IN_L] = { .m = 5405, + .b = -600, .R = -2, }, [PSC_POWER] = { .m = 1204, + .b = -6000, .R = -3, }, [PSC_POWER_L] = { .m = 605, + .b = -8000, .R = -3, }, [PSC_TEMPERATURE] = { @@ -211,7 +235,8 @@ struct lm25066_data { #define to_lm25066_data(x) container_of(x, struct lm25066_data, info) -static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) +static int lm25066_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct lm25066_data *data = to_lm25066_data(info); @@ -219,7 +244,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_VMON: - ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX); + ret = pmbus_read_word_data(client, 0, 0xff, LM25066_READ_VAUX); if (ret < 0) break; /* Adjust returned value to match VIN coefficients */ @@ -244,33 +269,40 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) } break; case PMBUS_READ_IIN: - ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_MFR_READ_IIN); break; case PMBUS_READ_PIN: - ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_MFR_READ_PIN); break; case PMBUS_IIN_OC_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25066_MFR_IIN_OC_WARN_LIMIT); break; case PMBUS_PIN_OP_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25066_MFR_PIN_OP_WARN_LIMIT); break; case PMBUS_VIRT_READ_VIN_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_VIN); break; case PMBUS_VIRT_READ_VOUT_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_VOUT); break; case PMBUS_VIRT_READ_IIN_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_IIN); break; case PMBUS_VIRT_READ_PIN_AVG: - ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_AVG_PIN); break; case PMBUS_VIRT_READ_PIN_MAX: - ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); + ret = pmbus_read_word_data(client, 0, 0xff, + LM25066_READ_PIN_PEAK); break; case PMBUS_VIRT_RESET_PIN_HISTORY: ret = 0; @@ -288,13 +320,14 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) +static int lm25056_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_VMON_UV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25056_VAUX_UV_WARN_LIMIT); if (ret < 0) break; @@ -302,7 +335,7 @@ static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) ret = DIV_ROUND_CLOSEST(ret * 293, 6140); break; case PMBUS_VIRT_VMON_OV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, LM25056_VAUX_OV_WARN_LIMIT); if (ret < 0) break; @@ -310,7 +343,7 @@ static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) ret = DIV_ROUND_CLOSEST(ret * 293, 6140); break; default: - ret = lm25066_read_word_data(client, page, reg); + ret = lm25066_read_word_data(client, page, phase, reg); break; } return ret; @@ -360,21 +393,18 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, case PMBUS_VIN_OV_WARN_LIMIT: word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, reg, word); - pmbus_clear_cache(client); break; case PMBUS_IIN_OC_WARN_LIMIT: word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_IIN_OC_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_PIN_OP_WARN_LIMIT: word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_PIN_OP_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_VIRT_VMON_UV_WARN_LIMIT: /* Adjust from VIN coefficients (for LM25056) */ @@ -382,7 +412,6 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25056_VAUX_UV_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_VIRT_VMON_OV_WARN_LIMIT: /* Adjust from VIN coefficients (for LM25056) */ @@ -390,7 +419,6 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25056_VAUX_OV_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_VIRT_RESET_PIN_HISTORY: ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); @@ -407,13 +435,41 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, return ret; } -static int lm25066_probe(struct i2c_client *client, - const struct i2c_device_id *id) +#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR) +static const struct regulator_desc lm25066_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), +}; +#endif + +static const struct i2c_device_id lm25066_id[] = { + {"lm25056", lm25056}, + {"lm25066", lm25066}, + {"lm5064", lm5064}, + {"lm5066", lm5066}, + {"lm5066i", lm5066i}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm25066_id); + +static const struct of_device_id __maybe_unused lm25066_of_match[] = { + { .compatible = "ti,lm25056", .data = (void *)lm25056, }, + { .compatible = "ti,lm25066", .data = (void *)lm25066, }, + { .compatible = "ti,lm5064", .data = (void *)lm5064, }, + { .compatible = "ti,lm5066", .data = (void *)lm5066, }, + { .compatible = "ti,lm5066i", .data = (void *)lm5066i, }, + { }, +}; +MODULE_DEVICE_TABLE(of, lm25066_of_match); + +static int lm25066_probe(struct i2c_client *client) { int config; + u32 shunt; struct lm25066_data *data; struct pmbus_driver_info *info; - struct __coeff *coeff; + const struct __coeff *coeff; + const struct of_device_id *of_id; + const struct i2c_device_id *i2c_id; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) @@ -428,7 +484,14 @@ static int lm25066_probe(struct i2c_client *client, if (config < 0) return config; - data->id = id->driver_data; + i2c_id = i2c_match_id(lm25066_id, client); + + of_id = of_match_device(lm25066_of_match, &client->dev); + if (of_id && (unsigned long)of_id->data != i2c_id->driver_data) + dev_notice(&client->dev, "Device mismatch: %s in device tree, %s detected\n", + of_id->name, i2c_id->name); + + data->id = i2c_id->driver_data; info = &data->info; info->pages = 1; @@ -478,27 +541,34 @@ static int lm25066_probe(struct i2c_client *client, info->b[PSC_POWER] = coeff[PSC_POWER].b; } - return pmbus_do_probe(client, id, info); -} - -static const struct i2c_device_id lm25066_id[] = { - {"lm25056", lm25056}, - {"lm25066", lm25066}, - {"lm5064", lm5064}, - {"lm5066", lm5066}, - {"lm5066i", lm5066i}, - { } -}; + /* + * Values in the TI datasheets are normalized for a 1mOhm sense + * resistor; assume that unless DT specifies a value explicitly. + */ + if (of_property_read_u32(client->dev.of_node, "shunt-resistor-micro-ohms", &shunt)) + shunt = 1000; + + info->m[PSC_CURRENT_IN] = info->m[PSC_CURRENT_IN] * shunt / 1000; + info->m[PSC_POWER] = info->m[PSC_POWER] * shunt / 1000; + +#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR) + /* LM25056 doesn't support OPERATION */ + if (data->id != lm25056) { + info->num_regulators = ARRAY_SIZE(lm25066_reg_desc); + info->reg_desc = lm25066_reg_desc; + } +#endif -MODULE_DEVICE_TABLE(i2c, lm25066_id); + return pmbus_do_probe(client, info); +} /* This is the driver that will be inserted */ static struct i2c_driver lm25066_driver = { .driver = { .name = "lm25066", - }, - .probe = lm25066_probe, - .remove = pmbus_do_remove, + .of_match_table = of_match_ptr(lm25066_of_match), + }, + .probe_new = lm25066_probe, .id_table = lm25066_id, }; @@ -507,3 +577,4 @@ module_i2c_driver(lm25066_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c new file mode 100644 index 000000000000..4cfe476fc92d --- /dev/null +++ b/drivers/hwmon/pmbus/lt7182s.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for Analog Devices LT7182S + * + * Copyright (c) 2022 Guenter Roeck + * + */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include "pmbus.h" + +#define LT7182S_NUM_PAGES 2 + +#define MFR_READ_EXTVCC 0xcd +#define MFR_READ_ITH 0xce +#define MFR_CONFIG_ALL_LT7182S 0xd1 +#define MFR_IOUT_PEAK 0xd7 +#define MFR_ADC_CONTROL_LT7182S 0xd8 + +#define MFR_DEBUG_TELEMETRY BIT(0) + +#define MFR_VOUT_PEAK 0xdd +#define MFR_VIN_PEAK 0xde +#define MFR_TEMPERATURE_1_PEAK 0xdf +#define MFR_CLEAR_PEAKS 0xe3 + +#define MFR_CONFIG_IEEE BIT(8) + +static int lt7182s_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + if (page == 0 || page == 1) + ret = pmbus_read_word_data(client, page, phase, MFR_READ_ITH); + else + ret = pmbus_read_word_data(client, 0, phase, MFR_READ_EXTVCC); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_VIN_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_TEMPERATURE_1_PEAK); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = (page == 0) ? 0 : -ENODATA; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int lt7182s_write_word_data(struct i2c_client *client, int page, int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = pmbus_write_byte(client, 0, MFR_CLEAR_PEAKS); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static struct pmbus_driver_info lt7182s_info = { + .pages = LT7182S_NUM_PAGES, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT, + .read_word_data = lt7182s_read_word_data, + .write_word_data = lt7182s_write_word_data, +}; + +static int lt7182s_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) { + dev_err(dev, "Failed to read PMBUS_MFR_ID\n"); + return ret; + } + if (ret != 3 || strncmp(buf, "ADI", 3)) { + buf[ret] = '\0'; + dev_err(dev, "Manufacturer '%s' not supported\n", buf); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) { + dev_err(dev, "Failed to read PMBUS_MFR_MODEL\n"); + return ret; + } + if (ret != 7 || strncmp(buf, "LT7182S", 7)) { + buf[ret] = '\0'; + dev_err(dev, "Model '%s' not supported\n", buf); + return -ENODEV; + } + + info = devm_kmemdup(dev, <7182s_info, + sizeof(struct pmbus_driver_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + /* Set data format to IEEE754 if configured */ + ret = i2c_smbus_read_word_data(client, MFR_CONFIG_ALL_LT7182S); + if (ret < 0) + return ret; + if (ret & MFR_CONFIG_IEEE) { + info->format[PSC_VOLTAGE_IN] = ieee754; + info->format[PSC_VOLTAGE_OUT] = ieee754; + info->format[PSC_CURRENT_IN] = ieee754; + info->format[PSC_CURRENT_OUT] = ieee754; + info->format[PSC_TEMPERATURE] = ieee754; + info->format[PSC_POWER] = ieee754; + } + + /* Enable VMON output if configured */ + ret = i2c_smbus_read_byte_data(client, MFR_ADC_CONTROL_LT7182S); + if (ret < 0) + return ret; + if (ret & MFR_DEBUG_TELEMETRY) { + info->pages = 3; + info->func[0] |= PMBUS_HAVE_VMON; + info->func[1] |= PMBUS_HAVE_VMON; + info->func[2] = PMBUS_HAVE_VMON; + } + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id lt7182s_id[] = { + { "lt7182s", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, lt7182s_id); + +static const struct of_device_id __maybe_unused lt7182s_of_match[] = { + { .compatible = "adi,lt7182s" }, + {} +}; + +static struct i2c_driver lt7182s_driver = { + .driver = { + .name = "lt7182s", + .of_match_table = of_match_ptr(lt7182s_of_match), + }, + .probe_new = lt7182s_probe, + .id_table = lt7182s_id, +}; + +module_i2c_driver(lt7182s_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices LT7182S"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index a91ed01abb68..6d2592731ba3 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -19,8 +19,15 @@ #include <linux/regulator/driver.h> #include "pmbus.h" -enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, - ltc3883, ltc3886, ltc3887, ltm2987, ltm4675, ltm4676, ltm4686 }; +enum chips { + /* Managers */ + ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980, + /* Controllers */ + ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7880, + /* Modules */ + ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, + ltm4700, +}; /* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd @@ -43,9 +50,10 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 -/* LTC3883 and LTC3886 only */ +/* LTC3883, LTC3884, LTC3886, LTC3889 and LTC7880 only */ #define LTC3883_MFR_IIN_PEAK 0xe1 + /* LTC2975 only */ #define LTC2975_MFR_IIN_PEAK 0xc4 #define LTC2975_MFR_IIN_MIN 0xc5 @@ -54,27 +62,41 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, #define LTC2978_ID_MASK 0xfff0 +#define LTC2972_ID 0x0310 #define LTC2974_ID 0x0210 #define LTC2975_ID 0x0220 #define LTC2977_ID 0x0130 #define LTC2978_ID_REV1 0x0110 /* Early revision */ #define LTC2978_ID_REV2 0x0120 +#define LTC2979_ID_A 0x8060 +#define LTC2979_ID_B 0x8070 #define LTC2980_ID_A 0x8030 /* A/B for two die IDs */ #define LTC2980_ID_B 0x8040 #define LTC3880_ID 0x4020 #define LTC3882_ID 0x4200 #define LTC3882_ID_D1 0x4240 /* Dash 1 */ #define LTC3883_ID 0x4300 +#define LTC3884_ID 0x4C00 #define LTC3886_ID 0x4600 #define LTC3887_ID 0x4700 #define LTM2987_ID_A 0x8010 /* A/B for two die IDs */ #define LTM2987_ID_B 0x8020 +#define LTC3889_ID 0x4900 +#define LTC7880_ID 0x49E0 +#define LTM4664_ID 0x4120 #define LTM4675_ID 0x47a0 #define LTM4676_ID_REV1 0x4400 #define LTM4676_ID_REV2 0x4480 #define LTM4676A_ID 0x47e0 +#define LTM4677_ID_REV1 0x47B0 +#define LTM4677_ID_REV2 0x47D0 +#define LTM4678_ID_REV1 0x4100 +#define LTM4678_ID_REV2 0x4110 +#define LTM4680_ID 0x4140 #define LTM4686_ID 0x4770 +#define LTM4700_ID 0x4130 +#define LTC2972_NUM_PAGES 2 #define LTC2974_NUM_PAGES 4 #define LTC2978_NUM_PAGES 8 #define LTC3880_NUM_PAGES 2 @@ -151,7 +173,8 @@ static int ltc_wait_ready(struct i2c_client *client) return -ETIMEDOUT; } -static int ltc_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc_read_word_data(struct i2c_client *client, int page, int phase, + int reg) { int ret; @@ -159,7 +182,7 @@ static int ltc_read_word_data(struct i2c_client *client, int page, int reg) if (ret < 0) return ret; - return pmbus_read_word_data(client, page, reg); + return pmbus_read_word_data(client, page, 0xff, reg); } static int ltc_read_byte_data(struct i2c_client *client, int page, int reg) @@ -173,6 +196,17 @@ static int ltc_read_byte_data(struct i2c_client *client, int page, int reg) return pmbus_read_byte_data(client, page, reg); } +static int ltc_write_byte_data(struct i2c_client *client, int page, int reg, u8 value) +{ + int ret; + + ret = ltc_wait_ready(client); + if (ret < 0) + return ret; + + return pmbus_write_byte_data(client, page, reg, value); +} + static int ltc_write_byte(struct i2c_client *client, int page, u8 byte) { int ret; @@ -202,7 +236,7 @@ static int ltc_get_max(struct ltc2978_data *data, struct i2c_client *client, { int ret; - ret = ltc_read_word_data(client, page, reg); + ret = ltc_read_word_data(client, page, 0xff, reg); if (ret >= 0) { if (lin11_to_val(ret) > lin11_to_val(*pmax)) *pmax = ret; @@ -216,7 +250,7 @@ static int ltc_get_min(struct ltc2978_data *data, struct i2c_client *client, { int ret; - ret = ltc_read_word_data(client, page, reg); + ret = ltc_read_word_data(client, page, 0xff, reg); if (ret >= 0) { if (lin11_to_val(ret) < lin11_to_val(*pmin)) *pmin = ret; @@ -238,7 +272,8 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page, &data->vin_max); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); + ret = ltc_read_word_data(client, page, 0xff, + LTC2978_MFR_VOUT_PEAK); if (ret >= 0) { /* * VOUT is 16 bit unsigned with fixed exponent, @@ -269,7 +304,8 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page, return ret; } -static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc2978_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -281,7 +317,8 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) &data->vin_min); break; case PMBUS_VIRT_READ_VOUT_MIN: - ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); + ret = ltc_read_word_data(client, page, phase, + LTC2978_MFR_VOUT_MIN); if (ret >= 0) { /* * VOUT_MIN is known to not be supported on some lots @@ -314,7 +351,8 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc2974_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -333,13 +371,14 @@ static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) ret = 0; break; default: - ret = ltc2978_read_word_data(client, page, reg); + ret = ltc2978_read_word_data(client, page, phase, reg); break; } return ret; } -static int ltc2975_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc2975_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -367,13 +406,14 @@ static int ltc2975_read_word_data(struct i2c_client *client, int page, int reg) ret = 0; break; default: - ret = ltc2978_read_word_data(client, page, reg); + ret = ltc2978_read_word_data(client, page, phase, reg); break; } return ret; } -static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc3880_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -405,7 +445,8 @@ static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc3883_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct ltc2978_data *data = to_ltc2978_data(info); @@ -420,7 +461,7 @@ static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) ret = 0; break; default: - ret = ltc3880_read_word_data(client, page, reg); + ret = ltc3880_read_word_data(client, page, phase, reg); break; } return ret; @@ -492,26 +533,53 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { + {"ltc2972", ltc2972}, {"ltc2974", ltc2974}, {"ltc2975", ltc2975}, {"ltc2977", ltc2977}, {"ltc2978", ltc2978}, + {"ltc2979", ltc2979}, {"ltc2980", ltc2980}, {"ltc3880", ltc3880}, {"ltc3882", ltc3882}, {"ltc3883", ltc3883}, + {"ltc3884", ltc3884}, {"ltc3886", ltc3886}, {"ltc3887", ltc3887}, + {"ltc3889", ltc3889}, + {"ltc7880", ltc7880}, {"ltm2987", ltm2987}, + {"ltm4664", ltm4664}, {"ltm4675", ltm4675}, {"ltm4676", ltm4676}, + {"ltm4677", ltm4677}, + {"ltm4678", ltm4678}, + {"ltm4680", ltm4680}, {"ltm4686", ltm4686}, + {"ltm4700", ltm4700}, {} }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) +#define LTC2978_ADC_RES 0xFFFF +#define LTC2978_N_ADC 122 +#define LTC2978_MAX_UV (LTC2978_ADC_RES * LTC2978_N_ADC) +#define LTC2978_UV_STEP 1000 +#define LTC2978_N_VOLTAGES ((LTC2978_MAX_UV / LTC2978_UV_STEP) + 1) + static const struct regulator_desc ltc2978_reg_desc[] = { + PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), +}; + +static const struct regulator_desc ltc2978_reg_desc_default[] = { PMBUS_REGULATOR("vout", 0), PMBUS_REGULATOR("vout", 1), PMBUS_REGULATOR("vout", 2), @@ -555,7 +623,9 @@ static int ltc2978_get_id(struct i2c_client *client) chip_id &= LTC2978_ID_MASK; - if (chip_id == LTC2974_ID) + if (chip_id == LTC2972_ID) + return ltc2972; + else if (chip_id == LTC2974_ID) return ltc2974; else if (chip_id == LTC2975_ID) return ltc2975; @@ -563,6 +633,8 @@ static int ltc2978_get_id(struct i2c_client *client) return ltc2977; else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) return ltc2978; + else if (chip_id == LTC2979_ID_A || chip_id == LTC2979_ID_B) + return ltc2979; else if (chip_id == LTC2980_ID_A || chip_id == LTC2980_ID_B) return ltc2980; else if (chip_id == LTC3880_ID) @@ -571,30 +643,46 @@ static int ltc2978_get_id(struct i2c_client *client) return ltc3882; else if (chip_id == LTC3883_ID) return ltc3883; + else if (chip_id == LTC3884_ID) + return ltc3884; else if (chip_id == LTC3886_ID) return ltc3886; else if (chip_id == LTC3887_ID) return ltc3887; + else if (chip_id == LTC3889_ID) + return ltc3889; + else if (chip_id == LTC7880_ID) + return ltc7880; else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B) return ltm2987; + else if (chip_id == LTM4664_ID) + return ltm4664; else if (chip_id == LTM4675_ID) return ltm4675; else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 || chip_id == LTM4676A_ID) return ltm4676; + else if (chip_id == LTM4677_ID_REV1 || chip_id == LTM4677_ID_REV2) + return ltm4677; + else if (chip_id == LTM4678_ID_REV1 || chip_id == LTM4678_ID_REV2) + return ltm4678; + else if (chip_id == LTM4680_ID) + return ltm4680; else if (chip_id == LTM4686_ID) return ltm4686; + else if (chip_id == LTM4700_ID) + return ltm4700; dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); return -ENODEV; } -static int ltc2978_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc2978_probe(struct i2c_client *client) { int i, chip_id; struct ltc2978_data *data; struct pmbus_driver_info *info; + const struct i2c_device_id *id; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) @@ -610,15 +698,18 @@ static int ltc2978_probe(struct i2c_client *client, return chip_id; data->id = chip_id; + id = i2c_match_id(ltc2978_id, client); if (data->id != id->driver_data) dev_warn(&client->dev, - "Device mismatch: Configured %s, detected %s\n", + "Device mismatch: Configured %s (%d), detected %d\n", id->name, - ltc2978_id[data->id].name); + (int) id->driver_data, + chip_id); info = &data->info; info->write_word_data = ltc2978_write_word_data; info->write_byte = ltc_write_byte; + info->write_byte_data = ltc_write_byte_data; info->read_word_data = ltc_read_word_data; info->read_byte_data = ltc_read_byte_data; @@ -637,6 +728,19 @@ static int ltc2978_probe(struct i2c_client *client, data->temp2_max = 0x7c00; switch (data->id) { + case ltc2972: + info->read_word_data = ltc2975_read_word_data; + info->pages = LTC2972_NUM_PAGES; + info->func[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + } + break; case ltc2974: info->read_word_data = ltc2974_read_word_data; info->pages = LTC2974_NUM_PAGES; @@ -662,8 +766,10 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; } break; + case ltc2977: case ltc2978: + case ltc2979: case ltc2980: case ltm2987: info->read_word_data = ltc2978_read_word_data; @@ -680,6 +786,7 @@ static int ltc2978_probe(struct i2c_client *client, case ltc3887: case ltm4675: case ltm4676: + case ltm4677: case ltm4686: data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; info->read_word_data = ltc3880_read_word_data; @@ -721,7 +828,14 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; break; + case ltc3884: case ltc3886: + case ltc3889: + case ltc7880: + case ltm4664: + case ltm4678: + case ltm4680: + case ltm4700: data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; info->read_word_data = ltc3883_read_word_data; info->pages = LTC3880_NUM_PAGES; @@ -742,32 +856,62 @@ static int ltc2978_probe(struct i2c_client *client, #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) info->num_regulators = info->pages; - info->reg_desc = ltc2978_reg_desc; - if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) { - dev_err(&client->dev, "num_regulators too large!"); - info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc); + switch (data->id) { + case ltc2972: + case ltc2974: + case ltc2975: + case ltc2977: + case ltc2978: + case ltc2979: + case ltc2980: + case ltm2987: + info->reg_desc = ltc2978_reg_desc; + if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) { + dev_warn(&client->dev, "num_regulators too large!"); + info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc); + } + break; + default: + info->reg_desc = ltc2978_reg_desc_default; + if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc_default)) { + dev_warn(&client->dev, "num_regulators too large!"); + info->num_regulators = + ARRAY_SIZE(ltc2978_reg_desc_default); + } + break; } #endif - return pmbus_do_probe(client, id, info); + return pmbus_do_probe(client, info); } + #ifdef CONFIG_OF static const struct of_device_id ltc2978_of_match[] = { + { .compatible = "lltc,ltc2972" }, { .compatible = "lltc,ltc2974" }, { .compatible = "lltc,ltc2975" }, { .compatible = "lltc,ltc2977" }, { .compatible = "lltc,ltc2978" }, + { .compatible = "lltc,ltc2979" }, { .compatible = "lltc,ltc2980" }, { .compatible = "lltc,ltc3880" }, { .compatible = "lltc,ltc3882" }, { .compatible = "lltc,ltc3883" }, + { .compatible = "lltc,ltc3884" }, { .compatible = "lltc,ltc3886" }, { .compatible = "lltc,ltc3887" }, + { .compatible = "lltc,ltc3889" }, + { .compatible = "lltc,ltc7880" }, { .compatible = "lltc,ltm2987" }, + { .compatible = "lltc,ltm4664" }, { .compatible = "lltc,ltm4675" }, { .compatible = "lltc,ltm4676" }, + { .compatible = "lltc,ltm4677" }, + { .compatible = "lltc,ltm4678" }, + { .compatible = "lltc,ltm4680" }, { .compatible = "lltc,ltm4686" }, + { .compatible = "lltc,ltm4700" }, { } }; MODULE_DEVICE_TABLE(of, ltc2978_of_match); @@ -778,8 +922,7 @@ static struct i2c_driver ltc2978_driver = { .name = "ltc2978", .of_match_table = of_match_ptr(ltc2978_of_match), }, - .probe = ltc2978_probe, - .remove = pmbus_do_remove, + .probe_new = ltc2978_probe, .id_table = ltc2978_id, }; @@ -788,3 +931,4 @@ module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC2978 and compatible chips"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index b83a18a58364..8e13a7ddcb42 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -55,7 +55,7 @@ static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg) * LTC3815 does not support the CLEAR_FAULTS command. * Emulate it by clearing the status register. */ - ret = pmbus_read_word_data(client, 0, PMBUS_STATUS_WORD); + ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_WORD); if (ret > 0) { pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD, ret); @@ -69,25 +69,31 @@ static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg) return ret; } -static int ltc3815_read_word_data(struct i2c_client *client, int page, int reg) +static int ltc3815_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_READ_VIN_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_VIN_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_VIN_PEAK); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_VOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_TEMP_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_TEMP_PEAK); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_IOUT_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_IIN_MAX: - ret = pmbus_read_word_data(client, page, LTC3815_MFR_IIN_PEAK); + ret = pmbus_read_word_data(client, page, phase, + LTC3815_MFR_IIN_PEAK); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: case PMBUS_VIRT_RESET_VIN_HISTORY: @@ -172,8 +178,7 @@ static struct pmbus_driver_info ltc3815_info = { .write_word_data = ltc3815_write_word_data, }; -static int ltc3815_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc3815_probe(struct i2c_client *client) { int chip_id; @@ -187,15 +192,14 @@ static int ltc3815_probe(struct i2c_client *client, if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID) return -ENODEV; - return pmbus_do_probe(client, id, <c3815_info); + return pmbus_do_probe(client, <c3815_info); } static struct i2c_driver ltc3815_driver = { .driver = { .name = "ltc3815", }, - .probe = ltc3815_probe, - .remove = pmbus_do_remove, + .probe_new = ltc3815_probe, .id_table = ltc3815_id, }; @@ -204,3 +208,4 @@ module_i2c_driver(ltc3815_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC3815"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c new file mode 100644 index 000000000000..0b6f88428ea8 --- /dev/null +++ b/drivers/hwmon/pmbus/max15301.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Maxim MAX15301 + * + * Copyright (c) 2021 Flextronics International Sweden AB + * + * Even though the specification does not specifically mention it, + * extensive empirical testing has revealed that auto-detection of + * limit-registers will fail in a random fashion unless the delay + * parameter is set to above about 80us. The default delay is set + * to 100us to include some safety margin. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/ktime.h> +#include <linux/delay.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +static const struct i2c_device_id max15301_id[] = { + {"bmr461", 0}, + {"max15301", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max15301_id); + +struct max15301_data { + int id; + ktime_t access; /* Chip access time */ + int delay; /* Delay between chip accesses in us */ + struct pmbus_driver_info info; +}; + +#define to_max15301_data(x) container_of(x, struct max15301_data, info) + +#define MAX15301_WAIT_TIME 100 /* us */ + +static ushort delay = MAX15301_WAIT_TIME; +module_param(delay, ushort, 0644); +MODULE_PARM_DESC(delay, "Delay between chip accesses in us"); + +static struct max15301_data max15301_data = { + .info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + } +}; + +/* This chip needs a delay between accesses */ +static inline void max15301_wait(const struct max15301_data *data) +{ + if (data->delay) { + s64 delta = ktime_us_delta(ktime_get(), data->access); + + if (delta < data->delay) + udelay(data->delay - delta); + } +} + +static int max15301_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_read_word_data(client, page, phase, reg); + data->access = ktime_get(); + + return ret; +} + +static int max15301_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_read_byte_data(client, page, reg); + data->access = ktime_get(); + + return ret; +} + +static int max15301_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_write_word_data(client, page, reg, word); + data->access = ktime_get(); + + return ret; +} + +static int max15301_write_byte(struct i2c_client *client, int page, u8 value) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_write_byte(client, page, value); + data->access = ktime_get(); + + return ret; +} + +static int max15301_probe(struct i2c_client *client) +{ + int status; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + struct pmbus_driver_info *info = &max15301_data.info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + status = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, device_id); + if (status < 0) { + dev_err(&client->dev, "Failed to read Device Id\n"); + return status; + } + for (mid = max15301_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + max15301_data.delay = delay; + + info->read_byte_data = max15301_read_byte_data; + info->read_word_data = max15301_read_word_data; + info->write_byte = max15301_write_byte; + info->write_word_data = max15301_write_word_data; + + return pmbus_do_probe(client, info); +} + +static struct i2c_driver max15301_driver = { + .driver = { + .name = "max15301", + }, + .probe_new = max15301_probe, + .id_table = max15301_id, +}; + +module_i2c_driver(max15301_driver); + +MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX15301"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index b3e7b8d2e69d..94f869039071 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -15,17 +15,18 @@ #define MAX16064_MFR_VOUT_PEAK 0xd4 #define MAX16064_MFR_TEMPERATURE_PEAK 0xd6 -static int max16064_read_word_data(struct i2c_client *client, int page, int reg) +static int max16064_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; switch (reg) { case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX16064_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX16064_MFR_TEMPERATURE_PEAK); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: @@ -84,10 +85,9 @@ static struct pmbus_driver_info max16064_info = { .write_word_data = max16064_write_word_data, }; -static int max16064_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max16064_probe(struct i2c_client *client) { - return pmbus_do_probe(client, id, &max16064_info); + return pmbus_do_probe(client, &max16064_info); } static const struct i2c_device_id max16064_id[] = { @@ -102,8 +102,7 @@ static struct i2c_driver max16064_driver = { .driver = { .name = "max16064", }, - .probe = max16064_probe, - .remove = pmbus_do_remove, + .probe_new = max16064_probe, .id_table = max16064_id, }; @@ -112,3 +111,4 @@ module_i2c_driver(max16064_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c new file mode 100644 index 000000000000..b628405e6586 --- /dev/null +++ b/drivers/hwmon/pmbus/max16601.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Maxim MAX16508, MAX16601 and MAX16602. + * + * Implementation notes: + * + * This chip series supports two rails, VCORE and VSA. Telemetry information + * for the two rails is reported in two subsequent I2C addresses. The driver + * instantiates a dummy I2C client at the second I2C address to report + * information for the VSA rail in a single instance of the driver. + * Telemetry for the VSA rail is reported to the PMBus core in PMBus page 2. + * + * The chip reports input current using two separate methods. The input current + * reported with the standard READ_IIN command is derived from the output + * current. The first method is reported to the PMBus core with PMBus page 0, + * the second method is reported with PMBus page 1. + * + * The chip supports reading per-phase temperatures and per-phase input/output + * currents for VCORE. Telemetry is reported in vendor specific registers. + * The driver translates the vendor specific register values to PMBus standard + * register values and reports per-phase information in PMBus page 0. + * + * Copyright 2019, 2020 Google LLC. + */ + +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "pmbus.h" + +enum chips { max16508, max16601, max16602 }; + +#define REG_DEFAULT_NUM_POP 0xc4 +#define REG_SETPT_DVID 0xd1 +#define DAC_10MV_MODE BIT(4) +#define REG_IOUT_AVG_PK 0xee +#define REG_IIN_SENSOR 0xf1 +#define REG_TOTAL_INPUT_POWER 0xf2 +#define REG_PHASE_ID 0xf3 +#define CORE_RAIL_INDICATOR BIT(7) +#define REG_PHASE_REPORTING 0xf4 + +#define MAX16601_NUM_PHASES 8 + +struct max16601_data { + enum chips id; + struct pmbus_driver_info info; + struct i2c_client *vsa; + int iout_avg_pkg; +}; + +#define to_max16601_data(x) container_of(x, struct max16601_data, info) + +static int max16601_read_byte(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max16601_data *data = to_max16601_data(info); + + if (page > 0) { + if (page == 2) /* VSA */ + return i2c_smbus_read_byte_data(data->vsa, reg); + return -EOPNOTSUPP; + } + return -ENODATA; +} + +static int max16601_read_word(struct i2c_client *client, int page, int phase, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max16601_data *data = to_max16601_data(info); + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + int ret; + + switch (page) { + case 0: /* VCORE */ + if (phase == 0xff) + return -ENODATA; + switch (reg) { + case PMBUS_READ_IIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_TEMPERATURE_1: + ret = i2c_smbus_write_byte_data(client, REG_PHASE_ID, + phase); + if (ret) + return ret; + ret = i2c_smbus_read_block_data(client, + REG_PHASE_REPORTING, + buf); + if (ret < 0) + return ret; + if (ret < 6) + return -EIO; + switch (reg) { + case PMBUS_READ_TEMPERATURE_1: + return buf[1] << 8 | buf[0]; + case PMBUS_READ_IOUT: + return buf[3] << 8 | buf[2]; + case PMBUS_READ_IIN: + return buf[5] << 8 | buf[4]; + default: + break; + } + } + return -EOPNOTSUPP; + case 1: /* VCORE, read IIN/PIN from sensor element */ + switch (reg) { + case PMBUS_READ_IIN: + return i2c_smbus_read_word_data(client, REG_IIN_SENSOR); + case PMBUS_READ_PIN: + return i2c_smbus_read_word_data(client, + REG_TOTAL_INPUT_POWER); + default: + break; + } + return -EOPNOTSUPP; + case 2: /* VSA */ + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = i2c_smbus_read_word_data(data->vsa, + REG_IOUT_AVG_PK); + if (ret < 0) + return ret; + if (sign_extend32(ret, 10) > + sign_extend32(data->iout_avg_pkg, 10)) + data->iout_avg_pkg = ret; + return data->iout_avg_pkg; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + return 0; + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_READ_IIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_STATUS_WORD: + return i2c_smbus_read_word_data(data->vsa, reg); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int max16601_write_byte(struct i2c_client *client, int page, u8 reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max16601_data *data = to_max16601_data(info); + + if (page == 2) { + if (reg == PMBUS_CLEAR_FAULTS) + return i2c_smbus_write_byte(data->vsa, reg); + return -EOPNOTSUPP; + } + return -ENODATA; +} + +static int max16601_write_word(struct i2c_client *client, int page, int reg, + u16 value) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max16601_data *data = to_max16601_data(info); + + switch (page) { + case 0: /* VCORE */ + return -ENODATA; + case 1: /* VCORE IIN/PIN from sensor element */ + default: + return -EOPNOTSUPP; + case 2: /* VSA */ + switch (reg) { + case PMBUS_VIRT_RESET_IOUT_HISTORY: + data->iout_avg_pkg = 0xfc00; + return 0; + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + return i2c_smbus_write_word_data(data->vsa, reg, value); + default: + return -EOPNOTSUPP; + } + } +} + +static int max16601_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + struct max16601_data *data = to_max16601_data(info); + int reg; + + reg = i2c_smbus_read_byte_data(client, REG_SETPT_DVID); + if (reg < 0) + return reg; + if (reg & DAC_10MV_MODE) + info->vrm_version[0] = vr13; + else + info->vrm_version[0] = vr12; + + if (data->id != max16601 && data->id != max16602) + return 0; + + reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP); + if (reg < 0) + return reg; + + /* + * If REG_DEFAULT_NUM_POP returns 0, we don't know how many phases + * are populated. Stick with the default in that case. + */ + reg &= 0x0f; + if (reg && reg <= MAX16601_NUM_PHASES) + info->phases[0] = reg; + + return 0; +} + +static struct pmbus_driver_info max16601_info = { + .pages = 3, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = vid, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT | PMBUS_PAGE_VIRTUAL | PMBUS_PHASE_VIRTUAL, + .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_PAGE_VIRTUAL, + .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_PAGE_VIRTUAL, + .phases[0] = MAX16601_NUM_PHASES, + .pfunc[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, + .pfunc[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT, + .pfunc[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, + .pfunc[3] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT, + .pfunc[4] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, + .pfunc[5] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT, + .pfunc[6] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, + .pfunc[7] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT, + .identify = max16601_identify, + .read_byte_data = max16601_read_byte, + .read_word_data = max16601_read_word, + .write_byte = max16601_write_byte, + .write_word_data = max16601_write_word, +}; + +static void max16601_remove(void *_data) +{ + struct max16601_data *data = _data; + + i2c_unregister_device(data->vsa); +} + +static const struct i2c_device_id max16601_id[] = { + {"max16508", max16508}, + {"max16601", max16601}, + {"max16602", max16602}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max16601_id); + +static int max16601_get_id(struct i2c_client *client) +{ + struct device *dev = &client->dev; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + enum chips id; + int ret; + + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0 || ret < 11) + return -ENODEV; + + /* + * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" or + * MAX16602y.xx or "MAX16500y.xx".cdxxcccccccccc + */ + if (!strncmp(buf, "MAX16500", 8)) { + id = max16508; + } else if (!strncmp(buf, "MAX16601", 8)) { + id = max16601; + } else if (!strncmp(buf, "MAX16602", 8)) { + id = max16602; + } else { + buf[ret] = '\0'; + dev_err(dev, "Unsupported chip '%s'\n", buf); + return -ENODEV; + } + return id; +} + +static int max16601_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct i2c_device_id *id; + struct max16601_data *data; + int ret, chip_id; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + chip_id = max16601_get_id(client); + if (chip_id < 0) + return chip_id; + + id = i2c_match_id(max16601_id, client); + if (chip_id != id->driver_data) + dev_warn(&client->dev, + "Device mismatch: Configured %s (%d), detected %d\n", + id->name, (int) id->driver_data, chip_id); + + ret = i2c_smbus_read_byte_data(client, REG_PHASE_ID); + if (ret < 0) + return ret; + if (!(ret & CORE_RAIL_INDICATOR)) { + dev_err(dev, + "Driver must be instantiated on CORE rail I2C address\n"); + return -ENODEV; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = chip_id; + data->iout_avg_pkg = 0xfc00; + data->vsa = i2c_new_dummy_device(client->adapter, client->addr + 1); + if (IS_ERR(data->vsa)) { + dev_err(dev, "Failed to register VSA client\n"); + return PTR_ERR(data->vsa); + } + ret = devm_add_action_or_reset(dev, max16601_remove, data); + if (ret) + return ret; + + data->info = max16601_info; + + return pmbus_do_probe(client, &data->info); +} + +static struct i2c_driver max16601_driver = { + .driver = { + .name = "max16601", + }, + .probe_new = max16601_probe, + .id_table = max16601_id, +}; + +module_i2c_driver(max16601_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index 294e2212f61e..ba39f03c6374 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Driver for MAX20730, MAX20734, and MAX20743 Integrated, Step-Down - * Switching Regulators + * Driver for MAX20710, MAX20730, MAX20734, and MAX20743 Integrated, + * Step-Down Switching Regulators * * Copyright 2019 Google LLC. + * Copyright 2020 Maxim Integrated */ #include <linux/bits.h> +#include <linux/debugfs.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/init.h> @@ -19,21 +21,374 @@ #include "pmbus.h" enum chips { + max20710, max20730, max20734, max20743 }; +enum { + MAX20730_DEBUGFS_VOUT_MIN = 0, + MAX20730_DEBUGFS_FREQUENCY, + MAX20730_DEBUGFS_PG_DELAY, + MAX20730_DEBUGFS_INTERNAL_GAIN, + MAX20730_DEBUGFS_BOOT_VOLTAGE, + MAX20730_DEBUGFS_OUT_V_RAMP_RATE, + MAX20730_DEBUGFS_OC_PROTECT_MODE, + MAX20730_DEBUGFS_SS_TIMING, + MAX20730_DEBUGFS_IMAX, + MAX20730_DEBUGFS_OPERATION, + MAX20730_DEBUGFS_ON_OFF_CONFIG, + MAX20730_DEBUGFS_SMBALERT_MASK, + MAX20730_DEBUGFS_VOUT_MODE, + MAX20730_DEBUGFS_VOUT_COMMAND, + MAX20730_DEBUGFS_VOUT_MAX, + MAX20730_DEBUGFS_NUM_ENTRIES +}; + struct max20730_data { enum chips id; struct pmbus_driver_info info; struct mutex lock; /* Used to protect against parallel writes */ u16 mfr_devset1; + u16 mfr_devset2; + u16 mfr_voutmin; + u32 vout_voltage_divider[2]; }; #define to_max20730_data(x) container_of(x, struct max20730_data, info) +#define VOLT_FROM_REG(val) DIV_ROUND_CLOSEST((val), 1 << 9) + +#define PMBUS_SMB_ALERT_MASK 0x1B + +#define MAX20730_MFR_VOUT_MIN 0xd1 #define MAX20730_MFR_DEVSET1 0xd2 +#define MAX20730_MFR_DEVSET2 0xd3 + +#define MAX20730_MFR_VOUT_MIN_MASK GENMASK(9, 0) +#define MAX20730_MFR_VOUT_MIN_BIT_POS 0 + +#define MAX20730_MFR_DEVSET1_RGAIN_MASK (BIT(13) | BIT(14)) +#define MAX20730_MFR_DEVSET1_OTP_MASK (BIT(11) | BIT(12)) +#define MAX20730_MFR_DEVSET1_VBOOT_MASK (BIT(8) | BIT(9)) +#define MAX20730_MFR_DEVSET1_OCP_MASK (BIT(5) | BIT(6)) +#define MAX20730_MFR_DEVSET1_FSW_MASK GENMASK(4, 2) +#define MAX20730_MFR_DEVSET1_TSTAT_MASK (BIT(0) | BIT(1)) + +#define MAX20730_MFR_DEVSET1_RGAIN_BIT_POS 13 +#define MAX20730_MFR_DEVSET1_OTP_BIT_POS 11 +#define MAX20730_MFR_DEVSET1_VBOOT_BIT_POS 8 +#define MAX20730_MFR_DEVSET1_OCP_BIT_POS 5 +#define MAX20730_MFR_DEVSET1_FSW_BIT_POS 2 +#define MAX20730_MFR_DEVSET1_TSTAT_BIT_POS 0 + +#define MAX20730_MFR_DEVSET2_IMAX_MASK GENMASK(10, 8) +#define MAX20730_MFR_DEVSET2_VRATE (BIT(6) | BIT(7)) +#define MAX20730_MFR_DEVSET2_OCPM_MASK BIT(5) +#define MAX20730_MFR_DEVSET2_SS_MASK (BIT(0) | BIT(1)) + +#define MAX20730_MFR_DEVSET2_IMAX_BIT_POS 8 +#define MAX20730_MFR_DEVSET2_VRATE_BIT_POS 6 +#define MAX20730_MFR_DEVSET2_OCPM_BIT_POS 5 +#define MAX20730_MFR_DEVSET2_SS_BIT_POS 0 + +#define DEBUG_FS_DATA_MAX 16 + +struct max20730_debugfs_data { + struct i2c_client *client; + int debugfs_entries[MAX20730_DEBUGFS_NUM_ENTRIES]; +}; + +#define to_psu(x, y) container_of((x), \ + struct max20730_debugfs_data, debugfs_entries[(y)]) + +#ifdef CONFIG_DEBUG_FS +static ssize_t max20730_debugfs_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int ret, len; + int *idxp = file->private_data; + int idx = *idxp; + struct max20730_debugfs_data *psu = to_psu(idxp, idx); + const struct pmbus_driver_info *info; + const struct max20730_data *data; + char tbuf[DEBUG_FS_DATA_MAX] = { 0 }; + u16 val; + + info = pmbus_get_driver_info(psu->client); + data = to_max20730_data(info); + + switch (idx) { + case MAX20730_DEBUGFS_VOUT_MIN: + ret = VOLT_FROM_REG(data->mfr_voutmin * 10000); + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d.%d\n", + ret / 10000, ret % 10000); + break; + case MAX20730_DEBUGFS_FREQUENCY: + val = (data->mfr_devset1 & MAX20730_MFR_DEVSET1_FSW_MASK) + >> MAX20730_MFR_DEVSET1_FSW_BIT_POS; + + if (val == 0) + ret = 400; + else if (val == 1) + ret = 500; + else if (val == 2 || val == 3) + ret = 600; + else if (val == 4) + ret = 700; + else if (val == 5) + ret = 800; + else + ret = 900; + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d\n", ret); + break; + case MAX20730_DEBUGFS_PG_DELAY: + val = (data->mfr_devset1 & MAX20730_MFR_DEVSET1_TSTAT_MASK) + >> MAX20730_MFR_DEVSET1_TSTAT_BIT_POS; + + if (val == 0) + len = strlcpy(tbuf, "2000\n", DEBUG_FS_DATA_MAX); + else if (val == 1) + len = strlcpy(tbuf, "125\n", DEBUG_FS_DATA_MAX); + else if (val == 2) + len = strlcpy(tbuf, "62.5\n", DEBUG_FS_DATA_MAX); + else + len = strlcpy(tbuf, "32\n", DEBUG_FS_DATA_MAX); + break; + case MAX20730_DEBUGFS_INTERNAL_GAIN: + val = (data->mfr_devset1 & MAX20730_MFR_DEVSET1_RGAIN_MASK) + >> MAX20730_MFR_DEVSET1_RGAIN_BIT_POS; + + if (data->id == max20734) { + /* AN6209 */ + if (val == 0) + len = strlcpy(tbuf, "0.8\n", DEBUG_FS_DATA_MAX); + else if (val == 1) + len = strlcpy(tbuf, "3.2\n", DEBUG_FS_DATA_MAX); + else if (val == 2) + len = strlcpy(tbuf, "1.6\n", DEBUG_FS_DATA_MAX); + else + len = strlcpy(tbuf, "6.4\n", DEBUG_FS_DATA_MAX); + } else if (data->id == max20730 || data->id == max20710) { + /* AN6042 or AN6140 */ + if (val == 0) + len = strlcpy(tbuf, "0.9\n", DEBUG_FS_DATA_MAX); + else if (val == 1) + len = strlcpy(tbuf, "3.6\n", DEBUG_FS_DATA_MAX); + else if (val == 2) + len = strlcpy(tbuf, "1.8\n", DEBUG_FS_DATA_MAX); + else + len = strlcpy(tbuf, "7.2\n", DEBUG_FS_DATA_MAX); + } else if (data->id == max20743) { + /* AN6042 */ + if (val == 0) + len = strlcpy(tbuf, "0.45\n", DEBUG_FS_DATA_MAX); + else if (val == 1) + len = strlcpy(tbuf, "1.8\n", DEBUG_FS_DATA_MAX); + else if (val == 2) + len = strlcpy(tbuf, "0.9\n", DEBUG_FS_DATA_MAX); + else + len = strlcpy(tbuf, "3.6\n", DEBUG_FS_DATA_MAX); + } else { + len = strlcpy(tbuf, "Not supported\n", DEBUG_FS_DATA_MAX); + } + break; + case MAX20730_DEBUGFS_BOOT_VOLTAGE: + val = (data->mfr_devset1 & MAX20730_MFR_DEVSET1_VBOOT_MASK) + >> MAX20730_MFR_DEVSET1_VBOOT_BIT_POS; + + if (val == 0) + len = strlcpy(tbuf, "0.6484\n", DEBUG_FS_DATA_MAX); + else if (val == 1) + len = strlcpy(tbuf, "0.8984\n", DEBUG_FS_DATA_MAX); + else if (val == 2) + len = strlcpy(tbuf, "1.0\n", DEBUG_FS_DATA_MAX); + else + len = strlcpy(tbuf, "Invalid\n", DEBUG_FS_DATA_MAX); + break; + case MAX20730_DEBUGFS_OUT_V_RAMP_RATE: + val = (data->mfr_devset2 & MAX20730_MFR_DEVSET2_VRATE) + >> MAX20730_MFR_DEVSET2_VRATE_BIT_POS; + + if (val == 0) + len = strlcpy(tbuf, "4\n", DEBUG_FS_DATA_MAX); + else if (val == 1) + len = strlcpy(tbuf, "2\n", DEBUG_FS_DATA_MAX); + else if (val == 2) + len = strlcpy(tbuf, "1\n", DEBUG_FS_DATA_MAX); + else + len = strlcpy(tbuf, "Invalid\n", DEBUG_FS_DATA_MAX); + break; + case MAX20730_DEBUGFS_OC_PROTECT_MODE: + ret = (data->mfr_devset2 & MAX20730_MFR_DEVSET2_OCPM_MASK) + >> MAX20730_MFR_DEVSET2_OCPM_BIT_POS; + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d\n", ret); + break; + case MAX20730_DEBUGFS_SS_TIMING: + val = (data->mfr_devset2 & MAX20730_MFR_DEVSET2_SS_MASK) + >> MAX20730_MFR_DEVSET2_SS_BIT_POS; + + if (val == 0) + len = strlcpy(tbuf, "0.75\n", DEBUG_FS_DATA_MAX); + else if (val == 1) + len = strlcpy(tbuf, "1.5\n", DEBUG_FS_DATA_MAX); + else if (val == 2) + len = strlcpy(tbuf, "3\n", DEBUG_FS_DATA_MAX); + else + len = strlcpy(tbuf, "6\n", DEBUG_FS_DATA_MAX); + break; + case MAX20730_DEBUGFS_IMAX: + ret = (data->mfr_devset2 & MAX20730_MFR_DEVSET2_IMAX_MASK) + >> MAX20730_MFR_DEVSET2_IMAX_BIT_POS; + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d\n", ret); + break; + case MAX20730_DEBUGFS_OPERATION: + ret = i2c_smbus_read_byte_data(psu->client, PMBUS_OPERATION); + if (ret < 0) + return ret; + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d\n", ret); + break; + case MAX20730_DEBUGFS_ON_OFF_CONFIG: + ret = i2c_smbus_read_byte_data(psu->client, PMBUS_ON_OFF_CONFIG); + if (ret < 0) + return ret; + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d\n", ret); + break; + case MAX20730_DEBUGFS_SMBALERT_MASK: + ret = i2c_smbus_read_word_data(psu->client, + PMBUS_SMB_ALERT_MASK); + if (ret < 0) + return ret; + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d\n", ret); + break; + case MAX20730_DEBUGFS_VOUT_MODE: + ret = i2c_smbus_read_byte_data(psu->client, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, "%d\n", ret); + break; + case MAX20730_DEBUGFS_VOUT_COMMAND: + ret = i2c_smbus_read_word_data(psu->client, PMBUS_VOUT_COMMAND); + if (ret < 0) + return ret; + + ret = VOLT_FROM_REG(ret * 10000); + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, + "%d.%d\n", ret / 10000, ret % 10000); + break; + case MAX20730_DEBUGFS_VOUT_MAX: + ret = i2c_smbus_read_word_data(psu->client, PMBUS_VOUT_MAX); + if (ret < 0) + return ret; + + ret = VOLT_FROM_REG(ret * 10000); + len = scnprintf(tbuf, DEBUG_FS_DATA_MAX, + "%d.%d\n", ret / 10000, ret % 10000); + break; + default: + len = strlcpy(tbuf, "Invalid\n", DEBUG_FS_DATA_MAX); + } + + return simple_read_from_buffer(buf, count, ppos, tbuf, len); +} + +static const struct file_operations max20730_fops = { + .llseek = noop_llseek, + .read = max20730_debugfs_read, + .write = NULL, + .open = simple_open, +}; + +static int max20730_init_debugfs(struct i2c_client *client, + struct max20730_data *data) +{ + int ret, i; + struct dentry *debugfs; + struct dentry *max20730_dir; + struct max20730_debugfs_data *psu; + + ret = i2c_smbus_read_word_data(client, MAX20730_MFR_DEVSET2); + if (ret < 0) + return ret; + data->mfr_devset2 = ret; + + ret = i2c_smbus_read_word_data(client, MAX20730_MFR_VOUT_MIN); + if (ret < 0) + return ret; + data->mfr_voutmin = ret; + + psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL); + if (!psu) + return -ENOMEM; + psu->client = client; + + debugfs = pmbus_get_debugfs_dir(client); + if (!debugfs) + return -ENOENT; + + max20730_dir = debugfs_create_dir(client->name, debugfs); + + for (i = 0; i < MAX20730_DEBUGFS_NUM_ENTRIES; ++i) + psu->debugfs_entries[i] = i; + + debugfs_create_file("vout_min", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_VOUT_MIN], + &max20730_fops); + debugfs_create_file("frequency", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_FREQUENCY], + &max20730_fops); + debugfs_create_file("power_good_delay", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_PG_DELAY], + &max20730_fops); + debugfs_create_file("internal_gain", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_INTERNAL_GAIN], + &max20730_fops); + debugfs_create_file("boot_voltage", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_BOOT_VOLTAGE], + &max20730_fops); + debugfs_create_file("out_voltage_ramp_rate", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_OUT_V_RAMP_RATE], + &max20730_fops); + debugfs_create_file("oc_protection_mode", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_OC_PROTECT_MODE], + &max20730_fops); + debugfs_create_file("soft_start_timing", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_SS_TIMING], + &max20730_fops); + debugfs_create_file("imax", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_IMAX], + &max20730_fops); + debugfs_create_file("operation", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_OPERATION], + &max20730_fops); + debugfs_create_file("on_off_config", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_ON_OFF_CONFIG], + &max20730_fops); + debugfs_create_file("smbalert_mask", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_SMBALERT_MASK], + &max20730_fops); + debugfs_create_file("vout_mode", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_VOUT_MODE], + &max20730_fops); + debugfs_create_file("vout_command", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_VOUT_COMMAND], + &max20730_fops); + debugfs_create_file("vout_max", 0444, max20730_dir, + &psu->debugfs_entries[MAX20730_DEBUGFS_VOUT_MAX], + &max20730_fops); + + return 0; +} +#else +static int max20730_init_debugfs(struct i2c_client *client, + struct max20730_data *data) +{ + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static const struct i2c_device_id max20730_id[]; /* * Convert discreet value to direct data format. Strictly speaking, all passed @@ -80,12 +435,14 @@ static long direct_to_val(u16 w, enum pmbus_sensor_classes class, } static u32 max_current[][5] = { + [max20710] = { 6200, 8000, 9700, 11600 }, [max20730] = { 13000, 16600, 20100, 23600 }, [max20734] = { 21000, 27000, 32000, 38000 }, [max20743] = { 18900, 24100, 29200, 34100 }, }; -static int max20730_read_word_data(struct i2c_client *client, int page, int reg) +static int max20730_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct max20730_data *data = to_max20730_data(info); @@ -110,6 +467,14 @@ static int max20730_read_word_data(struct i2c_client *client, int page, int reg) max_c = max_current[data->id][(data->mfr_devset1 >> 5) & 0x3]; ret = val_to_direct(max_c, PSC_CURRENT_OUT, info); break; + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret > 0 && data->vout_voltage_divider[0] && data->vout_voltage_divider[1]) { + u64 temp = DIV_ROUND_CLOSEST_ULL((u64)ret * data->vout_voltage_divider[1], + data->vout_voltage_divider[0]); + ret = clamp_val(temp, 0, 0xffff); + } + break; default: ret = -ENODATA; break; @@ -163,6 +528,35 @@ static int max20730_write_word_data(struct i2c_client *client, int page, } static const struct pmbus_driver_info max20730_info[] = { + [max20710] = { + .pages = 1, + .read_word_data = max20730_read_word_data, + .write_word_data = max20730_write_word_data, + + /* Source : Maxim AN6140 and AN6042 */ + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 21, + .b[PSC_TEMPERATURE] = 5887, + .R[PSC_TEMPERATURE] = -1, + + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 3609, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + + .format[PSC_CURRENT_OUT] = direct, + .m[PSC_CURRENT_OUT] = 153, + .b[PSC_CURRENT_OUT] = 4976, + .R[PSC_CURRENT_OUT] = -1, + + .format[PSC_VOLTAGE_OUT] = linear, + + .func[0] = PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_INPUT, + }, [max20730] = { .pages = 1, .read_word_data = max20730_read_word_data, @@ -199,7 +593,8 @@ static const struct pmbus_driver_info max20730_info[] = { .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | - PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_INPUT, }, [max20734] = { .pages = 1, @@ -227,7 +622,8 @@ static const struct pmbus_driver_info max20730_info[] = { .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | - PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_INPUT, }, [max20743] = { .pages = 1, @@ -255,12 +651,12 @@ static const struct pmbus_driver_info max20730_info[] = { .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | - PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_INPUT, }, }; -static int max20730_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max20730_probe(struct i2c_client *client) { struct device *dev = &client->dev; u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; @@ -320,7 +716,7 @@ static int max20730_probe(struct i2c_client *client, if (client->dev.of_node) chip_id = (enum chips)of_device_get_match_data(dev); else - chip_id = id->driver_data; + chip_id = i2c_match_id(max20730_id, client)->driver_data; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -328,16 +724,35 @@ static int max20730_probe(struct i2c_client *client, data->id = chip_id; mutex_init(&data->lock); memcpy(&data->info, &max20730_info[chip_id], sizeof(data->info)); + if (of_property_read_u32_array(client->dev.of_node, "vout-voltage-divider", + data->vout_voltage_divider, + ARRAY_SIZE(data->vout_voltage_divider)) != 0) + memset(data->vout_voltage_divider, 0, sizeof(data->vout_voltage_divider)); + if (data->vout_voltage_divider[1] < data->vout_voltage_divider[0]) { + dev_err(dev, + "The total resistance of voltage divider is less than output resistance\n"); + return -EINVAL; + } ret = i2c_smbus_read_word_data(client, MAX20730_MFR_DEVSET1); if (ret < 0) return ret; data->mfr_devset1 = ret; - return pmbus_do_probe(client, id, &data->info); + ret = pmbus_do_probe(client, &data->info); + if (ret < 0) + return ret; + + ret = max20730_init_debugfs(client, data); + if (ret) + dev_warn(dev, "Failed to register debugfs: %d\n", + ret); + + return 0; } static const struct i2c_device_id max20730_id[] = { + { "max20710", max20710 }, { "max20730", max20730 }, { "max20734", max20734 }, { "max20743", max20743 }, @@ -347,6 +762,7 @@ static const struct i2c_device_id max20730_id[] = { MODULE_DEVICE_TABLE(i2c, max20730_id); static const struct of_device_id max20730_of_match[] = { + { .compatible = "maxim,max20710", .data = (void *)max20710 }, { .compatible = "maxim,max20730", .data = (void *)max20730 }, { .compatible = "maxim,max20734", .data = (void *)max20734 }, { .compatible = "maxim,max20743", .data = (void *)max20743 }, @@ -360,13 +776,13 @@ static struct i2c_driver max20730_driver = { .name = "max20730", .of_match_table = max20730_of_match, }, - .probe = max20730_probe, - .remove = pmbus_do_remove, + .probe_new = max20730_probe, .id_table = max20730_id, }; module_i2c_driver(max20730_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("PMBus driver for Maxim MAX20730 / MAX20734 / MAX20743"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX20710 / MAX20730 / MAX20734 / MAX20743"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index da3c38cb9a5c..2272dc8c2e38 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -26,10 +26,9 @@ static struct pmbus_driver_info max20751_info = { PMBUS_HAVE_POUT, }; -static int max20751_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max20751_probe(struct i2c_client *client) { - return pmbus_do_probe(client, id, &max20751_info); + return pmbus_do_probe(client, &max20751_info); } static const struct i2c_device_id max20751_id[] = { @@ -43,8 +42,7 @@ static struct i2c_driver max20751_driver = { .driver = { .name = "max20751", }, - .probe = max20751_probe, - .remove = pmbus_do_remove, + .probe_new = max20751_probe, .id_table = max20751_id, }; @@ -53,3 +51,4 @@ module_i2c_driver(max20751_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 254b0f98c755..95d79a64b483 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -17,6 +17,7 @@ enum max31785_regs { #define MAX31785 0x3030 #define MAX31785A 0x3040 +#define MAX31785B 0x3061 #define MFR_FAN_CONFIG_DUAL_TACH BIT(12) @@ -72,7 +73,7 @@ static int max31785_read_long_data(struct i2c_client *client, int page, cmdbuf[0] = reg; - rc = pmbus_set_page(client, page); + rc = pmbus_set_page(client, page, 0xff); if (rc < 0) return rc; @@ -110,7 +111,7 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page) if (config < 0) return config; - command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1); + command = pmbus_read_word_data(client, page, 0xff, PMBUS_FAN_COMMAND_1); if (command < 0) return command; @@ -126,7 +127,7 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page) } static int max31785_read_word_data(struct i2c_client *client, int page, - int reg) + int phase, int reg) { u32 val; int rv; @@ -324,13 +325,12 @@ static int max31785_configure_dual_tach(struct i2c_client *client, return 0; } -static int max31785_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max31785_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct pmbus_driver_info *info; bool dual_tach = false; - s64 ret; + int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | @@ -351,12 +351,14 @@ static int max31785_probe(struct i2c_client *client, if (ret < 0) return ret; - if (ret == MAX31785A) { + if (ret == MAX31785A || ret == MAX31785B) { dual_tach = true; } else if (ret == MAX31785) { - if (!strcmp("max31785a", id->name)) - dev_warn(dev, "Expected max3175a, found max31785: cannot provide secondary tachometer readings\n"); + if (!strcmp("max31785a", client->name) || + !strcmp("max31785b", client->name)) + dev_warn(dev, "Expected max31785a/b, found max31785: cannot provide secondary tachometer readings\n"); } else { + dev_err(dev, "Unrecognized MAX31785 revision: %x\n", ret); return -ENODEV; } @@ -366,12 +368,13 @@ static int max31785_probe(struct i2c_client *client, return ret; } - return pmbus_do_probe(client, id, info); + return pmbus_do_probe(client, info); } static const struct i2c_device_id max31785_id[] = { { "max31785", 0 }, { "max31785a", 0 }, + { "max31785b", 0 }, { }, }; @@ -380,6 +383,7 @@ MODULE_DEVICE_TABLE(i2c, max31785_id); static const struct of_device_id max31785_of_match[] = { { .compatible = "maxim,max31785" }, { .compatible = "maxim,max31785a" }, + { .compatible = "maxim,max31785b" }, { }, }; @@ -390,8 +394,7 @@ static struct i2c_driver max31785_driver = { .name = "max31785", .of_match_table = max31785_of_match, }, - .probe = max31785_probe, - .remove = pmbus_do_remove, + .probe_new = max31785_probe, .id_table = max31785_id, }; @@ -400,3 +403,4 @@ module_i2c_driver(max31785_driver); MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 5c63a6600729..ea7609058a12 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -31,6 +31,13 @@ enum chips { max34440, max34441, max34446, max34451, max34460, max34461 }; #define MAX34440_STATUS_OT_FAULT BIT(5) #define MAX34440_STATUS_OT_WARN BIT(6) +/* + * The whole max344* family have IOUT_OC_WARN_LIMIT and IOUT_OC_FAULT_LIMIT + * swapped from the standard pmbus spec addresses. + */ +#define MAX34440_IOUT_OC_WARN_LIMIT 0x46 +#define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A + #define MAX34451_MFR_CHANNEL_CONFIG 0xe4 #define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f @@ -41,52 +48,63 @@ struct max34440_data { #define to_max34440_data(x) container_of(x, struct max34440_data, info) -static int max34440_read_word_data(struct i2c_client *client, int page, int reg) +static const struct i2c_device_id max34440_id[]; + +static int max34440_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct max34440_data *data = to_max34440_data(info); switch (reg) { + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, + MAX34440_IOUT_OC_FAULT_LIMIT); + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, + MAX34440_IOUT_OC_WARN_LIMIT); + break; case PMBUS_VIRT_READ_VOUT_MIN: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_VOUT_MIN); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_AVG: if (data->id != max34446 && data->id != max34451) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_POUT_AVG: if (data->id != max34446) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_POUT_AVG); break; case PMBUS_VIRT_READ_POUT_MAX: if (data->id != max34446) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_POUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_AVG: if (data->id != max34446 && data->id != max34460 && data->id != max34461) return -ENXIO; - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_TEMPERATURE_AVG); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, + ret = pmbus_read_word_data(client, page, phase, MAX34440_MFR_TEMPERATURE_PEAK); break; case PMBUS_VIRT_RESET_POUT_HISTORY: @@ -114,6 +132,14 @@ static int max34440_write_word_data(struct i2c_client *client, int page, int ret; switch (reg) { + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_FAULT_LIMIT, + word); + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_WARN_LIMIT, + word); + break; case PMBUS_VIRT_RESET_POUT_HISTORY: ret = pmbus_write_word_data(client, page, MAX34446_MFR_POUT_PEAK, 0); @@ -159,14 +185,14 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) int mfg_status; if (page >= 0) { - ret = pmbus_set_page(client, page); + ret = pmbus_set_page(client, page, 0xff); if (ret < 0) return ret; } switch (reg) { case PMBUS_STATUS_IOUT: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_MFR_SPECIFIC); if (mfg_status < 0) return mfg_status; @@ -176,7 +202,7 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) ret |= PB_IOUT_OC_FAULT; break; case PMBUS_STATUS_TEMPERATURE: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_MFR_SPECIFIC); if (mfg_status < 0) return mfg_status; @@ -387,7 +413,6 @@ static struct pmbus_driver_info max34440_info[] = { .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, - .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, @@ -418,7 +443,6 @@ static struct pmbus_driver_info max34440_info[] = { .func[15] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, - .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, @@ -454,14 +478,12 @@ static struct pmbus_driver_info max34440_info[] = { .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, - .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, }; -static int max34440_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max34440_probe(struct i2c_client *client) { struct max34440_data *data; int rv; @@ -470,8 +492,8 @@ static int max34440_probe(struct i2c_client *client, GFP_KERNEL); if (!data) return -ENOMEM; - data->id = id->driver_data; - data->info = max34440_info[id->driver_data]; + data->id = i2c_match_id(max34440_id, client)->driver_data; + data->info = max34440_info[data->id]; if (data->id == max34451) { rv = max34451_set_supported_funcs(client, data); @@ -479,7 +501,7 @@ static int max34440_probe(struct i2c_client *client, return rv; } - return pmbus_do_probe(client, id, &data->info); + return pmbus_do_probe(client, &data->info); } static const struct i2c_device_id max34440_id[] = { @@ -498,8 +520,7 @@ static struct i2c_driver max34440_driver = { .driver = { .name = "max34440", }, - .probe = max34440_probe, - .remove = pmbus_do_remove, + .probe_new = max34440_probe, .id_table = max34440_id, }; @@ -508,3 +529,4 @@ module_i2c_driver(max34440_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index bc5f4cb6450e..5e66c28c0b71 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -28,7 +28,8 @@ #define MAX8688_STATUS_OT_FAULT BIT(13) #define MAX8688_STATUS_OT_WARNING BIT(14) -static int max8688_read_word_data(struct i2c_client *client, int page, int reg) +static int max8688_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { int ret; @@ -37,13 +38,15 @@ static int max8688_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK); + ret = pmbus_read_word_data(client, 0, 0xff, + MAX8688_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK); + ret = pmbus_read_word_data(client, 0, 0xff, + MAX8688_MFR_IOUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, 0, + ret = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFR_TEMPERATURE_PEAK); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: @@ -94,7 +97,7 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_STATUS_VOUT: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFG_STATUS); if (mfg_status < 0) return mfg_status; @@ -108,7 +111,7 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) ret |= PB_VOLTAGE_OV_FAULT; break; case PMBUS_STATUS_IOUT: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFG_STATUS); if (mfg_status < 0) return mfg_status; @@ -120,7 +123,7 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) ret |= PB_IOUT_OC_FAULT; break; case PMBUS_STATUS_TEMPERATURE: - mfg_status = pmbus_read_word_data(client, 0, + mfg_status = pmbus_read_word_data(client, 0, 0xff, MAX8688_MFG_STATUS); if (mfg_status < 0) return mfg_status; @@ -162,10 +165,9 @@ static struct pmbus_driver_info max8688_info = { .write_word_data = max8688_write_word_data, }; -static int max8688_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max8688_probe(struct i2c_client *client) { - return pmbus_do_probe(client, id, &max8688_info); + return pmbus_do_probe(client, &max8688_info); } static const struct i2c_device_id max8688_id[] = { @@ -180,8 +182,7 @@ static struct i2c_driver max8688_driver = { .driver = { .name = "max8688", }, - .probe = max8688_probe, - .remove = pmbus_do_remove, + .probe_new = max8688_probe, .id_table = max8688_id, }; @@ -190,3 +191,4 @@ module_i2c_driver(max8688_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c new file mode 100644 index 000000000000..24e5194706cf --- /dev/null +++ b/drivers/hwmon/pmbus/mp2888.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers + * + * Copyright (C) 2020 Nvidia Technologies Ltd. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +/* Vendor specific registers. */ +#define MP2888_MFR_SYS_CONFIG 0x44 +#define MP2888_MFR_READ_CS1_2 0x73 +#define MP2888_MFR_READ_CS3_4 0x74 +#define MP2888_MFR_READ_CS5_6 0x75 +#define MP2888_MFR_READ_CS7_8 0x76 +#define MP2888_MFR_READ_CS9_10 0x77 +#define MP2888_MFR_VR_CONFIG1 0xe1 + +#define MP2888_TOTAL_CURRENT_RESOLUTION BIT(3) +#define MP2888_PHASE_CURRENT_RESOLUTION BIT(4) +#define MP2888_DRMOS_KCS GENMASK(2, 0) +#define MP2888_TEMP_UNIT 10 +#define MP2888_MAX_PHASE 10 + +struct mp2888_data { + struct pmbus_driver_info info; + int total_curr_resolution; + int phase_curr_resolution; + int curr_sense_gain; +}; + +#define to_mp2888_data(x) container_of(x, struct mp2888_data, info) + +static int mp2888_read_byte_data(struct i2c_client *client, int page, int reg) +{ + switch (reg) { + case PMBUS_VOUT_MODE: + /* Enforce VOUT direct format. */ + return PB_VOUT_MODE_DIRECT; + default: + return -ENODATA; + } +} + +static int +mp2888_current_sense_gain_and_resolution_get(struct i2c_client *client, struct mp2888_data *data) +{ + int ret; + + /* + * Obtain DrMOS current sense gain of power stage from the register + * , bits 0-2. The value is selected as below: + * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other + * values are reserved. + */ + ret = i2c_smbus_read_word_data(client, MP2888_MFR_SYS_CONFIG); + if (ret < 0) + return ret; + + switch (ret & MP2888_DRMOS_KCS) { + case 0: + data->curr_sense_gain = 85; + break; + case 1: + data->curr_sense_gain = 97; + break; + case 2: + data->curr_sense_gain = 100; + break; + case 3: + data->curr_sense_gain = 50; + break; + default: + return -EINVAL; + } + + /* + * Obtain resolution selector for total and phase current report and protection. + * 0: original resolution; 1: half resolution (in such case phase current value should + * be doubled. + */ + data->total_curr_resolution = (ret & MP2888_TOTAL_CURRENT_RESOLUTION) >> 3; + data->phase_curr_resolution = (ret & MP2888_PHASE_CURRENT_RESOLUTION) >> 4; + + return 0; +} + +static int +mp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page, int phase, u8 reg) +{ + int ret; + + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + if (!((phase + 1) % 2)) + ret >>= 8; + ret &= 0xff; + + /* + * Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs) + * where: + * - Kcs is the DrMOS current sense gain of power stage, which is obtained from the + * register MP2888_MFR_VR_CONFIG1, bits 13-12 with the following selection of DrMOS + * (data->curr_sense_gain): + * 00b - 8.5µA/A, 01b - 9.7µA/A, 1b - 10µA/A, 11b - 5µA/A. + * - Rcs is the internal phase current sense resistor. This parameter depends on hardware + * assembly. By default it is set to 1kΩ. In case of different assembly, user should + * scale this parameter by dividing it by Rcs. + * If phase current resolution bit is set to 1, READ_CSx value should be doubled. + * Note, that current phase sensing, providing by the device is not accurate. This is + * because sampling of current occurrence of bit weight has a big deviation, especially for + * light load. + */ + ret = DIV_ROUND_CLOSEST(ret * 200 - 19600, data->curr_sense_gain); + /* Scale according to total current resolution. */ + ret = (data->total_curr_resolution) ? ret * 2 : ret; + return ret; +} + +static int +mp2888_read_phases(struct i2c_client *client, struct mp2888_data *data, int page, int phase) +{ + int ret; + + switch (phase) { + case 0 ... 1: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS1_2); + break; + case 2 ... 3: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS3_4); + break; + case 4 ... 5: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS5_6); + break; + case 6 ... 7: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS7_8); + break; + case 8 ... 9: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS9_10); + break; + default: + return -ENODATA; + } + return ret; +} + +static int mp2888_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2888_data *data = to_mp2888_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret <= 0) + return ret; + + /* + * READ_VIN requires fixup to scale it to linear11 format. Register data format + * provides 10 bits for mantissa and 6 bits for exponent. Bits 15:10 are set with + * the fixed value 111011b. + */ + ret = (ret & GENMASK(9, 0)) | ((ret & GENMASK(31, 10)) << 1); + break; + case PMBUS_OT_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + /* + * Chip reports limits in degrees C, but the actual temperature in 10th of + * degrees C - scaling is needed to match both. + */ + ret *= MP2888_TEMP_UNIT; + break; + case PMBUS_READ_IOUT: + if (phase != 0xff) + return mp2888_read_phases(client, data, page, phase); + + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + /* + * READ_IOUT register has unused bits 15:12 with fixed value 1110b. Clear these + * bits and scale with total current resolution. Data is provided in direct format. + */ + ret &= GENMASK(11, 0); + ret = data->total_curr_resolution ? ret * 2 : ret; + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + ret &= GENMASK(9, 0); + /* + * Chip reports limits with resolution 1A or 2A, if total current resolution bit is + * set 1. Actual current is reported with 0.25A or respectively 0.5A resolution. + * Scaling is needed to match both. + */ + ret = data->total_curr_resolution ? ret * 8 : ret * 4; + break; + case PMBUS_READ_POUT: + case PMBUS_READ_PIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + ret = data->total_curr_resolution ? ret : DIV_ROUND_CLOSEST(ret, 2); + break; + case PMBUS_POUT_OP_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + /* + * Chip reports limits with resolution 1W or 2W, if total current resolution bit is + * set 1. Actual power is reported with 0.5W or 1W respectively resolution. Scaling + * is needed to match both. + */ + ret = data->total_curr_resolution ? ret * 2 : ret; + break; + /* + * The below registers are not implemented by device or implemented not according to the + * spec. Skip all of them to avoid exposing non-relevant inputs to sysfs. + */ + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_IOUT_OC_LV_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_POUT_MAX: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_PIN_MAX: + case PMBUS_MFR_POUT_MAX: + case PMBUS_MFR_MAX_TEMP_1: + return -ENXIO; + default: + return -ENODATA; + } + + return ret; +} + +static int mp2888_write_word_data(struct i2c_client *client, int page, int reg, u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2888_data *data = to_mp2888_data(info); + + switch (reg) { + case PMBUS_OT_WARN_LIMIT: + word = DIV_ROUND_CLOSEST(word, MP2888_TEMP_UNIT); + /* Drop unused bits 15:8. */ + word = clamp_val(word, 0, GENMASK(7, 0)); + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + /* Fix limit according to total curent resolution. */ + word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 8) : + DIV_ROUND_CLOSEST(word, 4); + /* Drop unused bits 15:10. */ + word = clamp_val(word, 0, GENMASK(9, 0)); + break; + case PMBUS_POUT_OP_WARN_LIMIT: + /* Fix limit according to total curent resolution. */ + word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 4) : + DIV_ROUND_CLOSEST(word, 2); + /* Drop unused bits 15:10. */ + word = clamp_val(word, 0, GENMASK(9, 0)); + break; + default: + return -ENODATA; + } + return pmbus_write_word_data(client, page, reg, word); +} + +static int +mp2888_identify_multiphase(struct i2c_client *client, struct mp2888_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + /* Identify multiphase number - could be from 1 to 10. */ + ret = i2c_smbus_read_word_data(client, MP2888_MFR_VR_CONFIG1); + if (ret <= 0) + return ret; + + info->phases[0] = ret & GENMASK(3, 0); + + /* + * The device provides a total of 10 PWM pins, and can be configured to different phase + * count applications for rail. + */ + if (info->phases[0] > MP2888_MAX_PHASE) + return -EINVAL; + + return 0; +} + +static struct pmbus_driver_info mp2888_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 1, + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 4, + .m[PSC_POWER] = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_PHASE_VIRTUAL, + .pfunc[0] = PMBUS_HAVE_IOUT, + .pfunc[1] = PMBUS_HAVE_IOUT, + .pfunc[2] = PMBUS_HAVE_IOUT, + .pfunc[3] = PMBUS_HAVE_IOUT, + .pfunc[4] = PMBUS_HAVE_IOUT, + .pfunc[5] = PMBUS_HAVE_IOUT, + .pfunc[6] = PMBUS_HAVE_IOUT, + .pfunc[7] = PMBUS_HAVE_IOUT, + .pfunc[8] = PMBUS_HAVE_IOUT, + .pfunc[9] = PMBUS_HAVE_IOUT, + .read_byte_data = mp2888_read_byte_data, + .read_word_data = mp2888_read_word_data, + .write_word_data = mp2888_write_word_data, +}; + +static int mp2888_probe(struct i2c_client *client) +{ + struct pmbus_driver_info *info; + struct mp2888_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2888_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2888_info, sizeof(*info)); + info = &data->info; + + /* Identify multiphase configuration. */ + ret = mp2888_identify_multiphase(client, data, info); + if (ret) + return ret; + + /* Obtain current sense gain of power stage and current resolution. */ + ret = mp2888_current_sense_gain_and_resolution_get(client, data); + if (ret) + return ret; + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id mp2888_id[] = { + {"mp2888", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mp2888_id); + +static const struct of_device_id __maybe_unused mp2888_of_match[] = { + {.compatible = "mps,mp2888"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2888_of_match); + +static struct i2c_driver mp2888_driver = { + .driver = { + .name = "mp2888", + .of_match_table = of_match_ptr(mp2888_of_match), + }, + .probe_new = mp2888_probe, + .id_table = mp2888_id, +}; + +module_i2c_driver(mp2888_driver); + +MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); +MODULE_DESCRIPTION("PMBus driver for MPS MP2888 device"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c new file mode 100644 index 000000000000..51986adfbf47 --- /dev/null +++ b/drivers/hwmon/pmbus/mp2975.c @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers + * + * Copyright (C) 2020 Nvidia Technologies Ltd. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +/* Vendor specific registers. */ +#define MP2975_MFR_APS_HYS_R2 0x0d +#define MP2975_MFR_SLOPE_TRIM3 0x1d +#define MP2975_MFR_VR_MULTI_CONFIG_R1 0x0d +#define MP2975_MFR_VR_MULTI_CONFIG_R2 0x1d +#define MP2975_MFR_APS_DECAY_ADV 0x56 +#define MP2975_MFR_DC_LOOP_CTRL 0x59 +#define MP2975_MFR_OCP_UCP_PHASE_SET 0x65 +#define MP2975_MFR_VR_CONFIG1 0x68 +#define MP2975_MFR_READ_CS1_2 0x82 +#define MP2975_MFR_READ_CS3_4 0x83 +#define MP2975_MFR_READ_CS5_6 0x84 +#define MP2975_MFR_READ_CS7_8 0x85 +#define MP2975_MFR_READ_CS9_10 0x86 +#define MP2975_MFR_READ_CS11_12 0x87 +#define MP2975_MFR_READ_IOUT_PK 0x90 +#define MP2975_MFR_READ_POUT_PK 0x91 +#define MP2975_MFR_READ_VREF_R1 0xa1 +#define MP2975_MFR_READ_VREF_R2 0xa3 +#define MP2975_MFR_OVP_TH_SET 0xe5 +#define MP2975_MFR_UVP_SET 0xe6 + +#define MP2975_VOUT_FORMAT BIT(15) +#define MP2975_VID_STEP_SEL_R1 BIT(4) +#define MP2975_IMVP9_EN_R1 BIT(13) +#define MP2975_VID_STEP_SEL_R2 BIT(3) +#define MP2975_IMVP9_EN_R2 BIT(12) +#define MP2975_PRT_THRES_DIV_OV_EN BIT(14) +#define MP2975_DRMOS_KCS GENMASK(13, 12) +#define MP2975_PROT_DEV_OV_OFF 10 +#define MP2975_PROT_DEV_OV_ON 5 +#define MP2975_SENSE_AMPL BIT(11) +#define MP2975_SENSE_AMPL_UNIT 1 +#define MP2975_SENSE_AMPL_HALF 2 +#define MP2975_VIN_UV_LIMIT_UNIT 8 + +#define MP2975_MAX_PHASE_RAIL1 8 +#define MP2975_MAX_PHASE_RAIL2 4 +#define MP2975_PAGE_NUM 2 + +#define MP2975_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_POUT | PMBUS_PHASE_VIRTUAL) + +struct mp2975_data { + struct pmbus_driver_info info; + int vout_scale; + int vid_step[MP2975_PAGE_NUM]; + int vref[MP2975_PAGE_NUM]; + int vref_off[MP2975_PAGE_NUM]; + int vout_max[MP2975_PAGE_NUM]; + int vout_ov_fixed[MP2975_PAGE_NUM]; + int vout_format[MP2975_PAGE_NUM]; + int curr_sense_gain[MP2975_PAGE_NUM]; +}; + +#define to_mp2975_data(x) container_of(x, struct mp2975_data, info) + +static int mp2975_read_byte_data(struct i2c_client *client, int page, int reg) +{ + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * Enforce VOUT direct format, since device allows to set the + * different formats for the different rails. Conversion from + * VID to direct provided by driver internally, in case it is + * necessary. + */ + return PB_VOUT_MODE_DIRECT; + default: + return -ENODATA; + } +} + +static int +mp2975_read_word_helper(struct i2c_client *client, int page, int phase, u8 reg, + u16 mask) +{ + int ret = pmbus_read_word_data(client, page, phase, reg); + + return (ret > 0) ? ret & mask : ret; +} + +static int +mp2975_vid2direct(int vrf, int val) +{ + switch (vrf) { + case vr12: + if (val >= 0x01) + return 250 + (val - 1) * 5; + break; + case vr13: + if (val >= 0x01) + return 500 + (val - 1) * 10; + break; + case imvp9: + if (val >= 0x01) + return 200 + (val - 1) * 10; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +mp2975_read_phase(struct i2c_client *client, struct mp2975_data *data, + int page, int phase, u8 reg) +{ + int ph_curr, ret; + + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + if (!((phase + 1) % MP2975_PAGE_NUM)) + ret >>= 8; + ret &= 0xff; + + /* + * Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs) + * where: + * - Kcs is the DrMOS current sense gain of power stage, which is + * obtained from the register MP2975_MFR_VR_CONFIG1, bits 13-12 with + * the following selection of DrMOS (data->curr_sense_gain[page]): + * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. + * - Rcs is the internal phase current sense resistor which is constant + * value 1kΩ. + */ + ph_curr = ret * 100 - 9800; + + /* + * Current phase sensing, providing by the device is not accurate + * for the light load. This because sampling of current occurrence of + * bit weight has a big deviation for light load. For handling such + * case phase current is represented as the maximum between the value + * calculated above and total rail current divided by number phases. + */ + ret = pmbus_read_word_data(client, page, phase, PMBUS_READ_IOUT); + if (ret < 0) + return ret; + + return max_t(int, DIV_ROUND_CLOSEST(ret, data->info.phases[page]), + DIV_ROUND_CLOSEST(ph_curr, data->curr_sense_gain[page])); +} + +static int +mp2975_read_phases(struct i2c_client *client, struct mp2975_data *data, + int page, int phase) +{ + int ret; + + if (page) { + switch (phase) { + case 0 ... 1: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS7_8); + break; + case 2 ... 3: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS9_10); + break; + case 4 ... 5: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS11_12); + break; + default: + return -ENODATA; + } + } else { + switch (phase) { + case 0 ... 1: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS1_2); + break; + case 2 ... 3: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS3_4); + break; + case 4 ... 5: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS5_6); + break; + case 6 ... 7: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS7_8); + break; + case 8 ... 9: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS9_10); + break; + case 10 ... 11: + ret = mp2975_read_phase(client, data, page, phase, + MP2975_MFR_READ_CS11_12); + break; + default: + return -ENODATA; + } + } + return ret; +} + +static int mp2975_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2975_data *data = to_mp2975_data(info); + int ret; + + switch (reg) { + case PMBUS_OT_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(7, 0)); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(7, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, MP2975_VIN_UV_LIMIT_UNIT); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + /* + * Register provides two values for over-voltage protection + * threshold for fixed (ovp2) and tracking (ovp1) modes. The + * minimum of these two values is provided as over-voltage + * fault alarm. + */ + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_OVP_TH_SET, + GENMASK(2, 0)); + if (ret < 0) + return ret; + + ret = min_t(int, data->vout_max[page] + 50 * (ret + 1), + data->vout_ov_fixed[page]); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_UVP_SET, + GENMASK(2, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(data->vref[page] * 10 - 50 * + (ret + 1) * data->vout_scale, 10); + break; + case PMBUS_READ_VOUT: + ret = mp2975_read_word_helper(client, page, phase, reg, + GENMASK(11, 0)); + if (ret < 0) + return ret; + + /* + * READ_VOUT can be provided in VID or direct format. The + * format type is specified by bit 15 of the register + * MP2975_MFR_DC_LOOP_CTRL. The driver enforces VOUT direct + * format, since device allows to set the different formats for + * the different rails and also all VOUT limits registers are + * provided in a direct format. In case format is VID - convert + * to direct. + */ + if (data->vout_format[page] == vid) + ret = mp2975_vid2direct(info->vrm_version[page], ret); + break; + case PMBUS_VIRT_READ_POUT_MAX: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_READ_POUT_PK, + GENMASK(12, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, 4); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = mp2975_read_word_helper(client, page, phase, + MP2975_MFR_READ_IOUT_PK, + GENMASK(12, 0)); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret, 4); + break; + case PMBUS_READ_IOUT: + ret = mp2975_read_phases(client, data, page, phase); + if (ret < 0) + return ret; + + break; + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_LV_FAULT_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + return -ENXIO; + default: + return -ENODATA; + } + + return ret; +} + +static int mp2975_identify_multiphase_rail2(struct i2c_client *client) +{ + int ret; + + /* + * Identify multiphase for rail 2 - could be from 0 to 4. + * In case phase number is zero – only page zero is supported + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify multiphase for rail 2 - could be from 0 to 4. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R2); + if (ret < 0) + return ret; + + ret &= GENMASK(2, 0); + return (ret >= 4) ? 4 : ret; +} + +static void mp2975_set_phase_rail1(struct pmbus_driver_info *info) +{ + int i; + + for (i = 0 ; i < info->phases[0]; i++) + info->pfunc[i] = PMBUS_HAVE_IOUT; +} + +static void +mp2975_set_phase_rail2(struct pmbus_driver_info *info, int num_phases) +{ + int i; + + /* Set phases for rail 2 from upper to lower. */ + for (i = 1; i <= num_phases; i++) + info->pfunc[MP2975_MAX_PHASE_RAIL1 - i] = PMBUS_HAVE_IOUT; +} + +static int +mp2975_identify_multiphase(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int num_phases2, ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify multiphase for rail 1 - could be from 1 to 8. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R1); + if (ret <= 0) + return ret; + + info->phases[0] = ret & GENMASK(3, 0); + + /* + * The device provides a total of 8 PWM pins, and can be configured + * to different phase count applications for rail 1 and rail 2. + * Rail 1 can be set to 8 phases, while rail 2 can only be set to 4 + * phases at most. When rail 1’s phase count is configured as 0, rail + * 1 operates with 1-phase DCM. When rail 2 phase count is configured + * as 0, rail 2 is disabled. + */ + if (info->phases[0] > MP2975_MAX_PHASE_RAIL1) + return -EINVAL; + + mp2975_set_phase_rail1(info); + num_phases2 = min(MP2975_MAX_PHASE_RAIL1 - info->phases[0], + MP2975_MAX_PHASE_RAIL2); + if (info->phases[1] && info->phases[1] <= num_phases2) + mp2975_set_phase_rail2(info, num_phases2); + + return 0; +} + +static int +mp2975_identify_vid(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info, u32 reg, int page, + u32 imvp_bit, u32 vr_bit) +{ + int ret; + + /* Identify VID mode and step selection. */ + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + if (ret & imvp_bit) { + info->vrm_version[page] = imvp9; + data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; + } else if (ret & vr_bit) { + info->vrm_version[page] = vr12; + data->vid_step[page] = MP2975_PROT_DEV_OV_ON; + } else { + info->vrm_version[page] = vr13; + data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; + } + + return 0; +} + +static int +mp2975_identify_rails_vid(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + /* Identify VID mode for rail 1. */ + ret = mp2975_identify_vid(client, data, info, + MP2975_MFR_VR_MULTI_CONFIG_R1, 0, + MP2975_IMVP9_EN_R1, MP2975_VID_STEP_SEL_R1); + if (ret < 0) + return ret; + + /* Identify VID mode for rail 2, if connected. */ + if (info->phases[1]) + ret = mp2975_identify_vid(client, data, info, + MP2975_MFR_VR_MULTI_CONFIG_R2, 1, + MP2975_IMVP9_EN_R2, + MP2975_VID_STEP_SEL_R2); + return ret; +} + +static int +mp2975_current_sense_gain_get(struct i2c_client *client, + struct mp2975_data *data) +{ + int i, ret; + + /* + * Obtain DrMOS current sense gain of power stage from the register + * MP2975_MFR_VR_CONFIG1, bits 13-12. The value is selected as below: + * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other + * values are invalid. + */ + for (i = 0 ; i < data->info.pages; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + ret = i2c_smbus_read_word_data(client, + MP2975_MFR_VR_CONFIG1); + if (ret < 0) + return ret; + + switch ((ret & MP2975_DRMOS_KCS) >> 12) { + case 0: + data->curr_sense_gain[i] = 50; + break; + case 1: + data->curr_sense_gain[i] = 85; + break; + case 2: + data->curr_sense_gain[i] = 97; + break; + default: + data->curr_sense_gain[i] = 100; + break; + } + } + + return 0; +} + +static int +mp2975_vref_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 3); + if (ret < 0) + return ret; + + /* Get voltage reference value for rail 1. */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R1); + if (ret < 0) + return ret; + + data->vref[0] = ret * data->vid_step[0]; + + /* Get voltage reference value for rail 2, if connected. */ + if (data->info.pages == MP2975_PAGE_NUM) { + ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R2); + if (ret < 0) + return ret; + + data->vref[1] = ret * data->vid_step[1]; + } + return 0; +} + +static int +mp2975_vref_offset_get(struct i2c_client *client, struct mp2975_data *data, + int page) +{ + int ret; + + ret = i2c_smbus_read_word_data(client, MP2975_MFR_OVP_TH_SET); + if (ret < 0) + return ret; + + switch ((ret & GENMASK(5, 3)) >> 3) { + case 1: + data->vref_off[page] = 140; + break; + case 2: + data->vref_off[page] = 220; + break; + case 4: + data->vref_off[page] = 400; + break; + default: + return -EINVAL; + } + return 0; +} + +static int +mp2975_vout_max_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info, int page) +{ + int ret; + + /* Get maximum reference voltage of VID-DAC in VID format. */ + ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_MAX); + if (ret < 0) + return ret; + + data->vout_max[page] = mp2975_vid2direct(info->vrm_version[page], ret & + GENMASK(8, 0)); + return 0; +} + +static int +mp2975_identify_vout_format(struct i2c_client *client, + struct mp2975_data *data, int page) +{ + int ret; + + ret = i2c_smbus_read_word_data(client, MP2975_MFR_DC_LOOP_CTRL); + if (ret < 0) + return ret; + + if (ret & MP2975_VOUT_FORMAT) + data->vout_format[page] = vid; + else + data->vout_format[page] = direct; + return 0; +} + +static int +mp2975_vout_ov_scale_get(struct i2c_client *client, struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int thres_dev, sense_ampl, ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + /* + * Get divider for over- and under-voltage protection thresholds + * configuration from the Advanced Options of Auto Phase Shedding and + * decay register. + */ + ret = i2c_smbus_read_word_data(client, MP2975_MFR_APS_DECAY_ADV); + if (ret < 0) + return ret; + thres_dev = ret & MP2975_PRT_THRES_DIV_OV_EN ? MP2975_PROT_DEV_OV_ON : + MP2975_PROT_DEV_OV_OFF; + + /* Select the gain of remote sense amplifier. */ + ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_SCALE_LOOP); + if (ret < 0) + return ret; + sense_ampl = ret & MP2975_SENSE_AMPL ? MP2975_SENSE_AMPL_HALF : + MP2975_SENSE_AMPL_UNIT; + + data->vout_scale = sense_ampl * thres_dev; + + return 0; +} + +static int +mp2975_vout_per_rail_config_get(struct i2c_client *client, + struct mp2975_data *data, + struct pmbus_driver_info *info) +{ + int i, ret; + + for (i = 0; i < data->info.pages; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + + /* Obtain voltage reference offsets. */ + ret = mp2975_vref_offset_get(client, data, i); + if (ret < 0) + return ret; + + /* Obtain maximum voltage values. */ + ret = mp2975_vout_max_get(client, data, info, i); + if (ret < 0) + return ret; + + /* + * Get VOUT format for READ_VOUT command : VID or direct. + * Pages on same device can be configured with different + * formats. + */ + ret = mp2975_identify_vout_format(client, data, i); + if (ret < 0) + return ret; + + /* + * Set over-voltage fixed value. Thresholds are provided as + * fixed value, and tracking value. The minimum of them are + * exposed as over-voltage critical threshold. + */ + data->vout_ov_fixed[i] = data->vref[i] + + DIV_ROUND_CLOSEST(data->vref_off[i] * + data->vout_scale, + 10); + } + + return 0; +} + +static struct pmbus_driver_info mp2975_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .m[PSC_TEMPERATURE] = 1, + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 1, + .m[PSC_POWER] = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_PHASE_VIRTUAL, + .read_byte_data = mp2975_read_byte_data, + .read_word_data = mp2975_read_word_data, +}; + +static int mp2975_probe(struct i2c_client *client) +{ + struct pmbus_driver_info *info; + struct mp2975_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2975_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2975_info, sizeof(*info)); + info = &data->info; + + /* Identify multiphase configuration for rail 2. */ + ret = mp2975_identify_multiphase_rail2(client); + if (ret < 0) + return ret; + + if (ret) { + /* Two rails are connected. */ + data->info.pages = MP2975_PAGE_NUM; + data->info.phases[1] = ret; + data->info.func[1] = MP2975_RAIL2_FUNC; + } + + /* Identify multiphase configuration. */ + ret = mp2975_identify_multiphase(client, data, info); + if (ret) + return ret; + + /* Identify VID setting per rail. */ + ret = mp2975_identify_rails_vid(client, data, info); + if (ret < 0) + return ret; + + /* Obtain current sense gain of power stage. */ + ret = mp2975_current_sense_gain_get(client, data); + if (ret) + return ret; + + /* Obtain voltage reference values. */ + ret = mp2975_vref_get(client, data, info); + if (ret) + return ret; + + /* Obtain vout over-voltage scales. */ + ret = mp2975_vout_ov_scale_get(client, data, info); + if (ret < 0) + return ret; + + /* Obtain offsets, maximum and format for vout. */ + ret = mp2975_vout_per_rail_config_get(client, data, info); + if (ret) + return ret; + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id mp2975_id[] = { + {"mp2975", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mp2975_id); + +static const struct of_device_id __maybe_unused mp2975_of_match[] = { + {.compatible = "mps,mp2975"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2975_of_match); + +static struct i2c_driver mp2975_driver = { + .driver = { + .name = "mp2975", + .of_match_table = of_match_ptr(mp2975_of_match), + }, + .probe_new = mp2975_probe, + .id_table = mp2975_id, +}; + +module_i2c_driver(mp2975_driver); + +MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); +MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/mp5023.c b/drivers/hwmon/pmbus/mp5023.c new file mode 100644 index 000000000000..791a06c3c54a --- /dev/null +++ b/drivers/hwmon/pmbus/mp5023.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MPS MP5023 Hot-Swap Controller + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +static struct pmbus_driver_info mp5023_info = { + .pages = 1, + + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .format[PSC_TEMPERATURE] = direct, + + .m[PSC_VOLTAGE_IN] = 32, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 32, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_OUT] = 16, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 0, + .m[PSC_POWER] = 1, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = 0, + .m[PSC_TEMPERATURE] = 2, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 0, + + .func[0] = + PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN | + PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, +}; + +static int mp5023_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &mp5023_info); +} + +static const struct of_device_id __maybe_unused mp5023_of_match[] = { + { .compatible = "mps,mp5023", }, + {} +}; + +MODULE_DEVICE_TABLE(of, mp5023_of_match); + +static struct i2c_driver mp5023_driver = { + .driver = { + .name = "mp5023", + .of_match_table = of_match_ptr(mp5023_of_match), + }, + .probe_new = mp5023_probe, +}; + +module_i2c_driver(mp5023_driver); + +MODULE_AUTHOR("Howard Chiu <howard.chiu@quantatw.com>"); +MODULE_DESCRIPTION("PMBus driver for MPS MP5023 HSC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c new file mode 100644 index 000000000000..273ff6e57654 --- /dev/null +++ b/drivers/hwmon/pmbus/pim4328.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for PIM4006, PIM4328 and PIM4820 + * + * Copyright (c) 2021 Flextronics International Sweden AB + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include <linux/slab.h> +#include "pmbus.h" + +enum chips { pim4006, pim4328, pim4820 }; + +struct pim4328_data { + enum chips id; + struct pmbus_driver_info info; +}; + +#define to_pim4328_data(x) container_of(x, struct pim4328_data, info) + +/* PIM4006 and PIM4328 */ +#define PIM4328_MFR_READ_VINA 0xd3 +#define PIM4328_MFR_READ_VINB 0xd4 + +/* PIM4006 */ +#define PIM4328_MFR_READ_IINA 0xd6 +#define PIM4328_MFR_READ_IINB 0xd7 +#define PIM4328_MFR_FET_CHECKSTATUS 0xd9 + +/* PIM4328 */ +#define PIM4328_MFR_STATUS_BITS 0xd5 + +/* PIM4820 */ +#define PIM4328_MFR_READ_STATUS 0xd0 + +static const struct i2c_device_id pim4328_id[] = { + {"bmr455", pim4328}, + {"pim4006", pim4006}, + {"pim4106", pim4006}, + {"pim4206", pim4006}, + {"pim4306", pim4006}, + {"pim4328", pim4328}, + {"pim4406", pim4006}, + {"pim4820", pim4820}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pim4328_id); + +static int pim4328_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + if (phase == 0xff) + return -ENODATA; + + switch (reg) { + case PMBUS_READ_VIN: + ret = pmbus_read_word_data(client, page, phase, + phase == 0 ? PIM4328_MFR_READ_VINA + : PIM4328_MFR_READ_VINB); + break; + case PMBUS_READ_IIN: + ret = pmbus_read_word_data(client, page, phase, + phase == 0 ? PIM4328_MFR_READ_IINA + : PIM4328_MFR_READ_IINB); + break; + default: + ret = -ENODATA; + } + + return ret; +} + +static int pim4328_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct pim4328_data *data = to_pim4328_data(info); + int ret, status; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_STATUS_BYTE: + ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE); + if (ret < 0) + return ret; + if (data->id == pim4006) { + status = pmbus_read_word_data(client, page, 0xff, + PIM4328_MFR_FET_CHECKSTATUS); + if (status < 0) + return status; + if (status & 0x0630) /* Input UV */ + ret |= PB_STATUS_VIN_UV; + } else if (data->id == pim4328) { + status = pmbus_read_byte_data(client, page, + PIM4328_MFR_STATUS_BITS); + if (status < 0) + return status; + if (status & 0x04) /* Input UV */ + ret |= PB_STATUS_VIN_UV; + if (status & 0x40) /* Output UV */ + ret |= PB_STATUS_NONE_ABOVE; + } else if (data->id == pim4820) { + status = pmbus_read_byte_data(client, page, + PIM4328_MFR_READ_STATUS); + if (status < 0) + return status; + if (status & 0x05) /* Input OV or OC */ + ret |= PB_STATUS_NONE_ABOVE; + if (status & 0x1a) /* Input UV */ + ret |= PB_STATUS_VIN_UV; + if (status & 0x40) /* OT */ + ret |= PB_STATUS_TEMPERATURE; + } + break; + default: + ret = -ENODATA; + } + + return ret; +} + +static int pim4328_probe(struct i2c_client *client) +{ + int status; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + struct pim4328_data *data; + struct pmbus_driver_info *info; + struct pmbus_platform_data *pdata; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct pim4328_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id); + if (status < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return status; + } + for (mid = pim4328_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + if (strcmp(client->name, mid->name)) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + client->name, mid->name); + + data->id = mid->driver_data; + info = &data->info; + info->pages = 1; + info->read_byte_data = pim4328_read_byte_data; + info->read_word_data = pim4328_read_word_data; + + pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + dev->platform_data = pdata; + pdata->flags = PMBUS_NO_CAPABILITY | PMBUS_NO_WRITE_PROTECT; + + switch (data->id) { + case pim4006: + info->phases[0] = 2; + info->func[0] = PMBUS_PHASE_VIRTUAL | PMBUS_HAVE_VIN + | PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT; + info->pfunc[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN; + info->pfunc[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN; + break; + case pim4328: + info->phases[0] = 2; + info->func[0] = PMBUS_PHASE_VIRTUAL + | PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN + | PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT; + info->pfunc[0] = PMBUS_HAVE_VIN; + info->pfunc[1] = PMBUS_HAVE_VIN; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_TEMPERATURE] = direct; + info->format[PSC_CURRENT_OUT] = direct; + pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD; + break; + case pim4820: + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_TEMP + | PMBUS_HAVE_IIN; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_TEMPERATURE] = direct; + info->format[PSC_CURRENT_IN] = direct; + pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD; + break; + default: + return -ENODEV; + } + + return pmbus_do_probe(client, info); +} + +static struct i2c_driver pim4328_driver = { + .driver = { + .name = "pim4328", + }, + .probe_new = pim4328_probe, + .id_table = pim4328_id, +}; + +module_i2c_driver(pim4328_driver); + +MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); +MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c new file mode 100644 index 000000000000..05b4ee35ba27 --- /dev/null +++ b/drivers/hwmon/pmbus/pli1209bc.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for Vicor PLI1209BC Digital Supervisor + * + * Copyright (c) 2022 9elements GmbH + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include <linux/regulator/driver.h> +#include "pmbus.h" + +/* + * The capability command is only supported at page 0. Probing the device while + * the page register is set to 1 will falsely enable PEC support. Disable + * capability probing accordingly, since the PLI1209BC does not have any + * additional capabilities. + */ +static struct pmbus_platform_data pli1209bc_plat_data = { + .flags = PMBUS_NO_CAPABILITY, +}; + +static int pli1209bc_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int data; + + switch (reg) { + /* PMBUS_READ_POUT uses a direct format with R=0 */ + case PMBUS_READ_POUT: + data = pmbus_read_word_data(client, page, phase, reg); + if (data < 0) + return data; + data = sign_extend32(data, 15) * 10; + return clamp_val(data, -32768, 32767) & 0xffff; + /* + * PMBUS_READ_VOUT and PMBUS_READ_TEMPERATURE_1 return invalid data + * when the BCM is turned off. Since it is not possible to return + * ENODATA error, return zero instead. + */ + case PMBUS_READ_VOUT: + case PMBUS_READ_TEMPERATURE_1: + data = pmbus_read_word_data(client, page, phase, + PMBUS_STATUS_WORD); + if (data < 0) + return data; + if (data & PB_STATUS_POWER_GOOD_N) + return 0; + return pmbus_read_word_data(client, page, phase, reg); + default: + return -ENODATA; + } +} + +#if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) +static const struct regulator_desc pli1209bc_reg_desc = { + .name = "vout2", + .id = 1, + .of_match = of_match_ptr("vout2"), + .regulators_node = of_match_ptr("regulators"), + .ops = &pmbus_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; +#endif + +static struct pmbus_driver_info pli1209bc_info = { + .pages = 2, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 1, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 1, + .m[PSC_CURRENT_IN] = 1, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = 3, + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 2, + .m[PSC_POWER] = 1, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = 1, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 0, + /* + * Page 0 sums up all attributes except voltage readings. + * The pli1209 digital supervisor only contains a single BCM, making + * page 0 redundant. + */ + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT, + .read_word_data = pli1209bc_read_word_data, +#if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) + .num_regulators = 1, + .reg_desc = &pli1209bc_reg_desc, +#endif +}; + +static int pli1209bc_probe(struct i2c_client *client) +{ + client->dev.platform_data = &pli1209bc_plat_data; + return pmbus_do_probe(client, &pli1209bc_info); +} + +static const struct i2c_device_id pli1209bc_id[] = { + {"pli1209bc", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pli1209bc_id); + +#ifdef CONFIG_OF +static const struct of_device_id pli1209bc_of_match[] = { + { .compatible = "vicor,pli1209bc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, pli1209bc_of_match); +#endif + +static struct i2c_driver pli1209bc_driver = { + .driver = { + .name = "pli1209bc", + .of_match_table = of_match_ptr(pli1209bc_of_match), + }, + .probe_new = pli1209bc_probe, + .id_table = pli1209bc_id, +}; + +module_i2c_driver(pli1209bc_driver); + +MODULE_AUTHOR("Marcello Sylvester Bauer <sylv@sylv.io>"); +MODULE_DESCRIPTION("PMBus driver for Vicor PLI1209BC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c new file mode 100644 index 000000000000..e0bbc8a30d21 --- /dev/null +++ b/drivers/hwmon/pmbus/pm6764tr.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for STMicroelectronics digital controller PM6764TR + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +#define PM6764TR_PMBUS_READ_VOUT 0xD4 + +static int pm6764tr_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, page, phase, PM6764TR_PMBUS_READ_VOUT); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static struct pmbus_driver_info pm6764tr_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = vid, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | PMBUS_HAVE_VMON | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = pm6764tr_read_word_data, +}; + +static int pm6764tr_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &pm6764tr_info); +} + +static const struct i2c_device_id pm6764tr_id[] = { + {"pm6764tr", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pm6764tr_id); + +static const struct of_device_id __maybe_unused pm6764tr_of_match[] = { + {.compatible = "st,pm6764tr"}, + {} +}; + +/* This is the driver that will be inserted */ +static struct i2c_driver pm6764tr_driver = { + .driver = { + .name = "pm6764tr", + .of_match_table = of_match_ptr(pm6764tr_of_match), + }, + .probe_new = pm6764tr_probe, + .id_table = pm6764tr_id, +}; + +module_i2c_driver(pm6764tr_driver); + +MODULE_AUTHOR("Charles Hsu"); +MODULE_DESCRIPTION("PMBus driver for ST PM6764TR"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 51e8312b6c2d..d0d386990af5 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -20,6 +20,8 @@ struct pmbus_device_info { u32 flags; }; +static const struct i2c_device_id pmbus_id[]; + /* * Find sensor groups and status registers on each page. */ @@ -102,10 +104,10 @@ static int pmbus_identify(struct i2c_client *client, int page; for (page = 1; page < PMBUS_PAGES; page++) { - if (pmbus_set_page(client, page) < 0) + if (pmbus_set_page(client, page, 0xff) < 0) break; } - pmbus_set_page(client, 0); + pmbus_set_page(client, 0, 0xff); info->pages = page; } else { info->pages = 1; @@ -159,8 +161,7 @@ abort: return ret; } -static int pmbus_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pmbus_probe(struct i2c_client *client) { struct pmbus_driver_info *info; struct pmbus_platform_data *pdata = NULL; @@ -171,43 +172,58 @@ static int pmbus_probe(struct i2c_client *client, if (!info) return -ENOMEM; - device_info = (struct pmbus_device_info *)id->driver_data; - if (device_info->flags & PMBUS_SKIP_STATUS_CHECK) { + device_info = (struct pmbus_device_info *)i2c_match_id(pmbus_id, client)->driver_data; + if (device_info->flags) { pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->flags = PMBUS_SKIP_STATUS_CHECK; + pdata->flags = device_info->flags; } info->pages = device_info->pages; info->identify = pmbus_identify; dev->platform_data = pdata; - return pmbus_do_probe(client, id, info); + return pmbus_do_probe(client, info); } static const struct pmbus_device_info pmbus_info_one = { .pages = 1, .flags = 0 }; + static const struct pmbus_device_info pmbus_info_zero = { .pages = 0, .flags = 0 }; + static const struct pmbus_device_info pmbus_info_one_skip = { .pages = 1, .flags = PMBUS_SKIP_STATUS_CHECK }; +static const struct pmbus_device_info pmbus_info_one_status = { + .pages = 1, + .flags = PMBUS_READ_STATUS_AFTER_FAILED_CHECK +}; + /* * Use driver_data to set the number of pages supported by the chip. */ static const struct i2c_device_id pmbus_id[] = { {"adp4000", (kernel_ulong_t)&pmbus_info_one}, + {"bmr310", (kernel_ulong_t)&pmbus_info_one_status}, {"bmr453", (kernel_ulong_t)&pmbus_info_one}, {"bmr454", (kernel_ulong_t)&pmbus_info_one}, + {"bmr456", (kernel_ulong_t)&pmbus_info_one}, + {"bmr457", (kernel_ulong_t)&pmbus_info_one}, + {"bmr458", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr480", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr490", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr491", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr492", (kernel_ulong_t)&pmbus_info_one}, {"dps460", (kernel_ulong_t)&pmbus_info_one_skip}, {"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip}, {"dps800", (kernel_ulong_t)&pmbus_info_one_skip}, @@ -236,8 +252,7 @@ static struct i2c_driver pmbus_driver = { .driver = { .name = "pmbus", }, - .probe = pmbus_probe, - .remove = pmbus_do_remove, + .probe_new = pmbus_probe, .id_table = pmbus_id, }; @@ -246,3 +261,4 @@ module_i2c_driver(pmbus_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("Generic PMBus driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 13b34bd67f23..10fb17879f8e 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -119,6 +119,22 @@ enum pmbus_regs { PMBUS_MFR_DATE = 0x9D, PMBUS_MFR_SERIAL = 0x9E, + PMBUS_MFR_VIN_MIN = 0xA0, + PMBUS_MFR_VIN_MAX = 0xA1, + PMBUS_MFR_IIN_MAX = 0xA2, + PMBUS_MFR_PIN_MAX = 0xA3, + PMBUS_MFR_VOUT_MIN = 0xA4, + PMBUS_MFR_VOUT_MAX = 0xA5, + PMBUS_MFR_IOUT_MAX = 0xA6, + PMBUS_MFR_POUT_MAX = 0xA7, + + PMBUS_IC_DEVICE_ID = 0xAD, + PMBUS_IC_DEVICE_REV = 0xAE, + + PMBUS_MFR_MAX_TEMP_1 = 0xC0, + PMBUS_MFR_MAX_TEMP_2 = 0xC1, + PMBUS_MFR_MAX_TEMP_3 = 0xC2, + /* * Virtual registers. * Useful to support attributes which are not supported by standard PMBus @@ -303,6 +319,7 @@ enum pmbus_fan_mode { percent = 0, rpm }; /* * STATUS_VOUT, STATUS_INPUT */ +#define PB_VOLTAGE_VIN_OFF BIT(3) #define PB_VOLTAGE_UV_FAULT BIT(4) #define PB_VOLTAGE_UV_WARNING BIT(5) #define PB_VOLTAGE_OV_WARNING BIT(6) @@ -359,6 +376,7 @@ enum pmbus_sensor_classes { }; #define PMBUS_PAGES 32 /* Per PMBus specification */ +#define PMBUS_PHASES 10 /* Maximum number of phases per page */ /* Functionality bit mask */ #define PMBUS_HAVE_VIN BIT(0) @@ -385,13 +403,15 @@ enum pmbus_sensor_classes { #define PMBUS_HAVE_PWM34 BIT(21) #define PMBUS_HAVE_SAMPLES BIT(22) -#define PMBUS_PAGE_VIRTUAL BIT(31) +#define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */ +#define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */ -enum pmbus_data_format { linear = 0, direct, vid }; +enum pmbus_data_format { linear = 0, ieee754, direct, vid }; enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; struct pmbus_driver_info { int pages; /* Total number of pages */ + u8 phases[PMBUS_PAGES]; /* Number of phases per page */ enum pmbus_data_format format[PSC_NUM_CLASSES]; enum vrm_version vrm_version[PMBUS_PAGES]; /* vrm version per page */ /* @@ -403,6 +423,7 @@ struct pmbus_driver_info { int R[PSC_NUM_CLASSES]; /* exponent */ u32 func[PMBUS_PAGES]; /* Functionality, per page */ + u32 pfunc[PMBUS_PHASES];/* Functionality, per phase */ /* * The following functions map manufacturing specific register values * to PMBus standard register values. Specify only if mapping is @@ -415,7 +436,10 @@ struct pmbus_driver_info { * the standard register. */ int (*read_byte_data)(struct i2c_client *client, int page, int reg); - int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int phase, + int reg); + int (*write_byte_data)(struct i2c_client *client, int page, int reg, + u8 byte); int (*write_word_data)(struct i2c_client *client, int page, int reg, u16 word); int (*write_byte)(struct i2c_client *client, int page, u8 value); @@ -439,8 +463,8 @@ struct pmbus_driver_info { extern const struct regulator_ops pmbus_regulator_ops; -/* Macro for filling in array of struct regulator_desc */ -#define PMBUS_REGULATOR(_name, _id) \ +/* Macros for filling in array of struct regulator_desc */ +#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step) \ [_id] = { \ .name = (_name # _id), \ .id = (_id), \ @@ -449,14 +473,21 @@ extern const struct regulator_ops pmbus_regulator_ops; .ops = &pmbus_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .n_voltages = _voltages, \ + .uV_step = _step, \ } +#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0) + /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); -int pmbus_set_page(struct i2c_client *client, int page); -int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg); -int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, u16 word); +void pmbus_set_update(struct i2c_client *client, u8 reg, bool update); +int pmbus_set_page(struct i2c_client *client, int page, int phase); +int pmbus_read_word_data(struct i2c_client *client, int page, int phase, + u8 reg); +int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, + u16 word); int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); int pmbus_write_byte(struct i2c_client *client, int page, u8 value); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, @@ -466,9 +497,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, void pmbus_clear_faults(struct i2c_client *client); bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); -int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, - struct pmbus_driver_info *info); -int pmbus_do_remove(struct i2c_client *client); +int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info); const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client); int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index d9c17feb7b4a..7ec04934747e 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -16,10 +16,11 @@ #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -#include <linux/jiffies.h> #include <linux/pmbus.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/of.h> +#include <linux/thermal.h> #include "pmbus.h" /* @@ -27,21 +28,6 @@ * with each call to krealloc */ #define PMBUS_ATTR_ALLOC_SIZE 32 - -/* - * Index into status register array, per status register group - */ -#define PB_STATUS_BASE 0 -#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) -#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) -#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) -#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) -#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) -#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) -#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) - -#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) - #define PMBUS_NAME_SIZE 24 struct pmbus_sensor { @@ -49,6 +35,7 @@ struct pmbus_sensor { char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ struct device_attribute attribute; u8 page; /* page number */ + u8 phase; /* phase number, 0xff for all phases */ u16 reg; /* register */ enum pmbus_sensor_classes class; /* sensor class */ bool update; /* runtime sensor update needed */ @@ -76,6 +63,21 @@ struct pmbus_label { #define to_pmbus_label(_attr) \ container_of(_attr, struct pmbus_label, attribute) +/* Macros for converting between sensor index and register/page/status mask */ + +#define PB_STATUS_MASK 0xffff +#define PB_REG_SHIFT 16 +#define PB_REG_MASK 0x3ff +#define PB_PAGE_SHIFT 26 +#define PB_PAGE_MASK 0x3f + +#define pb_reg_to_index(page, reg, mask) (((page) << PB_PAGE_SHIFT) | \ + ((reg) << PB_REG_SHIFT) | (mask)) + +#define pb_index_to_page(index) (((index) >> PB_PAGE_SHIFT) & PB_PAGE_MASK) +#define pb_index_to_reg(index) (((index) >> PB_REG_SHIFT) & PB_REG_MASK) +#define pb_index_to_mask(index) ((index) & PB_STATUS_MASK) + struct pmbus_data { struct device *dev; struct device *hwmon_dev; @@ -96,19 +98,15 @@ struct pmbus_data { struct pmbus_sensor *sensors; struct mutex update_lock; - bool valid; - unsigned long last_updated; /* in jiffies */ - - /* - * A single status register covers multiple attributes, - * so we keep them all together. - */ - u16 status[PB_NUM_STATUS_REG]; bool has_status_word; /* device uses STATUS_WORD register */ int (*read_status)(struct i2c_client *client, int page); - u8 currpage; + s16 currpage; /* current page, -1 for unknown/unset */ + s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */ + + int vout_low[PMBUS_PAGES]; /* voltage low margin */ + int vout_high[PMBUS_PAGES]; /* voltage high margin */ }; struct pmbus_debugfs_entry { @@ -141,20 +139,34 @@ static const int pmbus_fan_command_registers[] = { void pmbus_clear_cache(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor; + + for (sensor = data->sensors; sensor; sensor = sensor->next) + sensor->data = -ENODATA; +} +EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS); + +void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor; - data->valid = false; + for (sensor = data->sensors; sensor; sensor = sensor->next) + if (sensor->reg == reg) + sensor->update = update; } -EXPORT_SYMBOL_GPL(pmbus_clear_cache); +EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); -int pmbus_set_page(struct i2c_client *client, int page) +int pmbus_set_page(struct i2c_client *client, int page, int phase) { struct pmbus_data *data = i2c_get_clientdata(client); int rv; - if (page < 0 || page == data->currpage) + if (page < 0) return 0; - if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL)) { + if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && + data->info->pages > 1 && page != data->currpage) { rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); if (rv < 0) return rv; @@ -166,24 +178,32 @@ int pmbus_set_page(struct i2c_client *client, int page) if (rv != page) return -EIO; } - data->currpage = page; + if (data->info->phases[page] && data->currphase != phase && + !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, + phase); + if (rv) + return rv; + } + data->currphase = phase; + return 0; } -EXPORT_SYMBOL_GPL(pmbus_set_page); +EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS); int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_write_byte(client, value); } -EXPORT_SYMBOL_GPL(pmbus_write_byte); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); /* * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if @@ -208,13 +228,13 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_write_word_data(client, reg, word); } -EXPORT_SYMBOL_GPL(pmbus_write_word_data); +EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, @@ -261,6 +281,42 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, return pmbus_write_word_data(client, page, reg, word); } +/* + * _pmbus_write_byte_data() is similar to pmbus_write_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_byte_data(struct i2c_client *client, int page, int reg, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte_data) { + status = info->write_byte_data(client, page, reg, value); + if (status != -ENODATA) + return status; + } + return pmbus_write_byte_data(client, page, reg, value); +} + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_byte_data) { + status = info->read_byte_data(client, page, reg); + if (status != -ENODATA) + return status; + } + return pmbus_read_byte_data(client, page, reg); +} + int pmbus_update_fan(struct i2c_client *client, int page, int id, u8 config, u8 mask, u16 command) { @@ -268,14 +324,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, int rv; u8 to; - from = pmbus_read_byte_data(client, page, + from = _pmbus_read_byte_data(client, page, pmbus_fan_config_registers[id]); if (from < 0) return from; to = (from & ~mask) | (config & mask); if (to != from) { - rv = pmbus_write_byte_data(client, page, + rv = _pmbus_write_byte_data(client, page, pmbus_fan_config_registers[id], to); if (rv < 0) return rv; @@ -284,19 +340,19 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, return _pmbus_write_word_data(client, page, pmbus_fan_command_registers[id], command); } -EXPORT_SYMBOL_GPL(pmbus_update_fan); +EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS); -int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg) +int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, phase); if (rv < 0) return rv; return i2c_smbus_read_word_data(client, reg); } -EXPORT_SYMBOL_GPL(pmbus_read_word_data); +EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) { @@ -320,14 +376,15 @@ static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if * a device specific mapping function exists and calls it if necessary. */ -static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) +static int _pmbus_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { struct pmbus_data *data = i2c_get_clientdata(client); const struct pmbus_driver_info *info = data->info; int status; if (info->read_word_data) { - status = info->read_word_data(client, page, reg); + status = info->read_word_data(client, page, phase, reg); if (status != -ENODATA) return status; } @@ -335,32 +392,38 @@ static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) if (reg >= PMBUS_VIRT_BASE) return pmbus_read_virt_reg(client, page, reg); - return pmbus_read_word_data(client, page, reg); + return pmbus_read_word_data(client, page, phase, reg); +} + +/* Same as above, but without phase parameter, for use in check functions */ +static int __pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + return _pmbus_read_word_data(client, page, 0xff, reg); } int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_read_byte_data(client, reg); } -EXPORT_SYMBOL_GPL(pmbus_read_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) { int rv; - rv = pmbus_set_page(client, page); + rv = pmbus_set_page(client, page, 0xff); if (rv < 0) return rv; return i2c_smbus_write_byte_data(client, reg, value); } -EXPORT_SYMBOL_GPL(pmbus_write_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, u8 mask, u8 value) @@ -368,35 +431,29 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, unsigned int tmp; int rv; - rv = pmbus_read_byte_data(client, page, reg); + rv = _pmbus_read_byte_data(client, page, reg); if (rv < 0) return rv; tmp = (rv & ~mask) | (value & mask); if (tmp != rv) - rv = pmbus_write_byte_data(client, page, reg, tmp); + rv = _pmbus_write_byte_data(client, page, reg, tmp); return rv; } -EXPORT_SYMBOL_GPL(pmbus_update_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); -/* - * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if - * a device specific mapping function exists and calls it if necessary. - */ -static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, + char *data_buf) { - struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - int status; + int rv; - if (info->read_byte_data) { - status = info->read_byte_data(client, page, reg); - if (status != -ENODATA) - return status; - } - return pmbus_read_byte_data(client, page, reg); + rv = pmbus_set_page(client, page, 0xff); + if (rv < 0) + return rv; + + return i2c_smbus_read_block_data(client, reg, data_buf); } static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, @@ -433,14 +490,14 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id, return s->data; } - config = pmbus_read_byte_data(client, page, + config = _pmbus_read_byte_data(client, page, pmbus_fan_config_registers[id]); if (config < 0) return config; have_rpm = !!(config & pmbus_fan_rpm_mask[id]); if (want_rpm == have_rpm) - return pmbus_read_word_data(client, page, + return pmbus_read_word_data(client, page, 0xff, pmbus_fan_command_registers[id]); /* Can't sensibly map between RPM and PWM, just return zero */ @@ -452,14 +509,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, { return pmbus_get_fan_rate(client, page, id, mode, false); } -EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_device); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS); int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, enum pmbus_fan_mode mode) { return pmbus_get_fan_rate(client, page, id, mode, true); } -EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_cached); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS); static void pmbus_clear_fault_page(struct i2c_client *client, int page) { @@ -474,7 +531,7 @@ void pmbus_clear_faults(struct i2c_client *client) for (i = 0; i < data->info->pages; i++) pmbus_clear_fault_page(client, i); } -EXPORT_SYMBOL_GPL(pmbus_clear_faults); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS); static int pmbus_check_status_cml(struct i2c_client *client) { @@ -501,6 +558,8 @@ static bool pmbus_check_register(struct i2c_client *client, rv = func(client, page, reg); if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) rv = pmbus_check_status_cml(client); + if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK)) + data->read_status(client, -1); pmbus_clear_fault_page(client, -1); return rv >= 0; } @@ -526,13 +585,29 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); } -EXPORT_SYMBOL_GPL(pmbus_check_byte_register); +EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) { - return pmbus_check_register(client, _pmbus_read_word_data, page, reg); + return pmbus_check_register(client, __pmbus_read_word_data, page, reg); +} +EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS); + +static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client, + int page, int reg) +{ + int rv; + struct pmbus_data *data = i2c_get_clientdata(client); + char data_buf[I2C_SMBUS_BLOCK_MAX + 2]; + + rv = pmbus_read_block_data(client, page, reg, data_buf); + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) + rv = pmbus_check_status_cml(client); + if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK)) + data->read_status(client, -1); + pmbus_clear_fault_page(client, -1); + return rv >= 0; } -EXPORT_SYMBOL_GPL(pmbus_check_word_register); const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) { @@ -540,81 +615,103 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) return data->info; } -EXPORT_SYMBOL_GPL(pmbus_get_driver_info); - -static struct _pmbus_status { - u32 func; - u16 base; - u16 reg; -} pmbus_status[] = { - { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT }, - { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT }, - { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE, - PMBUS_STATUS_TEMPERATURE }, - { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 }, - { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 }, -}; +EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS); -static struct pmbus_data *pmbus_update_device(struct device *dev) +static int pmbus_get_status(struct i2c_client *client, int page, int reg) { - struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - struct pmbus_sensor *sensor; - - mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - int i, j; - - for (i = 0; i < info->pages; i++) { - data->status[PB_STATUS_BASE + i] - = data->read_status(client, i); - for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { - struct _pmbus_status *s = &pmbus_status[j]; - - if (!(info->func[i] & s->func)) - continue; - data->status[s->base + i] - = _pmbus_read_byte_data(client, i, - s->reg); - } - } + int status; - if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) - data->status[PB_STATUS_INPUT_BASE] - = _pmbus_read_byte_data(client, 0, - PMBUS_STATUS_INPUT); - - if (info->func[0] & PMBUS_HAVE_STATUS_VMON) - data->status[PB_STATUS_VMON_BASE] - = _pmbus_read_byte_data(client, 0, - PMBUS_VIRT_STATUS_VMON); - - for (sensor = data->sensors; sensor; sensor = sensor->next) { - if (!data->valid || sensor->update) - sensor->data - = _pmbus_read_word_data(client, - sensor->page, - sensor->reg); - } + switch (reg) { + case PMBUS_STATUS_WORD: + status = data->read_status(client, page); + break; + default: + status = _pmbus_read_byte_data(client, page, reg); + break; + } + if (status < 0) pmbus_clear_faults(client); - data->last_updated = jiffies; - data->valid = 1; + return status; +} + +static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sensor *sensor) +{ + if (sensor->data < 0 || sensor->update) + sensor->data = _pmbus_read_word_data(client, sensor->page, + sensor->phase, sensor->reg); +} + +/* + * Convert ieee754 sensor values to milli- or micro-units + * depending on sensor type. + * + * ieee754 data format: + * bit 15: sign + * bit 10..14: exponent + * bit 0..9: mantissa + * exponent=0: + * v=(−1)^signbit * 2^(−14) * 0.significantbits + * exponent=1..30: + * v=(−1)^signbit * 2^(exponent - 15) * 1.significantbits + * exponent=31: + * v=NaN + * + * Add the number mantissa bits into the calculations for simplicity. + * To do that, add '10' to the exponent. By doing that, we can just add + * 0x400 to normal values and get the expected result. + */ +static long pmbus_reg2data_ieee754(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + int exponent; + bool sign; + long val; + + /* only support half precision for now */ + sign = sensor->data & 0x8000; + exponent = (sensor->data >> 10) & 0x1f; + val = sensor->data & 0x3ff; + + if (exponent == 0) { /* subnormal */ + exponent = -(14 + 10); + } else if (exponent == 0x1f) { /* NaN, convert to min/max */ + exponent = 0; + val = 65504; + } else { + exponent -= (15 + 10); /* normal */ + val |= 0x400; } - mutex_unlock(&data->update_lock); - return data; + + /* scale result to milli-units for all sensors except fans */ + if (sensor->class != PSC_FAN) + val = val * 1000L; + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + if (sign) + val = -val; + + return val; } /* * Convert linear sensor values to milli- or micro-units * depending on sensor type. */ -static long pmbus_reg2data_linear(struct pmbus_data *data, - struct pmbus_sensor *sensor) +static s64 pmbus_reg2data_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor) { s16 exponent; s32 mantissa; - long val; + s64 val; if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ exponent = data->exponent[sensor->page]; @@ -628,11 +725,11 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, /* scale result to milli-units for all sensors except fans */ if (sensor->class != PSC_FAN) - val = val * 1000L; + val = val * 1000LL; /* scale result to micro-units for power sensors */ if (sensor->class == PSC_POWER) - val = val * 1000L; + val = val * 1000LL; if (exponent >= 0) val <<= exponent; @@ -646,8 +743,8 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, * Convert direct sensor values to milli- or micro-units * depending on sensor type. */ -static long pmbus_reg2data_direct(struct pmbus_data *data, - struct pmbus_sensor *sensor) +static s64 pmbus_reg2data_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor) { s64 b, val = (s16)sensor->data; s32 m, R; @@ -683,15 +780,15 @@ static long pmbus_reg2data_direct(struct pmbus_data *data, } val = div_s64(val - b, m); - return clamp_val(val, LONG_MIN, LONG_MAX); + return val; } /* * Convert VID sensor values to milli- or micro-units * depending on sensor type. */ -static long pmbus_reg2data_vid(struct pmbus_data *data, - struct pmbus_sensor *sensor) +static s64 pmbus_reg2data_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor) { long val = sensor->data; long rv = 0; @@ -721,9 +818,9 @@ static long pmbus_reg2data_vid(struct pmbus_data *data, return rv; } -static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) +static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) { - long val; + s64 val; if (!sensor->convert) return sensor->data; @@ -735,6 +832,9 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) case vid: val = pmbus_reg2data_vid(data, sensor); break; + case ieee754: + val = pmbus_reg2data_ieee754(data, sensor); + break; case linear: default: val = pmbus_reg2data_linear(data, sensor); @@ -743,11 +843,75 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) return val; } -#define MAX_MANTISSA (1023 * 1000) -#define MIN_MANTISSA (511 * 1000) +#define MAX_IEEE_MANTISSA (0x7ff * 1000) +#define MIN_IEEE_MANTISSA (0x400 * 1000) + +static u16 pmbus_data2reg_ieee754(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + u16 exponent = (15 + 10); + long mantissa; + u16 sign = 0; + + /* simple case */ + if (val == 0) + return 0; + + if (val < 0) { + sign = 0x8000; + val = -val; + } + + /* Power is in uW. Convert to mW before converting. */ + if (sensor->class == PSC_POWER) + val = DIV_ROUND_CLOSEST(val, 1000L); + + /* + * For simplicity, convert fan data to milli-units + * before calculating the exponent. + */ + if (sensor->class == PSC_FAN) + val = val * 1000; + + /* Reduce large mantissa until it fits into 10 bit */ + while (val > MAX_IEEE_MANTISSA && exponent < 30) { + exponent++; + val >>= 1; + } + /* + * Increase small mantissa to generate valid 'normal' + * number + */ + while (val < MIN_IEEE_MANTISSA && exponent > 1) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* + * Ensure that the resulting number is within range. + * Valid range is 0x400..0x7ff, where bit 10 reflects + * the implied high bit in normalized ieee754 numbers. + * Set the range to 0x400..0x7ff to reflect this. + * The upper bit is then removed by the mask against + * 0x3ff in the final assignment. + */ + if (mantissa > 0x7ff) + mantissa = 0x7ff; + else if (mantissa < 0x400) + mantissa = 0x400; + + /* Convert to sign, 5 bit exponent, 10 bit mantissa */ + return sign | (mantissa & 0x3ff) | ((exponent << 10) & 0x7c00); +} + +#define MAX_LIN_MANTISSA (1023 * 1000) +#define MIN_LIN_MANTISSA (511 * 1000) static u16 pmbus_data2reg_linear(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { s16 exponent = 0, mantissa; bool negative = false; @@ -769,8 +933,8 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, val <<= -data->exponent[sensor->page]; else val >>= data->exponent[sensor->page]; - val = DIV_ROUND_CLOSEST(val, 1000); - return val & 0xffff; + val = DIV_ROUND_CLOSEST_ULL(val, 1000); + return clamp_val(val, 0, 0xffff); } if (val < 0) { @@ -780,32 +944,28 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, /* Power is in uW. Convert to mW before converting. */ if (sensor->class == PSC_POWER) - val = DIV_ROUND_CLOSEST(val, 1000L); + val = DIV_ROUND_CLOSEST_ULL(val, 1000); /* * For simplicity, convert fan data to milli-units * before calculating the exponent. */ if (sensor->class == PSC_FAN) - val = val * 1000; + val = val * 1000LL; /* Reduce large mantissa until it fits into 10 bit */ - while (val >= MAX_MANTISSA && exponent < 15) { + while (val >= MAX_LIN_MANTISSA && exponent < 15) { exponent++; val >>= 1; } /* Increase small mantissa to improve precision */ - while (val < MIN_MANTISSA && exponent > -15) { + while (val < MIN_LIN_MANTISSA && exponent > -15) { exponent--; val <<= 1; } /* Convert mantissa from milli-units to units */ - mantissa = DIV_ROUND_CLOSEST(val, 1000); - - /* Ensure that resulting number is within range */ - if (mantissa > 0x3ff) - mantissa = 0x3ff; + mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff); /* restore sign */ if (negative) @@ -816,9 +976,9 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, } static u16 pmbus_data2reg_direct(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { - s64 b, val64 = val; + s64 b; s32 m, R; m = data->info->m[sensor->class]; @@ -836,30 +996,30 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, R -= 3; /* Adjust R and b for data in milli-units */ b *= 1000; } - val64 = val64 * m + b; + val = val * m + b; while (R > 0) { - val64 *= 10; + val *= 10; R--; } while (R < 0) { - val64 = div_s64(val64 + 5LL, 10L); /* round closest */ + val = div_s64(val + 5LL, 10L); /* round closest */ R++; } - return (u16)clamp_val(val64, S16_MIN, S16_MAX); + return (u16)clamp_val(val, S16_MIN, S16_MAX); } static u16 pmbus_data2reg_vid(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { val = clamp_val(val, 500, 1600); - return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); + return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); } static u16 pmbus_data2reg(struct pmbus_data *data, - struct pmbus_sensor *sensor, long val) + struct pmbus_sensor *sensor, s64 val) { u16 regval; @@ -873,6 +1033,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data, case vid: regval = pmbus_data2reg_vid(data, sensor, val); break; + case ieee754: + regval = pmbus_data2reg_ieee754(data, sensor, val); + break; case linear: default: regval = pmbus_data2reg_linear(data, sensor, val); @@ -904,38 +1067,56 @@ static u16 pmbus_data2reg(struct pmbus_data *data, * If a negative value is stored in any of the referenced registers, this value * reflects an error code which will be returned. */ -static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, +static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, int index) { + struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_sensor *s1 = b->s1; struct pmbus_sensor *s2 = b->s2; - u16 reg = (index >> 16) & 0xffff; - u16 mask = index & 0xffff; + u16 mask = pb_index_to_mask(index); + u8 page = pb_index_to_page(index); + u16 reg = pb_index_to_reg(index); int ret, status; u16 regval; - status = data->status[reg]; - if (status < 0) - return status; + mutex_lock(&data->update_lock); + status = pmbus_get_status(client, page, reg); + if (status < 0) { + ret = status; + goto unlock; + } + + if (s1) + pmbus_update_sensor_data(client, s1); + if (s2) + pmbus_update_sensor_data(client, s2); regval = status & mask; - if (!s1 && !s2) { - ret = !!regval; - } else if (!s1 || !s2) { - WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); - return 0; - } else { - long v1, v2; + if (regval) { + ret = _pmbus_write_byte_data(client, page, reg, regval); + if (ret) + goto unlock; + } + if (s1 && s2) { + s64 v1, v2; - if (s1->data < 0) - return s1->data; - if (s2->data < 0) - return s2->data; + if (s1->data < 0) { + ret = s1->data; + goto unlock; + } + if (s2->data < 0) { + ret = s2->data; + goto unlock; + } v1 = pmbus_reg2data(data, s1); v2 = pmbus_reg2data(data, s2); ret = !!(regval && v1 >= v2); + } else { + ret = !!regval; } +unlock: + mutex_unlock(&data->update_lock); return ret; } @@ -944,25 +1125,31 @@ static ssize_t pmbus_show_boolean(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct pmbus_boolean *boolean = to_pmbus_boolean(attr); - struct pmbus_data *data = pmbus_update_device(dev); + struct i2c_client *client = to_i2c_client(dev->parent); int val; - val = pmbus_get_boolean(data, boolean, attr->index); + val = pmbus_get_boolean(client, boolean, attr->index); if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t pmbus_show_sensor(struct device *dev, struct device_attribute *devattr, char *buf) { - struct pmbus_data *data = pmbus_update_device(dev); + struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + struct pmbus_data *data = i2c_get_clientdata(client); + ssize_t ret; + mutex_lock(&data->update_lock); + pmbus_update_sensor_data(client, sensor); if (sensor->data < 0) - return sensor->data; - - return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor)); + ret = sensor->data; + else + ret = sysfs_emit(buf, "%lld\n", pmbus_reg2data(data, sensor)); + mutex_unlock(&data->update_lock); + return ret; } static ssize_t pmbus_set_sensor(struct device *dev, @@ -973,11 +1160,11 @@ static ssize_t pmbus_set_sensor(struct device *dev, struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); ssize_t rv = count; - long val = 0; + s64 val; int ret; u16 regval; - if (kstrtol(buf, 10, &val) < 0) + if (kstrtos64(buf, 10, &val) < 0) return -EINVAL; mutex_lock(&data->update_lock); @@ -986,7 +1173,7 @@ static ssize_t pmbus_set_sensor(struct device *dev, if (ret < 0) rv = ret; else - sensor->data = regval; + sensor->data = -ENODATA; mutex_unlock(&data->update_lock); return rv; } @@ -996,16 +1183,16 @@ static ssize_t pmbus_show_label(struct device *dev, { struct pmbus_label *label = to_pmbus_label(da); - return snprintf(buf, PAGE_SIZE, "%s\n", label->label); + return sysfs_emit(buf, "%s\n", label->label); } static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) { if (data->num_attributes >= data->max_attributes - 1) { int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; - void *new_attrs = krealloc(data->group.attrs, - new_max_attrs * sizeof(void *), - GFP_KERNEL); + void *new_attrs = devm_krealloc(data->dev, data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); if (!new_attrs) return -ENOMEM; data->group.attrs = new_attrs; @@ -1053,11 +1240,14 @@ static int pmbus_add_boolean(struct pmbus_data *data, const char *name, const char *type, int seq, struct pmbus_sensor *s1, struct pmbus_sensor *s2, - u16 reg, u16 mask) + u8 page, u16 reg, u16 mask) { struct pmbus_boolean *boolean; struct sensor_device_attribute *a; + if (WARN((s1 && !s2) || (!s1 && s2), "Bad s1/s2 parameters\n")) + return -EINVAL; + boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); if (!boolean) return -ENOMEM; @@ -1069,14 +1259,77 @@ static int pmbus_add_boolean(struct pmbus_data *data, boolean->s1 = s1; boolean->s2 = s2; pmbus_attr_init(a, boolean->name, 0444, pmbus_show_boolean, NULL, - (reg << 16) | mask); + pb_reg_to_index(page, reg, mask)); return pmbus_add_attribute(data, &a->dev_attr.attr); } +/* of thermal for pmbus temperature sensors */ +struct pmbus_thermal_data { + struct pmbus_data *pmbus_data; + struct pmbus_sensor *sensor; +}; + +static int pmbus_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct pmbus_thermal_data *tdata = tz->devdata; + struct pmbus_sensor *sensor = tdata->sensor; + struct pmbus_data *pmbus_data = tdata->pmbus_data; + struct i2c_client *client = to_i2c_client(pmbus_data->dev); + struct device *dev = pmbus_data->hwmon_dev; + int ret = 0; + + if (!dev) { + /* May not even get to hwmon yet */ + *temp = 0; + return 0; + } + + mutex_lock(&pmbus_data->update_lock); + pmbus_update_sensor_data(client, sensor); + if (sensor->data < 0) + ret = sensor->data; + else + *temp = (int)pmbus_reg2data(pmbus_data, sensor); + mutex_unlock(&pmbus_data->update_lock); + + return ret; +} + +static const struct thermal_zone_device_ops pmbus_thermal_ops = { + .get_temp = pmbus_thermal_get_temp, +}; + +static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data, + struct pmbus_sensor *sensor, int index) +{ + struct device *dev = pmbus_data->dev; + struct pmbus_thermal_data *tdata; + struct thermal_zone_device *tzd; + + tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL); + if (!tdata) + return -ENOMEM; + + tdata->sensor = sensor; + tdata->pmbus_data = pmbus_data; + + tzd = devm_thermal_of_zone_register(dev, index, tdata, + &pmbus_thermal_ops); + /* + * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV, + * so ignore that error but forward any other error. + */ + if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV)) + return PTR_ERR(tzd); + + return 0; +} + static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, const char *name, const char *type, - int seq, int page, int reg, + int seq, int page, int phase, + int reg, enum pmbus_sensor_classes class, bool update, bool readonly, bool convert) @@ -1100,10 +1353,12 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, readonly = true; sensor->page = page; + sensor->phase = phase; sensor->reg = reg; sensor->class = class; sensor->update = update; sensor->convert = convert; + sensor->data = -ENODATA; pmbus_dev_attr_init(a, sensor->name, readonly ? 0444 : 0644, pmbus_show_sensor, pmbus_set_sensor); @@ -1114,12 +1369,16 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, sensor->next = data->sensors; data->sensors = sensor; + /* temperature sensors with _input values are registered with thermal */ + if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0) + pmbus_thermal_add_sensor(data, sensor, seq); + return sensor; } static int pmbus_add_label(struct pmbus_data *data, const char *name, int seq, - const char *lstring, int index) + const char *lstring, int index, int phase) { struct pmbus_label *label; struct device_attribute *a; @@ -1131,11 +1390,21 @@ static int pmbus_add_label(struct pmbus_data *data, a = &label->attribute; snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); - if (!index) - strncpy(label->label, lstring, sizeof(label->label) - 1); - else - snprintf(label->label, sizeof(label->label), "%s%d", lstring, - index); + if (!index) { + if (phase == 0xff) + strncpy(label->label, lstring, + sizeof(label->label) - 1); + else + snprintf(label->label, sizeof(label->label), "%s.%d", + lstring, phase); + } else { + if (phase == 0xff) + snprintf(label->label, sizeof(label->label), "%s%d", + lstring, index); + else + snprintf(label->label, sizeof(label->label), "%s%d.%d", + lstring, index, phase); + } pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL); return pmbus_add_attribute(data, &a->attr); @@ -1174,7 +1443,7 @@ struct pmbus_sensor_attr { bool compare; /* true if compare function needed */ u32 func; /* sensor mask */ u32 sfunc; /* sensor status mask */ - int sbase; /* status base register */ + int sreg; /* status register */ const struct pmbus_limit_attr *limit;/* limit registers */ }; @@ -1200,7 +1469,7 @@ static int pmbus_add_limit_attrs(struct i2c_client *client, for (i = 0; i < nlimit; i++) { if (pmbus_check_word_register(client, page, l->reg)) { curr = pmbus_add_sensor(data, name, l->attr, index, - page, l->reg, attr->class, + page, 0xff, l->reg, attr->class, attr->update || l->update, false, true); if (!curr) @@ -1212,7 +1481,7 @@ static int pmbus_add_limit_attrs(struct i2c_client *client, : NULL, attr->compare ? l->low ? base : curr : NULL, - attr->sbase + page, l->sbit); + page, attr->sreg, l->sbit); if (ret) return ret; have_alarm = 1; @@ -1227,7 +1496,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, struct pmbus_data *data, const struct pmbus_driver_info *info, const char *name, - int index, int page, + int index, int page, int phase, const struct pmbus_sensor_attr *attr, bool paged) { @@ -1237,15 +1506,16 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, if (attr->label) { ret = pmbus_add_label(data, name, index, attr->label, - paged ? page + 1 : 0); + paged ? page + 1 : 0, phase); if (ret) return ret; } - base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, - attr->class, true, true, true); + base = pmbus_add_sensor(data, name, "input", index, page, phase, + attr->reg, attr->class, true, true, true); if (!base) return -ENOMEM; - if (attr->sfunc) { + /* No limit and alarm attributes for phase specific sensors */ + if (attr->sfunc && phase == 0xff) { ret = pmbus_add_limit_attrs(client, data, info, name, index, page, base, attr); if (ret < 0) @@ -1257,11 +1527,11 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, * which global bit is set) for this page is accessible. */ if (!ret && attr->gbit && - (!upper || (upper && data->has_status_word)) && + (!upper || data->has_status_word) && pmbus_check_status_register(client, page)) { ret = pmbus_add_boolean(data, name, "alarm", index, NULL, NULL, - PB_STATUS_BASE + page, + page, PMBUS_STATUS_WORD, attr->gbit); if (ret) return ret; @@ -1311,14 +1581,29 @@ static int pmbus_add_sensor_attrs(struct i2c_client *client, pages = paged ? info->pages : 1; for (page = 0; page < pages; page++) { - if (!(info->func[page] & attrs->func)) - continue; - ret = pmbus_add_sensor_attrs_one(client, data, info, - name, index, page, - attrs, paged); - if (ret) - return ret; - index++; + if (info->func[page] & attrs->func) { + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + 0xff, attrs, paged); + if (ret) + return ret; + index++; + } + if (info->phases[page]) { + int phase; + + for (phase = 0; phase < info->phases[page]; + phase++) { + if (!(info->pfunc[phase] & attrs->func)) + continue; + ret = pmbus_add_sensor_attrs_one(client, + data, info, name, index, page, + phase, attrs, paged); + if (ret) + return ret; + index++; + } + } } attrs++; } @@ -1335,7 +1620,7 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = { .reg = PMBUS_VIN_UV_FAULT_LIMIT, .attr = "lcrit", .alarm = "lcrit_alarm", - .sbit = PB_VOLTAGE_UV_FAULT, + .sbit = PB_VOLTAGE_UV_FAULT | PB_VOLTAGE_VIN_OFF, }, { .reg = PMBUS_VIN_OV_WARN_LIMIT, .attr = "max", @@ -1361,6 +1646,12 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_VIN_HISTORY, .attr = "reset_history", + }, { + .reg = PMBUS_MFR_VIN_MIN, + .attr = "rated_min", + }, { + .reg = PMBUS_MFR_VIN_MAX, + .attr = "rated_max", }, }; @@ -1424,7 +1715,13 @@ static const struct pmbus_limit_attr vout_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_VOUT_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_VOUT_MIN, + .attr = "rated_min", + }, { + .reg = PMBUS_MFR_VOUT_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr voltage_attributes[] = { @@ -1434,7 +1731,7 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .label = "vin", .func = PMBUS_HAVE_VIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, - .sbase = PB_STATUS_INPUT_BASE, + .sreg = PMBUS_STATUS_INPUT, .gbit = PB_STATUS_VIN_UV, .limit = vin_limit_attrs, .nlimit = ARRAY_SIZE(vin_limit_attrs), @@ -1444,7 +1741,7 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .label = "vmon", .func = PMBUS_HAVE_VMON, .sfunc = PMBUS_HAVE_STATUS_VMON, - .sbase = PB_STATUS_VMON_BASE, + .sreg = PMBUS_VIRT_STATUS_VMON, .limit = vmon_limit_attrs, .nlimit = ARRAY_SIZE(vmon_limit_attrs), }, { @@ -1459,7 +1756,7 @@ static const struct pmbus_sensor_attr voltage_attributes[] = { .paged = true, .func = PMBUS_HAVE_VOUT, .sfunc = PMBUS_HAVE_STATUS_VOUT, - .sbase = PB_STATUS_VOUT_BASE, + .sreg = PMBUS_STATUS_VOUT, .gbit = PB_STATUS_VOUT_OV, .limit = vout_limit_attrs, .nlimit = ARRAY_SIZE(vout_limit_attrs), @@ -1494,7 +1791,10 @@ static const struct pmbus_limit_attr iin_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_IIN_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_IIN_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr iout_limit_attrs[] = { @@ -1528,7 +1828,10 @@ static const struct pmbus_limit_attr iout_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_IOUT_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_IOUT_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr current_attributes[] = { @@ -1538,7 +1841,7 @@ static const struct pmbus_sensor_attr current_attributes[] = { .label = "iin", .func = PMBUS_HAVE_IIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, - .sbase = PB_STATUS_INPUT_BASE, + .sreg = PMBUS_STATUS_INPUT, .gbit = PB_STATUS_INPUT, .limit = iin_limit_attrs, .nlimit = ARRAY_SIZE(iin_limit_attrs), @@ -1549,7 +1852,7 @@ static const struct pmbus_sensor_attr current_attributes[] = { .paged = true, .func = PMBUS_HAVE_IOUT, .sfunc = PMBUS_HAVE_STATUS_IOUT, - .sbase = PB_STATUS_IOUT_BASE, + .sreg = PMBUS_STATUS_IOUT, .gbit = PB_STATUS_IOUT_OC, .limit = iout_limit_attrs, .nlimit = ARRAY_SIZE(iout_limit_attrs), @@ -1579,7 +1882,10 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_PIN_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_PIN_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr pout_limit_attrs[] = { @@ -1613,7 +1919,10 @@ static const struct pmbus_limit_attr pout_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_POUT_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_POUT_MAX, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr power_attributes[] = { @@ -1623,7 +1932,7 @@ static const struct pmbus_sensor_attr power_attributes[] = { .label = "pin", .func = PMBUS_HAVE_PIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, - .sbase = PB_STATUS_INPUT_BASE, + .sreg = PMBUS_STATUS_INPUT, .gbit = PB_STATUS_INPUT, .limit = pin_limit_attrs, .nlimit = ARRAY_SIZE(pin_limit_attrs), @@ -1634,7 +1943,7 @@ static const struct pmbus_sensor_attr power_attributes[] = { .paged = true, .func = PMBUS_HAVE_POUT, .sfunc = PMBUS_HAVE_STATUS_IOUT, - .sbase = PB_STATUS_IOUT_BASE, + .sreg = PMBUS_STATUS_IOUT, .limit = pout_limit_attrs, .nlimit = ARRAY_SIZE(pout_limit_attrs), } @@ -1677,7 +1986,10 @@ static const struct pmbus_limit_attr temp_limit_attrs[] = { }, { .reg = PMBUS_VIRT_RESET_TEMP_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_MAX_TEMP_1, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr temp_limit_attrs2[] = { @@ -1715,7 +2027,10 @@ static const struct pmbus_limit_attr temp_limit_attrs2[] = { }, { .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, .attr = "reset_history", - } + }, { + .reg = PMBUS_MFR_MAX_TEMP_2, + .attr = "rated_max", + }, }; static const struct pmbus_limit_attr temp_limit_attrs3[] = { @@ -1741,7 +2056,10 @@ static const struct pmbus_limit_attr temp_limit_attrs3[] = { .attr = "crit", .alarm = "crit_alarm", .sbit = PB_TEMP_OT_FAULT, - } + }, { + .reg = PMBUS_MFR_MAX_TEMP_3, + .attr = "rated_max", + }, }; static const struct pmbus_sensor_attr temp_attributes[] = { @@ -1753,7 +2071,7 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .compare = true, .func = PMBUS_HAVE_TEMP, .sfunc = PMBUS_HAVE_STATUS_TEMP, - .sbase = PB_STATUS_TEMP_BASE, + .sreg = PMBUS_STATUS_TEMPERATURE, .gbit = PB_STATUS_TEMPERATURE, .limit = temp_limit_attrs, .nlimit = ARRAY_SIZE(temp_limit_attrs), @@ -1765,7 +2083,7 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .compare = true, .func = PMBUS_HAVE_TEMP2, .sfunc = PMBUS_HAVE_STATUS_TEMP, - .sbase = PB_STATUS_TEMP_BASE, + .sreg = PMBUS_STATUS_TEMPERATURE, .gbit = PB_STATUS_TEMPERATURE, .limit = temp_limit_attrs2, .nlimit = ARRAY_SIZE(temp_limit_attrs2), @@ -1777,7 +2095,7 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .compare = true, .func = PMBUS_HAVE_TEMP3, .sfunc = PMBUS_HAVE_STATUS_TEMP, - .sbase = PB_STATUS_TEMP_BASE, + .sreg = PMBUS_STATUS_TEMPERATURE, .gbit = PB_STATUS_TEMPERATURE, .limit = temp_limit_attrs3, .nlimit = ARRAY_SIZE(temp_limit_attrs3), @@ -1822,7 +2140,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client, struct pmbus_sensor *sensor; sensor = pmbus_add_sensor(data, "fan", "target", index, page, - PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN, + 0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN, false, false, true); if (!sensor) @@ -1833,14 +2151,14 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client, return 0; sensor = pmbus_add_sensor(data, "pwm", NULL, index, page, - PMBUS_VIRT_PWM_1 + id, PSC_PWM, + 0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM, false, false, true); if (!sensor) return -ENOMEM; sensor = pmbus_add_sensor(data, "pwm", "enable", index, page, - PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM, + 0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM, true, false, false); if (!sensor) @@ -1882,7 +2200,7 @@ static int pmbus_add_fan_attributes(struct i2c_client *client, continue; if (pmbus_add_sensor(data, "fan", "input", index, - page, pmbus_fan_registers[f], + page, 0xff, pmbus_fan_registers[f], PSC_FAN, true, true, true) == NULL) return -ENOMEM; @@ -1902,19 +2220,19 @@ static int pmbus_add_fan_attributes(struct i2c_client *client, if ((info->func[page] & pmbus_fan_status_flags[f]) && pmbus_check_byte_register(client, page, pmbus_fan_status_registers[f])) { - int base; + int reg; if (f > 1) /* fan 3, 4 */ - base = PB_STATUS_FAN34_BASE + page; + reg = PMBUS_STATUS_FAN_34; else - base = PB_STATUS_FAN_BASE + page; + reg = PMBUS_STATUS_FAN_12; ret = pmbus_add_boolean(data, "fan", - "alarm", index, NULL, NULL, base, + "alarm", index, NULL, NULL, page, reg, PB_FAN_FAN1_WARNING >> (f & 1)); if (ret) return ret; ret = pmbus_add_boolean(data, "fan", - "fault", index, NULL, NULL, base, + "fault", index, NULL, NULL, page, reg, PB_FAN_FAN1_FAULT >> (f & 1)); if (ret) return ret; @@ -1963,12 +2281,15 @@ static ssize_t pmbus_show_samples(struct device *dev, int val; struct i2c_client *client = to_i2c_client(dev->parent); struct pmbus_samples_reg *reg = to_samples_reg(devattr); + struct pmbus_data *data = i2c_get_clientdata(client); - val = _pmbus_read_word_data(client, reg->page, reg->attr->reg); + mutex_lock(&data->update_lock); + val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg); + mutex_unlock(&data->update_lock); if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t pmbus_set_samples(struct device *dev, @@ -2073,6 +2394,111 @@ static int pmbus_find_attributes(struct i2c_client *client, } /* + * The pmbus_class_attr_map structure maps one sensor class to + * it's corresponding sensor attributes array. + */ +struct pmbus_class_attr_map { + enum pmbus_sensor_classes class; + int nattr; + const struct pmbus_sensor_attr *attr; +}; + +static const struct pmbus_class_attr_map class_attr_map[] = { + { + .class = PSC_VOLTAGE_IN, + .attr = voltage_attributes, + .nattr = ARRAY_SIZE(voltage_attributes), + }, { + .class = PSC_VOLTAGE_OUT, + .attr = voltage_attributes, + .nattr = ARRAY_SIZE(voltage_attributes), + }, { + .class = PSC_CURRENT_IN, + .attr = current_attributes, + .nattr = ARRAY_SIZE(current_attributes), + }, { + .class = PSC_CURRENT_OUT, + .attr = current_attributes, + .nattr = ARRAY_SIZE(current_attributes), + }, { + .class = PSC_POWER, + .attr = power_attributes, + .nattr = ARRAY_SIZE(power_attributes), + }, { + .class = PSC_TEMPERATURE, + .attr = temp_attributes, + .nattr = ARRAY_SIZE(temp_attributes), + } +}; + +/* + * Read the coefficients for direct mode. + */ +static int pmbus_read_coefficients(struct i2c_client *client, + struct pmbus_driver_info *info, + const struct pmbus_sensor_attr *attr) +{ + int rv; + union i2c_smbus_data data; + enum pmbus_sensor_classes class = attr->class; + s8 R; + s16 m, b; + + data.block[0] = 2; + data.block[1] = attr->reg; + data.block[2] = 0x01; + + rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, + I2C_SMBUS_BLOCK_PROC_CALL, &data); + + if (rv < 0) + return rv; + + if (data.block[0] != 5) + return -EIO; + + m = data.block[1] | (data.block[2] << 8); + b = data.block[3] | (data.block[4] << 8); + R = data.block[5]; + info->m[class] = m; + info->b[class] = b; + info->R[class] = R; + + return rv; +} + +static int pmbus_init_coefficients(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int i, n, ret = -EINVAL; + const struct pmbus_class_attr_map *map; + const struct pmbus_sensor_attr *attr; + + for (i = 0; i < ARRAY_SIZE(class_attr_map); i++) { + map = &class_attr_map[i]; + if (info->format[map->class] != direct) + continue; + for (n = 0; n < map->nattr; n++) { + attr = &map->attr[n]; + if (map->class != attr->class) + continue; + ret = pmbus_read_coefficients(client, info, attr); + if (ret >= 0) + break; + } + if (ret < 0) { + dev_err(&client->dev, + "No coefficients found for sensor class %d\n", + map->class); + return -EINVAL; + } + } + + return 0; +} + +/* * Identify chip parameters. * This function is called for all chips. */ @@ -2104,6 +2530,10 @@ static int pmbus_identify_common(struct i2c_client *client, if (data->info->format[PSC_VOLTAGE_OUT] != direct) return -ENODEV; break; + case 3: /* ieee 754 half precision */ + if (data->info->format[PSC_VOLTAGE_OUT] != ieee754) + return -ENODEV; + break; default: return -ENODEV; } @@ -2120,7 +2550,43 @@ static int pmbus_read_status_byte(struct i2c_client *client, int page) static int pmbus_read_status_word(struct i2c_client *client, int page) { - return _pmbus_read_word_data(client, page, PMBUS_STATUS_WORD); + return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD); +} + +/* PEC attribute support */ + +static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); +} + +static ssize_t pec_store(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + bool enable; + int err; + + err = kstrtobool(buf, &enable); + if (err < 0) + return err; + + if (enable) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + + return count; +} + +static DEVICE_ATTR_RW(pec); + +static void pmbus_remove_pec(void *dev) +{ + device_remove_file(dev, &dev_attr_pec); } static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, @@ -2130,6 +2596,21 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, int page, ret; /* + * Figure out if PEC is enabled before accessing any other register. + * Make sure PEC is disabled, will be enabled later if needed. + */ + client->flags &= ~I2C_CLIENT_PEC; + + /* Enable PEC if the controller and bus supports it */ + if (!(data->flags & PMBUS_NO_CAPABILITY)) { + ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) + client->flags |= I2C_CLIENT_PEC; + } + } + + /* * Some PMBus chips don't support PMBUS_STATUS_WORD, so try * to use PMBUS_STATUS_BYTE instead if that is the case. * Bail out if both registers are not supported. @@ -2147,19 +2628,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, data->has_status_word = true; } - /* Enable PEC if the controller supports it */ - ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); - if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) - client->flags |= I2C_CLIENT_PEC; - /* * Check if the chip is write protected. If it is, we can not clear * faults, and we should not try it. Also, in that case, writes into * limit registers need to be disabled. */ - ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); - if (ret > 0 && (ret & PB_WP_ANY)) - data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { + ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); + if (ret > 0 && (ret & PB_WP_ANY)) + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + } if (data->info->pages) pmbus_clear_faults(client); @@ -2186,6 +2664,31 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return ret; } } + + if (data->flags & PMBUS_USE_COEFFICIENTS_CMD) { + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BLOCK_PROC_CALL)) + return -ENODEV; + + ret = pmbus_init_coefficients(client, info); + if (ret < 0) + return ret; + } + + if (client->flags & I2C_CLIENT_PEC) { + /* + * If I2C_CLIENT_PEC is set here, both the I2C adapter and the + * chip support PEC. Add 'pec' attribute to client device to let + * the user control it. + */ + ret = device_create_file(dev, &dev_attr_pec); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, pmbus_remove_pec, dev); + if (ret) + return ret; + } + return 0; } @@ -2194,10 +2697,14 @@ static int pmbus_regulator_is_enabled(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 ret; - ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION); + mutex_lock(&data->update_lock); + ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION); + mutex_unlock(&data->update_lock); + if (ret < 0) return ret; @@ -2208,11 +2715,17 @@ static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable) { 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 ret; - return pmbus_update_byte_data(client, page, PMBUS_OPERATION, - PB_OPERATION_CONTROL_ON, - enable ? PB_OPERATION_CONTROL_ON : 0); + mutex_lock(&data->update_lock); + ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION, + PB_OPERATION_CONTROL_ON, + enable ? PB_OPERATION_CONTROL_ON : 0); + mutex_unlock(&data->update_lock); + + return ret; } static int pmbus_regulator_enable(struct regulator_dev *rdev) @@ -2225,12 +2738,264 @@ static int pmbus_regulator_disable(struct regulator_dev *rdev) return _pmbus_regulator_on_off(rdev, 0); } +/* A PMBus status flag and the corresponding REGULATOR_ERROR_* flag */ +struct pmbus_regulator_status_assoc { + int pflag, rflag; +}; + +/* PMBus->regulator bit mappings for a PMBus status register */ +struct pmbus_regulator_status_category { + int func; + int reg; + const struct pmbus_regulator_status_assoc *bits; /* zero-terminated */ +}; + +static const struct pmbus_regulator_status_category pmbus_regulator_flag_map[] = { + { + .func = PMBUS_HAVE_STATUS_VOUT, + .reg = PMBUS_STATUS_VOUT, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN }, + { PB_VOLTAGE_UV_FAULT, REGULATOR_ERROR_UNDER_VOLTAGE }, + { PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN }, + { PB_VOLTAGE_OV_FAULT, REGULATOR_ERROR_REGULATION_OUT }, + { }, + }, + }, { + .func = PMBUS_HAVE_STATUS_IOUT, + .reg = PMBUS_STATUS_IOUT, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_IOUT_OC_WARNING, REGULATOR_ERROR_OVER_CURRENT_WARN }, + { PB_IOUT_OC_FAULT, REGULATOR_ERROR_OVER_CURRENT }, + { PB_IOUT_OC_LV_FAULT, REGULATOR_ERROR_OVER_CURRENT }, + { }, + }, + }, { + .func = PMBUS_HAVE_STATUS_TEMP, + .reg = PMBUS_STATUS_TEMPERATURE, + .bits = (const struct pmbus_regulator_status_assoc[]) { + { PB_TEMP_OT_WARNING, REGULATOR_ERROR_OVER_TEMP_WARN }, + { PB_TEMP_OT_FAULT, REGULATOR_ERROR_OVER_TEMP }, + { }, + }, + }, +}; + +static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) +{ + int i, status; + const struct pmbus_regulator_status_category *cat; + const struct pmbus_regulator_status_assoc *bit; + 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 func = data->info->func[page]; + + *flags = 0; + + mutex_lock(&data->update_lock); + + for (i = 0; i < ARRAY_SIZE(pmbus_regulator_flag_map); i++) { + cat = &pmbus_regulator_flag_map[i]; + if (!(func & cat->func)) + continue; + + status = _pmbus_read_byte_data(client, page, cat->reg); + if (status < 0) { + mutex_unlock(&data->update_lock); + return status; + } + + for (bit = cat->bits; bit->pflag; bit++) { + if (status & bit->pflag) + *flags |= bit->rflag; + } + } + + /* + * Map what bits of STATUS_{WORD,BYTE} we can to REGULATOR_ERROR_* + * bits. Some of the other bits are tempting (especially for cases + * where we don't have the relevant PMBUS_HAVE_STATUS_* + * functionality), but there's an unfortunate ambiguity in that + * they're defined as indicating a fault *or* a warning, so we can't + * easily determine whether to report REGULATOR_ERROR_<foo> or + * REGULATOR_ERROR_<foo>_WARN. + */ + status = pmbus_get_status(client, page, PMBUS_STATUS_WORD); + mutex_unlock(&data->update_lock); + if (status < 0) + return status; + + if (pmbus_regulator_is_enabled(rdev) && (status & PB_STATUS_OFF)) + *flags |= REGULATOR_ERROR_FAIL; + + /* + * Unlike most other status bits, PB_STATUS_{IOUT_OC,VOUT_OV} are + * defined strictly as fault indicators (not warnings). + */ + if (status & PB_STATUS_IOUT_OC) + *flags |= REGULATOR_ERROR_OVER_CURRENT; + if (status & PB_STATUS_VOUT_OV) + *flags |= REGULATOR_ERROR_REGULATION_OUT; + + /* + * If we haven't discovered any thermal faults or warnings via + * PMBUS_STATUS_TEMPERATURE, map PB_STATUS_TEMPERATURE to a warning as + * a (conservative) best-effort interpretation. + */ + if (!(*flags & (REGULATOR_ERROR_OVER_TEMP | REGULATOR_ERROR_OVER_TEMP_WARN)) && + (status & PB_STATUS_TEMPERATURE)) + *flags |= REGULATOR_ERROR_OVER_TEMP_WARN; + + return 0; +} + +static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = page, + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + + if (data->vout_low[page] < 0) { + if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MIN)) + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_MFR_VOUT_MIN); + if (s.data < 0) { + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_MARGIN_LOW); + if (s.data < 0) + return s.data; + } + data->vout_low[page] = pmbus_reg2data(data, &s); + } + + return data->vout_low[page]; +} + +static int pmbus_regulator_get_high_margin(struct i2c_client *client, int page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = page, + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + + if (data->vout_high[page] < 0) { + if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MAX)) + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_MFR_VOUT_MAX); + if (s.data < 0) { + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_MARGIN_HIGH); + if (s.data < 0) + return s.data; + } + data->vout_high[page] = pmbus_reg2data(data, &s); + } + + return data->vout_high[page]; +} + +static int pmbus_regulator_get_voltage(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); + struct pmbus_sensor s = { + .page = rdev_get_id(rdev), + .class = PSC_VOLTAGE_OUT, + .convert = true, + }; + + s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT); + if (s.data < 0) + return s.data; + + return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */ +} + +static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, + int max_uv, unsigned int *selector) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = rdev_get_id(rdev), + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */ + int low, high; + + *selector = 0; + + low = pmbus_regulator_get_low_margin(client, s.page); + if (low < 0) + return low; + + high = pmbus_regulator_get_high_margin(client, s.page); + if (high < 0) + return high; + + /* Make sure we are within margins */ + if (low > val) + val = low; + if (high < val) + val = high; + + val = pmbus_data2reg(data, &s, val); + + return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val); +} + +static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + int val, low, high; + + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) + return -EINVAL; + + selector -= rdev->desc->linear_min_sel; + val = DIV_ROUND_CLOSEST(rdev->desc->min_uV + + (rdev->desc->uV_step * selector), 1000); /* convert to mV */ + + low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev)); + if (low < 0) + return low; + + high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev)); + if (high < 0) + return high; + + if (val >= low && val <= high) + return val * 1000; /* unit is uV */ + + return 0; +} + const struct regulator_ops pmbus_regulator_ops = { .enable = pmbus_regulator_enable, .disable = pmbus_regulator_disable, .is_enabled = pmbus_regulator_is_enabled, + .get_error_flags = pmbus_regulator_get_error_flags, + .get_voltage = pmbus_regulator_get_voltage, + .set_voltage = pmbus_regulator_set_voltage, + .list_voltage = pmbus_regulator_list_voltage, }; -EXPORT_SYMBOL_GPL(pmbus_regulator_ops); +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS); static int pmbus_regulator_register(struct pmbus_data *data) { @@ -2251,11 +3016,10 @@ static int pmbus_regulator_register(struct pmbus_data *data) rdev = devm_regulator_register(dev, &info->reg_desc[i], &config); - if (IS_ERR(rdev)) { - dev_err(dev, "Failed to register %s regulator\n", - info->reg_desc[i].name); - return PTR_ERR(rdev); - } + if (IS_ERR(rdev)) + return dev_err_probe(dev, PTR_ERR(rdev), + "Failed to register %s regulator\n", + info->reg_desc[i].name); } return 0; @@ -2303,6 +3067,41 @@ static int pmbus_debugfs_get_status(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, NULL, "0x%04llx\n"); +static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int rc; + struct pmbus_debugfs_entry *entry = file->private_data; + char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + + rc = pmbus_read_block_data(entry->client, entry->page, entry->reg, + data); + if (rc < 0) + return rc; + + /* Add newline at the end of a read data */ + data[rc] = '\n'; + + /* Include newline into the length */ + rc += 1; + + return simple_read_from_buffer(buf, count, ppos, data, rc); +} + +static const struct file_operations pmbus_debugfs_ops_mfr = { + .llseek = noop_llseek, + .read = pmbus_debugfs_mfr_read, + .write = NULL, + .open = simple_open, +}; + +static void pmbus_remove_debugfs(void *data) +{ + struct dentry *entry = data; + + debugfs_remove_recursive(entry); +} + static int pmbus_init_debugfs(struct i2c_client *client, struct pmbus_data *data) { @@ -2324,13 +3123,80 @@ static int pmbus_init_debugfs(struct i2c_client *client, return -ENODEV; } - /* Allocate the max possible entries we need. */ + /* + * Allocate the max possible entries we need. + * 6 entries device-specific + * 10 entries page-specific + */ entries = devm_kcalloc(data->dev, - data->info->pages * 10, sizeof(*entries), + 6 + data->info->pages * 10, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; + /* + * Add device-specific entries. + * Please note that the PMBUS standard allows all registers to be + * page-specific. + * To reduce the number of debugfs entries for devices with many pages + * assume that values of the following registers are the same for all + * pages and report values only for page 0. + */ + if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_ID; + debugfs_create_file("mfr_id", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_MODEL; + debugfs_create_file("mfr_model", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_REVISION; + debugfs_create_file("mfr_revision", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_LOCATION; + debugfs_create_file("mfr_location", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_DATE; + debugfs_create_file("mfr_date", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_SERIAL; + debugfs_create_file("mfr_serial", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + /* Add page specific entries */ for (i = 0; i < data->info->pages; ++i) { /* Check accessibility of status register if it's not page 0 */ if (!i || pmbus_check_status_register(client, i)) { @@ -2435,7 +3301,8 @@ static int pmbus_init_debugfs(struct i2c_client *client, } } - return 0; + return devm_add_action_or_reset(data->dev, + pmbus_remove_debugfs, data->debugfs); } #else static int pmbus_init_debugfs(struct i2c_client *client, @@ -2445,14 +3312,15 @@ static int pmbus_init_debugfs(struct i2c_client *client, } #endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ -int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, - struct pmbus_driver_info *info) +int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) { struct device *dev = &client->dev; const struct pmbus_platform_data *pdata = dev_get_platdata(dev); struct pmbus_data *data; size_t groups_num = 0; int ret; + int i; + char *name; if (!info) return -ENODEV; @@ -2482,6 +3350,13 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, if (pdata) data->flags = pdata->flags; data->info = info; + data->currpage = -1; + data->currphase = -1; + + for (i = 0; i < ARRAY_SIZE(data->vout_low); i++) { + data->vout_low[i] = -1; + data->vout_high[i] = -1; + } ret = pmbus_init_common(client, data, info); if (ret < 0) @@ -2489,7 +3364,7 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, ret = pmbus_find_attributes(client, data); if (ret) - goto out_kfree; + return ret; /* * If there are no attributes, something is wrong. @@ -2497,49 +3372,34 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, */ if (!data->num_attributes) { dev_err(dev, "No attributes found\n"); - ret = -ENODEV; - goto out_kfree; + return -ENODEV; } + name = devm_kstrdup(dev, client->name, GFP_KERNEL); + if (!name) + return -ENOMEM; + strreplace(name, '-', '_'); + data->groups[0] = &data->group; memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num); - data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, - data, data->groups); + data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, + name, data, data->groups); if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); dev_err(dev, "Failed to register hwmon device\n"); - goto out_kfree; + return PTR_ERR(data->hwmon_dev); } ret = pmbus_regulator_register(data); if (ret) - goto out_unregister; + return ret; ret = pmbus_init_debugfs(client, data); if (ret) dev_warn(dev, "Failed to register debugfs\n"); return 0; - -out_unregister: - hwmon_device_unregister(data->hwmon_dev); -out_kfree: - kfree(data->group.attrs); - return ret; -} -EXPORT_SYMBOL_GPL(pmbus_do_probe); - -int pmbus_do_remove(struct i2c_client *client) -{ - struct pmbus_data *data = i2c_get_clientdata(client); - - debugfs_remove_recursive(data->debugfs); - - hwmon_device_unregister(data->hwmon_dev); - kfree(data->group.attrs); - return 0; } -EXPORT_SYMBOL_GPL(pmbus_do_remove); +EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) { @@ -2547,7 +3407,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) return data->debugfs; } -EXPORT_SYMBOL_GPL(pmbus_get_debugfs_dir); +EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS); static int __init pmbus_core_init(void) { diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index 517584cff3de..52bee5de2988 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -41,6 +41,15 @@ static int pxe1610_identify(struct i2c_client *client, info->vrm_version[i] = vr13; break; default: + /* + * If prior pages are available limit operation + * to them + */ + if (i != 0) { + info->pages = i; + return 0; + } + return -ENODEV; } } @@ -78,8 +87,7 @@ static struct pmbus_driver_info pxe1610_info = { .identify = pxe1610_identify, }; -static int pxe1610_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pxe1610_probe(struct i2c_client *client) { struct pmbus_driver_info *info; u8 buf[I2C_SMBUS_BLOCK_MAX]; @@ -115,7 +123,7 @@ static int pxe1610_probe(struct i2c_client *client, if (!info) return -ENOMEM; - return pmbus_do_probe(client, id, info); + return pmbus_do_probe(client, info); } static const struct i2c_device_id pxe1610_id[] = { @@ -131,8 +139,7 @@ static struct i2c_driver pxe1610_driver = { .driver = { .name = "pxe1610", }, - .probe = pxe1610_probe, - .remove = pmbus_do_remove, + .probe_new = pxe1610_probe, .id_table = pxe1610_id, }; @@ -141,3 +148,4 @@ module_i2c_driver(pxe1610_driver); MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c new file mode 100644 index 000000000000..fa298b4265a1 --- /dev/null +++ b/drivers/hwmon/pmbus/q54sj108a2.c @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Delta modules, Q54SJ108A2 series 1/4 Brick DC/DC + * Regulated Power Module + * + * Copyright 2020 Delta LLC. + */ + +#include <linux/debugfs.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "pmbus.h" + +#define STORE_DEFAULT_ALL 0x11 +#define ERASE_BLACKBOX_DATA 0xD1 +#define READ_HISTORY_EVENT_NUMBER 0xD2 +#define READ_HISTORY_EVENTS 0xE0 +#define SET_HISTORY_EVENT_OFFSET 0xE1 +#define PMBUS_FLASH_KEY_WRITE 0xEC + +enum chips { + q54sj108a2 +}; + +enum { + Q54SJ108A2_DEBUGFS_OPERATION = 0, + Q54SJ108A2_DEBUGFS_CLEARFAULT, + Q54SJ108A2_DEBUGFS_WRITEPROTECT, + Q54SJ108A2_DEBUGFS_STOREDEFAULT, + Q54SJ108A2_DEBUGFS_VOOV_RESPONSE, + Q54SJ108A2_DEBUGFS_IOOC_RESPONSE, + Q54SJ108A2_DEBUGFS_PMBUS_VERSION, + Q54SJ108A2_DEBUGFS_MFR_ID, + Q54SJ108A2_DEBUGFS_MFR_MODEL, + Q54SJ108A2_DEBUGFS_MFR_REVISION, + Q54SJ108A2_DEBUGFS_MFR_LOCATION, + Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE, + Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET, + Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET, + Q54SJ108A2_DEBUGFS_BLACKBOX_READ, + Q54SJ108A2_DEBUGFS_FLASH_KEY, + Q54SJ108A2_DEBUGFS_NUM_ENTRIES +}; + +struct q54sj108a2_data { + enum chips chip; + struct i2c_client *client; + + int debugfs_entries[Q54SJ108A2_DEBUGFS_NUM_ENTRIES]; +}; + +#define to_psu(x, y) container_of((x), struct q54sj108a2_data, debugfs_entries[(y)]) + +static struct pmbus_driver_info q54sj108a2_info[] = { + [q54sj108a2] = { + .pages = 1, + + /* Source : Delta Q54SJ108A2 */ + .format[PSC_TEMPERATURE] = linear, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + + .func[0] = PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_INPUT, + }, +}; + +static ssize_t q54sj108a2_debugfs_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int rc; + int *idxp = file->private_data; + int idx = *idxp; + struct q54sj108a2_data *psu = to_psu(idxp, idx); + char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + char data_char[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + char *res; + + switch (idx) { + case Q54SJ108A2_DEBUGFS_OPERATION: + rc = i2c_smbus_read_byte_data(psu->client, PMBUS_OPERATION); + if (rc < 0) + return rc; + + rc = snprintf(data, 3, "%02x", rc); + break; + case Q54SJ108A2_DEBUGFS_WRITEPROTECT: + rc = i2c_smbus_read_byte_data(psu->client, PMBUS_WRITE_PROTECT); + if (rc < 0) + return rc; + + rc = snprintf(data, 3, "%02x", rc); + break; + case Q54SJ108A2_DEBUGFS_VOOV_RESPONSE: + rc = i2c_smbus_read_byte_data(psu->client, PMBUS_VOUT_OV_FAULT_RESPONSE); + if (rc < 0) + return rc; + + rc = snprintf(data, 3, "%02x", rc); + break; + case Q54SJ108A2_DEBUGFS_IOOC_RESPONSE: + rc = i2c_smbus_read_byte_data(psu->client, PMBUS_IOUT_OC_FAULT_RESPONSE); + if (rc < 0) + return rc; + + rc = snprintf(data, 3, "%02x", rc); + break; + case Q54SJ108A2_DEBUGFS_PMBUS_VERSION: + rc = i2c_smbus_read_byte_data(psu->client, PMBUS_REVISION); + if (rc < 0) + return rc; + + rc = snprintf(data, 3, "%02x", rc); + break; + case Q54SJ108A2_DEBUGFS_MFR_ID: + rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_ID, data); + if (rc < 0) + return rc; + break; + case Q54SJ108A2_DEBUGFS_MFR_MODEL: + rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_MODEL, data); + if (rc < 0) + return rc; + break; + case Q54SJ108A2_DEBUGFS_MFR_REVISION: + rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_REVISION, data); + if (rc < 0) + return rc; + break; + case Q54SJ108A2_DEBUGFS_MFR_LOCATION: + rc = i2c_smbus_read_block_data(psu->client, PMBUS_MFR_LOCATION, data); + if (rc < 0) + return rc; + break; + case Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET: + rc = i2c_smbus_read_byte_data(psu->client, READ_HISTORY_EVENT_NUMBER); + if (rc < 0) + return rc; + + rc = snprintf(data, 3, "%02x", rc); + break; + case Q54SJ108A2_DEBUGFS_BLACKBOX_READ: + rc = i2c_smbus_read_block_data(psu->client, READ_HISTORY_EVENTS, data); + if (rc < 0) + return rc; + + res = bin2hex(data, data_char, 32); + rc = res - data; + + break; + case Q54SJ108A2_DEBUGFS_FLASH_KEY: + rc = i2c_smbus_read_block_data(psu->client, PMBUS_FLASH_KEY_WRITE, data); + if (rc < 0) + return rc; + + res = bin2hex(data, data_char, 4); + rc = res - data; + + break; + default: + return -EINVAL; + } + + data[rc] = '\n'; + rc += 2; + + return simple_read_from_buffer(buf, count, ppos, data, rc); +} + +static ssize_t q54sj108a2_debugfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + u8 flash_key[4]; + u8 dst_data; + ssize_t rc; + int *idxp = file->private_data; + int idx = *idxp; + struct q54sj108a2_data *psu = to_psu(idxp, idx); + + rc = i2c_smbus_write_byte_data(psu->client, PMBUS_WRITE_PROTECT, 0); + if (rc) + return rc; + + switch (idx) { + case Q54SJ108A2_DEBUGFS_OPERATION: + rc = kstrtou8_from_user(buf, count, 0, &dst_data); + if (rc < 0) + return rc; + + rc = i2c_smbus_write_byte_data(psu->client, PMBUS_OPERATION, dst_data); + if (rc < 0) + return rc; + + break; + case Q54SJ108A2_DEBUGFS_CLEARFAULT: + rc = i2c_smbus_write_byte(psu->client, PMBUS_CLEAR_FAULTS); + if (rc < 0) + return rc; + + break; + case Q54SJ108A2_DEBUGFS_STOREDEFAULT: + flash_key[0] = 0x7E; + flash_key[1] = 0x15; + flash_key[2] = 0xDC; + flash_key[3] = 0x42; + rc = i2c_smbus_write_block_data(psu->client, PMBUS_FLASH_KEY_WRITE, 4, flash_key); + if (rc < 0) + return rc; + + rc = i2c_smbus_write_byte(psu->client, STORE_DEFAULT_ALL); + if (rc < 0) + return rc; + + break; + case Q54SJ108A2_DEBUGFS_VOOV_RESPONSE: + rc = kstrtou8_from_user(buf, count, 0, &dst_data); + if (rc < 0) + return rc; + + rc = i2c_smbus_write_byte_data(psu->client, PMBUS_VOUT_OV_FAULT_RESPONSE, dst_data); + if (rc < 0) + return rc; + + break; + case Q54SJ108A2_DEBUGFS_IOOC_RESPONSE: + rc = kstrtou8_from_user(buf, count, 0, &dst_data); + if (rc < 0) + return rc; + + rc = i2c_smbus_write_byte_data(psu->client, PMBUS_IOUT_OC_FAULT_RESPONSE, dst_data); + if (rc < 0) + return rc; + + break; + case Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE: + rc = i2c_smbus_write_byte(psu->client, ERASE_BLACKBOX_DATA); + if (rc < 0) + return rc; + + break; + case Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET: + rc = kstrtou8_from_user(buf, count, 0, &dst_data); + if (rc < 0) + return rc; + + rc = i2c_smbus_write_byte_data(psu->client, SET_HISTORY_EVENT_OFFSET, dst_data); + if (rc < 0) + return rc; + + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct file_operations q54sj108a2_fops = { + .llseek = noop_llseek, + .read = q54sj108a2_debugfs_read, + .write = q54sj108a2_debugfs_write, + .open = simple_open, +}; + +static const struct i2c_device_id q54sj108a2_id[] = { + { "q54sj108a2", q54sj108a2 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, q54sj108a2_id); + +static int q54sj108a2_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + enum chips chip_id; + int ret, i; + struct dentry *debugfs; + struct dentry *q54sj108a2_dir; + struct q54sj108a2_data *psu; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + if (client->dev.of_node) + chip_id = (enum chips)(unsigned long)of_device_get_match_data(dev); + else + chip_id = i2c_match_id(q54sj108a2_id, client)->driver_data; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + if (ret != 6 || strncmp(buf, "DELTA", 5)) { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer ID '%s'\n", buf); + return -ENODEV; + } + + /* + * The chips support reading PMBUS_MFR_MODEL. + */ + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) { + dev_err(dev, "Failed to read Manufacturer Model\n"); + return ret; + } + if (ret != 14 || strncmp(buf, "Q54SJ108A2", 10)) { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_REVISION, buf); + if (ret < 0) { + dev_err(dev, "Failed to read Manufacturer Revision\n"); + return ret; + } + if (ret != 4 || buf[0] != 'S') { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer Revision '%s'\n", buf); + return -ENODEV; + } + + ret = pmbus_do_probe(client, &q54sj108a2_info[chip_id]); + if (ret) + return ret; + + psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL); + if (!psu) + return 0; + + psu->client = client; + + debugfs = pmbus_get_debugfs_dir(client); + + q54sj108a2_dir = debugfs_create_dir(client->name, debugfs); + + for (i = 0; i < Q54SJ108A2_DEBUGFS_NUM_ENTRIES; ++i) + psu->debugfs_entries[i] = i; + + debugfs_create_file("operation", 0644, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_OPERATION], + &q54sj108a2_fops); + debugfs_create_file("clear_fault", 0200, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_CLEARFAULT], + &q54sj108a2_fops); + debugfs_create_file("write_protect", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_WRITEPROTECT], + &q54sj108a2_fops); + debugfs_create_file("store_default", 0200, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_STOREDEFAULT], + &q54sj108a2_fops); + debugfs_create_file("vo_ov_response", 0644, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_VOOV_RESPONSE], + &q54sj108a2_fops); + debugfs_create_file("io_oc_response", 0644, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_IOOC_RESPONSE], + &q54sj108a2_fops); + debugfs_create_file("pmbus_revision", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_PMBUS_VERSION], + &q54sj108a2_fops); + debugfs_create_file("mfr_id", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_ID], + &q54sj108a2_fops); + debugfs_create_file("mfr_model", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_MODEL], + &q54sj108a2_fops); + debugfs_create_file("mfr_revision", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_REVISION], + &q54sj108a2_fops); + debugfs_create_file("mfr_location", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_LOCATION], + &q54sj108a2_fops); + debugfs_create_file("blackbox_erase", 0200, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE], + &q54sj108a2_fops); + debugfs_create_file("blackbox_read_offset", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET], + &q54sj108a2_fops); + debugfs_create_file("blackbox_set_offset", 0200, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET], + &q54sj108a2_fops); + debugfs_create_file("blackbox_read", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ], + &q54sj108a2_fops); + debugfs_create_file("flash_key", 0444, q54sj108a2_dir, + &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_FLASH_KEY], + &q54sj108a2_fops); + + return 0; +} + +static const struct of_device_id q54sj108a2_of_match[] = { + { .compatible = "delta,q54sj108a2", .data = (void *)q54sj108a2 }, + { }, +}; + +MODULE_DEVICE_TABLE(of, q54sj108a2_of_match); + +static struct i2c_driver q54sj108a2_driver = { + .driver = { + .name = "q54sj108a2", + .of_match_table = q54sj108a2_of_match, + }, + .probe_new = q54sj108a2_probe, + .id_table = q54sj108a2_id, +}; + +module_i2c_driver(q54sj108a2_driver); + +MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>"); +MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c new file mode 100644 index 000000000000..357b9d9d896b --- /dev/null +++ b/drivers/hwmon/pmbus/stpddc60.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for the STPDDC60 controller + * + * Copyright (c) 2021 Flextronics International Sweden AB. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +#define STPDDC60_MFR_READ_VOUT 0xd2 +#define STPDDC60_MFR_OV_LIMIT_OFFSET 0xe5 +#define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6 + +static const struct i2c_device_id stpddc60_id[] = { + {"stpddc60", 0}, + {"bmr481", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, stpddc60_id); + +static struct pmbus_driver_info stpddc60_info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT, +}; + +/* + * Calculate the closest absolute offset between commanded vout value + * and limit value in steps of 50mv in the range 0 (50mv) to 7 (400mv). + * Return 0 if the upper limit is lower than vout or if the lower limit + * is higher than vout. + */ +static u8 stpddc60_get_offset(int vout, u16 limit, bool over) +{ + int offset; + long v, l; + + v = 250 + (vout - 1) * 5; /* Convert VID to mv */ + l = (limit * 1000L) >> 8; /* Convert LINEAR to mv */ + + if (over == (l < v)) + return 0; + + offset = DIV_ROUND_CLOSEST(abs(l - v), 50); + + if (offset > 0) + offset--; + + return clamp_val(offset, 0, 7); +} + +/* + * Adjust the linear format word to use the given fixed exponent. + */ +static u16 stpddc60_adjust_linear(u16 word, s16 fixed) +{ + s16 e, m, d; + + e = ((s16)word) >> 11; + m = ((s16)((word & 0x7ff) << 5)) >> 5; + d = e - fixed; + + if (d >= 0) + m <<= d; + else + m >>= -d; + + return clamp_val(m, 0, 0x3ff) | ((fixed << 11) & 0xf800); +} + +/* + * The VOUT_COMMAND register uses the VID format but the vout alarm limit + * registers use the LINEAR format so we override VOUT_MODE here to force + * LINEAR format for all registers. + */ +static int stpddc60_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_VOUT_MODE: + ret = 0x18; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +/* + * The vout related registers return values in LINEAR11 format when LINEAR16 + * is expected. Clear the top 5 bits to set the exponent part to zero to + * convert the value to LINEAR16 format. + */ +static int stpddc60_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, page, phase, + STPDDC60_MFR_READ_VOUT); + if (ret < 0) + return ret; + ret &= 0x7ff; + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + ret &= 0x7ff; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +/* + * The vout under- and over-voltage limits are set as an offset relative to + * the commanded vout voltage. The vin, iout, pout and temp limits must use + * the same fixed exponent the chip uses to encode the data when read. + */ +static int stpddc60_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + u8 offset; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_VOUT_OV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_COMMAND); + if (ret < 0) + return ret; + offset = stpddc60_get_offset(ret, word, true); + ret = pmbus_write_byte_data(client, page, + STPDDC60_MFR_OV_LIMIT_OFFSET, + offset); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_COMMAND); + if (ret < 0) + return ret; + offset = stpddc60_get_offset(ret, word, false); + ret = pmbus_write_byte_data(client, page, + STPDDC60_MFR_UV_LIMIT_OFFSET, + offset); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_POUT_OP_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + word = stpddc60_adjust_linear(word, ret >> 11); + ret = pmbus_write_word_data(client, page, reg, word); + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int stpddc60_probe(struct i2c_client *client) +{ + int status; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + struct pmbus_driver_info *info = &stpddc60_info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id); + if (status < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return status; + } + for (mid = stpddc60_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + info->read_byte_data = stpddc60_read_byte_data; + info->read_word_data = stpddc60_read_word_data; + info->write_word_data = stpddc60_write_word_data; + + status = pmbus_do_probe(client, info); + if (status < 0) + return status; + + pmbus_set_update(client, PMBUS_VOUT_OV_FAULT_LIMIT, true); + pmbus_set_update(client, PMBUS_VOUT_UV_FAULT_LIMIT, true); + + return 0; +} + +static struct i2c_driver stpddc60_driver = { + .driver = { + .name = "stpddc60", + }, + .probe_new = stpddc60_probe, + .id_table = stpddc60_id, +}; + +module_i2c_driver(stpddc60_driver); + +MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); +MODULE_DESCRIPTION("PMBus driver for ST STPDDC60"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c index 2b83dcda964a..31bb83c0ef3e 100644 --- a/drivers/hwmon/pmbus/tps40422.c +++ b/drivers/hwmon/pmbus/tps40422.c @@ -25,10 +25,9 @@ static struct pmbus_driver_info tps40422_info = { | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, }; -static int tps40422_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tps40422_probe(struct i2c_client *client) { - return pmbus_do_probe(client, id, &tps40422_info); + return pmbus_do_probe(client, &tps40422_info); } static const struct i2c_device_id tps40422_id[] = { @@ -43,8 +42,7 @@ static struct i2c_driver tps40422_driver = { .driver = { .name = "tps40422", }, - .probe = tps40422_probe, - .remove = pmbus_do_remove, + .probe_new = tps40422_probe, .id_table = tps40422_id, }; @@ -53,3 +51,4 @@ module_i2c_driver(tps40422_driver); MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>"); MODULE_DESCRIPTION("PMBus driver for TI TPS40422"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 9c22e9013dd7..81b9d813655a 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -6,13 +6,24 @@ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> */ +#include <linux/bits.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_device.h> #include "pmbus.h" +enum chips { + tps53647, tps53667, tps53676, tps53679, tps53681, tps53688 +}; + +#define TPS53647_PAGE_NUM 1 + +#define TPS53676_USER_DATA_03 0xb3 +#define TPS53676_MAX_PHASES 7 + #define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ #define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ #define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ @@ -20,13 +31,21 @@ #define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ #define TPS53679_PAGE_NUM 2 -static int tps53679_identify(struct i2c_client *client, - struct pmbus_driver_info *info) +#define TPS53681_DEVICE_ID 0x81 + +#define TPS53681_PMBUS_REVISION 0x33 + +#define TPS53681_MFR_SPECIFIC_20 0xe4 /* Number of phases, per page */ + +static const struct i2c_device_id tps53679_id[]; + +static int tps53679_identify_mode(struct i2c_client *client, + struct pmbus_driver_info *info) { u8 vout_params; int i, ret; - for (i = 0; i < TPS53679_PAGE_NUM; i++) { + for (i = 0; i < info->pages; i++) { /* Read the register with VOUT scaling value.*/ ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); if (ret < 0) @@ -52,48 +71,225 @@ static int tps53679_identify(struct i2c_client *client, return 0; } +static int tps53679_identify_phases(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int ret; + + /* On TPS53681, only channel A provides per-phase output current */ + ret = pmbus_read_byte_data(client, 0, TPS53681_MFR_SPECIFIC_20); + if (ret < 0) + return ret; + info->phases[0] = (ret & 0x07) + 1; + + return 0; +} + +static int tps53679_identify_chip(struct i2c_client *client, + u8 revision, u16 id) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + ret = pmbus_read_byte_data(client, 0, PMBUS_REVISION); + if (ret < 0) + return ret; + if (ret != revision) { + dev_err(&client->dev, "Unexpected PMBus revision 0x%x\n", ret); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0) + return ret; + if (ret != 1 || buf[0] != id) { + dev_err(&client->dev, "Unexpected device ID 0x%x\n", buf[0]); + return -ENODEV; + } + return 0; +} + +/* + * Common identification function for chips with multi-phase support. + * Since those chips have special configuration registers, we want to have + * some level of reassurance that we are really talking with the chip + * being probed. Check PMBus revision and chip ID. + */ +static int tps53679_identify_multiphase(struct i2c_client *client, + struct pmbus_driver_info *info, + int pmbus_rev, int device_id) +{ + int ret; + + ret = tps53679_identify_chip(client, pmbus_rev, device_id); + if (ret < 0) + return ret; + + ret = tps53679_identify_mode(client, info); + if (ret < 0) + return ret; + + return tps53679_identify_phases(client, info); +} + +static int tps53679_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + return tps53679_identify_mode(client, info); +} + +static int tps53681_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + return tps53679_identify_multiphase(client, info, + TPS53681_PMBUS_REVISION, + TPS53681_DEVICE_ID); +} + +static int tps53676_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int phases_a = 0, phases_b = 0; + int i, ret; + + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0) + return ret; + if (strncmp("TI\x53\x67\x60", buf, 5)) { + dev_err(&client->dev, "Unexpected device ID: %s\n", buf); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf); + if (ret < 0) + return ret; + if (ret != 24) + return -EIO; + for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) { + if (buf[i + 1] & 0x80) { + if (buf[i] & 0x08) + phases_b++; + else + phases_a++; + } + } + + info->format[PSC_VOLTAGE_OUT] = linear; + info->pages = 1; + info->phases[0] = phases_a; + if (phases_b > 0) { + info->pages = 2; + info->phases[1] = phases_b; + } + return 0; +} + +static int tps53681_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + /* + * For reading the total output current (READ_IOUT) for all phases, + * the chip datasheet is a bit vague. It says "PHASE must be set to + * FFh to access all phases simultaneously. PHASE may also be set to + * 80h readack (!) the total phase current". + * Experiments show that the command does _not_ report the total + * current for all phases if the phase is set to 0xff. Instead, it + * appears to report the current of one of the phases. Override phase + * parameter with 0x80 when reading the total output current on page 0. + */ + if (reg == PMBUS_READ_IOUT && page == 0 && phase == 0xff) + return pmbus_read_word_data(client, page, 0x80, reg); + return -ENODATA; +} + static struct pmbus_driver_info tps53679_info = { - .pages = TPS53679_PAGE_NUM, .format[PSC_VOLTAGE_IN] = linear, .format[PSC_VOLTAGE_OUT] = vid, .format[PSC_TEMPERATURE] = linear, .format[PSC_CURRENT_OUT] = linear, .format[PSC_POWER] = linear, - .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT, - .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT, - .identify = tps53679_identify, + .pfunc[0] = PMBUS_HAVE_IOUT, + .pfunc[1] = PMBUS_HAVE_IOUT, + .pfunc[2] = PMBUS_HAVE_IOUT, + .pfunc[3] = PMBUS_HAVE_IOUT, + .pfunc[4] = PMBUS_HAVE_IOUT, + .pfunc[5] = PMBUS_HAVE_IOUT, + .pfunc[6] = PMBUS_HAVE_IOUT, }; -static int tps53679_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tps53679_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct pmbus_driver_info *info; + enum chips chip_id; - info = devm_kmemdup(&client->dev, &tps53679_info, sizeof(*info), - GFP_KERNEL); + if (dev->of_node) + chip_id = (enum chips)of_device_get_match_data(dev); + else + chip_id = i2c_match_id(tps53679_id, client)->driver_data; + + info = devm_kmemdup(dev, &tps53679_info, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - return pmbus_do_probe(client, id, info); + switch (chip_id) { + case tps53647: + case tps53667: + info->pages = TPS53647_PAGE_NUM; + info->identify = tps53679_identify; + break; + case tps53676: + info->identify = tps53676_identify; + break; + case tps53679: + case tps53688: + info->pages = TPS53679_PAGE_NUM; + info->identify = tps53679_identify; + break; + case tps53681: + info->pages = TPS53679_PAGE_NUM; + info->phases[0] = 6; + info->identify = tps53681_identify; + info->read_word_data = tps53681_read_word_data; + break; + default: + return -ENODEV; + } + + return pmbus_do_probe(client, info); } static const struct i2c_device_id tps53679_id[] = { - {"tps53679", 0}, - {"tps53688", 0}, + {"bmr474", tps53676}, + {"tps53647", tps53647}, + {"tps53667", tps53667}, + {"tps53676", tps53676}, + {"tps53679", tps53679}, + {"tps53681", tps53681}, + {"tps53688", tps53688}, {} }; MODULE_DEVICE_TABLE(i2c, tps53679_id); static const struct of_device_id __maybe_unused tps53679_of_match[] = { - {.compatible = "ti,tps53679"}, - {.compatible = "ti,tps53688"}, + {.compatible = "ti,tps53647", .data = (void *)tps53647}, + {.compatible = "ti,tps53667", .data = (void *)tps53667}, + {.compatible = "ti,tps53676", .data = (void *)tps53676}, + {.compatible = "ti,tps53679", .data = (void *)tps53679}, + {.compatible = "ti,tps53681", .data = (void *)tps53681}, + {.compatible = "ti,tps53688", .data = (void *)tps53688}, {} }; MODULE_DEVICE_TABLE(of, tps53679_of_match); @@ -103,8 +299,7 @@ static struct i2c_driver tps53679_driver = { .name = "tps53679", .of_match_table = of_match_ptr(tps53679_of_match), }, - .probe = tps53679_probe, - .remove = pmbus_do_remove, + .probe_new = tps53679_probe, .id_table = tps53679_id, }; @@ -113,3 +308,4 @@ module_i2c_driver(tps53679_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c new file mode 100644 index 000000000000..435f94304ad8 --- /dev/null +++ b/drivers/hwmon/pmbus/tps546d24.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for TEXAS TPS546D24 buck converter + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +static struct pmbus_driver_info tps546d24_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_CURRENT_OUT] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_IOUT | PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, +}; + +static int tps546d24_probe(struct i2c_client *client) +{ + int reg; + + reg = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); + if (reg < 0) + return reg; + + if (reg & 0x80) { + int err; + + err = i2c_smbus_write_byte_data(client, PMBUS_VOUT_MODE, reg & 0x7f); + if (err < 0) + return err; + } + return pmbus_do_probe(client, &tps546d24_info); +} + +static const struct i2c_device_id tps546d24_id[] = { + {"tps546d24", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tps546d24_id); + +static const struct of_device_id __maybe_unused tps546d24_of_match[] = { + {.compatible = "ti,tps546d24"}, + {} +}; +MODULE_DEVICE_TABLE(of, tps546d24_of_match); + +/* This is the driver that will be inserted */ +static struct i2c_driver tps546d24_driver = { + .driver = { + .name = "tps546d24", + .of_match_table = of_match_ptr(tps546d24_of_match), + }, + .probe_new = tps546d24_probe, + .id_table = tps546d24_id, +}; + +module_i2c_driver(tps546d24_driver); + +MODULE_AUTHOR("Duke Du <dukedu83@gmail.com>"); +MODULE_DESCRIPTION("PMBus driver for TI tps546d24"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 23ea3415f166..75fc770c9e40 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -370,7 +370,7 @@ static void ucd9000_probe_gpio(struct i2c_client *client, #ifdef CONFIG_DEBUG_FS static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer) { - int ret = pmbus_set_page(client, 0); + int ret = pmbus_set_page(client, 0, 0xff); if (ret < 0) return ret; @@ -487,8 +487,7 @@ static int ucd9000_init_debugfs(struct i2c_client *client, } #endif /* CONFIG_DEBUG_FS */ -static int ucd9000_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ucd9000_probe(struct i2c_client *client) { u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; struct ucd9000_data *data; @@ -523,12 +522,12 @@ static int ucd9000_probe(struct i2c_client *client, if (client->dev.of_node) chip = (enum chips)of_device_get_match_data(&client->dev); else - chip = id->driver_data; + chip = mid->driver_data; - if (chip != ucd9000 && chip != mid->driver_data) + if (chip != ucd9000 && strcmp(client->name, mid->name) != 0) dev_notice(&client->dev, "Device mismatch: Configured %s, detected %s\n", - id->name, mid->name); + client->name, mid->name); data = devm_kzalloc(&client->dev, sizeof(struct ucd9000_data), GFP_KERNEL); @@ -603,7 +602,7 @@ static int ucd9000_probe(struct i2c_client *client, ucd9000_probe_gpio(client, mid, data); - ret = pmbus_do_probe(client, mid, info); + ret = pmbus_do_probe(client, info); if (ret) return ret; @@ -621,8 +620,7 @@ static struct i2c_driver ucd9000_driver = { .name = "ucd9000", .of_match_table = of_match_ptr(ucd9000_of_match), }, - .probe = ucd9000_probe, - .remove = pmbus_do_remove, + .probe_new = ucd9000_probe, .id_table = ucd9000_id, }; @@ -631,3 +629,4 @@ module_i2c_driver(ucd9000_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index 7c04745a9709..3ad375a76f3e 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -71,8 +71,7 @@ static const struct of_device_id __maybe_unused ucd9200_of_match[] = { }; MODULE_DEVICE_TABLE(of, ucd9200_of_match); -static int ucd9200_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ucd9200_probe(struct i2c_client *client) { u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; struct pmbus_driver_info *info; @@ -106,12 +105,12 @@ static int ucd9200_probe(struct i2c_client *client, if (client->dev.of_node) chip = (enum chips)of_device_get_match_data(&client->dev); else - chip = id->driver_data; + chip = mid->driver_data; - if (chip != ucd9200 && chip != mid->driver_data) + if (chip != ucd9200 && strcmp(client->name, mid->name) != 0) dev_notice(&client->dev, "Device mismatch: Configured %s, detected %s\n", - id->name, mid->name); + client->name, mid->name); info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), GFP_KERNEL); @@ -149,7 +148,7 @@ static int ucd9200_probe(struct i2c_client *client, * This only affects the READ_IOUT and READ_TEMPERATURE2 registers. * READ_IOUT will return the sum of currents of all phases of a rail, * and READ_TEMPERATURE2 will return the maximum temperature detected - * for the the phases of the rail. + * for the phases of the rail. */ for (i = 0; i < info->pages; i++) { /* @@ -192,7 +191,7 @@ static int ucd9200_probe(struct i2c_client *client, if (mid->driver_data == ucd9240) info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12; - return pmbus_do_probe(client, mid, info); + return pmbus_do_probe(client, info); } /* This is the driver that will be inserted */ @@ -201,8 +200,7 @@ static struct i2c_driver ucd9200_driver = { .name = "ucd9200", .of_match_table = of_match_ptr(ucd9200_of_match), }, - .probe = ucd9200_probe, - .remove = pmbus_do_remove, + .probe_new = ucd9200_probe, .id_table = ucd9200_id, }; @@ -211,3 +209,4 @@ module_i2c_driver(ucd9200_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index 660556b89e9f..32bc7736d609 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -10,6 +10,8 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/regulator/driver.h> + #include "pmbus.h" #define XDPE122_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ @@ -18,7 +20,8 @@ #define XDPE122_AMD_625MV 0x10 /* AMD mode 6.25mV */ #define XDPE122_PAGE_NUM 2 -static int xdpe122_read_word_data(struct i2c_client *client, int page, int reg) +static int xdpe122_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); long val; @@ -29,7 +32,7 @@ static int xdpe122_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VOUT_OV_FAULT_LIMIT: case PMBUS_VOUT_UV_FAULT_LIMIT: - ret = pmbus_read_word_data(client, page, reg); + ret = pmbus_read_word_data(client, page, phase, reg); if (ret < 0) return ret; @@ -75,7 +78,22 @@ static int xdpe122_identify(struct i2c_client *client, struct pmbus_driver_info *info) { u8 vout_params; - int i, ret; + int i, ret, vout_mode; + + vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + switch (vout_mode >> 5) { + case 0: + info->format[PSC_VOLTAGE_OUT] = linear; + return 0; + case 1: + info->format[PSC_VOLTAGE_OUT] = vid; + info->read_word_data = xdpe122_read_word_data; + break; + default: + return -ENODEV; + } + } for (i = 0; i < XDPE122_PAGE_NUM; i++) { /* Read the register with VOUT scaling value.*/ @@ -106,10 +124,14 @@ static int xdpe122_identify(struct i2c_client *client, return 0; } +static const struct regulator_desc __maybe_unused xdpe122_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), + PMBUS_REGULATOR("vout", 1), +}; + static struct pmbus_driver_info xdpe122_info = { .pages = XDPE122_PAGE_NUM, .format[PSC_VOLTAGE_IN] = linear, - .format[PSC_VOLTAGE_OUT] = vid, .format[PSC_TEMPERATURE] = linear, .format[PSC_CURRENT_IN] = linear, .format[PSC_CURRENT_OUT] = linear, @@ -123,11 +145,13 @@ static struct pmbus_driver_info xdpe122_info = { PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, .identify = xdpe122_identify, - .read_word_data = xdpe122_read_word_data, +#if IS_ENABLED(CONFIG_SENSORS_XDPE122_REGULATOR) + .num_regulators = 2, + .reg_desc = xdpe122_reg_desc, +#endif }; -static int xdpe122_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int xdpe122_probe(struct i2c_client *client) { struct pmbus_driver_info *info; @@ -136,10 +160,11 @@ static int xdpe122_probe(struct i2c_client *client, if (!info) return -ENOMEM; - return pmbus_do_probe(client, id, info); + return pmbus_do_probe(client, info); } static const struct i2c_device_id xdpe122_id[] = { + {"xdpe11280", 0}, {"xdpe12254", 0}, {"xdpe12284", 0}, {} @@ -148,6 +173,7 @@ static const struct i2c_device_id xdpe122_id[] = { MODULE_DEVICE_TABLE(i2c, xdpe122_id); static const struct of_device_id __maybe_unused xdpe122_of_match[] = { + {.compatible = "infineon,xdpe11280"}, {.compatible = "infineon,xdpe12254"}, {.compatible = "infineon,xdpe12284"}, {} @@ -159,8 +185,7 @@ static struct i2c_driver xdpe122_driver = { .name = "xdpe12284", .of_match_table = of_match_ptr(xdpe122_of_match), }, - .probe = xdpe122_probe, - .remove = pmbus_do_remove, + .probe_new = xdpe122_probe, .id_table = xdpe122_id, }; @@ -169,3 +194,4 @@ module_i2c_driver(xdpe122_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c new file mode 100644 index 000000000000..b8a36ef73e45 --- /dev/null +++ b/drivers/hwmon/pmbus/xdpe152c4.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers + * + * Copyright (c) 2022 Infineon Technologies. All rights reserved. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define XDPE152_PAGE_NUM 2 + +static struct pmbus_driver_info xdpe152_info = { + .pages = XDPE152_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, +}; + +static int xdpe152_probe(struct i2c_client *client) +{ + struct pmbus_driver_info *info; + + info = devm_kmemdup(&client->dev, &xdpe152_info, sizeof(*info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id xdpe152_id[] = { + {"xdpe152c4", 0}, + {"xdpe15284", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, xdpe152_id); + +static const struct of_device_id __maybe_unused xdpe152_of_match[] = { + {.compatible = "infineon,xdpe152c4"}, + {.compatible = "infineon,xdpe15284"}, + {} +}; +MODULE_DEVICE_TABLE(of, xdpe152_of_match); + +static struct i2c_driver xdpe152_driver = { + .driver = { + .name = "xdpe152c4", + .of_match_table = of_match_ptr(xdpe152_of_match), + }, + .probe_new = xdpe152_probe, + .id_table = xdpe152_id, +}; + +module_i2c_driver(xdpe152_driver); + +MODULE_AUTHOR("Greg Schwendimann <greg.schwendimann@infineon.com>"); +MODULE_DESCRIPTION("PMBus driver for Infineon XDPE152 family"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 190b898e404a..e9df0c56d91e 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -18,7 +18,7 @@ #include "pmbus.h" enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, - zl9101, zl9117 }; + zl8802, zl9101, zl9117, zls1003, zls4009 }; struct zl6100_data { int id; @@ -34,6 +34,13 @@ struct zl6100_data { #define ZL6100_MFR_XTEMP_ENABLE BIT(7) +#define ZL8802_MFR_USER_GLOBAL_CONFIG 0xe9 +#define ZL8802_MFR_TMON_ENABLE BIT(12) +#define ZL8802_MFR_USER_CONFIG 0xd1 +#define ZL8802_MFR_XTEMP_ENABLE_2 BIT(1) +#define ZL8802_MFR_DDC_CONFIG 0xd3 +#define ZL8802_MFR_PHASES_MASK 0x0007 + #define MFR_VMON_OV_FAULT_LIMIT 0xf5 #define MFR_VMON_UV_FAULT_LIMIT 0xf6 #define MFR_READ_VMON 0xf7 @@ -125,13 +132,14 @@ static inline void zl6100_wait(const struct zl6100_data *data) } } -static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) +static int zl6100_read_word_data(struct i2c_client *client, int page, + int phase, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct zl6100_data *data = to_zl6100_data(info); int ret, vreg; - if (page > 0) + if (page >= info->pages) return -ENXIO; if (data->id == zl2005) { @@ -167,7 +175,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) } zl6100_wait(data); - ret = pmbus_read_word_data(client, page, vreg); + ret = pmbus_read_word_data(client, page, phase, vreg); data->access = ktime_get(); if (ret < 0) return ret; @@ -190,7 +198,7 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) struct zl6100_data *data = to_zl6100_data(info); int ret, status; - if (page > 0) + if (page >= info->pages) return -ENXIO; zl6100_wait(data); @@ -229,7 +237,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, struct zl6100_data *data = to_zl6100_data(info); int ret, vreg; - if (page > 0) + if (page >= info->pages) return -ENXIO; switch (reg) { @@ -270,7 +278,7 @@ static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) struct zl6100_data *data = to_zl6100_data(info); int ret; - if (page > 0) + if (page >= info->pages) return -ENXIO; zl6100_wait(data); @@ -286,6 +294,10 @@ static const struct i2c_device_id zl6100_id[] = { {"bmr462", zl2008}, {"bmr463", zl2008}, {"bmr464", zl2008}, + {"bmr465", zls4009}, + {"bmr466", zls1003}, + {"bmr467", zls4009}, + {"bmr469", zl8802}, {"zl2004", zl2004}, {"zl2005", zl2005}, {"zl2006", zl2006}, @@ -294,16 +306,18 @@ static const struct i2c_device_id zl6100_id[] = { {"zl2106", zl2106}, {"zl6100", zl6100}, {"zl6105", zl6105}, + {"zl8802", zl8802}, {"zl9101", zl9101}, {"zl9117", zl9117}, + {"zls1003", zls1003}, + {"zls4009", zls4009}, { } }; MODULE_DEVICE_TABLE(i2c, zl6100_id); -static int zl6100_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int zl6100_probe(struct i2c_client *client) { - int ret; + int ret, i; struct zl6100_data *data; struct pmbus_driver_info *info; u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; @@ -332,10 +346,10 @@ static int zl6100_probe(struct i2c_client *client, dev_err(&client->dev, "Unsupported device\n"); return -ENODEV; } - if (id->driver_data != mid->driver_data) + if (strcmp(client->name, mid->name) != 0) dev_notice(&client->dev, "Device mismatch: Configured %s, detected %s\n", - id->name, mid->name); + client->name, mid->name); data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data), GFP_KERNEL); @@ -367,18 +381,70 @@ static int zl6100_probe(struct i2c_client *client, | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; /* - * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage - * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon. + * ZL2004, ZL8802, ZL9101M, ZL9117M and ZLS4009 support monitoring + * an extra voltage (VMON for ZL2004, ZL8802 and ZLS4009, + * VDRV for ZL9101M and ZL9117M). Report it as vmon. */ - if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117) + if (data->id == zl2004 || data->id == zl8802 || data->id == zl9101 || + data->id == zl9117 || data->id == zls4009) info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; - ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); - if (ret < 0) - return ret; + /* + * ZL8802 has two outputs that can be used either independently or in + * a current sharing configuration. The driver uses the DDC_CONFIG + * register to check if the module is running with independent or + * shared outputs. If the module is in shared output mode, only one + * output voltage will be reported. + */ + if (data->id == zl8802) { + info->pages = 2; + info->func[0] |= PMBUS_HAVE_IIN; + + ret = i2c_smbus_read_word_data(client, ZL8802_MFR_DDC_CONFIG); + if (ret < 0) + return ret; + + data->access = ktime_get(); + zl6100_wait(data); + + if (ret & ZL8802_MFR_PHASES_MASK) + info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + else + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; - if (ret & ZL6100_MFR_XTEMP_ENABLE) - info->func[0] |= PMBUS_HAVE_TEMP2; + for (i = 0; i < 2; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + + data->access = ktime_get(); + zl6100_wait(data); + + ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL8802_MFR_XTEMP_ENABLE_2) + info->func[i] |= PMBUS_HAVE_TEMP2; + + data->access = ktime_get(); + zl6100_wait(data); + } + ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL8802_MFR_TMON_ENABLE) + info->func[0] |= PMBUS_HAVE_TEMP3; + } else { + ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL6100_MFR_XTEMP_ENABLE) + info->func[0] |= PMBUS_HAVE_TEMP2; + } data->access = ktime_get(); zl6100_wait(data); @@ -388,15 +454,14 @@ static int zl6100_probe(struct i2c_client *client, info->write_word_data = zl6100_write_word_data; info->write_byte = zl6100_write_byte; - return pmbus_do_probe(client, mid, info); + return pmbus_do_probe(client, info); } static struct i2c_driver zl6100_driver = { .driver = { .name = "zl6100", }, - .probe = zl6100_probe, - .remove = pmbus_do_remove, + .probe_new = zl6100_probe, .id_table = zl6100_id, }; @@ -405,3 +470,4 @@ module_i2c_driver(zl6100_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c index 65997421ee3c..f77dc6db31ac 100644 --- a/drivers/hwmon/powr1220.c +++ b/drivers/hwmon/powr1220.c @@ -4,7 +4,7 @@ * and monitor. Users can read all ADC inputs along with their labels * using the sysfs nodes. * - * Copyright (c) 2014 Echo360 http://www.echo360.com + * Copyright (c) 2014 Echo360 https://www.echo360.com * Scott Kanowitz <skanowitz@echo360.com> <scott.kanowitz@gmail.com> */ @@ -22,6 +22,8 @@ #define ADC_STEP_MV 2 #define ADC_MAX_LOW_MEASUREMENT_MV 2000 +enum powr1xxx_chips { powr1014, powr1220 }; + enum powr1220_regs { VMON_STATUS0, VMON_STATUS1, @@ -74,6 +76,7 @@ enum powr1220_adc_values { struct powr1220_data { struct i2c_client *client; struct mutex update_lock; + u8 max_channels; bool adc_valid[MAX_POWR1220_ADC_VALUES]; /* the next value is in jiffies */ unsigned long adc_last_updated[MAX_POWR1220_ADC_VALUES]; @@ -111,7 +114,7 @@ static int powr1220_read_adc(struct device *dev, int ch_num) mutex_lock(&data->update_lock); if (time_after(jiffies, data->adc_last_updated[ch_num] + HZ) || - !data->adc_valid[ch_num]) { + !data->adc_valid[ch_num]) { /* * figure out if we need to use the attenuator for * high inputs or inputs that we don't yet have a measurement @@ -119,12 +122,12 @@ static int powr1220_read_adc(struct device *dev, int ch_num) * max reading. */ if (data->adc_maxes[ch_num] > ADC_MAX_LOW_MEASUREMENT_MV || - data->adc_maxes[ch_num] == 0) + data->adc_maxes[ch_num] == 0) adc_range = 1 << 4; /* set the attenuator and mux */ result = i2c_smbus_write_byte_data(data->client, ADC_MUX, - adc_range | ch_num); + adc_range | ch_num); if (result) goto exit; @@ -167,138 +170,118 @@ exit: return result; } -/* Shows the voltage associated with the specified ADC channel */ -static ssize_t powr1220_voltage_show(struct device *dev, - struct device_attribute *dev_attr, - char *buf) +static umode_t +powr1220_is_visible(const void *data, enum hwmon_sensor_types type, u32 + attr, int channel) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); - int adc_val = powr1220_read_adc(dev, attr->index); - - if (adc_val < 0) - return adc_val; + struct powr1220_data *chip_data = (struct powr1220_data *)data; + + if (channel >= chip_data->max_channels) + return 0; + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_highest: + case hwmon_in_label: + return 0444; + default: + break; + } + break; + default: + break; + } - return sprintf(buf, "%d\n", adc_val); + return 0; } -/* Shows the maximum setting associated with the specified ADC channel */ -static ssize_t powr1220_max_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static int +powr1220_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); - struct powr1220_data *data = dev_get_drvdata(dev); + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_label: + *str = input_names[channel]; + return 0; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%d\n", data->adc_maxes[attr->index]); + return -EOPNOTSUPP; } -/* Shows the label associated with the specified ADC channel */ -static ssize_t powr1220_label_show(struct device *dev, - struct device_attribute *dev_attr, - char *buf) +static int +powr1220_read(struct device *dev, enum hwmon_sensor_types type, u32 + attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct powr1220_data *data = dev_get_drvdata(dev); + int ret; + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_input: + ret = powr1220_read_adc(dev, channel); + if (ret < 0) + return ret; + *val = ret; + break; + case hwmon_in_highest: + *val = data->adc_maxes[channel]; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; +} - return sprintf(buf, "%s\n", input_names[attr->index]); + return 0; } -static SENSOR_DEVICE_ATTR_RO(in0_input, powr1220_voltage, VMON1); -static SENSOR_DEVICE_ATTR_RO(in1_input, powr1220_voltage, VMON2); -static SENSOR_DEVICE_ATTR_RO(in2_input, powr1220_voltage, VMON3); -static SENSOR_DEVICE_ATTR_RO(in3_input, powr1220_voltage, VMON4); -static SENSOR_DEVICE_ATTR_RO(in4_input, powr1220_voltage, VMON5); -static SENSOR_DEVICE_ATTR_RO(in5_input, powr1220_voltage, VMON6); -static SENSOR_DEVICE_ATTR_RO(in6_input, powr1220_voltage, VMON7); -static SENSOR_DEVICE_ATTR_RO(in7_input, powr1220_voltage, VMON8); -static SENSOR_DEVICE_ATTR_RO(in8_input, powr1220_voltage, VMON9); -static SENSOR_DEVICE_ATTR_RO(in9_input, powr1220_voltage, VMON10); -static SENSOR_DEVICE_ATTR_RO(in10_input, powr1220_voltage, VMON11); -static SENSOR_DEVICE_ATTR_RO(in11_input, powr1220_voltage, VMON12); -static SENSOR_DEVICE_ATTR_RO(in12_input, powr1220_voltage, VCCA); -static SENSOR_DEVICE_ATTR_RO(in13_input, powr1220_voltage, VCCINP); - -static SENSOR_DEVICE_ATTR_RO(in0_highest, powr1220_max, VMON1); -static SENSOR_DEVICE_ATTR_RO(in1_highest, powr1220_max, VMON2); -static SENSOR_DEVICE_ATTR_RO(in2_highest, powr1220_max, VMON3); -static SENSOR_DEVICE_ATTR_RO(in3_highest, powr1220_max, VMON4); -static SENSOR_DEVICE_ATTR_RO(in4_highest, powr1220_max, VMON5); -static SENSOR_DEVICE_ATTR_RO(in5_highest, powr1220_max, VMON6); -static SENSOR_DEVICE_ATTR_RO(in6_highest, powr1220_max, VMON7); -static SENSOR_DEVICE_ATTR_RO(in7_highest, powr1220_max, VMON8); -static SENSOR_DEVICE_ATTR_RO(in8_highest, powr1220_max, VMON9); -static SENSOR_DEVICE_ATTR_RO(in9_highest, powr1220_max, VMON10); -static SENSOR_DEVICE_ATTR_RO(in10_highest, powr1220_max, VMON11); -static SENSOR_DEVICE_ATTR_RO(in11_highest, powr1220_max, VMON12); -static SENSOR_DEVICE_ATTR_RO(in12_highest, powr1220_max, VCCA); -static SENSOR_DEVICE_ATTR_RO(in13_highest, powr1220_max, VCCINP); - -static SENSOR_DEVICE_ATTR_RO(in0_label, powr1220_label, VMON1); -static SENSOR_DEVICE_ATTR_RO(in1_label, powr1220_label, VMON2); -static SENSOR_DEVICE_ATTR_RO(in2_label, powr1220_label, VMON3); -static SENSOR_DEVICE_ATTR_RO(in3_label, powr1220_label, VMON4); -static SENSOR_DEVICE_ATTR_RO(in4_label, powr1220_label, VMON5); -static SENSOR_DEVICE_ATTR_RO(in5_label, powr1220_label, VMON6); -static SENSOR_DEVICE_ATTR_RO(in6_label, powr1220_label, VMON7); -static SENSOR_DEVICE_ATTR_RO(in7_label, powr1220_label, VMON8); -static SENSOR_DEVICE_ATTR_RO(in8_label, powr1220_label, VMON9); -static SENSOR_DEVICE_ATTR_RO(in9_label, powr1220_label, VMON10); -static SENSOR_DEVICE_ATTR_RO(in10_label, powr1220_label, VMON11); -static SENSOR_DEVICE_ATTR_RO(in11_label, powr1220_label, VMON12); -static SENSOR_DEVICE_ATTR_RO(in12_label, powr1220_label, VCCA); -static SENSOR_DEVICE_ATTR_RO(in13_label, powr1220_label, VCCINP); - -static struct attribute *powr1220_attrs[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, - &sensor_dev_attr_in9_input.dev_attr.attr, - &sensor_dev_attr_in10_input.dev_attr.attr, - &sensor_dev_attr_in11_input.dev_attr.attr, - &sensor_dev_attr_in12_input.dev_attr.attr, - &sensor_dev_attr_in13_input.dev_attr.attr, - - &sensor_dev_attr_in0_highest.dev_attr.attr, - &sensor_dev_attr_in1_highest.dev_attr.attr, - &sensor_dev_attr_in2_highest.dev_attr.attr, - &sensor_dev_attr_in3_highest.dev_attr.attr, - &sensor_dev_attr_in4_highest.dev_attr.attr, - &sensor_dev_attr_in5_highest.dev_attr.attr, - &sensor_dev_attr_in6_highest.dev_attr.attr, - &sensor_dev_attr_in7_highest.dev_attr.attr, - &sensor_dev_attr_in8_highest.dev_attr.attr, - &sensor_dev_attr_in9_highest.dev_attr.attr, - &sensor_dev_attr_in10_highest.dev_attr.attr, - &sensor_dev_attr_in11_highest.dev_attr.attr, - &sensor_dev_attr_in12_highest.dev_attr.attr, - &sensor_dev_attr_in13_highest.dev_attr.attr, - - &sensor_dev_attr_in0_label.dev_attr.attr, - &sensor_dev_attr_in1_label.dev_attr.attr, - &sensor_dev_attr_in2_label.dev_attr.attr, - &sensor_dev_attr_in3_label.dev_attr.attr, - &sensor_dev_attr_in4_label.dev_attr.attr, - &sensor_dev_attr_in5_label.dev_attr.attr, - &sensor_dev_attr_in6_label.dev_attr.attr, - &sensor_dev_attr_in7_label.dev_attr.attr, - &sensor_dev_attr_in8_label.dev_attr.attr, - &sensor_dev_attr_in9_label.dev_attr.attr, - &sensor_dev_attr_in10_label.dev_attr.attr, - &sensor_dev_attr_in11_label.dev_attr.attr, - &sensor_dev_attr_in12_label.dev_attr.attr, - &sensor_dev_attr_in13_label.dev_attr.attr, +static const struct hwmon_channel_info *powr1220_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_HIGHEST | HWMON_I_LABEL), NULL }; -ATTRIBUTE_GROUPS(powr1220); +static const struct hwmon_ops powr1220_hwmon_ops = { + .read = powr1220_read, + .read_string = powr1220_read_string, + .is_visible = powr1220_is_visible, +}; + +static const struct hwmon_chip_info powr1220_chip_info = { + .ops = &powr1220_hwmon_ops, + .info = powr1220_info, +}; -static int powr1220_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id powr1220_ids[]; + +static int powr1220_probe(struct i2c_client *client) { struct powr1220_data *data; struct device *hwmon_dev; @@ -310,17 +293,30 @@ static int powr1220_probe(struct i2c_client *client, if (!data) return -ENOMEM; + switch (i2c_match_id(powr1220_ids, client)->driver_data) { + case powr1014: + data->max_channels = 10; + break; + default: + data->max_channels = 12; + break; + } + mutex_init(&data->update_lock); data->client = client; - hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, - client->name, data, powr1220_groups); + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, + client->name, + data, + &powr1220_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id powr1220_ids[] = { - { "powr1220", 0, }, + { "powr1014", powr1014, }, + { "powr1220", powr1220, }, { } }; @@ -331,7 +327,7 @@ static struct i2c_driver powr1220_driver = { .driver = { .name = "powr1220", }, - .probe = powr1220_probe, + .probe_new = powr1220_probe, .id_table = powr1220_ids, }; diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 30b7b3ea8836..83a347ca35da 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -8,7 +8,6 @@ */ #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/mutex.h> @@ -22,15 +21,33 @@ #define MAX_PWM 255 +struct pwm_fan_tach { + int irq; + atomic_t pulses; + unsigned int rpm; + u8 pulses_per_revolution; +}; + +enum pwm_fan_enable_mode { + pwm_off_reg_off, + pwm_disable_reg_enable, + pwm_enable_reg_enable, + pwm_disable_reg_disable, +}; + struct pwm_fan_ctx { + struct device *dev; + struct mutex lock; struct pwm_device *pwm; + struct pwm_state pwm_state; struct regulator *reg_en; + enum pwm_fan_enable_mode enable_mode; + bool regulator_enabled; + bool enabled; - int irq; - atomic_t pulses; - unsigned int rpm; - u8 pulses_per_revolution; + int tach_count; + struct pwm_fan_tach *tachs; ktime_t sample_start; struct timer_list rpm_timer; @@ -39,14 +56,17 @@ struct pwm_fan_ctx { unsigned int pwm_fan_max_state; unsigned int *pwm_fan_cooling_levels; struct thermal_cooling_device *cdev; + + struct hwmon_chip_info info; + struct hwmon_channel_info fan_channel; }; /* This handler assumes self resetting edge triggered interrupt. */ static irqreturn_t pulse_handler(int irq, void *dev_id) { - struct pwm_fan_ctx *ctx = dev_id; + struct pwm_fan_tach *tach = dev_id; - atomic_inc(&ctx->pulses); + atomic_inc(&tach->pulses); return IRQ_HANDLED; } @@ -54,39 +74,160 @@ static irqreturn_t pulse_handler(int irq, void *dev_id) static void sample_timer(struct timer_list *t) { struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer); - int pulses; - u64 tmp; + unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start); + int i; - pulses = atomic_read(&ctx->pulses); - atomic_sub(pulses, &ctx->pulses); - tmp = (u64)pulses * ktime_ms_delta(ktime_get(), ctx->sample_start) * 60; - do_div(tmp, ctx->pulses_per_revolution * 1000); - ctx->rpm = tmp; + if (delta) { + for (i = 0; i < ctx->tach_count; i++) { + struct pwm_fan_tach *tach = &ctx->tachs[i]; + int pulses; + + pulses = atomic_read(&tach->pulses); + atomic_sub(pulses, &tach->pulses); + tach->rpm = (unsigned int)(pulses * 1000 * 60) / + (tach->pulses_per_revolution * delta); + } + + ctx->sample_start = ktime_get(); + } - ctx->sample_start = ktime_get(); mod_timer(&ctx->rpm_timer, jiffies + HZ); } +static void pwm_fan_enable_mode_2_state(int enable_mode, + struct pwm_state *state, + bool *enable_regulator) +{ + switch (enable_mode) { + case pwm_disable_reg_enable: + /* disable pwm, keep regulator enabled */ + state->enabled = false; + *enable_regulator = true; + break; + case pwm_enable_reg_enable: + /* keep pwm and regulator enabled */ + state->enabled = true; + *enable_regulator = true; + break; + case pwm_off_reg_off: + case pwm_disable_reg_disable: + /* disable pwm and regulator */ + state->enabled = false; + *enable_regulator = false; + } +} + +static int pwm_fan_switch_power(struct pwm_fan_ctx *ctx, bool on) +{ + int ret = 0; + + if (!ctx->reg_en) + return ret; + + if (!ctx->regulator_enabled && on) { + ret = regulator_enable(ctx->reg_en); + if (ret == 0) + ctx->regulator_enabled = true; + } else if (ctx->regulator_enabled && !on) { + ret = regulator_disable(ctx->reg_en); + if (ret == 0) + ctx->regulator_enabled = false; + } + return ret; +} + +static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) +{ + struct pwm_state *state = &ctx->pwm_state; + int ret; + + if (ctx->enabled) + return 0; + + ret = pwm_fan_switch_power(ctx, true); + if (ret < 0) { + dev_err(ctx->dev, "failed to enable power supply\n"); + return ret; + } + + state->enabled = true; + ret = pwm_apply_state(ctx->pwm, state); + if (ret) { + dev_err(ctx->dev, "failed to enable PWM\n"); + goto disable_regulator; + } + + ctx->enabled = true; + + return 0; + +disable_regulator: + pwm_fan_switch_power(ctx, false); + return ret; +} + +static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) +{ + struct pwm_state *state = &ctx->pwm_state; + bool enable_regulator = false; + int ret; + + if (!ctx->enabled) + return 0; + + pwm_fan_enable_mode_2_state(ctx->enable_mode, + state, + &enable_regulator); + + state->enabled = false; + state->duty_cycle = 0; + ret = pwm_apply_state(ctx->pwm, state); + if (ret) { + dev_err(ctx->dev, "failed to disable PWM\n"); + return ret; + } + + pwm_fan_switch_power(ctx, enable_regulator); + + ctx->enabled = false; + + return 0; +} + static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { + struct pwm_state *state = &ctx->pwm_state; unsigned long period; int ret = 0; - struct pwm_state state = { }; - - mutex_lock(&ctx->lock); - if (ctx->pwm_value == pwm) - goto exit_set_pwm_err; - pwm_init_state(ctx->pwm, &state); - period = ctx->pwm->args.period; - state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); - state.enabled = pwm ? true : false; + if (pwm > 0) { + if (ctx->enable_mode == pwm_off_reg_off) + /* pwm-fan hard disabled */ + return 0; - ret = pwm_apply_state(ctx->pwm, &state); + period = state->period; + state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + ret = pwm_apply_state(ctx->pwm, state); + if (ret) + return ret; + ret = pwm_fan_power_on(ctx); + } else { + ret = pwm_fan_power_off(ctx); + } if (!ret) ctx->pwm_value = pwm; -exit_set_pwm_err: + + return ret; +} + +static int set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) +{ + int ret; + + mutex_lock(&ctx->lock); + ret = __set_pwm(ctx, pwm); mutex_unlock(&ctx->lock); + return ret; } @@ -101,70 +242,127 @@ static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm) ctx->pwm_fan_state = i; } -static ssize_t pwm_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int pwm_fan_update_enable(struct pwm_fan_ctx *ctx, long val) { - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - unsigned long pwm; - int ret; + int ret = 0; + int old_val; - if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) - return -EINVAL; + mutex_lock(&ctx->lock); - ret = __set_pwm(ctx, pwm); - if (ret) - return ret; + if (ctx->enable_mode == val) + goto out; - pwm_fan_update_state(ctx, pwm); - return count; -} + old_val = ctx->enable_mode; + ctx->enable_mode = val; -static ssize_t pwm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + if (val == 0) { + /* Disable pwm-fan unconditionally */ + if (ctx->enabled) + ret = __set_pwm(ctx, 0); + else + ret = pwm_fan_switch_power(ctx, false); + if (ret) + ctx->enable_mode = old_val; + pwm_fan_update_state(ctx, 0); + } else { + /* + * Change PWM and/or regulator state if currently disabled + * Nothing to do if currently enabled + */ + if (!ctx->enabled) { + struct pwm_state *state = &ctx->pwm_state; + bool enable_regulator = false; + + state->duty_cycle = 0; + pwm_fan_enable_mode_2_state(val, + state, + &enable_regulator); + + pwm_apply_state(ctx->pwm, state); + pwm_fan_switch_power(ctx, enable_regulator); + pwm_fan_update_state(ctx, 0); + } + } +out: + mutex_unlock(&ctx->lock); - return sprintf(buf, "%u\n", ctx->pwm_value); + return ret; } -static ssize_t rpm_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int pwm_fan_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + int ret; - return sprintf(buf, "%u\n", ctx->rpm); -} + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > MAX_PWM) + return -EINVAL; + ret = set_pwm(ctx, val); + if (ret) + return ret; + pwm_fan_update_state(ctx, val); + break; + case hwmon_pwm_enable: + if (val < 0 || val > 3) + ret = -EINVAL; + else + ret = pwm_fan_update_enable(ctx, val); -static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); -static SENSOR_DEVICE_ATTR_RO(fan1_input, rpm, 0); + return ret; + default: + return -EOPNOTSUPP; + } -static struct attribute *pwm_fan_attrs[] = { - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - NULL, -}; + return 0; +} -static umode_t pwm_fan_attrs_visible(struct kobject *kobj, struct attribute *a, - int n) +static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct device *dev = container_of(kobj, struct device, kobj); struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - /* Hide fan_input in case no interrupt is available */ - if (n == 1 && ctx->irq <= 0) + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + *val = ctx->pwm_value; + return 0; + case hwmon_pwm_enable: + *val = ctx->enable_mode; + return 0; + } + return -EOPNOTSUPP; + case hwmon_fan: + *val = ctx->tachs[channel].rpm; return 0; - return a->mode; + default: + return -ENOTSUPP; + } } -static const struct attribute_group pwm_fan_group = { - .attrs = pwm_fan_attrs, - .is_visible = pwm_fan_attrs_visible, -}; +static umode_t pwm_fan_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + return 0644; -static const struct attribute_group *pwm_fan_groups[] = { - &pwm_fan_group, - NULL, + case hwmon_fan: + return 0444; + + default: + return 0; + } +} + +static const struct hwmon_ops pwm_fan_hwmon_ops = { + .is_visible = pwm_fan_is_visible, + .read = pwm_fan_read, + .write = pwm_fan_write, }; /* thermal cooling device callbacks */ @@ -206,7 +404,7 @@ pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) if (state == ctx->pwm_fan_state) return 0; - ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]); + ret = set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]); if (ret) { dev_err(&cdev->device, "Cannot set pwm!\n"); return ret; @@ -264,16 +462,14 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, return 0; } -static void pwm_fan_regulator_disable(void *data) -{ - regulator_disable(data); -} - -static void pwm_fan_pwm_disable(void *__ctx) +static void pwm_fan_cleanup(void *__ctx) { struct pwm_fan_ctx *ctx = __ctx; - pwm_disable(ctx->pwm); + del_timer_sync(&ctx->rpm_timer); + /* Switch off everything */ + ctx->enable_mode = pwm_disable_reg_disable; + pwm_fan_power_off(ctx); } static int pwm_fan_probe(struct platform_device *pdev) @@ -283,8 +479,10 @@ static int pwm_fan_probe(struct platform_device *pdev) struct pwm_fan_ctx *ctx; struct device *hwmon; int ret; - struct pwm_state state = { }; - u32 ppr = 2; + const struct hwmon_channel_info **channels; + u32 *fan_channel_config; + int channel_count = 1; /* We always have a PWM channel. */ + int i; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -292,77 +490,125 @@ static int pwm_fan_probe(struct platform_device *pdev) mutex_init(&ctx->lock); - ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL); - if (IS_ERR(ctx->pwm)) { - ret = PTR_ERR(ctx->pwm); - - if (ret != -EPROBE_DEFER) - dev_err(dev, "Could not get PWM: %d\n", ret); - - return ret; - } + ctx->dev = &pdev->dev; + ctx->pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(ctx->pwm)) + return dev_err_probe(dev, PTR_ERR(ctx->pwm), "Could not get PWM\n"); platform_set_drvdata(pdev, ctx); - ctx->irq = platform_get_irq_optional(pdev, 0); - if (ctx->irq == -EPROBE_DEFER) - return ctx->irq; - ctx->reg_en = devm_regulator_get_optional(dev, "fan"); if (IS_ERR(ctx->reg_en)) { if (PTR_ERR(ctx->reg_en) != -ENODEV) return PTR_ERR(ctx->reg_en); ctx->reg_en = NULL; - } else { - ret = regulator_enable(ctx->reg_en); - if (ret) { - dev_err(dev, "Failed to enable fan supply: %d\n", ret); - return ret; - } - ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable, - ctx->reg_en); - if (ret) - return ret; } - ctx->pwm_value = MAX_PWM; + pwm_init_state(ctx->pwm, &ctx->pwm_state); + + /* + * set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned + * long. Check this here to prevent the fan running at a too low + * frequency. + */ + if (ctx->pwm_state.period > ULONG_MAX / MAX_PWM + 1) { + dev_err(dev, "Configured period too big\n"); + return -EINVAL; + } - /* Set duty cycle to maximum allowed and enable PWM output */ - pwm_init_state(ctx->pwm, &state); - state.duty_cycle = ctx->pwm->args.period - 1; - state.enabled = true; + ctx->enable_mode = pwm_disable_reg_enable; - ret = pwm_apply_state(ctx->pwm, &state); + /* + * Set duty cycle to maximum allowed and enable PWM output as well as + * the regulator. In case of error nothing is changed + */ + ret = set_pwm(ctx, MAX_PWM); if (ret) { dev_err(dev, "Failed to configure PWM: %d\n", ret); return ret; } timer_setup(&ctx->rpm_timer, sample_timer, 0); - ret = devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx); + ret = devm_add_action_or_reset(dev, pwm_fan_cleanup, ctx); if (ret) return ret; - of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr); - ctx->pulses_per_revolution = ppr; - if (!ctx->pulses_per_revolution) { - dev_err(dev, "pulses-per-revolution can't be zero.\n"); - return -EINVAL; + ctx->tach_count = platform_irq_count(pdev); + if (ctx->tach_count < 0) + return dev_err_probe(dev, ctx->tach_count, + "Could not get number of fan tachometer inputs\n"); + dev_dbg(dev, "%d fan tachometer inputs\n", ctx->tach_count); + + if (ctx->tach_count) { + channel_count++; /* We also have a FAN channel. */ + + ctx->tachs = devm_kcalloc(dev, ctx->tach_count, + sizeof(struct pwm_fan_tach), + GFP_KERNEL); + if (!ctx->tachs) + return -ENOMEM; + + ctx->fan_channel.type = hwmon_fan; + fan_channel_config = devm_kcalloc(dev, ctx->tach_count + 1, + sizeof(u32), GFP_KERNEL); + if (!fan_channel_config) + return -ENOMEM; + ctx->fan_channel.config = fan_channel_config; } - if (ctx->irq > 0) { - ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0, - pdev->name, ctx); - if (ret) { - dev_err(dev, "Failed to request interrupt: %d\n", ret); - return ret; + channels = devm_kcalloc(dev, channel_count + 1, + sizeof(struct hwmon_channel_info *), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE); + + for (i = 0; i < ctx->tach_count; i++) { + struct pwm_fan_tach *tach = &ctx->tachs[i]; + u32 ppr = 2; + + tach->irq = platform_get_irq(pdev, i); + if (tach->irq == -EPROBE_DEFER) + return tach->irq; + if (tach->irq > 0) { + ret = devm_request_irq(dev, tach->irq, pulse_handler, 0, + pdev->name, tach); + if (ret) { + dev_err(dev, + "Failed to request interrupt: %d\n", + ret); + return ret; + } } + + of_property_read_u32_index(dev->of_node, + "pulses-per-revolution", + i, + &ppr); + tach->pulses_per_revolution = ppr; + if (!tach->pulses_per_revolution) { + dev_err(dev, "pulses-per-revolution can't be zero.\n"); + return -EINVAL; + } + + fan_channel_config[i] = HWMON_F_INPUT; + + dev_dbg(dev, "tach%d: irq=%d, pulses_per_revolution=%d\n", + i, tach->irq, tach->pulses_per_revolution); + } + + if (ctx->tach_count > 0) { ctx->sample_start = ktime_get(); mod_timer(&ctx->rpm_timer, jiffies + HZ); + + channels[1] = &ctx->fan_channel; } - hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan", - ctx, pwm_fan_groups); + ctx->info.ops = &pwm_fan_hwmon_ops; + ctx->info.info = channels; + + hwmon = devm_hwmon_device_register_with_info(dev, "pwmfan", + ctx, &ctx->info, NULL); if (IS_ERR(hwmon)) { dev_err(dev, "Failed to register hwmon device\n"); return PTR_ERR(hwmon); @@ -384,34 +630,6 @@ static int pwm_fan_probe(struct platform_device *pdev) return ret; } ctx->cdev = cdev; - thermal_cdev_update(cdev); - } - - return 0; -} - -static int pwm_fan_disable(struct device *dev) -{ - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - struct pwm_args args; - int ret; - - pwm_get_args(ctx->pwm, &args); - - if (ctx->pwm_value) { - ret = pwm_config(ctx->pwm, 0, args.period); - if (ret < 0) - return ret; - - pwm_disable(ctx->pwm); - } - - if (ctx->reg_en) { - ret = regulator_disable(ctx->reg_en); - if (ret) { - dev_err(dev, "Failed to disable fan supply: %d\n", ret); - return ret; - } } return 0; @@ -419,43 +637,26 @@ static int pwm_fan_disable(struct device *dev) static void pwm_fan_shutdown(struct platform_device *pdev) { - pwm_fan_disable(&pdev->dev); + struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev); + + pwm_fan_cleanup(ctx); } -#ifdef CONFIG_PM_SLEEP static int pwm_fan_suspend(struct device *dev) { - return pwm_fan_disable(dev); + struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + + return pwm_fan_power_off(ctx); } static int pwm_fan_resume(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - struct pwm_args pargs; - unsigned long duty; - int ret; - if (ctx->reg_en) { - ret = regulator_enable(ctx->reg_en); - if (ret) { - dev_err(dev, "Failed to enable fan supply: %d\n", ret); - return ret; - } - } - - if (ctx->pwm_value == 0) - return 0; - - pwm_get_args(ctx->pwm, &pargs); - duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM); - ret = pwm_config(ctx->pwm, duty, pargs.period); - if (ret) - return ret; - return pwm_enable(ctx->pwm); + return set_pwm(ctx, ctx->pwm_value); } -#endif -static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume); static const struct of_device_id of_pwm_fan_match[] = { { .compatible = "pwm-fan", }, @@ -468,7 +669,7 @@ static struct platform_driver pwm_fan_driver = { .shutdown = pwm_fan_shutdown, .driver = { .name = "pwm-fan", - .pm = &pwm_fan_pm, + .pm = pm_sleep_ptr(&pwm_fan_pm), .of_match_table = of_pwm_fan_match, }, }; diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index d3a64a35f7a9..1650d3b4c26e 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -7,6 +7,7 @@ * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com> */ #include <linux/device.h> +#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/hwmon.h> #include <linux/module.h> @@ -52,7 +53,7 @@ static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) else dev_info(data->hwmon_dev, "Voltage normalised\n"); - sysfs_notify(&data->hwmon_dev->kobj, NULL, "in0_lcrit_alarm"); + hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0); } static void get_values_poll(struct work_struct *work) @@ -106,6 +107,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpi_hwmon_data *data; + int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -118,28 +120,22 @@ static int rpi_hwmon_probe(struct platform_device *pdev) data, &rpi_chip_info, NULL); + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); - INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll); + ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work, + get_values_poll); + if (ret) + return ret; platform_set_drvdata(pdev, data); - if (!PTR_ERR_OR_ZERO(data->hwmon_dev)) - schedule_delayed_work(&data->get_values_poll_work, 2 * HZ); - - return PTR_ERR_OR_ZERO(data->hwmon_dev); -} - -static int rpi_hwmon_remove(struct platform_device *pdev) -{ - struct rpi_hwmon_data *data = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&data->get_values_poll_work); + schedule_delayed_work(&data->get_values_poll_work, 2 * HZ); return 0; } static struct platform_driver rpi_hwmon_driver = { .probe = rpi_hwmon_probe, - .remove = rpi_hwmon_remove, .driver = { .name = "raspberrypi-hwmon", }, diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index b490fe3d2ee8..70ae665db477 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -20,7 +20,7 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -#include <plat/adc.h> +#include <linux/soc/samsung/s3c-adc.h> #include <linux/platform_data/hwmon-s3c.h> struct s3c_hwmon_attr { @@ -166,7 +166,7 @@ static ssize_t s3c_hwmon_ch_show(struct device *dev, ret *= cfg->mult; ret = DIV_ROUND_CLOSEST(ret, cfg->div); - return snprintf(buf, PAGE_SIZE, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } /** @@ -187,7 +187,7 @@ static ssize_t s3c_hwmon_label_show(struct device *dev, cfg = pdata->in[sen_attr->index]; - return snprintf(buf, PAGE_SIZE, "%s\n", cfg->name); + return sysfs_emit(buf, "%s\n", cfg->name); } /** diff --git a/drivers/hwmon/sbrmi.c b/drivers/hwmon/sbrmi.c new file mode 100644 index 000000000000..7bf0c3fba75f --- /dev/null +++ b/drivers/hwmon/sbrmi.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * sbrmi.c - hwmon driver for a SB-RMI mailbox + * compliant AMD SoC device. + * + * Copyright (C) 2020-2021 Advanced Micro Devices, Inc. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> + +/* Do not allow setting negative power limit */ +#define SBRMI_PWR_MIN 0 +/* Mask for Status Register bit[1] */ +#define SW_ALERT_MASK 0x2 + +/* Software Interrupt for triggering */ +#define START_CMD 0x80 +#define TRIGGER_MAILBOX 0x01 + +/* + * SB-RMI supports soft mailbox service request to MP1 (power management + * firmware) through SBRMI inbound/outbound message registers. + * SB-RMI message IDs + */ +enum sbrmi_msg_id { + SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1, + SBRMI_WRITE_PKG_PWR_LIMIT, + SBRMI_READ_PKG_PWR_LIMIT, + SBRMI_READ_PKG_MAX_PWR_LIMIT, +}; + +/* SB-RMI registers */ +enum sbrmi_reg { + SBRMI_CTRL = 0x01, + SBRMI_STATUS, + SBRMI_OUTBNDMSG0 = 0x30, + SBRMI_OUTBNDMSG1, + SBRMI_OUTBNDMSG2, + SBRMI_OUTBNDMSG3, + SBRMI_OUTBNDMSG4, + SBRMI_OUTBNDMSG5, + SBRMI_OUTBNDMSG6, + SBRMI_OUTBNDMSG7, + SBRMI_INBNDMSG0, + SBRMI_INBNDMSG1, + SBRMI_INBNDMSG2, + SBRMI_INBNDMSG3, + SBRMI_INBNDMSG4, + SBRMI_INBNDMSG5, + SBRMI_INBNDMSG6, + SBRMI_INBNDMSG7, + SBRMI_SW_INTERRUPT, +}; + +/* Each client has this additional data */ +struct sbrmi_data { + struct i2c_client *client; + struct mutex lock; + u32 pwr_limit_max; +}; + +struct sbrmi_mailbox_msg { + u8 cmd; + bool read; + u32 data_in; + u32 data_out; +}; + +static int sbrmi_enable_alert(struct i2c_client *client) +{ + int ctrl; + + /* + * Enable the SB-RMI Software alert status + * by writing 0 to bit 4 of Control register(0x1) + */ + ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL); + if (ctrl < 0) + return ctrl; + + if (ctrl & 0x10) { + ctrl &= ~0x10; + return i2c_smbus_write_byte_data(client, + SBRMI_CTRL, ctrl); + } + + return 0; +} + +static int rmi_mailbox_xfer(struct sbrmi_data *data, + struct sbrmi_mailbox_msg *msg) +{ + int i, ret, retry = 10; + int sw_status; + u8 byte; + + mutex_lock(&data->lock); + + /* Indicate firmware a command is to be serviced */ + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_INBNDMSG7, START_CMD); + if (ret < 0) + goto exit_unlock; + + /* Write the command to SBRMI::InBndMsg_inst0 */ + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_INBNDMSG0, msg->cmd); + if (ret < 0) + goto exit_unlock; + + /* + * For both read and write the initiator (BMC) writes + * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1] + * SBRMI_x3C(MSB):SBRMI_x39(LSB) + */ + for (i = 0; i < 4; i++) { + byte = (msg->data_in >> i * 8) & 0xff; + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_INBNDMSG1 + i, byte); + if (ret < 0) + goto exit_unlock; + } + + /* + * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to + * perform the requested read or write command + */ + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX); + if (ret < 0) + goto exit_unlock; + + /* + * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate + * an ALERT (if enabled) to initiator (BMC) to indicate completion + * of the requested command + */ + do { + sw_status = i2c_smbus_read_byte_data(data->client, + SBRMI_STATUS); + if (sw_status < 0) { + ret = sw_status; + goto exit_unlock; + } + if (sw_status & SW_ALERT_MASK) + break; + usleep_range(50, 100); + } while (retry--); + + if (retry < 0) { + dev_err(&data->client->dev, + "Firmware fail to indicate command completion\n"); + ret = -EIO; + goto exit_unlock; + } + + /* + * For a read operation, the initiator (BMC) reads the firmware + * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1] + * {SBRMI_x34(MSB):SBRMI_x31(LSB)}. + */ + if (msg->read) { + for (i = 0; i < 4; i++) { + ret = i2c_smbus_read_byte_data(data->client, + SBRMI_OUTBNDMSG1 + i); + if (ret < 0) + goto exit_unlock; + msg->data_out |= ret << i * 8; + } + } + + /* + * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the + * ALERT to initiator + */ + ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS, + sw_status | SW_ALERT_MASK); + +exit_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + int ret; + + if (type != hwmon_power) + return -EINVAL; + + msg.read = true; + switch (attr) { + case hwmon_power_input: + msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap: + msg.cmd = SBRMI_READ_PKG_PWR_LIMIT; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap_max: + msg.data_out = data->pwr_limit_max; + ret = 0; + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + /* hwmon power attributes are in microWatt */ + *val = (long)msg.data_out * 1000; + return ret; +} + +static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + + if (type != hwmon_power && attr != hwmon_power_cap) + return -EINVAL; + /* + * hwmon power attributes are in microWatt + * mailbox read/write is in mWatt + */ + val /= 1000; + + val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max); + + msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; + msg.data_in = val; + msg.read = false; + + return rmi_mailbox_xfer(data, &msg); +} + +static umode_t sbrmi_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_cap_max: + return 0444; + case hwmon_power_cap: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *sbrmi_info[] = { + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), + NULL +}; + +static const struct hwmon_ops sbrmi_hwmon_ops = { + .is_visible = sbrmi_is_visible, + .read = sbrmi_read, + .write = sbrmi_write, +}; + +static const struct hwmon_chip_info sbrmi_chip_info = { + .ops = &sbrmi_hwmon_ops, + .info = sbrmi_info, +}; + +static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) +{ + struct sbrmi_mailbox_msg msg = { 0 }; + int ret; + + msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; + msg.read = true; + ret = rmi_mailbox_xfer(data, &msg); + if (ret < 0) + return ret; + data->pwr_limit_max = msg.data_out; + + return ret; +} + +static int sbrmi_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct sbrmi_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->lock); + + /* Enable alert for SB-RMI sequence */ + ret = sbrmi_enable_alert(client); + if (ret < 0) + return ret; + + /* Cache maximum power limit */ + ret = sbrmi_get_max_pwr_limit(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &sbrmi_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id sbrmi_id[] = { + {"sbrmi", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sbrmi_id); + +static const struct of_device_id __maybe_unused sbrmi_of_match[] = { + { + .compatible = "amd,sbrmi", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sbrmi_of_match); + +static struct i2c_driver sbrmi_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "sbrmi", + .of_match_table = of_match_ptr(sbrmi_of_match), + }, + .probe = sbrmi_probe, + .id_table = sbrmi_id, +}; + +module_i2c_driver(sbrmi_driver); + +MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>"); +MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c new file mode 100644 index 000000000000..e35357c48b8e --- /dev/null +++ b/drivers/hwmon/sbtsi_temp.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * sbtsi_temp.c - hwmon driver for a SBI Temperature Sensor Interface (SB-TSI) + * compliant AMD SoC temperature device. + * + * Copyright (c) 2020, Google Inc. + * Copyright (c) 2020, Kun Yi <kunyi@google.com> + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/hwmon.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/of.h> + +/* + * SB-TSI registers only support SMBus byte data access. "_INT" registers are + * the integer part of a temperature value or limit, and "_DEC" registers are + * corresponding decimal parts. + */ +#define SBTSI_REG_TEMP_INT 0x01 /* RO */ +#define SBTSI_REG_STATUS 0x02 /* RO */ +#define SBTSI_REG_CONFIG 0x03 /* RO */ +#define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */ +#define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */ +#define SBTSI_REG_TEMP_DEC 0x10 /* RW */ +#define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */ +#define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */ + +#define SBTSI_CONFIG_READ_ORDER_SHIFT 5 + +#define SBTSI_TEMP_MIN 0 +#define SBTSI_TEMP_MAX 255875 + +/* Each client has this additional data */ +struct sbtsi_data { + struct i2c_client *client; + struct mutex lock; +}; + +/* + * From SB-TSI spec: CPU temperature readings and limit registers encode the + * temperature in increments of 0.125 from 0 to 255.875. The "high byte" + * register encodes the base-2 of the integer portion, and the upper 3 bits of + * the "low byte" encode in base-2 the decimal portion. + * + * e.g. INT=0x19, DEC=0x20 represents 25.125 degrees Celsius + * + * Therefore temperature in millidegree Celsius = + * (INT + DEC / 256) * 1000 = (INT * 8 + DEC / 32) * 125 + */ +static inline int sbtsi_reg_to_mc(s32 integer, s32 decimal) +{ + return ((integer << 3) + (decimal >> 5)) * 125; +} + +/* + * Inversely, given temperature in millidegree Celsius + * INT = (TEMP / 125) / 8 + * DEC = ((TEMP / 125) % 8) * 32 + * Caller have to make sure temp doesn't exceed 255875, the max valid value. + */ +static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal) +{ + temp /= 125; + *integer = temp >> 3; + *decimal = (temp & 0x7) << 5; +} + +static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sbtsi_data *data = dev_get_drvdata(dev); + s32 temp_int, temp_dec; + int err; + + switch (attr) { + case hwmon_temp_input: + /* + * ReadOrder bit specifies the reading order of integer and + * decimal part of CPU temp for atomic reads. If bit == 0, + * reading integer part triggers latching of the decimal part, + * so integer part should be read first. If bit == 1, read + * order should be reversed. + */ + err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); + if (err < 0) + return err; + + mutex_lock(&data->lock); + if (err & BIT(SBTSI_CONFIG_READ_ORDER_SHIFT)) { + temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); + temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); + } else { + temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); + temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); + } + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); + temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT); + temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC); + mutex_unlock(&data->lock); + break; + case hwmon_temp_min: + mutex_lock(&data->lock); + temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT); + temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC); + mutex_unlock(&data->lock); + break; + default: + return -EINVAL; + } + + + if (temp_int < 0) + return temp_int; + if (temp_dec < 0) + return temp_dec; + + *val = sbtsi_reg_to_mc(temp_int, temp_dec); + + return 0; +} + +static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sbtsi_data *data = dev_get_drvdata(dev); + int reg_int, reg_dec, err; + u8 temp_int, temp_dec; + + switch (attr) { + case hwmon_temp_max: + reg_int = SBTSI_REG_TEMP_HIGH_INT; + reg_dec = SBTSI_REG_TEMP_HIGH_DEC; + break; + case hwmon_temp_min: + reg_int = SBTSI_REG_TEMP_LOW_INT; + reg_dec = SBTSI_REG_TEMP_LOW_DEC; + break; + default: + return -EINVAL; + } + + val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX); + sbtsi_mc_to_reg(val, &temp_int, &temp_dec); + + mutex_lock(&data->lock); + err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int); + if (err) + goto exit; + + err = i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec); +exit: + mutex_unlock(&data->lock); + return err; +} + +static umode_t sbtsi_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_min: + return 0644; + case hwmon_temp_max: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *sbtsi_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX), + NULL +}; + +static const struct hwmon_ops sbtsi_hwmon_ops = { + .is_visible = sbtsi_is_visible, + .read = sbtsi_read, + .write = sbtsi_write, +}; + +static const struct hwmon_chip_info sbtsi_chip_info = { + .ops = &sbtsi_hwmon_ops, + .info = sbtsi_info, +}; + +static int sbtsi_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct sbtsi_data *data; + + data = devm_kzalloc(dev, sizeof(struct sbtsi_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->lock); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &sbtsi_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id sbtsi_id[] = { + {"sbtsi", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sbtsi_id); + +static const struct of_device_id __maybe_unused sbtsi_of_match[] = { + { + .compatible = "amd,sbtsi", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sbtsi_of_match); + +static struct i2c_driver sbtsi_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "sbtsi", + .of_match_table = of_match_ptr(sbtsi_of_match), + }, + .probe = sbtsi_probe, + .id_table = sbtsi_id, +}; + +module_i2c_driver(sbtsi_driver); + +MODULE_AUTHOR("Kun Yi <kunyi@google.com>"); +MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 039644263101..25fbbd4c9a2b 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c @@ -7,12 +7,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/platform_device.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include "sch56xx-common.h" @@ -52,6 +52,9 @@ static const u16 SCH5627_REG_FAN[SCH5627_NO_FANS] = { static const u16 SCH5627_REG_FAN_MIN[SCH5627_NO_FANS] = { 0x62, 0x64, 0x66, 0x68 }; +static const u16 SCH5627_REG_PWM_MAP[SCH5627_NO_FANS] = { + 0xA0, 0xA1, 0xA2, 0xA3 }; + static const u16 SCH5627_REG_IN_MSB[SCH5627_NO_IN] = { 0x22, 0x23, 0x24, 0x25, 0x189 }; static const u16 SCH5627_REG_IN_LSN[SCH5627_NO_IN] = { @@ -65,8 +68,6 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = { struct sch5627_data { unsigned short addr; - struct device *hwmon_dev; - struct sch56xx_watchdog_data *watchdog; u8 control; u8 temp_max[SCH5627_NO_TEMPS]; u8 temp_crit[SCH5627_NO_TEMPS]; @@ -74,66 +75,96 @@ struct sch5627_data { struct mutex update_lock; unsigned long last_battery; /* In jiffies */ - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ + char temp_valid; /* !=0 if following fields are valid */ + char fan_valid; + char in_valid; + unsigned long temp_last_updated; /* In jiffies */ + unsigned long fan_last_updated; + unsigned long in_last_updated; u16 temp[SCH5627_NO_TEMPS]; u16 fan[SCH5627_NO_FANS]; u16 in[SCH5627_NO_IN]; }; -static struct sch5627_data *sch5627_update_device(struct device *dev) +static int sch5627_update_temp(struct sch5627_data *data) { - struct sch5627_data *data = dev_get_drvdata(dev); - struct sch5627_data *ret = data; + int ret = 0; int i, val; mutex_lock(&data->update_lock); - /* Trigger a Vbat voltage measurement every 5 minutes */ - if (time_after(jiffies, data->last_battery + 300 * HZ)) { - sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, - data->control | 0x10); - data->last_battery = jiffies; - } - /* Cache the values for 1 second */ - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + if (time_after(jiffies, data->temp_last_updated + HZ) || !data->temp_valid) { for (i = 0; i < SCH5627_NO_TEMPS; i++) { - val = sch56xx_read_virtual_reg12(data->addr, - SCH5627_REG_TEMP_MSB[i], - SCH5627_REG_TEMP_LSN[i], - SCH5627_REG_TEMP_HIGH_NIBBLE[i]); + val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_TEMP_MSB[i], + SCH5627_REG_TEMP_LSN[i], + SCH5627_REG_TEMP_HIGH_NIBBLE[i]); if (unlikely(val < 0)) { - ret = ERR_PTR(val); + ret = val; goto abort; } data->temp[i] = val; } + data->temp_last_updated = jiffies; + data->temp_valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} +static int sch5627_update_fan(struct sch5627_data *data) +{ + int ret = 0; + int i, val; + + mutex_lock(&data->update_lock); + + /* Cache the values for 1 second */ + if (time_after(jiffies, data->fan_last_updated + HZ) || !data->fan_valid) { for (i = 0; i < SCH5627_NO_FANS; i++) { - val = sch56xx_read_virtual_reg16(data->addr, - SCH5627_REG_FAN[i]); + val = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_FAN[i]); if (unlikely(val < 0)) { - ret = ERR_PTR(val); + ret = val; goto abort; } data->fan[i] = val; } + data->fan_last_updated = jiffies; + data->fan_valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int sch5627_update_in(struct sch5627_data *data) +{ + int ret = 0; + int i, val; + + mutex_lock(&data->update_lock); + /* Trigger a Vbat voltage measurement every 5 minutes */ + if (time_after(jiffies, data->last_battery + 300 * HZ)) { + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10); + data->last_battery = jiffies; + } + + /* Cache the values for 1 second */ + if (time_after(jiffies, data->in_last_updated + HZ) || !data->in_valid) { for (i = 0; i < SCH5627_NO_IN; i++) { - val = sch56xx_read_virtual_reg12(data->addr, - SCH5627_REG_IN_MSB[i], - SCH5627_REG_IN_LSN[i], - SCH5627_REG_IN_HIGH_NIBBLE[i]); + val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_IN_MSB[i], + SCH5627_REG_IN_LSN[i], + SCH5627_REG_IN_HIGH_NIBBLE[i]); if (unlikely(val < 0)) { - ret = ERR_PTR(val); + ret = val; goto abort; } data->in[i] = val; } - - data->last_updated = jiffies; - data->valid = 1; + data->in_last_updated = jiffies; + data->in_valid = 1; } abort: mutex_unlock(&data->update_lock); @@ -192,269 +223,205 @@ static int reg_to_rpm(u16 reg) return 5400540 / reg; } -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf) +static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, + int channel) { - return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); -} + if (type == hwmon_pwm && attr == hwmon_pwm_auto_channels_temp) + return 0644; -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - int val; - - if (IS_ERR(data)) - return PTR_ERR(data); - - val = reg_to_temp(data->temp[attr->index]); - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return 0444; } -static ssize_t temp_fault_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return snprintf(buf, PAGE_SIZE, "%d\n", data->temp[attr->index] == 0); -} - -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sch5627_data *data = dev_get_drvdata(dev); - int val; - - val = reg_to_temp_limit(data->temp_max[attr->index]); - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t temp_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = dev_get_drvdata(dev); - int val; - - val = reg_to_temp_limit(data->temp_crit[attr->index]); - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - int val; - - if (IS_ERR(data)) - return PTR_ERR(data); - - val = reg_to_rpm(data->fan[attr->index]); - if (val < 0) - return val; + int ret; + + switch (type) { + case hwmon_temp: + ret = sch5627_update_temp(data); + if (ret < 0) + return ret; + switch (attr) { + case hwmon_temp_input: + *val = reg_to_temp(data->temp[channel]); + return 0; + case hwmon_temp_max: + *val = reg_to_temp_limit(data->temp_max[channel]); + return 0; + case hwmon_temp_crit: + *val = reg_to_temp_limit(data->temp_crit[channel]); + return 0; + case hwmon_temp_fault: + *val = (data->temp[channel] == 0); + return 0; + default: + break; + } + break; + case hwmon_fan: + ret = sch5627_update_fan(data); + if (ret < 0) + return ret; + switch (attr) { + case hwmon_fan_input: + ret = reg_to_rpm(data->fan[channel]); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_fan_min: + ret = reg_to_rpm(data->fan_min[channel]); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_fan_fault: + *val = (data->fan[channel] == 0xffff); + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_auto_channels_temp: + mutex_lock(&data->update_lock); + ret = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel]); + mutex_unlock(&data->update_lock); + + if (ret < 0) + return ret; + + *val = ret; + + return 0; + default: + break; + } + break; + case hwmon_in: + ret = sch5627_update_in(data); + if (ret < 0) + return ret; + switch (attr) { + case hwmon_in_input: + *val = DIV_ROUND_CLOSEST(data->in[channel] * SCH5627_REG_IN_FACTOR[channel], + 10000); + return 0; + default: + break; + } + break; + default: + break; + } - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return -EOPNOTSUPP; } -static ssize_t fan_fault_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int sch5627_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_label: + *str = SCH5627_IN_LABELS[channel]; + return 0; + default: + break; + } + break; + default: + break; + } - return snprintf(buf, PAGE_SIZE, "%d\n", - data->fan[attr->index] == 0xffff); + return -EOPNOTSUPP; } -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sch5627_data *data = dev_get_drvdata(dev); - int val = reg_to_rpm(data->fan_min[attr->index]); - if (val < 0) - return val; - - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t in_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - int val; - - if (IS_ERR(data)) - return PTR_ERR(data); - - val = DIV_ROUND_CLOSEST( - data->in[attr->index] * SCH5627_REG_IN_FACTOR[attr->index], - 10000); - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t in_label_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int ret; + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_auto_channels_temp: + /* registers are 8 bit wide */ + if (val > U8_MAX || val < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = sch56xx_write_virtual_reg(data->addr, SCH5627_REG_PWM_MAP[channel], + val); + mutex_unlock(&data->update_lock); + + return ret; + default: + break; + } + break; + default: + break; + } - return snprintf(buf, PAGE_SIZE, "%s\n", - SCH5627_IN_LABELS[attr->index]); + return -EOPNOTSUPP; } -static DEVICE_ATTR_RO(name); -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_input, temp, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_input, temp, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_input, temp, 7); -static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_fault, temp_fault, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_fault, temp_fault, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_fault, temp_fault, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_fault, temp_fault, 7); -static SENSOR_DEVICE_ATTR_RO(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_max, temp_max, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_max, temp_max, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_max, temp_max, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_max, temp_max, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_max, temp_max, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_max, temp_max, 7); -static SENSOR_DEVICE_ATTR_RO(temp1_crit, temp_crit, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_crit, temp_crit, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_crit, temp_crit, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_crit, temp_crit, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_crit, temp_crit, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_crit, temp_crit, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_crit, temp_crit, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_crit, temp_crit, 7); - -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3); -static SENSOR_DEVICE_ATTR_RO(fan1_fault, fan_fault, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_fault, fan_fault, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_fault, fan_fault, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_fault, fan_fault, 3); -static SENSOR_DEVICE_ATTR_RO(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_min, fan_min, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_min, fan_min, 3); - -static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4); -static SENSOR_DEVICE_ATTR_RO(in0_label, in_label, 0); -static SENSOR_DEVICE_ATTR_RO(in1_label, in_label, 1); -static SENSOR_DEVICE_ATTR_RO(in2_label, in_label, 2); -static SENSOR_DEVICE_ATTR_RO(in3_label, in_label, 3); - -static struct attribute *sch5627_attributes[] = { - &dev_attr_name.attr, - - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp6_input.dev_attr.attr, - &sensor_dev_attr_temp7_input.dev_attr.attr, - &sensor_dev_attr_temp8_input.dev_attr.attr, - &sensor_dev_attr_temp1_fault.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, - &sensor_dev_attr_temp6_fault.dev_attr.attr, - &sensor_dev_attr_temp7_fault.dev_attr.attr, - &sensor_dev_attr_temp8_fault.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, - &sensor_dev_attr_temp6_max.dev_attr.attr, - &sensor_dev_attr_temp7_max.dev_attr.attr, - &sensor_dev_attr_temp8_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - &sensor_dev_attr_temp4_crit.dev_attr.attr, - &sensor_dev_attr_temp5_crit.dev_attr.attr, - &sensor_dev_attr_temp6_crit.dev_attr.attr, - &sensor_dev_attr_temp7_crit.dev_attr.attr, - &sensor_dev_attr_temp8_crit.dev_attr.attr, - - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan1_fault.dev_attr.attr, - &sensor_dev_attr_fan2_fault.dev_attr.attr, - &sensor_dev_attr_fan3_fault.dev_attr.attr, - &sensor_dev_attr_fan4_fault.dev_attr.attr, - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan3_min.dev_attr.attr, - &sensor_dev_attr_fan4_min.dev_attr.attr, - - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in0_label.dev_attr.attr, - &sensor_dev_attr_in1_label.dev_attr.attr, - &sensor_dev_attr_in2_label.dev_attr.attr, - &sensor_dev_attr_in3_label.dev_attr.attr, - /* No in4_label as in4 is a generic input pin */ +static const struct hwmon_ops sch5627_ops = { + .is_visible = sch5627_is_visible, + .read = sch5627_read, + .read_string = sch5627_read_string, + .write = sch5627_write, +}; +static const struct hwmon_channel_info *sch5627_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT + ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT + ), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP + ), + 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 + ), NULL }; -static const struct attribute_group sch5627_group = { - .attrs = sch5627_attributes, +static const struct hwmon_chip_info sch5627_chip_info = { + .ops = &sch5627_ops, + .info = sch5627_info, }; -static int sch5627_remove(struct platform_device *pdev) -{ - struct sch5627_data *data = platform_get_drvdata(pdev); - - if (data->watchdog) - sch56xx_watchdog_unregister(data->watchdog); - - if (data->hwmon_dev) - hwmon_device_unregister(data->hwmon_dev); - - sysfs_remove_group(&pdev->dev.kobj, &sch5627_group); - - return 0; -} - static int sch5627_probe(struct platform_device *pdev) { struct sch5627_data *data; + struct device *hwmon_dev; int err, build_code, build_id, hwmon_rev, val; data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data), @@ -467,72 +434,58 @@ static int sch5627_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + if (val != SCH5627_HWMON_ID) { pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "hwmon", val, SCH5627_HWMON_ID); - err = -ENODEV; - goto error; + return -ENODEV; } val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + if (val != SCH5627_COMPANY_ID) { pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "company", val, SCH5627_COMPANY_ID); - err = -ENODEV; - goto error; + return -ENODEV; } val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + if (val != SCH5627_PRIMARY_ID) { pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "primary", val, SCH5627_PRIMARY_ID); - err = -ENODEV; - goto error; + return -ENODEV; } build_code = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_BUILD_CODE); - if (build_code < 0) { - err = build_code; - goto error; - } + if (build_code < 0) + return build_code; build_id = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_BUILD_ID); - if (build_id < 0) { - err = build_id; - goto error; - } + if (build_id < 0) + return build_id; hwmon_rev = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_REV); - if (hwmon_rev < 0) { - err = hwmon_rev; - goto error; - } + if (hwmon_rev < 0) + return hwmon_rev; val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + data->control = val; if (!(data->control & 0x01)) { pr_err("hardware monitoring not enabled\n"); - err = -ENODEV; - goto error; + return -ENODEV; } /* Trigger a Vbat voltage measurement, so that we get a valid reading the first time we read Vbat */ @@ -546,42 +499,39 @@ static int sch5627_probe(struct platform_device *pdev) */ err = sch5627_read_limits(data); if (err) - goto error; + return err; pr_info("found %s chip at %#hx\n", DEVNAME, data->addr); pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", build_code, build_id, hwmon_rev); - /* Register sysfs interface files */ - err = sysfs_create_group(&pdev->dev.kobj, &sch5627_group); - if (err) - goto error; - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - data->hwmon_dev = NULL; - goto error; - } + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, DEVNAME, data, + &sch5627_chip_info, NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); /* Note failing to register the watchdog is not a fatal error */ - data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, - (build_code << 24) | (build_id << 8) | hwmon_rev, - &data->update_lock, 1); + sch56xx_watchdog_register(&pdev->dev, data->addr, + (build_code << 24) | (build_id << 8) | hwmon_rev, + &data->update_lock, 1); return 0; - -error: - sch5627_remove(pdev); - return err; } +static const struct platform_device_id sch5627_device_id[] = { + { + .name = "sch5627", + }, + { } +}; +MODULE_DEVICE_TABLE(platform, sch5627_device_id); + static struct platform_driver sch5627_driver = { .driver = { .name = DRVNAME, }, .probe = sch5627_probe, - .remove = sch5627_remove, + .id_table = sch5627_device_id, }; module_platform_driver(sch5627_driver); diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index 200bb2bfc986..269757bc3a9e 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -54,10 +55,9 @@ static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = { struct sch5636_data { unsigned short addr; struct device *hwmon_dev; - struct sch56xx_watchdog_data *watchdog; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[SCH5636_NO_INS]; u8 temp_val[SCH5636_NO_TEMPS]; @@ -141,7 +141,7 @@ static struct sch5636_data *sch5636_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; abort: mutex_unlock(&data->update_lock); return ret; @@ -160,7 +160,7 @@ static int reg_to_rpm(u16 reg) static ssize_t name_show(struct device *dev, struct device_attribute *devattr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); + return sysfs_emit(buf, "%s\n", DEVNAME); } static ssize_t in_value_show(struct device *dev, @@ -176,7 +176,7 @@ static ssize_t in_value_show(struct device *dev, val = DIV_ROUND_CLOSEST( data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index], 255); - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t in_label_show(struct device *dev, @@ -184,8 +184,8 @@ static ssize_t in_label_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - return snprintf(buf, PAGE_SIZE, "%s\n", - SCH5636_IN_LABELS[attr->index]); + return sysfs_emit(buf, "%s\n", + SCH5636_IN_LABELS[attr->index]); } static ssize_t temp_value_show(struct device *dev, @@ -199,7 +199,7 @@ static ssize_t temp_value_show(struct device *dev, return PTR_ERR(data); val = (data->temp_val[attr->index] - 64) * 1000; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t temp_fault_show(struct device *dev, @@ -213,7 +213,7 @@ static ssize_t temp_fault_show(struct device *dev, return PTR_ERR(data); val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t temp_alarm_show(struct device *dev, @@ -227,7 +227,7 @@ static ssize_t temp_alarm_show(struct device *dev, return PTR_ERR(data); val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t fan_value_show(struct device *dev, @@ -244,7 +244,7 @@ static ssize_t fan_value_show(struct device *dev, if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t fan_fault_show(struct device *dev, @@ -258,7 +258,7 @@ static ssize_t fan_fault_show(struct device *dev, return PTR_ERR(data); val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t fan_alarm_show(struct device *dev, @@ -272,7 +272,7 @@ static ssize_t fan_alarm_show(struct device *dev, return PTR_ERR(data); val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static struct sensor_device_attribute sch5636_attr[] = { @@ -372,9 +372,6 @@ static int sch5636_remove(struct platform_device *pdev) struct sch5636_data *data = platform_get_drvdata(pdev); int i; - if (data->watchdog) - sch56xx_watchdog_unregister(data->watchdog); - if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); @@ -495,9 +492,8 @@ static int sch5636_probe(struct platform_device *pdev) } /* Note failing to register the watchdog is not a fatal error */ - data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, - (revision[0] << 8) | revision[1], - &data->update_lock, 0); + sch56xx_watchdog_register(&pdev->dev, data->addr, (revision[0] << 8) | revision[1], + &data->update_lock, 0); return 0; @@ -506,12 +502,21 @@ error: return err; } +static const struct platform_device_id sch5636_device_id[] = { + { + .name = "sch5636", + }, + { } +}; +MODULE_DEVICE_TABLE(platform, sch5636_device_id); + static struct platform_driver sch5636_driver = { .driver = { .name = DRVNAME, }, .probe = sch5636_probe, .remove = sch5636_remove, + .id_table = sch5636_device_id, }; module_platform_driver(sch5636_driver); diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index 6c84780e358e..de3a0886c2f7 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -7,8 +7,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/dmi.h> #include <linux/err.h> #include <linux/io.h> #include <linux/acpi.h> @@ -19,9 +21,12 @@ #include <linux/slab.h> #include "sch56xx-common.h" -/* Insmod parameters */ -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool ignore_dmi; +module_param(ignore_dmi, bool, 0); +MODULE_PARM_DESC(ignore_dmi, "Omit DMI check for supported devices (default=0)"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -134,7 +139,7 @@ static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) /* EM Interface Polling "Algorithm" */ for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { if (i >= max_busy_polls) - msleep(1); + usleep_range(1000, 2000); /* Read Interrupt source Register */ val = inb(addr + 8); /* Write Clear the interrupt source bits */ @@ -378,8 +383,8 @@ static const struct watchdog_ops watchdog_ops = { .set_timeout = watchdog_set_timeout, }; -struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, - u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) +void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, + struct mutex *io_lock, int check_enabled) { struct sch56xx_watchdog_data *data; int err, control, output_enable; @@ -393,23 +398,22 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, mutex_unlock(io_lock); if (control < 0) - return NULL; + return; if (output_enable < 0) - return NULL; + return; if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) { pr_warn("Watchdog not enabled by BIOS, not registering\n"); - return NULL; + return; } - data = kzalloc(sizeof(struct sch56xx_watchdog_data), GFP_KERNEL); + data = devm_kzalloc(parent, sizeof(struct sch56xx_watchdog_data), GFP_KERNEL); if (!data) - return NULL; + return; data->addr = addr; data->io_lock = io_lock; - strlcpy(data->wdinfo.identity, "sch56xx watchdog", - sizeof(data->wdinfo.identity)); + strscpy(data->wdinfo.identity, "sch56xx watchdog", sizeof(data->wdinfo.identity)); data->wdinfo.firmware_version = revision; data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT; if (!nowayout) @@ -421,10 +425,9 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, data->wddev.timeout = 60; data->wddev.min_timeout = 1; data->wddev.max_timeout = 255 * 60; - if (nowayout) - set_bit(WDOG_NO_WAY_OUT, &data->wddev.status); + watchdog_set_nowayout(&data->wddev, nowayout); if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) - set_bit(WDOG_ACTIVE, &data->wddev.status); + set_bit(WDOG_HW_RUNNING, &data->wddev.status); /* Since the watchdog uses a downcounter there is no register to read the BIOS set timeout from (if any was set at all) -> @@ -438,24 +441,14 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, data->watchdog_output_enable = output_enable; watchdog_set_drvdata(&data->wddev, data); - err = watchdog_register_device(&data->wddev); + err = devm_watchdog_register_device(parent, &data->wddev); if (err) { pr_err("Registering watchdog chardev: %d\n", err); - kfree(data); - return NULL; + devm_kfree(parent, data); } - - return data; } EXPORT_SYMBOL(sch56xx_watchdog_register); -void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) -{ - watchdog_unregister_device(&data->wddev); - kfree(data); -} -EXPORT_SYMBOL(sch56xx_watchdog_unregister); - /* * platform dev find, add and remove functions */ @@ -516,44 +509,80 @@ static int __init sch56xx_device_add(int address, const char *name) struct resource res = { .start = address, .end = address + REGION_LENGTH - 1, + .name = name, .flags = IORESOURCE_IO, }; int err; - sch56xx_pdev = platform_device_alloc(name, address); - if (!sch56xx_pdev) - return -ENOMEM; - - res.name = sch56xx_pdev->name; err = acpi_check_resource_conflict(&res); if (err) - goto exit_device_put; - - err = platform_device_add_resources(sch56xx_pdev, &res, 1); - if (err) { - pr_err("Device resource addition failed\n"); - goto exit_device_put; - } + return err; - err = platform_device_add(sch56xx_pdev); - if (err) { - pr_err("Device addition failed\n"); - goto exit_device_put; - } + sch56xx_pdev = platform_device_register_simple(name, -1, &res, 1); - return 0; + return PTR_ERR_OR_ZERO(sch56xx_pdev); +} -exit_device_put: - platform_device_put(sch56xx_pdev); +static const struct dmi_system_id sch56xx_dmi_override_table[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS W380"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO P710"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO E9900"), + }, + }, + { } +}; - return err; -} +/* For autoloading only */ +static const struct dmi_system_id sch56xx_dmi_table[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + }, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, sch56xx_dmi_table); static int __init sch56xx_init(void) { - int address; const char *name = NULL; + int address; + + if (!ignore_dmi) { + if (!dmi_check_system(sch56xx_dmi_table)) + return -ENODEV; + + if (!dmi_check_system(sch56xx_dmi_override_table)) { + /* + * Some machines like the Esprimo P720 and Esprimo C700 have + * onboard devices named " Antiope"/" Theseus" instead of + * "Antiope"/"Theseus", so we need to check for both. + */ + if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) && + !dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) && + !dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) && + !dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL)) + return -ENODEV; + } + } + /* + * Some devices like the Esprimo C700 have both onboard devices, + * so we still have to check manually + */ address = sch56xx_find(0x4e, &name); if (address < 0) address = sch56xx_find(0x2e, &name); diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h index 75eb73617cf2..e907d9da0dd5 100644 --- a/drivers/hwmon/sch56xx-common.h +++ b/drivers/hwmon/sch56xx-common.h @@ -14,6 +14,6 @@ int sch56xx_read_virtual_reg16(u16 addr, u16 reg); int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, int high_nibble); -struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, - u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); +void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, + struct mutex *io_lock, int check_enabled); void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index 286d3cfda7de..e192f0c67146 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -2,7 +2,7 @@ /* * System Control and Management Interface(SCMI) based hwmon sensor driver * - * Copyright (C) 2018 ARM Ltd. + * Copyright (C) 2018-2021 ARM Ltd. * Sudeep Holla <sudeep.holla@arm.com> */ @@ -13,11 +13,18 @@ #include <linux/sysfs.h> #include <linux/thermal.h> +static const struct scmi_sensor_proto_ops *sensor_ops; + struct scmi_sensors { - const struct scmi_handle *handle; + const struct scmi_protocol_handle *ph; const struct scmi_sensor_info **info[hwmon_max]; }; +struct scmi_thermal_sensor { + const struct scmi_protocol_handle *ph; + const struct scmi_sensor_info *info; +}; + static inline u64 __pow10(u8 x) { u64 r = 1; @@ -30,7 +37,7 @@ static inline u64 __pow10(u8 x) static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value) { - s8 scale = sensor->scale; + int scale = sensor->scale; u64 f; switch (sensor->type) { @@ -62,17 +69,14 @@ static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value) return 0; } -static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) +static int scmi_hwmon_read_scaled_value(const struct scmi_protocol_handle *ph, + const struct scmi_sensor_info *sensor, + long *val) { int ret; u64 value; - const struct scmi_sensor_info *sensor; - struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev); - const struct scmi_handle *h = scmi_sensors->handle; - sensor = *(scmi_sensors->info[type] + channel); - ret = h->sensor_ops->reading_get(h, sensor->id, &value); + ret = sensor_ops->reading_get(ph, sensor->id, &value); if (ret) return ret; @@ -83,6 +87,17 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, return ret; } +static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + const struct scmi_sensor_info *sensor; + struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev); + + sensor = *(scmi_sensors->info[type] + channel); + + return scmi_hwmon_read_scaled_value(scmi_sensors->ph, sensor, val); +} + static int scmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, const char **str) @@ -121,6 +136,25 @@ static struct hwmon_chip_info scmi_chip_info = { .info = NULL, }; +static int scmi_hwmon_thermal_get_temp(struct thermal_zone_device *tz, + int *temp) +{ + int ret; + long value; + struct scmi_thermal_sensor *th_sensor = tz->devdata; + + ret = scmi_hwmon_read_scaled_value(th_sensor->ph, th_sensor->info, + &value); + if (!ret) + *temp = value; + + return ret; +} + +static const struct thermal_zone_device_ops scmi_hwmon_thermal_ops = { + .get_temp = scmi_hwmon_thermal_get_temp, +}; + static int scmi_hwmon_add_chan_info(struct hwmon_channel_info *scmi_hwmon_chan, struct device *dev, int num, enum hwmon_sensor_types type, u32 config) @@ -147,8 +181,7 @@ static enum hwmon_sensor_types scmi_types[] = { [ENERGY] = hwmon_energy, }; -static u32 hwmon_attributes[] = { - [hwmon_chip] = HWMON_C_REGISTER_TZ, +static u32 hwmon_attributes[hwmon_max] = { [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, @@ -156,6 +189,43 @@ static u32 hwmon_attributes[] = { [hwmon_energy] = HWMON_E_INPUT | HWMON_E_LABEL, }; +static int scmi_thermal_sensor_register(struct device *dev, + const struct scmi_protocol_handle *ph, + const struct scmi_sensor_info *sensor) +{ + struct scmi_thermal_sensor *th_sensor; + struct thermal_zone_device *tzd; + + th_sensor = devm_kzalloc(dev, sizeof(*th_sensor), GFP_KERNEL); + if (!th_sensor) + return -ENOMEM; + + th_sensor->ph = ph; + th_sensor->info = sensor; + + /* + * Try to register a temperature sensor with the Thermal Framework: + * skip sensors not defined as part of any thermal zone (-ENODEV) but + * report any other errors related to misconfigured zones/sensors. + */ + tzd = devm_thermal_of_zone_register(dev, th_sensor->info->id, th_sensor, + &scmi_hwmon_thermal_ops); + if (IS_ERR(tzd)) { + devm_kfree(dev, th_sensor); + + if (PTR_ERR(tzd) != -ENODEV) + return PTR_ERR(tzd); + + dev_dbg(dev, "Sensor '%s' not attached to any thermal zone.\n", + sensor->name); + } else { + dev_dbg(dev, "Sensor '%s' attached to thermal zone ID:%d\n", + sensor->name, tzd->id); + } + + return 0; +} + static int scmi_hwmon_probe(struct scmi_device *sdev) { int i, idx; @@ -163,17 +233,22 @@ static int scmi_hwmon_probe(struct scmi_device *sdev) enum hwmon_sensor_types type; struct scmi_sensors *scmi_sensors; const struct scmi_sensor_info *sensor; - int nr_count[hwmon_max] = {0}, nr_types = 0; + int nr_count[hwmon_max] = {0}, nr_types = 0, nr_count_temp = 0; const struct hwmon_chip_info *chip_info; struct device *hwdev, *dev = &sdev->dev; struct hwmon_channel_info *scmi_hwmon_chan; const struct hwmon_channel_info **ptr_scmi_ci; const struct scmi_handle *handle = sdev->handle; + struct scmi_protocol_handle *ph; - if (!handle || !handle->sensor_ops) + if (!handle) return -ENODEV; - nr_sensors = handle->sensor_ops->count_get(handle); + sensor_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_SENSOR, &ph); + if (IS_ERR(sensor_ops)) + return PTR_ERR(sensor_ops); + + nr_sensors = sensor_ops->count_get(ph); if (!nr_sensors) return -EIO; @@ -181,10 +256,10 @@ static int scmi_hwmon_probe(struct scmi_device *sdev) if (!scmi_sensors) return -ENOMEM; - scmi_sensors->handle = handle; + scmi_sensors->ph = ph; for (i = 0; i < nr_sensors; i++) { - sensor = handle->sensor_ops->info_get(handle, i); + sensor = sensor_ops->info_get(ph, i); if (!sensor) return -EINVAL; @@ -203,7 +278,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev) } if (nr_count[hwmon_temp]) - nr_count[hwmon_chip]++, nr_types++; + nr_count_temp = nr_count[hwmon_temp]; scmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*scmi_hwmon_chan), GFP_KERNEL); @@ -234,7 +309,7 @@ static int scmi_hwmon_probe(struct scmi_device *sdev) } for (i = nr_sensors - 1; i >= 0 ; i--) { - sensor = handle->sensor_ops->info_get(handle, i); + sensor = sensor_ops->info_get(ph, i); if (!sensor) continue; @@ -254,8 +329,31 @@ static int scmi_hwmon_probe(struct scmi_device *sdev) hwdev = devm_hwmon_device_register_with_info(dev, "scmi_sensors", scmi_sensors, chip_info, NULL); + if (IS_ERR(hwdev)) + return PTR_ERR(hwdev); - return PTR_ERR_OR_ZERO(hwdev); + for (i = 0; i < nr_count_temp; i++) { + int ret; + + sensor = *(scmi_sensors->info[hwmon_temp] + i); + if (!sensor) + continue; + + /* + * Warn on any misconfiguration related to thermal zones but + * bail out of probing only on memory errors. + */ + ret = scmi_thermal_sensor_register(dev, ph, sensor); + if (ret) { + if (ret == -ENOMEM) + return ret; + dev_warn(dev, + "Thermal zone misconfigured for %s. err=%d\n", + sensor->name, ret); + } + } + + return 0; } static const struct scmi_device_id scmi_id_table[] = { diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 25aac40f2764..4d75385f7d5e 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -62,9 +62,9 @@ static void scpi_scale_reading(u64 *value, struct sensor_data *sensor) } } -static int scpi_read_temp(void *dev, int *temp) +static int scpi_read_temp(struct thermal_zone_device *tz, int *temp) { - struct scpi_thermal_zone *zone = dev; + struct scpi_thermal_zone *zone = tz->devdata; struct scpi_sensors *scpi_sensors = zone->scpi_sensors; struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops; struct sensor_data *sensor = &scpi_sensors->data[zone->sensor_id]; @@ -99,6 +99,15 @@ scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf) scpi_scale_reading(&value, sensor); + /* + * Temperature sensor values are treated as signed values based on + * observation even though that is not explicitly specified, and + * because an unsigned u64 temperature does not really make practical + * sense especially when the temperature is below zero degrees Celsius. + */ + if (sensor->info.class == TEMPERATURE) + return sprintf(buf, "%lld\n", (s64)value); + return sprintf(buf, "%llu\n", value); } @@ -112,7 +121,7 @@ scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%s\n", sensor->info.name); } -static const struct thermal_zone_of_device_ops scpi_sensor_ops = { +static const struct thermal_zone_device_ops scpi_sensor_ops = { .get_temp = scpi_read_temp, }; @@ -132,7 +141,6 @@ static int scpi_hwmon_probe(struct platform_device *pdev) struct scpi_ops *scpi_ops; struct device *hwdev, *dev = &pdev->dev; struct scpi_sensors *scpi_sensors; - const struct of_device_id *of_id; int idx, ret; scpi_ops = get_scpi_ops(); @@ -162,12 +170,11 @@ static int scpi_hwmon_probe(struct platform_device *pdev) scpi_sensors->scpi_ops = scpi_ops; - of_id = of_match_device(scpi_of_match, &pdev->dev); - if (!of_id) { + scale = of_device_get_match_data(&pdev->dev); + if (!scale) { dev_err(&pdev->dev, "Unable to initialize scpi-hwmon data\n"); return -ENODEV; } - scale = of_id->data; for (i = 0, idx = 0; i < nr_sensors; i++) { struct sensor_data *sensor = &scpi_sensors->data[idx]; @@ -268,10 +275,10 @@ static int scpi_hwmon_probe(struct platform_device *pdev) zone->sensor_id = i; zone->scpi_sensors = scpi_sensors; - z = devm_thermal_zone_of_sensor_register(dev, - sensor->info.sensor_id, - zone, - &scpi_sensor_ops); + z = devm_thermal_of_zone_register(dev, + sensor->info.sensor_id, + zone, + &scpi_sensor_ops); /* * The call to thermal_zone_of_sensor_register returns * an error for sensors that are not associated with diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 7f4a63959730..ae4d14257a11 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1020,25 +1020,20 @@ err_release_reg: static int sht15_remove(struct platform_device *pdev) { struct sht15_data *data = platform_get_drvdata(pdev); + int ret; - /* - * Make sure any reads from the device are done and - * prevent new ones beginning - */ - mutex_lock(&data->read_lock); - if (sht15_soft_reset(data)) { - mutex_unlock(&data->read_lock); - return -EFAULT; - } hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group); + + ret = sht15_soft_reset(data); + if (ret) + dev_err(&pdev->dev, "Failed to reset device (%pe)\n", ERR_PTR(ret)); + if (!IS_ERR(data->reg)) { regulator_unregister_notifier(data->reg, &data->nb); regulator_disable(data->reg); } - mutex_unlock(&data->read_lock); - return 0; } diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index bc70c8332d9a..e23dbf287233 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.com> * - * Data sheet available at http://www.sensirion.com/file/datasheet_sht21 + * Data sheet available at https://www.sensirion.com/file/datasheet_sht21 */ #include <linux/module.h> @@ -41,7 +41,7 @@ struct sht21 { unsigned long last_update; int temperature; int humidity; - char valid; + bool valid; char eic[18]; }; @@ -105,7 +105,7 @@ static int sht21_update_measurements(struct device *dev) goto out; sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret); sht21->last_update = jiffies; - sht21->valid = 1; + sht21->valid = true; } out: mutex_unlock(&sht21->lock); @@ -250,8 +250,7 @@ static struct attribute *sht21_attrs[] = { ATTRIBUTE_GROUPS(sht21); -static int sht21_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sht21_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -286,7 +285,7 @@ MODULE_DEVICE_TABLE(i2c, sht21_id); static struct i2c_driver sht21_driver = { .driver.name = "sht21", - .probe = sht21_probe, + .probe_new = sht21_probe, .id_table = sht21_id, }; diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index 7364764baaeb..3f279aa1cee5 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -662,8 +662,9 @@ static struct attribute *sts3x_attrs[] = { ATTRIBUTE_GROUPS(sht3x); ATTRIBUTE_GROUPS(sts3x); -static int sht3x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id sht3x_ids[]; + +static int sht3x_probe(struct i2c_client *client) { int ret; struct sht3x_data *data; @@ -715,7 +716,7 @@ static int sht3x_probe(struct i2c_client *client, if (ret) return ret; - if (id->driver_data == sts3x) + if (i2c_match_id(sht3x_ids, client)->driver_data == sts3x) attribute_groups = sts3x_groups; else attribute_groups = sht3x_groups; @@ -742,7 +743,7 @@ MODULE_DEVICE_TABLE(i2c, sht3x_ids); static struct i2c_driver sht3x_i2c_driver = { .driver.name = "sht3x", - .probe = sht3x_probe, + .probe_new = sht3x_probe, .id_table = sht3x_ids, }; diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c new file mode 100644 index 000000000000..13ac2d8f22c7 --- /dev/null +++ b/drivers/hwmon/sht4x.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) Linumiz 2021 + * + * sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor + * + * Author: Navin Sankar Velliangiri <navin@linumiz.com> + */ + +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/jiffies.h> +#include <linux/module.h> + +/* + * Poll intervals (in milliseconds) + */ +#define SHT4X_MIN_POLL_INTERVAL 2000 + +/* + * I2C command delays (in microseconds) + */ +#define SHT4X_MEAS_DELAY_HPM 8200 /* see t_MEAS,h in datasheet */ +#define SHT4X_DELAY_EXTRA 10000 + +/* + * Command Bytes + */ +#define SHT4X_CMD_MEASURE_HPM 0b11111101 +#define SHT4X_CMD_RESET 0b10010100 + +#define SHT4X_CMD_LEN 1 +#define SHT4X_CRC8_LEN 1 +#define SHT4X_WORD_LEN 2 +#define SHT4X_RESPONSE_LENGTH 6 +#define SHT4X_CRC8_POLYNOMIAL 0x31 +#define SHT4X_CRC8_INIT 0xff +#define SHT4X_MIN_TEMPERATURE -45000 +#define SHT4X_MAX_TEMPERATURE 125000 +#define SHT4X_MIN_HUMIDITY 0 +#define SHT4X_MAX_HUMIDITY 100000 + +DECLARE_CRC8_TABLE(sht4x_crc8_table); + +/** + * struct sht4x_data - All the data required to operate an SHT4X chip + * @client: the i2c client associated with the SHT4X + * @lock: a mutex that is used to prevent parallel access to the i2c client + * @update_interval: the minimum poll interval + * @last_updated: the previous time that the SHT4X was polled + * @temperature: the latest temperature value received from the SHT4X + * @humidity: the latest humidity value received from the SHT4X + */ +struct sht4x_data { + struct i2c_client *client; + struct mutex lock; /* atomic read data updates */ + bool valid; /* validity of fields below */ + long update_interval; /* in milli-seconds */ + long last_updated; /* in jiffies */ + s32 temperature; + s32 humidity; +}; + +/** + * sht4x_read_values() - read and parse the raw data from the SHT4X + * @sht4x_data: the struct sht4x_data to use for the lock + * Return: 0 if successful, -ERRNO if not + */ +static int sht4x_read_values(struct sht4x_data *data) +{ + int ret = 0; + u16 t_ticks, rh_ticks; + unsigned long next_update; + struct i2c_client *client = data->client; + u8 crc; + u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM}; + u8 raw_data[SHT4X_RESPONSE_LENGTH]; + + mutex_lock(&data->lock); + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval); + + if (data->valid && time_before_eq(jiffies, next_update)) + goto unlock; + + ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); + if (ret < 0) + goto unlock; + + usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA); + + ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH); + if (ret != SHT4X_RESPONSE_LENGTH) { + if (ret >= 0) + ret = -ENODATA; + goto unlock; + } + + t_ticks = raw_data[0] << 8 | raw_data[1]; + rh_ticks = raw_data[3] << 8 | raw_data[4]; + + crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE); + if (crc != raw_data[2]) { + dev_err(&client->dev, "data integrity check failed\n"); + ret = -EIO; + goto unlock; + } + + crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE); + if (crc != raw_data[5]) { + dev_err(&client->dev, "data integrity check failed\n"); + ret = -EIO; + goto unlock; + } + + data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000; + data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000; + data->last_updated = jiffies; + data->valid = true; + ret = 0; + +unlock: + mutex_unlock(&data->lock); + return ret; +} + +static ssize_t sht4x_interval_write(struct sht4x_data *data, long val) +{ + data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, INT_MAX); + + return 0; +} + +/* sht4x_interval_read() - read the minimum poll interval in milliseconds */ +static size_t sht4x_interval_read(struct sht4x_data *data, long *val) +{ + *val = data->update_interval; + return 0; +} + +/* sht4x_temperature1_read() - read the temperature in millidegrees */ +static int sht4x_temperature1_read(struct sht4x_data *data, long *val) +{ + int ret; + + ret = sht4x_read_values(data); + if (ret < 0) + return ret; + + *val = data->temperature; + + return 0; +} + +/* sht4x_humidity1_read() - read a relative humidity in millipercent */ +static int sht4x_humidity1_read(struct sht4x_data *data, long *val) +{ + int ret; + + ret = sht4x_read_values(data); + if (ret < 0) + return ret; + + *val = data->humidity; + + return 0; +} + +static umode_t sht4x_hwmon_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + case hwmon_humidity: + return 0444; + case hwmon_chip: + return 0644; + default: + return 0; + } +} + +static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + return sht4x_temperature1_read(data, val); + case hwmon_humidity: + return sht4x_humidity1_read(data, val); + case hwmon_chip: + return sht4x_interval_read(data, val); + default: + return -EOPNOTSUPP; + } +} + +static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return sht4x_interval_write(data, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *sht4x_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), + NULL, +}; + +static const struct hwmon_ops sht4x_hwmon_ops = { + .is_visible = sht4x_hwmon_visible, + .read = sht4x_hwmon_read, + .write = sht4x_hwmon_write, +}; + +static const struct hwmon_chip_info sht4x_chip_info = { + .ops = &sht4x_hwmon_ops, + .info = sht4x_info, +}; + +static int sht4x_probe(struct i2c_client *client, + const struct i2c_device_id *sht4x_id) +{ + struct device *device = &client->dev; + struct device *hwmon_dev; + struct sht4x_data *data; + u8 cmd[] = {SHT4X_CMD_RESET}; + int ret; + + /* + * we require full i2c support since the sht4x uses multi-byte read and + * writes as well as multi-byte commands which are not supported by + * the smbus protocol + */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->update_interval = SHT4X_MIN_POLL_INTERVAL; + data->client = client; + + mutex_init(&data->lock); + + crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL); + + ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); + if (ret < 0) + return ret; + if (ret != SHT4X_CMD_LEN) + return -EIO; + + hwmon_dev = devm_hwmon_device_register_with_info(device, + client->name, + data, + &sht4x_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id sht4x_id[] = { + { "sht4x", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sht4x_id); + +static const struct of_device_id sht4x_of_match[] = { + { .compatible = "sensirion,sht4x" }, + { } +}; +MODULE_DEVICE_TABLE(of, sht4x_of_match); + +static struct i2c_driver sht4x_driver = { + .driver = { + .name = "sht4x", + .of_match_table = sht4x_of_match, + }, + .probe = sht4x_probe, + .id_table = sht4x_id, +}; + +module_i2c_driver(sht4x_driver); + +MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>"); +MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index a0078ccede03..18546ebc8e9f 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -14,6 +14,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/platform_data/shtc1.h> +#include <linux/of.h> /* commands (high precision mode) */ static const unsigned char shtc1_cmd_measure_blocking_hpm[] = { 0x7C, 0xA2 }; @@ -185,17 +186,19 @@ static void shtc1_select_command(struct shtc1_data *data) } } -static int shtc1_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id shtc1_id[]; + +static int shtc1_probe(struct i2c_client *client) { int ret; u16 id_reg; char id_reg_buf[2]; struct shtc1_data *data; struct device *hwmon_dev; - enum shtcx_chips chip = id->driver_data; + enum shtcx_chips chip = i2c_match_id(shtc1_id, client)->driver_data; struct i2c_adapter *adap = client->adapter; struct device *dev = &client->dev; + struct device_node *np = dev->of_node; if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) { dev_err(dev, "plain i2c transactions not supported\n"); @@ -233,8 +236,14 @@ static int shtc1_probe(struct i2c_client *client, data->client = client; data->chip = chip; - if (client->dev.platform_data) - data->setup = *(struct shtc1_platform_data *)dev->platform_data; + if (np) { + data->setup.blocking_io = of_property_read_bool(np, "sensirion,blocking-io"); + data->setup.high_precision = !of_property_read_bool(np, "sensicon,low-precision"); + } else { + if (client->dev.platform_data) + data->setup = *(struct shtc1_platform_data *)dev->platform_data; + } + shtc1_select_command(data); mutex_init(&data->update_lock); @@ -257,9 +266,20 @@ static const struct i2c_device_id shtc1_id[] = { }; MODULE_DEVICE_TABLE(i2c, shtc1_id); +static const struct of_device_id shtc1_of_match[] = { + { .compatible = "sensirion,shtc1" }, + { .compatible = "sensirion,shtw1" }, + { .compatible = "sensirion,shtc3" }, + { } +}; +MODULE_DEVICE_TABLE(of, shtc1_of_match); + static struct i2c_driver shtc1_i2c_driver = { - .driver.name = "shtc1", - .probe = shtc1_probe, + .driver = { + .name = "shtc1", + .of_match_table = shtc1_of_match, + }, + .probe_new = shtc1_probe, .id_table = shtc1_id, }; diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 0c6741f949f5..b0b05fd12221 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -37,6 +37,7 @@ * 735 0008 0735 */ +#define DRIVER_NAME "sis5595" #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> @@ -172,7 +173,7 @@ struct sis5595_data { struct mutex lock; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ char maxins; /* == 3 if temp enabled, otherwise == 4 */ u8 revision; /* Reg. value */ @@ -191,21 +192,75 @@ struct sis5595_data { static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ -static int sis5595_probe(struct platform_device *pdev); -static int sis5595_remove(struct platform_device *pdev); +/* ISA access must be locked explicitly. */ +static int sis5595_read_value(struct sis5595_data *data, u8 reg) +{ + int res; -static int sis5595_read_value(struct sis5595_data *data, u8 reg); -static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value); -static struct sis5595_data *sis5595_update_device(struct device *dev); -static void sis5595_init_device(struct sis5595_data *data); + mutex_lock(&data->lock); + outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); + res = inb_p(data->addr + SIS5595_DATA_REG_OFFSET); + mutex_unlock(&data->lock); + return res; +} -static struct platform_driver sis5595_driver = { - .driver = { - .name = "sis5595", - }, - .probe = sis5595_probe, - .remove = sis5595_remove, -}; +static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value) +{ + mutex_lock(&data->lock); + outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); + outb_p(value, data->addr + SIS5595_DATA_REG_OFFSET); + mutex_unlock(&data->lock); +} + +static struct sis5595_data *sis5595_update_device(struct device *dev) +{ + struct sis5595_data *data = dev_get_drvdata(dev); + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + for (i = 0; i <= data->maxins; i++) { + data->in[i] = + sis5595_read_value(data, SIS5595_REG_IN(i)); + data->in_min[i] = + sis5595_read_value(data, + SIS5595_REG_IN_MIN(i)); + data->in_max[i] = + sis5595_read_value(data, + SIS5595_REG_IN_MAX(i)); + } + for (i = 0; i < 2; i++) { + data->fan[i] = + sis5595_read_value(data, SIS5595_REG_FAN(i)); + data->fan_min[i] = + sis5595_read_value(data, + SIS5595_REG_FAN_MIN(i)); + } + if (data->maxins == 3) { + data->temp = + sis5595_read_value(data, SIS5595_REG_TEMP); + data->temp_over = + sis5595_read_value(data, SIS5595_REG_TEMP_OVER); + data->temp_hyst = + sis5595_read_value(data, SIS5595_REG_TEMP_HYST); + } + i = sis5595_read_value(data, SIS5595_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + sis5595_read_value(data, SIS5595_REG_ALARM1) | + (sis5595_read_value(data, SIS5595_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} /* 4 Voltages */ static ssize_t in_show(struct device *dev, struct device_attribute *da, @@ -568,6 +623,15 @@ static const struct attribute_group sis5595_group_temp1 = { .attrs = sis5595_attributes_temp1, }; +/* Called when we have found a new SIS5595. */ +static void sis5595_init_device(struct sis5595_data *data) +{ + u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG); + if (!(config & 0x01)) + sis5595_write_value(data, SIS5595_REG_CONFIG, + (config & 0xf7) | 0x01); +} + /* This is called when the module is loaded */ static int sis5595_probe(struct platform_device *pdev) { @@ -580,7 +644,7 @@ static int sis5595_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, SIS5595_EXTENT, - sis5595_driver.driver.name)) + DRIVER_NAME)) return -EBUSY; data = devm_kzalloc(&pdev->dev, sizeof(struct sis5595_data), @@ -591,7 +655,7 @@ static int sis5595_probe(struct platform_device *pdev) mutex_init(&data->lock); mutex_init(&data->update_lock); data->addr = res->start; - data->name = "sis5595"; + data->name = DRIVER_NAME; platform_set_drvdata(pdev, data); /* @@ -657,85 +721,6 @@ static int sis5595_remove(struct platform_device *pdev) return 0; } -/* ISA access must be locked explicitly. */ -static int sis5595_read_value(struct sis5595_data *data, u8 reg) -{ - int res; - - mutex_lock(&data->lock); - outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); - res = inb_p(data->addr + SIS5595_DATA_REG_OFFSET); - mutex_unlock(&data->lock); - return res; -} - -static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value) -{ - mutex_lock(&data->lock); - outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); - outb_p(value, data->addr + SIS5595_DATA_REG_OFFSET); - mutex_unlock(&data->lock); -} - -/* Called when we have found a new SIS5595. */ -static void sis5595_init_device(struct sis5595_data *data) -{ - u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG); - if (!(config & 0x01)) - sis5595_write_value(data, SIS5595_REG_CONFIG, - (config & 0xf7) | 0x01); -} - -static struct sis5595_data *sis5595_update_device(struct device *dev) -{ - struct sis5595_data *data = dev_get_drvdata(dev); - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - for (i = 0; i <= data->maxins; i++) { - data->in[i] = - sis5595_read_value(data, SIS5595_REG_IN(i)); - data->in_min[i] = - sis5595_read_value(data, - SIS5595_REG_IN_MIN(i)); - data->in_max[i] = - sis5595_read_value(data, - SIS5595_REG_IN_MAX(i)); - } - for (i = 0; i < 2; i++) { - data->fan[i] = - sis5595_read_value(data, SIS5595_REG_FAN(i)); - data->fan_min[i] = - sis5595_read_value(data, - SIS5595_REG_FAN_MIN(i)); - } - if (data->maxins == 3) { - data->temp = - sis5595_read_value(data, SIS5595_REG_TEMP); - data->temp_over = - sis5595_read_value(data, SIS5595_REG_TEMP_OVER); - data->temp_hyst = - sis5595_read_value(data, SIS5595_REG_TEMP_HYST); - } - i = sis5595_read_value(data, SIS5595_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - data->alarms = - sis5595_read_value(data, SIS5595_REG_ALARM1) | - (sis5595_read_value(data, SIS5595_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} - static const struct pci_device_id sis5595_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, { 0, } @@ -764,7 +749,7 @@ static int sis5595_device_add(unsigned short address) struct resource res = { .start = address, .end = address + SIS5595_EXTENT - 1, - .name = "sis5595", + .name = DRIVER_NAME, .flags = IORESOURCE_IO, }; int err; @@ -773,7 +758,7 @@ static int sis5595_device_add(unsigned short address) if (err) goto exit; - pdev = platform_device_alloc("sis5595", address); + pdev = platform_device_alloc(DRIVER_NAME, address); if (!pdev) { err = -ENOMEM; pr_err("Device allocation failed\n"); @@ -800,6 +785,14 @@ exit: return err; } +static struct platform_driver sis5595_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = sis5595_probe, + .remove = sis5595_remove, +}; + static int sis5595_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { @@ -886,7 +879,7 @@ exit: } static struct pci_driver sis5595_pci_driver = { - .name = "sis5595", + .name = DRIVER_NAME, .id_table = sis5595_pci_ids, .probe = sis5595_pci_probe, }; diff --git a/drivers/hwmon/sl28cpld-hwmon.c b/drivers/hwmon/sl28cpld-hwmon.c new file mode 100644 index 000000000000..9ce4899a81a5 --- /dev/null +++ b/drivers/hwmon/sl28cpld-hwmon.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * sl28cpld hardware monitoring driver + * + * Copyright 2020 Kontron Europe GmbH + */ + +#include <linux/bitfield.h> +#include <linux/hwmon.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> + +#define FAN_INPUT 0x00 +#define FAN_SCALE_X8 BIT(7) +#define FAN_VALUE_MASK GENMASK(6, 0) + +struct sl28cpld_hwmon { + struct regmap *regmap; + u32 offset; +}; + +static umode_t sl28cpld_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return 0444; +} + +static int sl28cpld_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, long *input) +{ + struct sl28cpld_hwmon *hwmon = dev_get_drvdata(dev); + unsigned int value; + int ret; + + switch (attr) { + case hwmon_fan_input: + ret = regmap_read(hwmon->regmap, hwmon->offset + FAN_INPUT, + &value); + if (ret) + return ret; + /* + * The register has a 7 bit value and 1 bit which indicates the + * scale. If the MSB is set, then the lower 7 bit has to be + * multiplied by 8, to get the correct reading. + */ + if (value & FAN_SCALE_X8) + value = FIELD_GET(FAN_VALUE_MASK, value) << 3; + + /* + * The counter period is 1000ms and the sysfs specification + * says we should assume 2 pulses per revolution. + */ + value *= 60 / 2; + + break; + default: + return -EOPNOTSUPP; + } + + *input = value; + return 0; +} + +static const struct hwmon_channel_info *sl28cpld_hwmon_info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), + NULL +}; + +static const struct hwmon_ops sl28cpld_hwmon_ops = { + .is_visible = sl28cpld_hwmon_is_visible, + .read = sl28cpld_hwmon_read, +}; + +static const struct hwmon_chip_info sl28cpld_hwmon_chip_info = { + .ops = &sl28cpld_hwmon_ops, + .info = sl28cpld_hwmon_info, +}; + +static int sl28cpld_hwmon_probe(struct platform_device *pdev) +{ + struct sl28cpld_hwmon *hwmon; + struct device *hwmon_dev; + int ret; + + if (!pdev->dev.parent) + return -ENODEV; + + hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!hwmon->regmap) + return -ENODEV; + + ret = device_property_read_u32(&pdev->dev, "reg", &hwmon->offset); + if (ret) + return -EINVAL; + + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "sl28cpld_hwmon", hwmon, + &sl28cpld_hwmon_chip_info, NULL); + if (IS_ERR(hwmon_dev)) + dev_err(&pdev->dev, "failed to register as hwmon device"); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id sl28cpld_hwmon_of_match[] = { + { .compatible = "kontron,sl28cpld-fan" }, + {} +}; +MODULE_DEVICE_TABLE(of, sl28cpld_hwmon_of_match); + +static struct platform_driver sl28cpld_hwmon_driver = { + .probe = sl28cpld_hwmon_probe, + .driver = { + .name = "sl28cpld-fan", + .of_match_table = sl28cpld_hwmon_of_match, + }, +}; +module_platform_driver(sl28cpld_hwmon_driver); + +MODULE_DESCRIPTION("sl28cpld Hardware Monitoring Driver"); +MODULE_AUTHOR("Michael Walle <michael@walle.cc>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c index af01f763f7d1..c36bdbe423de 100644 --- a/drivers/hwmon/smm665.c +++ b/drivers/hwmon/smm665.c @@ -265,7 +265,7 @@ static struct smm665_data *smm665_update_device(struct device *dev) data->adc[i] = val; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } abort: mutex_unlock(&data->update_lock); @@ -351,7 +351,7 @@ static ssize_t smm665_show_crit_alarm(struct device *dev, if (data->faults & (1 << attr->index)) val = 1; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t smm665_show_input(struct device *dev, @@ -366,7 +366,7 @@ static ssize_t smm665_show_input(struct device *dev, return PTR_ERR(data); val = smm665_convert(data->adc[adc], adc); - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } #define SMM665_SHOW(what) \ @@ -562,8 +562,9 @@ static struct attribute *smm665_attrs[] = { ATTRIBUTE_GROUPS(smm665); -static int smm665_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id smm665_id[]; + +static int smm665_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct smm665_data *data; @@ -585,7 +586,7 @@ static int smm665_probe(struct i2c_client *client, mutex_init(&data->update_lock); data->client = client; - data->type = id->driver_data; + data->type = i2c_match_id(smm665_id, client)->driver_data; data->cmdreg = i2c_new_dummy_device(adapter, (client->addr & ~SMM665_REGMASK) | SMM665_CMDREG_BASE); if (IS_ERR(data->cmdreg)) @@ -670,12 +671,11 @@ out_unregister: return ret; } -static int smm665_remove(struct i2c_client *client) +static void smm665_remove(struct i2c_client *client) { struct smm665_data *data = i2c_get_clientdata(client); i2c_unregister_device(data->cmdreg); - return 0; } static const struct i2c_device_id smm665_id[] = { @@ -694,7 +694,7 @@ static struct i2c_driver smm665_driver = { .driver = { .name = "smm665", }, - .probe = smm665_probe, + .probe_new = smm665_probe, .remove = smm665_remove, .id_table = smm665_id, }; diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index f928b8d4ff48..c26d6eae0e4e 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -96,7 +96,7 @@ struct smsc47b397_data { struct mutex update_lock; unsigned long last_updated; /* in jiffies */ - int valid; + bool valid; /* register values */ u16 fan[4]; @@ -137,7 +137,7 @@ static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; dev_dbg(dev, "... device update complete\n"); } diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index b637836b58a1..37531b5c8254 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -682,7 +682,7 @@ static int __init smsc47m1_handle_resources(unsigned short address, /* Request the resources */ if (!devm_request_region(dev, start, len, DRVNAME)) { dev_err(dev, - "Region 0x%hx-0x%hx already in use!\n", + "Region 0x%x-0x%x already in use!\n", start, start + len); return -EBUSY; } diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 6cbb119e3d0e..70d2152234e2 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -86,7 +86,7 @@ struct smsc47m192_data { struct i2c_client *client; const struct attribute_group *groups[3]; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[8]; /* Register value */ @@ -157,7 +157,7 @@ static struct smsc47m192_data *smsc47m192_update_device(struct device *dev) SMSC47M192_REG_ALARM2) << 8); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -582,13 +582,12 @@ static int smsc47m192_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "smsc47m192", I2C_NAME_SIZE); + strscpy(info->type, "smsc47m192", I2C_NAME_SIZE); return 0; } -static int smsc47m192_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int smsc47m192_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -629,7 +628,7 @@ static struct i2c_driver smsc47m192_driver = { .driver = { .name = "smsc47m192", }, - .probe = smsc47m192_probe, + .probe_new = smsc47m192_probe, .id_table = smsc47m192_id, .detect = smsc47m192_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/sparx5-temp.c b/drivers/hwmon/sparx5-temp.c new file mode 100644 index 000000000000..04fd8505e5d6 --- /dev/null +++ b/drivers/hwmon/sparx5-temp.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Sparx5 SoC temperature sensor driver + * + * Copyright (C) 2020 Lars Povlsen <lars.povlsen@microchip.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#define TEMP_CTRL 0 +#define TEMP_CFG 4 +#define TEMP_CFG_CYCLES GENMASK(24, 15) +#define TEMP_CFG_ENA BIT(0) +#define TEMP_STAT 8 +#define TEMP_STAT_VALID BIT(12) +#define TEMP_STAT_TEMP GENMASK(11, 0) + +struct s5_hwmon { + void __iomem *base; + struct clk *clk; +}; + +static void s5_temp_enable(struct s5_hwmon *hwmon) +{ + u32 val = readl(hwmon->base + TEMP_CFG); + u32 clk = clk_get_rate(hwmon->clk) / USEC_PER_SEC; + + val &= ~TEMP_CFG_CYCLES; + val |= FIELD_PREP(TEMP_CFG_CYCLES, clk); + val |= TEMP_CFG_ENA; + + writel(val, hwmon->base + TEMP_CFG); +} + +static int s5_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + struct s5_hwmon *hwmon = dev_get_drvdata(dev); + int rc = 0, value; + u32 stat; + + switch (attr) { + case hwmon_temp_input: + stat = readl_relaxed(hwmon->base + TEMP_STAT); + if (!(stat & TEMP_STAT_VALID)) + return -EAGAIN; + value = stat & TEMP_STAT_TEMP; + /* + * From register documentation: + * Temp(C) = TEMP_SENSOR_STAT.TEMP / 4096 * 352.2 - 109.4 + */ + value = DIV_ROUND_CLOSEST(value * 3522, 4096) - 1094; + /* + * Scale down by 10 from above and multiply by 1000 to + * have millidegrees as specified by the hwmon sysfs + * interface. + */ + value *= 100; + *temp = value; + break; + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +static umode_t s5_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static const struct hwmon_channel_info *s5_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops s5_hwmon_ops = { + .is_visible = s5_is_visible, + .read = s5_read, +}; + +static const struct hwmon_chip_info s5_chip_info = { + .ops = &s5_hwmon_ops, + .info = s5_info, +}; + +static int s5_temp_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + struct s5_hwmon *hwmon; + + hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hwmon->base)) + return PTR_ERR(hwmon->base); + + hwmon->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(hwmon->clk)) + return PTR_ERR(hwmon->clk); + + s5_temp_enable(hwmon); + + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "s5_temp", + hwmon, + &s5_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id s5_temp_match[] = { + { .compatible = "microchip,sparx5-temp" }, + {}, +}; +MODULE_DEVICE_TABLE(of, s5_temp_match); + +static struct platform_driver s5_temp_driver = { + .probe = s5_temp_probe, + .driver = { + .name = "sparx5-temp", + .of_match_table = s5_temp_match, + }, +}; + +module_platform_driver(s5_temp_driver); + +MODULE_AUTHOR("Lars Povlsen <lars.povlsen@microchip.com>"); +MODULE_DESCRIPTION("Sparx5 SoC temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index 35b353c2b0a1..2f67c6747ead 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -387,7 +387,7 @@ static ssize_t max_alarm_show(struct device *dev, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->max_alert); + return sysfs_emit(buf, "%d\n", priv->max_alert); } static ssize_t min_alarm_show(struct device *dev, @@ -404,7 +404,7 @@ static ssize_t min_alarm_show(struct device *dev, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->min_alert); + return sysfs_emit(buf, "%d\n", priv->min_alert); } static ssize_t input_show(struct device *dev, struct device_attribute *attr, @@ -419,7 +419,7 @@ static ssize_t input_show(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->temp); + return sysfs_emit(buf, "%d\n", priv->temp); } static ssize_t therm_show(struct device *dev, struct device_attribute *attr, @@ -427,7 +427,7 @@ static ssize_t therm_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm); + return sysfs_emit(buf, "%d\n", priv->therm); } static ssize_t therm_store(struct device *dev, struct device_attribute *attr, @@ -469,7 +469,7 @@ static ssize_t hyst_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->hyst); + return sysfs_emit(buf, "%d\n", priv->hyst); } static ssize_t hyst_store(struct device *dev, struct device_attribute *attr, @@ -509,7 +509,7 @@ static ssize_t therm_trip_show(struct device *dev, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm_trip); + return sysfs_emit(buf, "%d\n", priv->therm_trip); } static ssize_t max_show(struct device *dev, struct device_attribute *attr, @@ -517,7 +517,7 @@ static ssize_t max_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_max); + return sysfs_emit(buf, "%d\n", priv->event_max); } static ssize_t max_store(struct device *dev, struct device_attribute *attr, @@ -551,7 +551,7 @@ static ssize_t min_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_min); + return sysfs_emit(buf, "%d\n", priv->event_min); } static ssize_t min_store(struct device *dev, struct device_attribute *attr, @@ -585,8 +585,8 @@ static ssize_t interval_show(struct device *dev, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", - stts751_intervals[priv->interval]); + return sysfs_emit(buf, "%d\n", + stts751_intervals[priv->interval]); } static ssize_t interval_store(struct device *dev, @@ -692,7 +692,7 @@ static int stts751_detect(struct i2c_client *new_client, } dev_dbg(&new_client->dev, "Chip %s detected", name); - strlcpy(info->type, stts751_id[0].name, I2C_NAME_SIZE); + strscpy(info->type, stts751_id[0].name, I2C_NAME_SIZE); return 0; } @@ -762,8 +762,7 @@ static struct attribute *stts751_attrs[] = { }; ATTRIBUTE_GROUPS(stts751); -static int stts751_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int stts751_probe(struct i2c_client *client) { struct stts751_priv *priv; int ret; @@ -822,7 +821,7 @@ static struct i2c_driver stts751_driver = { .name = DEVNAME, .of_match_table = of_match_ptr(stts751_of_match), }, - .probe = stts751_probe, + .probe_new = stts751_probe, .id_table = stts751_id, .detect = stts751_detect, .alert = stts751_alert, diff --git a/drivers/hwmon/sy7636a-hwmon.c b/drivers/hwmon/sy7636a-hwmon.c new file mode 100644 index 000000000000..6dd9c2a0f0e0 --- /dev/null +++ b/drivers/hwmon/sy7636a-hwmon.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functions to access SY3686A power management chip temperature + * + * Copyright (C) 2021 reMarkable AS - http://www.remarkable.com/ + * + * Authors: Lars Ivar Miljeteig <lars.ivar.miljeteig@remarkable.com> + * Alistair Francis <alistair@alistair23.me> + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/machine.h> + +#include <linux/mfd/sy7636a.h> + +static int sy7636a_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + struct regmap *regmap = dev_get_drvdata(dev); + int ret, reg_val; + + ret = regmap_read(regmap, + SY7636A_REG_TERMISTOR_READOUT, ®_val); + if (ret) + return ret; + + *temp = reg_val * 1000; + + return 0; +} + +static umode_t sy7636a_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + if (attr != hwmon_temp_input) + return 0; + + return 0444; +} + +static const struct hwmon_ops sy7636a_hwmon_ops = { + .is_visible = sy7636a_is_visible, + .read = sy7636a_read, +}; + +static const struct hwmon_channel_info *sy7636a_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_chip_info sy7636a_chip_info = { + .ops = &sy7636a_hwmon_ops, + .info = sy7636a_info, +}; + +static int sy7636a_sensor_probe(struct platform_device *pdev) +{ + struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); + struct regulator *regulator; + struct device *hwmon_dev; + int err; + + if (!regmap) + return -EPROBE_DEFER; + + regulator = devm_regulator_get(&pdev->dev, "vcom"); + if (IS_ERR(regulator)) + return PTR_ERR(regulator); + + err = regulator_enable(regulator); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "sy7636a_temperature", regmap, + &sy7636a_chip_info, NULL); + + if (IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + dev_err(&pdev->dev, "Unable to register hwmon device, returned %d\n", err); + return err; + } + + return 0; +} + +static struct platform_driver sy7636a_sensor_driver = { + .probe = sy7636a_sensor_probe, + .driver = { + .name = "sy7636a-temperature", + }, +}; +module_platform_driver(sy7636a_sensor_driver); + +MODULE_DESCRIPTION("SY7636A sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c index 3e3b8c61bd76..54cd33d09688 100644 --- a/drivers/hwmon/tc654.c +++ b/drivers/hwmon/tc654.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <linux/thermal.h> #include <linux/util_macros.h> enum tc654_regs { @@ -379,28 +380,20 @@ static ssize_t pwm_show(struct device *dev, struct device_attribute *da, return sprintf(buf, "%d\n", pwm); } -static ssize_t pwm_store(struct device *dev, struct device_attribute *da, - const char *buf, size_t count) +static int _set_pwm(struct tc654_data *data, unsigned long val) { - struct tc654_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - unsigned long val; int ret; - if (kstrtoul(buf, 10, &val)) - return -EINVAL; - if (val > 255) - return -EINVAL; - mutex_lock(&data->update_lock); - if (val == 0) + if (val == 0) { data->config |= TC654_REG_CONFIG_SDM; - else + data->duty_cycle = 0; + } else { data->config &= ~TC654_REG_CONFIG_SDM; - - data->duty_cycle = find_closest(val, tc654_pwm_map, - ARRAY_SIZE(tc654_pwm_map)); + data->duty_cycle = val - 1; + } ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config); if (ret < 0) @@ -411,6 +404,24 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *da, out: mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t pwm_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct tc654_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + if (val > 255) + return -EINVAL; + if (val > 0) + val = find_closest(val, tc654_pwm_map, ARRAY_SIZE(tc654_pwm_map)) + 1; + + ret = _set_pwm(data, val); return ret < 0 ? ret : count; } @@ -443,11 +454,62 @@ static struct attribute *tc654_attrs[] = { ATTRIBUTE_GROUPS(tc654); /* + * thermal cooling device functions + * + * Account for the "ShutDown Mode (SDM)" state by offsetting + * the 16 PWM duty cycle states by 1. + * + * State 0 = 0% PWM | Shutdown - Fan(s) are off + * State 1 = 30% PWM | duty_cycle = 0 + * State 2 = ~35% PWM | duty_cycle = 1 + * [...] + * State 15 = ~95% PWM | duty_cycle = 14 + * State 16 = 100% PWM | duty_cycle = 15 + */ +#define TC654_MAX_COOLING_STATE 16 + +static int tc654_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + *state = TC654_MAX_COOLING_STATE; + return 0; +} + +static int tc654_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct tc654_data *data = tc654_update_client(cdev->devdata); + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->config & TC654_REG_CONFIG_SDM) + *state = 0; /* FAN is off */ + else + *state = data->duty_cycle + 1; /* offset PWM States by 1 */ + + return 0; +} + +static int tc654_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct tc654_data *data = tc654_update_client(cdev->devdata); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return _set_pwm(data, clamp_val(state, 0, TC654_MAX_COOLING_STATE)); +} + +static const struct thermal_cooling_device_ops tc654_fan_cool_ops = { + .get_max_state = tc654_get_max_state, + .get_cur_state = tc654_get_cur_state, + .set_cur_state = tc654_set_cur_state, +}; + +/* * device probe and removal */ -static int tc654_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tc654_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tc654_data *data; @@ -473,7 +535,18 @@ static int tc654_probe(struct i2c_client *client, hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, tc654_groups); - return PTR_ERR_OR_ZERO(hwmon_dev); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + if (IS_ENABLED(CONFIG_THERMAL)) { + struct thermal_cooling_device *cdev; + + cdev = devm_thermal_of_cooling_device_register(dev, dev->of_node, client->name, + hwmon_dev, &tc654_fan_cool_ops); + return PTR_ERR_OR_ZERO(cdev); + } + + return 0; } static const struct i2c_device_id tc654_id[] = { @@ -488,7 +561,7 @@ static struct i2c_driver tc654_driver = { .driver = { .name = "tc654", }, - .probe = tc654_probe, + .probe_new = tc654_probe, .id_table = tc654_id, }; diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c index fcf638ed16a9..ace55da97fc2 100644 --- a/drivers/hwmon/tc74.c +++ b/drivers/hwmon/tc74.c @@ -103,8 +103,7 @@ static struct attribute *tc74_attrs[] = { ATTRIBUTE_GROUPS(tc74); -static int tc74_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int tc74_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tc74_data *data; @@ -161,7 +160,7 @@ static struct i2c_driver tc74_driver = { .driver = { .name = "tc74", }, - .probe = tc74_probe, + .probe_new = tc74_probe, .id_table = tc74_id, }; diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 3f5a983d9289..81cdb012993c 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -62,7 +62,7 @@ struct thmc50_data { enum chips type; unsigned long last_updated; /* In jiffies */ char has_temp3; /* !=0 if it is ADM1022 in temp3 mode */ - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ /* Register values */ s8 temp_input[3]; @@ -107,7 +107,7 @@ static struct thmc50_data *thmc50_update_device(struct device *dev) data->alarms = i2c_smbus_read_byte_data(client, THMC50_REG_INTR); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -352,7 +352,7 @@ static int thmc50_detect(struct i2c_client *client, pr_debug("thmc50: Detected %s (version %x, revision %x)\n", type_name, (revision >> 4) - 0xc, revision & 0xf); - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } @@ -377,8 +377,9 @@ static void thmc50_init_client(struct thmc50_data *data) i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config); } -static int thmc50_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id thmc50_id[]; + +static int thmc50_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct thmc50_data *data; @@ -390,7 +391,7 @@ static int thmc50_probe(struct i2c_client *client, return -ENOMEM; data->client = client; - data->type = id->driver_data; + data->type = i2c_match_id(thmc50_id, client)->driver_data; mutex_init(&data->update_lock); thmc50_init_client(data); @@ -419,7 +420,7 @@ static struct i2c_driver thmc50_driver = { .driver = { .name = "thmc50", }, - .probe = thmc50_probe, + .probe_new = thmc50_probe, .id_table = thmc50_id, .detect = thmc50_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 5fe35e5b2f73..2bf496a62206 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -189,8 +189,7 @@ static const struct regmap_config tmp102_regmap_config = { .use_single_write = true, }; -static int tmp102_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tmp102_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -261,7 +260,6 @@ static int tmp102_probe(struct i2c_client *client, return 0; } -#ifdef CONFIG_PM_SLEEP static int tmp102_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -284,9 +282,8 @@ static int tmp102_resume(struct device *dev) return err; } -#endif /* CONFIG_PM */ -static SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); static const struct i2c_device_id tmp102_id[] = { { "tmp102", 0 }, @@ -303,8 +300,8 @@ MODULE_DEVICE_TABLE(of, tmp102_of_match); static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, .driver.of_match_table = of_match_ptr(tmp102_of_match), - .driver.pm = &tmp102_dev_pm_ops, - .probe = tmp102_probe, + .driver.pm = pm_sleep_ptr(&tmp102_dev_pm_ops), + .probe_new = tmp102_probe, .id_table = tmp102_id, }; diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c index 49851533935e..56d5cbf36a45 100644 --- a/drivers/hwmon/tmp103.c +++ b/drivers/hwmon/tmp103.c @@ -51,51 +51,92 @@ static inline u8 tmp103_mc_to_reg(int val) return DIV_ROUND_CLOSEST(val, 1000); } -static ssize_t tmp103_temp_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int tmp103_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) { - struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); struct regmap *regmap = dev_get_drvdata(dev); unsigned int regval; - int ret; + int err, reg; + + switch (attr) { + case hwmon_temp_input: + reg = TMP103_TEMP_REG; + break; + case hwmon_temp_min: + reg = TMP103_TLOW_REG; + break; + case hwmon_temp_max: + reg = TMP103_THIGH_REG; + break; + default: + return -EOPNOTSUPP; + } - ret = regmap_read(regmap, sda->index, ®val); - if (ret < 0) - return ret; + err = regmap_read(regmap, reg, ®val); + if (err < 0) + return err; + + *temp = tmp103_reg_to_mc(regval); - return sprintf(buf, "%d\n", tmp103_reg_to_mc(regval)); + return 0; } -static ssize_t tmp103_temp_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int tmp103_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long temp) { - struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); struct regmap *regmap = dev_get_drvdata(dev); - long val; - int ret; - - if (kstrtol(buf, 10, &val) < 0) - return -EINVAL; + int reg; + + switch (attr) { + case hwmon_temp_min: + reg = TMP103_TLOW_REG; + break; + case hwmon_temp_max: + reg = TMP103_THIGH_REG; + break; + default: + return -EOPNOTSUPP; + } - val = clamp_val(val, -55000, 127000); - ret = regmap_write(regmap, sda->index, tmp103_mc_to_reg(val)); - return ret ? ret : count; + temp = clamp_val(temp, -55000, 127000); + return regmap_write(regmap, reg, tmp103_mc_to_reg(temp)); } -static SENSOR_DEVICE_ATTR_RO(temp1_input, tmp103_temp, TMP103_TEMP_REG); +static umode_t tmp103_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + return 0644; + default: + return 0; + } +} -static SENSOR_DEVICE_ATTR_RW(temp1_min, tmp103_temp, TMP103_TLOW_REG); +static const struct hwmon_channel_info *tmp103_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN), + NULL +}; -static SENSOR_DEVICE_ATTR_RW(temp1_max, tmp103_temp, TMP103_THIGH_REG); +static const struct hwmon_ops tmp103_hwmon_ops = { + .is_visible = tmp103_is_visible, + .read = tmp103_read, + .write = tmp103_write, +}; -static struct attribute *tmp103_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - NULL +static const struct hwmon_chip_info tmp103_chip_info = { + .ops = &tmp103_hwmon_ops, + .info = tmp103_info, }; -ATTRIBUTE_GROUPS(tmp103); static bool tmp103_regmap_is_volatile(struct device *dev, unsigned int reg) { @@ -109,8 +150,7 @@ static const struct regmap_config tmp103_regmap_config = { .volatile_reg = tmp103_regmap_is_volatile, }; -static int tmp103_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tmp103_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -131,12 +171,14 @@ static int tmp103_probe(struct i2c_client *client, } i2c_set_clientdata(client, regmap); - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - regmap, tmp103_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + regmap, + &tmp103_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __maybe_unused tmp103_suspend(struct device *dev) +static int tmp103_suspend(struct device *dev) { struct regmap *regmap = dev_get_drvdata(dev); @@ -144,7 +186,7 @@ static int __maybe_unused tmp103_suspend(struct device *dev) TMP103_CONF_SD_MASK, 0); } -static int __maybe_unused tmp103_resume(struct device *dev) +static int tmp103_resume(struct device *dev) { struct regmap *regmap = dev_get_drvdata(dev); @@ -152,7 +194,7 @@ static int __maybe_unused tmp103_resume(struct device *dev) TMP103_CONF_SD_MASK, TMP103_CONF_SD); } -static SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume); static const struct i2c_device_id tmp103_id[] = { { "tmp103", 0 }, @@ -170,9 +212,9 @@ static struct i2c_driver tmp103_driver = { .driver = { .name = "tmp103", .of_match_table = of_match_ptr(tmp103_of_match), - .pm = &tmp103_dev_pm_ops, + .pm = pm_sleep_ptr(&tmp103_dev_pm_ops), }, - .probe = tmp103_probe, + .probe_new = tmp103_probe, .id_table = tmp103_id, }; diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index fe587d4f9b2d..acb4ba750b09 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -323,8 +323,7 @@ static const struct regmap_config tmp108_regmap_config = { .use_single_write = true, }; -static int tmp108_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tmp108_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -391,7 +390,7 @@ static int tmp108_probe(struct i2c_client *client, return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __maybe_unused tmp108_suspend(struct device *dev) +static int tmp108_suspend(struct device *dev) { struct tmp108 *tmp108 = dev_get_drvdata(dev); @@ -399,7 +398,7 @@ static int __maybe_unused tmp108_suspend(struct device *dev) TMP108_CONF_MODE_MASK, TMP108_MODE_SHUTDOWN); } -static int __maybe_unused tmp108_resume(struct device *dev) +static int tmp108_resume(struct device *dev) { struct tmp108 *tmp108 = dev_get_drvdata(dev); int err; @@ -411,7 +410,7 @@ static int __maybe_unused tmp108_resume(struct device *dev) return err; } -static SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); static const struct i2c_device_id tmp108_i2c_ids[] = { { "tmp108", 0 }, @@ -430,10 +429,10 @@ MODULE_DEVICE_TABLE(of, tmp108_of_ids); static struct i2c_driver tmp108_driver = { .driver = { .name = DRIVER_NAME, - .pm = &tmp108_dev_pm_ops, + .pm = pm_sleep_ptr(&tmp108_dev_pm_ops), .of_match_table = of_match_ptr(tmp108_of_ids), }, - .probe = tmp108_probe, + .probe_new = tmp108_probe, .id_table = tmp108_i2c_ids, }; diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index fa361d9949db..f358ba679626 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -18,69 +18,42 @@ * and thus has 16 bits registers for its value and limit instead of 8 bits. */ -#include <linux/module.h> -#include <linux/init.h> #include <linux/bitops.h> -#include <linux/slab.h> -#include <linux/jiffies.h> +#include <linux/err.h> #include <linux/i2c.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> +#include <linux/init.h> +#include <linux/module.h> #include <linux/mutex.h> -#include <linux/sysfs.h> +#include <linux/regmap.h> +#include <linux/slab.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { tmp401, tmp411, tmp431, tmp432, tmp435, tmp461 }; +enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 }; /* * The TMP401 registers, note some registers have different addresses for * reading and writing */ #define TMP401_STATUS 0x02 -#define TMP401_CONFIG_READ 0x03 -#define TMP401_CONFIG_WRITE 0x09 -#define TMP401_CONVERSION_RATE_READ 0x04 -#define TMP401_CONVERSION_RATE_WRITE 0x0A +#define TMP401_CONFIG 0x03 +#define TMP401_CONVERSION_RATE 0x04 +#define TMP4XX_N_FACTOR_REG 0x18 +#define TMP43X_BETA_RANGE 0x25 #define TMP401_TEMP_CRIT_HYST 0x21 #define TMP401_MANUFACTURER_ID_REG 0xFE #define TMP401_DEVICE_ID_REG 0xFF -static const u8 TMP401_TEMP_MSB_READ[7][2] = { - { 0x00, 0x01 }, /* temp */ - { 0x06, 0x08 }, /* low limit */ - { 0x05, 0x07 }, /* high limit */ - { 0x20, 0x19 }, /* therm (crit) limit */ - { 0x30, 0x34 }, /* lowest */ - { 0x32, 0x36 }, /* highest */ - { 0, 0x11 }, /* offset */ -}; - -static const u8 TMP401_TEMP_MSB_WRITE[7][2] = { - { 0, 0 }, /* temp (unused) */ - { 0x0C, 0x0E }, /* low limit */ - { 0x0B, 0x0D }, /* high limit */ - { 0x20, 0x19 }, /* therm (crit) limit */ - { 0x30, 0x34 }, /* lowest */ - { 0x32, 0x36 }, /* highest */ - { 0, 0x11 }, /* offset */ -}; - -static const u8 TMP432_TEMP_MSB_READ[4][3] = { +static const u8 TMP401_TEMP_MSB[7][3] = { { 0x00, 0x01, 0x23 }, /* temp */ { 0x06, 0x08, 0x16 }, /* low limit */ { 0x05, 0x07, 0x15 }, /* high limit */ - { 0x20, 0x19, 0x1A }, /* therm (crit) limit */ -}; - -static const u8 TMP432_TEMP_MSB_WRITE[4][3] = { - { 0, 0, 0 }, /* temp - unused */ - { 0x0C, 0x0E, 0x16 }, /* low limit */ - { 0x0B, 0x0D, 0x15 }, /* high limit */ - { 0x20, 0x19, 0x1A }, /* therm (crit) limit */ + { 0x20, 0x19, 0x1a }, /* therm (crit) limit */ + { 0x30, 0x34, 0x00 }, /* lowest */ + { 0x32, 0xf6, 0x00 }, /* highest */ }; /* [0] = fault, [1] = low, [2] = high, [3] = therm/crit */ @@ -123,7 +96,6 @@ static const struct i2c_device_id tmp401_id[] = { { "tmp431", tmp431 }, { "tmp432", tmp432 }, { "tmp435", tmp435 }, - { "tmp461", tmp461 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -134,311 +106,323 @@ MODULE_DEVICE_TABLE(i2c, tmp401_id); struct tmp401_data { struct i2c_client *client; - const struct attribute_group *groups[3]; + struct regmap *regmap; struct mutex update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ enum chips kind; - unsigned int update_interval; /* in milliseconds */ + bool extended_range; - /* register values */ - u8 status[4]; - u8 config; - u16 temp[7][3]; - u8 temp_crit_hyst; + /* hwmon API configuration data */ + u32 chip_channel_config[4]; + struct hwmon_channel_info chip_info; + u32 temp_channel_config[4]; + struct hwmon_channel_info temp_info; + const struct hwmon_channel_info *info[3]; + struct hwmon_chip_info chip; }; -/* - * Sysfs attr show / store functions - */ - -static int tmp401_register_to_temp(u16 reg, u8 config) -{ - int temp = reg; - - if (config & TMP401_CONFIG_RANGE) - temp -= 64 * 256; - - return DIV_ROUND_CLOSEST(temp * 125, 32); -} +/* regmap */ -static u16 tmp401_temp_to_register(long temp, u8 config, int zbits) +static bool tmp401_regmap_is_volatile(struct device *dev, unsigned int reg) { - if (config & TMP401_CONFIG_RANGE) { - temp = clamp_val(temp, -64000, 191000); - temp += 64000; - } else - temp = clamp_val(temp, 0, 127000); - - return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits; -} - -static int tmp401_update_device_reg16(struct i2c_client *client, - struct tmp401_data *data) -{ - int i, j, val; - int num_regs = data->kind == tmp411 ? 6 : 4; - int num_sensors = data->kind == tmp432 ? 3 : 2; - - for (i = 0; i < num_sensors; i++) { /* local / r1 / r2 */ - for (j = 0; j < num_regs; j++) { /* temp / low / ... */ - u8 regaddr; - - regaddr = data->kind == tmp432 ? - TMP432_TEMP_MSB_READ[j][i] : - TMP401_TEMP_MSB_READ[j][i]; - if (j == 3) { /* crit is msb only */ - val = i2c_smbus_read_byte_data(client, regaddr); - } else { - val = i2c_smbus_read_word_swapped(client, - regaddr); - } - if (val < 0) - return val; - - data->temp[j][i] = j == 3 ? val << 8 : val; - } + switch (reg) { + case 0: /* local temp msb */ + case 1: /* remote temp msb */ + case 2: /* status */ + case 0x10: /* remote temp lsb */ + case 0x15: /* local temp lsb */ + case 0x1b: /* status (tmp432) */ + case 0x23 ... 0x24: /* remote temp 2 msb / lsb */ + case 0x30 ... 0x37: /* lowest/highest temp; status (tmp432) */ + return true; + default: + return false; } - return 0; } -static struct tmp401_data *tmp401_update_device(struct device *dev) +static int tmp401_reg_read(void *context, unsigned int reg, unsigned int *val) { - struct tmp401_data *data = dev_get_drvdata(dev); + struct tmp401_data *data = context; struct i2c_client *client = data->client; - struct tmp401_data *ret = data; - int i, val; - unsigned long next_update; + int regval; - mutex_lock(&data->update_lock); - - next_update = data->last_updated + - msecs_to_jiffies(data->update_interval); - if (time_after(jiffies, next_update) || !data->valid) { - if (data->kind != tmp432) { - /* - * The driver uses the TMP432 status format internally. - * Convert status to TMP432 format for other chips. - */ - val = i2c_smbus_read_byte_data(client, TMP401_STATUS); - if (val < 0) { - ret = ERR_PTR(val); - goto abort; - } - data->status[0] = - (val & TMP401_STATUS_REMOTE_OPEN) >> 1; - data->status[1] = - ((val & TMP401_STATUS_REMOTE_LOW) >> 2) | - ((val & TMP401_STATUS_LOCAL_LOW) >> 5); - data->status[2] = - ((val & TMP401_STATUS_REMOTE_HIGH) >> 3) | - ((val & TMP401_STATUS_LOCAL_HIGH) >> 6); - data->status[3] = val & (TMP401_STATUS_LOCAL_CRIT - | TMP401_STATUS_REMOTE_CRIT); - } else { - for (i = 0; i < ARRAY_SIZE(data->status); i++) { - val = i2c_smbus_read_byte_data(client, - TMP432_STATUS_REG[i]); - if (val < 0) { - ret = ERR_PTR(val); - goto abort; - } - data->status[i] = val; - } - } - - val = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); - if (val < 0) { - ret = ERR_PTR(val); - goto abort; - } - data->config = val; - val = tmp401_update_device_reg16(client, data); - if (val < 0) { - ret = ERR_PTR(val); - goto abort; + switch (reg) { + case 0: /* local temp msb */ + case 1: /* remote temp msb */ + case 5: /* local temp high limit msb */ + case 6: /* local temp low limit msb */ + case 7: /* remote temp ligh limit msb */ + case 8: /* remote temp low limit msb */ + case 0x15: /* remote temp 2 high limit msb */ + case 0x16: /* remote temp 2 low limit msb */ + case 0x23: /* remote temp 2 msb */ + case 0x30: /* local temp minimum, tmp411 */ + case 0x32: /* local temp maximum, tmp411 */ + case 0x34: /* remote temp minimum, tmp411 */ + case 0xf6: /* remote temp maximum, tmp411 (really 0x36) */ + /* work around register overlap between TMP411 and TMP432 */ + if (reg == 0xf6) + reg = 0x36; + regval = i2c_smbus_read_word_swapped(client, reg); + if (regval < 0) + return regval; + *val = regval; + break; + case 0x19: /* critical limits, 8-bit registers */ + case 0x1a: + case 0x20: + regval = i2c_smbus_read_byte_data(client, reg); + if (regval < 0) + return regval; + *val = regval << 8; + break; + case 0x1b: + case 0x35 ... 0x37: + if (data->kind == tmp432) { + regval = i2c_smbus_read_byte_data(client, reg); + if (regval < 0) + return regval; + *val = regval; + break; } - val = i2c_smbus_read_byte_data(client, TMP401_TEMP_CRIT_HYST); - if (val < 0) { - ret = ERR_PTR(val); - goto abort; + /* simulate TMP432 status registers */ + regval = i2c_smbus_read_byte_data(client, TMP401_STATUS); + if (regval < 0) + return regval; + *val = 0; + switch (reg) { + case 0x1b: /* open / fault */ + if (regval & TMP401_STATUS_REMOTE_OPEN) + *val |= BIT(1); + break; + case 0x35: /* high limit */ + if (regval & TMP401_STATUS_LOCAL_HIGH) + *val |= BIT(0); + if (regval & TMP401_STATUS_REMOTE_HIGH) + *val |= BIT(1); + break; + case 0x36: /* low limit */ + if (regval & TMP401_STATUS_LOCAL_LOW) + *val |= BIT(0); + if (regval & TMP401_STATUS_REMOTE_LOW) + *val |= BIT(1); + break; + case 0x37: /* therm / crit limit */ + if (regval & TMP401_STATUS_LOCAL_CRIT) + *val |= BIT(0); + if (regval & TMP401_STATUS_REMOTE_CRIT) + *val |= BIT(1); + break; } - data->temp_crit_hyst = val; - - data->last_updated = jiffies; - data->valid = 1; + break; + default: + regval = i2c_smbus_read_byte_data(client, reg); + if (regval < 0) + return regval; + *val = regval; + break; } - -abort: - mutex_unlock(&data->update_lock); - return ret; + return 0; } -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) +static int tmp401_reg_write(void *context, unsigned int reg, unsigned int val) { - int nr = to_sensor_dev_attr_2(devattr)->nr; - int index = to_sensor_dev_attr_2(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); + struct tmp401_data *data = context; + struct i2c_client *client = data->client; - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp[nr][index], data->config)); + switch (reg) { + case 0x05: /* local temp high limit msb */ + case 0x06: /* local temp low limit msb */ + case 0x07: /* remote temp ligh limit msb */ + case 0x08: /* remote temp low limit msb */ + reg += 6; /* adjust for register write address */ + fallthrough; + case 0x15: /* remote temp 2 high limit msb */ + case 0x16: /* remote temp 2 low limit msb */ + return i2c_smbus_write_word_swapped(client, reg, val); + case 0x19: /* critical limits, 8-bit registers */ + case 0x1a: + case 0x20: + return i2c_smbus_write_byte_data(client, reg, val >> 8); + case TMP401_CONVERSION_RATE: + case TMP401_CONFIG: + reg += 6; /* adjust for register write address */ + fallthrough; + default: + return i2c_smbus_write_byte_data(client, reg, val); + } } -static ssize_t temp_crit_hyst_show(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int temp, index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - mutex_lock(&data->update_lock); - temp = tmp401_register_to_temp(data->temp[3][index], data->config); - temp -= data->temp_crit_hyst * 1000; - mutex_unlock(&data->update_lock); +static const struct regmap_config tmp401_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tmp401_regmap_is_volatile, + .reg_read = tmp401_reg_read, + .reg_write = tmp401_reg_write, +}; - return sprintf(buf, "%d\n", temp); -} +/* temperature conversion */ -static ssize_t status_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int tmp401_register_to_temp(u16 reg, bool extended) { - int nr = to_sensor_dev_attr_2(devattr)->nr; - int mask = to_sensor_dev_attr_2(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); + int temp = reg; - if (IS_ERR(data)) - return PTR_ERR(data); + if (extended) + temp -= 64 * 256; - return sprintf(buf, "%d\n", !!(data->status[nr] & mask)); + return DIV_ROUND_CLOSEST(temp * 125, 32); } -static ssize_t temp_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) +static u16 tmp401_temp_to_register(long temp, bool extended, int zbits) { - int nr = to_sensor_dev_attr_2(devattr)->nr; - int index = to_sensor_dev_attr_2(devattr)->index; - struct tmp401_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long val; - u16 reg; - u8 regaddr; - - if (kstrtol(buf, 10, &val)) - return -EINVAL; - - reg = tmp401_temp_to_register(val, data->config, nr == 3 ? 8 : 4); - - mutex_lock(&data->update_lock); - - regaddr = data->kind == tmp432 ? TMP432_TEMP_MSB_WRITE[nr][index] - : TMP401_TEMP_MSB_WRITE[nr][index]; - if (nr == 3) { /* crit is msb only */ - i2c_smbus_write_byte_data(client, regaddr, reg >> 8); + if (extended) { + temp = clamp_val(temp, -64000, 191000); + temp += 64000; } else { - /* Hardware expects big endian data --> use _swapped */ - i2c_smbus_write_word_swapped(client, regaddr, reg); + temp = clamp_val(temp, 0, 127000); } - data->temp[nr][index] = reg; - - mutex_unlock(&data->update_lock); - return count; + return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits; } -static ssize_t temp_crit_hyst_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - int temp, index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - long val; - u8 reg; - - if (IS_ERR(data)) - return PTR_ERR(data); - - if (kstrtol(buf, 10, &val)) - return -EINVAL; - - if (data->config & TMP401_CONFIG_RANGE) - val = clamp_val(val, -64000, 191000); - else - val = clamp_val(val, 0, 127000); +/* hwmon API functions */ - mutex_lock(&data->update_lock); - temp = tmp401_register_to_temp(data->temp[3][index], data->config); - val = clamp_val(val, temp - 255000, temp); - reg = ((temp - val) + 500) / 1000; - - i2c_smbus_write_byte_data(data->client, TMP401_TEMP_CRIT_HYST, - reg); - - data->temp_crit_hyst = reg; +static const u8 tmp401_temp_reg_index[] = { + [hwmon_temp_input] = 0, + [hwmon_temp_min] = 1, + [hwmon_temp_max] = 2, + [hwmon_temp_crit] = 3, + [hwmon_temp_lowest] = 4, + [hwmon_temp_highest] = 5, +}; - mutex_unlock(&data->update_lock); +static const u8 tmp401_status_reg_index[] = { + [hwmon_temp_fault] = 0, + [hwmon_temp_min_alarm] = 1, + [hwmon_temp_max_alarm] = 2, + [hwmon_temp_crit_alarm] = 3, +}; - return count; +static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct tmp401_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + int reg, ret; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_lowest: + case hwmon_temp_highest: + reg = TMP401_TEMP_MSB[tmp401_temp_reg_index[attr]][channel]; + ret = regmap_read(regmap, reg, ®val); + if (ret < 0) + return ret; + *val = tmp401_register_to_temp(regval, data->extended_range); + break; + case hwmon_temp_crit_hyst: + mutex_lock(&data->update_lock); + reg = TMP401_TEMP_MSB[3][channel]; + ret = regmap_read(regmap, reg, ®val); + if (ret < 0) + goto unlock; + *val = tmp401_register_to_temp(regval, data->extended_range); + ret = regmap_read(regmap, TMP401_TEMP_CRIT_HYST, ®val); + if (ret < 0) + goto unlock; + *val -= regval * 1000; +unlock: + mutex_unlock(&data->update_lock); + if (ret < 0) + return ret; + break; + case hwmon_temp_fault: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + reg = TMP432_STATUS_REG[tmp401_status_reg_index[attr]]; + ret = regmap_read(regmap, reg, ®val); + if (ret < 0) + return ret; + *val = !!(regval & BIT(channel)); + break; + default: + return -EOPNOTSUPP; + } + return 0; } -/* - * Resets the historical measurements of minimum and maximum temperatures. - * This is done by writing any value to any of the minimum/maximum registers - * (0x30-0x37). - */ -static ssize_t reset_temp_history_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int tmp401_temp_write(struct device *dev, u32 attr, int channel, + long val) { struct tmp401_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long val; - - if (kstrtol(buf, 10, &val)) - return -EINVAL; + struct regmap *regmap = data->regmap; + unsigned int regval; + int reg, ret, temp; - if (val != 1) { - dev_err(dev, - "temp_reset_history value %ld not supported. Use 1 to reset the history!\n", - val); - return -EINVAL; - } mutex_lock(&data->update_lock); - i2c_smbus_write_byte_data(client, TMP401_TEMP_MSB_WRITE[5][0], val); - data->valid = 0; + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + reg = TMP401_TEMP_MSB[tmp401_temp_reg_index[attr]][channel]; + regval = tmp401_temp_to_register(val, data->extended_range, + attr == hwmon_temp_crit ? 8 : 4); + ret = regmap_write(regmap, reg, regval); + break; + case hwmon_temp_crit_hyst: + if (data->extended_range) + val = clamp_val(val, -64000, 191000); + else + val = clamp_val(val, 0, 127000); + + reg = TMP401_TEMP_MSB[3][channel]; + ret = regmap_read(regmap, reg, ®val); + if (ret < 0) + break; + temp = tmp401_register_to_temp(regval, data->extended_range); + val = clamp_val(val, temp - 255000, temp); + regval = ((temp - val) + 500) / 1000; + ret = regmap_write(regmap, TMP401_TEMP_CRIT_HYST, regval); + break; + default: + ret = -EOPNOTSUPP; + break; + } mutex_unlock(&data->update_lock); - - return count; + return ret; } -static ssize_t update_interval_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int tmp401_chip_read(struct device *dev, u32 attr, int channel, long *val) { struct tmp401_data *data = dev_get_drvdata(dev); + u32 regval; + int ret; + + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(data->regmap, TMP401_CONVERSION_RATE, ®val); + if (ret < 0) + return ret; + *val = (1 << (7 - regval)) * 125; + break; + case hwmon_chip_temp_reset_history: + *val = 0; + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%u\n", data->update_interval); + return 0; } -static ssize_t update_interval_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int tmp401_set_convrate(struct regmap *regmap, long val) { - struct tmp401_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; - int err, rate; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; + int rate; /* * For valid rates, interval can be calculated as @@ -450,168 +434,177 @@ static ssize_t update_interval_store(struct device *dev, */ val = clamp_val(val, 125, 16000); rate = 7 - __fls(val * 4 / (125 * 3)); - mutex_lock(&data->update_lock); - i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, rate); - data->update_interval = (1 << (7 - rate)) * 125; - mutex_unlock(&data->update_lock); - - return count; + return regmap_write(regmap, TMP401_CONVERSION_RATE, rate); } -static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0, 0); -static SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, 1, 0); -static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, 2, 0); -static SENSOR_DEVICE_ATTR_2_RW(temp1_crit, temp, 3, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_crit_hyst, 0); -static SENSOR_DEVICE_ATTR_2_RO(temp1_min_alarm, status, 1, - TMP432_STATUS_LOCAL); -static SENSOR_DEVICE_ATTR_2_RO(temp1_max_alarm, status, 2, - TMP432_STATUS_LOCAL); -static SENSOR_DEVICE_ATTR_2_RO(temp1_crit_alarm, status, 3, - TMP432_STATUS_LOCAL); -static SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, 0, 1); -static SENSOR_DEVICE_ATTR_2_RW(temp2_min, temp, 1, 1); -static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, 2, 1); -static SENSOR_DEVICE_ATTR_2_RW(temp2_crit, temp, 3, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, temp_crit_hyst, 1); -static SENSOR_DEVICE_ATTR_2_RO(temp2_fault, status, 0, TMP432_STATUS_REMOTE1); -static SENSOR_DEVICE_ATTR_2_RO(temp2_min_alarm, status, 1, - TMP432_STATUS_REMOTE1); -static SENSOR_DEVICE_ATTR_2_RO(temp2_max_alarm, status, 2, - TMP432_STATUS_REMOTE1); -static SENSOR_DEVICE_ATTR_2_RO(temp2_crit_alarm, status, 3, - TMP432_STATUS_REMOTE1); - -static DEVICE_ATTR_RW(update_interval); - -static struct attribute *tmp401_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - - &dev_attr_update_interval.attr, - - NULL -}; - -static const struct attribute_group tmp401_group = { - .attrs = tmp401_attributes, -}; - -/* - * Additional features of the TMP411 chip. - * The TMP411 stores the minimum and maximum - * temperature measured since power-on, chip-reset, or - * minimum and maximum register reset for both the local - * and remote channels. - */ -static SENSOR_DEVICE_ATTR_2_RO(temp1_lowest, temp, 4, 0); -static SENSOR_DEVICE_ATTR_2_RO(temp1_highest, temp, 5, 0); -static SENSOR_DEVICE_ATTR_2_RO(temp2_lowest, temp, 4, 1); -static SENSOR_DEVICE_ATTR_2_RO(temp2_highest, temp, 5, 1); -static SENSOR_DEVICE_ATTR_WO(temp_reset_history, reset_temp_history, 0); - -static struct attribute *tmp411_attributes[] = { - &sensor_dev_attr_temp1_highest.dev_attr.attr, - &sensor_dev_attr_temp1_lowest.dev_attr.attr, - &sensor_dev_attr_temp2_highest.dev_attr.attr, - &sensor_dev_attr_temp2_lowest.dev_attr.attr, - &sensor_dev_attr_temp_reset_history.dev_attr.attr, - NULL -}; +static int tmp401_chip_write(struct device *dev, u32 attr, int channel, long val) +{ + struct tmp401_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int err; -static const struct attribute_group tmp411_group = { - .attrs = tmp411_attributes, -}; + mutex_lock(&data->update_lock); + switch (attr) { + case hwmon_chip_update_interval: + err = tmp401_set_convrate(regmap, val); + break; + case hwmon_chip_temp_reset_history: + if (val != 1) { + err = -EINVAL; + break; + } + /* + * Reset history by writing any value to any of the + * minimum/maximum registers (0x30-0x37). + */ + err = regmap_write(regmap, 0x30, 0); + break; + default: + err = -EOPNOTSUPP; + break; + } + mutex_unlock(&data->update_lock); -static SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, 0, 2); -static SENSOR_DEVICE_ATTR_2_RW(temp3_min, temp, 1, 2); -static SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, 2, 2); -static SENSOR_DEVICE_ATTR_2_RW(temp3_crit, temp, 3, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, temp_crit_hyst, 2); -static SENSOR_DEVICE_ATTR_2_RO(temp3_fault, status, 0, TMP432_STATUS_REMOTE2); -static SENSOR_DEVICE_ATTR_2_RO(temp3_min_alarm, status, 1, - TMP432_STATUS_REMOTE2); -static SENSOR_DEVICE_ATTR_2_RO(temp3_max_alarm, status, 2, - TMP432_STATUS_REMOTE2); -static SENSOR_DEVICE_ATTR_2_RO(temp3_crit_alarm, status, 3, - TMP432_STATUS_REMOTE2); - -static struct attribute *tmp432_attributes[] = { - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, - - NULL -}; + return err; +} -static const struct attribute_group tmp432_group = { - .attrs = tmp432_attributes, -}; +static int tmp401_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return tmp401_chip_read(dev, attr, channel, val); + case hwmon_temp: + return tmp401_temp_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} -/* - * Additional features of the TMP461 chip. - * The TMP461 temperature offset for the remote channel. - */ -static SENSOR_DEVICE_ATTR_2_RW(temp2_offset, temp, 6, 1); +static int tmp401_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return tmp401_chip_write(dev, attr, channel, val); + case hwmon_temp: + return tmp401_temp_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} -static struct attribute *tmp461_attributes[] = { - &sensor_dev_attr_temp2_offset.dev_attr.attr, - NULL -}; +static umode_t tmp401_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + case hwmon_chip_temp_reset_history: + return 0644; + default: + break; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_fault: + case hwmon_temp_lowest: + case hwmon_temp_highest: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + return 0644; + default: + break; + } + break; + default: + break; + } + return 0; +} -static const struct attribute_group tmp461_group = { - .attrs = tmp461_attributes, +static const struct hwmon_ops tmp401_ops = { + .is_visible = tmp401_is_visible, + .read = tmp401_read, + .write = tmp401_write, }; -/* - * Begin non sysfs callback code (aka Real code) - */ +/* chip initialization, detect, probe */ -static int tmp401_init_client(struct tmp401_data *data, - struct i2c_client *client) +static int tmp401_init_client(struct tmp401_data *data) { - int config, config_orig, status = 0; + struct regmap *regmap = data->regmap; + u32 config, config_orig; + int ret; + u32 val = 0; + s32 nfactor = 0; - /* Set the conversion rate to 2 Hz */ - i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); - data->update_interval = 500; + /* Set conversion rate to 2 Hz */ + ret = regmap_write(regmap, TMP401_CONVERSION_RATE, 5); + if (ret < 0) + return ret; /* Start conversions (disable shutdown if necessary) */ - config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); - if (config < 0) - return config; + ret = regmap_read(regmap, TMP401_CONFIG, &config); + if (ret < 0) + return ret; config_orig = config; config &= ~TMP401_CONFIG_SHUTDOWN; - if (config != config_orig) - status = i2c_smbus_write_byte_data(client, - TMP401_CONFIG_WRITE, - config); + if (of_property_read_bool(data->client->dev.of_node, "ti,extended-range-enable")) { + /* Enable measurement over extended temperature range */ + config |= TMP401_CONFIG_RANGE; + } + + data->extended_range = !!(config & TMP401_CONFIG_RANGE); + + if (config != config_orig) { + ret = regmap_write(regmap, TMP401_CONFIG, config); + if (ret < 0) + return ret; + } + + ret = of_property_read_u32(data->client->dev.of_node, "ti,n-factor", &nfactor); + if (!ret) { + if (data->kind == tmp401) { + dev_err(&data->client->dev, "ti,tmp401 does not support n-factor correction\n"); + return -EINVAL; + } + if (nfactor < -128 || nfactor > 127) { + dev_err(&data->client->dev, "n-factor is invalid (%d)\n", nfactor); + return -EINVAL; + } + ret = regmap_write(regmap, TMP4XX_N_FACTOR_REG, (unsigned int)nfactor); + if (ret < 0) + return ret; + } + + ret = of_property_read_u32(data->client->dev.of_node, "ti,beta-compensation", &val); + if (!ret) { + if (data->kind == tmp401 || data->kind == tmp411) { + dev_err(&data->client->dev, "ti,tmp401 or ti,tmp411 does not support beta compensation\n"); + return -EINVAL; + } + if (val > 15) { + dev_err(&data->client->dev, "beta-compensation is invalid (%u)\n", val); + return -EINVAL; + } + ret = regmap_write(regmap, TMP43X_BETA_RANGE, val); + if (ret < 0) + return ret; + } - return status; + return 0; } static int tmp401_detect(struct i2c_client *client, @@ -669,30 +662,30 @@ static int tmp401_detect(struct i2c_client *client, return -ENODEV; } - reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); + reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG); if (reg & 0x1b) return -ENODEV; - reg = i2c_smbus_read_byte_data(client, TMP401_CONVERSION_RATE_READ); + reg = i2c_smbus_read_byte_data(client, TMP401_CONVERSION_RATE); /* Datasheet says: 0x1-0x6 */ if (reg > 15) return -ENODEV; - strlcpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE); + strscpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE); return 0; } -static int tmp401_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tmp401_probe(struct i2c_client *client) { static const char * const names[] = { - "TMP401", "TMP411", "TMP431", "TMP432", "TMP435", "TMP461" + "TMP401", "TMP411", "TMP431", "TMP432", "TMP435" }; struct device *dev = &client->dev; + struct hwmon_channel_info *info; struct device *hwmon_dev; struct tmp401_data *data; - int groups = 0, status; + int status; data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) @@ -700,29 +693,55 @@ static int tmp401_probe(struct i2c_client *client, data->client = client; mutex_init(&data->update_lock); - data->kind = id->driver_data; + data->kind = i2c_match_id(tmp401_id, client)->driver_data; - /* Initialize the TMP401 chip */ - status = tmp401_init_client(data, client); - if (status < 0) - return status; + data->regmap = devm_regmap_init(dev, NULL, data, &tmp401_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); - /* Register sysfs hooks */ - data->groups[groups++] = &tmp401_group; + /* initialize configuration data */ + data->chip.ops = &tmp401_ops; + data->chip.info = data->info; - /* Register additional tmp411 sysfs hooks */ - if (data->kind == tmp411) - data->groups[groups++] = &tmp411_group; + data->info[0] = &data->chip_info; + data->info[1] = &data->temp_info; - /* Register additional tmp432 sysfs hooks */ - if (data->kind == tmp432) - data->groups[groups++] = &tmp432_group; + info = &data->chip_info; + info->type = hwmon_chip; + info->config = data->chip_channel_config; - if (data->kind == tmp461) - data->groups[groups++] = &tmp461_group; + data->chip_channel_config[0] = HWMON_C_UPDATE_INTERVAL; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, data->groups); + info = &data->temp_info; + info->type = hwmon_temp; + info->config = data->temp_channel_config; + + data->temp_channel_config[0] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM; + data->temp_channel_config[1] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT; + + if (data->kind == tmp411) { + data->temp_channel_config[0] |= HWMON_T_HIGHEST | HWMON_T_LOWEST; + data->temp_channel_config[1] |= HWMON_T_HIGHEST | HWMON_T_LOWEST; + data->chip_channel_config[0] |= HWMON_C_TEMP_RESET_HISTORY; + } + + if (data->kind == tmp432) { + data->temp_channel_config[2] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT; + } + + /* Initialize the TMP401 chip */ + status = tmp401_init_client(data); + if (status < 0) + return status; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &data->chip, NULL); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); @@ -731,12 +750,23 @@ static int tmp401_probe(struct i2c_client *client, return 0; } +static const struct of_device_id __maybe_unused tmp4xx_of_match[] = { + { .compatible = "ti,tmp401", }, + { .compatible = "ti,tmp411", }, + { .compatible = "ti,tmp431", }, + { .compatible = "ti,tmp432", }, + { .compatible = "ti,tmp435", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tmp4xx_of_match); + static struct i2c_driver tmp401_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "tmp401", + .of_match_table = of_match_ptr(tmp4xx_of_match), }, - .probe = tmp401_probe, + .probe_new = tmp401_probe, .id_table = tmp401_id, .detect = tmp401_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 83a4fab151d2..45fd7fb5ee01 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -29,15 +29,20 @@ static const unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f, enum chips { tmp421, tmp422, tmp423, tmp441, tmp442 }; +#define MAX_CHANNELS 4 /* The TMP421 registers */ #define TMP421_STATUS_REG 0x08 #define TMP421_CONFIG_REG_1 0x09 +#define TMP421_CONFIG_REG_2 0x0A +#define TMP421_CONFIG_REG_REN(x) (BIT(3 + (x))) +#define TMP421_CONFIG_REG_REN_MASK GENMASK(6, 3) #define TMP421_CONVERSION_RATE_REG 0x0B +#define TMP421_N_FACTOR_REG_1 0x21 #define TMP421_MANUFACTURER_ID_REG 0xFE #define TMP421_DEVICE_ID_REG 0xFF -static const u8 TMP421_TEMP_MSB[4] = { 0x00, 0x01, 0x02, 0x03 }; -static const u8 TMP421_TEMP_LSB[4] = { 0x10, 0x11, 0x12, 0x13 }; +static const u8 TMP421_TEMP_MSB[MAX_CHANNELS] = { 0x00, 0x01, 0x02, 0x03 }; +static const u8 TMP421_TEMP_LSB[MAX_CHANNELS] = { 0x10, 0x11, 0x12, 0x13 }; /* Flags */ #define TMP421_CONFIG_SHUTDOWN 0x40 @@ -86,85 +91,136 @@ static const struct of_device_id __maybe_unused tmp421_of_match[] = { }; MODULE_DEVICE_TABLE(of, tmp421_of_match); +struct tmp421_channel { + const char *label; + bool enabled; + s16 temp; +}; + struct tmp421_data { struct i2c_client *client; struct mutex update_lock; - u32 temp_config[5]; + u32 temp_config[MAX_CHANNELS + 1]; struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[2]; struct hwmon_chip_info chip; - char valid; + bool valid; unsigned long last_updated; unsigned long channels; u8 config; - s16 temp[4]; + struct tmp421_channel channel[MAX_CHANNELS]; }; -static int temp_from_s16(s16 reg) +static int temp_from_raw(u16 reg, bool extended) { /* Mask out status bits */ int temp = reg & ~0xf; - return (temp * 1000 + 128) / 256; -} - -static int temp_from_u16(u16 reg) -{ - /* Mask out status bits */ - int temp = reg & ~0xf; - - /* Add offset for extended temperature range. */ - temp -= 64 * 256; + if (extended) + temp = temp - 64 * 256; + else + temp = (s16)temp; - return (temp * 1000 + 128) / 256; + return DIV_ROUND_CLOSEST(temp * 1000, 256); } -static struct tmp421_data *tmp421_update_device(struct device *dev) +static int tmp421_update_device(struct tmp421_data *data) { - struct tmp421_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; + int ret = 0; int i; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + (HZ / 2)) || !data->valid) { - data->config = i2c_smbus_read_byte_data(client, - TMP421_CONFIG_REG_1); + ret = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1); + if (ret < 0) + goto exit; + data->config = ret; for (i = 0; i < data->channels; i++) { - data->temp[i] = i2c_smbus_read_byte_data(client, - TMP421_TEMP_MSB[i]) << 8; - data->temp[i] |= i2c_smbus_read_byte_data(client, - TMP421_TEMP_LSB[i]); + ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_MSB[i]); + if (ret < 0) + goto exit; + data->channel[i].temp = ret << 8; + + ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_LSB[i]); + if (ret < 0) + goto exit; + data->channel[i].temp |= ret; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } +exit: mutex_unlock(&data->update_lock); - return data; + if (ret < 0) { + data->valid = false; + return ret; + } + + return 0; +} + +static int tmp421_enable_channels(struct tmp421_data *data) +{ + int err; + struct i2c_client *client = data->client; + struct device *dev = &client->dev; + int old = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_2); + int new, i; + + if (old < 0) { + dev_err(dev, "error reading register, can't disable channels\n"); + return old; + } + + new = old & ~TMP421_CONFIG_REG_REN_MASK; + for (i = 0; i < data->channels; i++) + if (data->channel[i].enabled) + new |= TMP421_CONFIG_REG_REN(i); + + if (new == old) + return 0; + + err = i2c_smbus_write_byte_data(client, TMP421_CONFIG_REG_2, new); + if (err < 0) + dev_err(dev, "error writing register, can't disable channels\n"); + + return err; } static int tmp421_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { - struct tmp421_data *tmp421 = tmp421_update_device(dev); + struct tmp421_data *tmp421 = dev_get_drvdata(dev); + int ret = 0; + + ret = tmp421_update_device(tmp421); + if (ret) + return ret; switch (attr) { case hwmon_temp_input: - if (tmp421->config & TMP421_CONFIG_RANGE) - *val = temp_from_u16(tmp421->temp[channel]); - else - *val = temp_from_s16(tmp421->temp[channel]); + if (!tmp421->channel[channel].enabled) + return -ENODATA; + *val = temp_from_raw(tmp421->channel[channel].temp, + tmp421->config & TMP421_CONFIG_RANGE); return 0; case hwmon_temp_fault: + if (!tmp421->channel[channel].enabled) + return -ENODATA; /* - * The OPEN bit signals a fault. This is bit 0 of the temperature - * register (low byte). + * Any of OPEN or /PVLD bits indicate a hardware mulfunction + * and the conversion result may be incorrect */ - *val = tmp421->temp[channel] & 0x01; + *val = !!(tmp421->channel[channel].temp & 0x03); + return 0; + case hwmon_temp_enable: + *val = tmp421->channel[channel].enabled; return 0; default: return -EOPNOTSUPP; @@ -172,24 +228,54 @@ static int tmp421_read(struct device *dev, enum hwmon_sensor_types type, } +static int tmp421_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct tmp421_data *data = dev_get_drvdata(dev); + + *str = data->channel[channel].label; + + return 0; +} + +static int tmp421_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct tmp421_data *data = dev_get_drvdata(dev); + int ret; + + switch (attr) { + case hwmon_temp_enable: + data->channel[channel].enabled = val; + ret = tmp421_enable_channels(data); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) { switch (attr) { case hwmon_temp_fault: - if (channel == 0) - return 0; - return 0444; case hwmon_temp_input: return 0444; + case hwmon_temp_label: + return 0444; + case hwmon_temp_enable: + return 0644; default: return 0; } } -static int tmp421_init_client(struct i2c_client *client) +static int tmp421_init_client(struct tmp421_data *data) { int config, config_orig; + struct i2c_client *client = data->client; /* Set the conversion rate to 2 Hz */ i2c_smbus_write_byte_data(client, TMP421_CONVERSION_RATE_REG, 0x05); @@ -210,7 +296,7 @@ static int tmp421_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, TMP421_CONFIG_REG_1, config); } - return 0; + return tmp421_enable_channels(data); } static int tmp421_detect(struct i2c_client *client, @@ -267,20 +353,88 @@ static int tmp421_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE); + strscpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", names[kind], client->addr); return 0; } +static int tmp421_probe_child_from_dt(struct i2c_client *client, + struct device_node *child, + struct tmp421_data *data) + +{ + struct device *dev = &client->dev; + u32 i; + s32 val; + int err; + + err = of_property_read_u32(child, "reg", &i); + if (err) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return err; + } + + if (i >= data->channels) { + dev_err(dev, "invalid reg %d of %pOFn\n", i, child); + return -EINVAL; + } + + of_property_read_string(child, "label", &data->channel[i].label); + if (data->channel[i].label) + data->temp_config[i] |= HWMON_T_LABEL; + + data->channel[i].enabled = of_device_is_available(child); + + err = of_property_read_s32(child, "ti,n-factor", &val); + if (!err) { + if (i == 0) { + dev_err(dev, "n-factor can't be set for internal channel\n"); + return -EINVAL; + } + + if (val > 127 || val < -128) { + dev_err(dev, "n-factor for channel %d invalid (%d)\n", + i, val); + return -EINVAL; + } + i2c_smbus_write_byte_data(client, TMP421_N_FACTOR_REG_1 + i - 1, + val); + } + + return 0; +} + +static int tmp421_probe_from_dt(struct i2c_client *client, struct tmp421_data *data) +{ + struct device *dev = &client->dev; + const struct device_node *np = dev->of_node; + struct device_node *child; + int err; + + for_each_child_of_node(np, child) { + if (strcmp(child->name, "channel")) + continue; + + err = tmp421_probe_child_from_dt(client, child, data); + if (err) { + of_node_put(child); + return err; + } + } + + return 0; +} + static const struct hwmon_ops tmp421_ops = { .is_visible = tmp421_is_visible, .read = tmp421_read, + .read_string = tmp421_read_string, + .write = tmp421_write, }; -static int tmp421_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tmp421_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -296,15 +450,21 @@ static int tmp421_probe(struct i2c_client *client, data->channels = (unsigned long) of_device_get_match_data(&client->dev); else - data->channels = id->driver_data; + data->channels = i2c_match_id(tmp421_id, client)->driver_data; data->client = client; - err = tmp421_init_client(client); + for (i = 0; i < data->channels; i++) { + data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_ENABLE; + data->channel[i].enabled = true; + } + + err = tmp421_probe_from_dt(client, data); if (err) return err; - for (i = 0; i < data->channels; i++) - data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT; + err = tmp421_init_client(data); + if (err) + return err; data->chip.ops = &tmp421_ops; data->chip.info = data->info; @@ -327,7 +487,7 @@ static struct i2c_driver tmp421_driver = { .name = "tmp421", .of_match_table = of_match_ptr(tmp421_of_match), }, - .probe = tmp421_probe, + .probe_new = tmp421_probe, .id_table = tmp421_id, .detect = tmp421_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c new file mode 100644 index 000000000000..7814f39bd1a3 --- /dev/null +++ b/drivers/hwmon/tmp464.c @@ -0,0 +1,712 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* Driver for the Texas Instruments TMP464 SMBus temperature sensor IC. + * Supported models: TMP464, TMP468 + + * Copyright (C) 2022 Agathe Porte <agathe.porte@nokia.com> + * Preliminary support by: + * Lionel Pouliquen <lionel.lp.pouliquen@nokia.com> + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END }; + +#define TMP464_NUM_CHANNELS 5 /* chan 0 is internal, 1-4 are remote */ +#define TMP468_NUM_CHANNELS 9 /* chan 0 is internal, 1-8 are remote */ + +#define MAX_CHANNELS 9 + +#define TMP464_TEMP_REG(channel) (channel) +#define TMP464_TEMP_OFFSET_REG(channel) (0x40 + ((channel) - 1) * 8) +#define TMP464_N_FACTOR_REG(channel) (0x41 + ((channel) - 1) * 8) + +static const u8 TMP464_THERM_LIMIT[MAX_CHANNELS] = { + 0x39, 0x42, 0x4A, 0x52, 0x5A, 0x62, 0x6a, 0x72, 0x7a }; +static const u8 TMP464_THERM2_LIMIT[MAX_CHANNELS] = { + 0x3A, 0x43, 0x4B, 0x53, 0x5B, 0x63, 0x6b, 0x73, 0x7b }; + +#define TMP464_THERM_STATUS_REG 0x21 +#define TMP464_THERM2_STATUS_REG 0x22 +#define TMP464_REMOTE_OPEN_REG 0x23 +#define TMP464_CONFIG_REG 0x30 +#define TMP464_TEMP_HYST_REG 0x38 +#define TMP464_LOCK_REG 0xc4 + +/* Identification */ +#define TMP464_MANUFACTURER_ID_REG 0xFE +#define TMP464_DEVICE_ID_REG 0xFF + +/* Flags */ +#define TMP464_CONFIG_SHUTDOWN BIT(5) +#define TMP464_CONFIG_RANGE 0x04 +#define TMP464_CONFIG_REG_REN(x) (BIT(7 + (x))) +#define TMP464_CONFIG_REG_REN_MASK GENMASK(15, 7) +#define TMP464_CONFIG_CONVERSION_RATE_B0 2 +#define TMP464_CONFIG_CONVERSION_RATE_B2 4 +#define TMP464_CONFIG_CONVERSION_RATE_MASK GENMASK(TMP464_CONFIG_CONVERSION_RATE_B2, \ + TMP464_CONFIG_CONVERSION_RATE_B0) + +#define TMP464_UNLOCK_VAL 0xeb19 +#define TMP464_LOCK_VAL 0x5ca6 +#define TMP464_LOCKED 0x8000 + +/* Manufacturer / Device ID's */ +#define TMP464_MANUFACTURER_ID 0x5449 +#define TMP464_DEVICE_ID 0x1468 +#define TMP468_DEVICE_ID 0x0468 + +static const struct i2c_device_id tmp464_id[] = { + { "tmp464", TMP464_NUM_CHANNELS }, + { "tmp468", TMP468_NUM_CHANNELS }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp464_id); + +static const struct of_device_id __maybe_unused tmp464_of_match[] = { + { + .compatible = "ti,tmp464", + .data = (void *)TMP464_NUM_CHANNELS + }, + { + .compatible = "ti,tmp468", + .data = (void *)TMP468_NUM_CHANNELS + }, + {}, +}; +MODULE_DEVICE_TABLE(of, tmp464_of_match); + +struct tmp464_channel { + const char *label; + bool enabled; +}; + +struct tmp464_data { + struct regmap *regmap; + struct mutex update_lock; + int channels; + s16 config_orig; + u16 open_reg; + unsigned long last_updated; + bool valid; + int update_interval; + struct tmp464_channel channel[MAX_CHANNELS]; +}; + +static int temp_from_reg(s16 reg) +{ + return DIV_ROUND_CLOSEST((reg >> 3) * 625, 10); +} + +static s16 temp_to_limit_reg(long temp) +{ + return DIV_ROUND_CLOSEST(temp, 500) << 6; +} + +static s16 temp_to_offset_reg(long temp) +{ + return DIV_ROUND_CLOSEST(temp * 10, 625) << 3; +} + +static int tmp464_enable_channels(struct tmp464_data *data) +{ + struct regmap *regmap = data->regmap; + u16 enable = 0; + int i; + + for (i = 0; i < data->channels; i++) + if (data->channel[i].enabled) + enable |= TMP464_CONFIG_REG_REN(i); + + return regmap_update_bits(regmap, TMP464_CONFIG_REG, TMP464_CONFIG_REG_REN_MASK, enable); +} + +static int tmp464_chip_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct tmp464_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_update_interval: + *val = data->update_interval; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct tmp464_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval, regval2; + int err = 0; + + mutex_lock(&data->update_lock); + + switch (attr) { + case hwmon_temp_max_alarm: + err = regmap_read(regmap, TMP464_THERM_STATUS_REG, ®val); + if (err < 0) + break; + *val = !!(regval & BIT(channel + 7)); + break; + case hwmon_temp_crit_alarm: + err = regmap_read(regmap, TMP464_THERM2_STATUS_REG, ®val); + if (err < 0) + break; + *val = !!(regval & BIT(channel + 7)); + break; + case hwmon_temp_fault: + /* + * The chip clears TMP464_REMOTE_OPEN_REG after it is read + * and only updates it after the next measurement cycle is + * complete. That means we have to cache the value internally + * for one measurement cycle and report the cached value. + */ + if (!data->valid || time_after(jiffies, data->last_updated + + msecs_to_jiffies(data->update_interval))) { + err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, ®val); + if (err < 0) + break; + data->open_reg = regval; + data->last_updated = jiffies; + data->valid = true; + } + *val = !!(data->open_reg & BIT(channel + 7)); + break; + case hwmon_temp_max_hyst: + err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val); + if (err < 0) + break; + err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); + if (err < 0) + break; + regval -= regval2; + *val = temp_from_reg(regval); + break; + case hwmon_temp_max: + err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val); + if (err < 0) + break; + *val = temp_from_reg(regval); + break; + case hwmon_temp_crit_hyst: + err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val); + if (err < 0) + break; + err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); + if (err < 0) + break; + regval -= regval2; + *val = temp_from_reg(regval); + break; + case hwmon_temp_crit: + err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val); + if (err < 0) + break; + *val = temp_from_reg(regval); + break; + case hwmon_temp_offset: + err = regmap_read(regmap, TMP464_TEMP_OFFSET_REG(channel), ®val); + if (err < 0) + break; + *val = temp_from_reg(regval); + break; + case hwmon_temp_input: + if (!data->channel[channel].enabled) { + err = -ENODATA; + break; + } + err = regmap_read(regmap, TMP464_TEMP_REG(channel), ®val); + if (err < 0) + break; + *val = temp_from_reg(regval); + break; + case hwmon_temp_enable: + *val = data->channel[channel].enabled; + break; + default: + err = -EOPNOTSUPP; + break; + } + + mutex_unlock(&data->update_lock); + + return err; +} + +static int tmp464_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return tmp464_chip_read(dev, attr, channel, val); + case hwmon_temp: + return tmp464_temp_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int tmp464_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct tmp464_data *data = dev_get_drvdata(dev); + + *str = data->channel[channel].label; + + return 0; +} + +static int tmp464_set_convrate(struct tmp464_data *data, long interval) +{ + int rate; + + /* + * For valid rates, interval in milli-seconds can be calculated as + * interval = 125 << (7 - rate); + * or + * interval = (1 << (7 - rate)) * 125; + * The rate is therefore + * rate = 7 - __fls(interval / 125); + * and the rounded rate is + * rate = 7 - __fls(interval * 4 / (125 * 3)); + * Use clamp_val() to avoid overflows, and to ensure valid input + * for __fls. + */ + interval = clamp_val(interval, 125, 16000); + rate = 7 - __fls(interval * 4 / (125 * 3)); + data->update_interval = 125 << (7 - rate); + + return regmap_update_bits(data->regmap, TMP464_CONFIG_REG, + TMP464_CONFIG_CONVERSION_RATE_MASK, + rate << TMP464_CONFIG_CONVERSION_RATE_B0); +} + +static int tmp464_chip_write(struct tmp464_data *data, u32 attr, int channel, long val) +{ + switch (attr) { + case hwmon_chip_update_interval: + return tmp464_set_convrate(data, val); + default: + return -EOPNOTSUPP; + } +} + +static int tmp464_temp_write(struct tmp464_data *data, u32 attr, int channel, long val) +{ + struct regmap *regmap = data->regmap; + unsigned int regval; + int err = 0; + + switch (attr) { + case hwmon_temp_max_hyst: + err = regmap_read(regmap, TMP464_THERM_LIMIT[0], ®val); + if (err < 0) + break; + val = clamp_val(val, -256000, 256000); /* prevent overflow/underflow */ + val = clamp_val(temp_from_reg(regval) - val, 0, 255000); + err = regmap_write(regmap, TMP464_TEMP_HYST_REG, + DIV_ROUND_CLOSEST(val, 1000) << 7); + break; + case hwmon_temp_max: + val = temp_to_limit_reg(clamp_val(val, -255000, 255500)); + err = regmap_write(regmap, TMP464_THERM_LIMIT[channel], val); + break; + case hwmon_temp_crit: + val = temp_to_limit_reg(clamp_val(val, -255000, 255500)); + err = regmap_write(regmap, TMP464_THERM2_LIMIT[channel], val); + break; + case hwmon_temp_offset: + val = temp_to_offset_reg(clamp_val(val, -128000, 127937)); + err = regmap_write(regmap, TMP464_TEMP_OFFSET_REG(channel), val); + break; + case hwmon_temp_enable: + data->channel[channel].enabled = !!val; + err = tmp464_enable_channels(data); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int tmp464_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct tmp464_data *data = dev_get_drvdata(dev); + int err; + + mutex_lock(&data->update_lock); + + switch (type) { + case hwmon_chip: + err = tmp464_chip_write(data, attr, channel, val); + break; + case hwmon_temp: + err = tmp464_temp_write(data, attr, channel, val); + break; + default: + err = -EOPNOTSUPP; + break; + } + + mutex_unlock(&data->update_lock); + + return err; +} + +static umode_t tmp464_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct tmp464_data *data = _data; + + if (channel >= data->channels) + return 0; + + if (type == hwmon_chip) { + if (attr == hwmon_chip_update_interval) + return 0644; + return 0; + } + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_crit_hyst: + return 0444; + case hwmon_temp_enable: + case hwmon_temp_max: + case hwmon_temp_crit: + return 0644; + case hwmon_temp_max_hyst: + if (!channel) + return 0644; + return 0444; + case hwmon_temp_label: + if (data->channel[channel].label) + return 0444; + return 0; + case hwmon_temp_fault: + if (channel) + return 0444; + return 0; + case hwmon_temp_offset: + if (channel) + return 0644; + return 0; + default: + return 0; + } +} + +static void tmp464_restore_lock(void *regmap) +{ + regmap_write(regmap, TMP464_LOCK_REG, TMP464_LOCK_VAL); +} + +static void tmp464_restore_config(void *_data) +{ + struct tmp464_data *data = _data; + + regmap_write(data->regmap, TMP464_CONFIG_REG, data->config_orig); +} + +static int tmp464_init_client(struct device *dev, struct tmp464_data *data) +{ + struct regmap *regmap = data->regmap; + unsigned int regval; + int err; + + err = regmap_read(regmap, TMP464_LOCK_REG, ®val); + if (err) + return err; + if (regval == TMP464_LOCKED) { + /* Explicitly unlock chip if it is locked */ + err = regmap_write(regmap, TMP464_LOCK_REG, TMP464_UNLOCK_VAL); + if (err) + return err; + /* and lock it again when unloading the driver */ + err = devm_add_action_or_reset(dev, tmp464_restore_lock, regmap); + if (err) + return err; + } + + err = regmap_read(regmap, TMP464_CONFIG_REG, ®val); + if (err) + return err; + data->config_orig = regval; + err = devm_add_action_or_reset(dev, tmp464_restore_config, data); + if (err) + return err; + + /* Default to 500 ms update interval */ + err = regmap_update_bits(regmap, TMP464_CONFIG_REG, + TMP464_CONFIG_CONVERSION_RATE_MASK | TMP464_CONFIG_SHUTDOWN, + BIT(TMP464_CONFIG_CONVERSION_RATE_B0) | + BIT(TMP464_CONFIG_CONVERSION_RATE_B2)); + if (err) + return err; + + data->update_interval = 500; + + return tmp464_enable_channels(data); +} + +static int tmp464_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + char *name, *chip; + int reg; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + reg = i2c_smbus_read_word_swapped(client, TMP464_MANUFACTURER_ID_REG); + if (reg < 0) + return reg; + if (reg != TMP464_MANUFACTURER_ID) + return -ENODEV; + + /* Check for "always return zero" bits */ + reg = i2c_smbus_read_word_swapped(client, TMP464_THERM_STATUS_REG); + if (reg < 0) + return reg; + if (reg & 0x1f) + return -ENODEV; + reg = i2c_smbus_read_word_swapped(client, TMP464_THERM2_STATUS_REG); + if (reg < 0) + return reg; + if (reg & 0x1f) + return -ENODEV; + + reg = i2c_smbus_read_word_swapped(client, TMP464_DEVICE_ID_REG); + if (reg < 0) + return reg; + switch (reg) { + case TMP464_DEVICE_ID: + name = "tmp464"; + chip = "TMP464"; + break; + case TMP468_DEVICE_ID: + name = "tmp468"; + chip = "TMP468"; + break; + default: + return -ENODEV; + } + + strscpy(info->type, name, I2C_NAME_SIZE); + dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", chip, client->addr); + + return 0; +} + +static int tmp464_probe_child_from_dt(struct device *dev, + struct device_node *child, + struct tmp464_data *data) + +{ + struct regmap *regmap = data->regmap; + u32 channel; + s32 nfactor; + int err; + + err = of_property_read_u32(child, "reg", &channel); + if (err) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return err; + } + + if (channel >= data->channels) { + dev_err(dev, "invalid reg %d of %pOFn\n", channel, child); + return -EINVAL; + } + + of_property_read_string(child, "label", &data->channel[channel].label); + + data->channel[channel].enabled = of_device_is_available(child); + + err = of_property_read_s32(child, "ti,n-factor", &nfactor); + if (err && err != -EINVAL) + return err; + if (!err) { + if (channel == 0) { + dev_err(dev, "n-factor can't be set for internal channel\n"); + return -EINVAL; + } + if (nfactor > 127 || nfactor < -128) { + dev_err(dev, "n-factor for channel %d invalid (%d)\n", + channel, nfactor); + return -EINVAL; + } + err = regmap_write(regmap, TMP464_N_FACTOR_REG(channel), + (nfactor << 8) & 0xff00); + if (err) + return err; + } + + return 0; +} + +static int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data) +{ + const struct device_node *np = dev->of_node; + struct device_node *child; + int err; + + for_each_child_of_node(np, child) { + if (strcmp(child->name, "channel")) + continue; + + err = tmp464_probe_child_from_dt(dev, child, data); + if (err) { + of_node_put(child); + return err; + } + } + + return 0; +} + +static const struct hwmon_ops tmp464_ops = { + .is_visible = tmp464_is_visible, + .read = tmp464_read, + .read_string = tmp464_read_string, + .write = tmp464_write, +}; + +static const struct hwmon_channel_info *tmp464_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT | + HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE, + HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE), + NULL +}; + +static const struct hwmon_chip_info tmp464_chip_info = { + .ops = &tmp464_ops, + .info = tmp464_info, +}; + +/* regmap */ + +static bool tmp464_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (reg < TMP464_TEMP_REG(TMP468_NUM_CHANNELS) || + reg == TMP464_THERM_STATUS_REG || + reg == TMP464_THERM2_STATUS_REG || + reg == TMP464_REMOTE_OPEN_REG); +} + +static const struct regmap_config tmp464_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = TMP464_DEVICE_ID_REG, + .volatile_reg = tmp464_is_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static int tmp464_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct tmp464_data *data; + int i, err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, "i2c functionality check failed\n"); + return -ENODEV; + } + data = devm_kzalloc(dev, sizeof(struct tmp464_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->update_lock); + + if (dev->of_node) + data->channels = (int)(unsigned long)of_device_get_match_data(&client->dev); + else + data->channels = i2c_match_id(tmp464_id, client)->driver_data; + + data->regmap = devm_regmap_init_i2c(client, &tmp464_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + for (i = 0; i < data->channels; i++) + data->channel[i].enabled = true; + + err = tmp464_init_client(dev, data); + if (err) + return err; + + if (dev->of_node) { + err = tmp464_probe_from_dt(dev, data); + if (err) + return err; + } + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &tmp464_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static struct i2c_driver tmp464_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "tmp464", + .of_match_table = of_match_ptr(tmp464_of_match), + }, + .probe_new = tmp464_probe, + .id_table = tmp464_id, + .detect = tmp464_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(tmp464_driver); + +MODULE_AUTHOR("Agathe Porte <agathe.porte@nokia.com>"); +MODULE_DESCRIPTION("Texas Instruments TMP464 temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c index df66e0bc1253..47bbe47e062f 100644 --- a/drivers/hwmon/tmp513.c +++ b/drivers/hwmon/tmp513.c @@ -5,12 +5,12 @@ * TMP513: * Thermal/Power Management with Triple Remote and * Local Temperature Sensor and Current Shunt Monitor - * Datasheet: http://www.ti.com/lit/gpn/tmp513 + * Datasheet: https://www.ti.com/lit/gpn/tmp513 * * TMP512: * Thermal/Power Management with Dual Remote * and Local Temperature Sensor and Current Shunt Monitor - * Datasheet: http://www.ti.com/lit/gpn/tmp512 + * Datasheet: https://www.ti.com/lit/gpn/tmp512 * * Copyright (C) 2019 Eric Tremblay <etremblay@distech-controls.com> * @@ -192,7 +192,7 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos, /* * The valus is read in voltage in the chip but reported as * current to the user. - * 2's compliment number shifted by one to four depending + * 2's complement number shifted by one to four depending * on the pga gain setting. 1lsb = 10uV */ *val = sign_extend32(regval, 17 - tmp51x_get_pga_shift(data)); @@ -709,8 +709,7 @@ static int tmp51x_configure(struct device *dev, struct tmp51x_data *data) return 0; } -static int tmp51x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tmp51x_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tmp51x_data *data; @@ -724,7 +723,7 @@ static int tmp51x_probe(struct i2c_client *client, if (client->dev.of_node) data->id = (enum tmp51x_ids)device_get_match_data(&client->dev); else - data->id = id->driver_data; + data->id = i2c_match_id(tmp51x_id, client)->driver_data; ret = tmp51x_configure(dev, data); if (ret < 0) { @@ -751,7 +750,7 @@ static int tmp51x_probe(struct i2c_client *client, if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - dev_dbg(dev, "power monitor %s\n", id->name); + dev_dbg(dev, "power monitor %s\n", client->name); return 0; } @@ -761,7 +760,7 @@ static struct i2c_driver tmp51x_driver = { .name = "tmp51x", .of_match_table = of_match_ptr(tmp51x_of_match), }, - .probe = tmp51x_probe, + .probe_new = tmp51x_probe, .id_table = tmp51x_id, }; diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c new file mode 100644 index 000000000000..68c77c493270 --- /dev/null +++ b/drivers/hwmon/tps23861.c @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 Sartura Ltd. + * + * Driver for the TI TPS23861 PoE PSE. + * + * Author: Robert Marko <robert.marko@sartura.hr> + */ + +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +#define TEMPERATURE 0x2c +#define INPUT_VOLTAGE_LSB 0x2e +#define INPUT_VOLTAGE_MSB 0x2f +#define PORT_1_CURRENT_LSB 0x30 +#define PORT_1_CURRENT_MSB 0x31 +#define PORT_1_VOLTAGE_LSB 0x32 +#define PORT_1_VOLTAGE_MSB 0x33 +#define PORT_2_CURRENT_LSB 0x34 +#define PORT_2_CURRENT_MSB 0x35 +#define PORT_2_VOLTAGE_LSB 0x36 +#define PORT_2_VOLTAGE_MSB 0x37 +#define PORT_3_CURRENT_LSB 0x38 +#define PORT_3_CURRENT_MSB 0x39 +#define PORT_3_VOLTAGE_LSB 0x3a +#define PORT_3_VOLTAGE_MSB 0x3b +#define PORT_4_CURRENT_LSB 0x3c +#define PORT_4_CURRENT_MSB 0x3d +#define PORT_4_VOLTAGE_LSB 0x3e +#define PORT_4_VOLTAGE_MSB 0x3f +#define PORT_N_CURRENT_LSB_OFFSET 0x04 +#define PORT_N_VOLTAGE_LSB_OFFSET 0x04 +#define VOLTAGE_CURRENT_MASK GENMASK(13, 0) +#define PORT_1_RESISTANCE_LSB 0x60 +#define PORT_1_RESISTANCE_MSB 0x61 +#define PORT_2_RESISTANCE_LSB 0x62 +#define PORT_2_RESISTANCE_MSB 0x63 +#define PORT_3_RESISTANCE_LSB 0x64 +#define PORT_3_RESISTANCE_MSB 0x65 +#define PORT_4_RESISTANCE_LSB 0x66 +#define PORT_4_RESISTANCE_MSB 0x67 +#define PORT_N_RESISTANCE_LSB_OFFSET 0x02 +#define PORT_RESISTANCE_MASK GENMASK(13, 0) +#define PORT_RESISTANCE_RSN_MASK GENMASK(15, 14) +#define PORT_RESISTANCE_RSN_OTHER 0 +#define PORT_RESISTANCE_RSN_LOW 1 +#define PORT_RESISTANCE_RSN_OPEN 2 +#define PORT_RESISTANCE_RSN_SHORT 3 +#define PORT_1_STATUS 0x0c +#define PORT_2_STATUS 0x0d +#define PORT_3_STATUS 0x0e +#define PORT_4_STATUS 0x0f +#define PORT_STATUS_CLASS_MASK GENMASK(7, 4) +#define PORT_STATUS_DETECT_MASK GENMASK(3, 0) +#define PORT_CLASS_UNKNOWN 0 +#define PORT_CLASS_1 1 +#define PORT_CLASS_2 2 +#define PORT_CLASS_3 3 +#define PORT_CLASS_4 4 +#define PORT_CLASS_RESERVED 5 +#define PORT_CLASS_0 6 +#define PORT_CLASS_OVERCURRENT 7 +#define PORT_CLASS_MISMATCH 8 +#define PORT_DETECT_UNKNOWN 0 +#define PORT_DETECT_SHORT 1 +#define PORT_DETECT_RESERVED 2 +#define PORT_DETECT_RESISTANCE_LOW 3 +#define PORT_DETECT_RESISTANCE_OK 4 +#define PORT_DETECT_RESISTANCE_HIGH 5 +#define PORT_DETECT_OPEN_CIRCUIT 6 +#define PORT_DETECT_RESERVED_2 7 +#define PORT_DETECT_MOSFET_FAULT 8 +#define PORT_DETECT_LEGACY 9 +/* Measurment beyond clamp voltage */ +#define PORT_DETECT_CAPACITANCE_INVALID_BEYOND 10 +/* Insufficient voltage delta */ +#define PORT_DETECT_CAPACITANCE_INVALID_DELTA 11 +#define PORT_DETECT_CAPACITANCE_OUT_OF_RANGE 12 +#define POE_PLUS 0x40 +#define OPERATING_MODE 0x12 +#define OPERATING_MODE_OFF 0 +#define OPERATING_MODE_MANUAL 1 +#define OPERATING_MODE_SEMI 2 +#define OPERATING_MODE_AUTO 3 +#define OPERATING_MODE_PORT_1_MASK GENMASK(1, 0) +#define OPERATING_MODE_PORT_2_MASK GENMASK(3, 2) +#define OPERATING_MODE_PORT_3_MASK GENMASK(5, 4) +#define OPERATING_MODE_PORT_4_MASK GENMASK(7, 6) + +#define DETECT_CLASS_RESTART 0x18 +#define POWER_ENABLE 0x19 +#define TPS23861_NUM_PORTS 4 + +#define TPS23861_GENERAL_MASK_1 0x17 +#define TPS23861_CURRENT_SHUNT_MASK BIT(0) + +#define TEMPERATURE_LSB 652 /* 0.652 degrees Celsius */ +#define VOLTAGE_LSB 3662 /* 3.662 mV */ +#define SHUNT_RESISTOR_DEFAULT 255000 /* 255 mOhm */ +#define CURRENT_LSB_250 62260 /* 62.260 uA */ +#define CURRENT_LSB_255 61039 /* 61.039 uA */ +#define RESISTANCE_LSB 110966 /* 11.0966 Ohm*/ +#define RESISTANCE_LSB_LOW 157216 /* 15.7216 Ohm*/ + +struct tps23861_data { + struct regmap *regmap; + u32 shunt_resistor; + struct i2c_client *client; + struct dentry *debugfs_dir; +}; + +static struct regmap_config tps23861_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x6f, +}; + +static int tps23861_read_temp(struct tps23861_data *data, long *val) +{ + unsigned int regval; + int err; + + err = regmap_read(data->regmap, TEMPERATURE, ®val); + if (err < 0) + return err; + + *val = (regval * TEMPERATURE_LSB) - 20000; + + return 0; +} + +static int tps23861_read_voltage(struct tps23861_data *data, int channel, + long *val) +{ + __le16 regval; + long raw_val; + int err; + + if (channel < TPS23861_NUM_PORTS) { + err = regmap_bulk_read(data->regmap, + PORT_1_VOLTAGE_LSB + channel * PORT_N_VOLTAGE_LSB_OFFSET, + ®val, 2); + } else { + err = regmap_bulk_read(data->regmap, + INPUT_VOLTAGE_LSB, + ®val, 2); + } + if (err < 0) + return err; + + raw_val = le16_to_cpu(regval); + *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * VOLTAGE_LSB) / 1000; + + return 0; +} + +static int tps23861_read_current(struct tps23861_data *data, int channel, + long *val) +{ + long raw_val, current_lsb; + __le16 regval; + + int err; + + if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) + current_lsb = CURRENT_LSB_255; + else + current_lsb = CURRENT_LSB_250; + + err = regmap_bulk_read(data->regmap, + PORT_1_CURRENT_LSB + channel * PORT_N_CURRENT_LSB_OFFSET, + ®val, 2); + if (err < 0) + return err; + + raw_val = le16_to_cpu(regval); + *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * current_lsb) / 1000000; + + return 0; +} + +static int tps23861_port_disable(struct tps23861_data *data, int channel) +{ + unsigned int regval = 0; + int err; + + regval |= BIT(channel + 4); + err = regmap_write(data->regmap, POWER_ENABLE, regval); + + return err; +} + +static int tps23861_port_enable(struct tps23861_data *data, int channel) +{ + unsigned int regval = 0; + int err; + + regval |= BIT(channel); + regval |= BIT(channel + 4); + err = regmap_write(data->regmap, DETECT_CLASS_RESTART, regval); + + return err; +} + +static umode_t tps23861_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + default: + return 0; + } + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + return 0444; + case hwmon_in_enable: + return 0200; + default: + return 0; + } + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_label: + return 0444; + default: + return 0; + } + default: + return 0; + } +} + +static int tps23861_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct tps23861_data *data = dev_get_drvdata(dev); + int err; + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_enable: + if (val == 0) + err = tps23861_port_disable(data, channel); + else if (val == 1) + err = tps23861_port_enable(data, channel); + else + err = -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return err; +} + +static int tps23861_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct tps23861_data *data = dev_get_drvdata(dev); + int err; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + err = tps23861_read_temp(data, val); + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + err = tps23861_read_voltage(data, channel, val); + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + err = tps23861_read_current(data, channel, val); + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return err; +} + +static const char * const tps23861_port_label[] = { + "Port1", + "Port2", + "Port3", + "Port4", + "Input", +}; + +static int tps23861_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_in: + case hwmon_curr: + *str = tps23861_port_label[channel]; + break; + case hwmon_temp: + *str = "Die"; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct hwmon_channel_info *tps23861_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_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), + NULL +}; + +static const struct hwmon_ops tps23861_hwmon_ops = { + .is_visible = tps23861_is_visible, + .write = tps23861_write, + .read = tps23861_read, + .read_string = tps23861_read_string, +}; + +static const struct hwmon_chip_info tps23861_chip_info = { + .ops = &tps23861_hwmon_ops, + .info = tps23861_info, +}; + +static char *port_operating_mode_string(uint8_t mode_reg, unsigned int port) +{ + unsigned int mode = ~0; + + if (port < TPS23861_NUM_PORTS) + mode = (mode_reg >> (2 * port)) & OPERATING_MODE_PORT_1_MASK; + + switch (mode) { + case OPERATING_MODE_OFF: + return "Off"; + case OPERATING_MODE_MANUAL: + return "Manual"; + case OPERATING_MODE_SEMI: + return "Semi-Auto"; + case OPERATING_MODE_AUTO: + return "Auto"; + default: + return "Invalid"; + } +} + +static char *port_detect_status_string(uint8_t status_reg) +{ + switch (FIELD_GET(PORT_STATUS_DETECT_MASK, status_reg)) { + case PORT_DETECT_UNKNOWN: + return "Unknown device"; + case PORT_DETECT_SHORT: + return "Short circuit"; + case PORT_DETECT_RESISTANCE_LOW: + return "Too low resistance"; + case PORT_DETECT_RESISTANCE_OK: + return "Valid resistance"; + case PORT_DETECT_RESISTANCE_HIGH: + return "Too high resistance"; + case PORT_DETECT_OPEN_CIRCUIT: + return "Open circuit"; + case PORT_DETECT_MOSFET_FAULT: + return "MOSFET fault"; + case PORT_DETECT_LEGACY: + return "Legacy device"; + case PORT_DETECT_CAPACITANCE_INVALID_BEYOND: + return "Invalid capacitance, beyond clamp voltage"; + case PORT_DETECT_CAPACITANCE_INVALID_DELTA: + return "Invalid capacitance, insufficient voltage delta"; + case PORT_DETECT_CAPACITANCE_OUT_OF_RANGE: + return "Valid capacitance, outside of legacy range"; + case PORT_DETECT_RESERVED: + case PORT_DETECT_RESERVED_2: + default: + return "Invalid"; + } +} + +static char *port_class_status_string(uint8_t status_reg) +{ + switch (FIELD_GET(PORT_STATUS_CLASS_MASK, status_reg)) { + case PORT_CLASS_UNKNOWN: + return "Unknown"; + case PORT_CLASS_RESERVED: + case PORT_CLASS_0: + return "0"; + case PORT_CLASS_1: + return "1"; + case PORT_CLASS_2: + return "2"; + case PORT_CLASS_3: + return "3"; + case PORT_CLASS_4: + return "4"; + case PORT_CLASS_OVERCURRENT: + return "Overcurrent"; + case PORT_CLASS_MISMATCH: + return "Mismatch"; + default: + return "Invalid"; + } +} + +static char *port_poe_plus_status_string(uint8_t poe_plus, unsigned int port) +{ + return (BIT(port + 4) & poe_plus) ? "Yes" : "No"; +} + +static int tps23861_port_resistance(struct tps23861_data *data, int port) +{ + unsigned int raw_val; + __le16 regval; + + regmap_bulk_read(data->regmap, + PORT_1_RESISTANCE_LSB + PORT_N_RESISTANCE_LSB_OFFSET * port, + ®val, + 2); + + raw_val = le16_to_cpu(regval); + switch (FIELD_GET(PORT_RESISTANCE_RSN_MASK, raw_val)) { + case PORT_RESISTANCE_RSN_OTHER: + return (FIELD_GET(PORT_RESISTANCE_MASK, raw_val) * RESISTANCE_LSB) / 10000; + case PORT_RESISTANCE_RSN_LOW: + return (FIELD_GET(PORT_RESISTANCE_MASK, raw_val) * RESISTANCE_LSB_LOW) / 10000; + case PORT_RESISTANCE_RSN_SHORT: + case PORT_RESISTANCE_RSN_OPEN: + default: + return 0; + } +} + +static int tps23861_port_status_show(struct seq_file *s, void *data) +{ + struct tps23861_data *priv = s->private; + unsigned int i, mode, poe_plus, status; + + regmap_read(priv->regmap, OPERATING_MODE, &mode); + regmap_read(priv->regmap, POE_PLUS, &poe_plus); + + for (i = 0; i < TPS23861_NUM_PORTS; i++) { + regmap_read(priv->regmap, PORT_1_STATUS + i, &status); + + seq_printf(s, "Port: \t\t%d\n", i + 1); + seq_printf(s, "Operating mode: %s\n", port_operating_mode_string(mode, i)); + seq_printf(s, "Detected: \t%s\n", port_detect_status_string(status)); + seq_printf(s, "Class: \t\t%s\n", port_class_status_string(status)); + seq_printf(s, "PoE Plus: \t%s\n", port_poe_plus_status_string(poe_plus, i)); + seq_printf(s, "Resistance: \t%d\n", tps23861_port_resistance(priv, i)); + seq_putc(s, '\n'); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(tps23861_port_status); + +static void tps23861_init_debugfs(struct tps23861_data *data, + struct device *hwmon_dev) +{ + const char *debugfs_name; + + debugfs_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "%s-%s", + data->client->name, dev_name(hwmon_dev)); + if (!debugfs_name) + return; + + data->debugfs_dir = debugfs_create_dir(debugfs_name, NULL); + + debugfs_create_file("port_status", + 0400, + data->debugfs_dir, + data, + &tps23861_port_status_fops); +} + +static int tps23861_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tps23861_data *data; + struct device *hwmon_dev; + u32 shunt_resistor; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + + data->regmap = devm_regmap_init_i2c(client, &tps23861_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + if (!of_property_read_u32(dev->of_node, "shunt-resistor-micro-ohms", &shunt_resistor)) + data->shunt_resistor = shunt_resistor; + else + data->shunt_resistor = SHUNT_RESISTOR_DEFAULT; + + if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) + regmap_clear_bits(data->regmap, + TPS23861_GENERAL_MASK_1, + TPS23861_CURRENT_SHUNT_MASK); + else + regmap_set_bits(data->regmap, + TPS23861_GENERAL_MASK_1, + TPS23861_CURRENT_SHUNT_MASK); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &tps23861_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + tps23861_init_debugfs(data, hwmon_dev); + + return 0; +} + +static void tps23861_remove(struct i2c_client *client) +{ + struct tps23861_data *data = i2c_get_clientdata(client); + + debugfs_remove_recursive(data->debugfs_dir); +} + +static const struct of_device_id __maybe_unused tps23861_of_match[] = { + { .compatible = "ti,tps23861", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tps23861_of_match); + +static struct i2c_driver tps23861_driver = { + .probe_new = tps23861_probe, + .remove = tps23861_remove, + .driver = { + .name = "tps23861", + .of_match_table = of_match_ptr(tps23861_of_match), + }, +}; +module_i2c_driver(tps23861_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); +MODULE_DESCRIPTION("TI TPS23861 PoE PSE"); diff --git a/drivers/hwmon/vexpress-hwmon.c b/drivers/hwmon/vexpress-hwmon.c index e7109657129a..2ac5fb96bba4 100644 --- a/drivers/hwmon/vexpress-hwmon.c +++ b/drivers/hwmon/vexpress-hwmon.c @@ -27,7 +27,7 @@ static ssize_t vexpress_hwmon_label_show(struct device *dev, { const char *label = of_get_property(dev->of_node, "label", NULL); - return snprintf(buffer, PAGE_SIZE, "%s\n", label); + return sysfs_emit(buffer, "%s\n", label); } static ssize_t vexpress_hwmon_u32_show(struct device *dev, @@ -41,8 +41,8 @@ static ssize_t vexpress_hwmon_u32_show(struct device *dev, if (err) return err; - return snprintf(buffer, PAGE_SIZE, "%u\n", value / - to_sensor_dev_attr(dev_attr)->index); + return sysfs_emit(buffer, "%u\n", value / + to_sensor_dev_attr(dev_attr)->index); } static ssize_t vexpress_hwmon_u64_show(struct device *dev, @@ -60,9 +60,9 @@ static ssize_t vexpress_hwmon_u64_show(struct device *dev, if (err) return err; - return snprintf(buffer, PAGE_SIZE, "%llu\n", - div_u64(((u64)value_hi << 32) | value_lo, - to_sensor_dev_attr(dev_attr)->index)); + return sysfs_emit(buffer, "%llu\n", + div_u64(((u64)value_hi << 32) | value_lo, + to_sensor_dev_attr(dev_attr)->index)); } static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, @@ -207,7 +207,6 @@ MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); static int vexpress_hwmon_probe(struct platform_device *pdev) { - const struct of_device_id *match; struct vexpress_hwmon_data *data; const struct vexpress_hwmon_type *type; @@ -216,10 +215,9 @@ static int vexpress_hwmon_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, data); - match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); - if (!match) + type = of_device_get_match_data(&pdev->dev); + if (!type) return -ENODEV; - type = match->data; data->reg = devm_regmap_init_vexpress_config(&pdev->dev); if (IS_ERR(data->reg)) diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 8264e849e588..e5d18dac8ee7 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -270,10 +270,10 @@ static int via_cputemp_down_prep(unsigned int cpu) } static const struct x86_cpu_id __initconst cputemp_ids[] = { - { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */ - { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */ - { X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */ - { X86_VENDOR_CENTAUR, 7, X86_MODEL_ANY, }, + X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_A, NULL), + X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_D, NULL), + X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_NANO, NULL), + X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, X86_MODEL_ANY, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index a2eddd2c2538..37d7374896f6 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -34,6 +34,8 @@ #include <linux/acpi.h> #include <linux/io.h> +#define DRIVER_NAME "via686a" + /* * If force_addr is set to anything different from 0, we forcibly enable * the device at the given address. @@ -304,7 +306,7 @@ struct via686a_data { const char *name; struct device *hwmon_dev; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[5]; /* Register value */ @@ -321,9 +323,6 @@ struct via686a_data { static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ -static int via686a_probe(struct platform_device *pdev); -static int via686a_remove(struct platform_device *pdev); - static inline int via686a_read_value(struct via686a_data *data, u8 reg) { return inb_p(data->addr + reg); @@ -335,8 +334,76 @@ static inline void via686a_write_value(struct via686a_data *data, u8 reg, outb_p(value, data->addr + reg); } -static struct via686a_data *via686a_update_device(struct device *dev); -static void via686a_init_device(struct via686a_data *data); +static void via686a_update_fan_div(struct via686a_data *data) +{ + int reg = via686a_read_value(data, VIA686A_REG_FANDIV); + data->fan_div[0] = (reg >> 4) & 0x03; + data->fan_div[1] = reg >> 6; +} + +static struct via686a_data *via686a_update_device(struct device *dev) +{ + struct via686a_data *data = dev_get_drvdata(dev); + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i <= 4; i++) { + data->in[i] = + via686a_read_value(data, VIA686A_REG_IN(i)); + data->in_min[i] = via686a_read_value(data, + VIA686A_REG_IN_MIN + (i)); + data->in_max[i] = + via686a_read_value(data, VIA686A_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + via686a_read_value(data, VIA686A_REG_FAN(i)); + data->fan_min[i - 1] = via686a_read_value(data, + VIA686A_REG_FAN_MIN(i)); + } + for (i = 0; i <= 2; i++) { + data->temp[i] = via686a_read_value(data, + VIA686A_REG_TEMP[i]) << 2; + data->temp_over[i] = + via686a_read_value(data, + VIA686A_REG_TEMP_OVER[i]); + data->temp_hyst[i] = + via686a_read_value(data, + VIA686A_REG_TEMP_HYST[i]); + } + /* + * add in lower 2 bits + * temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 + * temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 + * temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 + */ + data->temp[0] |= (via686a_read_value(data, + VIA686A_REG_TEMP_LOW1) + & 0xc0) >> 6; + data->temp[1] |= + (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & + 0x30) >> 4; + data->temp[2] |= + (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & + 0xc0) >> 6; + + via686a_update_fan_div(data); + data->alarms = + via686a_read_value(data, + VIA686A_REG_ALARM1) | + (via686a_read_value(data, VIA686A_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} /* following are the sysfs callback functions */ @@ -654,13 +721,23 @@ static const struct attribute_group via686a_group = { .attrs = via686a_attributes, }; -static struct platform_driver via686a_driver = { - .driver = { - .name = "via686a", - }, - .probe = via686a_probe, - .remove = via686a_remove, -}; +static void via686a_init_device(struct via686a_data *data) +{ + u8 reg; + + /* Start monitoring */ + reg = via686a_read_value(data, VIA686A_REG_CONFIG); + via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F); + + /* Configure temp interrupt mode for continuous-interrupt operation */ + reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE); + via686a_write_value(data, VIA686A_REG_TEMP_MODE, + (reg & ~VIA686A_TEMP_MODE_MASK) + | VIA686A_TEMP_MODE_CONTINUOUS); + + /* Pre-read fan clock divisor values */ + via686a_update_fan_div(data); +} /* This is called when the module is loaded */ static int via686a_probe(struct platform_device *pdev) @@ -672,7 +749,7 @@ static int via686a_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, VIA686A_EXTENT, - via686a_driver.driver.name)) { + DRIVER_NAME)) { dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->end); return -ENODEV; @@ -685,7 +762,7 @@ static int via686a_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); data->addr = res->start; - data->name = "via686a"; + data->name = DRIVER_NAME; mutex_init(&data->update_lock); /* Initialize the VIA686A chip */ @@ -719,94 +796,13 @@ static int via686a_remove(struct platform_device *pdev) return 0; } -static void via686a_update_fan_div(struct via686a_data *data) -{ - int reg = via686a_read_value(data, VIA686A_REG_FANDIV); - data->fan_div[0] = (reg >> 4) & 0x03; - data->fan_div[1] = reg >> 6; -} - -static void via686a_init_device(struct via686a_data *data) -{ - u8 reg; - - /* Start monitoring */ - reg = via686a_read_value(data, VIA686A_REG_CONFIG); - via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F); - - /* Configure temp interrupt mode for continuous-interrupt operation */ - reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE); - via686a_write_value(data, VIA686A_REG_TEMP_MODE, - (reg & ~VIA686A_TEMP_MODE_MASK) - | VIA686A_TEMP_MODE_CONTINUOUS); - - /* Pre-read fan clock divisor values */ - via686a_update_fan_div(data); -} - -static struct via686a_data *via686a_update_device(struct device *dev) -{ - struct via686a_data *data = dev_get_drvdata(dev); - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i <= 4; i++) { - data->in[i] = - via686a_read_value(data, VIA686A_REG_IN(i)); - data->in_min[i] = via686a_read_value(data, - VIA686A_REG_IN_MIN - (i)); - data->in_max[i] = - via686a_read_value(data, VIA686A_REG_IN_MAX(i)); - } - for (i = 1; i <= 2; i++) { - data->fan[i - 1] = - via686a_read_value(data, VIA686A_REG_FAN(i)); - data->fan_min[i - 1] = via686a_read_value(data, - VIA686A_REG_FAN_MIN(i)); - } - for (i = 0; i <= 2; i++) { - data->temp[i] = via686a_read_value(data, - VIA686A_REG_TEMP[i]) << 2; - data->temp_over[i] = - via686a_read_value(data, - VIA686A_REG_TEMP_OVER[i]); - data->temp_hyst[i] = - via686a_read_value(data, - VIA686A_REG_TEMP_HYST[i]); - } - /* - * add in lower 2 bits - * temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 - * temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 - * temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 - */ - data->temp[0] |= (via686a_read_value(data, - VIA686A_REG_TEMP_LOW1) - & 0xc0) >> 6; - data->temp[1] |= - (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & - 0x30) >> 4; - data->temp[2] |= - (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & - 0xc0) >> 6; - - via686a_update_fan_div(data); - data->alarms = - via686a_read_value(data, - VIA686A_REG_ALARM1) | - (via686a_read_value(data, VIA686A_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} +static struct platform_driver via686a_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = via686a_probe, + .remove = via686a_remove, +}; static const struct pci_device_id via686a_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) }, @@ -819,7 +815,7 @@ static int via686a_device_add(unsigned short address) struct resource res = { .start = address, .end = address + VIA686A_EXTENT - 1, - .name = "via686a", + .name = DRIVER_NAME, .flags = IORESOURCE_IO, }; int err; @@ -828,7 +824,7 @@ static int via686a_device_add(unsigned short address) if (err) goto exit; - pdev = platform_device_alloc("via686a", address); + pdev = platform_device_alloc(DRIVER_NAME, address); if (!pdev) { err = -ENOMEM; pr_err("Device allocation failed\n"); @@ -918,7 +914,7 @@ exit: } static struct pci_driver via686a_pci_driver = { - .name = "via686a", + .name = DRIVER_NAME, .id_table = via686a_pci_ids, .probe = via686a_pci_probe, }; diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index 2fbdc532aed4..4a5e911d26eb 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -105,7 +105,7 @@ struct vt1211_data { struct device *hwmon_dev; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ /* Register values */ @@ -319,7 +319,7 @@ static struct vt1211_data *vt1211_update_device(struct device *dev) vt1211_read8(data, VT1211_REG_ALARM1); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index 2335d440f72d..3b7f8922b0d5 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -38,6 +38,8 @@ static struct platform_device *pdev; #define VT8231_BASE_REG 0x70 #define VT8231_ENABLE_REG 0x74 +#define DRIVER_NAME "vt8231" + /* * The VT8231 registers * @@ -145,7 +147,7 @@ struct vt8231_data { struct mutex update_lock; struct device *hwmon_dev; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[6]; /* Register value */ @@ -162,10 +164,6 @@ struct vt8231_data { }; static struct pci_dev *s_bridge; -static int vt8231_probe(struct platform_device *pdev); -static int vt8231_remove(struct platform_device *pdev); -static struct vt8231_data *vt8231_update_device(struct device *dev); -static void vt8231_init_device(struct vt8231_data *data); static inline int vt8231_read_value(struct vt8231_data *data, u8 reg) { @@ -178,6 +176,74 @@ static inline void vt8231_write_value(struct vt8231_data *data, u8 reg, outb_p(value, data->addr + reg); } +static struct vt8231_data *vt8231_update_device(struct device *dev) +{ + struct vt8231_data *data = dev_get_drvdata(dev); + int i; + u16 low; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i < 6; i++) { + if (ISVOLT(i, data->uch_config)) { + data->in[i] = vt8231_read_value(data, + regvolt[i]); + data->in_min[i] = vt8231_read_value(data, + regvoltmin[i]); + data->in_max[i] = vt8231_read_value(data, + regvoltmax[i]); + } + } + for (i = 0; i < 2; i++) { + data->fan[i] = vt8231_read_value(data, + VT8231_REG_FAN(i)); + data->fan_min[i] = vt8231_read_value(data, + VT8231_REG_FAN_MIN(i)); + } + + low = vt8231_read_value(data, VT8231_REG_TEMP_LOW01); + low = (low >> 6) | ((low & 0x30) >> 2) + | (vt8231_read_value(data, VT8231_REG_TEMP_LOW25) << 4); + for (i = 0; i < 6; i++) { + if (ISTEMP(i, data->uch_config)) { + data->temp[i] = (vt8231_read_value(data, + regtemp[i]) << 2) + | ((low >> (2 * i)) & 0x03); + data->temp_max[i] = vt8231_read_value(data, + regtempmax[i]); + data->temp_min[i] = vt8231_read_value(data, + regtempmin[i]); + } + } + + i = vt8231_read_value(data, VT8231_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = vt8231_read_value(data, VT8231_REG_ALARM1) | + (vt8231_read_value(data, VT8231_REG_ALARM2) << 8); + + /* Set alarm flags correctly */ + if (!data->fan[0] && data->fan_min[0]) + data->alarms |= 0x40; + else if (data->fan[0] && !data->fan_min[0]) + data->alarms &= ~0x40; + + if (!data->fan[1] && data->fan_min[1]) + data->alarms |= 0x80; + else if (data->fan[1] && !data->fan_min[1]) + data->alarms &= ~0x80; + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} + /* following are the sysfs callback functions */ static ssize_t in_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -751,29 +817,11 @@ static const struct attribute_group vt8231_group = { .attrs = vt8231_attributes, }; -static struct platform_driver vt8231_driver = { - .driver = { - .name = "vt8231", - }, - .probe = vt8231_probe, - .remove = vt8231_remove, -}; - -static const struct pci_device_id vt8231_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4) }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, vt8231_pci_ids); - -static int vt8231_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id); - -static struct pci_driver vt8231_pci_driver = { - .name = "vt8231", - .id_table = vt8231_pci_ids, - .probe = vt8231_pci_probe, -}; +static void vt8231_init_device(struct vt8231_data *data) +{ + vt8231_write_value(data, VT8231_REG_TEMP1_CONFIG, 0); + vt8231_write_value(data, VT8231_REG_TEMP2_CONFIG, 0); +} static int vt8231_probe(struct platform_device *pdev) { @@ -784,7 +832,7 @@ static int vt8231_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, VT8231_EXTENT, - vt8231_driver.driver.name)) { + DRIVER_NAME)) { dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->end); return -ENODEV; @@ -796,7 +844,7 @@ static int vt8231_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); data->addr = res->start; - data->name = "vt8231"; + data->name = DRIVER_NAME; mutex_init(&data->update_lock); vt8231_init_device(data); @@ -863,86 +911,28 @@ static int vt8231_remove(struct platform_device *pdev) return 0; } -static void vt8231_init_device(struct vt8231_data *data) -{ - vt8231_write_value(data, VT8231_REG_TEMP1_CONFIG, 0); - vt8231_write_value(data, VT8231_REG_TEMP2_CONFIG, 0); -} -static struct vt8231_data *vt8231_update_device(struct device *dev) -{ - struct vt8231_data *data = dev_get_drvdata(dev); - int i; - u16 low; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i < 6; i++) { - if (ISVOLT(i, data->uch_config)) { - data->in[i] = vt8231_read_value(data, - regvolt[i]); - data->in_min[i] = vt8231_read_value(data, - regvoltmin[i]); - data->in_max[i] = vt8231_read_value(data, - regvoltmax[i]); - } - } - for (i = 0; i < 2; i++) { - data->fan[i] = vt8231_read_value(data, - VT8231_REG_FAN(i)); - data->fan_min[i] = vt8231_read_value(data, - VT8231_REG_FAN_MIN(i)); - } - - low = vt8231_read_value(data, VT8231_REG_TEMP_LOW01); - low = (low >> 6) | ((low & 0x30) >> 2) - | (vt8231_read_value(data, VT8231_REG_TEMP_LOW25) << 4); - for (i = 0; i < 6; i++) { - if (ISTEMP(i, data->uch_config)) { - data->temp[i] = (vt8231_read_value(data, - regtemp[i]) << 2) - | ((low >> (2 * i)) & 0x03); - data->temp_max[i] = vt8231_read_value(data, - regtempmax[i]); - data->temp_min[i] = vt8231_read_value(data, - regtempmin[i]); - } - } - - i = vt8231_read_value(data, VT8231_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - data->alarms = vt8231_read_value(data, VT8231_REG_ALARM1) | - (vt8231_read_value(data, VT8231_REG_ALARM2) << 8); - - /* Set alarm flags correctly */ - if (!data->fan[0] && data->fan_min[0]) - data->alarms |= 0x40; - else if (data->fan[0] && !data->fan_min[0]) - data->alarms &= ~0x40; - - if (!data->fan[1] && data->fan_min[1]) - data->alarms |= 0x80; - else if (data->fan[1] && !data->fan_min[1]) - data->alarms &= ~0x80; - - data->last_updated = jiffies; - data->valid = 1; - } +static struct platform_driver vt8231_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = vt8231_probe, + .remove = vt8231_remove, +}; - mutex_unlock(&data->update_lock); +static const struct pci_device_id vt8231_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4) }, + { 0, } +}; - return data; -} +MODULE_DEVICE_TABLE(pci, vt8231_pci_ids); static int vt8231_device_add(unsigned short address) { struct resource res = { .start = address, .end = address + VT8231_EXTENT - 1, - .name = "vt8231", + .name = DRIVER_NAME, .flags = IORESOURCE_IO, }; int err; @@ -951,7 +941,7 @@ static int vt8231_device_add(unsigned short address) if (err) goto exit; - pdev = platform_device_alloc("vt8231", address); + pdev = platform_device_alloc(DRIVER_NAME, address); if (!pdev) { err = -ENOMEM; pr_err("Device allocation failed\n"); @@ -992,8 +982,8 @@ static int vt8231_pci_probe(struct pci_dev *dev, return -ENODEV; } - if (PCIBIOS_SUCCESSFUL != pci_read_config_word(dev, VT8231_BASE_REG, - &val)) + pci_read_config_word(dev, VT8231_BASE_REG, &val); + if (val == (u16)~0) return -ENODEV; address = val & ~(VT8231_EXTENT - 1); @@ -1002,8 +992,8 @@ static int vt8231_pci_probe(struct pci_dev *dev, return -ENODEV; } - if (PCIBIOS_SUCCESSFUL != pci_read_config_word(dev, VT8231_ENABLE_REG, - &val)) + pci_read_config_word(dev, VT8231_ENABLE_REG, &val); + if (val == (u16)~0) return -ENODEV; if (!(val & 0x0001)) { @@ -1040,6 +1030,12 @@ exit: return -ENODEV; } +static struct pci_driver vt8231_pci_driver = { + .name = DRIVER_NAME, + .id_table = vt8231_pci_ids, + .probe = vt8231_pci_probe, +}; + static int __init sm_vt8231_init(void) { return pci_register_driver(&vt8231_pci_driver); diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 5a5120121e50..939d4c35e713 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -320,7 +320,7 @@ struct w83627ehf_data { const u16 *scale_in; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ /* Register values */ @@ -372,12 +372,10 @@ struct w83627ehf_data { u8 temp3_val_only:1; u8 have_vid:1; -#ifdef CONFIG_PM /* Remember extra register values over suspend/resume */ u8 vbat; u8 fandiv1; u8 fandiv2; -#endif }; struct w83627ehf_sio_data { @@ -690,7 +688,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) W83627EHF_REG_CASEOPEN_DET); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -1083,7 +1081,7 @@ cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) struct w83627ehf_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } -DEVICE_ATTR_RO(cpu0_vid); +static DEVICE_ATTR_RO(cpu0_vid); /* Case open detection */ @@ -1101,7 +1099,7 @@ clear_caseopen(struct device *dev, struct w83627ehf_data *data, int channel, reg = w83627ehf_read_value(data, W83627EHF_REG_CASEOPEN_CLR); w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg | mask); w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg & ~mask); - data->valid = 0; /* Force cache refresh */ + data->valid = false; /* Force cache refresh */ mutex_unlock(&data->update_lock); return 0; @@ -1110,7 +1108,7 @@ clear_caseopen(struct device *dev, struct w83627ehf_data *data, int channel, static umode_t w83627ehf_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct w83627ehf_data *data = dev_get_drvdata(dev); struct device_attribute *devattr; struct sensor_device_attribute *sda; @@ -1694,7 +1692,7 @@ static const struct hwmon_chip_info w83627ehf_chip_info = { .info = w83627ehf_info, }; -static int w83627ehf_probe(struct platform_device *pdev) +static int __init w83627ehf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); @@ -1705,20 +1703,12 @@ static int w83627ehf_probe(struct platform_device *pdev) struct device *hwmon_dev; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, IOREGION_LENGTH, DRVNAME)) { - err = -EBUSY; - dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", - (unsigned long)res->start, - (unsigned long)res->start + IOREGION_LENGTH - 1); - goto exit; - } + if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME)) + return -EBUSY; - data = devm_kzalloc(&pdev->dev, sizeof(struct w83627ehf_data), - GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit_release; - } + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; data->addr = res->start; mutex_init(&data->lock); @@ -1882,7 +1872,7 @@ static int w83627ehf_probe(struct platform_device *pdev) err = superio_enter(sio_data->sioreg); if (err) - goto exit_release; + return err; /* Read VID value */ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { @@ -1951,25 +1941,9 @@ static int w83627ehf_probe(struct platform_device *pdev) data, &w83627ehf_chip_info, w83627ehf_groups); - return PTR_ERR_OR_ZERO(hwmon_dev); - -exit_release: - release_region(res->start, IOREGION_LENGTH); -exit: - return err; -} - -static int w83627ehf_remove(struct platform_device *pdev) -{ - struct w83627ehf_data *data = platform_get_drvdata(pdev); - - release_region(data->addr, IOREGION_LENGTH); - - return 0; } -#ifdef CONFIG_PM static int w83627ehf_suspend(struct device *dev) { struct w83627ehf_data *data = w83627ehf_update_device(dev); @@ -2030,31 +2004,19 @@ static int w83627ehf_resume(struct device *dev) w83627ehf_write_value(data, W83627EHF_REG_VBAT, data->vbat); /* Force re-reading all values */ - data->valid = 0; + data->valid = false; mutex_unlock(&data->update_lock); return 0; } -static const struct dev_pm_ops w83627ehf_dev_pm_ops = { - .suspend = w83627ehf_suspend, - .resume = w83627ehf_resume, - .freeze = w83627ehf_suspend, - .restore = w83627ehf_resume, -}; - -#define W83627EHF_DEV_PM_OPS (&w83627ehf_dev_pm_ops) -#else -#define W83627EHF_DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +static DEFINE_SIMPLE_DEV_PM_OPS(w83627ehf_dev_pm_ops, w83627ehf_suspend, w83627ehf_resume); static struct platform_driver w83627ehf_driver = { .driver = { .name = DRVNAME, - .pm = W83627EHF_DEV_PM_OPS, + .pm = pm_sleep_ptr(&w83627ehf_dev_pm_ops), }, - .probe = w83627ehf_probe, - .remove = w83627ehf_remove, }; /* w83627ehf_find() looks for a '627 in the Super-I/O config space */ @@ -2146,8 +2108,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, /* * when Super-I/O functions move to a separate file, the Super-I/O * bus will manage the lifetime of the device and this module will only keep - * track of the w83627ehf driver. But since we platform_device_alloc(), we - * must keep track of the device + * track of the w83627ehf driver. */ static struct platform_device *pdev; @@ -2155,7 +2116,10 @@ static int __init sensors_w83627ehf_init(void) { int err; unsigned short address; - struct resource res; + struct resource res = { + .name = DRVNAME, + .flags = IORESOURCE_IO, + }; struct w83627ehf_sio_data sio_data; /* @@ -2169,55 +2133,17 @@ static int __init sensors_w83627ehf_init(void) w83627ehf_find(0x4e, &address, &sio_data)) return -ENODEV; - err = platform_driver_register(&w83627ehf_driver); - if (err) - goto exit; - - pdev = platform_device_alloc(DRVNAME, address); - if (!pdev) { - err = -ENOMEM; - pr_err("Device allocation failed\n"); - goto exit_unregister; - } - - err = platform_device_add_data(pdev, &sio_data, - sizeof(struct w83627ehf_sio_data)); - if (err) { - pr_err("Platform data allocation failed\n"); - goto exit_device_put; - } - - memset(&res, 0, sizeof(res)); - res.name = DRVNAME; res.start = address + IOREGION_OFFSET; res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; - res.flags = IORESOURCE_IO; err = acpi_check_resource_conflict(&res); if (err) - goto exit_device_put; - - err = platform_device_add_resources(pdev, &res, 1); - if (err) { - pr_err("Device resource addition failed (%d)\n", err); - goto exit_device_put; - } - - /* platform_device_add calls probe() */ - err = platform_device_add(pdev); - if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_put; - } + return err; - return 0; + pdev = platform_create_bundle(&w83627ehf_driver, w83627ehf_probe, &res, 1, &sio_data, + sizeof(struct w83627ehf_sio_data)); -exit_device_put: - platform_device_put(pdev); -exit_unregister: - platform_driver_unregister(&w83627ehf_driver); -exit: - return err; + return PTR_ERR_OR_ZERO(pdev); } static void __exit sensors_w83627ehf_exit(void) diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index e1d10a6b7f7c..b638d672ac45 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -355,7 +355,7 @@ struct w83627hf_data { enum chips type; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ u8 in[9]; /* Register value */ @@ -389,14 +389,184 @@ struct w83627hf_data { #endif }; -static int w83627hf_probe(struct platform_device *pdev); -static int w83627hf_remove(struct platform_device *pdev); +/* Registers 0x50-0x5f are banked */ +static inline void w83627hf_set_bank(struct w83627hf_data *data, u16 reg) +{ + if ((reg & 0x00f0) == 0x50) { + outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, data->addr + W83781D_DATA_REG_OFFSET); + } +} + +/* Not strictly necessary, but play it safe for now */ +static inline void w83627hf_reset_bank(struct w83627hf_data *data, u16 reg) +{ + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->addr + W83781D_DATA_REG_OFFSET); + } +} + +static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) +{ + int res, word_sized; + + mutex_lock(&data->lock); + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + w83627hf_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(data->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + data->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(data->addr + + W83781D_DATA_REG_OFFSET); + } + w83627hf_reset_bank(data, reg); + mutex_unlock(&data->lock); + return res; +} + +static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value) +{ + int word_sized; + + mutex_lock(&data->lock); + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + w83627hf_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + data->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + data->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, + data->addr + W83781D_DATA_REG_OFFSET); + w83627hf_reset_bank(data, reg); + mutex_unlock(&data->lock); + return 0; +} + +static void w83627hf_update_fan_div(struct w83627hf_data *data) +{ + int reg; + + reg = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); + data->fan_div[0] = (reg >> 4) & 0x03; + data->fan_div[1] = (reg >> 6) & 0x03; + if (data->type != w83697hf) { + data->fan_div[2] = (w83627hf_read_value(data, + W83781D_REG_PIN) >> 6) & 0x03; + } + reg = w83627hf_read_value(data, W83781D_REG_VBAT); + data->fan_div[0] |= (reg >> 3) & 0x04; + data->fan_div[1] |= (reg >> 4) & 0x04; + if (data->type != w83697hf) + data->fan_div[2] |= (reg >> 5) & 0x04; +} -static int w83627hf_read_value(struct w83627hf_data *data, u16 reg); -static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value); -static void w83627hf_update_fan_div(struct w83627hf_data *data); -static struct w83627hf_data *w83627hf_update_device(struct device *dev); -static void w83627hf_init_device(struct platform_device *pdev); +static struct w83627hf_data *w83627hf_update_device(struct device *dev) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + int i, num_temps = (data->type == w83697hf) ? 2 : 3; + int num_pwms = (data->type == w83697hf) ? 2 : 3; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i <= 8; i++) { + /* skip missing sensors */ + if (((data->type == w83697hf) && (i == 1)) || + ((data->type != w83627hf && data->type != w83697hf) + && (i == 5 || i == 6))) + continue; + data->in[i] = + w83627hf_read_value(data, W83781D_REG_IN(i)); + data->in_min[i] = + w83627hf_read_value(data, + W83781D_REG_IN_MIN(i)); + data->in_max[i] = + w83627hf_read_value(data, + W83781D_REG_IN_MAX(i)); + } + for (i = 0; i <= 2; i++) { + data->fan[i] = + w83627hf_read_value(data, W83627HF_REG_FAN(i)); + data->fan_min[i] = + w83627hf_read_value(data, + W83627HF_REG_FAN_MIN(i)); + } + for (i = 0; i <= 2; i++) { + u8 tmp = w83627hf_read_value(data, + W836X7HF_REG_PWM(data->type, i)); + /* bits 0-3 are reserved in 627THF */ + if (data->type == w83627thf) + tmp &= 0xf0; + data->pwm[i] = tmp; + if (i == 1 && + (data->type == w83627hf || data->type == w83697hf)) + break; + } + if (data->type == w83627hf) { + u8 tmp = w83627hf_read_value(data, + W83627HF_REG_PWM_FREQ); + data->pwm_freq[0] = tmp & 0x07; + data->pwm_freq[1] = (tmp >> 4) & 0x07; + } else if (data->type != w83627thf) { + for (i = 1; i <= 3; i++) { + data->pwm_freq[i - 1] = + w83627hf_read_value(data, + W83637HF_REG_PWM_FREQ[i - 1]); + if (i == 2 && (data->type == w83697hf)) + break; + } + } + if (data->type != w83627hf) { + for (i = 0; i < num_pwms; i++) { + u8 tmp = w83627hf_read_value(data, + W83627THF_REG_PWM_ENABLE[i]); + data->pwm_enable[i] = + ((tmp >> W83627THF_PWM_ENABLE_SHIFT[i]) + & 0x03) + 1; + } + } + for (i = 0; i < num_temps; i++) { + data->temp[i] = w83627hf_read_value( + data, w83627hf_reg_temp[i]); + data->temp_max[i] = w83627hf_read_value( + data, w83627hf_reg_temp_over[i]); + data->temp_max_hyst[i] = w83627hf_read_value( + data, w83627hf_reg_temp_hyst[i]); + } + + w83627hf_update_fan_div(data); + + data->alarms = + w83627hf_read_value(data, W83781D_REG_ALARM1) | + (w83627hf_read_value(data, W83781D_REG_ALARM2) << 8) | + (w83627hf_read_value(data, W83781D_REG_ALARM3) << 16); + i = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2); + data->beep_mask = (i << 8) | + w83627hf_read_value(data, W83781D_REG_BEEP_INTS1) | + w83627hf_read_value(data, W83781D_REG_BEEP_INTS3) << 16; + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} #ifdef CONFIG_PM static int w83627hf_suspend(struct device *dev) @@ -448,7 +618,7 @@ static int w83627hf_resume(struct device *dev) w83627hf_write_value(data, W83781D_REG_SCFG2, data->scfg2); /* Force re-reading all values */ - data->valid = 0; + data->valid = false; mutex_unlock(&data->update_lock); return 0; @@ -464,99 +634,171 @@ static const struct dev_pm_ops w83627hf_dev_pm_ops = { #define W83627HF_DEV_PM_OPS NULL #endif /* CONFIG_PM */ -static struct platform_driver w83627hf_driver = { - .driver = { - .name = DRVNAME, - .pm = W83627HF_DEV_PM_OPS, - }, - .probe = w83627hf_probe, - .remove = w83627hf_remove, -}; - -static ssize_t -in_input_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in[nr])); -} -static ssize_t -in_min_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_min[nr])); -} -static ssize_t -in_max_show(struct device *dev, struct device_attribute *devattr, char *buf) +static int w83627thf_read_gpio5(struct platform_device *pdev) { - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_max[nr])); + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); + int res = 0xff, sel; + + if (superio_enter(sio_data)) { + /* + * Some other driver reserved the address space for itself. + * We don't want to fail driver instantiation because of that, + * so display a warning and keep going. + */ + dev_warn(&pdev->dev, + "Can not read VID data: Failed to enable SuperIO access\n"); + return res; + } + + superio_select(sio_data, W83627HF_LD_GPIO5); + + res = 0xff; + + /* Make sure these GPIO pins are enabled */ + if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) { + dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); + goto exit; + } + + /* + * Make sure the pins are configured for input + * There must be at least five (VRM 9), and possibly 6 (VRM 10) + */ + sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f; + if ((sel & 0x1f) != 0x1f) { + dev_dbg(&pdev->dev, "GPIO5 not configured for VID " + "function\n"); + goto exit; + } + + dev_info(&pdev->dev, "Reading VID from GPIO5\n"); + res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel; + +exit: + superio_exit(sio_data); + return res; } -static ssize_t -in_min_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) + +static int w83687thf_read_vid(struct platform_device *pdev) { - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - long val; - int err; + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); + int res = 0xff; - err = kstrtol(buf, 10, &val); - if (err) - return err; + if (superio_enter(sio_data)) { + /* + * Some other driver reserved the address space for itself. + * We don't want to fail driver instantiation because of that, + * so display a warning and keep going. + */ + dev_warn(&pdev->dev, + "Can not read VID data: Failed to enable SuperIO access\n"); + return res; + } - mutex_lock(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); - w83627hf_write_value(data, W83781D_REG_IN_MIN(nr), data->in_min[nr]); - mutex_unlock(&data->update_lock); - return count; + superio_select(sio_data, W83627HF_LD_HWM); + + /* Make sure these GPIO pins are enabled */ + if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) { + dev_dbg(&pdev->dev, "VID disabled, no VID function\n"); + goto exit; + } + + /* Make sure the pins are configured for input */ + if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) { + dev_dbg(&pdev->dev, "VID configured as output, " + "no VID function\n"); + goto exit; + } + + res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f; + +exit: + superio_exit(sio_data); + return res; } -static ssize_t -in_max_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) + +static void w83627hf_init_device(struct platform_device *pdev) { - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - long val; - int err; + struct w83627hf_data *data = platform_get_drvdata(pdev); + int i; + enum chips type = data->type; + u8 tmp; - err = kstrtol(buf, 10, &val); - if (err) - return err; + /* Minimize conflicts with other winbond i2c-only clients... */ + /* disable i2c subclients... how to disable main i2c client?? */ + /* force i2c address to relatively uncommon address */ + if (type == w83627hf) { + w83627hf_write_value(data, W83781D_REG_I2C_SUBADDR, 0x89); + w83627hf_write_value(data, W83781D_REG_I2C_ADDR, force_i2c); + } - mutex_lock(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); - w83627hf_write_value(data, W83781D_REG_IN_MAX(nr), data->in_max[nr]); - mutex_unlock(&data->update_lock); - return count; -} + /* Read VID only once */ + if (type == w83627hf || type == w83637hf) { + int lo = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); + int hi = w83627hf_read_value(data, W83781D_REG_CHIPID); + data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); + } else if (type == w83627thf) { + data->vid = w83627thf_read_gpio5(pdev); + } else if (type == w83687thf) { + data->vid = w83687thf_read_vid(pdev); + } -static SENSOR_DEVICE_ATTR_RO(in1_input, in_input, 1); -static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); -static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, in_input, 2); -static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); -static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, in_input, 3); -static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); -static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, in_input, 4); -static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); -static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, in_input, 5); -static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5); -static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5); -static SENSOR_DEVICE_ATTR_RO(in6_input, in_input, 6); -static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 6); -static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 6); -static SENSOR_DEVICE_ATTR_RO(in7_input, in_input, 7); -static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 7); -static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 7); -static SENSOR_DEVICE_ATTR_RO(in8_input, in_input, 8); -static SENSOR_DEVICE_ATTR_RW(in8_min, in_min, 8); -static SENSOR_DEVICE_ATTR_RW(in8_max, in_max, 8); + /* Read VRM & OVT Config only once */ + if (type == w83627thf || type == w83637hf || type == w83687thf) { + data->vrm_ovt = + w83627hf_read_value(data, W83627THF_REG_VRM_OVT_CFG); + } + + tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); + for (i = 1; i <= 3; i++) { + if (!(tmp & BIT_SCFG1[i - 1])) { + data->sens[i - 1] = 4; + } else { + if (w83627hf_read_value + (data, + W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) + data->sens[i - 1] = 1; + else + data->sens[i - 1] = 2; + } + if ((type == w83697hf) && (i == 2)) + break; + } + + if(init) { + /* Enable temp2 */ + tmp = w83627hf_read_value(data, W83627HF_REG_TEMP2_CONFIG); + if (tmp & 0x01) { + dev_warn(&pdev->dev, "Enabling temp2, readings " + "might not make sense\n"); + w83627hf_write_value(data, W83627HF_REG_TEMP2_CONFIG, + tmp & 0xfe); + } + + /* Enable temp3 */ + if (type != w83697hf) { + tmp = w83627hf_read_value(data, + W83627HF_REG_TEMP3_CONFIG); + if (tmp & 0x01) { + dev_warn(&pdev->dev, "Enabling temp3, " + "readings might not make sense\n"); + w83627hf_write_value(data, + W83627HF_REG_TEMP3_CONFIG, tmp & 0xfe); + } + } + } + + /* Start monitoring */ + w83627hf_write_value(data, W83781D_REG_CONFIG, + (w83627hf_read_value(data, + W83781D_REG_CONFIG) & 0xf7) + | 0x01); + + /* Enable VBAT monitoring if needed */ + tmp = w83627hf_read_value(data, W83781D_REG_VBAT); + if (!(tmp & 0x01)) + w83627hf_write_value(data, W83781D_REG_VBAT, tmp | 0x01); +} /* use a different set of functions for in0 */ static ssize_t show_in_0(struct w83627hf_data *data, char *buf, u8 reg) @@ -582,6 +824,7 @@ static ssize_t in0_input_show(struct device *dev, struct w83627hf_data *data = w83627hf_update_device(dev); return show_in_0(data, buf, data->in[0]); } +static DEVICE_ATTR_RO(in0_input); static ssize_t in0_min_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -590,13 +833,6 @@ static ssize_t in0_min_show(struct device *dev, struct device_attribute *attr, return show_in_0(data, buf, data->in_min[0]); } -static ssize_t in0_max_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return show_in_0(data, buf, data->in_max[0]); -} - static ssize_t in0_min_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -627,6 +863,15 @@ static ssize_t in0_min_store(struct device *dev, return count; } +static DEVICE_ATTR_RW(in0_min); + +static ssize_t in0_max_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return show_in_0(data, buf, data->in_max[0]); +} + static ssize_t in0_max_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -657,193 +902,16 @@ static ssize_t in0_max_store(struct device *dev, return count; } -static DEVICE_ATTR_RO(in0_input); -static DEVICE_ATTR_RW(in0_min); static DEVICE_ATTR_RW(in0_max); static ssize_t -fan_input_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan[nr], - (long)DIV_FROM_REG(data->fan_div[nr]))); -} -static ssize_t -fan_min_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan_min[nr], - (long)DIV_FROM_REG(data->fan_div[nr]))); -} -static ssize_t -fan_min_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - w83627hf_write_value(data, W83627HF_REG_FAN_MIN(nr), - data->fan_min[nr]); - - mutex_unlock(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0); -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1); -static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_input, 2); -static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2); - -static ssize_t -temp_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - - u16 tmp = data->temp[nr]; - return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) - : (long) TEMP_FROM_REG(tmp)); -} - -static ssize_t -temp_max_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - - u16 tmp = data->temp_max[nr]; - return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) - : (long) TEMP_FROM_REG(tmp)); -} - -static ssize_t -temp_max_hyst_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - - u16 tmp = data->temp_max_hyst[nr]; - return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) - : (long) TEMP_FROM_REG(tmp)); -} - -static ssize_t -temp_max_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - u16 tmp; - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); - mutex_lock(&data->update_lock); - data->temp_max[nr] = tmp; - w83627hf_write_value(data, w83627hf_reg_temp_over[nr], tmp); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t -temp_max_hyst_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - u16 tmp; - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); - mutex_lock(&data->update_lock); - data->temp_max_hyst[nr] = tmp; - w83627hf_write_value(data, w83627hf_reg_temp_hyst[nr], tmp); - mutex_unlock(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp_max_hyst, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, temp_max_hyst, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, temp_max_hyst, 2); - -static ssize_t -cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); -} -static DEVICE_ATTR_RO(cpu0_vid); - -static ssize_t -vrm_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%ld\n", (long) data->vrm); -} -static ssize_t -vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - if (val > 255) - return -EINVAL; - data->vrm = val; - - return count; -} -static DEVICE_ATTR_RW(vrm); - -static ssize_t -alarms_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->alarms); -} -static DEVICE_ATTR_RO(alarms); - -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct w83627hf_data *data = w83627hf_update_device(dev); int bitnr = to_sensor_dev_attr(attr)->index; return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } + static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); @@ -861,44 +929,6 @@ static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5); static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 13); static ssize_t -beep_mask_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", - (long)BEEP_MASK_FROM_REG(data->beep_mask)); -} - -static ssize_t -beep_mask_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - - /* preserve beep enable */ - data->beep_mask = (data->beep_mask & 0x8000) - | BEEP_MASK_TO_REG(val); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, - data->beep_mask & 0xff); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, - ((data->beep_mask) >> 16) & 0xff); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, - (data->beep_mask >> 8) & 0xff); - - mutex_unlock(&data->update_lock); - return count; -} - -static DEVICE_ATTR_RW(beep_mask); - -static ssize_t beep_show(struct device *dev, struct device_attribute *attr, char *buf) { struct w83627hf_data *data = w83627hf_update_device(dev); @@ -974,6 +1004,143 @@ static SENSOR_DEVICE_ATTR_RW(temp3_beep, beep, 13); static SENSOR_DEVICE_ATTR_RW(beep_enable, beep, 15); static ssize_t +in_input_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in[nr])); +} + +static ssize_t +in_min_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_min[nr])); +} + +static ssize_t +in_min_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val); + w83627hf_write_value(data, W83781D_REG_IN_MIN(nr), data->in_min[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +in_max_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_max[nr])); +} + +static ssize_t +in_max_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val); + w83627hf_write_value(data, W83781D_REG_IN_MAX(nr), data->in_max[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_RO(in1_input, in_input, 1); +static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); +static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); +static SENSOR_DEVICE_ATTR_RO(in2_input, in_input, 2); +static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); +static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); +static SENSOR_DEVICE_ATTR_RO(in3_input, in_input, 3); +static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); +static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); +static SENSOR_DEVICE_ATTR_RO(in4_input, in_input, 4); +static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); +static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); +static SENSOR_DEVICE_ATTR_RO(in5_input, in_input, 5); +static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5); +static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5); +static SENSOR_DEVICE_ATTR_RO(in6_input, in_input, 6); +static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 6); +static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 6); +static SENSOR_DEVICE_ATTR_RO(in7_input, in_input, 7); +static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 7); +static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 7); +static SENSOR_DEVICE_ATTR_RO(in8_input, in_input, 8); +static SENSOR_DEVICE_ATTR_RW(in8_min, in_min, 8); +static SENSOR_DEVICE_ATTR_RW(in8_max, in_max, 8); + +static ssize_t +fan_input_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan[nr], + (long)DIV_FROM_REG(data->fan_div[nr]))); +} + +static ssize_t +fan_min_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan_min[nr], + (long)DIV_FROM_REG(data->fan_div[nr]))); +} + +static ssize_t +fan_min_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + w83627hf_write_value(data, W83627HF_REG_FAN_MIN(nr), + data->fan_min[nr]); + + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0); +static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); +static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1); +static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); +static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_input, 2); +static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2); + +static ssize_t fan_div_show(struct device *dev, struct device_attribute *devattr, char *buf) { int nr = to_sensor_dev_attr(devattr)->index; @@ -981,6 +1148,7 @@ fan_div_show(struct device *dev, struct device_attribute *devattr, char *buf) return sprintf(buf, "%ld\n", (long) DIV_FROM_REG(data->fan_div[nr])); } + /* * Note: we save and restore the fan minimum here, because its value is * determined in part by the fan divisor. This follows the principle of @@ -1033,138 +1201,92 @@ static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1); static SENSOR_DEVICE_ATTR_RW(fan3_div, fan_div, 2); static ssize_t -pwm_show(struct device *dev, struct device_attribute *devattr, char *buf) +temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->pwm[nr]); -} -static ssize_t -pwm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - - if (data->type == w83627thf) { - /* bits 0-3 are reserved in 627THF */ - data->pwm[nr] = PWM_TO_REG(val) & 0xf0; - w83627hf_write_value(data, - W836X7HF_REG_PWM(data->type, nr), - data->pwm[nr] | - (w83627hf_read_value(data, - W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); - } else { - data->pwm[nr] = PWM_TO_REG(val); - w83627hf_write_value(data, - W836X7HF_REG_PWM(data->type, nr), - data->pwm[nr]); - } - - mutex_unlock(&data->update_lock); - return count; + u16 tmp = data->temp[nr]; + return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) + : (long) TEMP_FROM_REG(tmp)); } -static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2); - static ssize_t -pwm_enable_show(struct device *dev, struct device_attribute *devattr, - char *buf) +temp_max_show(struct device *dev, struct device_attribute *devattr, char *buf) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%d\n", data->pwm_enable[nr]); + + u16 tmp = data->temp_max[nr]; + return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) + : (long) TEMP_FROM_REG(tmp)); } static ssize_t -pwm_enable_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +temp_max_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - u8 reg; - unsigned long val; + u16 tmp; + long val; int err; - err = kstrtoul(buf, 10, &val); + err = kstrtol(buf, 10, &val); if (err) return err; - if (!val || val > 3) /* modes 1, 2 and 3 are supported */ - return -EINVAL; + tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); - data->pwm_enable[nr] = val; - reg = w83627hf_read_value(data, W83627THF_REG_PWM_ENABLE[nr]); - reg &= ~(0x03 << W83627THF_PWM_ENABLE_SHIFT[nr]); - reg |= (val - 1) << W83627THF_PWM_ENABLE_SHIFT[nr]; - w83627hf_write_value(data, W83627THF_REG_PWM_ENABLE[nr], reg); + data->temp_max[nr] = tmp; + w83627hf_write_value(data, w83627hf_reg_temp_over[nr], tmp); mutex_unlock(&data->update_lock); return count; } -static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_enable, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_enable, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_enable, 2); - static ssize_t -pwm_freq_show(struct device *dev, struct device_attribute *devattr, char *buf) +temp_max_hyst_show(struct device *dev, struct device_attribute *devattr, + char *buf) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - if (data->type == w83627hf) - return sprintf(buf, "%ld\n", - pwm_freq_from_reg_627hf(data->pwm_freq[nr])); - else - return sprintf(buf, "%ld\n", - pwm_freq_from_reg(data->pwm_freq[nr])); + + u16 tmp = data->temp_max_hyst[nr]; + return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) + : (long) TEMP_FROM_REG(tmp)); } static ssize_t -pwm_freq_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +temp_max_hyst_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - static const u8 mask[]={0xF8, 0x8F}; - unsigned long val; + u16 tmp; + long val; int err; - err = kstrtoul(buf, 10, &val); + err = kstrtol(buf, 10, &val); if (err) return err; + tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); - - if (data->type == w83627hf) { - data->pwm_freq[nr] = pwm_freq_to_reg_627hf(val); - w83627hf_write_value(data, W83627HF_REG_PWM_FREQ, - (data->pwm_freq[nr] << (nr*4)) | - (w83627hf_read_value(data, - W83627HF_REG_PWM_FREQ) & mask[nr])); - } else { - data->pwm_freq[nr] = pwm_freq_to_reg(val); - w83627hf_write_value(data, W83637HF_REG_PWM_FREQ[nr], - data->pwm_freq[nr]); - } - + data->temp_max_hyst[nr] = tmp; + w83627hf_write_value(data, w83627hf_reg_temp_hyst[nr], tmp); mutex_unlock(&data->update_lock); return count; } -static SENSOR_DEVICE_ATTR_RW(pwm1_freq, pwm_freq, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2_freq, pwm_freq, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3_freq, pwm_freq, 2); +static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); +static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); +static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp_max_hyst, 0); +static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); +static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); +static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, temp_max_hyst, 1); +static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); +static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); +static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, temp_max_hyst, 2); static ssize_t temp_type_show(struct device *dev, struct device_attribute *devattr, @@ -1213,7 +1335,7 @@ temp_type_store(struct device *dev, struct device_attribute *devattr, case W83781D_DEFAULT_BETA: dev_warn(dev, "Sensor type %d is deprecated, please use 4 " "instead\n", W83781D_DEFAULT_BETA); - /* fall through */ + fallthrough; case 4: /* thermistor */ tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); w83627hf_write_value(data, W83781D_REG_SCFG1, @@ -1236,81 +1358,12 @@ static SENSOR_DEVICE_ATTR_RW(temp2_type, temp_type, 1); static SENSOR_DEVICE_ATTR_RW(temp3_type, temp_type, 2); static ssize_t -name_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - struct w83627hf_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", data->name); -} -static DEVICE_ATTR_RO(name); - -static int __init w83627hf_find(int sioaddr, unsigned short *addr, - struct w83627hf_sio_data *sio_data) +alarms_show(struct device *dev, struct device_attribute *attr, char *buf) { - int err; - u16 val; - - static __initconst char *const names[] = { - "W83627HF", - "W83627THF", - "W83697HF", - "W83637HF", - "W83687THF", - }; - - sio_data->sioaddr = sioaddr; - err = superio_enter(sio_data); - if (err) - return err; - - err = -ENODEV; - val = force_id ? force_id : superio_inb(sio_data, DEVID); - switch (val) { - case W627_DEVID: - sio_data->type = w83627hf; - break; - case W627THF_DEVID: - sio_data->type = w83627thf; - break; - case W697_DEVID: - sio_data->type = w83697hf; - break; - case W637_DEVID: - sio_data->type = w83637hf; - break; - case W687THF_DEVID: - sio_data->type = w83687thf; - break; - case 0xff: /* No device at all */ - goto exit; - default: - pr_debug(DRVNAME ": Unsupported chip (DEVID=0x%02x)\n", val); - goto exit; - } - - superio_select(sio_data, W83627HF_LD_HWM); - val = (superio_inb(sio_data, WINB_BASE_REG) << 8) | - superio_inb(sio_data, WINB_BASE_REG + 1); - *addr = val & WINB_ALIGNMENT; - if (*addr == 0) { - pr_warn("Base address not set, skipping\n"); - goto exit; - } - - val = superio_inb(sio_data, WINB_ACT_REG); - if (!(val & 0x01)) { - pr_warn("Enabling HWM logical device\n"); - superio_outb(sio_data, WINB_ACT_REG, val | 0x01); - } - - err = 0; - pr_info(DRVNAME ": Found %s chip at %#x\n", - names[sio_data->type], *addr); - - exit: - superio_exit(sio_data); - return err; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->alarms); } +static DEVICE_ATTR_RO(alarms); #define VIN_UNIT_ATTRS(_X_) \ &sensor_dev_attr_in##_X_##_input.dev_attr.attr, \ @@ -1334,6 +1387,100 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, &sensor_dev_attr_temp##_X_##_alarm.dev_attr.attr, \ &sensor_dev_attr_temp##_X_##_beep.dev_attr.attr +static ssize_t +beep_mask_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", + (long)BEEP_MASK_FROM_REG(data->beep_mask)); +} + +static ssize_t +beep_mask_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + + /* preserve beep enable */ + data->beep_mask = (data->beep_mask & 0x8000) + | BEEP_MASK_TO_REG(val); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, + data->beep_mask & 0xff); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, + ((data->beep_mask) >> 16) & 0xff); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, + (data->beep_mask >> 8) & 0xff); + + mutex_unlock(&data->update_lock); + return count; +} + +static DEVICE_ATTR_RW(beep_mask); + +static ssize_t +pwm_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->pwm[nr]); +} + +static ssize_t +pwm_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + + if (data->type == w83627thf) { + /* bits 0-3 are reserved in 627THF */ + data->pwm[nr] = PWM_TO_REG(val) & 0xf0; + w83627hf_write_value(data, + W836X7HF_REG_PWM(data->type, nr), + data->pwm[nr] | + (w83627hf_read_value(data, + W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); + } else { + data->pwm[nr] = PWM_TO_REG(val); + w83627hf_write_value(data, + W836X7HF_REG_PWM(data->type, nr), + data->pwm[nr]); + } + + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); +static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); +static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2); + +static ssize_t +name_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static DEVICE_ATTR_RO(name); + static struct attribute *w83627hf_attributes[] = { &dev_attr_in0_input.attr, &dev_attr_in0_min.attr, @@ -1366,6 +1513,131 @@ static const struct attribute_group w83627hf_group = { .attrs = w83627hf_attributes, }; +static ssize_t +pwm_freq_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + if (data->type == w83627hf) + return sprintf(buf, "%ld\n", + pwm_freq_from_reg_627hf(data->pwm_freq[nr])); + else + return sprintf(buf, "%ld\n", + pwm_freq_from_reg(data->pwm_freq[nr])); +} + +static ssize_t +pwm_freq_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + static const u8 mask[]={0xF8, 0x8F}; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + + if (data->type == w83627hf) { + data->pwm_freq[nr] = pwm_freq_to_reg_627hf(val); + w83627hf_write_value(data, W83627HF_REG_PWM_FREQ, + (data->pwm_freq[nr] << (nr*4)) | + (w83627hf_read_value(data, + W83627HF_REG_PWM_FREQ) & mask[nr])); + } else { + data->pwm_freq[nr] = pwm_freq_to_reg(val); + w83627hf_write_value(data, W83637HF_REG_PWM_FREQ[nr], + data->pwm_freq[nr]); + } + + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_RW(pwm1_freq, pwm_freq, 0); +static SENSOR_DEVICE_ATTR_RW(pwm2_freq, pwm_freq, 1); +static SENSOR_DEVICE_ATTR_RW(pwm3_freq, pwm_freq, 2); + +static ssize_t +cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); +} + +static DEVICE_ATTR_RO(cpu0_vid); + +static ssize_t +vrm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%ld\n", (long) data->vrm); +} + +static ssize_t +vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (val > 255) + return -EINVAL; + data->vrm = val; + + return count; +} + +static DEVICE_ATTR_RW(vrm); + +static ssize_t +pwm_enable_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_enable[nr]); +} + +static ssize_t +pwm_enable_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (!val || val > 3) /* modes 1, 2 and 3 are supported */ + return -EINVAL; + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val; + reg = w83627hf_read_value(data, W83627THF_REG_PWM_ENABLE[nr]); + reg &= ~(0x03 << W83627THF_PWM_ENABLE_SHIFT[nr]); + reg |= (val - 1) << W83627THF_PWM_ENABLE_SHIFT[nr]; + w83627hf_write_value(data, W83627THF_REG_PWM_ENABLE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_enable, 0); +static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_enable, 1); +static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_enable, 2); + static struct attribute *w83627hf_attributes_opt[] = { VIN_UNIT_ATTRS(1), VIN_UNIT_ATTRS(5), @@ -1568,349 +1840,81 @@ static int w83627hf_remove(struct platform_device *pdev) return 0; } -/* Registers 0x50-0x5f are banked */ -static inline void w83627hf_set_bank(struct w83627hf_data *data, u16 reg) -{ - if ((reg & 0x00f0) == 0x50) { - outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, data->addr + W83781D_DATA_REG_OFFSET); - } -} - -/* Not strictly necessary, but play it safe for now */ -static inline void w83627hf_reset_bank(struct w83627hf_data *data, u16 reg) -{ - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, data->addr + W83781D_DATA_REG_OFFSET); - } -} - -static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) -{ - int res, word_sized; - - mutex_lock(&data->lock); - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x50) - || ((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - w83627hf_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(data->addr + W83781D_DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - data->addr + W83781D_ADDR_REG_OFFSET); - res = - (res << 8) + inb_p(data->addr + - W83781D_DATA_REG_OFFSET); - } - w83627hf_reset_bank(data, reg); - mutex_unlock(&data->lock); - return res; -} +static struct platform_driver w83627hf_driver = { + .driver = { + .name = DRVNAME, + .pm = W83627HF_DEV_PM_OPS, + }, + .probe = w83627hf_probe, + .remove = w83627hf_remove, +}; -static int w83627thf_read_gpio5(struct platform_device *pdev) +static int __init w83627hf_find(int sioaddr, unsigned short *addr, + struct w83627hf_sio_data *sio_data) { - struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); - int res = 0xff, sel; - - if (superio_enter(sio_data)) { - /* - * Some other driver reserved the address space for itself. - * We don't want to fail driver instantiation because of that, - * so display a warning and keep going. - */ - dev_warn(&pdev->dev, - "Can not read VID data: Failed to enable SuperIO access\n"); - return res; - } + int err; + u16 val; - superio_select(sio_data, W83627HF_LD_GPIO5); + static __initconst char *const names[] = { + "W83627HF", + "W83627THF", + "W83697HF", + "W83637HF", + "W83687THF", + }; - res = 0xff; + sio_data->sioaddr = sioaddr; + err = superio_enter(sio_data); + if (err) + return err; - /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) { - dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); + err = -ENODEV; + val = force_id ? force_id : superio_inb(sio_data, DEVID); + switch (val) { + case W627_DEVID: + sio_data->type = w83627hf; + break; + case W627THF_DEVID: + sio_data->type = w83627thf; + break; + case W697_DEVID: + sio_data->type = w83697hf; + break; + case W637_DEVID: + sio_data->type = w83637hf; + break; + case W687THF_DEVID: + sio_data->type = w83687thf; + break; + case 0xff: /* No device at all */ goto exit; - } - - /* - * Make sure the pins are configured for input - * There must be at least five (VRM 9), and possibly 6 (VRM 10) - */ - sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f; - if ((sel & 0x1f) != 0x1f) { - dev_dbg(&pdev->dev, "GPIO5 not configured for VID " - "function\n"); + default: + pr_debug(DRVNAME ": Unsupported chip (DEVID=0x%02x)\n", val); goto exit; } - dev_info(&pdev->dev, "Reading VID from GPIO5\n"); - res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel; - -exit: - superio_exit(sio_data); - return res; -} - -static int w83687thf_read_vid(struct platform_device *pdev) -{ - struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); - int res = 0xff; - - if (superio_enter(sio_data)) { - /* - * Some other driver reserved the address space for itself. - * We don't want to fail driver instantiation because of that, - * so display a warning and keep going. - */ - dev_warn(&pdev->dev, - "Can not read VID data: Failed to enable SuperIO access\n"); - return res; - } - superio_select(sio_data, W83627HF_LD_HWM); - - /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) { - dev_dbg(&pdev->dev, "VID disabled, no VID function\n"); + val = (superio_inb(sio_data, WINB_BASE_REG) << 8) | + superio_inb(sio_data, WINB_BASE_REG + 1); + *addr = val & WINB_ALIGNMENT; + if (*addr == 0) { + pr_warn("Base address not set, skipping\n"); goto exit; } - /* Make sure the pins are configured for input */ - if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) { - dev_dbg(&pdev->dev, "VID configured as output, " - "no VID function\n"); - goto exit; + val = superio_inb(sio_data, WINB_ACT_REG); + if (!(val & 0x01)) { + pr_warn("Enabling HWM logical device\n"); + superio_outb(sio_data, WINB_ACT_REG, val | 0x01); } - res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f; + err = 0; + pr_info(DRVNAME ": Found %s chip at %#x\n", + names[sio_data->type], *addr); -exit: + exit: superio_exit(sio_data); - return res; -} - -static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value) -{ - int word_sized; - - mutex_lock(&data->lock); - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - w83627hf_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, - data->addr + W83781D_DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - data->addr + W83781D_ADDR_REG_OFFSET); - } - outb_p(value & 0xff, - data->addr + W83781D_DATA_REG_OFFSET); - w83627hf_reset_bank(data, reg); - mutex_unlock(&data->lock); - return 0; -} - -static void w83627hf_init_device(struct platform_device *pdev) -{ - struct w83627hf_data *data = platform_get_drvdata(pdev); - int i; - enum chips type = data->type; - u8 tmp; - - /* Minimize conflicts with other winbond i2c-only clients... */ - /* disable i2c subclients... how to disable main i2c client?? */ - /* force i2c address to relatively uncommon address */ - if (type == w83627hf) { - w83627hf_write_value(data, W83781D_REG_I2C_SUBADDR, 0x89); - w83627hf_write_value(data, W83781D_REG_I2C_ADDR, force_i2c); - } - - /* Read VID only once */ - if (type == w83627hf || type == w83637hf) { - int lo = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); - int hi = w83627hf_read_value(data, W83781D_REG_CHIPID); - data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); - } else if (type == w83627thf) { - data->vid = w83627thf_read_gpio5(pdev); - } else if (type == w83687thf) { - data->vid = w83687thf_read_vid(pdev); - } - - /* Read VRM & OVT Config only once */ - if (type == w83627thf || type == w83637hf || type == w83687thf) { - data->vrm_ovt = - w83627hf_read_value(data, W83627THF_REG_VRM_OVT_CFG); - } - - tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); - for (i = 1; i <= 3; i++) { - if (!(tmp & BIT_SCFG1[i - 1])) { - data->sens[i - 1] = 4; - } else { - if (w83627hf_read_value - (data, - W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) - data->sens[i - 1] = 1; - else - data->sens[i - 1] = 2; - } - if ((type == w83697hf) && (i == 2)) - break; - } - - if(init) { - /* Enable temp2 */ - tmp = w83627hf_read_value(data, W83627HF_REG_TEMP2_CONFIG); - if (tmp & 0x01) { - dev_warn(&pdev->dev, "Enabling temp2, readings " - "might not make sense\n"); - w83627hf_write_value(data, W83627HF_REG_TEMP2_CONFIG, - tmp & 0xfe); - } - - /* Enable temp3 */ - if (type != w83697hf) { - tmp = w83627hf_read_value(data, - W83627HF_REG_TEMP3_CONFIG); - if (tmp & 0x01) { - dev_warn(&pdev->dev, "Enabling temp3, " - "readings might not make sense\n"); - w83627hf_write_value(data, - W83627HF_REG_TEMP3_CONFIG, tmp & 0xfe); - } - } - } - - /* Start monitoring */ - w83627hf_write_value(data, W83781D_REG_CONFIG, - (w83627hf_read_value(data, - W83781D_REG_CONFIG) & 0xf7) - | 0x01); - - /* Enable VBAT monitoring if needed */ - tmp = w83627hf_read_value(data, W83781D_REG_VBAT); - if (!(tmp & 0x01)) - w83627hf_write_value(data, W83781D_REG_VBAT, tmp | 0x01); -} - -static void w83627hf_update_fan_div(struct w83627hf_data *data) -{ - int reg; - - reg = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); - data->fan_div[0] = (reg >> 4) & 0x03; - data->fan_div[1] = (reg >> 6) & 0x03; - if (data->type != w83697hf) { - data->fan_div[2] = (w83627hf_read_value(data, - W83781D_REG_PIN) >> 6) & 0x03; - } - reg = w83627hf_read_value(data, W83781D_REG_VBAT); - data->fan_div[0] |= (reg >> 3) & 0x04; - data->fan_div[1] |= (reg >> 4) & 0x04; - if (data->type != w83697hf) - data->fan_div[2] |= (reg >> 5) & 0x04; -} - -static struct w83627hf_data *w83627hf_update_device(struct device *dev) -{ - struct w83627hf_data *data = dev_get_drvdata(dev); - int i, num_temps = (data->type == w83697hf) ? 2 : 3; - int num_pwms = (data->type == w83697hf) ? 2 : 3; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i <= 8; i++) { - /* skip missing sensors */ - if (((data->type == w83697hf) && (i == 1)) || - ((data->type != w83627hf && data->type != w83697hf) - && (i == 5 || i == 6))) - continue; - data->in[i] = - w83627hf_read_value(data, W83781D_REG_IN(i)); - data->in_min[i] = - w83627hf_read_value(data, - W83781D_REG_IN_MIN(i)); - data->in_max[i] = - w83627hf_read_value(data, - W83781D_REG_IN_MAX(i)); - } - for (i = 0; i <= 2; i++) { - data->fan[i] = - w83627hf_read_value(data, W83627HF_REG_FAN(i)); - data->fan_min[i] = - w83627hf_read_value(data, - W83627HF_REG_FAN_MIN(i)); - } - for (i = 0; i <= 2; i++) { - u8 tmp = w83627hf_read_value(data, - W836X7HF_REG_PWM(data->type, i)); - /* bits 0-3 are reserved in 627THF */ - if (data->type == w83627thf) - tmp &= 0xf0; - data->pwm[i] = tmp; - if (i == 1 && - (data->type == w83627hf || data->type == w83697hf)) - break; - } - if (data->type == w83627hf) { - u8 tmp = w83627hf_read_value(data, - W83627HF_REG_PWM_FREQ); - data->pwm_freq[0] = tmp & 0x07; - data->pwm_freq[1] = (tmp >> 4) & 0x07; - } else if (data->type != w83627thf) { - for (i = 1; i <= 3; i++) { - data->pwm_freq[i - 1] = - w83627hf_read_value(data, - W83637HF_REG_PWM_FREQ[i - 1]); - if (i == 2 && (data->type == w83697hf)) - break; - } - } - if (data->type != w83627hf) { - for (i = 0; i < num_pwms; i++) { - u8 tmp = w83627hf_read_value(data, - W83627THF_REG_PWM_ENABLE[i]); - data->pwm_enable[i] = - ((tmp >> W83627THF_PWM_ENABLE_SHIFT[i]) - & 0x03) + 1; - } - } - for (i = 0; i < num_temps; i++) { - data->temp[i] = w83627hf_read_value( - data, w83627hf_reg_temp[i]); - data->temp_max[i] = w83627hf_read_value( - data, w83627hf_reg_temp_over[i]); - data->temp_max_hyst[i] = w83627hf_read_value( - data, w83627hf_reg_temp_hyst[i]); - } - - w83627hf_update_fan_div(data); - - data->alarms = - w83627hf_read_value(data, W83781D_REG_ALARM1) | - (w83627hf_read_value(data, W83781D_REG_ALARM2) << 8) | - (w83627hf_read_value(data, W83781D_REG_ALARM3) << 16); - i = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2); - data->beep_mask = (i << 8) | - w83627hf_read_value(data, W83781D_REG_BEEP_INTS1) | - w83627hf_read_value(data, W83781D_REG_BEEP_INTS3) << 16; - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; + return err; } static int __init w83627hf_device_add(unsigned short address, diff --git a/drivers/hwmon/w83773g.c b/drivers/hwmon/w83773g.c index 96b695b32572..88d11dc5feb9 100644 --- a/drivers/hwmon/w83773g.c +++ b/drivers/hwmon/w83773g.c @@ -259,8 +259,7 @@ static const struct regmap_config w83773_regmap_config = { .val_bits = 8, }; -static int w83773_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int w83773_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -296,7 +295,7 @@ static struct i2c_driver w83773_driver = { .name = "w83773g", .of_match_table = of_match_ptr(w83773_of_match), }, - .probe = w83773_probe, + .probe_new = w83773_probe, .id_table = w83773_id, }; diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 015f1ea31966..dacabf25e83f 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -203,7 +203,7 @@ struct w83781d_data { int isa_addr; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ struct i2c_client *lm75[2]; /* for secondary I2C addresses */ @@ -814,7 +814,7 @@ store_sensor(struct device *dev, struct device_attribute *da, dev_warn(dev, "Sensor type %d is deprecated, please use 4 instead\n", W83781D_DEFAULT_BETA); - /* fall through */ + fallthrough; case 4: /* thermistor */ tmp = w83781d_read_value(data, W83781D_REG_SCFG1); w83781d_write_value(data, W83781D_REG_SCFG1, @@ -1171,7 +1171,7 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info) if (isa) mutex_unlock(&isa->update_lock); - strlcpy(info->type, client_name, I2C_NAME_SIZE); + strscpy(info->type, client_name, I2C_NAME_SIZE); return 0; @@ -1192,8 +1192,9 @@ static void w83781d_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &w83781d_group_other); } -static int -w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) +static const struct i2c_device_id w83781d_ids[]; + +static int w83781d_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct w83781d_data *data; @@ -1207,7 +1208,7 @@ w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) mutex_init(&data->lock); mutex_init(&data->update_lock); - data->type = id->driver_data; + data->type = i2c_match_id(w83781d_ids, client)->driver_data; data->client = client; /* attach secondary i2c lm75-like clients */ @@ -1238,7 +1239,7 @@ w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) return err; } -static int +static void w83781d_remove(struct i2c_client *client) { struct w83781d_data *data = i2c_get_clientdata(client); @@ -1249,8 +1250,6 @@ w83781d_remove(struct i2c_client *client) i2c_unregister_device(data->lm75[0]); i2c_unregister_device(data->lm75[1]); - - return 0; } static int @@ -1553,7 +1552,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) W83781D_REG_BEEP_INTS3) << 16; } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -1570,12 +1569,23 @@ static const struct i2c_device_id w83781d_ids[] = { }; MODULE_DEVICE_TABLE(i2c, w83781d_ids); +static const struct of_device_id w83781d_of_match[] = { + { .compatible = "winbond,w83781d" }, + { .compatible = "winbond,w83781g" }, + { .compatible = "winbond,w83782d" }, + { .compatible = "winbond,w83783s" }, + { .compatible = "asus,as99127f" }, + { }, +}; +MODULE_DEVICE_TABLE(of, w83781d_of_match); + static struct i2c_driver w83781d_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "w83781d", + .of_match_table = w83781d_of_match, }, - .probe = w83781d_probe, + .probe_new = w83781d_probe, .remove = w83781d_remove, .id_table = w83781d_ids, .detect = w83781d_detect, diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index aad8d4da5802..eaf691365023 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -270,12 +270,9 @@ struct w83791d_data { struct device *hwmon_dev; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ - /* array of 2 pointers to subclients */ - struct i2c_client *lm75[2]; - /* volts */ u8 in[NUMBER_OF_VIN]; /* Register value */ u8 in_max[NUMBER_OF_VIN]; /* Register value */ @@ -315,11 +312,10 @@ struct w83791d_data { u8 vrm; /* hwmon-vid */ }; -static int w83791d_probe(struct i2c_client *client, - const struct i2c_device_id *id); +static int w83791d_probe(struct i2c_client *client); static int w83791d_detect(struct i2c_client *client, struct i2c_board_info *info); -static int w83791d_remove(struct i2c_client *client); +static void w83791d_remove(struct i2c_client *client); static int w83791d_read(struct i2c_client *client, u8 reg); static int w83791d_write(struct i2c_client *client, u8 reg, u8 value); @@ -342,7 +338,7 @@ static struct i2c_driver w83791d_driver = { .driver = { .name = "w83791d", }, - .probe = w83791d_probe, + .probe_new = w83791d_probe, .remove = w83791d_remove, .id_table = w83791d_id, .detect = w83791d_detect, @@ -1258,7 +1254,6 @@ static const struct attribute_group w83791d_group_fanpwm45 = { static int w83791d_detect_subclients(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; - struct w83791d_data *data = i2c_get_clientdata(client); int address = client->addr; int i, id; u8 val; @@ -1281,22 +1276,19 @@ static int w83791d_detect_subclients(struct i2c_client *client) } val = w83791d_read(client, W83791D_REG_I2C_SUBADDR); - if (!(val & 0x08)) - data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter, - 0x48 + (val & 0x7)); - if (!(val & 0x80)) { - if (!IS_ERR(data->lm75[0]) && - ((val & 0x7) == ((val >> 4) & 0x7))) { - dev_err(&client->dev, - "duplicate addresses 0x%x, " - "use force_subclient\n", - data->lm75[0]->addr); - return -ENODEV; - } - data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter, - 0x48 + ((val >> 4) & 0x7)); + + if (!(val & 0x88) && (val & 0x7) == ((val >> 4) & 0x7)) { + dev_err(&client->dev, + "duplicate addresses 0x%x, use force_subclient\n", 0x48 + (val & 0x7)); + return -ENODEV; } + if (!(val & 0x08)) + devm_i2c_new_dummy_device(&client->dev, adapter, 0x48 + (val & 0x7)); + + if (!(val & 0x80)) + devm_i2c_new_dummy_device(&client->dev, adapter, 0x48 + ((val >> 4) & 0x7)); + return 0; } @@ -1341,13 +1333,12 @@ static int w83791d_detect(struct i2c_client *client, if (val1 != 0x71 || val2 != 0x5c) return -ENODEV; - strlcpy(info->type, "w83791d", I2C_NAME_SIZE); + strscpy(info->type, "w83791d", I2C_NAME_SIZE); return 0; } -static int w83791d_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int w83791d_probe(struct i2c_client *client) { struct w83791d_data *data; struct device *dev = &client->dev; @@ -1414,14 +1405,12 @@ error4: return err; } -static int w83791d_remove(struct i2c_client *client) +static void w83791d_remove(struct i2c_client *client) { struct w83791d_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &w83791d_group); - - return 0; } static void w83791d_init_client(struct i2c_client *client) @@ -1605,7 +1594,7 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) << 4; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 7fc8a1160c8f..6d160eee1446 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -261,12 +261,9 @@ struct w83792d_data { struct device *hwmon_dev; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ - /* array of 2 pointers to subclients */ - struct i2c_client *lm75[2]; - u8 in[9]; /* Register value */ u8 in_max[9]; /* Register value */ u8 in_min[9]; /* Register value */ @@ -286,11 +283,10 @@ struct w83792d_data { u8 sf2_levels[3][4]; /* Smart FanII: Fan1,2,3 duty cycle levels */ }; -static int w83792d_probe(struct i2c_client *client, - const struct i2c_device_id *id); +static int w83792d_probe(struct i2c_client *client); static int w83792d_detect(struct i2c_client *client, struct i2c_board_info *info); -static int w83792d_remove(struct i2c_client *client); +static void w83792d_remove(struct i2c_client *client); static struct w83792d_data *w83792d_update_device(struct device *dev); #ifdef DEBUG @@ -310,7 +306,7 @@ static struct i2c_driver w83792d_driver = { .driver = { .name = "w83792d", }, - .probe = w83792d_probe, + .probe_new = w83792d_probe, .remove = w83792d_remove, .id_table = w83792d_id, .detect = w83792d_detect, @@ -744,7 +740,7 @@ intrusion0_alarm_store(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); reg = w83792d_read_value(client, W83792D_REG_CHASSIS_CLR); w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, reg | 0x80); - data->valid = 0; /* Force cache refresh */ + data->valid = false; /* Force cache refresh */ mutex_unlock(&data->update_lock); return count; @@ -928,7 +924,6 @@ w83792d_detect_subclients(struct i2c_client *new_client) int address = new_client->addr; u8 val; struct i2c_adapter *adapter = new_client->adapter; - struct w83792d_data *data = i2c_get_clientdata(new_client); id = i2c_adapter_id(adapter); if (force_subclients[0] == id && force_subclients[1] == address) { @@ -947,21 +942,19 @@ w83792d_detect_subclients(struct i2c_client *new_client) } val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR); - if (!(val & 0x08)) - data->lm75[0] = devm_i2c_new_dummy_device(&new_client->dev, adapter, - 0x48 + (val & 0x7)); - if (!(val & 0x80)) { - if (!IS_ERR(data->lm75[0]) && - ((val & 0x7) == ((val >> 4) & 0x7))) { - dev_err(&new_client->dev, - "duplicate addresses 0x%x, use force_subclient\n", - data->lm75[0]->addr); - return -ENODEV; - } - data->lm75[1] = devm_i2c_new_dummy_device(&new_client->dev, adapter, - 0x48 + ((val >> 4) & 0x7)); + + if (!(val & 0x88) && (val & 0x7) == ((val >> 4) & 0x7)) { + dev_err(&new_client->dev, + "duplicate addresses 0x%x, use force_subclient\n", 0x48 + (val & 0x7)); + return -ENODEV; } + if (!(val & 0x08)) + devm_i2c_new_dummy_device(&new_client->dev, adapter, 0x48 + (val & 0x7)); + + if (!(val & 0x80)) + devm_i2c_new_dummy_device(&new_client->dev, adapter, 0x48 + ((val >> 4) & 0x7)); + return 0; } @@ -1353,13 +1346,13 @@ w83792d_detect(struct i2c_client *client, struct i2c_board_info *info) if (val1 != 0x7a || val2 != 0x5c) return -ENODEV; - strlcpy(info->type, "w83792d", I2C_NAME_SIZE); + strscpy(info->type, "w83792d", I2C_NAME_SIZE); return 0; } static int -w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id) +w83792d_probe(struct i2c_client *client) { struct w83792d_data *data; struct device *dev = &client->dev; @@ -1436,7 +1429,7 @@ exit_remove_files: return err; } -static int +static void w83792d_remove(struct i2c_client *client) { struct w83792d_data *data = i2c_get_clientdata(client); @@ -1447,8 +1440,6 @@ w83792d_remove(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++) sysfs_remove_group(&client->dev.kobj, &w83792d_group_fan[i]); - - return 0; } static void @@ -1596,7 +1587,7 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 3f59f2a1a5e3..a4926d907198 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -202,10 +202,9 @@ static inline s8 TEMP_TO_REG(long val, s8 min, s8 max) } struct w83793_data { - struct i2c_client *lm75[2]; struct device *hwmon_dev; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ unsigned long last_nonvolatile; /* In jiffies, last time we update the * nonvolatile registers @@ -283,11 +282,10 @@ static void w83793_release_resources(struct kref *ref) static u8 w83793_read_value(struct i2c_client *client, u16 reg); static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value); -static int w83793_probe(struct i2c_client *client, - const struct i2c_device_id *id); +static int w83793_probe(struct i2c_client *client); static int w83793_detect(struct i2c_client *client, struct i2c_board_info *info); -static int w83793_remove(struct i2c_client *client); +static void w83793_remove(struct i2c_client *client); static void w83793_init_client(struct i2c_client *client); static void w83793_update_nonvolatile(struct device *dev); static struct w83793_data *w83793_update_device(struct device *dev); @@ -303,7 +301,7 @@ static struct i2c_driver w83793_driver = { .driver = { .name = "w83793", }, - .probe = w83793_probe, + .probe_new = w83793_probe, .remove = w83793_remove, .id_table = w83793_id, .detect = w83793_detect, @@ -454,7 +452,7 @@ store_chassis_clear(struct device *dev, mutex_lock(&data->update_lock); reg = w83793_read_value(client, W83793_REG_CLR_CHASSIS); w83793_write_value(client, W83793_REG_CLR_CHASSIS, reg | 0x80); - data->valid = 0; /* Force cache refresh */ + data->valid = false; /* Force cache refresh */ mutex_unlock(&data->update_lock); return count; } @@ -1497,7 +1495,7 @@ static struct notifier_block watchdog_notifier = { * Init / remove routines */ -static int w83793_remove(struct i2c_client *client) +static void w83793_remove(struct i2c_client *client) { struct w83793_data *data = i2c_get_clientdata(client); struct device *dev = &client->dev; @@ -1556,8 +1554,6 @@ static int w83793_remove(struct i2c_client *client) mutex_lock(&watchdog_data_mutex); kref_put(&data->kref, w83793_release_resources); mutex_unlock(&watchdog_data_mutex); - - return 0; } static int @@ -1567,7 +1563,6 @@ w83793_detect_subclients(struct i2c_client *client) int address = client->addr; u8 tmp; struct i2c_adapter *adapter = client->adapter; - struct w83793_data *data = i2c_get_clientdata(client); id = i2c_adapter_id(adapter); if (force_subclients[0] == id && force_subclients[1] == address) { @@ -1587,21 +1582,19 @@ w83793_detect_subclients(struct i2c_client *client) } tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR); - if (!(tmp & 0x08)) - data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter, - 0x48 + (tmp & 0x7)); - if (!(tmp & 0x80)) { - if (!IS_ERR(data->lm75[0]) - && ((tmp & 0x7) == ((tmp >> 4) & 0x7))) { - dev_err(&client->dev, - "duplicate addresses 0x%x, " - "use force_subclients\n", data->lm75[0]->addr); - return -ENODEV; - } - data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter, - 0x48 + ((tmp >> 4) & 0x7)); + + if (!(tmp & 0x88) && (tmp & 0x7) == ((tmp >> 4) & 0x7)) { + dev_err(&client->dev, + "duplicate addresses 0x%x, use force_subclient\n", 0x48 + (tmp & 0x7)); + return -ENODEV; } + if (!(tmp & 0x08)) + devm_i2c_new_dummy_device(&client->dev, adapter, 0x48 + (tmp & 0x7)); + + if (!(tmp & 0x80)) + devm_i2c_new_dummy_device(&client->dev, adapter, 0x48 + ((tmp >> 4) & 0x7)); + return 0; } @@ -1641,13 +1634,12 @@ static int w83793_detect(struct i2c_client *client, if (chip_id != 0x7b) return -ENODEV; - strlcpy(info->type, "w83793", I2C_NAME_SIZE); + strscpy(info->type, "w83793", I2C_NAME_SIZE); return 0; } -static int w83793_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int w83793_probe(struct i2c_client *client) { struct device *dev = &client->dev; static const int watchdog_minors[] = { @@ -2083,7 +2075,7 @@ static struct w83793_data *w83793_update_device(struct device *dev) data->vid[1] = w83793_read_value(client, W83793_REG_VID_INB); w83793_update_nonvolatile(dev); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; END: mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 44f68b965aec..84ff5c57e98c 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -379,7 +379,7 @@ struct w83795_data { u8 enable_beep; u8 beeps[6]; /* Register value */ - char valid; + bool valid; char valid_limits; char valid_pwm_config; }; @@ -684,7 +684,7 @@ static struct w83795_data *w83795_update_device(struct device *dev) tmp & ~ALARM_CTRL_RTSACS); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; END: mutex_unlock(&data->update_lock); @@ -764,7 +764,7 @@ store_chassis_clear(struct device *dev, /* Clear status and force cache refresh */ w83795_read(client, W83795_REG_ALARM(5)); - data->valid = 0; + data->valid = false; mutex_unlock(&data->update_lock); return count; } @@ -1967,7 +1967,7 @@ static int w83795_detect(struct i2c_client *client, else chip_name = "w83795g"; - strlcpy(info->type, chip_name, I2C_NAME_SIZE); + strscpy(info->type, chip_name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Found %s rev. %c at 0x%02hx\n", chip_name, 'A' + (device_id & 0xf), address); @@ -2127,15 +2127,16 @@ static void w83795_apply_temp_config(struct w83795_data *data, u8 config, if (temp_chan >= 4) break; data->temp_mode |= 1 << temp_chan; - /* fall through */ + fallthrough; case 0x3: /* Thermistor */ data->has_temp |= 1 << temp_chan; break; } } -static int w83795_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id w83795_id[]; + +static int w83795_probe(struct i2c_client *client) { int i; u8 tmp; @@ -2148,7 +2149,7 @@ static int w83795_probe(struct i2c_client *client, return -ENOMEM; i2c_set_clientdata(client, data); - data->chip_type = id->driver_data; + data->chip_type = i2c_match_id(w83795_id, client)->driver_data; data->bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); mutex_init(&data->update_lock); @@ -2234,14 +2235,12 @@ exit_remove: return err; } -static int w83795_remove(struct i2c_client *client) +static void w83795_remove(struct i2c_client *client) { struct w83795_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); w83795_handle_files(&client->dev, device_remove_file_wrapper); - - return 0; } @@ -2256,7 +2255,7 @@ static struct i2c_driver w83795_driver = { .driver = { .name = "w83795", }, - .probe = w83795_probe, + .probe_new = w83795_probe, .remove = w83795_remove, .id_table = w83795_id, diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index 6f6d925cf017..f3622de0d96f 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -62,11 +62,10 @@ static const unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; * Functions declaration */ -static int w83l785ts_probe(struct i2c_client *client, - const struct i2c_device_id *id); +static int w83l785ts_probe(struct i2c_client *client); static int w83l785ts_detect(struct i2c_client *client, struct i2c_board_info *info); -static int w83l785ts_remove(struct i2c_client *client); +static void w83l785ts_remove(struct i2c_client *client); static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval); static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); @@ -85,7 +84,7 @@ static struct i2c_driver w83l785ts_driver = { .driver = { .name = "w83l785ts", }, - .probe = w83l785ts_probe, + .probe_new = w83l785ts_probe, .remove = w83l785ts_remove, .id_table = w83l785ts_id, .detect = w83l785ts_detect, @@ -99,7 +98,7 @@ static struct i2c_driver w83l785ts_driver = { struct w83l785ts_data { struct device *hwmon_dev; struct mutex update_lock; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values */ @@ -158,13 +157,12 @@ static int w83l785ts_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "w83l785ts", I2C_NAME_SIZE); + strscpy(info->type, "w83l785ts", I2C_NAME_SIZE); return 0; } -static int w83l785ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int w83l785ts_probe(struct i2c_client *client) { struct w83l785ts_data *data; struct device *dev = &client->dev; @@ -205,7 +203,7 @@ exit_remove: return err; } -static int w83l785ts_remove(struct i2c_client *client) +static void w83l785ts_remove(struct i2c_client *client) { struct w83l785ts_data *data = i2c_get_clientdata(client); @@ -214,8 +212,6 @@ static int w83l785ts_remove(struct i2c_client *client) &sensor_dev_attr_temp1_input.dev_attr); device_remove_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr); - - return 0; } static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) @@ -272,7 +268,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev) W83L785TS_REG_TEMP_OVER, data->temp[1]); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index ce98ec8794e2..2c4646fa8426 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -113,7 +113,7 @@ DIV_TO_REG(long val) struct w83l786ng_data { struct i2c_client *client; struct mutex update_lock; - char valid; /* !=0 if following fields are valid */ + bool valid; /* true if following fields are valid */ unsigned long last_updated; /* In jiffies */ unsigned long last_nonvolatile; /* In jiffies, last time we update the * nonvolatile registers */ @@ -209,7 +209,7 @@ static struct w83l786ng_data *w83l786ng_update_device(struct device *dev) data->tolerance[1] = (reg_tmp >> 4) & 0x0f; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } @@ -687,7 +687,7 @@ w83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE); + strscpy(info->type, "w83l786ng", I2C_NAME_SIZE); return 0; } @@ -706,7 +706,7 @@ static void w83l786ng_init_client(struct i2c_client *client) } static int -w83l786ng_probe(struct i2c_client *client, const struct i2c_device_id *id) +w83l786ng_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct w83l786ng_data *data; @@ -752,7 +752,7 @@ static struct i2c_driver w83l786ng_driver = { .driver = { .name = "w83l786ng", }, - .probe = w83l786ng_probe, + .probe_new = w83l786ng_probe, .id_table = w83l786ng_id, .detect = w83l786ng_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index f2a5af239c95..5cde837bfd09 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -93,6 +93,7 @@ struct slimpro_resp_msg { struct xgene_hwmon_dev { struct device *dev; struct mbox_chan *mbox_chan; + struct pcc_mbox_chan *pcc_chan; struct mbox_client mbox_client; int mbox_idx; @@ -329,14 +330,14 @@ static ssize_t temp1_input_show(struct device *dev, temp = sign_extend32(val, TEMP_NEGATIVE_BIT); - return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp)); + return sysfs_emit(buf, "%d\n", CELSIUS_TO_mCELSIUS(temp)); } static ssize_t temp1_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "SoC Temperature\n"); + return sysfs_emit(buf, "SoC Temperature\n"); } static ssize_t temp1_critical_alarm_show(struct device *dev, @@ -345,21 +346,21 @@ static ssize_t temp1_critical_alarm_show(struct device *dev, { struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm); + return sysfs_emit(buf, "%d\n", ctx->temp_critical_alarm); } static ssize_t power1_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "CPU power\n"); + return sysfs_emit(buf, "CPU power\n"); } static ssize_t power2_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "IO power\n"); + return sysfs_emit(buf, "IO power\n"); } static ssize_t power1_input_show(struct device *dev, @@ -374,7 +375,7 @@ static ssize_t power1_input_show(struct device *dev, if (rc < 0) return rc; - return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); + return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val)); } static ssize_t power2_input_show(struct device *dev, @@ -389,7 +390,7 @@ static ssize_t power2_input_show(struct device *dev, if (rc < 0) return rc; - return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); + return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val)); } static DEVICE_ATTR_RO(temp1_label); @@ -652,14 +653,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev) goto out_mbox_free; } } else { - struct acpi_pcct_hw_reduced *cppc_ss; + struct pcc_mbox_chan *pcc_chan; const struct acpi_device_id *acpi_id; int version; acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); - if (!acpi_id) - return -EINVAL; + if (!acpi_id) { + rc = -EINVAL; + goto out_mbox_free; + } version = (int)acpi_id->driver_data; @@ -671,26 +674,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev) } cl->rx_callback = xgene_hwmon_pcc_rx_cb; - ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx); - if (IS_ERR(ctx->mbox_chan)) { + pcc_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx); + if (IS_ERR(pcc_chan)) { dev_err(&pdev->dev, "PPC channel request failed\n"); rc = -ENODEV; goto out_mbox_free; } - /* - * The PCC mailbox controller driver should - * have parsed the PCCT (global table of all - * PCC channels) and stored pointers to the - * subspace communication region in con_priv. - */ - cppc_ss = ctx->mbox_chan->con_priv; - if (!cppc_ss) { - dev_err(&pdev->dev, "PPC subspace not found\n"); - rc = -ENODEV; - goto out; - } + ctx->pcc_chan = pcc_chan; + ctx->mbox_chan = pcc_chan->mchan; if (!ctx->mbox_chan->mbox->txdone_irq) { dev_err(&pdev->dev, "PCC IRQ not supported\n"); @@ -702,16 +695,16 @@ static int xgene_hwmon_probe(struct platform_device *pdev) * This is the shared communication region * for the OS and Platform to communicate over. */ - ctx->comm_base_addr = cppc_ss->base_address; + ctx->comm_base_addr = pcc_chan->shmem_base_addr; if (ctx->comm_base_addr) { if (version == XGENE_HWMON_V2) ctx->pcc_comm_addr = (void __force *)ioremap( ctx->comm_base_addr, - cppc_ss->length); + pcc_chan->shmem_size); else ctx->pcc_comm_addr = memremap( ctx->comm_base_addr, - cppc_ss->length, + pcc_chan->shmem_size, MEMREMAP_WB); } else { dev_err(&pdev->dev, "Failed to get PCC comm region\n"); @@ -727,11 +720,11 @@ static int xgene_hwmon_probe(struct platform_device *pdev) } /* - * cppc_ss->latency is just a Nominal value. In reality + * pcc_chan->latency is just a Nominal value. In reality * the remote processor could be much slower to reply. * So add an arbitrary amount of wait on top of Nominal. */ - ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency; + ctx->usecs_lat = PCC_NUM_RETRIES * pcc_chan->latency; } ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev, @@ -757,7 +750,7 @@ out: if (acpi_disabled) mbox_free_channel(ctx->mbox_chan); else - pcc_mbox_free_channel(ctx->mbox_chan); + pcc_mbox_free_channel(ctx->pcc_chan); out_mbox_free: kfifo_free(&ctx->async_msg_fifo); @@ -773,7 +766,7 @@ static int xgene_hwmon_remove(struct platform_device *pdev) if (acpi_disabled) mbox_free_channel(ctx->mbox_chan); else - pcc_mbox_free_channel(ctx->mbox_chan); + pcc_mbox_free_channel(ctx->pcc_chan); return 0; } @@ -784,7 +777,7 @@ static const struct of_device_id xgene_hwmon_of_match[] = { }; MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match); -static struct platform_driver xgene_hwmon_driver __refdata = { +static struct platform_driver xgene_hwmon_driver = { .probe = xgene_hwmon_probe, .remove = xgene_hwmon_remove, .driver = { |