summaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/hwmon/Kconfig2386
-rw-r--r--drivers/hwmon/Makefile223
-rw-r--r--drivers/hwmon/abituguru.c1632
-rw-r--r--drivers/hwmon/abituguru3.c1303
-rw-r--r--drivers/hwmon/acpi_power_meter.c1012
-rw-r--r--drivers/hwmon/ad7314.c134
-rw-r--r--drivers/hwmon/ad7414.c234
-rw-r--r--drivers/hwmon/ad7418.c318
-rw-r--r--drivers/hwmon/adc128d818.c535
-rw-r--r--drivers/hwmon/adcxx.c232
-rw-r--r--drivers/hwmon/adm1021.c505
-rw-r--r--drivers/hwmon/adm1025.c572
-rw-r--r--drivers/hwmon/adm1026.c1873
-rw-r--r--drivers/hwmon/adm1029.c402
-rw-r--r--drivers/hwmon/adm1031.c1081
-rw-r--r--drivers/hwmon/adm1177.c287
-rw-r--r--drivers/hwmon/adm9240.c833
-rw-r--r--drivers/hwmon/ads7828.c218
-rw-r--r--drivers/hwmon/ads7871.c207
-rw-r--r--drivers/hwmon/adt7310.c164
-rw-r--r--drivers/hwmon/adt7410.c111
-rw-r--r--drivers/hwmon/adt7411.c720
-rw-r--r--drivers/hwmon/adt7462.c1832
-rw-r--r--drivers/hwmon/adt7470.c1328
-rw-r--r--drivers/hwmon/adt7475.c2022
-rw-r--r--drivers/hwmon/adt7x10.c419
-rw-r--r--drivers/hwmon/adt7x10.h25
-rw-r--r--drivers/hwmon/aht10.c348
-rw-r--r--drivers/hwmon/amc6821.c952
-rw-r--r--drivers/hwmon/applesmc.c1419
-rw-r--r--drivers/hwmon/aquacomputer_d5next.c1059
-rw-r--r--drivers/hwmon/as370-hwmon.c135
-rw-r--r--drivers/hwmon/asb100.c1008
-rw-r--r--drivers/hwmon/asc7621.c1231
-rw-r--r--drivers/hwmon/aspeed-pwm-tacho.c972
-rw-r--r--drivers/hwmon/asus-ec-sensors.c1043
-rw-r--r--drivers/hwmon/asus_atk0110.c1394
-rw-r--r--drivers/hwmon/asus_wmi_sensors.c663
-rw-r--r--drivers/hwmon/atxp1.c294
-rw-r--r--drivers/hwmon/axi-fan-control.c541
-rw-r--r--drivers/hwmon/bt1-pvt.c1178
-rw-r--r--drivers/hwmon/bt1-pvt.h247
-rw-r--r--drivers/hwmon/coretemp.c787
-rw-r--r--drivers/hwmon/corsair-cpro.c583
-rw-r--r--drivers/hwmon/corsair-psu.c860
-rw-r--r--drivers/hwmon/da9052-hwmon.c507
-rw-r--r--drivers/hwmon/da9055-hwmon.c283
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c1468
-rw-r--r--drivers/hwmon/dme1737.c2787
-rw-r--r--drivers/hwmon/drivetemp.c624
-rw-r--r--drivers/hwmon/ds1621.c395
-rw-r--r--drivers/hwmon/ds620.c256
-rw-r--r--drivers/hwmon/emc1403.c466
-rw-r--r--drivers/hwmon/emc2103.c666
-rw-r--r--drivers/hwmon/emc2305.c626
-rw-r--r--drivers/hwmon/emc6w201.c487
-rw-r--r--drivers/hwmon/f71805f.c1649
-rw-r--r--drivers/hwmon/f71882fg.c2705
-rw-r--r--drivers/hwmon/f75375s.c908
-rw-r--r--drivers/hwmon/fam15h_power.c500
-rw-r--r--drivers/hwmon/fschmd.c1370
-rw-r--r--drivers/hwmon/ftsteutates.c838
-rw-r--r--drivers/hwmon/g760a.c217
-rw-r--r--drivers/hwmon/g762.c1090
-rw-r--r--drivers/hwmon/gl518sm.c667
-rw-r--r--drivers/hwmon/gl520sm.c910
-rw-r--r--drivers/hwmon/gpio-fan.c597
-rw-r--r--drivers/hwmon/gsc-hwmon.c423
-rw-r--r--drivers/hwmon/hih6130.c260
-rw-r--r--drivers/hwmon/hwmon-vid.c304
-rw-r--r--drivers/hwmon/hwmon.c1192
-rw-r--r--drivers/hwmon/i5500_temp.c148
-rw-r--r--drivers/hwmon/i5k_amb.c608
-rw-r--r--drivers/hwmon/ibmaem.c1107
-rw-r--r--drivers/hwmon/ibmpex.c601
-rw-r--r--drivers/hwmon/ibmpowernv.c719
-rw-r--r--drivers/hwmon/iio_hwmon.c186
-rw-r--r--drivers/hwmon/ina209.c606
-rw-r--r--drivers/hwmon/ina238.c644
-rw-r--r--drivers/hwmon/ina2xx.c728
-rw-r--r--drivers/hwmon/ina3221.c1026
-rw-r--r--drivers/hwmon/intel-m10-bmc-hwmon.c567
-rw-r--r--drivers/hwmon/it87.c3371
-rw-r--r--drivers/hwmon/jc42.c643
-rw-r--r--drivers/hwmon/k10temp.c535
-rw-r--r--drivers/hwmon/k8temp.c225
-rw-r--r--drivers/hwmon/lan966x-hwmon.c418
-rw-r--r--drivers/hwmon/lineage-pem.c522
-rw-r--r--drivers/hwmon/lm63.c1177
-rw-r--r--drivers/hwmon/lm70.c222
-rw-r--r--drivers/hwmon/lm73.c290
-rw-r--r--drivers/hwmon/lm75.c958
-rw-r--r--drivers/hwmon/lm75.h41
-rw-r--r--drivers/hwmon/lm77.c361
-rw-r--r--drivers/hwmon/lm78.c1033
-rw-r--r--drivers/hwmon/lm80.c647
-rw-r--r--drivers/hwmon/lm83.c467
-rw-r--r--drivers/hwmon/lm85.c1713
-rw-r--r--drivers/hwmon/lm87.c1007
-rw-r--r--drivers/hwmon/lm90.c3001
-rw-r--r--drivers/hwmon/lm92.c343
-rw-r--r--drivers/hwmon/lm93.c2649
-rw-r--r--drivers/hwmon/lm95234.c733
-rw-r--r--drivers/hwmon/lm95241.c481
-rw-r--r--drivers/hwmon/lm95245.c610
-rw-r--r--drivers/hwmon/lochnagar-hwmon.c412
-rw-r--r--drivers/hwmon/ltc2945.c490
-rw-r--r--drivers/hwmon/ltc2947-core.c1171
-rw-r--r--drivers/hwmon/ltc2947-i2c.c48
-rw-r--r--drivers/hwmon/ltc2947-spi.c50
-rw-r--r--drivers/hwmon/ltc2947.h12
-rw-r--r--drivers/hwmon/ltc2990.c279
-rw-r--r--drivers/hwmon/ltc2992.c939
-rw-r--r--drivers/hwmon/ltc4151.c216
-rw-r--r--drivers/hwmon/ltc4215.c266
-rw-r--r--drivers/hwmon/ltc4222.c221
-rw-r--r--drivers/hwmon/ltc4245.c490
-rw-r--r--drivers/hwmon/ltc4260.c184
-rw-r--r--drivers/hwmon/ltc4261.c244
-rw-r--r--drivers/hwmon/ltq-cputemp.c133
-rw-r--r--drivers/hwmon/max1111.c292
-rw-r--r--drivers/hwmon/max127.c352
-rw-r--r--drivers/hwmon/max16065.c611
-rw-r--r--drivers/hwmon/max1619.c318
-rw-r--r--drivers/hwmon/max1668.c448
-rw-r--r--drivers/hwmon/max197.c344
-rw-r--r--drivers/hwmon/max31722.c155
-rw-r--r--drivers/hwmon/max31730.c440
-rw-r--r--drivers/hwmon/max31760.c596
-rw-r--r--drivers/hwmon/max31790.c558
-rw-r--r--drivers/hwmon/max6620.c514
-rw-r--r--drivers/hwmon/max6621.c565
-rw-r--r--drivers/hwmon/max6639.c637
-rw-r--r--drivers/hwmon/max6642.c314
-rw-r--r--drivers/hwmon/max6650.c830
-rw-r--r--drivers/hwmon/max6697.c797
-rw-r--r--drivers/hwmon/mc13783-adc.c331
-rw-r--r--drivers/hwmon/mcp3021.c209
-rw-r--r--drivers/hwmon/menf21bmc_hwmon.c231
-rw-r--r--drivers/hwmon/mlxreg-fan.c620
-rw-r--r--drivers/hwmon/mr75203.c927
-rw-r--r--drivers/hwmon/nct6683.c1505
-rw-r--r--drivers/hwmon/nct6775-core.c4210
-rw-r--r--drivers/hwmon/nct6775-i2c.c195
-rw-r--r--drivers/hwmon/nct6775-platform.c1325
-rw-r--r--drivers/hwmon/nct6775.h254
-rw-r--r--drivers/hwmon/nct7802.c1235
-rw-r--r--drivers/hwmon/nct7904.c1184
-rw-r--r--drivers/hwmon/npcm750-pwm-fan.c1035
-rw-r--r--drivers/hwmon/nsa320-hwmon.c207
-rw-r--r--drivers/hwmon/ntc_thermistor.c708
-rw-r--r--drivers/hwmon/nzxt-kraken2.c234
-rw-r--r--drivers/hwmon/nzxt-smart2.c832
-rw-r--r--drivers/hwmon/occ/Kconfig37
-rw-r--r--drivers/hwmon/occ/Makefile8
-rw-r--r--drivers/hwmon/occ/common.c1251
-rw-r--r--drivers/hwmon/occ/common.h136
-rw-r--r--drivers/hwmon/occ/p8_i2c.c257
-rw-r--r--drivers/hwmon/occ/p9_sbe.c205
-rw-r--r--drivers/hwmon/occ/sysfs.c257
-rw-r--r--drivers/hwmon/pc87360.c1803
-rw-r--r--drivers/hwmon/pc87427.c1331
-rw-r--r--drivers/hwmon/pcf8591.c320
-rw-r--r--drivers/hwmon/peci/Kconfig31
-rw-r--r--drivers/hwmon/peci/Makefile7
-rw-r--r--drivers/hwmon/peci/common.h58
-rw-r--r--drivers/hwmon/peci/cputemp.c592
-rw-r--r--drivers/hwmon/peci/dimmtemp.c615
-rw-r--r--drivers/hwmon/pmbus/Kconfig464
-rw-r--r--drivers/hwmon/pmbus/Makefile50
-rw-r--r--drivers/hwmon/pmbus/adm1266.c514
-rw-r--r--drivers/hwmon/pmbus/adm1275.c844
-rw-r--r--drivers/hwmon/pmbus/bel-pfe.c132
-rw-r--r--drivers/hwmon/pmbus/bpa-rs600.c208
-rw-r--r--drivers/hwmon/pmbus/delta-ahe50dc-fan.c130
-rw-r--r--drivers/hwmon/pmbus/dps920ab.c206
-rw-r--r--drivers/hwmon/pmbus/fsp-3y.c294
-rw-r--r--drivers/hwmon/pmbus/ibm-cffps.c653
-rw-r--r--drivers/hwmon/pmbus/inspur-ipsps.c227
-rw-r--r--drivers/hwmon/pmbus/ir35221.c148
-rw-r--r--drivers/hwmon/pmbus/ir36021.c80
-rw-r--r--drivers/hwmon/pmbus/ir38064.c90
-rw-r--r--drivers/hwmon/pmbus/irps5401.c66
-rw-r--r--drivers/hwmon/pmbus/isl68137.c335
-rw-r--r--drivers/hwmon/pmbus/lm25066.c580
-rw-r--r--drivers/hwmon/pmbus/lt7182s.c195
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c934
-rw-r--r--drivers/hwmon/pmbus/ltc3815.c211
-rw-r--r--drivers/hwmon/pmbus/max15301.c190
-rw-r--r--drivers/hwmon/pmbus/max16064.c114
-rw-r--r--drivers/hwmon/pmbus/max16601.c365
-rw-r--r--drivers/hwmon/pmbus/max20730.c788
-rw-r--r--drivers/hwmon/pmbus/max20751.c54
-rw-r--r--drivers/hwmon/pmbus/max31785.c406
-rw-r--r--drivers/hwmon/pmbus/max34440.c532
-rw-r--r--drivers/hwmon/pmbus/max8688.c194
-rw-r--r--drivers/hwmon/pmbus/mp2888.c407
-rw-r--r--drivers/hwmon/pmbus/mp2975.c769
-rw-r--r--drivers/hwmon/pmbus/mp5023.c67
-rw-r--r--drivers/hwmon/pmbus/pim4328.c233
-rw-r--r--drivers/hwmon/pmbus/pli1209bc.c146
-rw-r--r--drivers/hwmon/pmbus/pm6764tr.c76
-rw-r--r--drivers/hwmon/pmbus/pmbus.c264
-rw-r--r--drivers/hwmon/pmbus/pmbus.h511
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c3431
-rw-r--r--drivers/hwmon/pmbus/pxe1610.c151
-rw-r--r--drivers/hwmon/pmbus/q54sj108a2.c423
-rw-r--r--drivers/hwmon/pmbus/stpddc60.c249
-rw-r--r--drivers/hwmon/pmbus/tps40422.c54
-rw-r--r--drivers/hwmon/pmbus/tps53679.c311
-rw-r--r--drivers/hwmon/pmbus/tps546d24.c71
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c707
-rw-r--r--drivers/hwmon/pmbus/ucd9200.c212
-rw-r--r--drivers/hwmon/pmbus/xdpe12284.c197
-rw-r--r--drivers/hwmon/pmbus/xdpe152c4.c75
-rw-r--r--drivers/hwmon/pmbus/zl6100.c473
-rw-r--r--drivers/hwmon/powr1220.c338
-rw-r--r--drivers/hwmon/pwm-fan.c682
-rw-r--r--drivers/hwmon/raspberrypi-hwmon.c148
-rw-r--r--drivers/hwmon/s3c-hwmon.c379
-rw-r--r--drivers/hwmon/sbrmi.c359
-rw-r--r--drivers/hwmon/sbtsi_temp.c250
-rw-r--r--drivers/hwmon/sch5627.c554
-rw-r--r--drivers/hwmon/sch5636.c526
-rw-r--r--drivers/hwmon/sch56xx-common.c545
-rw-r--r--drivers/hwmon/sch56xx-common.h19
-rw-r--r--drivers/hwmon/scmi-hwmon.c374
-rw-r--r--drivers/hwmon/scpi-hwmon.c306
-rw-r--r--drivers/hwmon/sht15.c1062
-rw-r--r--drivers/hwmon/sht21.c296
-rw-r--r--drivers/hwmon/sht3x.c755
-rw-r--r--drivers/hwmon/sht4x.c303
-rw-r--r--drivers/hwmon/shtc1.c290
-rw-r--r--drivers/hwmon/sis5595.c908
-rw-r--r--drivers/hwmon/sl28cpld-hwmon.c132
-rw-r--r--drivers/hwmon/smm665.c706
-rw-r--r--drivers/hwmon/smsc47b397.c370
-rw-r--r--drivers/hwmon/smsc47m1.c949
-rw-r--r--drivers/hwmon/smsc47m192.c641
-rw-r--r--drivers/hwmon/sparx5-temp.c151
-rw-r--r--drivers/hwmon/stts751.c835
-rw-r--r--drivers/hwmon/sy7636a-hwmon.c106
-rw-r--r--drivers/hwmon/tc654.c572
-rw-r--r--drivers/hwmon/tc74.c172
-rw-r--r--drivers/hwmon/thmc50.c432
-rw-r--r--drivers/hwmon/tmp102.c312
-rw-r--r--drivers/hwmon/tmp103.c225
-rw-r--r--drivers/hwmon/tmp108.c443
-rw-r--r--drivers/hwmon/tmp401.c779
-rw-r--r--drivers/hwmon/tmp421.c500
-rw-r--r--drivers/hwmon/tmp464.c712
-rw-r--r--drivers/hwmon/tmp513.c771
-rw-r--r--drivers/hwmon/tps23861.c595
-rw-r--r--drivers/hwmon/ultra45_env.c324
-rw-r--r--drivers/hwmon/vexpress-hwmon.c245
-rw-r--r--drivers/hwmon/via-cputemp.c329
-rw-r--r--drivers/hwmon/via686a.c945
-rw-r--r--drivers/hwmon/vt1211.c1367
-rw-r--r--drivers/hwmon/vt8231.c1060
-rw-r--r--drivers/hwmon/w83627ehf.c2160
-rw-r--r--drivers/hwmon/w83627hf.c2009
-rw-r--r--drivers/hwmon/w83773g.c306
-rw-r--r--drivers/hwmon/w83781d.c2095
-rw-r--r--drivers/hwmon/w83791d.c1657
-rw-r--r--drivers/hwmon/w83792d.c1644
-rw-r--r--drivers/hwmon/w83793.c2144
-rw-r--r--drivers/hwmon/w83795.c2271
-rw-r--r--drivers/hwmon/w83l785ts.c283
-rw-r--r--drivers/hwmon/w83l786ng.c765
-rw-r--r--drivers/hwmon/wm831x-hwmon.c153
-rw-r--r--drivers/hwmon/wm8350-hwmon.c92
-rw-r--r--drivers/hwmon/xgene-hwmon.c793
272 files changed, 176950 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
new file mode 100644
index 000000000..a5143d01b
--- /dev/null
+++ b/drivers/hwmon/Kconfig
@@ -0,0 +1,2386 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Hardware monitoring chip drivers configuration
+#
+
+menuconfig HWMON
+ tristate "Hardware Monitoring support"
+ depends on HAS_IOMEM
+ default y
+ help
+ Hardware monitoring devices let you monitor the hardware health
+ of a system. Most modern motherboards include such a device. It
+ can include temperature sensors, voltage sensors, fan speed
+ sensors and various additional features such as the ability to
+ control the speed of the fans. If you want this support you
+ should say Y here and also to the specific driver(s) for your
+ sensors chip(s) below.
+
+ To find out which specific driver(s) you need, use the
+ sensors-detect script from the lm_sensors package. Read
+ <file:Documentation/hwmon/userspace-tools.rst> for details.
+
+ This support can also be built as a module. If so, the module
+ will be called hwmon.
+
+if HWMON
+
+config HWMON_VID
+ tristate
+
+config HWMON_DEBUG_CHIP
+ bool "Hardware Monitoring Chip debugging messages"
+ help
+ Say Y here if you want the I2C chip drivers to produce a bunch of
+ debug messages to the system log. Select this if you are having
+ a problem with I2C support and want to see more of what is going
+ on.
+
+comment "Native drivers"
+
+config SENSORS_ABITUGURU
+ tristate "Abit uGuru (rev 1 & 2)"
+ depends on X86 && DMI
+ help
+ If you say yes here you get support for the sensor part of the first
+ and second revision of the Abit uGuru chip. The voltage and frequency
+ control parts of the Abit uGuru are not supported. The Abit uGuru
+ chip can be found on Abit uGuru featuring motherboards (most modern
+ Abit motherboards from before end 2005). For more info and a list
+ of which motherboards have which revision see
+ Documentation/hwmon/abituguru.rst
+
+ This driver can also be built as a module. If so, the module
+ will be called abituguru.
+
+config SENSORS_ABITUGURU3
+ tristate "Abit uGuru (rev 3)"
+ depends on X86 && DMI
+ help
+ If you say yes here you get support for the sensor part of the
+ third revision of the Abit uGuru chip. Only reading the sensors
+ and their settings is supported. The third revision of the Abit
+ uGuru chip can be found on recent Abit motherboards (since end
+ 2005). For more info and a list of which motherboards have which
+ revision see Documentation/hwmon/abituguru3.rst
+
+ This driver can also be built as a module. If so, the module
+ will be called abituguru3.
+
+config SENSORS_AD7314
+ tristate "Analog Devices AD7314 and compatibles"
+ depends on SPI
+ help
+ If you say yes here you get support for the Analog Devices
+ AD7314, ADT7301 and ADT7302 temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called ad7314.
+
+config SENSORS_AD7414
+ tristate "Analog Devices AD7414"
+ depends on I2C
+ help
+ If you say yes here you get support for the Analog Devices
+ AD7414 temperature monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called ad7414.
+
+config SENSORS_AD7418
+ tristate "Analog Devices AD7416, AD7417 and AD7418"
+ depends on I2C
+ help
+ If you say yes here you get support for the Analog Devices
+ AD7416, AD7417 and AD7418 temperature monitoring chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called 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,
+ Genesys Logic GL523SM, National Semiconductor LM84 and TI THMC10.
+
+ This driver can also be built as a module. If so, the module
+ will be called adm1021.
+
+config SENSORS_ADM1025
+ tristate "Analog Devices ADM1025 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for Analog Devices ADM1025
+ and Philips NE1619 sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adm1025.
+
+config SENSORS_ADM1026
+ tristate "Analog Devices ADM1026 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for Analog Devices ADM1026
+ sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called adm1026.
+
+config SENSORS_ADM1029
+ tristate "Analog Devices ADM1029"
+ depends on I2C
+ help
+ If you say yes here you get support for Analog Devices ADM1029
+ sensor chip.
+ Very rare chip, please let us know you use it.
+
+ This driver can also be built as a module. If so, the module
+ will be called adm1029.
+
+config SENSORS_ADM1031
+ tristate "Analog Devices ADM1031 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for Analog Devices ADM1031
+ and ADM1030 sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adm1031.
+
+config SENSORS_ADM1177
+ tristate "Analog Devices ADM1177 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for Analog Devices ADM1177
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adm1177.
+
+config SENSORS_ADM9240
+ tristate "Analog Devices ADM9240 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for Analog Devices ADM9240,
+ Dallas DS1780, National Semiconductor LM81 sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called 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.
+
+ If built as a module, the module will be called adt7x10.
+
+config SENSORS_ADT7310
+ tristate "Analog Devices ADT7310/ADT7320"
+ depends on SPI_MASTER
+ select SENSORS_ADT7X10
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7310 and ADT7320 temperature monitoring chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adt7310.
+
+config SENSORS_ADT7410
+ tristate "Analog Devices ADT7410/ADT7420"
+ depends on I2C
+ select SENSORS_ADT7X10
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7410 and ADT7420 temperature monitoring chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adt7410.
+
+config SENSORS_ADT7411
+ tristate "Analog Devices ADT7411"
+ depends on I2C
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7411 voltage and temperature monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called adt7411.
+
+config SENSORS_ADT7462
+ tristate "Analog Devices ADT7462"
+ depends on I2C
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7462 temperature monitoring chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adt7462.
+
+config SENSORS_ADT7470
+ tristate "Analog Devices ADT7470"
+ depends on I2C
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7470 temperature monitoring chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adt7470.
+
+config SENSORS_ADT7475
+ tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7473, ADT7475, ADT7476 and ADT7490 hardware monitoring
+ chips.
+
+ 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
+ If you say yes here you get support for the PVT sensors of
+ the Synaptics AS370 SoC
+
+ This driver can also be built as a module. If so, the module
+ will be called as370-hwmon.
+
+
+config SENSORS_ASC7621
+ tristate "Andigilog aSC7621"
+ depends on I2C
+ help
+ If you say yes here you get support for the aSC7621
+ family of SMBus sensors chip found on most Intel X38, X48, X58,
+ 945, 965 and 975 desktop boards. Currently supported chips:
+ aSC7621
+ aSC7621a
+
+ 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
+ help
+ If you say yes here you get support for the temperature
+ sensor(s) inside your CPU. Supported is whole AMD K8
+ microarchitecture. Please note that you will need at least
+ lm-sensors 2.10.1 for proper userspace support.
+
+ This driver can also be built as a module. If so, the module
+ will be called k8temp.
+
+config SENSORS_K10TEMP
+ tristate "AMD Family 10h+ temperature sensor"
+ depends on X86 && PCI && AMD_NB
+ help
+ If you say yes here you get support for the temperature
+ sensor(s) inside your CPU. Supported are later revisions of
+ the AMD Family 10h and all revisions of the AMD Family 11h,
+ 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity/Kaveri/Carrizo)
+ and 16h (Kabini/Mullins) microarchitectures.
+
+ This driver can also be built as a module. If so, the module
+ will be called k10temp.
+
+config SENSORS_FAM15H_POWER
+ tristate "AMD Family 15h processor power"
+ depends on X86 && PCI && CPU_SUP_AMD
+ help
+ If you say yes here you get support for processor power
+ information of your AMD family 15h CPU.
+
+ This driver can also be built as a module. If so, the module
+ will be called fam15h_power.
+
+config SENSORS_APPLESMC
+ tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
+ depends on INPUT && X86
+ select NEW_LEDS
+ select LEDS_CLASS
+ help
+ This driver provides support for the Apple System Management
+ Controller, which provides an accelerometer (Apple Sudden Motion
+ Sensor), light sensors, temperature sensors, keyboard backlight
+ control and fan control.
+
+ Only Intel-based Apple's computers are supported (MacBook Pro,
+ MacBook, MacMini).
+
+ Data from the different sensors, keyboard backlight control and fan
+ control are accessible via sysfs.
+
+ This driver also provides an absolute input class device, allowing
+ the laptop to act as a pinball machine-esque joystick.
+
+ Say Y here if you have an applicable laptop and want to experience
+ the awesome power of applesmc.
+
+config SENSORS_ARM_SCMI
+ tristate "ARM SCMI Sensors"
+ depends on ARM_SCMI_PROTOCOL
+ depends on THERMAL || !THERMAL_OF
+ help
+ This driver provides support for temperature, voltage, current
+ and power sensors available on SCMI based platforms. The actual
+ number and type of sensors exported depend on the platform.
+
+ This driver can also be built as a module. If so, the module
+ will be called scmi-hwmon.
+
+config SENSORS_ARM_SCPI
+ tristate "ARM SCPI Sensors"
+ depends on ARM_SCPI_PROTOCOL
+ depends on THERMAL || !THERMAL_OF
+ help
+ This driver provides support for temperature, voltage, current
+ and power sensors available on ARM Ltd's SCP based platforms. The
+ actual number and type of sensors exported depend on the platform.
+
+config SENSORS_ASB100
+ tristate "Asus ASB100 Bach"
+ depends on (X86 || COMPILE_TEST) && I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for the ASB100 Bach sensor
+ chip found on some Asus mainboards.
+
+ This driver can also be built as a module. If so, the module
+ will be called 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
+ This driver provides support for ASPEED AST2400/AST2500 PWM
+ and Fan Tacho controllers.
+
+ This driver can also be built as a module. If so, the module
+ will be called aspeed_pwm_tacho.
+
+config SENSORS_ATXP1
+ tristate "Attansic ATXP1 VID controller"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for the Attansic ATXP1 VID
+ controller.
+
+ If your board have such a chip, you are able to control your CPU
+ core and other voltages.
+
+ 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
+ help
+ If you say yes you get support for the temperature sensor on
+ hard disk drives.
+
+ This driver can also be built as a module. If so, the module
+ will be called drivetemp.
+
+config SENSORS_DS620
+ tristate "Dallas Semiconductor DS620"
+ depends on I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS620 sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called ds620.
+
+config SENSORS_DS1621
+ tristate "Dallas Semiconductor DS1621 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor/Maxim
+ Integrated DS1621 sensor chips and compatible models including:
+
+ - Dallas Semiconductor DS1625
+ - Maxim Integrated DS1631
+ - Maxim Integrated DS1721
+ - Maxim Integrated DS1731
+
+ This driver can also be built as a module. If so, the module
+ will be called 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
+ Mode provided by Dell BIOS.
+
+ 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
+ help
+ Say y here to support the ADC found on Dialog Semiconductor
+ DA9052-BC and DA9053-AA/Bx PMICs.
+
+ This driver can also be built as module. If so, the module
+ will be called da9052-hwmon.
+
+config SENSORS_DA9055
+ tristate "Dialog Semiconductor DA9055 ADC"
+ depends on MFD_DA9055
+ help
+ If you say yes here you get support for ADC on the Dialog
+ Semiconductor DA9055 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9055-hwmon.
+
+config SENSORS_I5K_AMB
+ tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
+ depends on PCI
+ help
+ If you say yes here you get support for FB-DIMM AMB temperature
+ monitoring chips on systems with the Intel 5000 series chipset.
+
+ 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
+ help
+ If you say yes here you get support for hardware monitoring
+ features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG
+ Super-I/O chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called f71805f.
+
+config SENSORS_F71882FG
+ tristate "Fintek F71882FG and compatibles"
+ depends on !PPC
+ help
+ If you say yes here you get support for hardware monitoring
+ features of many Fintek Super-I/O (LPC) chips. The currently
+ supported chips are:
+ F71808E/A
+ F71858FG
+ F71862FG
+ F71863FG
+ F71869F/E/A
+ F71882FG
+ F71883FG
+ F71889FG/ED/A
+ F8000
+ F81801U
+ F81865F
+
+ This driver can also be built as a module. If so, the module
+ will be called f71882fg.
+
+config SENSORS_F75375S
+ tristate "Fintek F75375S/SP, F75373 and F75387"
+ depends on I2C
+ help
+ If you say yes here you get support for hardware monitoring
+ features of the Fintek F75375S/SP, F75373 and F75387
+
+ 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
+ help
+ Support for the A/D converter on MC13783 and MC13892 PMIC.
+
+config SENSORS_FSCHMD
+ tristate "Fujitsu Siemens Computers sensor chips"
+ 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,
+ Heimdall, Heracles, Hades and Syleus including support for the
+ integrated watchdog.
+
+ This is a merged driver for FSC sensor chips replacing the fscpos,
+ fscscy and fscher drivers and adding support for several other FSC
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called fschmd.
+
+config SENSORS_FTSTEUTATES
+ tristate "Fujitsu Technology Solutions sensor chip Teutates"
+ depends on I2C && WATCHDOG
+ select WATCHDOG_CORE
+ help
+ If you say yes here you get support for the Fujitsu Technology
+ Solutions (FTS) sensor chip "Teutates" including support for
+ the integrated watchdog.
+
+ This driver can also be built as a module. If so, the module
+ will be called ftsteutates.
+
+config SENSORS_GL518SM
+ tristate "Genesys Logic GL518SM"
+ depends on I2C
+ help
+ If you say yes here you get support for Genesys Logic GL518SM
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called gl518sm.
+
+config SENSORS_GL520SM
+ tristate "Genesys Logic GL520SM"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for Genesys Logic GL520SM
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called gl520sm.
+
+config SENSORS_G760A
+ tristate "GMT G760A"
+ depends on I2C
+ help
+ If you say yes here you get support for Global Mixed-mode
+ Technology Inc G760A fan speed PWM controller chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called g760a.
+
+config SENSORS_G762
+ tristate "GMT G762 and G763"
+ depends on I2C
+ help
+ If you say yes here you get support for Global Mixed-mode
+ Technology Inc G762 and G763 fan speed PWM controller chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called g762.
+
+config SENSORS_GPIO_FAN
+ tristate "GPIO fan"
+ depends on OF_GPIO
+ depends on GPIOLIB || COMPILE_TEST
+ depends on THERMAL || THERMAL=n
+ help
+ If you say yes here you get support for fans connected to GPIO lines.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-fan.
+
+config SENSORS_HIH6130
+ tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor"
+ depends on I2C
+ help
+ If you say yes here you get support for Honeywell Humidicon
+ HIH-6130 and HIH-6131 Humidicon humidity sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called hih6130.
+
+config SENSORS_IBMAEM
+ tristate "IBM Active Energy Manager temperature/power sensors and control"
+ select IPMI_SI
+ depends on IPMI_HANDLER
+ help
+ If you say yes here you get support for the temperature and
+ power sensors and capping hardware in various IBM System X
+ servers that support Active Energy Manager. This includes
+ the x3350, x3550, x3650, x3655, x3755, x3850 M2, x3950 M2,
+ and certain HC10/HS2x/LS2x/QS2x blades.
+
+ This driver can also be built as a module. If so, the module
+ will be called ibmaem.
+
+config SENSORS_IBMPEX
+ tristate "IBM PowerExecutive temperature/power sensors"
+ select IPMI_SI
+ depends on IPMI_HANDLER
+ help
+ If you say yes here you get support for the temperature and
+ power sensors in various IBM System X servers that support
+ PowerExecutive. So far this includes the x3350, x3550, x3650,
+ x3655, and x3755; the x3800, x3850, and x3950 models that have
+ PCI Express; and some of the HS2x, LS2x, and QS2x blades.
+
+ This driver can also be built as a module. If so, the module
+ will be called ibmpex.
+
+config SENSORS_IBMPOWERNV
+ tristate "IBM POWERNV platform sensors"
+ depends on PPC_POWERNV
+ default y
+ help
+ If you say yes here you get support for the temperature/fan/power
+ sensors on your PowerNV platform.
+
+ This driver can also be built as a module. If so, the module
+ will be called ibmpowernv.
+
+config SENSORS_IIO_HWMON
+ tristate "Hwmon driver that uses channels specified via iio maps"
+ depends on IIO
+ help
+ This is a platform driver that in combination with a suitable
+ map allows IIO devices to provide basic hwmon functionality
+ for those channels specified in the map. This map can be provided
+ either via platform data or the device tree bindings.
+
+config SENSORS_I5500
+ tristate "Intel 5500/5520/X58 temperature sensor"
+ depends on X86 && PCI
+ help
+ If you say yes here you get support for the temperature
+ sensor inside the Intel 5500, 5520 and X58 chipsets.
+
+ This driver can also be built as a module. If so, the module
+ will be called i5500_temp.
+
+config SENSORS_CORETEMP
+ tristate "Intel Core/Core2/Atom temperature sensor"
+ depends on X86
+ help
+ If you say yes here you get support for the temperature
+ sensor inside your CPU. Most of the family 6 CPUs
+ are supported. Check Documentation/hwmon/coretemp.rst for details.
+
+config SENSORS_IT87
+ tristate "ITE IT87xx and compatibles"
+ depends on !PPC
+ select HWMON_VID
+ help
+ If you say yes here you get support for ITE IT8705F, IT8712F, IT8716F,
+ IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F, IT8758E,
+ IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E,
+ IT8603E, IT8620E, IT8623E, and IT8628E sensor chips, and the SiS950
+ clone.
+
+ This driver can also be built as a module. If so, the module
+ will be called it87.
+
+config SENSORS_JC42
+ tristate "JEDEC JC42.4 compliant memory module temperature sensors"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here, you get support for JEDEC JC42.4 compliant
+ temperature sensors, which are used on many DDR3 memory modules for
+ mobile devices and servers. Support will include, but not be limited
+ to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805,
+ MCP9808, MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98,
+ STTS424(E), STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001.
+
+ This driver can also be built as a module. If so, the module
+ will be called jc42.
+
+config SENSORS_POWR1220
+ tristate "Lattice POWR1220 Power Monitoring"
+ depends on I2C
+ help
+ If you say yes here you get access to the hardware monitoring
+ functions of the Lattice POWR1220 isp Power Supply Monitoring,
+ Sequencing and Margining Controller.
+
+ 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
+ help
+ If you say yes here you get support for the Lineage Compact Power Line
+ series of DC/DC and AC/DC converters such as CP1800, CP2000AC,
+ CP2000DC, CP2725, and others.
+
+ This driver can also be built as a module. If so, the module
+ will be called lineage-pem.
+
+config SENSORS_LOCHNAGAR
+ tristate "Lochnagar Hardware Monitor"
+ depends on MFD_LOCHNAGAR
+ help
+ If you say yes here you get support for Lochnagar 2 temperature,
+ voltage and current sensors abilities.
+
+ This driver can also be built as a module. If so, the module
+ will be called lochnagar-hwmon.
+
+config SENSORS_LTC2945
+ tristate "Linear Technology LTC2945"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Linear Technology LTC2945
+ I2C System Monitor.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2945.
+
+config SENSORS_LTC2947
+ tristate
+
+config SENSORS_LTC2947_I2C
+ tristate "Analog Devices LTC2947 High Precision Power and Energy Monitor over I2C"
+ depends on I2C
+ select REGMAP_I2C
+ select SENSORS_LTC2947
+ help
+ If you say yes here you get support for Linear Technology LTC2947
+ I2C High Precision Power and Energy Monitor
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2947-i2c.
+
+config SENSORS_LTC2947_SPI
+ tristate "Analog Devices LTC2947 High Precision Power and Energy Monitor over SPI"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ select SENSORS_LTC2947
+ help
+ If you say yes here you get support for Linear Technology LTC2947
+ SPI High Precision Power and Energy Monitor
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2947-spi.
+
+config SENSORS_LTC2990
+ tristate "Linear Technology LTC2990"
+ depends on I2C
+ help
+ If you say yes here you get support for Linear Technology LTC2990
+ I2C System Monitor. The LTC2990 supports a combination of voltage,
+ current and temperature monitoring.
+
+ 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
+ help
+ If you say yes here you get support for Linear Technology LTC4151
+ High Voltage I2C Current and Voltage Monitor interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc4151.
+
+config SENSORS_LTC4215
+ tristate "Linear Technology LTC4215"
+ depends on I2C
+ help
+ If you say yes here you get support for Linear Technology LTC4215
+ Hot Swap Controller I2C interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc4215.
+
+config SENSORS_LTC4222
+ tristate "Linear Technology LTC4222"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Linear Technology LTC4222
+ Dual Hot Swap Controller I2C interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc4222.
+
+config SENSORS_LTC4245
+ tristate "Linear Technology LTC4245"
+ depends on I2C
+ help
+ If you say yes here you get support for Linear Technology LTC4245
+ Multiple Supply Hot Swap Controller I2C interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc4245.
+
+config SENSORS_LTC4260
+ tristate "Linear Technology LTC4260"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Linear Technology LTC4260
+ Positive Voltage Hot Swap Controller I2C interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc4260.
+
+config SENSORS_LTC4261
+ tristate "Linear Technology LTC4261"
+ depends on I2C
+ help
+ If you say yes here you get support for Linear Technology LTC4261
+ Negative Voltage Hot Swap Controller I2C interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc4261.
+
+config SENSORS_LTQ_CPUTEMP
+ bool "Lantiq cpu temperature sensor driver"
+ depends on SOC_XWAY
+ help
+ If you say yes here you get support for the temperature
+ sensor inside your CPU.
+
+config SENSORS_MAX1111
+ tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
+ depends on SPI_MASTER
+ help
+ Say y here to support Maxim's MAX1110, MAX1111, MAX1112, and MAX1113
+ ADC chips.
+
+ 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
+ help
+ If you say yes here you get support for hardware monitoring
+ capabilities of the following Maxim System Manager chips.
+ MAX16065
+ MAX16066
+ MAX16067
+ MAX16068
+ MAX16070
+ MAX16071
+
+ This driver can also be built as a module. If so, the module
+ will be called max16065.
+
+config SENSORS_MAX1619
+ tristate "Maxim MAX1619 sensor chip"
+ depends on I2C
+ help
+ If you say yes here you get support for MAX1619 sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called max1619.
+
+config SENSORS_MAX1668
+ tristate "Maxim MAX1668 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for MAX1668, MAX1989 and
+ MAX1805 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called max1668.
+
+config SENSORS_MAX197
+ tristate "Maxim MAX197 and compatibles"
+ help
+ Support for the Maxim MAX197 A/D converter.
+ Support will include, but not be limited to, MAX197, and MAX199.
+
+ This driver can also be built as a module. If so, the module
+ will be called max197.
+
+config SENSORS_MAX31722
+ tristate "MAX31722 temperature sensor"
+ depends on SPI
+ help
+ Support for the Maxim Integrated MAX31722/MAX31723 digital
+ thermometers/thermostats operating over an SPI interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called max31722.
+
+config SENSORS_MAX31730
+ tristate "MAX31730 temperature sensor"
+ depends on I2C
+ help
+ Support for the Maxim Integrated MAX31730 3-Channel Remote
+ Temperature Sensor.
+
+ 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
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for MAX6621 sensor chip.
+ MAX6621 is a PECI-to-I2C translator provides an efficient,
+ low-cost solution for PECI-to-SMBus/I2C protocol conversion.
+ It allows reading the temperature from the PECI-compliant
+ host directly from up to four PECI-enabled CPUs.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6621.
+
+config SENSORS_MAX6639
+ tristate "Maxim MAX6639 sensor chip"
+ depends on I2C
+ help
+ If you say yes here you get support for the MAX6639
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called 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
+ with Overtemperature Alarm from Maxim.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6642.
+
+config SENSORS_MAX6650
+ tristate "Maxim MAX6650 sensor chip"
+ depends on I2C
+ depends on THERMAL || THERMAL=n
+ help
+ If you say yes here you get support for the MAX6650 / MAX6651
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6650.
+
+config SENSORS_MAX6697
+ tristate "Maxim MAX6697 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for MAX6581, MAX6602, MAX6622,
+ MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699
+ temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called max6697.
+
+config SENSORS_MAX31790
+ tristate "Maxim MAX31790 sensor chip"
+ depends on I2C
+ help
+ If you say yes here you get support for 6-Channel PWM-Output
+ Fan RPM Controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called max31790.
+
+config SENSORS_MCP3021
+ tristate "Microchip MCP3021 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for MCP3021 and MCP3221.
+ The MCP3021 is a A/D converter (ADC) with 10-bit and the MCP3221
+ with 12-bit resolution.
+
+ This driver can also be built as a module. If so, the module
+ will be called mcp3021.
+
+config SENSORS_MLXREG_FAN
+ tristate "Mellanox FAN driver"
+ depends on MELLANOX_PLATFORM
+ imply THERMAL
+ select REGMAP
+ help
+ This option enables support for the FAN control on the Mellanox
+ Ethernet and InfiniBand switches. The driver can be activated by the
+ platform device add call. Say Y to enable these. To compile this
+ driver as a module, choose 'M' here: the module will be called
+ mlxreg-fan.
+
+config SENSORS_TC654
+ tristate "Microchip TC654/TC655 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for TC654 and TC655.
+ The TC654 and TC655 are PWM mode fan speed controllers with
+ FanSense technology for use with brushless DC fans.
+
+ 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
+ help
+ Say Y here to include support for the MEN 14F021P00 BMC
+ hardware monitoring.
+
+ 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
+ help
+ If you say yes here you get support for the National Semiconductor
+ ADC<bb><c>S<sss> chip family, where
+ * bb is the resolution in number of bits (8, 10, 12)
+ * c is the number of channels (1, 2, 4, 8)
+ * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500
+ kSPS and 101 for 1 MSPS)
+
+ Examples : ADC081S101, ADC124S501, ...
+
+ This driver can also be built as a module. If so, the module
+ will be called adcxx.
+
+config SENSORS_LM63
+ tristate "National Semiconductor LM63 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for the National
+ Semiconductor LM63, LM64, and LM96163 remote diode digital temperature
+ sensors with integrated fan control. Such chips are found
+ on the Tyan S4882 (Thunder K8QS Pro) motherboard, among
+ others.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm63.
+
+config SENSORS_LM70
+ tristate "National Semiconductor LM70 and compatibles"
+ 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, TMP122/TMP124,
+ TMP125 digital temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm70.
+
+config SENSORS_LM73
+ tristate "National Semiconductor LM73"
+ depends on I2C
+ help
+ If you say yes here you get support for National Semiconductor LM73
+ sensor chips.
+ This driver can also be built as a module. If so, the module
+ will be called lm73.
+
+config SENSORS_LM75
+ tristate "National Semiconductor LM75 and compatibles"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for one common type of
+ 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
+ - Microchip MCP980x
+ - National Semiconductor LM75, LM75A
+ - NXP's LM75A
+ - ST Microelectronics STDS75
+ - ST Microelectronics STLM75
+ - TelCom (now Microchip) TCN75
+ - Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75,
+ TMP175, TMP275
+
+ This driver supports driver model based binding through board
+ specific I2C device tables.
+
+ It also supports the "legacy" style of driver binding. To use
+ that with some chips which don't replicate LM75 quirks exactly,
+ you may need the "force" module parameter.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm75.
+
+config SENSORS_LM77
+ tristate "National Semiconductor LM77"
+ depends on I2C
+ help
+ If you say yes here you get support for National Semiconductor LM77
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm77.
+
+config SENSORS_LM78
+ tristate "National Semiconductor LM78 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for National Semiconductor LM78,
+ LM78-J and LM79.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm78.
+
+config SENSORS_LM80
+ tristate "National Semiconductor LM80 and LM96080"
+ depends on I2C
+ help
+ If you say yes here you get support for National Semiconductor
+ LM80 and LM96080 sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called 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.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm83.
+
+config SENSORS_LM85
+ tristate "National Semiconductor LM85 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for National Semiconductor LM85
+ sensor chips and clones: ADM1027, ADT7463, ADT7468, EMC6D100,
+ EMC6D101, EMC6D102, and EMC6D103.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm85.
+
+config SENSORS_LM87
+ tristate "National Semiconductor LM87 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for National Semiconductor LM87
+ and Analog Devices ADM1024 sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm87.
+
+config SENSORS_LM90
+ tristate "National Semiconductor LM90 and compatibles"
+ depends on I2C
+ help
+ 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.
+
+config SENSORS_LM92
+ tristate "National Semiconductor LM92 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for National Semiconductor LM92
+ and Maxim MAX6635 sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm92.
+
+config SENSORS_LM93
+ tristate "National Semiconductor LM93 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for National Semiconductor LM93,
+ LM94, and compatible sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm93.
+
+config SENSORS_LM95234
+ tristate "National Semiconductor LM95234 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for the LM95233 and LM95234
+ temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm95234.
+
+config SENSORS_LM95241
+ tristate "National Semiconductor LM95241 and compatibles"
+ depends on I2C
+ help
+ If you say yes here you get support for LM95231 and LM95241 sensor
+ chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm95241.
+
+config SENSORS_LM95245
+ tristate "National Semiconductor LM95245 and compatibles"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for LM95235 and LM95245
+ temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm95245.
+
+config SENSORS_PC87360
+ tristate "National Semiconductor PC87360 family"
+ depends on !PPC
+ select HWMON_VID
+ help
+ If you say yes here you get access to the hardware monitoring
+ functions of the National Semiconductor PC8736x Super-I/O chips.
+ The PC87360, PC87363 and PC87364 only have fan monitoring and
+ control. The PC87365 and PC87366 additionally have voltage and
+ temperature monitoring.
+
+ This driver can also be built as a module. If so, the module
+ will be called pc87360.
+
+config SENSORS_PC87427
+ tristate "National Semiconductor PC87427"
+ depends on !PPC
+ help
+ If you say yes here you get access to the hardware monitoring
+ functions of the National Semiconductor PC87427 Super-I/O chip.
+ The chip has two distinct logical devices, one for fan speed
+ monitoring and control, and one for voltage and temperature
+ monitoring. Fan speed monitoring and control are supported, as
+ well as temperature monitoring. Voltages aren't supported yet.
+
+ This driver can also be built as a module. If so, the module
+ will be called pc87427.
+
+config SENSORS_NTC_THERMISTOR
+ tristate "NTC thermistor support"
+ depends on IIO
+ depends on THERMAL || !THERMAL_OF
+ help
+ This driver supports NTC thermistors sensor reading and its
+ interpretation. The driver can also monitor the temperature and
+ send notifications about the temperature.
+
+ Currently, this driver supports
+ NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333,
+ NCP03WF104 and NCP15XH103 from Murata and B57330V2103 and
+ B57891S0103 from EPCOS.
+
+ This driver can also be built as a module. If so, the module
+ will be called ntc-thermistor.
+
+config SENSORS_NCT6683
+ tristate "Nuvoton NCT6683D"
+ depends on !PPC
+ help
+ If you say yes here you get support for the hardware monitoring
+ functionality of the Nuvoton NCT6683D eSIO chip.
+
+ 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 "Platform driver for Nuvoton NCT6775F and compatibles"
+ depends on !PPC
+ depends on ACPI || ACPI=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,
+ NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, and compatible
+ Super-I/O chips. This driver replaces the w83627ehf driver for
+ NCT6775F and NCT6776F.
+
+ 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
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the Nuvoton NCT7802Y
+ hardware monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called nct7802.
+
+config SENSORS_NCT7904
+ tristate "Nuvoton NCT7904"
+ 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
+ and support for the integrated watchdog.
+
+ This driver can also be built as a module. If so, the module
+ will be called nct7904.
+
+config SENSORS_NPCM7XX
+ tristate "Nuvoton NPCM750 and compatible PWM and Fan controllers"
+ imply THERMAL
+ help
+ This driver provides support for Nuvoton NPCM750/730/715/705 PWM
+ and Fan controllers.
+
+ This driver can also be built as a module. If so, the module
+ will be called npcm750-pwm-fan.
+
+config SENSORS_NSA320
+ tristate "ZyXEL NSA320 and compatible fan speed and temperature sensors"
+ depends on GPIOLIB && OF
+ depends on MACH_KIRKWOOD || COMPILE_TEST
+ help
+ If you say yes here you get support for hardware monitoring
+ for the ZyXEL NSA320 Media Server and other compatible devices
+ (probably the NSA325 and some NSA310 variants).
+
+ The sensor data is taken from a Holtek HT46R065 microcontroller
+ connected to GPIO lines.
+
+ 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
+ tristate "Philips PCF8591 ADC/DAC"
+ depends on I2C
+ help
+ If you say yes here you get support for Philips PCF8591 4-channel
+ ADC, 1-channel DAC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called 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
+ tristate "PWM fan"
+ depends on (PWM && OF) || COMPILE_TEST
+ depends on THERMAL || THERMAL=n
+ help
+ If you say yes here you get support for fans connected to PWM lines.
+ The driver uses the generic PWM interface, thus it will work on a
+ variety of SoCs.
+
+ This driver can also be built as a module. If so, the module
+ will be called pwm-fan.
+
+config SENSORS_RASPBERRYPI_HWMON
+ tristate "Raspberry Pi voltage monitor"
+ depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
+ help
+ If you say yes here you get support for voltage sensor on the
+ Raspberry Pi.
+
+ 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
+ select BITREVERSE
+ help
+ If you say yes here you get support for the Sensiron SHT10, SHT11,
+ SHT15, SHT71, SHT75 humidity and temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called sht15.
+
+config SENSORS_SHT21
+ tristate "Sensiron humidity and temperature sensors. SHT21 and compat."
+ depends on I2C
+ help
+ If you say yes here you get support for the Sensiron SHT21, SHT25
+ humidity and temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called sht21.
+
+config SENSORS_SHT3x
+ tristate "Sensiron humidity and temperature sensors. SHT3x and compat."
+ depends on I2C
+ select CRC8
+ help
+ If you say yes here you get support for the Sensiron SHT30 and SHT31
+ humidity and temperature sensors.
+
+ 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
+ help
+ If you say yes here you get support for the Sensiron SHTC1, SHTW1,
+ and SHTC3 humidity and temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called shtc1.
+
+config SENSORS_S3C
+ tristate "Samsung built-in ADC"
+ depends on S3C_ADC
+ help
+ If you say yes here you get support for the on-board ADCs of
+ the Samsung S3C24XX, S3C64XX and other series of SoC
+
+ This driver can also be built as a module. If so, the module
+ will be called s3c-hwmon.
+
+config SENSORS_S3C_RAW
+ bool "Include raw channel attributes in sysfs"
+ depends on SENSORS_S3C
+ help
+ Say Y here if you want to include raw copies of all the ADC
+ channels in sysfs.
+
+config SENSORS_SIS5595
+ tristate "Silicon Integrated Systems Corp. SiS5595"
+ depends on PCI
+ help
+ If you say yes here you get support for the integrated sensors in
+ SiS5595 South Bridges.
+
+ 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
+ select HWMON_VID
+ help
+ If you say yes here you get support for the hardware monitoring
+ and fan control features of the SMSC DME1737, SCH311x, SCH5027, and
+ Asus A8000 Super-I/O chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called dme1737.
+
+config SENSORS_EMC1403
+ tristate "SMSC EMC1403/23 thermal sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the SMSC EMC1403/23
+ temperature monitoring chip.
+
+ Threshold values can be configured using sysfs.
+ Data from the different diodes are accessible via sysfs.
+
+config SENSORS_EMC2103
+ tristate "SMSC EMC2103"
+ depends on I2C
+ help
+ If you say yes here you get support for the temperature
+ and fan sensors of the SMSC EMC2103 chips.
+
+ 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
+ help
+ If you say yes here you get support for the SMSC EMC6W201
+ hardware monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called emc6w201.
+
+config SENSORS_SMSC47M1
+ tristate "SMSC LPC47M10x and compatibles"
+ depends on !PPC
+ help
+ If you say yes here you get support for the integrated fan
+ monitoring and control capabilities of the SMSC LPC47B27x,
+ LPC47M10x, LPC47M112, LPC47M13x, LPC47M14x, LPC47M15x,
+ LPC47M192, LPC47M292 and LPC47M997 chips.
+
+ The temperature and voltage sensor features of the LPC47M15x,
+ LPC47M192, LPC47M292 and LPC47M997 are supported by another
+ driver, select also "SMSC LPC47M192 and compatibles" below for
+ those.
+
+ This driver can also be built as a module. If so, the module
+ will be called smsc47m1.
+
+config SENSORS_SMSC47M192
+ tristate "SMSC LPC47M192 and compatibles"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for the temperature and
+ voltage sensors of the SMSC LPC47M192, LPC47M15x, LPC47M292
+ and LPC47M997 chips.
+
+ The fan monitoring and control capabilities of these chips
+ are supported by another driver, select
+ "SMSC LPC47M10x and compatibles" above. You need both drivers
+ if you want fan control and voltage/temperature sensor support.
+
+ This driver can also be built as a module. If so, the module
+ will be called smsc47m192.
+
+config SENSORS_SMSC47B397
+ tristate "SMSC LPC47B397-NC"
+ depends on !PPC
+ help
+ If you say yes here you get support for the SMSC LPC47B397-NC
+ sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called smsc47b397.
+
+config SENSORS_SCH56XX_COMMON
+ tristate
+
+config SENSORS_SCH5627
+ tristate "SMSC SCH5627"
+ depends on !PPC && WATCHDOG
+ select SENSORS_SCH56XX_COMMON
+ select WATCHDOG_CORE
+ help
+ If you say yes here you get support for the hardware monitoring
+ features of the SMSC SCH5627 Super-I/O chip including support for
+ the integrated watchdog.
+
+ This driver can also be built as a module. If so, the module
+ will be called sch5627.
+
+config SENSORS_SCH5636
+ tristate "SMSC SCH5636"
+ depends on !PPC && WATCHDOG
+ select SENSORS_SCH56XX_COMMON
+ select WATCHDOG_CORE
+ help
+ SMSC SCH5636 Super I/O chips include an embedded microcontroller for
+ hardware monitoring solutions, allowing motherboard manufacturers to
+ create their own custom hwmon solution based upon the SCH5636.
+
+ Currently this driver only supports the Fujitsu Theseus SCH5636 based
+ hwmon solution. Say yes here if you want support for the Fujitsu
+ Theseus' hardware monitoring features including support for the
+ integrated watchdog.
+
+ This driver can also be built as a module. If so, the module
+ will be called sch5636.
+
+config SENSORS_STTS751
+ tristate "ST Microelectronics STTS751"
+ depends on I2C
+ help
+ If you say yes here you get support for STTS751
+ temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called stts751.
+
+config SENSORS_SMM665
+ tristate "Summit Microelectronics SMM665"
+ depends on I2C
+ help
+ If you say yes here you get support for the hardware monitoring
+ features of the Summit Microelectronics SMM665/SMM665B Six-Channel
+ Active DC Output Controller / Monitor.
+
+ Other supported chips are SMM465, SMM665C, SMM764, and SMM766.
+ Support for those chips is untested.
+
+ This driver can also be built as a module. If so, the module will
+ be called smm665.
+
+config SENSORS_ADC128D818
+ tristate "Texas Instruments ADC128D818"
+ depends on I2C
+ help
+ If you say yes here you get support for the Texas Instruments
+ ADC128D818 System Monitor with Temperature Sensor chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called adc128d818.
+
+config SENSORS_ADS7828
+ tristate "Texas Instruments ADS7828 and compatibles"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Texas Instruments ADS7828 and
+ ADS7830 8-channel A/D converters. ADS7828 resolution is 12-bit, while
+ it is 8-bit on ADS7830.
+
+ This driver can also be built as a module. If so, the module
+ will be called ads7828.
+
+config SENSORS_ADS7871
+ tristate "Texas Instruments ADS7871 A/D converter"
+ depends on SPI
+ help
+ If you say yes here you get support for TI ADS7871 & ADS7870
+
+ This driver can also be built as a module. If so, the module
+ will be called ads7871.
+
+config SENSORS_AMC6821
+ tristate "Texas Instruments AMC6821"
+ depends on I2C
+ help
+ If you say yes here you get support for the Texas Instruments
+ AMC6821 hardware monitoring chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called amc6821.
+
+config SENSORS_INA209
+ tristate "TI / Burr Brown INA209"
+ depends on I2C
+ help
+ If you say yes here you get support for the TI / Burr Brown INA209
+ voltage / current / power monitor I2C interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called ina209.
+
+config SENSORS_INA2XX
+ tristate "Texas Instruments INA219 and compatibles"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for INA219, INA220, INA226,
+ INA230, and INA231 power monitor chips.
+
+ The INA2xx driver is configured for the default configuration of
+ the part as described in the datasheet.
+ Default value for Rshunt is 10 mOhms.
+ 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
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the TI INA3221 Triple Power
+ Monitor.
+
+ This driver can also be built as a module. If so, the module
+ will be called ina3221.
+
+config SENSORS_TC74
+ tristate "Microchip TC74"
+ depends on I2C
+ help
+ If you say yes here you get support for Microchip TC74 single
+ input temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tc74.
+
+config SENSORS_THMC50
+ tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
+ depends on I2C
+ help
+ If you say yes here you get support for Texas Instruments THMC50
+ sensor chips and clones: the Analog Devices ADM1022.
+
+ This driver can also be built as a module. If so, the module
+ will be called thmc50.
+
+config SENSORS_TMP102
+ tristate "Texas Instruments TMP102"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Texas Instruments TMP102
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tmp102.
+
+config SENSORS_TMP103
+ tristate "Texas Instruments TMP103"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Texas Instruments TMP103
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tmp103.
+
+config SENSORS_TMP108
+ tristate "Texas Instruments TMP108"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for Texas Instruments TMP108
+ sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called 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, and TMP435 temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tmp401.
+
+config SENSORS_TMP421
+ tristate "Texas Instruments TMP421 and compatible"
+ depends on I2C
+ help
+ If you say yes here you get support for Texas Instruments TMP421,
+ TMP422, TMP423, TMP441, and TMP442 temperature sensor chips.
+
+ 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
+ help
+ If you say yes here you get support for Texas Instruments TMP512,
+ and TMP513 temperature and power supply sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tmp513.
+
+config SENSORS_VEXPRESS
+ tristate "Versatile Express"
+ depends on VEXPRESS_CONFIG
+ help
+ This driver provides support for hardware sensors available on
+ the ARM Ltd's Versatile Express platform. It can provide wide
+ range of information like temperature, power, energy.
+
+config SENSORS_VIA_CPUTEMP
+ tristate "VIA CPU temperature sensor"
+ depends on X86
+ select HWMON_VID
+ help
+ If you say yes here you get support for the temperature
+ sensor inside your CPU. Supported are all known variants of
+ the VIA C7 and Nano.
+
+config SENSORS_VIA686A
+ tristate "VIA686A"
+ depends on PCI
+ help
+ If you say yes here you get support for the integrated sensors in
+ Via 686A/B South Bridges.
+
+ This driver can also be built as a module. If so, the module
+ will be called via686a.
+
+config SENSORS_VT1211
+ tristate "VIA VT1211"
+ depends on !PPC
+ select HWMON_VID
+ help
+ If you say yes here then you get support for hardware monitoring
+ features of the VIA VT1211 Super-I/O chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called vt1211.
+
+config SENSORS_VT8231
+ tristate "VIA VT8231"
+ depends on PCI
+ select HWMON_VID
+ help
+ If you say yes here then you get support for the integrated sensors
+ in the VIA VT8231 device.
+
+ This driver can also be built as a module. If so, the module
+ will be called vt8231.
+
+config SENSORS_W83773G
+ tristate "Nuvoton W83773G"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the Nuvoton W83773G hardware
+ monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83773g.
+
+config SENSORS_W83781D
+ tristate "Winbond W83781D, W83782D, W83783S, Asus AS99127F"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for the Winbond W8378x series
+ of sensor chips: the W83781D, W83782D and W83783S, and the similar
+ Asus AS99127F.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83781d.
+
+config SENSORS_W83791D
+ tristate "Winbond W83791D"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for the Winbond W83791D chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83791d.
+
+config SENSORS_W83792D
+ tristate "Winbond W83792D"
+ depends on I2C
+ help
+ If you say yes here you get support for the Winbond W83792D chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83792d.
+
+config SENSORS_W83793
+ tristate "Winbond W83793"
+ depends on I2C
+ select HWMON_VID
+ help
+ If you say yes here you get support for the Winbond W83793
+ hardware monitoring chip, including support for the integrated
+ watchdog.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83793.
+
+config SENSORS_W83795
+ tristate "Winbond/Nuvoton W83795G/ADG"
+ depends on I2C
+ help
+ If you say yes here you get support for the Winbond W83795G and
+ W83795ADG hardware monitoring chip, including manual fan speed
+ control.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83795.
+
+config SENSORS_W83795_FANCTRL
+ bool "Include automatic fan control support"
+ depends on SENSORS_W83795
+ help
+ If you say yes here, support for automatic fan speed control
+ will be included in the driver.
+
+ Please also note that this option will create sysfs attribute
+ files which may change in the future, so you shouldn't rely
+ on them being stable.
+
+config SENSORS_W83L785TS
+ tristate "Winbond W83L785TS-S"
+ depends on I2C
+ help
+ If you say yes here you get support for the Winbond W83L785TS-S
+ sensor chip, which is used on the Asus A7N8X, among other
+ motherboards.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83l785ts.
+
+config SENSORS_W83L786NG
+ tristate "Winbond W83L786NG, W83L786NR"
+ depends on I2C
+ help
+ If you say yes here you get support for the Winbond W83L786NG
+ and W83L786NR sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83l786ng.
+
+config SENSORS_W83627HF
+ tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF"
+ depends on !PPC
+ select HWMON_VID
+ help
+ If you say yes here you get support for the Winbond W836X7 series
+ of sensor chips: the W83627HF, W83627THF, W83637HF, W83687THF and
+ W83697HF.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83627hf.
+
+config SENSORS_W83627EHF
+ tristate "Winbond W83627EHF/EHG/DHG/UHG, W83667HG"
+ depends on !PPC
+ select HWMON_VID
+ help
+ If you say yes here you get support for the hardware
+ monitoring functionality of the Winbond W83627EHF Super-I/O chip.
+
+ This driver also supports the W83627EHG, which is the lead-free
+ version of the W83627EHF, and the W83627DHG, which is a similar
+ chip suited for specific Intel processors that use PECI such as
+ the Core 2 Duo. And also the W83627UHG, which is a stripped down
+ version of the W83627DHG (as far as hardware monitoring goes.)
+
+ This driver also supports Nuvoton W83667HG and W83667HG-B.
+
+ This driver can also be built as a module. If so, the module
+ will be called w83627ehf.
+
+config SENSORS_WM831X
+ tristate "WM831x PMICs"
+ depends on MFD_WM831X
+ help
+ If you say yes here you get support for the hardware
+ monitoring functionality of the Wolfson Microelectronics
+ WM831x series of PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called wm831x-hwmon.
+
+config SENSORS_WM8350
+ tristate "Wolfson Microelectronics WM835x"
+ depends on MFD_WM8350
+ help
+ If you say yes here you get support for the hardware
+ monitoring features of the WM835x series of PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called wm8350-hwmon.
+
+config SENSORS_ULTRA45
+ tristate "Sun Ultra45 PIC16F747"
+ depends on SPARC64
+ help
+ This driver provides support for the Ultra45 workstation environmental
+ sensors.
+
+config SENSORS_XGENE
+ tristate "APM X-Gene SoC hardware monitoring driver"
+ depends on XGENE_SLIMPRO_MBOX || PCC
+ help
+ 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"
+
+config SENSORS_ACPI_POWER
+ tristate "ACPI 4.0 power meter"
+ help
+ This driver exposes ACPI 4.0 power meters as hardware monitoring
+ devices. Say Y (or M) if you have a computer with ACPI 4.0 firmware
+ and a power meter.
+
+ To compile this driver as a module, choose M here:
+ the module will be called acpi_power_meter.
+
+config SENSORS_ATK0110
+ tristate "ASUS ATK0110"
+ depends on X86
+ help
+ If you say yes here you get support for the ACPI hardware
+ monitoring interface found in many 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_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
new file mode 100644
index 000000000..11d076cad
--- /dev/null
+++ b/drivers/hwmon/Makefile
@@ -0,0 +1,223 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for sensor chip drivers.
+#
+
+obj-$(CONFIG_HWMON) += hwmon.o
+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.
+obj-$(CONFIG_SENSORS_ASB100) += asb100.o
+obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o
+obj-$(CONFIG_SENSORS_W83773G) += w83773g.o
+obj-$(CONFIG_SENSORS_W83792D) += w83792d.o
+obj-$(CONFIG_SENSORS_W83793) += w83793.o
+obj-$(CONFIG_SENSORS_W83795) += w83795.o
+obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
+obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
+
+obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
+obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
+obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
+obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
+obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
+obj-$(CONFIG_SENSORS_ADC128D818) += adc128d818.o
+obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o
+obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
+obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
+obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
+obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o
+obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
+obj-$(CONFIG_SENSORS_ADM1177) += adm1177.o
+obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
+obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o
+obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o
+obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o
+obj-$(CONFIG_SENSORS_ADT7310) += adt7310.o
+obj-$(CONFIG_SENSORS_ADT7410) += adt7410.o
+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
+obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
+obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o
+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
+obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
+obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
+obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o
+obj-$(CONFIG_SENSORS_FTSTEUTATES) += ftsteutates.o
+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
+obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
+obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
+obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
+obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
+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
+obj-$(CONFIG_SENSORS_LM70) += lm70.o
+obj-$(CONFIG_SENSORS_LM73) += lm73.o
+obj-$(CONFIG_SENSORS_LM75) += lm75.o
+obj-$(CONFIG_SENSORS_LM77) += lm77.o
+obj-$(CONFIG_SENSORS_LM78) += lm78.o
+obj-$(CONFIG_SENSORS_LM80) += lm80.o
+obj-$(CONFIG_SENSORS_LM83) += lm83.o
+obj-$(CONFIG_SENSORS_LM85) += lm85.o
+obj-$(CONFIG_SENSORS_LM87) += lm87.o
+obj-$(CONFIG_SENSORS_LM90) += lm90.o
+obj-$(CONFIG_SENSORS_LM92) += lm92.o
+obj-$(CONFIG_SENSORS_LM93) += lm93.o
+obj-$(CONFIG_SENSORS_LM95234) += lm95234.o
+obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
+obj-$(CONFIG_SENSORS_LM95245) += lm95245.o
+obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o
+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
+obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
+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
+obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
+obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
+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
+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
+obj-$(CONFIG_SENSORS_TMP102) += tmp102.o
+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
+obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
+obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
+obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
+obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
+obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
+obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
+obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
+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/abituguru.c b/drivers/hwmon/abituguru.c
new file mode 100644
index 000000000..a7cae6568
--- /dev/null
+++ b/drivers/hwmon/abituguru.c
@@ -0,0 +1,1632 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * abituguru.c Copyright (c) 2005-2006 Hans de Goede <hdegoede@redhat.com>
+ */
+/*
+ * This driver supports the sensor part of the first and second revision of
+ * the custom Abit uGuru chip found on Abit uGuru motherboards. Note: because
+ * of lack of specs the CPU/RAM voltage & frequency control is not supported!
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+
+/* Banks */
+#define ABIT_UGURU_ALARM_BANK 0x20 /* 1x 3 bytes */
+#define ABIT_UGURU_SENSOR_BANK1 0x21 /* 16x volt and temp */
+#define ABIT_UGURU_FAN_PWM 0x24 /* 3x 5 bytes */
+#define ABIT_UGURU_SENSOR_BANK2 0x26 /* fans */
+/* max nr of sensors in bank1, a bank1 sensor can be in, temp or nc */
+#define ABIT_UGURU_MAX_BANK1_SENSORS 16
+/*
+ * Warning if you increase one of the 2 MAX defines below to 10 or higher you
+ * should adjust the belonging _NAMES_LENGTH macro for the 2 digit number!
+ */
+/* max nr of sensors in bank2, currently mb's with max 6 fans are known */
+#define ABIT_UGURU_MAX_BANK2_SENSORS 6
+/* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */
+#define ABIT_UGURU_MAX_PWMS 5
+/* uGuru sensor bank 1 flags */ /* Alarm if: */
+#define ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE 0x01 /* temp over warn */
+#define ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE 0x02 /* volt over max */
+#define ABIT_UGURU_VOLT_LOW_ALARM_ENABLE 0x04 /* volt under min */
+#define ABIT_UGURU_TEMP_HIGH_ALARM_FLAG 0x10 /* temp is over warn */
+#define ABIT_UGURU_VOLT_HIGH_ALARM_FLAG 0x20 /* volt is over max */
+#define ABIT_UGURU_VOLT_LOW_ALARM_FLAG 0x40 /* volt is under min */
+/* uGuru sensor bank 2 flags */ /* Alarm if: */
+#define ABIT_UGURU_FAN_LOW_ALARM_ENABLE 0x01 /* fan under min */
+/* uGuru sensor bank common flags */
+#define ABIT_UGURU_BEEP_ENABLE 0x08 /* beep if alarm */
+#define ABIT_UGURU_SHUTDOWN_ENABLE 0x80 /* shutdown if alarm */
+/* uGuru fan PWM (speed control) flags */
+#define ABIT_UGURU_FAN_PWM_ENABLE 0x80 /* enable speed control */
+/* Values used for conversion */
+#define ABIT_UGURU_FAN_MAX 15300 /* RPM */
+/* Bank1 sensor types */
+#define ABIT_UGURU_IN_SENSOR 0
+#define ABIT_UGURU_TEMP_SENSOR 1
+#define ABIT_UGURU_NC 2
+/*
+ * In many cases we need to wait for the uGuru to reach a certain status, most
+ * of the time it will reach this status within 30 - 90 ISA reads, and thus we
+ * can best busy wait. This define gives the total amount of reads to try.
+ */
+#define ABIT_UGURU_WAIT_TIMEOUT 125
+/*
+ * However sometimes older versions of the uGuru seem to be distracted and they
+ * do not respond for a long time. To handle this we sleep before each of the
+ * last ABIT_UGURU_WAIT_TIMEOUT_SLEEP tries.
+ */
+#define ABIT_UGURU_WAIT_TIMEOUT_SLEEP 5
+/*
+ * Normally all expected status in abituguru_ready, are reported after the
+ * first read, but sometimes not and we need to poll.
+ */
+#define ABIT_UGURU_READY_TIMEOUT 5
+/* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */
+#define ABIT_UGURU_MAX_RETRIES 3
+#define ABIT_UGURU_RETRY_DELAY (HZ/5)
+/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is an error */
+#define ABIT_UGURU_MAX_TIMEOUTS 2
+/* utility macros */
+#define ABIT_UGURU_NAME "abituguru"
+#define ABIT_UGURU_DEBUG(level, format, arg...) \
+ do { \
+ if (level <= verbose) \
+ pr_debug(format , ## arg); \
+ } while (0)
+
+/* Macros to help calculate the sysfs_names array length */
+/*
+ * sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0,
+ * in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0
+ */
+#define ABITUGURU_IN_NAMES_LENGTH (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14)
+/*
+ * sum of strlen of: temp??_input\0, temp??_max\0, temp??_crit\0,
+ * temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0
+ */
+#define ABITUGURU_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16)
+/*
+ * sum of strlen of: fan?_input\0, fan?_min\0, fan?_alarm\0,
+ * fan?_alarm_enable\0, fan?_beep\0, fan?_shutdown\0
+ */
+#define ABITUGURU_FAN_NAMES_LENGTH (11 + 9 + 11 + 18 + 10 + 14)
+/*
+ * sum of strlen of: pwm?_enable\0, pwm?_auto_channels_temp\0,
+ * pwm?_auto_point{1,2}_pwm\0, pwm?_auto_point{1,2}_temp\0
+ */
+#define ABITUGURU_PWM_NAMES_LENGTH (12 + 24 + 2 * 21 + 2 * 22)
+/* IN_NAMES_LENGTH > TEMP_NAMES_LENGTH so assume all bank1 sensors are in */
+#define ABITUGURU_SYSFS_NAMES_LENGTH ( \
+ ABIT_UGURU_MAX_BANK1_SENSORS * ABITUGURU_IN_NAMES_LENGTH + \
+ ABIT_UGURU_MAX_BANK2_SENSORS * ABITUGURU_FAN_NAMES_LENGTH + \
+ ABIT_UGURU_MAX_PWMS * ABITUGURU_PWM_NAMES_LENGTH)
+
+/*
+ * All the macros below are named identical to the oguru and oguru2 programs
+ * reverse engineered by Olle Sandberg, hence the names might not be 100%
+ * logical. I could come up with better names, but I prefer keeping the names
+ * identical so that this driver can be compared with his work more easily.
+ */
+/* Two i/o-ports are used by uGuru */
+#define ABIT_UGURU_BASE 0x00E0
+/* Used to tell uGuru what to read and to read the actual data */
+#define ABIT_UGURU_CMD 0x00
+/* Mostly used to check if uGuru is busy */
+#define ABIT_UGURU_DATA 0x04
+#define ABIT_UGURU_REGION_LENGTH 5
+/* uGuru status' */
+#define ABIT_UGURU_STATUS_WRITE 0x00 /* Ready to be written */
+#define ABIT_UGURU_STATUS_READ 0x01 /* Ready to be read */
+#define ABIT_UGURU_STATUS_INPUT 0x08 /* More input */
+#define ABIT_UGURU_STATUS_READY 0x09 /* Ready to be written */
+
+/* Constants */
+/* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */
+static const int abituguru_bank1_max_value[2] = { 3494, 255000 };
+/*
+ * Min / Max allowed values for sensor2 (fan) alarm threshold, these values
+ * correspond to 300-3000 RPM
+ */
+static const u8 abituguru_bank2_min_threshold = 5;
+static const u8 abituguru_bank2_max_threshold = 50;
+/*
+ * Register 0 is a bitfield, 1 and 2 are pwm settings (255 = 100%), 3 and 4
+ * are temperature trip points.
+ */
+static const int abituguru_pwm_settings_multiplier[5] = { 0, 1, 1, 1000, 1000 };
+/*
+ * Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a
+ * special case the minimum allowed pwm% setting for this is 30% (77) on
+ * some MB's this special case is handled in the code!
+ */
+static const u8 abituguru_pwm_min[5] = { 0, 170, 170, 25, 25 };
+static const u8 abituguru_pwm_max[5] = { 0, 255, 255, 75, 75 };
+
+
+/* Insmod parameters */
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Set to one to force detection.");
+static int bank1_types[ABIT_UGURU_MAX_BANK1_SENSORS] = { -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+module_param_array(bank1_types, int, NULL, 0);
+MODULE_PARM_DESC(bank1_types, "Bank1 sensortype autodetection override:\n"
+ " -1 autodetect\n"
+ " 0 volt sensor\n"
+ " 1 temp sensor\n"
+ " 2 not connected");
+static int fan_sensors;
+module_param(fan_sensors, int, 0);
+MODULE_PARM_DESC(fan_sensors, "Number of fan sensors on the uGuru "
+ "(0 = autodetect)");
+static int pwms;
+module_param(pwms, int, 0);
+MODULE_PARM_DESC(pwms, "Number of PWMs on the uGuru "
+ "(0 = autodetect)");
+
+/* Default verbose is 2, since this driver is still in the testing phase */
+static int verbose = 2;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "How verbose should the driver be? (0-3):\n"
+ " 0 normal output\n"
+ " 1 + verbose error reporting\n"
+ " 2 + sensors type probing info\n"
+ " 3 + retryable error reporting");
+
+
+/*
+ * For the Abit uGuru, we need to keep some data in memory.
+ * The structure is dynamically allocated, at the same time when a new
+ * abituguru device is allocated.
+ */
+struct abituguru_data {
+ struct device *hwmon_dev; /* hwmon registered device */
+ struct mutex update_lock; /* protect access to data and uGuru */
+ unsigned long last_updated; /* In jiffies */
+ unsigned short addr; /* uguru base address */
+ char uguru_ready; /* is the uguru in ready state? */
+ unsigned char update_timeouts; /*
+ * number of update timeouts since last
+ * successful update
+ */
+
+ /*
+ * The sysfs attr and their names are generated automatically, for bank1
+ * we cannot use a predefined array because we don't know beforehand
+ * of a sensor is a volt or a temp sensor, for bank2 and the pwms its
+ * easier todo things the same way. For in sensors we have 9 (temp 7)
+ * sysfs entries per sensor, for bank2 and pwms 6.
+ */
+ struct sensor_device_attribute_2 sysfs_attr[
+ ABIT_UGURU_MAX_BANK1_SENSORS * 9 +
+ ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6];
+ /* Buffer to store the dynamically generated sysfs names */
+ char sysfs_names[ABITUGURU_SYSFS_NAMES_LENGTH];
+
+ /* Bank 1 data */
+ /* number of and addresses of [0] in, [1] temp sensors */
+ u8 bank1_sensors[2];
+ u8 bank1_address[2][ABIT_UGURU_MAX_BANK1_SENSORS];
+ u8 bank1_value[ABIT_UGURU_MAX_BANK1_SENSORS];
+ /*
+ * This array holds 3 entries per sensor for the bank 1 sensor settings
+ * (flags, min, max for voltage / flags, warn, shutdown for temp).
+ */
+ u8 bank1_settings[ABIT_UGURU_MAX_BANK1_SENSORS][3];
+ /*
+ * Maximum value for each sensor used for scaling in mV/millidegrees
+ * Celsius.
+ */
+ int bank1_max_value[ABIT_UGURU_MAX_BANK1_SENSORS];
+
+ /* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */
+ u8 bank2_sensors; /* actual number of bank2 sensors found */
+ u8 bank2_value[ABIT_UGURU_MAX_BANK2_SENSORS];
+ u8 bank2_settings[ABIT_UGURU_MAX_BANK2_SENSORS][2]; /* flags, min */
+
+ /* Alarms 2 bytes for bank1, 1 byte for bank2 */
+ u8 alarms[3];
+
+ /* Fan PWM (speed control) 5 bytes per PWM */
+ u8 pwms; /* actual number of pwms found */
+ u8 pwm_settings[ABIT_UGURU_MAX_PWMS][5];
+};
+
+static const char *never_happen = "This should never happen.";
+static const char *report_this =
+ "Please report this to the abituguru maintainer (see MAINTAINERS)";
+
+/* wait till the uguru is in the specified state */
+static int abituguru_wait(struct abituguru_data *data, u8 state)
+{
+ int timeout = ABIT_UGURU_WAIT_TIMEOUT;
+
+ while (inb_p(data->addr + ABIT_UGURU_DATA) != state) {
+ timeout--;
+ if (timeout == 0)
+ return -EBUSY;
+ /*
+ * sleep a bit before our last few tries, see the comment on
+ * this where ABIT_UGURU_WAIT_TIMEOUT_SLEEP is defined.
+ */
+ if (timeout <= ABIT_UGURU_WAIT_TIMEOUT_SLEEP)
+ msleep(0);
+ }
+ return 0;
+}
+
+/* Put the uguru in ready for input state */
+static int abituguru_ready(struct abituguru_data *data)
+{
+ int timeout = ABIT_UGURU_READY_TIMEOUT;
+
+ if (data->uguru_ready)
+ return 0;
+
+ /* Reset? / Prepare for next read/write cycle */
+ outb(0x00, data->addr + ABIT_UGURU_DATA);
+
+ /* Wait till the uguru is ready */
+ if (abituguru_wait(data, ABIT_UGURU_STATUS_READY)) {
+ ABIT_UGURU_DEBUG(1,
+ "timeout exceeded waiting for ready state\n");
+ return -EIO;
+ }
+
+ /* Cmd port MUST be read now and should contain 0xAC */
+ while (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) {
+ timeout--;
+ if (timeout == 0) {
+ ABIT_UGURU_DEBUG(1,
+ "CMD reg does not hold 0xAC after ready command\n");
+ return -EIO;
+ }
+ msleep(0);
+ }
+
+ /*
+ * After this the ABIT_UGURU_DATA port should contain
+ * ABIT_UGURU_STATUS_INPUT
+ */
+ timeout = ABIT_UGURU_READY_TIMEOUT;
+ while (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) {
+ timeout--;
+ if (timeout == 0) {
+ ABIT_UGURU_DEBUG(1,
+ "state != more input after ready command\n");
+ return -EIO;
+ }
+ msleep(0);
+ }
+
+ data->uguru_ready = 1;
+ return 0;
+}
+
+/*
+ * Send the bank and then sensor address to the uGuru for the next read/write
+ * cycle. This function gets called as the first part of a read/write by
+ * abituguru_read and abituguru_write. This function should never be
+ * called by any other function.
+ */
+static int abituguru_send_address(struct abituguru_data *data,
+ u8 bank_addr, u8 sensor_addr, int retries)
+{
+ /*
+ * assume the caller does error handling itself if it has not requested
+ * any retries, and thus be quiet.
+ */
+ int report_errors = retries;
+
+ for (;;) {
+ /*
+ * Make sure the uguru is ready and then send the bank address,
+ * after this the uguru is no longer "ready".
+ */
+ if (abituguru_ready(data) != 0)
+ return -EIO;
+ outb(bank_addr, data->addr + ABIT_UGURU_DATA);
+ data->uguru_ready = 0;
+
+ /*
+ * Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again
+ * and send the sensor addr
+ */
+ if (abituguru_wait(data, ABIT_UGURU_STATUS_INPUT)) {
+ if (retries) {
+ ABIT_UGURU_DEBUG(3, "timeout exceeded "
+ "waiting for more input state, %d "
+ "tries remaining\n", retries);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(ABIT_UGURU_RETRY_DELAY);
+ retries--;
+ continue;
+ }
+ if (report_errors)
+ ABIT_UGURU_DEBUG(1, "timeout exceeded "
+ "waiting for more input state "
+ "(bank: %d)\n", (int)bank_addr);
+ return -EBUSY;
+ }
+ outb(sensor_addr, data->addr + ABIT_UGURU_CMD);
+ return 0;
+ }
+}
+
+/*
+ * Read count bytes from sensor sensor_addr in bank bank_addr and store the
+ * result in buf, retry the send address part of the read retries times.
+ */
+static int abituguru_read(struct abituguru_data *data,
+ u8 bank_addr, u8 sensor_addr, u8 *buf, int count, int retries)
+{
+ int i;
+
+ /* Send the address */
+ i = abituguru_send_address(data, bank_addr, sensor_addr, retries);
+ if (i)
+ return i;
+
+ /* And read the data */
+ for (i = 0; i < count; i++) {
+ if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) {
+ ABIT_UGURU_DEBUG(retries ? 1 : 3,
+ "timeout exceeded waiting for "
+ "read state (bank: %d, sensor: %d)\n",
+ (int)bank_addr, (int)sensor_addr);
+ break;
+ }
+ buf[i] = inb(data->addr + ABIT_UGURU_CMD);
+ }
+
+ /* Last put the chip back in ready state */
+ abituguru_ready(data);
+
+ return i;
+}
+
+/*
+ * Write count bytes from buf to sensor sensor_addr in bank bank_addr, the send
+ * address part of the write is always retried ABIT_UGURU_MAX_RETRIES times.
+ */
+static int abituguru_write(struct abituguru_data *data,
+ u8 bank_addr, u8 sensor_addr, u8 *buf, int count)
+{
+ /*
+ * We use the ready timeout as we have to wait for 0xAC just like the
+ * ready function
+ */
+ int i, timeout = ABIT_UGURU_READY_TIMEOUT;
+
+ /* Send the address */
+ i = abituguru_send_address(data, bank_addr, sensor_addr,
+ ABIT_UGURU_MAX_RETRIES);
+ if (i)
+ return i;
+
+ /* And write the data */
+ for (i = 0; i < count; i++) {
+ if (abituguru_wait(data, ABIT_UGURU_STATUS_WRITE)) {
+ ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for "
+ "write state (bank: %d, sensor: %d)\n",
+ (int)bank_addr, (int)sensor_addr);
+ break;
+ }
+ outb(buf[i], data->addr + ABIT_UGURU_CMD);
+ }
+
+ /*
+ * Now we need to wait till the chip is ready to be read again,
+ * so that we can read 0xAC as confirmation that our write has
+ * succeeded.
+ */
+ if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) {
+ ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for read state "
+ "after write (bank: %d, sensor: %d)\n", (int)bank_addr,
+ (int)sensor_addr);
+ return -EIO;
+ }
+
+ /* Cmd port MUST be read now and should contain 0xAC */
+ while (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) {
+ timeout--;
+ if (timeout == 0) {
+ ABIT_UGURU_DEBUG(1, "CMD reg does not hold 0xAC after "
+ "write (bank: %d, sensor: %d)\n",
+ (int)bank_addr, (int)sensor_addr);
+ return -EIO;
+ }
+ msleep(0);
+ }
+
+ /* Last put the chip back in ready state */
+ abituguru_ready(data);
+
+ return i;
+}
+
+/*
+ * Detect sensor type. Temp and Volt sensors are enabled with
+ * different masks and will ignore enable masks not meant for them.
+ * This enables us to test what kind of sensor we're dealing with.
+ * By setting the alarm thresholds so that we will always get an
+ * alarm for sensor type X and then enabling the sensor as sensor type
+ * X, if we then get an alarm it is a sensor of type X.
+ */
+static int
+abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
+ u8 sensor_addr)
+{
+ u8 val, test_flag, buf[3];
+ int i, ret = -ENODEV; /* error is the most common used retval :| */
+
+ /* If overriden by the user return the user selected type */
+ if (bank1_types[sensor_addr] >= ABIT_UGURU_IN_SENSOR &&
+ bank1_types[sensor_addr] <= ABIT_UGURU_NC) {
+ ABIT_UGURU_DEBUG(2, "assuming sensor type %d for bank1 sensor "
+ "%d because of \"bank1_types\" module param\n",
+ bank1_types[sensor_addr], (int)sensor_addr);
+ return bank1_types[sensor_addr];
+ }
+
+ /* First read the sensor and the current settings */
+ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val,
+ 1, ABIT_UGURU_MAX_RETRIES) != 1)
+ return -ENODEV;
+
+ /* Test val is sane / usable for sensor type detection. */
+ if ((val < 10u) || (val > 250u)) {
+ pr_warn("bank1-sensor: %d reading (%d) too close to limits, "
+ "unable to determine sensor type, skipping sensor\n",
+ (int)sensor_addr, (int)val);
+ /*
+ * assume no sensor is there for sensors for which we can't
+ * determine the sensor type because their reading is too close
+ * to their limits, this usually means no sensor is there.
+ */
+ return ABIT_UGURU_NC;
+ }
+
+ ABIT_UGURU_DEBUG(2, "testing bank1 sensor %d\n", (int)sensor_addr);
+ /*
+ * Volt sensor test, enable volt low alarm, set min value ridiculously
+ * high, or vica versa if the reading is very high. If its a volt
+ * sensor this should always give us an alarm.
+ */
+ if (val <= 240u) {
+ buf[0] = ABIT_UGURU_VOLT_LOW_ALARM_ENABLE;
+ buf[1] = 245;
+ buf[2] = 250;
+ test_flag = ABIT_UGURU_VOLT_LOW_ALARM_FLAG;
+ } else {
+ buf[0] = ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE;
+ buf[1] = 5;
+ buf[2] = 10;
+ test_flag = ABIT_UGURU_VOLT_HIGH_ALARM_FLAG;
+ }
+
+ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
+ buf, 3) != 3)
+ goto abituguru_detect_bank1_sensor_type_exit;
+ /*
+ * Now we need 20 ms to give the uguru time to read the sensors
+ * and raise a voltage alarm
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/50);
+ /* Check for alarm and check the alarm is a volt low alarm. */
+ if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
+ ABIT_UGURU_MAX_RETRIES) != 3)
+ goto abituguru_detect_bank1_sensor_type_exit;
+ if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
+ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
+ sensor_addr, buf, 3,
+ ABIT_UGURU_MAX_RETRIES) != 3)
+ goto abituguru_detect_bank1_sensor_type_exit;
+ if (buf[0] & test_flag) {
+ ABIT_UGURU_DEBUG(2, " found volt sensor\n");
+ ret = ABIT_UGURU_IN_SENSOR;
+ goto abituguru_detect_bank1_sensor_type_exit;
+ } else
+ ABIT_UGURU_DEBUG(2, " alarm raised during volt "
+ "sensor test, but volt range flag not set\n");
+ } else
+ ABIT_UGURU_DEBUG(2, " alarm not raised during volt sensor "
+ "test\n");
+
+ /*
+ * Temp sensor test, enable sensor as a temp sensor, set beep value
+ * ridiculously low (but not too low, otherwise uguru ignores it).
+ * If its a temp sensor this should always give us an alarm.
+ */
+ buf[0] = ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE;
+ buf[1] = 5;
+ buf[2] = 10;
+ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr,
+ buf, 3) != 3)
+ goto abituguru_detect_bank1_sensor_type_exit;
+ /*
+ * Now we need 50 ms to give the uguru time to read the sensors
+ * and raise a temp alarm
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/20);
+ /* Check for alarm and check the alarm is a temp high alarm. */
+ if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3,
+ ABIT_UGURU_MAX_RETRIES) != 3)
+ goto abituguru_detect_bank1_sensor_type_exit;
+ if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) {
+ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
+ sensor_addr, buf, 3,
+ ABIT_UGURU_MAX_RETRIES) != 3)
+ goto abituguru_detect_bank1_sensor_type_exit;
+ if (buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) {
+ ABIT_UGURU_DEBUG(2, " found temp sensor\n");
+ ret = ABIT_UGURU_TEMP_SENSOR;
+ goto abituguru_detect_bank1_sensor_type_exit;
+ } else
+ ABIT_UGURU_DEBUG(2, " alarm raised during temp "
+ "sensor test, but temp high flag not set\n");
+ } else
+ ABIT_UGURU_DEBUG(2, " alarm not raised during temp sensor "
+ "test\n");
+
+ ret = ABIT_UGURU_NC;
+abituguru_detect_bank1_sensor_type_exit:
+ /*
+ * Restore original settings, failing here is really BAD, it has been
+ * reported that some BIOS-es hang when entering the uGuru menu with
+ * invalid settings present in the uGuru, so we try this 3 times.
+ */
+ for (i = 0; i < 3; i++)
+ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2,
+ sensor_addr, data->bank1_settings[sensor_addr],
+ 3) == 3)
+ break;
+ if (i == 3) {
+ pr_err("Fatal error could not restore original settings. %s %s\n",
+ never_happen, report_this);
+ return -ENODEV;
+ }
+ return ret;
+}
+
+/*
+ * These functions try to find out how many sensors there are in bank2 and how
+ * many pwms there are. The purpose of this is to make sure that we don't give
+ * the user the possibility to change settings for non-existent sensors / pwm.
+ * The uGuru will happily read / write whatever memory happens to be after the
+ * memory storing the PWM settings when reading/writing to a PWM which is not
+ * there. Notice even if we detect a PWM which doesn't exist we normally won't
+ * write to it, unless the user tries to change the settings.
+ *
+ * Although the uGuru allows reading (settings) from non existing bank2
+ * sensors, my version of the uGuru does seem to stop writing to them, the
+ * write function above aborts in this case with:
+ * "CMD reg does not hold 0xAC after write"
+ *
+ * Notice these 2 tests are non destructive iow read-only tests, otherwise
+ * they would defeat their purpose. Although for the bank2_sensors detection a
+ * read/write test would be feasible because of the reaction above, I've
+ * however opted to stay on the safe side.
+ */
+static void
+abituguru_detect_no_bank2_sensors(struct abituguru_data *data)
+{
+ int i;
+
+ if (fan_sensors > 0 && fan_sensors <= ABIT_UGURU_MAX_BANK2_SENSORS) {
+ data->bank2_sensors = fan_sensors;
+ ABIT_UGURU_DEBUG(2, "assuming %d fan sensors because of "
+ "\"fan_sensors\" module param\n",
+ (int)data->bank2_sensors);
+ return;
+ }
+
+ ABIT_UGURU_DEBUG(2, "detecting number of fan sensors\n");
+ for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) {
+ /*
+ * 0x89 are the known used bits:
+ * -0x80 enable shutdown
+ * -0x08 enable beep
+ * -0x01 enable alarm
+ * All other bits should be 0, but on some motherboards
+ * 0x40 (bit 6) is also high for some of the fans??
+ */
+ if (data->bank2_settings[i][0] & ~0xC9) {
+ ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem "
+ "to be a fan sensor: settings[0] = %02X\n",
+ i, (unsigned int)data->bank2_settings[i][0]);
+ break;
+ }
+
+ /* check if the threshold is within the allowed range */
+ if (data->bank2_settings[i][1] <
+ abituguru_bank2_min_threshold) {
+ ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem "
+ "to be a fan sensor: the threshold (%d) is "
+ "below the minimum (%d)\n", i,
+ (int)data->bank2_settings[i][1],
+ (int)abituguru_bank2_min_threshold);
+ break;
+ }
+ if (data->bank2_settings[i][1] >
+ abituguru_bank2_max_threshold) {
+ ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem "
+ "to be a fan sensor: the threshold (%d) is "
+ "above the maximum (%d)\n", i,
+ (int)data->bank2_settings[i][1],
+ (int)abituguru_bank2_max_threshold);
+ break;
+ }
+ }
+
+ data->bank2_sensors = i;
+ ABIT_UGURU_DEBUG(2, " found: %d fan sensors\n",
+ (int)data->bank2_sensors);
+}
+
+static void
+abituguru_detect_no_pwms(struct abituguru_data *data)
+{
+ int i, j;
+
+ if (pwms > 0 && pwms <= ABIT_UGURU_MAX_PWMS) {
+ data->pwms = pwms;
+ ABIT_UGURU_DEBUG(2, "assuming %d PWM outputs because of "
+ "\"pwms\" module param\n", (int)data->pwms);
+ return;
+ }
+
+ ABIT_UGURU_DEBUG(2, "detecting number of PWM outputs\n");
+ for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) {
+ /*
+ * 0x80 is the enable bit and the low
+ * nibble is which temp sensor to use,
+ * the other bits should be 0
+ */
+ if (data->pwm_settings[i][0] & ~0x8F) {
+ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
+ "to be a pwm channel: settings[0] = %02X\n",
+ i, (unsigned int)data->pwm_settings[i][0]);
+ break;
+ }
+
+ /*
+ * the low nibble must correspond to one of the temp sensors
+ * we've found
+ */
+ for (j = 0; j < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR];
+ j++) {
+ if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][j] ==
+ (data->pwm_settings[i][0] & 0x0F))
+ break;
+ }
+ if (j == data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) {
+ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
+ "to be a pwm channel: %d is not a valid temp "
+ "sensor address\n", i,
+ data->pwm_settings[i][0] & 0x0F);
+ break;
+ }
+
+ /* check if all other settings are within the allowed range */
+ for (j = 1; j < 5; j++) {
+ u8 min;
+ /* special case pwm1 min pwm% */
+ if ((i == 0) && ((j == 1) || (j == 2)))
+ min = 77;
+ else
+ min = abituguru_pwm_min[j];
+ if (data->pwm_settings[i][j] < min) {
+ ABIT_UGURU_DEBUG(2, " pwm channel %d does "
+ "not seem to be a pwm channel: "
+ "setting %d (%d) is below the minimum "
+ "value (%d)\n", i, j,
+ (int)data->pwm_settings[i][j],
+ (int)min);
+ goto abituguru_detect_no_pwms_exit;
+ }
+ if (data->pwm_settings[i][j] > abituguru_pwm_max[j]) {
+ ABIT_UGURU_DEBUG(2, " pwm channel %d does "
+ "not seem to be a pwm channel: "
+ "setting %d (%d) is above the maximum "
+ "value (%d)\n", i, j,
+ (int)data->pwm_settings[i][j],
+ (int)abituguru_pwm_max[j]);
+ goto abituguru_detect_no_pwms_exit;
+ }
+ }
+
+ /* check that min temp < max temp and min pwm < max pwm */
+ if (data->pwm_settings[i][1] >= data->pwm_settings[i][2]) {
+ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
+ "to be a pwm channel: min pwm (%d) >= "
+ "max pwm (%d)\n", i,
+ (int)data->pwm_settings[i][1],
+ (int)data->pwm_settings[i][2]);
+ break;
+ }
+ if (data->pwm_settings[i][3] >= data->pwm_settings[i][4]) {
+ ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem "
+ "to be a pwm channel: min temp (%d) >= "
+ "max temp (%d)\n", i,
+ (int)data->pwm_settings[i][3],
+ (int)data->pwm_settings[i][4]);
+ break;
+ }
+ }
+
+abituguru_detect_no_pwms_exit:
+ data->pwms = i;
+ ABIT_UGURU_DEBUG(2, " found: %d PWM outputs\n", (int)data->pwms);
+}
+
+/*
+ * Following are the sysfs callback functions. These functions expect:
+ * sensor_device_attribute_2->index: sensor address/offset in the bank
+ * sensor_device_attribute_2->nr: register offset, bitmask or NA.
+ */
+static struct abituguru_data *abituguru_update_device(struct device *dev);
+
+static ssize_t show_bank1_value(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = abituguru_update_device(dev);
+ if (!data)
+ return -EIO;
+ return sprintf(buf, "%d\n", (data->bank1_value[attr->index] *
+ data->bank1_max_value[attr->index] + 128) / 255);
+}
+
+static ssize_t show_bank1_setting(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ (data->bank1_settings[attr->index][attr->nr] *
+ data->bank1_max_value[attr->index] + 128) / 255);
+}
+
+static ssize_t show_bank2_value(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = abituguru_update_device(dev);
+ if (!data)
+ return -EIO;
+ return sprintf(buf, "%d\n", (data->bank2_value[attr->index] *
+ ABIT_UGURU_FAN_MAX + 128) / 255);
+}
+
+static ssize_t show_bank2_setting(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ (data->bank2_settings[attr->index][attr->nr] *
+ ABIT_UGURU_FAN_MAX + 128) / 255);
+}
+
+static ssize_t store_bank1_setting(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ ssize_t ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = count;
+ val = (val * 255 + data->bank1_max_value[attr->index] / 2) /
+ data->bank1_max_value[attr->index];
+ if (val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (data->bank1_settings[attr->index][attr->nr] != val) {
+ u8 orig_val = data->bank1_settings[attr->index][attr->nr];
+ data->bank1_settings[attr->index][attr->nr] = val;
+ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2,
+ attr->index, data->bank1_settings[attr->index],
+ 3) <= attr->nr) {
+ data->bank1_settings[attr->index][attr->nr] = orig_val;
+ ret = -EIO;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t store_bank2_setting(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ ssize_t ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = count;
+ val = (val * 255 + ABIT_UGURU_FAN_MAX / 2) / ABIT_UGURU_FAN_MAX;
+
+ /* this check can be done before taking the lock */
+ if (val < abituguru_bank2_min_threshold ||
+ val > abituguru_bank2_max_threshold)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (data->bank2_settings[attr->index][attr->nr] != val) {
+ u8 orig_val = data->bank2_settings[attr->index][attr->nr];
+ data->bank2_settings[attr->index][attr->nr] = val;
+ if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK2 + 2,
+ attr->index, data->bank2_settings[attr->index],
+ 2) <= attr->nr) {
+ data->bank2_settings[attr->index][attr->nr] = orig_val;
+ ret = -EIO;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t show_bank1_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = abituguru_update_device(dev);
+ if (!data)
+ return -EIO;
+ /*
+ * See if the alarm bit for this sensor is set, and if the
+ * alarm matches the type of alarm we're looking for (for volt
+ * it can be either low or high). The type is stored in a few
+ * readonly bits in the settings part of the relevant sensor.
+ * The bitmask of the type is passed to us in attr->nr.
+ */
+ if ((data->alarms[attr->index / 8] & (0x01 << (attr->index % 8))) &&
+ (data->bank1_settings[attr->index][0] & attr->nr))
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t show_bank2_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = abituguru_update_device(dev);
+ if (!data)
+ return -EIO;
+ if (data->alarms[2] & (0x01 << attr->index))
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t show_bank1_mask(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ if (data->bank1_settings[attr->index][0] & attr->nr)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t show_bank2_mask(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ if (data->bank2_settings[attr->index][0] & attr->nr)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t store_bank1_mask(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ ssize_t ret;
+ u8 orig_val;
+ unsigned long mask;
+
+ ret = kstrtoul(buf, 10, &mask);
+ if (ret)
+ return ret;
+
+ ret = count;
+ mutex_lock(&data->update_lock);
+ orig_val = data->bank1_settings[attr->index][0];
+
+ if (mask)
+ data->bank1_settings[attr->index][0] |= attr->nr;
+ else
+ data->bank1_settings[attr->index][0] &= ~attr->nr;
+
+ if ((data->bank1_settings[attr->index][0] != orig_val) &&
+ (abituguru_write(data,
+ ABIT_UGURU_SENSOR_BANK1 + 2, attr->index,
+ data->bank1_settings[attr->index], 3) < 1)) {
+ data->bank1_settings[attr->index][0] = orig_val;
+ ret = -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t store_bank2_mask(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ ssize_t ret;
+ u8 orig_val;
+ unsigned long mask;
+
+ ret = kstrtoul(buf, 10, &mask);
+ if (ret)
+ return ret;
+
+ ret = count;
+ mutex_lock(&data->update_lock);
+ orig_val = data->bank2_settings[attr->index][0];
+
+ if (mask)
+ data->bank2_settings[attr->index][0] |= attr->nr;
+ else
+ data->bank2_settings[attr->index][0] &= ~attr->nr;
+
+ if ((data->bank2_settings[attr->index][0] != orig_val) &&
+ (abituguru_write(data,
+ ABIT_UGURU_SENSOR_BANK2 + 2, attr->index,
+ data->bank2_settings[attr->index], 2) < 1)) {
+ data->bank2_settings[attr->index][0] = orig_val;
+ ret = -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+/* Fan PWM (speed control) */
+static ssize_t show_pwm_setting(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", data->pwm_settings[attr->index][attr->nr] *
+ abituguru_pwm_settings_multiplier[attr->nr]);
+}
+
+static ssize_t store_pwm_setting(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ u8 min;
+ unsigned long val;
+ ssize_t ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = count;
+ val = (val + abituguru_pwm_settings_multiplier[attr->nr] / 2) /
+ abituguru_pwm_settings_multiplier[attr->nr];
+
+ /* special case pwm1 min pwm% */
+ if ((attr->index == 0) && ((attr->nr == 1) || (attr->nr == 2)))
+ min = 77;
+ else
+ min = abituguru_pwm_min[attr->nr];
+
+ /* this check can be done before taking the lock */
+ if (val < min || val > abituguru_pwm_max[attr->nr])
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ /* this check needs to be done after taking the lock */
+ if ((attr->nr & 1) &&
+ (val >= data->pwm_settings[attr->index][attr->nr + 1]))
+ ret = -EINVAL;
+ else if (!(attr->nr & 1) &&
+ (val <= data->pwm_settings[attr->index][attr->nr - 1]))
+ ret = -EINVAL;
+ else if (data->pwm_settings[attr->index][attr->nr] != val) {
+ u8 orig_val = data->pwm_settings[attr->index][attr->nr];
+ data->pwm_settings[attr->index][attr->nr] = val;
+ if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
+ attr->index, data->pwm_settings[attr->index],
+ 5) <= attr->nr) {
+ data->pwm_settings[attr->index][attr->nr] =
+ orig_val;
+ ret = -EIO;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t show_pwm_sensor(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ int i;
+ /*
+ * We need to walk to the temp sensor addresses to find what
+ * the userspace id of the configured temp sensor is.
+ */
+ for (i = 0; i < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; i++)
+ if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][i] ==
+ (data->pwm_settings[attr->index][0] & 0x0F))
+ return sprintf(buf, "%d\n", i+1);
+
+ return -ENXIO;
+}
+
+static ssize_t store_pwm_sensor(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ ssize_t ret;
+ unsigned long val;
+ u8 orig_val;
+ u8 address;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val == 0 || val > data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR])
+ return -EINVAL;
+
+ val -= 1;
+ ret = count;
+ mutex_lock(&data->update_lock);
+ orig_val = data->pwm_settings[attr->index][0];
+ address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val];
+ data->pwm_settings[attr->index][0] &= 0xF0;
+ data->pwm_settings[attr->index][0] |= address;
+ if (data->pwm_settings[attr->index][0] != orig_val) {
+ if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, attr->index,
+ data->pwm_settings[attr->index], 5) < 1) {
+ data->pwm_settings[attr->index][0] = orig_val;
+ ret = -EIO;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t show_pwm_enable(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ int res = 0;
+ if (data->pwm_settings[attr->index][0] & ABIT_UGURU_FAN_PWM_ENABLE)
+ res = 2;
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ u8 orig_val;
+ ssize_t ret;
+ unsigned long user_val;
+
+ ret = kstrtoul(buf, 10, &user_val);
+ if (ret)
+ return ret;
+
+ ret = count;
+ mutex_lock(&data->update_lock);
+ orig_val = data->pwm_settings[attr->index][0];
+ switch (user_val) {
+ case 0:
+ data->pwm_settings[attr->index][0] &=
+ ~ABIT_UGURU_FAN_PWM_ENABLE;
+ break;
+ case 2:
+ data->pwm_settings[attr->index][0] |= ABIT_UGURU_FAN_PWM_ENABLE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if ((data->pwm_settings[attr->index][0] != orig_val) &&
+ (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1,
+ attr->index, data->pwm_settings[attr->index],
+ 5) < 1)) {
+ data->pwm_settings[attr->index][0] = orig_val;
+ ret = -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", ABIT_UGURU_NAME);
+}
+
+/* Sysfs attr templates, the real entries are generated automatically. */
+static const
+struct sensor_device_attribute_2 abituguru_sysfs_bank1_templ[2][9] = {
+ {
+ SENSOR_ATTR_2(in%d_input, 0444, show_bank1_value, NULL, 0, 0),
+ SENSOR_ATTR_2(in%d_min, 0644, show_bank1_setting,
+ store_bank1_setting, 1, 0),
+ SENSOR_ATTR_2(in%d_min_alarm, 0444, show_bank1_alarm, NULL,
+ ABIT_UGURU_VOLT_LOW_ALARM_FLAG, 0),
+ SENSOR_ATTR_2(in%d_max, 0644, show_bank1_setting,
+ store_bank1_setting, 2, 0),
+ SENSOR_ATTR_2(in%d_max_alarm, 0444, show_bank1_alarm, NULL,
+ ABIT_UGURU_VOLT_HIGH_ALARM_FLAG, 0),
+ SENSOR_ATTR_2(in%d_beep, 0644, show_bank1_mask,
+ store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0),
+ SENSOR_ATTR_2(in%d_shutdown, 0644, show_bank1_mask,
+ store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
+ SENSOR_ATTR_2(in%d_min_alarm_enable, 0644, show_bank1_mask,
+ store_bank1_mask, ABIT_UGURU_VOLT_LOW_ALARM_ENABLE, 0),
+ SENSOR_ATTR_2(in%d_max_alarm_enable, 0644, show_bank1_mask,
+ store_bank1_mask, ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE, 0),
+ }, {
+ SENSOR_ATTR_2(temp%d_input, 0444, show_bank1_value, NULL, 0, 0),
+ SENSOR_ATTR_2(temp%d_alarm, 0444, show_bank1_alarm, NULL,
+ ABIT_UGURU_TEMP_HIGH_ALARM_FLAG, 0),
+ SENSOR_ATTR_2(temp%d_max, 0644, show_bank1_setting,
+ store_bank1_setting, 1, 0),
+ SENSOR_ATTR_2(temp%d_crit, 0644, show_bank1_setting,
+ store_bank1_setting, 2, 0),
+ SENSOR_ATTR_2(temp%d_beep, 0644, show_bank1_mask,
+ store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0),
+ SENSOR_ATTR_2(temp%d_shutdown, 0644, show_bank1_mask,
+ store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
+ SENSOR_ATTR_2(temp%d_alarm_enable, 0644, show_bank1_mask,
+ store_bank1_mask, ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE, 0),
+ }
+};
+
+static const struct sensor_device_attribute_2 abituguru_sysfs_fan_templ[6] = {
+ SENSOR_ATTR_2(fan%d_input, 0444, show_bank2_value, NULL, 0, 0),
+ SENSOR_ATTR_2(fan%d_alarm, 0444, show_bank2_alarm, NULL, 0, 0),
+ SENSOR_ATTR_2(fan%d_min, 0644, show_bank2_setting,
+ store_bank2_setting, 1, 0),
+ SENSOR_ATTR_2(fan%d_beep, 0644, show_bank2_mask,
+ store_bank2_mask, ABIT_UGURU_BEEP_ENABLE, 0),
+ SENSOR_ATTR_2(fan%d_shutdown, 0644, show_bank2_mask,
+ store_bank2_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0),
+ SENSOR_ATTR_2(fan%d_alarm_enable, 0644, show_bank2_mask,
+ store_bank2_mask, ABIT_UGURU_FAN_LOW_ALARM_ENABLE, 0),
+};
+
+static const struct sensor_device_attribute_2 abituguru_sysfs_pwm_templ[6] = {
+ SENSOR_ATTR_2(pwm%d_enable, 0644, show_pwm_enable,
+ store_pwm_enable, 0, 0),
+ SENSOR_ATTR_2(pwm%d_auto_channels_temp, 0644, show_pwm_sensor,
+ store_pwm_sensor, 0, 0),
+ SENSOR_ATTR_2(pwm%d_auto_point1_pwm, 0644, show_pwm_setting,
+ store_pwm_setting, 1, 0),
+ SENSOR_ATTR_2(pwm%d_auto_point2_pwm, 0644, show_pwm_setting,
+ store_pwm_setting, 2, 0),
+ SENSOR_ATTR_2(pwm%d_auto_point1_temp, 0644, show_pwm_setting,
+ store_pwm_setting, 3, 0),
+ SENSOR_ATTR_2(pwm%d_auto_point2_temp, 0644, show_pwm_setting,
+ store_pwm_setting, 4, 0),
+};
+
+static struct sensor_device_attribute_2 abituguru_sysfs_attr[] = {
+ SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0),
+};
+
+static int abituguru_probe(struct platform_device *pdev)
+{
+ struct abituguru_data *data;
+ int i, j, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV;
+ char *sysfs_filename;
+
+ /*
+ * El weirdo probe order, to keep the sysfs order identical to the
+ * BIOS and window-appliction listing order.
+ */
+ static const u8 probe_order[ABIT_UGURU_MAX_BANK1_SENSORS] = {
+ 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02,
+ 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C };
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct abituguru_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ /* See if the uGuru is ready */
+ if (inb_p(data->addr + ABIT_UGURU_DATA) == ABIT_UGURU_STATUS_INPUT)
+ data->uguru_ready = 1;
+
+ /*
+ * Completely read the uGuru this has 2 purposes:
+ * - testread / see if one really is there.
+ * - make an in memory copy of all the uguru settings for future use.
+ */
+ if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
+ data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3)
+ goto abituguru_probe_error;
+
+ for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
+ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, i,
+ &data->bank1_value[i], 1,
+ ABIT_UGURU_MAX_RETRIES) != 1)
+ goto abituguru_probe_error;
+ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1+1, i,
+ data->bank1_settings[i], 3,
+ ABIT_UGURU_MAX_RETRIES) != 3)
+ goto abituguru_probe_error;
+ }
+ /*
+ * Note: We don't know how many bank2 sensors / pwms there really are,
+ * but in order to "detect" this we need to read the maximum amount
+ * anyways. If we read sensors/pwms not there we'll just read crap
+ * this can't hurt. We need the detection because we don't want
+ * unwanted writes, which will hurt!
+ */
+ for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) {
+ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i,
+ &data->bank2_value[i], 1,
+ ABIT_UGURU_MAX_RETRIES) != 1)
+ goto abituguru_probe_error;
+ if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2+1, i,
+ data->bank2_settings[i], 2,
+ ABIT_UGURU_MAX_RETRIES) != 2)
+ goto abituguru_probe_error;
+ }
+ for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) {
+ if (abituguru_read(data, ABIT_UGURU_FAN_PWM, i,
+ data->pwm_settings[i], 5,
+ ABIT_UGURU_MAX_RETRIES) != 5)
+ goto abituguru_probe_error;
+ }
+ data->last_updated = jiffies;
+
+ /* Detect sensor types and fill the sysfs attr for bank1 */
+ sysfs_attr_i = 0;
+ sysfs_filename = data->sysfs_names;
+ sysfs_names_free = ABITUGURU_SYSFS_NAMES_LENGTH;
+ for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
+ res = abituguru_detect_bank1_sensor_type(data, probe_order[i]);
+ if (res < 0)
+ goto abituguru_probe_error;
+ if (res == ABIT_UGURU_NC)
+ continue;
+
+ /* res 1 (temp) sensors have 7 sysfs entries, 0 (in) 9 */
+ for (j = 0; j < (res ? 7 : 9); j++) {
+ used = snprintf(sysfs_filename, sysfs_names_free,
+ abituguru_sysfs_bank1_templ[res][j].dev_attr.
+ attr.name, data->bank1_sensors[res] + res)
+ + 1;
+ data->sysfs_attr[sysfs_attr_i] =
+ abituguru_sysfs_bank1_templ[res][j];
+ data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
+ sysfs_filename;
+ data->sysfs_attr[sysfs_attr_i].index = probe_order[i];
+ sysfs_filename += used;
+ sysfs_names_free -= used;
+ sysfs_attr_i++;
+ }
+ data->bank1_max_value[probe_order[i]] =
+ abituguru_bank1_max_value[res];
+ data->bank1_address[res][data->bank1_sensors[res]] =
+ probe_order[i];
+ data->bank1_sensors[res]++;
+ }
+ /* Detect number of sensors and fill the sysfs attr for bank2 (fans) */
+ abituguru_detect_no_bank2_sensors(data);
+ for (i = 0; i < data->bank2_sensors; i++) {
+ for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_fan_templ); j++) {
+ used = snprintf(sysfs_filename, sysfs_names_free,
+ abituguru_sysfs_fan_templ[j].dev_attr.attr.name,
+ i + 1) + 1;
+ data->sysfs_attr[sysfs_attr_i] =
+ abituguru_sysfs_fan_templ[j];
+ data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
+ sysfs_filename;
+ data->sysfs_attr[sysfs_attr_i].index = i;
+ sysfs_filename += used;
+ sysfs_names_free -= used;
+ sysfs_attr_i++;
+ }
+ }
+ /* Detect number of sensors and fill the sysfs attr for pwms */
+ abituguru_detect_no_pwms(data);
+ for (i = 0; i < data->pwms; i++) {
+ for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_pwm_templ); j++) {
+ used = snprintf(sysfs_filename, sysfs_names_free,
+ abituguru_sysfs_pwm_templ[j].dev_attr.attr.name,
+ i + 1) + 1;
+ data->sysfs_attr[sysfs_attr_i] =
+ abituguru_sysfs_pwm_templ[j];
+ data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
+ sysfs_filename;
+ data->sysfs_attr[sysfs_attr_i].index = i;
+ sysfs_filename += used;
+ sysfs_names_free -= used;
+ sysfs_attr_i++;
+ }
+ }
+ /* Fail safe check, this should never happen! */
+ if (sysfs_names_free < 0) {
+ pr_err("Fatal error ran out of space for sysfs attr names. %s %s",
+ never_happen, report_this);
+ res = -ENAMETOOLONG;
+ goto abituguru_probe_error;
+ }
+ pr_info("found Abit uGuru\n");
+
+ /* Register sysfs hooks */
+ for (i = 0; i < sysfs_attr_i; i++) {
+ res = device_create_file(&pdev->dev,
+ &data->sysfs_attr[i].dev_attr);
+ if (res)
+ goto abituguru_probe_error;
+ }
+ for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) {
+ res = device_create_file(&pdev->dev,
+ &abituguru_sysfs_attr[i].dev_attr);
+ if (res)
+ goto abituguru_probe_error;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (!IS_ERR(data->hwmon_dev))
+ return 0; /* success */
+
+ res = PTR_ERR(data->hwmon_dev);
+abituguru_probe_error:
+ for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
+ device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
+ device_remove_file(&pdev->dev,
+ &abituguru_sysfs_attr[i].dev_attr);
+ return res;
+}
+
+static int abituguru_remove(struct platform_device *pdev)
+{
+ int i;
+ struct abituguru_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
+ device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
+ device_remove_file(&pdev->dev,
+ &abituguru_sysfs_attr[i].dev_attr);
+
+ return 0;
+}
+
+static struct abituguru_data *abituguru_update_device(struct device *dev)
+{
+ int i, err;
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ /* fake a complete successful read if no update necessary. */
+ char success = 1;
+
+ mutex_lock(&data->update_lock);
+ if (time_after(jiffies, data->last_updated + HZ)) {
+ success = 0;
+ err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0,
+ data->alarms, 3, 0);
+ if (err != 3)
+ goto LEAVE_UPDATE;
+ for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) {
+ err = abituguru_read(data, ABIT_UGURU_SENSOR_BANK1,
+ i, &data->bank1_value[i], 1, 0);
+ if (err != 1)
+ goto LEAVE_UPDATE;
+ err = abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1,
+ i, data->bank1_settings[i], 3, 0);
+ if (err != 3)
+ goto LEAVE_UPDATE;
+ }
+ for (i = 0; i < data->bank2_sensors; i++) {
+ err = abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i,
+ &data->bank2_value[i], 1, 0);
+ if (err != 1)
+ goto LEAVE_UPDATE;
+ }
+ /* success! */
+ success = 1;
+ data->update_timeouts = 0;
+LEAVE_UPDATE:
+ /* handle timeout condition */
+ if (!success && (err == -EBUSY || err >= 0)) {
+ /* No overflow please */
+ if (data->update_timeouts < 255u)
+ data->update_timeouts++;
+ if (data->update_timeouts <= ABIT_UGURU_MAX_TIMEOUTS) {
+ ABIT_UGURU_DEBUG(3, "timeout exceeded, will "
+ "try again next update\n");
+ /* Just a timeout, fake a successful read */
+ success = 1;
+ } else
+ ABIT_UGURU_DEBUG(1, "timeout exceeded %d "
+ "times waiting for more input state\n",
+ (int)data->update_timeouts);
+ }
+ /* On success set last_updated */
+ if (success)
+ data->last_updated = jiffies;
+ }
+ mutex_unlock(&data->update_lock);
+
+ if (success)
+ return data;
+ else
+ return NULL;
+}
+
+static int abituguru_suspend(struct device *dev)
+{
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ /*
+ * make sure all communications with the uguru are done and no new
+ * ones are started
+ */
+ mutex_lock(&data->update_lock);
+ return 0;
+}
+
+static int abituguru_resume(struct device *dev)
+{
+ struct abituguru_data *data = dev_get_drvdata(dev);
+ /* See if the uGuru is still ready */
+ if (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT)
+ data->uguru_ready = 0;
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume);
+
+static struct platform_driver abituguru_driver = {
+ .driver = {
+ .name = ABIT_UGURU_NAME,
+ .pm = pm_sleep_ptr(&abituguru_pm),
+ },
+ .probe = abituguru_probe,
+ .remove = abituguru_remove,
+};
+
+static int __init abituguru_detect(void)
+{
+ /*
+ * See if there is an uguru there. After a reboot uGuru will hold 0x00
+ * at DATA and 0xAC, when this driver has already been loaded once
+ * DATA will hold 0x08. For most uGuru's CMD will hold 0xAC in either
+ * scenario but some will hold 0x00.
+ * Some uGuru's initially hold 0x09 at DATA and will only hold 0x08
+ * after reading CMD first, so CMD must be read first!
+ */
+ u8 cmd_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_CMD);
+ u8 data_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_DATA);
+ if (((data_val == 0x00) || (data_val == 0x08)) &&
+ ((cmd_val == 0x00) || (cmd_val == 0xAC)))
+ return ABIT_UGURU_BASE;
+
+ ABIT_UGURU_DEBUG(2, "no Abit uGuru found, data = 0x%02X, cmd = "
+ "0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val);
+
+ if (force) {
+ pr_info("Assuming Abit uGuru is present because of \"force\" parameter\n");
+ return ABIT_UGURU_BASE;
+ }
+
+ /* No uGuru found */
+ return -ENODEV;
+}
+
+static struct platform_device *abituguru_pdev;
+
+static int __init abituguru_init(void)
+{
+ int address, err;
+ struct resource res = { .flags = IORESOURCE_IO };
+ const char *board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+
+ /* safety check, refuse to load on non Abit motherboards */
+ if (!force && (!board_vendor ||
+ strcmp(board_vendor, "http://www.abit.com.tw/")))
+ return -ENODEV;
+
+ address = abituguru_detect();
+ if (address < 0)
+ return address;
+
+ err = platform_driver_register(&abituguru_driver);
+ if (err)
+ goto exit;
+
+ abituguru_pdev = platform_device_alloc(ABIT_UGURU_NAME, address);
+ if (!abituguru_pdev) {
+ pr_err("Device allocation failed\n");
+ err = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ res.start = address;
+ res.end = address + ABIT_UGURU_REGION_LENGTH - 1;
+ res.name = ABIT_UGURU_NAME;
+
+ err = platform_device_add_resources(abituguru_pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(abituguru_pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(abituguru_pdev);
+exit_driver_unregister:
+ platform_driver_unregister(&abituguru_driver);
+exit:
+ return err;
+}
+
+static void __exit abituguru_exit(void)
+{
+ platform_device_unregister(abituguru_pdev);
+ platform_driver_unregister(&abituguru_driver);
+}
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Abit uGuru Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(abituguru_init);
+module_exit(abituguru_exit);
diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c
new file mode 100644
index 000000000..afb21f730
--- /dev/null
+++ b/drivers/hwmon/abituguru3.c
@@ -0,0 +1,1303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * abituguru3.c
+ *
+ * Copyright (c) 2006-2008 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (c) 2008 Alistair John Strachan <alistair@devzero.co.uk>
+ */
+/*
+ * This driver supports the sensor part of revision 3 of the custom Abit uGuru
+ * chip found on newer Abit uGuru motherboards. Note: because of lack of specs
+ * only reading the sensors and their settings is supported.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+
+/* uGuru3 bank addresses */
+#define ABIT_UGURU3_SETTINGS_BANK 0x01
+#define ABIT_UGURU3_SENSORS_BANK 0x08
+#define ABIT_UGURU3_MISC_BANK 0x09
+#define ABIT_UGURU3_ALARMS_START 0x1E
+#define ABIT_UGURU3_SETTINGS_START 0x24
+#define ABIT_UGURU3_VALUES_START 0x80
+#define ABIT_UGURU3_BOARD_ID 0x0A
+/* uGuru3 sensor bank flags */ /* Alarm if: */
+#define ABIT_UGURU3_TEMP_HIGH_ALARM_ENABLE 0x01 /* temp over warn */
+#define ABIT_UGURU3_VOLT_HIGH_ALARM_ENABLE 0x02 /* volt over max */
+#define ABIT_UGURU3_VOLT_LOW_ALARM_ENABLE 0x04 /* volt under min */
+#define ABIT_UGURU3_TEMP_HIGH_ALARM_FLAG 0x10 /* temp is over warn */
+#define ABIT_UGURU3_VOLT_HIGH_ALARM_FLAG 0x20 /* volt is over max */
+#define ABIT_UGURU3_VOLT_LOW_ALARM_FLAG 0x40 /* volt is under min */
+#define ABIT_UGURU3_FAN_LOW_ALARM_ENABLE 0x01 /* fan under min */
+#define ABIT_UGURU3_BEEP_ENABLE 0x08 /* beep if alarm */
+#define ABIT_UGURU3_SHUTDOWN_ENABLE 0x80 /* shutdown if alarm */
+/* sensor types */
+#define ABIT_UGURU3_IN_SENSOR 0
+#define ABIT_UGURU3_TEMP_SENSOR 1
+#define ABIT_UGURU3_FAN_SENSOR 2
+
+/*
+ * Timeouts / Retries, if these turn out to need a lot of fiddling we could
+ * convert them to params. Determined by trial and error. I assume this is
+ * cpu-speed independent, since the ISA-bus and not the CPU should be the
+ * bottleneck.
+ */
+#define ABIT_UGURU3_WAIT_TIMEOUT 250
+/*
+ * Normally the 0xAC at the end of synchronize() is reported after the
+ * first read, but sometimes not and we need to poll
+ */
+#define ABIT_UGURU3_SYNCHRONIZE_TIMEOUT 5
+/* utility macros */
+#define ABIT_UGURU3_NAME "abituguru3"
+#define ABIT_UGURU3_DEBUG(format, arg...) \
+ do { \
+ if (verbose) \
+ pr_debug(format , ## arg); \
+ } while (0)
+
+/* Macros to help calculate the sysfs_names array length */
+#define ABIT_UGURU3_MAX_NO_SENSORS 26
+/*
+ * sum of strlen +1 of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0,
+ * in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0, in??_label\0
+ */
+#define ABIT_UGURU3_IN_NAMES_LENGTH \
+ (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14 + 11)
+/*
+ * sum of strlen +1 of: temp??_input\0, temp??_max\0, temp??_crit\0,
+ * temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0,
+ * temp??_label\0
+ */
+#define ABIT_UGURU3_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16 + 13)
+/*
+ * sum of strlen +1 of: fan??_input\0, fan??_min\0, fan??_alarm\0,
+ * fan??_alarm_enable\0, fan??_beep\0, fan??_shutdown\0, fan??_label\0
+ */
+#define ABIT_UGURU3_FAN_NAMES_LENGTH (12 + 10 + 12 + 19 + 11 + 15 + 12)
+/*
+ * Worst case scenario 16 in sensors (longest names_length) and the rest
+ * temp sensors (second longest names_length).
+ */
+#define ABIT_UGURU3_SYSFS_NAMES_LENGTH (16 * ABIT_UGURU3_IN_NAMES_LENGTH + \
+ (ABIT_UGURU3_MAX_NO_SENSORS - 16) * ABIT_UGURU3_TEMP_NAMES_LENGTH)
+
+/*
+ * All the macros below are named identical to the openguru2 program
+ * reverse engineered by Louis Kruger, hence the names might not be 100%
+ * logical. I could come up with better names, but I prefer keeping the names
+ * identical so that this driver can be compared with his work more easily.
+ */
+/* Two i/o-ports are used by uGuru */
+#define ABIT_UGURU3_BASE 0x00E0
+#define ABIT_UGURU3_CMD 0x00
+#define ABIT_UGURU3_DATA 0x04
+#define ABIT_UGURU3_REGION_LENGTH 5
+/*
+ * The wait_xxx functions return this on success and the last contents
+ * of the DATA register (0-255) on failure.
+ */
+#define ABIT_UGURU3_SUCCESS -1
+/* uGuru status flags */
+#define ABIT_UGURU3_STATUS_READY_FOR_READ 0x01
+#define ABIT_UGURU3_STATUS_BUSY 0x02
+
+
+/* Structures */
+struct abituguru3_sensor_info {
+ const char *name;
+ int port;
+ int type;
+ int multiplier;
+ int divisor;
+ int offset;
+};
+
+/* Avoid use of flexible array members */
+#define ABIT_UGURU3_MAX_DMI_NAMES 2
+
+struct abituguru3_motherboard_info {
+ u16 id;
+ const char *dmi_name[ABIT_UGURU3_MAX_DMI_NAMES + 1];
+ /* + 1 -> end of sensors indicated by a sensor with name == NULL */
+ struct abituguru3_sensor_info sensors[ABIT_UGURU3_MAX_NO_SENSORS + 1];
+};
+
+/*
+ * For the Abit uGuru, we need to keep some data in memory.
+ * The structure is dynamically allocated, at the same time when a new
+ * abituguru3 device is allocated.
+ */
+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 */
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /*
+ * For convenience the sysfs attr and their names are generated
+ * automatically. We have max 10 entries per sensor (for in sensors)
+ */
+ struct sensor_device_attribute_2 sysfs_attr[ABIT_UGURU3_MAX_NO_SENSORS
+ * 10];
+
+ /* Buffer to store the dynamically generated sysfs names */
+ char sysfs_names[ABIT_UGURU3_SYSFS_NAMES_LENGTH];
+
+ /* Pointer to the sensors info for the detected motherboard */
+ const struct abituguru3_sensor_info *sensors;
+
+ /*
+ * The abituguru3 supports up to 48 sensors, and thus has registers
+ * sets for 48 sensors, for convenience reasons / simplicity of the
+ * code we always read and store all registers for all 48 sensors
+ */
+
+ /* Alarms for all 48 sensors (1 bit per sensor) */
+ u8 alarms[48/8];
+
+ /* Value of all 48 sensors */
+ u8 value[48];
+
+ /*
+ * Settings of all 48 sensors, note in and temp sensors (the first 32
+ * sensors) have 3 bytes of settings, while fans only have 2 bytes,
+ * for convenience we use 3 bytes for all sensors
+ */
+ u8 settings[48][3];
+};
+
+
+/* Constants */
+static const struct abituguru3_motherboard_info abituguru3_motherboards[] = {
+ { 0x000C, { NULL } /* Unknown, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 10, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH & PCIE 1.5V", 4, 0, 10, 1, 0 },
+ { "MCH 2.5V", 5, 0, 20, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS FAN", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 35, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x000D, { NULL } /* Abit AW8, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 10, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH & PCIE 1.5V", 4, 0, 10, 1, 0 },
+ { "MCH 2.5V", 5, 0, 20, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM1", 26, 1, 1, 1, 0 },
+ { "PWM2", 27, 1, 1, 1, 0 },
+ { "PWM3", 28, 1, 1, 1, 0 },
+ { "PWM4", 29, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 35, 2, 60, 1, 0 },
+ { "AUX2 Fan", 36, 2, 60, 1, 0 },
+ { "AUX3 Fan", 37, 2, 60, 1, 0 },
+ { "AUX4 Fan", 38, 2, 60, 1, 0 },
+ { "AUX5 Fan", 39, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x000E, { NULL } /* AL-8, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 10, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH & PCIE 1.5V", 4, 0, 10, 1, 0 },
+ { "MCH 2.5V", 5, 0, 20, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x000F, { NULL } /* Unknown, need DMI string */, {
+
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 10, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH & PCIE 1.5V", 4, 0, 10, 1, 0 },
+ { "MCH 2.5V", 5, 0, 20, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0010, { NULL } /* Abit NI8 SLI GR, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 10, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "NB 1.4V", 4, 0, 10, 1, 0 },
+ { "SB 1.5V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "SYS", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 35, 2, 60, 1, 0 },
+ { "OTES1 Fan", 36, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0011, { "AT8 32X", NULL }, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 20, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VDDA 2.5V", 6, 0, 20, 1, 0 },
+ { "NB 1.8V", 4, 0, 10, 1, 0 },
+ { "NB 1.8V Dual", 5, 0, 10, 1, 0 },
+ { "HTV 1.2", 3, 0, 10, 1, 0 },
+ { "PCIE 1.2V", 12, 0, 10, 1, 0 },
+ { "NB 1.2V", 13, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "NB", 25, 1, 1, 1, 0 },
+ { "System", 26, 1, 1, 1, 0 },
+ { "PWM", 27, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 35, 2, 60, 1, 0 },
+ { "AUX2 Fan", 36, 2, 60, 1, 0 },
+ { "AUX3 Fan", 37, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0012, { NULL } /* Abit AN8 32X, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 20, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "HyperTransport", 3, 0, 10, 1, 0 },
+ { "CPU VDDA 2.5V", 5, 0, 20, 1, 0 },
+ { "NB", 4, 0, 10, 1, 0 },
+ { "SB", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "SYS", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 36, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0013, { NULL } /* Abit AW8D, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 10, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH & PCIE 1.5V", 4, 0, 10, 1, 0 },
+ { "MCH 2.5V", 5, 0, 20, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM1", 26, 1, 1, 1, 0 },
+ { "PWM2", 27, 1, 1, 1, 0 },
+ { "PWM3", 28, 1, 1, 1, 0 },
+ { "PWM4", 29, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 35, 2, 60, 1, 0 },
+ { "AUX2 Fan", 36, 2, 60, 1, 0 },
+ { "AUX3 Fan", 37, 2, 60, 1, 0 },
+ { "AUX4 Fan", 38, 2, 60, 1, 0 },
+ { "AUX5 Fan", 39, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0014, { "AB9", "AB9 Pro", NULL }, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 10, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH & PCIE 1.5V", 4, 0, 10, 1, 0 },
+ { "MCH 2.5V", 5, 0, 20, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0015, { NULL } /* Unknown, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR", 1, 0, 20, 1, 0 },
+ { "DDR VTT", 2, 0, 10, 1, 0 },
+ { "HyperTransport", 3, 0, 10, 1, 0 },
+ { "CPU VDDA 2.5V", 5, 0, 20, 1, 0 },
+ { "NB", 4, 0, 10, 1, 0 },
+ { "SB", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "SYS", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 33, 2, 60, 1, 0 },
+ { "AUX2 Fan", 35, 2, 60, 1, 0 },
+ { "AUX3 Fan", 36, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0016, { "AW9D-MAX", NULL }, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR2", 1, 0, 20, 1, 0 },
+ { "DDR2 VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH & PCIE 1.5V", 4, 0, 10, 1, 0 },
+ { "MCH 2.5V", 5, 0, 20, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM1", 26, 1, 1, 1, 0 },
+ { "PWM2", 27, 1, 1, 1, 0 },
+ { "PWM3", 28, 1, 1, 1, 0 },
+ { "PWM4", 29, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "NB Fan", 33, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 35, 2, 60, 1, 0 },
+ { "AUX2 Fan", 36, 2, 60, 1, 0 },
+ { "AUX3 Fan", 37, 2, 60, 1, 0 },
+ { "OTES1 Fan", 38, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0017, { NULL } /* Unknown, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR2", 1, 0, 20, 1, 0 },
+ { "DDR2 VTT", 2, 0, 10, 1, 0 },
+ { "HyperTransport", 3, 0, 10, 1, 0 },
+ { "CPU VDDA 2.5V", 6, 0, 20, 1, 0 },
+ { "NB 1.8V", 4, 0, 10, 1, 0 },
+ { "NB 1.2V ", 13, 0, 10, 1, 0 },
+ { "SB 1.2V", 5, 0, 10, 1, 0 },
+ { "PCIE 1.2V", 12, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "ATX +3.3V", 10, 0, 20, 1, 0 },
+ { "ATX 5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 26, 1, 1, 1, 0 },
+ { "PWM", 27, 1, 1, 1, 0 },
+ { "CPU FAN", 32, 2, 60, 1, 0 },
+ { "SYS FAN", 34, 2, 60, 1, 0 },
+ { "AUX1 FAN", 35, 2, 60, 1, 0 },
+ { "AUX2 FAN", 36, 2, 60, 1, 0 },
+ { "AUX3 FAN", 37, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0018, { "AB9 QuadGT", NULL }, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR2", 1, 0, 20, 1, 0 },
+ { "DDR2 VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT", 3, 0, 10, 1, 0 },
+ { "MCH 1.25V", 4, 0, 10, 1, 0 },
+ { "ICHIO 1.5V", 5, 0, 10, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM Phase1", 26, 1, 1, 1, 0 },
+ { "PWM Phase2", 27, 1, 1, 1, 0 },
+ { "PWM Phase3", 28, 1, 1, 1, 0 },
+ { "PWM Phase4", 29, 1, 1, 1, 0 },
+ { "PWM Phase5", 30, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 33, 2, 60, 1, 0 },
+ { "AUX2 Fan", 35, 2, 60, 1, 0 },
+ { "AUX3 Fan", 36, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0019, { "IN9 32X MAX", NULL }, {
+ { "CPU Core", 7, 0, 10, 1, 0 },
+ { "DDR2", 13, 0, 20, 1, 0 },
+ { "DDR2 VTT", 14, 0, 10, 1, 0 },
+ { "CPU VTT", 3, 0, 20, 1, 0 },
+ { "NB 1.2V", 4, 0, 10, 1, 0 },
+ { "SB 1.5V", 6, 0, 10, 1, 0 },
+ { "HyperTransport", 5, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 12, 0, 60, 1, 0 },
+ { "ATX +12V (4-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "ATX +3.3V", 10, 0, 20, 1, 0 },
+ { "ATX 5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM Phase1", 26, 1, 1, 1, 0 },
+ { "PWM Phase2", 27, 1, 1, 1, 0 },
+ { "PWM Phase3", 28, 1, 1, 1, 0 },
+ { "PWM Phase4", 29, 1, 1, 1, 0 },
+ { "PWM Phase5", 30, 1, 1, 1, 0 },
+ { "CPU FAN", 32, 2, 60, 1, 0 },
+ { "SYS FAN", 34, 2, 60, 1, 0 },
+ { "AUX1 FAN", 33, 2, 60, 1, 0 },
+ { "AUX2 FAN", 35, 2, 60, 1, 0 },
+ { "AUX3 FAN", 36, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x001A, { "IP35 Pro", "IP35 Pro XE", NULL }, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR2", 1, 0, 20, 1, 0 },
+ { "DDR2 VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT 1.2V", 3, 0, 10, 1, 0 },
+ { "MCH 1.25V", 4, 0, 10, 1, 0 },
+ { "ICHIO 1.5V", 5, 0, 10, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (8-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM", 26, 1, 1, 1, 0 },
+ { "PWM Phase2", 27, 1, 1, 1, 0 },
+ { "PWM Phase3", 28, 1, 1, 1, 0 },
+ { "PWM Phase4", 29, 1, 1, 1, 0 },
+ { "PWM Phase5", 30, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 33, 2, 60, 1, 0 },
+ { "AUX2 Fan", 35, 2, 60, 1, 0 },
+ { "AUX3 Fan", 36, 2, 60, 1, 0 },
+ { "AUX4 Fan", 37, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x001B, { NULL } /* Unknown, need DMI string */, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR3", 1, 0, 20, 1, 0 },
+ { "DDR3 VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT", 3, 0, 10, 1, 0 },
+ { "MCH 1.25V", 4, 0, 10, 1, 0 },
+ { "ICHIO 1.5V", 5, 0, 10, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (8-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM Phase1", 26, 1, 1, 1, 0 },
+ { "PWM Phase2", 27, 1, 1, 1, 0 },
+ { "PWM Phase3", 28, 1, 1, 1, 0 },
+ { "PWM Phase4", 29, 1, 1, 1, 0 },
+ { "PWM Phase5", 30, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 33, 2, 60, 1, 0 },
+ { "AUX2 Fan", 35, 2, 60, 1, 0 },
+ { "AUX3 Fan", 36, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x001C, { "IX38 QuadGT", NULL }, {
+ { "CPU Core", 0, 0, 10, 1, 0 },
+ { "DDR2", 1, 0, 20, 1, 0 },
+ { "DDR2 VTT", 2, 0, 10, 1, 0 },
+ { "CPU VTT", 3, 0, 10, 1, 0 },
+ { "MCH 1.25V", 4, 0, 10, 1, 0 },
+ { "ICHIO 1.5V", 5, 0, 10, 1, 0 },
+ { "ICH 1.05V", 6, 0, 10, 1, 0 },
+ { "ATX +12V (24-Pin)", 7, 0, 60, 1, 0 },
+ { "ATX +12V (8-pin)", 8, 0, 60, 1, 0 },
+ { "ATX +5V", 9, 0, 30, 1, 0 },
+ { "+3.3V", 10, 0, 20, 1, 0 },
+ { "5VSB", 11, 0, 30, 1, 0 },
+ { "CPU", 24, 1, 1, 1, 0 },
+ { "System", 25, 1, 1, 1, 0 },
+ { "PWM Phase1", 26, 1, 1, 1, 0 },
+ { "PWM Phase2", 27, 1, 1, 1, 0 },
+ { "PWM Phase3", 28, 1, 1, 1, 0 },
+ { "PWM Phase4", 29, 1, 1, 1, 0 },
+ { "PWM Phase5", 30, 1, 1, 1, 0 },
+ { "CPU Fan", 32, 2, 60, 1, 0 },
+ { "SYS Fan", 34, 2, 60, 1, 0 },
+ { "AUX1 Fan", 33, 2, 60, 1, 0 },
+ { "AUX2 Fan", 35, 2, 60, 1, 0 },
+ { "AUX3 Fan", 36, 2, 60, 1, 0 },
+ { NULL, 0, 0, 0, 0, 0 } }
+ },
+ { 0x0000, { NULL }, { { NULL, 0, 0, 0, 0, 0 } } }
+};
+
+
+/* Insmod parameters */
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Set to one to force detection.");
+/* Default verbose is 1, since this driver is still in the testing phase */
+static bool verbose = 1;
+module_param(verbose, bool, 0644);
+MODULE_PARM_DESC(verbose, "Enable/disable verbose error reporting");
+
+static const char *never_happen = "This should never happen.";
+static const char *report_this =
+ "Please report this to the abituguru3 maintainer (see MAINTAINERS)";
+
+/* wait while the uguru is busy (usually after a write) */
+static int abituguru3_wait_while_busy(struct abituguru3_data *data)
+{
+ u8 x;
+ int timeout = ABIT_UGURU3_WAIT_TIMEOUT;
+
+ while ((x = inb_p(data->addr + ABIT_UGURU3_DATA)) &
+ ABIT_UGURU3_STATUS_BUSY) {
+ timeout--;
+ if (timeout == 0)
+ return x;
+ /*
+ * sleep a bit before our last try, to give the uGuru3 one
+ * last chance to respond.
+ */
+ if (timeout == 1)
+ msleep(1);
+ }
+ return ABIT_UGURU3_SUCCESS;
+}
+
+/* wait till uguru is ready to be read */
+static int abituguru3_wait_for_read(struct abituguru3_data *data)
+{
+ u8 x;
+ int timeout = ABIT_UGURU3_WAIT_TIMEOUT;
+
+ while (!((x = inb_p(data->addr + ABIT_UGURU3_DATA)) &
+ ABIT_UGURU3_STATUS_READY_FOR_READ)) {
+ timeout--;
+ if (timeout == 0)
+ return x;
+ /*
+ * sleep a bit before our last try, to give the uGuru3 one
+ * last chance to respond.
+ */
+ if (timeout == 1)
+ msleep(1);
+ }
+ return ABIT_UGURU3_SUCCESS;
+}
+
+/*
+ * This synchronizes us with the uGuru3's protocol state machine, this
+ * must be done before each command.
+ */
+static int abituguru3_synchronize(struct abituguru3_data *data)
+{
+ int x, timeout = ABIT_UGURU3_SYNCHRONIZE_TIMEOUT;
+
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("synchronize timeout during initial busy "
+ "wait, status: 0x%02x\n", x);
+ return -EIO;
+ }
+
+ outb(0x20, data->addr + ABIT_UGURU3_DATA);
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x20, "
+ "status: 0x%02x\n", x);
+ return -EIO;
+ }
+
+ outb(0x10, data->addr + ABIT_UGURU3_CMD);
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x10, "
+ "status: 0x%02x\n", x);
+ return -EIO;
+ }
+
+ outb(0x00, data->addr + ABIT_UGURU3_CMD);
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x00, "
+ "status: 0x%02x\n", x);
+ return -EIO;
+ }
+
+ x = abituguru3_wait_for_read(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("synchronize timeout waiting for read, "
+ "status: 0x%02x\n", x);
+ return -EIO;
+ }
+
+ while ((x = inb(data->addr + ABIT_UGURU3_CMD)) != 0xAC) {
+ timeout--;
+ if (timeout == 0) {
+ ABIT_UGURU3_DEBUG("synchronize timeout cmd does not "
+ "hold 0xAC after synchronize, cmd: 0x%02x\n",
+ x);
+ return -EIO;
+ }
+ msleep(1);
+ }
+ return 0;
+}
+
+/*
+ * Read count bytes from sensor sensor_addr in bank bank_addr and store the
+ * result in buf
+ */
+static int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset,
+ u8 count, u8 *buf)
+{
+ int i, x;
+
+ x = abituguru3_synchronize(data);
+ if (x)
+ return x;
+
+ outb(0x1A, data->addr + ABIT_UGURU3_DATA);
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
+ "sending 0x1A, status: 0x%02x\n", (unsigned int)bank,
+ (unsigned int)offset, x);
+ return -EIO;
+ }
+
+ outb(bank, data->addr + ABIT_UGURU3_CMD);
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
+ "sending the bank, status: 0x%02x\n",
+ (unsigned int)bank, (unsigned int)offset, x);
+ return -EIO;
+ }
+
+ outb(offset, data->addr + ABIT_UGURU3_CMD);
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
+ "sending the offset, status: 0x%02x\n",
+ (unsigned int)bank, (unsigned int)offset, x);
+ return -EIO;
+ }
+
+ outb(count, data->addr + ABIT_UGURU3_CMD);
+ x = abituguru3_wait_while_busy(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after "
+ "sending the count, status: 0x%02x\n",
+ (unsigned int)bank, (unsigned int)offset, x);
+ return -EIO;
+ }
+
+ for (i = 0; i < count; i++) {
+ x = abituguru3_wait_for_read(data);
+ if (x != ABIT_UGURU3_SUCCESS) {
+ ABIT_UGURU3_DEBUG("timeout reading byte %d from "
+ "0x%02x:0x%02x, status: 0x%02x\n", i,
+ (unsigned int)bank, (unsigned int)offset, x);
+ break;
+ }
+ buf[i] = inb(data->addr + ABIT_UGURU3_CMD);
+ }
+ return i;
+}
+
+/*
+ * Sensor settings are stored 1 byte per offset with the bytes
+ * placed add consecutive offsets.
+ */
+static int abituguru3_read_increment_offset(struct abituguru3_data *data,
+ u8 bank, u8 offset, u8 count,
+ u8 *buf, int offset_count)
+{
+ int i, x;
+
+ for (i = 0; i < offset_count; i++) {
+ x = abituguru3_read(data, bank, offset + i, count,
+ buf + i * count);
+ if (x != count) {
+ if (x < 0)
+ return x;
+ return i * count + x;
+ }
+ }
+
+ return i * count;
+}
+
+/*
+ * Following are the sysfs callback functions. These functions expect:
+ * sensor_device_attribute_2->index: index into the data->sensors array
+ * sensor_device_attribute_2->nr: register offset, bitmask or NA.
+ */
+static struct abituguru3_data *abituguru3_update_device(struct device *dev);
+
+static ssize_t show_value(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int value;
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru3_data *data = abituguru3_update_device(dev);
+ const struct abituguru3_sensor_info *sensor;
+
+ if (!data)
+ return -EIO;
+
+ sensor = &data->sensors[attr->index];
+
+ /* are we reading a setting, or is this a normal read? */
+ if (attr->nr)
+ value = data->settings[sensor->port][attr->nr];
+ else
+ value = data->value[sensor->port];
+
+ /* convert the value */
+ value = (value * sensor->multiplier) / sensor->divisor +
+ sensor->offset;
+
+ /*
+ * alternatively we could update the sensors settings struct for this,
+ * but then its contents would differ from the windows sw ini files
+ */
+ if (sensor->type == ABIT_UGURU3_TEMP_SENSOR)
+ value *= 1000;
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int port;
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru3_data *data = abituguru3_update_device(dev);
+
+ if (!data)
+ return -EIO;
+
+ port = data->sensors[attr->index].port;
+
+ /*
+ * See if the alarm bit for this sensor is set and if a bitmask is
+ * given in attr->nr also check if the alarm matches the type of alarm
+ * we're looking for (for volt it can be either low or high). The type
+ * is stored in a few readonly bits in the settings of the sensor.
+ */
+ if ((data->alarms[port / 8] & (0x01 << (port % 8))) &&
+ (!attr->nr || (data->settings[port][0] & attr->nr)))
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t show_mask(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru3_data *data = dev_get_drvdata(dev);
+
+ if (data->settings[data->sensors[attr->index].port][0] & attr->nr)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct abituguru3_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->sensors[attr->index].name);
+}
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", ABIT_UGURU3_NAME);
+}
+
+/* Sysfs attr templates, the real entries are generated automatically. */
+static const
+struct sensor_device_attribute_2 abituguru3_sysfs_templ[3][10] = { {
+ SENSOR_ATTR_2(in%d_input, 0444, show_value, NULL, 0, 0),
+ SENSOR_ATTR_2(in%d_min, 0444, show_value, NULL, 1, 0),
+ SENSOR_ATTR_2(in%d_max, 0444, show_value, NULL, 2, 0),
+ SENSOR_ATTR_2(in%d_min_alarm, 0444, show_alarm, NULL,
+ ABIT_UGURU3_VOLT_LOW_ALARM_FLAG, 0),
+ SENSOR_ATTR_2(in%d_max_alarm, 0444, show_alarm, NULL,
+ ABIT_UGURU3_VOLT_HIGH_ALARM_FLAG, 0),
+ SENSOR_ATTR_2(in%d_beep, 0444, show_mask, NULL,
+ ABIT_UGURU3_BEEP_ENABLE, 0),
+ SENSOR_ATTR_2(in%d_shutdown, 0444, show_mask, NULL,
+ ABIT_UGURU3_SHUTDOWN_ENABLE, 0),
+ SENSOR_ATTR_2(in%d_min_alarm_enable, 0444, show_mask, NULL,
+ ABIT_UGURU3_VOLT_LOW_ALARM_ENABLE, 0),
+ SENSOR_ATTR_2(in%d_max_alarm_enable, 0444, show_mask, NULL,
+ ABIT_UGURU3_VOLT_HIGH_ALARM_ENABLE, 0),
+ SENSOR_ATTR_2(in%d_label, 0444, show_label, NULL, 0, 0)
+ }, {
+ SENSOR_ATTR_2(temp%d_input, 0444, show_value, NULL, 0, 0),
+ SENSOR_ATTR_2(temp%d_max, 0444, show_value, NULL, 1, 0),
+ SENSOR_ATTR_2(temp%d_crit, 0444, show_value, NULL, 2, 0),
+ SENSOR_ATTR_2(temp%d_alarm, 0444, show_alarm, NULL, 0, 0),
+ SENSOR_ATTR_2(temp%d_beep, 0444, show_mask, NULL,
+ ABIT_UGURU3_BEEP_ENABLE, 0),
+ SENSOR_ATTR_2(temp%d_shutdown, 0444, show_mask, NULL,
+ ABIT_UGURU3_SHUTDOWN_ENABLE, 0),
+ SENSOR_ATTR_2(temp%d_alarm_enable, 0444, show_mask, NULL,
+ ABIT_UGURU3_TEMP_HIGH_ALARM_ENABLE, 0),
+ SENSOR_ATTR_2(temp%d_label, 0444, show_label, NULL, 0, 0)
+ }, {
+ SENSOR_ATTR_2(fan%d_input, 0444, show_value, NULL, 0, 0),
+ SENSOR_ATTR_2(fan%d_min, 0444, show_value, NULL, 1, 0),
+ SENSOR_ATTR_2(fan%d_alarm, 0444, show_alarm, NULL, 0, 0),
+ SENSOR_ATTR_2(fan%d_beep, 0444, show_mask, NULL,
+ ABIT_UGURU3_BEEP_ENABLE, 0),
+ SENSOR_ATTR_2(fan%d_shutdown, 0444, show_mask, NULL,
+ ABIT_UGURU3_SHUTDOWN_ENABLE, 0),
+ SENSOR_ATTR_2(fan%d_alarm_enable, 0444, show_mask, NULL,
+ ABIT_UGURU3_FAN_LOW_ALARM_ENABLE, 0),
+ SENSOR_ATTR_2(fan%d_label, 0444, show_label, NULL, 0, 0)
+} };
+
+static struct sensor_device_attribute_2 abituguru3_sysfs_attr[] = {
+ SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0),
+};
+
+static int abituguru3_probe(struct platform_device *pdev)
+{
+ const int no_sysfs_attr[3] = { 10, 8, 7 };
+ int sensor_index[3] = { 0, 1, 1 };
+ struct abituguru3_data *data;
+ int i, j, type, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV;
+ char *sysfs_filename;
+ u8 buf[2];
+ u16 id;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct abituguru3_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ /* Read the motherboard ID */
+ i = abituguru3_read(data, ABIT_UGURU3_MISC_BANK, ABIT_UGURU3_BOARD_ID,
+ 2, buf);
+ if (i != 2)
+ goto abituguru3_probe_error;
+
+ /* Completely read the uGuru to see if one really is there */
+ if (!abituguru3_update_device(&pdev->dev))
+ goto abituguru3_probe_error;
+
+ /* lookup the ID in our motherboard table */
+ id = ((u16)buf[0] << 8) | (u16)buf[1];
+ for (i = 0; abituguru3_motherboards[i].id; i++)
+ if (abituguru3_motherboards[i].id == id)
+ break;
+ if (!abituguru3_motherboards[i].id) {
+ pr_err("error unknown motherboard ID: %04X. %s\n",
+ (unsigned int)id, report_this);
+ goto abituguru3_probe_error;
+ }
+ data->sensors = abituguru3_motherboards[i].sensors;
+
+ pr_info("found Abit uGuru3, motherboard ID: %04X\n", (unsigned int)id);
+
+ /* Fill the sysfs attr array */
+ sysfs_attr_i = 0;
+ sysfs_filename = data->sysfs_names;
+ sysfs_names_free = ABIT_UGURU3_SYSFS_NAMES_LENGTH;
+ for (i = 0; data->sensors[i].name; i++) {
+ /* Fail safe check, this should never happen! */
+ if (i >= ABIT_UGURU3_MAX_NO_SENSORS) {
+ pr_err("Fatal error motherboard has more sensors then ABIT_UGURU3_MAX_NO_SENSORS. %s %s\n",
+ never_happen, report_this);
+ res = -ENAMETOOLONG;
+ goto abituguru3_probe_error;
+ }
+ type = data->sensors[i].type;
+ for (j = 0; j < no_sysfs_attr[type]; j++) {
+ used = snprintf(sysfs_filename, sysfs_names_free,
+ abituguru3_sysfs_templ[type][j].dev_attr.attr.
+ name, sensor_index[type]) + 1;
+ data->sysfs_attr[sysfs_attr_i] =
+ abituguru3_sysfs_templ[type][j];
+ data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name =
+ sysfs_filename;
+ data->sysfs_attr[sysfs_attr_i].index = i;
+ sysfs_filename += used;
+ sysfs_names_free -= used;
+ sysfs_attr_i++;
+ }
+ sensor_index[type]++;
+ }
+ /* Fail safe check, this should never happen! */
+ if (sysfs_names_free < 0) {
+ pr_err("Fatal error ran out of space for sysfs attr names. %s %s\n",
+ never_happen, report_this);
+ res = -ENAMETOOLONG;
+ goto abituguru3_probe_error;
+ }
+
+ /* Register sysfs hooks */
+ for (i = 0; i < sysfs_attr_i; i++)
+ if (device_create_file(&pdev->dev,
+ &data->sysfs_attr[i].dev_attr))
+ goto abituguru3_probe_error;
+ for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
+ if (device_create_file(&pdev->dev,
+ &abituguru3_sysfs_attr[i].dev_attr))
+ goto abituguru3_probe_error;
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ res = PTR_ERR(data->hwmon_dev);
+ goto abituguru3_probe_error;
+ }
+
+ return 0; /* success */
+
+abituguru3_probe_error:
+ for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
+ device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
+ device_remove_file(&pdev->dev,
+ &abituguru3_sysfs_attr[i].dev_attr);
+ return res;
+}
+
+static int abituguru3_remove(struct platform_device *pdev)
+{
+ int i;
+ struct abituguru3_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
+ device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
+ device_remove_file(&pdev->dev,
+ &abituguru3_sysfs_attr[i].dev_attr);
+ return 0;
+}
+
+static struct abituguru3_data *abituguru3_update_device(struct device *dev)
+{
+ int i;
+ struct abituguru3_data *data = dev_get_drvdata(dev);
+
+ mutex_lock(&data->update_lock);
+ if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
+ /* Clear data->valid while updating */
+ data->valid = false;
+ /* Read alarms */
+ if (abituguru3_read_increment_offset(data,
+ ABIT_UGURU3_SETTINGS_BANK,
+ ABIT_UGURU3_ALARMS_START,
+ 1, data->alarms, 48/8) != (48/8))
+ goto LEAVE_UPDATE;
+ /* Read in and temp sensors (3 byte settings / sensor) */
+ for (i = 0; i < 32; i++) {
+ if (abituguru3_read(data, ABIT_UGURU3_SENSORS_BANK,
+ ABIT_UGURU3_VALUES_START + i,
+ 1, &data->value[i]) != 1)
+ goto LEAVE_UPDATE;
+ if (abituguru3_read_increment_offset(data,
+ ABIT_UGURU3_SETTINGS_BANK,
+ ABIT_UGURU3_SETTINGS_START + i * 3,
+ 1,
+ data->settings[i], 3) != 3)
+ goto LEAVE_UPDATE;
+ }
+ /* Read temp sensors (2 byte settings / sensor) */
+ for (i = 0; i < 16; i++) {
+ if (abituguru3_read(data, ABIT_UGURU3_SENSORS_BANK,
+ ABIT_UGURU3_VALUES_START + 32 + i,
+ 1, &data->value[32 + i]) != 1)
+ goto LEAVE_UPDATE;
+ if (abituguru3_read_increment_offset(data,
+ ABIT_UGURU3_SETTINGS_BANK,
+ ABIT_UGURU3_SETTINGS_START + 32 * 3 +
+ i * 2, 1,
+ data->settings[32 + i], 2) != 2)
+ goto LEAVE_UPDATE;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+LEAVE_UPDATE:
+ mutex_unlock(&data->update_lock);
+ if (data->valid)
+ return data;
+ else
+ return NULL;
+}
+
+static int abituguru3_suspend(struct device *dev)
+{
+ struct abituguru3_data *data = dev_get_drvdata(dev);
+ /*
+ * make sure all communications with the uguru3 are done and no new
+ * ones are started
+ */
+ mutex_lock(&data->update_lock);
+ return 0;
+}
+
+static int abituguru3_resume(struct device *dev)
+{
+ struct abituguru3_data *data = dev_get_drvdata(dev);
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume);
+
+static struct platform_driver abituguru3_driver = {
+ .driver = {
+ .name = ABIT_UGURU3_NAME,
+ .pm = pm_sleep_ptr(&abituguru3_pm),
+ },
+ .probe = abituguru3_probe,
+ .remove = abituguru3_remove,
+};
+
+static int __init abituguru3_dmi_detect(void)
+{
+ const char *board_vendor, *board_name;
+ int i, err = (force) ? 1 : -ENODEV;
+ const char *const *dmi_name;
+ size_t sublen;
+
+ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ if (!board_vendor || strcmp(board_vendor, "http://www.abit.com.tw/"))
+ return err;
+
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+ if (!board_name)
+ return err;
+
+ /*
+ * At the moment, we don't care about the part of the vendor
+ * DMI string contained in brackets. Truncate the string at
+ * the first occurrence of a bracket. Trim any trailing space
+ * from the substring.
+ */
+ sublen = strcspn(board_name, "(");
+ while (sublen > 0 && board_name[sublen - 1] == ' ')
+ sublen--;
+
+ for (i = 0; abituguru3_motherboards[i].id; i++) {
+ dmi_name = abituguru3_motherboards[i].dmi_name;
+ for ( ; *dmi_name; dmi_name++) {
+ if (strlen(*dmi_name) != sublen)
+ continue;
+ if (!strncasecmp(board_name, *dmi_name, sublen))
+ return 0;
+ }
+ }
+
+ /* No match found */
+ return 1;
+}
+
+/*
+ * FIXME: Manual detection should die eventually; we need to collect stable
+ * DMI model names first before we can rely entirely on CONFIG_DMI.
+ */
+
+static int __init abituguru3_detect(void)
+{
+ /*
+ * See if there is an uguru3 there. An idle uGuru3 will hold 0x00 or
+ * 0x08 at DATA and 0xAC at CMD. Sometimes the uGuru3 will hold 0x05
+ * or 0x55 at CMD instead, why is unknown.
+ */
+ u8 data_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_DATA);
+ u8 cmd_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_CMD);
+ if (((data_val == 0x00) || (data_val == 0x08)) &&
+ ((cmd_val == 0xAC) || (cmd_val == 0x05) ||
+ (cmd_val == 0x55)))
+ return 0;
+
+ ABIT_UGURU3_DEBUG("no Abit uGuru3 found, data = 0x%02X, cmd = "
+ "0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val);
+
+ if (force) {
+ pr_info("Assuming Abit uGuru3 is present because of \"force\" parameter\n");
+ return 0;
+ }
+
+ /* No uGuru3 found */
+ return -ENODEV;
+}
+
+static struct platform_device *abituguru3_pdev;
+
+static int __init abituguru3_init(void)
+{
+ struct resource res = { .flags = IORESOURCE_IO };
+ int err;
+
+ /* Attempt DMI detection first */
+ err = abituguru3_dmi_detect();
+ if (err < 0)
+ return err;
+
+ /*
+ * Fall back to manual detection if there was no exact
+ * board name match, or force was specified.
+ */
+ if (err > 0) {
+ err = abituguru3_detect();
+ if (err)
+ return err;
+
+ pr_warn("this motherboard was not detected using DMI. "
+ "Please send the output of \"dmidecode\" to the abituguru3 maintainer (see MAINTAINERS)\n");
+ }
+
+ err = platform_driver_register(&abituguru3_driver);
+ if (err)
+ goto exit;
+
+ abituguru3_pdev = platform_device_alloc(ABIT_UGURU3_NAME,
+ ABIT_UGURU3_BASE);
+ if (!abituguru3_pdev) {
+ pr_err("Device allocation failed\n");
+ err = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ res.start = ABIT_UGURU3_BASE;
+ res.end = ABIT_UGURU3_BASE + ABIT_UGURU3_REGION_LENGTH - 1;
+ res.name = ABIT_UGURU3_NAME;
+
+ err = platform_device_add_resources(abituguru3_pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(abituguru3_pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(abituguru3_pdev);
+exit_driver_unregister:
+ platform_driver_unregister(&abituguru3_driver);
+exit:
+ return err;
+}
+
+static void __exit abituguru3_exit(void)
+{
+ platform_device_unregister(abituguru3_pdev);
+ platform_driver_unregister(&abituguru3_driver);
+}
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Abit uGuru3 Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(abituguru3_init);
+module_exit(abituguru3_exit);
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
new file mode 100644
index 000000000..2147afb72
--- /dev/null
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -0,0 +1,1012 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A hwmon driver for ACPI 4.0 power meters
+ * Copyright (C) 2009 IBM
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+#include <linux/kdev_t.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/err.h>
+#include <linux/acpi.h>
+
+#define ACPI_POWER_METER_NAME "power_meter"
+#define ACPI_POWER_METER_DEVICE_NAME "Power Meter"
+#define ACPI_POWER_METER_CLASS "pwr_meter_resource"
+
+#define NUM_SENSORS 17
+
+#define POWER_METER_CAN_MEASURE (1 << 0)
+#define POWER_METER_CAN_TRIP (1 << 1)
+#define POWER_METER_CAN_CAP (1 << 2)
+#define POWER_METER_CAN_NOTIFY (1 << 3)
+#define POWER_METER_IS_BATTERY (1 << 8)
+#define UNKNOWN_HYSTERESIS 0xFFFFFFFF
+#define UNKNOWN_POWER 0xFFFFFFFF
+
+#define METER_NOTIFY_CONFIG 0x80
+#define METER_NOTIFY_TRIP 0x81
+#define METER_NOTIFY_CAP 0x82
+#define METER_NOTIFY_CAPPING 0x83
+#define METER_NOTIFY_INTERVAL 0x84
+
+#define POWER_AVERAGE_NAME "power1_average"
+#define POWER_CAP_NAME "power1_cap"
+#define POWER_AVG_INTERVAL_NAME "power1_average_interval"
+#define POWER_ALARM_NAME "power1_alarm"
+
+static int cap_in_hardware;
+static bool force_cap_on;
+
+static int can_cap_in_hardware(void)
+{
+ return force_cap_on || cap_in_hardware;
+}
+
+static const struct acpi_device_id power_meter_ids[] = {
+ {"ACPI000D", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, power_meter_ids);
+
+struct acpi_power_meter_capabilities {
+ u64 flags;
+ u64 units;
+ u64 type;
+ u64 accuracy;
+ u64 sampling_time;
+ u64 min_avg_interval;
+ u64 max_avg_interval;
+ u64 hysteresis;
+ u64 configurable_cap;
+ u64 min_cap;
+ u64 max_cap;
+};
+
+struct acpi_power_meter_resource {
+ struct acpi_device *acpi_dev;
+ acpi_bus_id name;
+ struct mutex lock;
+ struct device *hwmon_dev;
+ struct acpi_power_meter_capabilities caps;
+ acpi_string model_number;
+ acpi_string serial_number;
+ acpi_string oem_info;
+ u64 power;
+ u64 cap;
+ u64 avg_interval;
+ int sensors_valid;
+ unsigned long sensors_last_updated;
+ struct sensor_device_attribute sensors[NUM_SENSORS];
+ int num_sensors;
+ s64 trip[2];
+ int num_domain_devices;
+ struct acpi_device **domain_devices;
+ struct kobject *holders_dir;
+};
+
+struct sensor_template {
+ char *label;
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+ ssize_t (*set)(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+ int index;
+};
+
+/* Averaging interval */
+static int update_avg_interval(struct acpi_power_meter_resource *resource)
+{
+ unsigned long long data;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GAI",
+ NULL, &data);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_GAI",
+ status);
+ return -ENODEV;
+ }
+
+ resource->avg_interval = data;
+ return 0;
+}
+
+static ssize_t show_avg_interval(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+
+ mutex_lock(&resource->lock);
+ update_avg_interval(resource);
+ mutex_unlock(&resource->lock);
+
+ return sprintf(buf, "%llu\n", resource->avg_interval);
+}
+
+static ssize_t set_avg_interval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+ union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+ struct acpi_object_list args = { 1, &arg0 };
+ int res;
+ unsigned long temp;
+ unsigned long long data;
+ acpi_status status;
+
+ res = kstrtoul(buf, 10, &temp);
+ if (res)
+ return res;
+
+ if (temp > resource->caps.max_avg_interval ||
+ temp < resource->caps.min_avg_interval)
+ return -EINVAL;
+ arg0.integer.value = temp;
+
+ mutex_lock(&resource->lock);
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI",
+ &args, &data);
+ if (ACPI_SUCCESS(status))
+ resource->avg_interval = temp;
+ mutex_unlock(&resource->lock);
+
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI",
+ status);
+ return -EINVAL;
+ }
+
+ /* _PAI returns 0 on success, nonzero otherwise */
+ if (data)
+ return -EINVAL;
+
+ return count;
+}
+
+/* Cap functions */
+static int update_cap(struct acpi_power_meter_resource *resource)
+{
+ unsigned long long data;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GHL",
+ NULL, &data);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_GHL",
+ status);
+ return -ENODEV;
+ }
+
+ resource->cap = data;
+ return 0;
+}
+
+static ssize_t show_cap(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+
+ mutex_lock(&resource->lock);
+ update_cap(resource);
+ mutex_unlock(&resource->lock);
+
+ return sprintf(buf, "%llu\n", resource->cap * 1000);
+}
+
+static ssize_t set_cap(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+ union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+ struct acpi_object_list args = { 1, &arg0 };
+ int res;
+ unsigned long temp;
+ unsigned long long data;
+ acpi_status status;
+
+ res = kstrtoul(buf, 10, &temp);
+ if (res)
+ return res;
+
+ temp = DIV_ROUND_CLOSEST(temp, 1000);
+ if (temp > resource->caps.max_cap || temp < resource->caps.min_cap)
+ return -EINVAL;
+ arg0.integer.value = temp;
+
+ mutex_lock(&resource->lock);
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL",
+ &args, &data);
+ if (ACPI_SUCCESS(status))
+ resource->cap = temp;
+ mutex_unlock(&resource->lock);
+
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL",
+ status);
+ return -EINVAL;
+ }
+
+ /* _SHL returns 0 on success, nonzero otherwise */
+ if (data)
+ return -EINVAL;
+
+ return count;
+}
+
+/* Power meter trip points */
+static int set_acpi_trip(struct acpi_power_meter_resource *resource)
+{
+ union acpi_object arg_objs[] = {
+ {ACPI_TYPE_INTEGER},
+ {ACPI_TYPE_INTEGER}
+ };
+ struct acpi_object_list args = { 2, arg_objs };
+ unsigned long long data;
+ acpi_status status;
+
+ /* Both trip levels must be set */
+ if (resource->trip[0] < 0 || resource->trip[1] < 0)
+ return 0;
+
+ /* This driver stores min, max; ACPI wants max, min. */
+ arg_objs[0].integer.value = resource->trip[1];
+ arg_objs[1].integer.value = resource->trip[0];
+
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PTP",
+ &args, &data);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PTP",
+ status);
+ return -EINVAL;
+ }
+
+ /* _PTP returns 0 on success, nonzero otherwise */
+ if (data)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t set_trip(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+ int res;
+ unsigned long temp;
+
+ res = kstrtoul(buf, 10, &temp);
+ if (res)
+ return res;
+
+ temp = DIV_ROUND_CLOSEST(temp, 1000);
+
+ mutex_lock(&resource->lock);
+ resource->trip[attr->index - 7] = temp;
+ res = set_acpi_trip(resource);
+ mutex_unlock(&resource->lock);
+
+ if (res)
+ return res;
+
+ return count;
+}
+
+/* Power meter */
+static int update_meter(struct acpi_power_meter_resource *resource)
+{
+ unsigned long long data;
+ acpi_status status;
+ unsigned long local_jiffies = jiffies;
+
+ if (time_before(local_jiffies, resource->sensors_last_updated +
+ msecs_to_jiffies(resource->caps.sampling_time)) &&
+ resource->sensors_valid)
+ return 0;
+
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PMM",
+ NULL, &data);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PMM",
+ status);
+ return -ENODEV;
+ }
+
+ resource->power = data;
+ resource->sensors_valid = 1;
+ resource->sensors_last_updated = jiffies;
+ return 0;
+}
+
+static ssize_t show_power(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+
+ mutex_lock(&resource->lock);
+ update_meter(resource);
+ mutex_unlock(&resource->lock);
+
+ if (resource->power == UNKNOWN_POWER)
+ return -ENODATA;
+
+ return sprintf(buf, "%llu\n", resource->power * 1000);
+}
+
+/* Miscellaneous */
+static ssize_t show_str(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+ acpi_string val;
+ int ret;
+
+ mutex_lock(&resource->lock);
+ switch (attr->index) {
+ case 0:
+ val = resource->model_number;
+ break;
+ case 1:
+ val = resource->serial_number;
+ break;
+ case 2:
+ val = resource->oem_info;
+ break;
+ default:
+ WARN(1, "Implementation error: unexpected attribute index %d\n",
+ attr->index);
+ val = "";
+ break;
+ }
+ ret = sprintf(buf, "%s\n", val);
+ mutex_unlock(&resource->lock);
+ return ret;
+}
+
+static ssize_t show_val(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+ u64 val = 0;
+
+ switch (attr->index) {
+ case 0:
+ val = resource->caps.min_avg_interval;
+ break;
+ case 1:
+ val = resource->caps.max_avg_interval;
+ break;
+ case 2:
+ val = resource->caps.min_cap * 1000;
+ break;
+ case 3:
+ val = resource->caps.max_cap * 1000;
+ break;
+ case 4:
+ if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS)
+ return sprintf(buf, "unknown\n");
+
+ val = resource->caps.hysteresis * 1000;
+ break;
+ case 5:
+ if (resource->caps.flags & POWER_METER_IS_BATTERY)
+ val = 1;
+ else
+ val = 0;
+ break;
+ case 6:
+ if (resource->power > resource->cap)
+ val = 1;
+ else
+ val = 0;
+ break;
+ case 7:
+ case 8:
+ if (resource->trip[attr->index - 7] < 0)
+ return sprintf(buf, "unknown\n");
+
+ val = resource->trip[attr->index - 7] * 1000;
+ break;
+ default:
+ WARN(1, "Implementation error: unexpected attribute index %d\n",
+ attr->index);
+ break;
+ }
+
+ return sprintf(buf, "%llu\n", val);
+}
+
+static ssize_t show_accuracy(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct acpi_device *acpi_dev = to_acpi_device(dev);
+ struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
+ unsigned int acc = resource->caps.accuracy;
+
+ return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000);
+}
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME);
+}
+
+#define RO_SENSOR_TEMPLATE(_label, _show, _index) \
+ { \
+ .label = _label, \
+ .show = _show, \
+ .index = _index, \
+ }
+
+#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \
+ { \
+ .label = _label, \
+ .show = _show, \
+ .set = _set, \
+ .index = _index, \
+ }
+
+/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */
+static struct sensor_template meter_attrs[] = {
+ RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0),
+ RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0),
+ RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0),
+ 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),
+ {},
+};
+
+static struct sensor_template misc_cap_attrs[] = {
+ RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2),
+ RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3),
+ RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4),
+ RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6),
+ {},
+};
+
+static struct sensor_template ro_cap_attrs[] = {
+ RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0),
+ {},
+};
+
+static struct sensor_template rw_cap_attrs[] = {
+ RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0),
+ {},
+};
+
+static struct sensor_template trip_attrs[] = {
+ RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7),
+ RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8),
+ {},
+};
+
+static struct sensor_template misc_attrs[] = {
+ RO_SENSOR_TEMPLATE("name", show_name, 0),
+ RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0),
+ RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2),
+ RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1),
+ {},
+};
+
+#undef RO_SENSOR_TEMPLATE
+#undef RW_SENSOR_TEMPLATE
+
+/* Read power domain data */
+static void remove_domain_devices(struct acpi_power_meter_resource *resource)
+{
+ int i;
+
+ if (!resource->num_domain_devices)
+ return;
+
+ 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));
+ acpi_dev_put(obj);
+ }
+
+ kfree(resource->domain_devices);
+ kobject_put(resource->holders_dir);
+ resource->num_domain_devices = 0;
+}
+
+static int read_domain_devices(struct acpi_power_meter_resource *resource)
+{
+ int res = 0;
+ int i;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *pss;
+ acpi_status status;
+
+ status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMD", NULL,
+ &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PMD",
+ status);
+ return -ENODEV;
+ }
+
+ pss = buffer.pointer;
+ if (!pss ||
+ pss->type != ACPI_TYPE_PACKAGE) {
+ dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME
+ "Invalid _PMD data\n");
+ res = -EFAULT;
+ goto end;
+ }
+
+ if (!pss->package.count)
+ goto end;
+
+ resource->domain_devices = kcalloc(pss->package.count,
+ sizeof(struct acpi_device *),
+ GFP_KERNEL);
+ if (!resource->domain_devices) {
+ res = -ENOMEM;
+ goto end;
+ }
+
+ resource->holders_dir = kobject_create_and_add("measures",
+ &resource->acpi_dev->dev.kobj);
+ if (!resource->holders_dir) {
+ res = -ENOMEM;
+ goto exit_free;
+ }
+
+ resource->num_domain_devices = pss->package.count;
+
+ for (i = 0; i < pss->package.count; i++) {
+ struct acpi_device *obj;
+ 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 */
+ obj = acpi_get_acpi_dev(element->reference.handle);
+ resource->domain_devices[i] = obj;
+ if (!obj)
+ continue;
+
+ res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj,
+ kobject_name(&obj->dev.kobj));
+ if (res) {
+ acpi_dev_put(obj);
+ resource->domain_devices[i] = NULL;
+ }
+ }
+
+ res = 0;
+ goto end;
+
+exit_free:
+ kfree(resource->domain_devices);
+end:
+ kfree(buffer.pointer);
+ return res;
+}
+
+/* Registration and deregistration */
+static int register_attrs(struct acpi_power_meter_resource *resource,
+ struct sensor_template *attrs)
+{
+ struct device *dev = &resource->acpi_dev->dev;
+ struct sensor_device_attribute *sensors =
+ &resource->sensors[resource->num_sensors];
+ int res = 0;
+
+ while (attrs->label) {
+ sensors->dev_attr.attr.name = attrs->label;
+ sensors->dev_attr.attr.mode = 0444;
+ sensors->dev_attr.show = attrs->show;
+ sensors->index = attrs->index;
+
+ if (attrs->set) {
+ sensors->dev_attr.attr.mode |= 0200;
+ sensors->dev_attr.store = attrs->set;
+ }
+
+ sysfs_attr_init(&sensors->dev_attr.attr);
+ res = device_create_file(dev, &sensors->dev_attr);
+ if (res) {
+ sensors->dev_attr.attr.name = NULL;
+ goto error;
+ }
+ sensors++;
+ resource->num_sensors++;
+ attrs++;
+ }
+
+error:
+ return res;
+}
+
+static void remove_attrs(struct acpi_power_meter_resource *resource)
+{
+ int i;
+
+ for (i = 0; i < resource->num_sensors; i++) {
+ if (!resource->sensors[i].dev_attr.attr.name)
+ continue;
+ device_remove_file(&resource->acpi_dev->dev,
+ &resource->sensors[i].dev_attr);
+ }
+
+ remove_domain_devices(resource);
+
+ resource->num_sensors = 0;
+}
+
+static int setup_attrs(struct acpi_power_meter_resource *resource)
+{
+ int res = 0;
+
+ res = read_domain_devices(resource);
+ if (res)
+ return res;
+
+ if (resource->caps.flags & POWER_METER_CAN_MEASURE) {
+ res = register_attrs(resource, meter_attrs);
+ if (res)
+ goto error;
+ }
+
+ if (resource->caps.flags & POWER_METER_CAN_CAP) {
+ if (!can_cap_in_hardware()) {
+ dev_warn(&resource->acpi_dev->dev,
+ "Ignoring unsafe software power cap!\n");
+ goto skip_unsafe_cap;
+ }
+
+ if (resource->caps.configurable_cap)
+ res = register_attrs(resource, rw_cap_attrs);
+ else
+ res = register_attrs(resource, ro_cap_attrs);
+
+ if (res)
+ goto error;
+
+ res = register_attrs(resource, misc_cap_attrs);
+ if (res)
+ goto error;
+ }
+
+skip_unsafe_cap:
+ if (resource->caps.flags & POWER_METER_CAN_TRIP) {
+ res = register_attrs(resource, trip_attrs);
+ if (res)
+ goto error;
+ }
+
+ res = register_attrs(resource, misc_attrs);
+ if (res)
+ goto error;
+
+ return res;
+error:
+ remove_attrs(resource);
+ return res;
+}
+
+static void free_capabilities(struct acpi_power_meter_resource *resource)
+{
+ acpi_string *str;
+ int i;
+
+ str = &resource->model_number;
+ for (i = 0; i < 3; i++, str++) {
+ kfree(*str);
+ *str = NULL;
+ }
+}
+
+static int read_capabilities(struct acpi_power_meter_resource *resource)
+{
+ int res = 0;
+ int i;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer state = { 0, NULL };
+ struct acpi_buffer format = { sizeof("NNNNNNNNNNN"), "NNNNNNNNNNN" };
+ union acpi_object *pss;
+ acpi_string *str;
+ acpi_status status;
+
+ status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMC", NULL,
+ &buffer);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PMC",
+ status);
+ return -ENODEV;
+ }
+
+ pss = buffer.pointer;
+ if (!pss ||
+ pss->type != ACPI_TYPE_PACKAGE ||
+ pss->package.count != 14) {
+ dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME
+ "Invalid _PMC data\n");
+ res = -EFAULT;
+ goto end;
+ }
+
+ /* Grab all the integer data at once */
+ state.length = sizeof(struct acpi_power_meter_capabilities);
+ state.pointer = &resource->caps;
+
+ status = acpi_extract_package(pss, &format, &state);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME
+ "_PMC package parsing failed: %s\n",
+ acpi_format_exception(status));
+ res = -EFAULT;
+ goto end;
+ }
+
+ if (resource->caps.units) {
+ dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME
+ "Unknown units %llu.\n",
+ resource->caps.units);
+ res = -EINVAL;
+ goto end;
+ }
+
+ /* Grab the string data */
+ str = &resource->model_number;
+
+ for (i = 11; i < 14; i++) {
+ union acpi_object *element = &pss->package.elements[i];
+
+ if (element->type != ACPI_TYPE_STRING) {
+ res = -EINVAL;
+ goto error;
+ }
+
+ *str = kcalloc(element->string.length + 1, sizeof(u8),
+ GFP_KERNEL);
+ if (!*str) {
+ res = -ENOMEM;
+ goto error;
+ }
+
+ strncpy(*str, element->string.pointer, element->string.length);
+ str++;
+ }
+
+ dev_info(&resource->acpi_dev->dev, "Found ACPI power meter.\n");
+ goto end;
+error:
+ free_capabilities(resource);
+end:
+ kfree(buffer.pointer);
+ return res;
+}
+
+/* Handle ACPI event notifications */
+static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
+{
+ struct acpi_power_meter_resource *resource;
+ int res;
+
+ if (!device || !acpi_driver_data(device))
+ return;
+
+ resource = acpi_driver_data(device);
+
+ switch (event) {
+ case METER_NOTIFY_CONFIG:
+ mutex_lock(&resource->lock);
+ free_capabilities(resource);
+ res = read_capabilities(resource);
+ mutex_unlock(&resource->lock);
+ if (res)
+ break;
+
+ remove_attrs(resource);
+ setup_attrs(resource);
+ break;
+ case METER_NOTIFY_TRIP:
+ sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME);
+ break;
+ case METER_NOTIFY_CAP:
+ sysfs_notify(&device->dev.kobj, NULL, POWER_CAP_NAME);
+ break;
+ case METER_NOTIFY_INTERVAL:
+ sysfs_notify(&device->dev.kobj, NULL, POWER_AVG_INTERVAL_NAME);
+ break;
+ case METER_NOTIFY_CAPPING:
+ sysfs_notify(&device->dev.kobj, NULL, POWER_ALARM_NAME);
+ dev_info(&device->dev, "Capping in progress.\n");
+ break;
+ default:
+ WARN(1, "Unexpected event %d\n", event);
+ break;
+ }
+
+ acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS,
+ dev_name(&device->dev), event, 0);
+}
+
+static int acpi_power_meter_add(struct acpi_device *device)
+{
+ int res;
+ struct acpi_power_meter_resource *resource;
+
+ if (!device)
+ return -EINVAL;
+
+ resource = kzalloc(sizeof(*resource), GFP_KERNEL);
+ if (!resource)
+ return -ENOMEM;
+
+ resource->sensors_valid = 0;
+ resource->acpi_dev = device;
+ mutex_init(&resource->lock);
+ strcpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_POWER_METER_CLASS);
+ device->driver_data = resource;
+
+ res = read_capabilities(resource);
+ if (res)
+ goto exit_free;
+
+ resource->trip[0] = -1;
+ resource->trip[1] = -1;
+
+ res = setup_attrs(resource);
+ if (res)
+ goto exit_free_capability;
+
+ resource->hwmon_dev = hwmon_device_register(&device->dev);
+ if (IS_ERR(resource->hwmon_dev)) {
+ res = PTR_ERR(resource->hwmon_dev);
+ goto exit_remove;
+ }
+
+ res = 0;
+ goto exit;
+
+exit_remove:
+ remove_attrs(resource);
+exit_free_capability:
+ free_capabilities(resource);
+exit_free:
+ kfree(resource);
+exit:
+ return res;
+}
+
+static int acpi_power_meter_remove(struct acpi_device *device)
+{
+ struct acpi_power_meter_resource *resource;
+
+ if (!device || !acpi_driver_data(device))
+ return -EINVAL;
+
+ resource = acpi_driver_data(device);
+ hwmon_device_unregister(resource->hwmon_dev);
+
+ remove_attrs(resource);
+ free_capabilities(resource);
+
+ kfree(resource);
+ return 0;
+}
+
+static int acpi_power_meter_resume(struct device *dev)
+{
+ struct acpi_power_meter_resource *resource;
+
+ if (!dev)
+ return -EINVAL;
+
+ resource = acpi_driver_data(to_acpi_device(dev));
+ if (!resource)
+ return -EINVAL;
+
+ free_capabilities(resource);
+ read_capabilities(resource);
+
+ return 0;
+}
+
+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",
+ .class = ACPI_POWER_METER_CLASS,
+ .ids = power_meter_ids,
+ .ops = {
+ .add = acpi_power_meter_add,
+ .remove = acpi_power_meter_remove,
+ .notify = acpi_power_meter_notify,
+ },
+ .drv.pm = pm_sleep_ptr(&acpi_power_meter_pm),
+};
+
+/* Module init/exit routines */
+static int __init enable_cap_knobs(const struct dmi_system_id *d)
+{
+ cap_in_hardware = 1;
+ return 0;
+}
+
+static const struct dmi_system_id pm_dmi_table[] __initconst = {
+ {
+ enable_cap_knobs, "IBM Active Energy Manager",
+ {
+ DMI_MATCH(DMI_SYS_VENDOR, "IBM")
+ },
+ },
+ {}
+};
+
+static int __init acpi_power_meter_init(void)
+{
+ int result;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ dmi_check_system(pm_dmi_table);
+
+ result = acpi_bus_register_driver(&acpi_power_meter_driver);
+ if (result < 0)
+ return result;
+
+ return 0;
+}
+
+static void __exit acpi_power_meter_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_power_meter_driver);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
+MODULE_DESCRIPTION("ACPI 4.0 power meter driver");
+MODULE_LICENSE("GPL");
+
+module_param(force_cap_on, bool, 0644);
+MODULE_PARM_DESC(force_cap_on, "Enable power cap even it is unsafe to do so.");
+
+module_init(acpi_power_meter_init);
+module_exit(acpi_power_meter_exit);
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
new file mode 100644
index 000000000..7802bbf5f
--- /dev/null
+++ b/drivers/hwmon/ad7314.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Conversion to hwmon from IIO done by Jonathan Cameron <jic23@cam.ac.uk>
+ */
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/bitops.h>
+
+/*
+ * AD7314 temperature masks
+ */
+#define AD7314_TEMP_MASK 0x7FE0
+#define AD7314_TEMP_SHIFT 5
+
+/*
+ * ADT7301 and ADT7302 temperature masks
+ */
+#define ADT7301_TEMP_MASK 0x3FFF
+
+enum ad7314_variant {
+ adt7301,
+ adt7302,
+ ad7314,
+};
+
+struct ad7314_data {
+ struct spi_device *spi_dev;
+ u16 rx ____cacheline_aligned;
+};
+
+static int ad7314_spi_read(struct ad7314_data *chip)
+{
+ int ret;
+
+ ret = spi_read(chip->spi_dev, (u8 *)&chip->rx, sizeof(chip->rx));
+ if (ret < 0) {
+ dev_err(&chip->spi_dev->dev, "SPI read error\n");
+ return ret;
+ }
+
+ return be16_to_cpu(chip->rx);
+}
+
+static ssize_t ad7314_temperature_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad7314_data *chip = dev_get_drvdata(dev);
+ s16 data;
+ int ret;
+
+ ret = ad7314_spi_read(chip);
+ if (ret < 0)
+ return ret;
+ switch (spi_get_device_id(chip->spi_dev)->driver_data) {
+ case ad7314:
+ data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT;
+ data = sign_extend32(data, 9);
+
+ return sprintf(buf, "%d\n", 250 * data);
+ case adt7301:
+ case adt7302:
+ /*
+ * Documented as a 13 bit twos complement register
+ * with a sign bit - which is a 14 bit 2's complement
+ * register. 1lsb - 31.25 milli degrees centigrade
+ */
+ data = ret & ADT7301_TEMP_MASK;
+ data = sign_extend32(data, 13);
+
+ return sprintf(buf, "%d\n",
+ DIV_ROUND_CLOSEST(data * 3125, 100));
+ default:
+ return -EINVAL;
+ }
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, ad7314_temperature, 0);
+
+static struct attribute *ad7314_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(ad7314);
+
+static int ad7314_probe(struct spi_device *spi_dev)
+{
+ struct ad7314_data *chip;
+ struct device *hwmon_dev;
+
+ chip = devm_kzalloc(&spi_dev->dev, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->spi_dev = spi_dev;
+ hwmon_dev = devm_hwmon_device_register_with_groups(&spi_dev->dev,
+ spi_dev->modalias,
+ chip, ad7314_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct spi_device_id ad7314_id[] = {
+ { "adt7301", adt7301 },
+ { "adt7302", adt7302 },
+ { "ad7314", ad7314 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7314_id);
+
+static struct spi_driver ad7314_driver = {
+ .driver = {
+ .name = "ad7314",
+ },
+ .probe = ad7314_probe,
+ .id_table = ad7314_id,
+};
+
+module_spi_driver(ad7314_driver);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital temperature sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c
new file mode 100644
index 000000000..0afb89c46
--- /dev/null
+++ b/drivers/hwmon/ad7414.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * An hwmon driver for the Analog Devices AD7414
+ *
+ * Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
+ *
+ * Copyright (c) 2008 PIKA Technologies
+ * Sean MacLennan <smaclennan@pikatech.com>
+ *
+ * Copyright (c) 2008 Spansion Inc.
+ * Frank Edelhaeuser <frank.edelhaeuser at spansion.com>
+ * (converted to "new style" I2C driver model, removed checkpatch.pl warnings)
+ *
+ * Based on ad7418.c
+ * Copyright 2006 Tower Technologies, Alessandro Zummo <a.zummo at towertech.it>
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+
+
+/* AD7414 registers */
+#define AD7414_REG_TEMP 0x00
+#define AD7414_REG_CONF 0x01
+#define AD7414_REG_T_HIGH 0x02
+#define AD7414_REG_T_LOW 0x03
+
+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 */
+ 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)];
+};
+
+/* REG: (0.25C/bit, two's complement) << 6 */
+static inline int ad7414_temp_from_reg(s16 reg)
+{
+ /*
+ * use integer division instead of equivalent right shift to
+ * guarantee arithmetic shift and preserve the sign
+ */
+ return ((int)reg / 64) * 250;
+}
+
+static inline int ad7414_read(struct i2c_client *client, u8 reg)
+{
+ if (reg == AD7414_REG_TEMP)
+ return i2c_smbus_read_word_swapped(client, reg);
+ else
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int ad7414_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static struct ad7414_data *ad7414_update_device(struct device *dev)
+{
+ struct ad7414_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->lock);
+
+ if (time_after(jiffies, data->next_update) || !data->valid) {
+ int value, i;
+
+ dev_dbg(&client->dev, "starting ad7414 update\n");
+
+ value = ad7414_read(client, AD7414_REG_TEMP);
+ if (value < 0)
+ dev_dbg(&client->dev, "AD7414_REG_TEMP err %d\n",
+ value);
+ else
+ data->temp_input = value;
+
+ for (i = 0; i < ARRAY_SIZE(AD7414_REG_LIMIT); ++i) {
+ value = ad7414_read(client, AD7414_REG_LIMIT[i]);
+ if (value < 0)
+ dev_dbg(&client->dev, "AD7414 reg %d err %d\n",
+ AD7414_REG_LIMIT[i], value);
+ else
+ data->temps[i] = value;
+ }
+
+ data->next_update = jiffies + HZ + HZ / 2;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return data;
+}
+
+static ssize_t temp_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad7414_data *data = ad7414_update_device(dev);
+ return sprintf(buf, "%d\n", ad7414_temp_from_reg(data->temp_input));
+}
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
+
+static ssize_t max_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct ad7414_data *data = ad7414_update_device(dev);
+ return sprintf(buf, "%d\n", data->temps[index] * 1000);
+}
+
+static ssize_t max_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ad7414_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int index = to_sensor_dev_attr(attr)->index;
+ u8 reg = AD7414_REG_LIMIT[index];
+ long temp;
+ int ret = kstrtol(buf, 10, &temp);
+
+ if (ret < 0)
+ return ret;
+
+ temp = clamp_val(temp, -40000, 85000);
+ temp = (temp + (temp < 0 ? -500 : 500)) / 1000;
+
+ mutex_lock(&data->lock);
+ data->temps[index] = temp;
+ ad7414_write(client, reg, temp);
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_max, max_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, max_min, 1);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct ad7414_data *data = ad7414_update_device(dev);
+ int value = (data->temp_input >> bitnr) & 1;
+ return sprintf(buf, "%d\n", value);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 4);
+
+static struct attribute *ad7414_attrs[] = {
+ &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_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(ad7414);
+
+static int ad7414_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct ad7414_data *data;
+ struct device *hwmon_dev;
+ int conf;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ data = devm_kzalloc(dev, sizeof(struct ad7414_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+
+ dev_info(&client->dev, "chip found\n");
+
+ /* Make sure the chip is powered up. */
+ conf = i2c_smbus_read_byte_data(client, AD7414_REG_CONF);
+ if (conf < 0)
+ dev_warn(dev, "ad7414_probe unable to read config register.\n");
+ else {
+ conf &= ~(1 << 7);
+ i2c_smbus_write_byte_data(client, AD7414_REG_CONF, conf);
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name,
+ data, ad7414_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ad7414_id[] = {
+ { "ad7414", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad7414_id);
+
+static const struct of_device_id __maybe_unused ad7414_of_match[] = {
+ { .compatible = "ad,ad7414" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7414_of_match);
+
+static struct i2c_driver ad7414_driver = {
+ .driver = {
+ .name = "ad7414",
+ .of_match_table = of_match_ptr(ad7414_of_match),
+ },
+ .probe_new = ad7414_probe,
+ .id_table = ad7414_id,
+};
+
+module_i2c_driver(ad7414_driver);
+
+MODULE_AUTHOR("Stefan Roese <sr at denx.de>, "
+ "Frank Edelhaeuser <frank.edelhaeuser at spansion.com>");
+
+MODULE_DESCRIPTION("AD7414 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c
new file mode 100644
index 000000000..22bdb7e5b
--- /dev/null
+++ b/drivers/hwmon/ad7418.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * An hwmon driver for the Analog Devices AD7416/17/18
+ * Copyright (C) 2006-07 Tower Technologies
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * Based on lm75.c
+ * Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "lm75.h"
+
+#define DRV_VERSION "0.4"
+
+enum chips { ad7416, ad7417, ad7418 };
+
+/* AD7418 registers */
+#define AD7418_REG_TEMP_IN 0x00
+#define AD7418_REG_CONF 0x01
+#define AD7418_REG_TEMP_HYST 0x02
+#define AD7418_REG_TEMP_OS 0x03
+#define AD7418_REG_ADC 0x04
+#define AD7418_REG_CONF2 0x05
+
+#define AD7418_REG_ADC_CH(x) ((x) << 5)
+#define AD7418_CH_TEMP AD7418_REG_ADC_CH(0)
+
+static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN,
+ AD7418_REG_TEMP_HYST,
+ AD7418_REG_TEMP_OS };
+
+struct ad7418_data {
+ struct i2c_client *client;
+ enum chips type;
+ struct mutex lock;
+ int adc_max; /* number of ADC channels */
+ bool valid;
+ unsigned long last_updated; /* In jiffies */
+ s16 temp[3]; /* Register values */
+ u16 in[4];
+};
+
+static int ad7418_update_device(struct device *dev)
+{
+ struct ad7418_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ s32 val;
+
+ mutex_lock(&data->lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ u8 cfg;
+ int i, ch;
+
+ /* read config register and clear channel bits */
+ val = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
+ if (val < 0)
+ goto abort;
+
+ cfg = val;
+ cfg &= 0x1F;
+
+ val = i2c_smbus_write_byte_data(client, AD7418_REG_CONF,
+ cfg | AD7418_CH_TEMP);
+ if (val < 0)
+ goto abort;
+
+ udelay(30);
+
+ for (i = 0; i < 3; i++) {
+ val = i2c_smbus_read_word_swapped(client,
+ AD7418_REG_TEMP[i]);
+ if (val < 0)
+ goto abort;
+
+ data->temp[i] = val;
+ }
+
+ for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
+ val = i2c_smbus_write_byte_data(client, AD7418_REG_CONF,
+ cfg | AD7418_REG_ADC_CH(ch));
+ if (val < 0)
+ goto abort;
+
+ udelay(15);
+ val = i2c_smbus_read_word_swapped(client,
+ AD7418_REG_ADC);
+ if (val < 0)
+ goto abort;
+
+ data->in[data->adc_max - 1 - i] = val;
+ }
+
+ /* restore old configuration value */
+ val = i2c_smbus_write_word_swapped(client, AD7418_REG_CONF,
+ cfg);
+ if (val < 0)
+ goto abort;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->lock);
+ return 0;
+
+abort:
+ data->valid = false;
+ mutex_unlock(&data->lock);
+ return val;
+}
+
+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 ad7418_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ad7418_update_device(dev);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n",
+ LM75_TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static ssize_t adc_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct ad7418_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ad7418_update_device(dev);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n",
+ ((data->in[attr->index] >> 6) * 2500 + 512) / 1024);
+}
+
+static ssize_t temp_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 ad7418_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+ int ret = kstrtol(buf, 10, &temp);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->lock);
+ data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
+ i2c_smbus_write_word_swapped(client,
+ AD7418_REG_TEMP[attr->index],
+ data->temp[attr->index]);
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 2);
+
+static SENSOR_DEVICE_ATTR_RO(in1_input, adc, 0);
+static SENSOR_DEVICE_ATTR_RO(in2_input, adc, 1);
+static SENSOR_DEVICE_ATTR_RO(in3_input, adc, 2);
+static SENSOR_DEVICE_ATTR_RO(in4_input, adc, 3);
+
+static struct attribute *ad7416_attrs[] = {
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(ad7416);
+
+static struct attribute *ad7417_attrs[] = {
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_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,
+ NULL
+};
+ATTRIBUTE_GROUPS(ad7417);
+
+static struct attribute *ad7418_attrs[] = {
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(ad7418);
+
+static void ad7418_init_client(struct i2c_client *client)
+{
+ struct ad7418_data *data = i2c_get_clientdata(client);
+
+ int reg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
+ if (reg < 0) {
+ dev_err(&client->dev, "cannot read configuration register\n");
+ } else {
+ dev_info(&client->dev, "configuring for mode 1\n");
+ i2c_smbus_write_byte_data(client, AD7418_REG_CONF, reg & 0xfe);
+
+ if (data->type == ad7417 || data->type == ad7418)
+ i2c_smbus_write_byte_data(client,
+ AD7418_REG_CONF2, 0x00);
+ }
+}
+
+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;
+ struct ad7418_data *data;
+ struct device *hwmon_dev;
+ const struct attribute_group **attr_groups = NULL;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ data = devm_kzalloc(dev, sizeof(struct ad7418_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+
+ mutex_init(&data->lock);
+ data->client = client;
+ if (dev->of_node)
+ data->type = (enum chips)of_device_get_match_data(dev);
+ else
+ data->type = i2c_match_id(ad7418_id, client)->driver_data;
+
+ switch (data->type) {
+ case ad7416:
+ data->adc_max = 0;
+ attr_groups = ad7416_groups;
+ break;
+
+ case ad7417:
+ data->adc_max = 4;
+ attr_groups = ad7417_groups;
+ break;
+
+ case ad7418:
+ data->adc_max = 1;
+ attr_groups = ad7418_groups;
+ break;
+ }
+
+ dev_info(dev, "%s chip found\n", client->name);
+
+ /* Initialize the AD7418 chip */
+ ad7418_init_client(client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name,
+ data, attr_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ad7418_id[] = {
+ { "ad7416", ad7416 },
+ { "ad7417", ad7417 },
+ { "ad7418", ad7418 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad7418_id);
+
+static const struct of_device_id ad7418_dt_ids[] = {
+ { .compatible = "adi,ad7416", .data = (void *)ad7416, },
+ { .compatible = "adi,ad7417", .data = (void *)ad7417, },
+ { .compatible = "adi,ad7418", .data = (void *)ad7418, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7418_dt_ids);
+
+static struct i2c_driver ad7418_driver = {
+ .driver = {
+ .name = "ad7418",
+ .of_match_table = ad7418_dt_ids,
+ },
+ .probe_new = ad7418_probe,
+ .id_table = ad7418_id,
+};
+
+module_i2c_driver(ad7418_driver);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("AD7416/17/18 driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c
new file mode 100644
index 000000000..97b330b6c
--- /dev/null
+++ b/drivers/hwmon/adc128d818.c
@@ -0,0 +1,535 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for TI ADC128D818 System Monitor with Temperature Sensor
+ *
+ * Copyright (c) 2014 Guenter Roeck
+ *
+ * Derived from lm80.c
+ * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ * and Philip Edelbrock <phil@netroedge.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+
+/* Addresses to scan
+ * The chip also supports addresses 0x35..0x37. Don't scan those addresses
+ * since they are also used by some EEPROMs, which may result in false
+ * positives.
+ */
+static const unsigned short normal_i2c[] = {
+ 0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
+
+/* registers */
+#define ADC128_REG_IN_MAX(nr) (0x2a + (nr) * 2)
+#define ADC128_REG_IN_MIN(nr) (0x2b + (nr) * 2)
+#define ADC128_REG_IN(nr) (0x20 + (nr))
+
+#define ADC128_REG_TEMP 0x27
+#define ADC128_REG_TEMP_MAX 0x38
+#define ADC128_REG_TEMP_HYST 0x39
+
+#define ADC128_REG_CONFIG 0x00
+#define ADC128_REG_ALARM 0x01
+#define ADC128_REG_MASK 0x03
+#define ADC128_REG_CONV_RATE 0x07
+#define ADC128_REG_ONESHOT 0x09
+#define ADC128_REG_SHUTDOWN 0x0a
+#define ADC128_REG_CONFIG_ADV 0x0b
+#define ADC128_REG_BUSY_STATUS 0x0c
+
+#define ADC128_REG_MAN_ID 0x3e
+#define ADC128_REG_DEV_ID 0x3f
+
+/* No. of voltage entries in adc128_attrs */
+#define ADC128_ATTR_NUM_VOLT (8 * 4)
+
+/* Voltage inputs visible per operation mode */
+static const u8 num_inputs[] = { 7, 8, 4, 6 };
+
+struct adc128_data {
+ struct i2c_client *client;
+ struct regulator *regulator;
+ int vref; /* Reference voltage in mV */
+ struct mutex update_lock;
+ u8 mode; /* Operation mode */
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u16 in[3][8]; /* Register value, normalized to 12 bit
+ * 0: input voltage
+ * 1: min limit
+ * 2: max limit
+ */
+ s16 temp[3]; /* Register value, normalized to 9 bit
+ * 0: sensor 1: limit 2: hyst
+ */
+ u8 alarms; /* alarm register value */
+};
+
+static struct adc128_data *adc128_update_device(struct device *dev)
+{
+ struct adc128_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct adc128_data *ret = data;
+ int i, rv;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ for (i = 0; i < num_inputs[data->mode]; i++) {
+ rv = i2c_smbus_read_word_swapped(client,
+ ADC128_REG_IN(i));
+ if (rv < 0)
+ goto abort;
+ data->in[0][i] = rv >> 4;
+
+ rv = i2c_smbus_read_byte_data(client,
+ ADC128_REG_IN_MIN(i));
+ if (rv < 0)
+ goto abort;
+ data->in[1][i] = rv << 4;
+
+ rv = i2c_smbus_read_byte_data(client,
+ ADC128_REG_IN_MAX(i));
+ if (rv < 0)
+ goto abort;
+ data->in[2][i] = rv << 4;
+ }
+
+ if (data->mode != 1) {
+ rv = i2c_smbus_read_word_swapped(client,
+ ADC128_REG_TEMP);
+ if (rv < 0)
+ goto abort;
+ data->temp[0] = rv >> 7;
+
+ rv = i2c_smbus_read_byte_data(client,
+ ADC128_REG_TEMP_MAX);
+ if (rv < 0)
+ goto abort;
+ data->temp[1] = rv << 1;
+
+ rv = i2c_smbus_read_byte_data(client,
+ ADC128_REG_TEMP_HYST);
+ if (rv < 0)
+ goto abort;
+ data->temp[2] = rv << 1;
+ }
+
+ rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM);
+ if (rv < 0)
+ goto abort;
+ data->alarms |= rv;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+ goto done;
+
+abort:
+ ret = ERR_PTR(rv);
+ data->valid = false;
+done:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t adc128_in_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adc128_data *data = adc128_update_device(dev);
+ int index = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = DIV_ROUND_CLOSEST(data->in[index][nr] * data->vref, 4095);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t adc128_in_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adc128_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ u8 reg, regval;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ /* 10 mV LSB on limit registers */
+ regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255);
+ data->in[index][nr] = regval << 4;
+ reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr);
+ i2c_smbus_write_byte_data(data->client, reg, regval);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t adc128_temp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adc128_data *data = adc128_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int temp;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ temp = sign_extend32(data->temp[index], 8);
+ return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */
+}
+
+static ssize_t adc128_temp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adc128_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ long val;
+ int err;
+ s8 regval;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+ data->temp[index] = regval << 1;
+ i2c_smbus_write_byte_data(data->client,
+ index == 1 ? ADC128_REG_TEMP_MAX
+ : ADC128_REG_TEMP_HYST,
+ regval);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t adc128_alarm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adc128_data *data = adc128_update_device(dev);
+ int mask = 1 << to_sensor_dev_attr(attr)->index;
+ u8 alarms;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ /*
+ * Clear an alarm after reporting it to user space. If it is still
+ * active, the next update sequence will set the alarm bit again.
+ */
+ alarms = data->alarms;
+ data->alarms &= ~mask;
+
+ return sprintf(buf, "%u\n", !!(alarms & mask));
+}
+
+static umode_t adc128_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct adc128_data *data = dev_get_drvdata(dev);
+
+ if (index < ADC128_ATTR_NUM_VOLT) {
+ /* Voltage, visible according to num_inputs[] */
+ if (index >= num_inputs[data->mode] * 4)
+ return 0;
+ } else {
+ /* Temperature, visible if not in mode 1 */
+ if (data->mode == 1)
+ return 0;
+ }
+
+ return attr->mode;
+}
+
+static SENSOR_DEVICE_ATTR_2_RO(in0_input, adc128_in, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in0_min, adc128_in, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in0_max, adc128_in, 0, 2);
+
+static SENSOR_DEVICE_ATTR_2_RO(in1_input, adc128_in, 1, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in1_min, adc128_in, 1, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in1_max, adc128_in, 1, 2);
+
+static SENSOR_DEVICE_ATTR_2_RO(in2_input, adc128_in, 2, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in2_min, adc128_in, 2, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_max, adc128_in, 2, 2);
+
+static SENSOR_DEVICE_ATTR_2_RO(in3_input, adc128_in, 3, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in3_min, adc128_in, 3, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in3_max, adc128_in, 3, 2);
+
+static SENSOR_DEVICE_ATTR_2_RO(in4_input, adc128_in, 4, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in4_min, adc128_in, 4, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in4_max, adc128_in, 4, 2);
+
+static SENSOR_DEVICE_ATTR_2_RO(in5_input, adc128_in, 5, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in5_min, adc128_in, 5, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in5_max, adc128_in, 5, 2);
+
+static SENSOR_DEVICE_ATTR_2_RO(in6_input, adc128_in, 6, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in6_min, adc128_in, 6, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in6_max, adc128_in, 6, 2);
+
+static SENSOR_DEVICE_ATTR_2_RO(in7_input, adc128_in, 7, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in7_min, adc128_in, 7, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in7_max, adc128_in, 7, 2);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, adc128_temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, adc128_temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, adc128_temp, 2);
+
+static SENSOR_DEVICE_ATTR_RO(in0_alarm, adc128_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(in1_alarm, adc128_alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(in2_alarm, adc128_alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(in3_alarm, adc128_alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(in4_alarm, adc128_alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(in5_alarm, adc128_alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(in6_alarm, adc128_alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(in7_alarm, adc128_alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, adc128_alarm, 7);
+
+static struct attribute *adc128_attrs[] = {
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adc128_group = {
+ .attrs = adc128_attrs,
+ .is_visible = adc128_is_visible,
+};
+__ATTRIBUTE_GROUPS(adc128);
+
+static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ int man_id, dev_id;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ man_id = i2c_smbus_read_byte_data(client, ADC128_REG_MAN_ID);
+ dev_id = i2c_smbus_read_byte_data(client, ADC128_REG_DEV_ID);
+ if (man_id != 0x01 || dev_id != 0x09)
+ return -ENODEV;
+
+ /* Check unused bits for confirmation */
+ if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG) & 0xf4)
+ return -ENODEV;
+ if (i2c_smbus_read_byte_data(client, ADC128_REG_CONV_RATE) & 0xfe)
+ return -ENODEV;
+ if (i2c_smbus_read_byte_data(client, ADC128_REG_ONESHOT) & 0xfe)
+ return -ENODEV;
+ if (i2c_smbus_read_byte_data(client, ADC128_REG_SHUTDOWN) & 0xfe)
+ return -ENODEV;
+ if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV) & 0xf8)
+ return -ENODEV;
+ if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc)
+ return -ENODEV;
+
+ strscpy(info->type, "adc128d818", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int adc128_init_client(struct adc128_data *data)
+{
+ struct i2c_client *client = data->client;
+ int err;
+ u8 regval = 0x0;
+
+ /*
+ * Reset chip to defaults.
+ * This makes most other initializations unnecessary.
+ */
+ err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x80);
+ if (err)
+ return err;
+
+ /* Set operation mode, if non-default */
+ 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;
+ }
+
+ /* Start monitoring */
+ err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int adc128_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regulator *regulator;
+ struct device *hwmon_dev;
+ struct adc128_data *data;
+ int err, vref;
+
+ data = devm_kzalloc(dev, sizeof(struct adc128_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* vref is optional. If specified, is used as chip reference voltage */
+ regulator = devm_regulator_get_optional(dev, "vref");
+ if (!IS_ERR(regulator)) {
+ data->regulator = regulator;
+ err = regulator_enable(regulator);
+ if (err < 0)
+ return err;
+ vref = regulator_get_voltage(regulator);
+ if (vref < 0) {
+ err = vref;
+ goto error;
+ }
+ data->vref = DIV_ROUND_CLOSEST(vref, 1000);
+ } else {
+ data->vref = 2560; /* 2.56V, in mV */
+ }
+
+ /* Operation mode is optional. If unspecified, keep current mode */
+ if (of_property_read_u8(dev->of_node, "ti,mode", &data->mode) == 0) {
+ if (data->mode > 3) {
+ dev_err(dev, "invalid operation mode %d\n",
+ data->mode);
+ err = -EINVAL;
+ goto error;
+ }
+ } else {
+ err = i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV);
+ if (err < 0)
+ goto error;
+ data->mode = (err >> 1) & ADC128_REG_MASK;
+ }
+
+ data->client = client;
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the chip */
+ err = adc128_init_client(data);
+ if (err < 0)
+ goto error;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, adc128_groups);
+ if (IS_ERR(hwmon_dev)) {
+ err = PTR_ERR(hwmon_dev);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (data->regulator)
+ regulator_disable(data->regulator);
+ return err;
+}
+
+static void adc128_remove(struct i2c_client *client)
+{
+ struct adc128_data *data = i2c_get_clientdata(client);
+
+ if (data->regulator)
+ regulator_disable(data->regulator);
+}
+
+static const struct i2c_device_id adc128_id[] = {
+ { "adc128d818", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adc128_id);
+
+static const struct of_device_id __maybe_unused adc128_of_match[] = {
+ { .compatible = "ti,adc128d818" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adc128_of_match);
+
+static struct i2c_driver adc128_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adc128d818",
+ .of_match_table = of_match_ptr(adc128_of_match),
+ },
+ .probe_new = adc128_probe,
+ .remove = adc128_remove,
+ .id_table = adc128_id,
+ .detect = adc128_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adc128_driver);
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("Driver for ADC128D818");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adcxx.c b/drivers/hwmon/adcxx.c
new file mode 100644
index 000000000..de37bce24
--- /dev/null
+++ b/drivers/hwmon/adcxx.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * adcxx.c
+ *
+ * The adcxx4s is an AD converter family from National Semiconductor (NS).
+ *
+ * Copyright (c) 2008 Marc Pignat <marc.pignat@hevs.ch>
+ *
+ * The adcxx4s communicates with a host processor via an SPI/Microwire Bus
+ * interface. This driver supports the whole family of devices with name
+ * ADC<bb><c>S<sss>, where
+ * * bb is the resolution in number of bits (8, 10, 12)
+ * * c is the number of channels (1, 2, 4, 8)
+ * * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 kSPS
+ * and 101 for 1 MSPS)
+ *
+ * Complete datasheets are available at National's website here:
+ * http://www.national.com/ds/DC/ADC<bb><c>S<sss>.pdf
+ *
+ * Handling of 8, 10 and 12 bits converters are the same, the
+ * unavailable bits are 0 :)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+
+#define DRVNAME "adcxx"
+
+struct adcxx {
+ struct device *hwmon_dev;
+ struct mutex lock;
+ u32 channels;
+ u32 reference; /* in millivolts */
+};
+
+/* sysfs hook function */
+static ssize_t adcxx_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adcxx *adc = spi_get_drvdata(spi);
+ u8 tx_buf[2];
+ u8 rx_buf[2];
+ int status;
+ u32 value;
+
+ if (mutex_lock_interruptible(&adc->lock))
+ return -ERESTARTSYS;
+
+ if (adc->channels == 1) {
+ status = spi_read(spi, rx_buf, sizeof(rx_buf));
+ } else {
+ tx_buf[0] = attr->index << 3; /* other bits are don't care */
+ status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf),
+ rx_buf, sizeof(rx_buf));
+ }
+ if (status < 0) {
+ dev_warn(dev, "SPI synch. transfer failed with status %d\n",
+ status);
+ goto out;
+ }
+
+ value = (rx_buf[0] << 8) + rx_buf[1];
+ dev_dbg(dev, "raw value = 0x%x\n", value);
+
+ value = value * adc->reference >> 12;
+ status = sprintf(buf, "%d\n", value);
+out:
+ mutex_unlock(&adc->lock);
+ return status;
+}
+
+static ssize_t adcxx_min_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ /* The minimum reference is 0 for this chip family */
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t adcxx_max_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct adcxx *adc = spi_get_drvdata(spi);
+ u32 reference;
+
+ if (mutex_lock_interruptible(&adc->lock))
+ return -ERESTARTSYS;
+
+ reference = adc->reference;
+
+ mutex_unlock(&adc->lock);
+
+ return sprintf(buf, "%d\n", reference);
+}
+
+static ssize_t adcxx_max_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct adcxx *adc = spi_get_drvdata(spi);
+ unsigned long value;
+
+ if (kstrtoul(buf, 10, &value))
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&adc->lock))
+ return -ERESTARTSYS;
+
+ adc->reference = value;
+
+ mutex_unlock(&adc->lock);
+
+ return count;
+}
+
+static ssize_t adcxx_name_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
+static struct sensor_device_attribute ad_input[] = {
+ SENSOR_ATTR_RO(name, adcxx_name, 0),
+ SENSOR_ATTR_RO(in_min, adcxx_min, 0),
+ SENSOR_ATTR_RW(in_max, adcxx_max, 0),
+ SENSOR_ATTR_RO(in0_input, adcxx, 0),
+ SENSOR_ATTR_RO(in1_input, adcxx, 1),
+ SENSOR_ATTR_RO(in2_input, adcxx, 2),
+ SENSOR_ATTR_RO(in3_input, adcxx, 3),
+ SENSOR_ATTR_RO(in4_input, adcxx, 4),
+ SENSOR_ATTR_RO(in5_input, adcxx, 5),
+ SENSOR_ATTR_RO(in6_input, adcxx, 6),
+ SENSOR_ATTR_RO(in7_input, adcxx, 7),
+};
+
+/*----------------------------------------------------------------------*/
+
+static int adcxx_probe(struct spi_device *spi)
+{
+ int channels = spi_get_device_id(spi)->driver_data;
+ struct adcxx *adc;
+ int status;
+ int i;
+
+ adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL);
+ if (!adc)
+ return -ENOMEM;
+
+ /* set a default value for the reference */
+ adc->reference = 3300;
+ adc->channels = channels;
+ mutex_init(&adc->lock);
+
+ mutex_lock(&adc->lock);
+
+ spi_set_drvdata(spi, adc);
+
+ for (i = 0; i < 3 + adc->channels; i++) {
+ status = device_create_file(&spi->dev, &ad_input[i].dev_attr);
+ if (status) {
+ dev_err(&spi->dev, "device_create_file failed.\n");
+ goto out_err;
+ }
+ }
+
+ adc->hwmon_dev = hwmon_device_register(&spi->dev);
+ if (IS_ERR(adc->hwmon_dev)) {
+ dev_err(&spi->dev, "hwmon_device_register failed.\n");
+ status = PTR_ERR(adc->hwmon_dev);
+ goto out_err;
+ }
+
+ mutex_unlock(&adc->lock);
+ return 0;
+
+out_err:
+ for (i--; i >= 0; i--)
+ device_remove_file(&spi->dev, &ad_input[i].dev_attr);
+
+ mutex_unlock(&adc->lock);
+ return status;
+}
+
+static void adcxx_remove(struct spi_device *spi)
+{
+ struct adcxx *adc = spi_get_drvdata(spi);
+ int i;
+
+ mutex_lock(&adc->lock);
+ hwmon_device_unregister(adc->hwmon_dev);
+ for (i = 0; i < 3 + adc->channels; i++)
+ device_remove_file(&spi->dev, &ad_input[i].dev_attr);
+
+ mutex_unlock(&adc->lock);
+}
+
+static const struct spi_device_id adcxx_ids[] = {
+ { "adcxx1s", 1 },
+ { "adcxx2s", 2 },
+ { "adcxx4s", 4 },
+ { "adcxx8s", 8 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, adcxx_ids);
+
+static struct spi_driver adcxx_driver = {
+ .driver = {
+ .name = "adcxx",
+ },
+ .id_table = adcxx_ids,
+ .probe = adcxx_probe,
+ .remove = adcxx_remove,
+};
+
+module_spi_driver(adcxx_driver);
+
+MODULE_AUTHOR("Marc Pignat");
+MODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
new file mode 100644
index 000000000..2dc45e958
--- /dev/null
+++ b/drivers/hwmon/adm1021.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * adm1021.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
+ * Philip Edelbrock <phil@netroedge.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+
+enum chips {
+ adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066 };
+
+/* adm1021 constants specified below */
+
+/* The adm1021 registers */
+/* Read-only */
+/* For nr in 0-1 */
+#define ADM1021_REG_TEMP(nr) (nr)
+#define ADM1021_REG_STATUS 0x02
+/* 0x41 = AD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi */
+#define ADM1021_REG_MAN_ID 0xFE
+/* ADM1021 = 0x0X, ADM1023 = 0x3X */
+#define ADM1021_REG_DEV_ID 0xFF
+/* These use different addresses for reading/writing */
+#define ADM1021_REG_CONFIG_R 0x03
+#define ADM1021_REG_CONFIG_W 0x09
+#define ADM1021_REG_CONV_RATE_R 0x04
+#define ADM1021_REG_CONV_RATE_W 0x0A
+/* These are for the ADM1023's additional precision on the remote temp sensor */
+#define ADM1023_REG_REM_TEMP_PREC 0x10
+#define ADM1023_REG_REM_OFFSET 0x11
+#define ADM1023_REG_REM_OFFSET_PREC 0x12
+#define ADM1023_REG_REM_TOS_PREC 0x13
+#define ADM1023_REG_REM_THYST_PREC 0x14
+/* limits */
+/* For nr in 0-1 */
+#define ADM1021_REG_TOS_R(nr) (0x05 + 2 * (nr))
+#define ADM1021_REG_TOS_W(nr) (0x0B + 2 * (nr))
+#define ADM1021_REG_THYST_R(nr) (0x06 + 2 * (nr))
+#define ADM1021_REG_THYST_W(nr) (0x0C + 2 * (nr))
+/* write-only */
+#define ADM1021_REG_ONESHOT 0x0F
+
+/* Initial values */
+
+/*
+ * Note: Even though I left the low and high limits named os and hyst,
+ * they don't quite work like a thermostat the way the LM75 does. I.e.,
+ * a lower temp than THYST actually triggers an alarm instead of
+ * clearing it. Weird, ey? --Phil
+ */
+
+/* Each client has this additional data */
+struct adm1021_data {
+ struct i2c_client *client;
+ enum chips type;
+
+ const struct attribute_group *groups[3];
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ char low_power; /* !=0 if device in low power mode */
+ unsigned long last_updated; /* In jiffies */
+
+ int temp_max[2]; /* Register values */
+ int temp_min[2];
+ int temp[2];
+ u8 alarms;
+ /* Special values for ADM1023 only */
+ u8 remote_temp_offset;
+ u8 remote_temp_offset_prec;
+};
+
+/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
+static bool read_only;
+
+static struct adm1021_data *adm1021_update_device(struct device *dev)
+{
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ int i;
+
+ dev_dbg(dev, "Starting adm1021 update\n");
+
+ for (i = 0; i < 2; i++) {
+ data->temp[i] = 1000 *
+ (s8) i2c_smbus_read_byte_data(
+ client, ADM1021_REG_TEMP(i));
+ data->temp_max[i] = 1000 *
+ (s8) i2c_smbus_read_byte_data(
+ client, ADM1021_REG_TOS_R(i));
+ if (data->type != lm84) {
+ data->temp_min[i] = 1000 *
+ (s8) i2c_smbus_read_byte_data(client,
+ ADM1021_REG_THYST_R(i));
+ }
+ }
+ data->alarms = i2c_smbus_read_byte_data(client,
+ ADM1021_REG_STATUS) & 0x7c;
+ if (data->type == adm1023) {
+ /*
+ * The ADM1023 provides 3 extra bits of precision for
+ * the remote sensor in extra registers.
+ */
+ data->temp[1] += 125 * (i2c_smbus_read_byte_data(
+ client, ADM1023_REG_REM_TEMP_PREC) >> 5);
+ data->temp_max[1] += 125 * (i2c_smbus_read_byte_data(
+ client, ADM1023_REG_REM_TOS_PREC) >> 5);
+ data->temp_min[1] += 125 * (i2c_smbus_read_byte_data(
+ client, ADM1023_REG_REM_THYST_PREC) >> 5);
+ data->remote_temp_offset =
+ i2c_smbus_read_byte_data(client,
+ ADM1023_REG_REM_OFFSET);
+ data->remote_temp_offset_prec =
+ i2c_smbus_read_byte_data(client,
+ ADM1023_REG_REM_OFFSET_PREC);
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct adm1021_data *data = adm1021_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->temp[index]);
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct adm1021_data *data = adm1021_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->temp_max[index]);
+}
+
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct adm1021_data *data = adm1021_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->temp_min[index]);
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1021_data *data = adm1021_update_device(dev);
+ return sprintf(buf, "%u\n", (data->alarms >> index) & 1);
+}
+
+static ssize_t alarms_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1021_data *data = adm1021_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+ int reg_val, err;
+
+ err = kstrtol(buf, 10, &temp);
+ if (err)
+ return err;
+ temp /= 1000;
+
+ mutex_lock(&data->update_lock);
+ reg_val = clamp_val(temp, -128, 127);
+ data->temp_max[index] = reg_val * 1000;
+ if (!read_only)
+ i2c_smbus_write_byte_data(client, ADM1021_REG_TOS_W(index),
+ reg_val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+ int reg_val, err;
+
+ err = kstrtol(buf, 10, &temp);
+ if (err)
+ return err;
+ temp /= 1000;
+
+ mutex_lock(&data->update_lock);
+ reg_val = clamp_val(temp, -128, 127);
+ data->temp_min[index] = reg_val * 1000;
+ if (!read_only)
+ i2c_smbus_write_byte_data(client, ADM1021_REG_THYST_W(index),
+ reg_val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t low_power_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adm1021_data *data = adm1021_update_device(dev);
+ return sprintf(buf, "%d\n", data->low_power);
+}
+
+static ssize_t low_power_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adm1021_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ char low_power;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ low_power = val != 0;
+
+ mutex_lock(&data->update_lock);
+ if (low_power != data->low_power) {
+ int config = i2c_smbus_read_byte_data(
+ client, ADM1021_REG_CONFIG_R);
+ data->low_power = low_power;
+ i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W,
+ (config & 0xBF) | (low_power << 6));
+ }
+ 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_min, temp_min, 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_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2);
+
+static DEVICE_ATTR_RO(alarms);
+static DEVICE_ATTR_RW(low_power);
+
+static struct attribute *adm1021_attributes[] = {
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_low_power.attr,
+ NULL
+};
+
+static const struct attribute_group adm1021_group = {
+ .attrs = adm1021_attributes,
+};
+
+static struct attribute *adm1021_min_attributes[] = {
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adm1021_min_group = {
+ .attrs = adm1021_min_attributes,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int adm1021_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *type_name;
+ 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");
+ return -ENODEV;
+ }
+
+ status = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS);
+ conv_rate = i2c_smbus_read_byte_data(client,
+ ADM1021_REG_CONV_RATE_R);
+ config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R);
+
+ /* Check unused bits */
+ if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) {
+ pr_debug("detect failed, chip not detected!\n");
+ return -ENODEV;
+ }
+
+ /* Determine the chip type. */
+ man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID);
+ dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID);
+
+ if (man_id < 0 || dev_id < 0)
+ return -ENODEV;
+
+ 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) {
+ if ((dev_id & 0xF0) == 0x30)
+ type_name = "adm1023";
+ else if ((dev_id & 0xF0) == 0x00)
+ type_name = "adm1021";
+ else
+ return -ENODEV;
+ } else if (man_id == 0x49)
+ type_name = "thmc10";
+ else if (man_id == 0x23)
+ type_name = "gl523sm";
+ else if (man_id == 0x54)
+ type_name = "mc1066";
+ else {
+ int lte, rte, lhi, rhi, llo, rlo;
+
+ /* extra checks for LM84 and MAX1617 to avoid misdetections */
+
+ llo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(0));
+ rlo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(1));
+
+ /* fail if any of the additional register reads failed */
+ if (llo < 0 || rlo < 0)
+ return -ENODEV;
+
+ lte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(0));
+ rte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(1));
+ lhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(0));
+ rhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(1));
+
+ /*
+ * Fail for negative temperatures and negative high limits.
+ * This check also catches read errors on the tested registers.
+ */
+ if ((s8)lte < 0 || (s8)rte < 0 || (s8)lhi < 0 || (s8)rhi < 0)
+ return -ENODEV;
+
+ /* fail if all registers hold the same value */
+ if (lte == rte && lte == lhi && lte == rhi && lte == llo
+ && lte == rlo)
+ return -ENODEV;
+
+ /*
+ * LM84 Mfr ID is in a different place,
+ * 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;
+ type_name = "max1617";
+ }
+ }
+
+ pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n",
+ type_name, i2c_adapter_id(adapter), client->addr);
+ strscpy(info->type, type_name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void adm1021_init_client(struct i2c_client *client)
+{
+ /* Enable ADC and disable suspend mode */
+ i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W,
+ i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R) & 0xBF);
+ /* Set Conversion rate to 1/sec (this can be tinkered with) */
+ i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04);
+}
+
+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;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct adm1021_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->type = i2c_match_id(adm1021_id, client)->driver_data;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the ADM1021 chip */
+ if (data->type != lm84 && !read_only)
+ adm1021_init_client(client);
+
+ data->groups[0] = &adm1021_group;
+ if (data->type != lm84)
+ data->groups[1] = &adm1021_min_group;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adm1021_id[] = {
+ { "adm1021", adm1021 },
+ { "adm1023", adm1023 },
+ { "max1617", max1617 },
+ { "max1617a", max1617a },
+ { "thmc10", thmc10 },
+ { "lm84", lm84 },
+ { "gl523sm", gl523sm },
+ { "mc1066", mc1066 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1021_id);
+
+static struct i2c_driver adm1021_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adm1021",
+ },
+ .probe_new = adm1021_probe,
+ .id_table = adm1021_id,
+ .detect = adm1021_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adm1021_driver);
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
+ "Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("adm1021 driver");
+MODULE_LICENSE("GPL");
+
+module_param(read_only, bool, 0);
+MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c
new file mode 100644
index 000000000..2984c4f98
--- /dev/null
+++ b/drivers/hwmon/adm1025.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * adm1025.c
+ *
+ * Copyright (C) 2000 Chen-Yuan Wu <gwu@esoft.com>
+ * Copyright (C) 2003-2009 Jean Delvare <jdelvare@suse.de>
+ *
+ * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6
+ * voltages (including its own power source) and up to two temperatures
+ * (its own plus up to one external one). Voltages are scaled internally
+ * (which is not the common way) with ratios such that the nominal value
+ * of each voltage correspond to a register value of 192 (which means a
+ * 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:
+ * 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
+ * on-chip 100k pull-ups on the VID inputs". It doesn't make any
+ * difference for us.
+ *
+ * This driver also supports the NE1619, a sensor chip made by Philips.
+ * That chip is similar to the ADM1025A, with a few differences. The only
+ * difference that matters to us is that the NE1619 has only two possible
+ * addresses while the ADM1025A has a third one. Complete datasheet can be
+ * obtained from Philips's website at:
+ * http://www.semiconductors.philips.com/pip/NE1619DS.html
+ *
+ * Since the ADM1025 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/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/*
+ * Addresses to scan
+ * ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e.
+ * NE1619 has two possible addresses: 0x2c and 0x2d.
+ */
+
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+enum chips { adm1025, ne1619 };
+
+/*
+ * The ADM1025 registers
+ */
+
+#define ADM1025_REG_MAN_ID 0x3E
+#define ADM1025_REG_CHIP_ID 0x3F
+#define ADM1025_REG_CONFIG 0x40
+#define ADM1025_REG_STATUS1 0x41
+#define ADM1025_REG_STATUS2 0x42
+#define ADM1025_REG_IN(nr) (0x20 + (nr))
+#define ADM1025_REG_IN_MAX(nr) (0x2B + (nr) * 2)
+#define ADM1025_REG_IN_MIN(nr) (0x2C + (nr) * 2)
+#define ADM1025_REG_TEMP(nr) (0x26 + (nr))
+#define ADM1025_REG_TEMP_HIGH(nr) (0x37 + (nr) * 2)
+#define ADM1025_REG_TEMP_LOW(nr) (0x38 + (nr) * 2)
+#define ADM1025_REG_VID 0x47
+#define ADM1025_REG_VID4 0x49
+
+/*
+ * Conversions and various macros
+ * The ADM1025 uses signed 8-bit values for temperatures.
+ */
+
+static const int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
+
+#define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192)
+#define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \
+ (val) >= (scale) * 255 / 192 ? 255 : \
+ ((val) * 192 + (scale) / 2) / (scale))
+
+#define TEMP_FROM_REG(reg) ((reg) * 1000)
+#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \
+ (val) >= 126500 ? 127 : \
+ (((val) < 0 ? (val) - 500 : \
+ (val) + 500) / 1000))
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct adm1025_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+ struct mutex update_lock;
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ u8 in[6]; /* register value */
+ u8 in_max[6]; /* register value */
+ u8 in_min[6]; /* register value */
+ s8 temp[2]; /* register value */
+ s8 temp_min[2]; /* register value */
+ s8 temp_max[2]; /* register value */
+ u16 alarms; /* register values, combined */
+ u8 vid; /* register values, combined */
+ u8 vrm;
+};
+
+static struct adm1025_data *adm1025_update_device(struct device *dev)
+{
+ struct adm1025_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
+ int i;
+
+ dev_dbg(&client->dev, "Updating data.\n");
+ for (i = 0; i < 6; i++) {
+ data->in[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN(i));
+ data->in_min[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN_MIN(i));
+ data->in_max[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN_MAX(i));
+ }
+ for (i = 0; i < 2; i++) {
+ data->temp[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP(i));
+ data->temp_min[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP_LOW(i));
+ data->temp_max[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP_HIGH(i));
+ }
+ data->alarms = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_STATUS1)
+ | (i2c_smbus_read_byte_data(client,
+ ADM1025_REG_STATUS2) << 8);
+ data->vid = (i2c_smbus_read_byte_data(client,
+ ADM1025_REG_VID) & 0x0f)
+ | ((i2c_smbus_read_byte_data(client,
+ ADM1025_REG_VID4) & 0x01) << 4);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t
+in_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in[index],
+ in_scale[index]));
+}
+
+static ssize_t
+in_min_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[index],
+ in_scale[index]));
+}
+
+static ssize_t
+in_max_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[index],
+ in_scale[index]));
+}
+
+static ssize_t
+temp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[index]));
+}
+
+static ssize_t
+temp_min_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[index]));
+}
+
+static ssize_t
+temp_max_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[index]));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[index] = IN_TO_REG(val, in_scale[index]);
+ i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(index),
+ data->in_min[index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_max[index] = IN_TO_REG(val, in_scale[index]);
+ i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(index),
+ data->in_max[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);
+
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[index] = TEMP_TO_REG(val);
+ i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(index),
+ data->temp_min[index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct adm1025_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[index] = TEMP_TO_REG(val);
+ i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(index),
+ data->temp_max[index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
+
+static ssize_t
+alarms_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+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 adm1025_data *data = adm1025_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, 5);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp1_fault, alarm, 14);
+
+static ssize_t
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", 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 adm1025_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)
+{
+ struct adm1025_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);
+
+/*
+ * Real code
+ */
+
+static struct attribute *adm1025_attributes[] = {
+ &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_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group adm1025_group = {
+ .attrs = adm1025_attributes,
+};
+
+static struct attribute *adm1025_attributes_in4[] = {
+ &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,
+ NULL
+};
+
+static const struct attribute_group adm1025_group_in4 = {
+ .attrs = adm1025_attributes_in4,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int adm1025_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *name;
+ u8 man_id, chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Check for unused bits */
+ if ((i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG) & 0x80)
+ || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS1) & 0xC0)
+ || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS2) & 0xBC)) {
+ dev_dbg(&adapter->dev, "ADM1025 detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ /* Identification */
+ chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID);
+ if ((chip_id & 0xF0) != 0x20)
+ return -ENODEV;
+
+ man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID);
+ if (man_id == 0x41)
+ name = "adm1025";
+ else if (man_id == 0xA1 && client->addr != 0x2E)
+ name = "ne1619";
+ else
+ return -ENODEV;
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void adm1025_init_client(struct i2c_client *client)
+{
+ u8 reg;
+ struct adm1025_data *data = i2c_get_clientdata(client);
+ int i;
+
+ data->vrm = vid_which_vrm();
+
+ /*
+ * Set high limits
+ * Usually we avoid setting limits on driver init, but it happens
+ * that the ADM1025 comes with stupid default limits (all registers
+ * set to 0). In case the chip has not gone through any limit
+ * setting yet, we better set the high limits to the max so that
+ * no alarm triggers.
+ */
+ for (i = 0; i < 6; i++) {
+ reg = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN_MAX(i));
+ if (reg == 0)
+ i2c_smbus_write_byte_data(client,
+ ADM1025_REG_IN_MAX(i),
+ 0xFF);
+ }
+ for (i = 0; i < 2; i++) {
+ reg = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP_HIGH(i));
+ if (reg == 0)
+ i2c_smbus_write_byte_data(client,
+ ADM1025_REG_TEMP_HIGH(i),
+ 0x7F);
+ }
+
+ /*
+ * Start the conversions
+ */
+ reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
+ if (!(reg & 0x01))
+ i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG,
+ (reg&0x7E)|0x01);
+}
+
+static int adm1025_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct adm1025_data *data;
+ u8 config;
+
+ data = devm_kzalloc(dev, sizeof(struct adm1025_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the ADM1025 chip */
+ adm1025_init_client(client);
+
+ /* sysfs hooks */
+ data->groups[0] = &adm1025_group;
+ /* Pin 11 is either in4 (+12V) or VID4 */
+ config = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
+ if (!(config & 0x20))
+ data->groups[1] = &adm1025_group_in4;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adm1025_id[] = {
+ { "adm1025", adm1025 },
+ { "ne1619", ne1619 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1025_id);
+
+static struct i2c_driver adm1025_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adm1025",
+ },
+ .probe_new = adm1025_probe,
+ .id_table = adm1025_id,
+ .detect = adm1025_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adm1025_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("ADM1025 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
new file mode 100644
index 000000000..1f084f708
--- /dev/null
+++ b/drivers/hwmon/adm1026.c
@@ -0,0 +1,1873 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * adm1026.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
+ * Copyright (C) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
+ *
+ * Chip details at:
+ *
+ * <https://www.onsemi.com/PowerSolutions/product.do?id=ADM1026>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+static int gpio_input[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_inverted[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_normal[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+module_param_array(gpio_input, int, NULL, 0);
+MODULE_PARM_DESC(gpio_input, "List of GPIO pins (0-16) to program as inputs");
+module_param_array(gpio_output, int, NULL, 0);
+MODULE_PARM_DESC(gpio_output,
+ "List of GPIO pins (0-16) to program as outputs");
+module_param_array(gpio_inverted, int, NULL, 0);
+MODULE_PARM_DESC(gpio_inverted,
+ "List of GPIO pins (0-16) to program as inverted");
+module_param_array(gpio_normal, int, NULL, 0);
+MODULE_PARM_DESC(gpio_normal,
+ "List of GPIO pins (0-16) to program as normal/non-inverted");
+module_param_array(gpio_fan, int, NULL, 0);
+MODULE_PARM_DESC(gpio_fan, "List of GPIO pins (0-7) to program as fan tachs");
+
+/* Many ADM1026 constants specified below */
+
+/* The ADM1026 registers */
+#define ADM1026_REG_CONFIG1 0x00
+#define CFG1_MONITOR 0x01
+#define CFG1_INT_ENABLE 0x02
+#define CFG1_INT_CLEAR 0x04
+#define CFG1_AIN8_9 0x08
+#define CFG1_THERM_HOT 0x10
+#define CFG1_DAC_AFC 0x20
+#define CFG1_PWM_AFC 0x40
+#define CFG1_RESET 0x80
+
+#define ADM1026_REG_CONFIG2 0x01
+/* CONFIG2 controls FAN0/GPIO0 through FAN7/GPIO7 */
+
+#define ADM1026_REG_CONFIG3 0x07
+#define CFG3_GPIO16_ENABLE 0x01
+#define CFG3_CI_CLEAR 0x02
+#define CFG3_VREF_250 0x04
+#define CFG3_GPIO16_DIR 0x40
+#define CFG3_GPIO16_POL 0x80
+
+#define ADM1026_REG_E2CONFIG 0x13
+#define E2CFG_READ 0x01
+#define E2CFG_WRITE 0x02
+#define E2CFG_ERASE 0x04
+#define E2CFG_ROM 0x08
+#define E2CFG_CLK_EXT 0x80
+
+/*
+ * There are 10 general analog inputs and 7 dedicated inputs
+ * They are:
+ * 0 - 9 = AIN0 - AIN9
+ * 10 = Vbat
+ * 11 = 3.3V Standby
+ * 12 = 3.3V Main
+ * 13 = +5V
+ * 14 = Vccp (CPU core voltage)
+ * 15 = +12V
+ * 16 = -12V
+ */
+static u16 ADM1026_REG_IN[] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x27, 0x29, 0x26, 0x2a,
+ 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+ };
+static u16 ADM1026_REG_IN_MIN[] = {
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d,
+ 0x5e, 0x5f, 0x6d, 0x49, 0x6b, 0x4a,
+ 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+ };
+static u16 ADM1026_REG_IN_MAX[] = {
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x57, 0x6c, 0x41, 0x6a, 0x42,
+ 0x43, 0x44, 0x45, 0x46, 0x47
+ };
+
+/*
+ * Temperatures are:
+ * 0 - Internal
+ * 1 - External 1
+ * 2 - External 2
+ */
+static u16 ADM1026_REG_TEMP[] = { 0x1f, 0x28, 0x29 };
+static u16 ADM1026_REG_TEMP_MIN[] = { 0x69, 0x48, 0x49 };
+static u16 ADM1026_REG_TEMP_MAX[] = { 0x68, 0x40, 0x41 };
+static u16 ADM1026_REG_TEMP_TMIN[] = { 0x10, 0x11, 0x12 };
+static u16 ADM1026_REG_TEMP_THERM[] = { 0x0d, 0x0e, 0x0f };
+static u16 ADM1026_REG_TEMP_OFFSET[] = { 0x1e, 0x6e, 0x6f };
+
+#define ADM1026_REG_FAN(nr) (0x38 + (nr))
+#define ADM1026_REG_FAN_MIN(nr) (0x60 + (nr))
+#define ADM1026_REG_FAN_DIV_0_3 0x02
+#define ADM1026_REG_FAN_DIV_4_7 0x03
+
+#define ADM1026_REG_DAC 0x04
+#define ADM1026_REG_PWM 0x05
+
+#define ADM1026_REG_GPIO_CFG_0_3 0x08
+#define ADM1026_REG_GPIO_CFG_4_7 0x09
+#define ADM1026_REG_GPIO_CFG_8_11 0x0a
+#define ADM1026_REG_GPIO_CFG_12_15 0x0b
+/* CFG_16 in REG_CFG3 */
+#define ADM1026_REG_GPIO_STATUS_0_7 0x24
+#define ADM1026_REG_GPIO_STATUS_8_15 0x25
+/* STATUS_16 in REG_STATUS4 */
+#define ADM1026_REG_GPIO_MASK_0_7 0x1c
+#define ADM1026_REG_GPIO_MASK_8_15 0x1d
+/* MASK_16 in REG_MASK4 */
+
+#define ADM1026_REG_COMPANY 0x16
+#define ADM1026_REG_VERSTEP 0x17
+/* These are the recognized values for the above regs */
+#define ADM1026_COMPANY_ANALOG_DEV 0x41
+#define ADM1026_VERSTEP_GENERIC 0x40
+#define ADM1026_VERSTEP_ADM1026 0x44
+
+#define ADM1026_REG_MASK1 0x18
+#define ADM1026_REG_MASK2 0x19
+#define ADM1026_REG_MASK3 0x1a
+#define ADM1026_REG_MASK4 0x1b
+
+#define ADM1026_REG_STATUS1 0x20
+#define ADM1026_REG_STATUS2 0x21
+#define ADM1026_REG_STATUS3 0x22
+#define ADM1026_REG_STATUS4 0x23
+
+#define ADM1026_FAN_ACTIVATION_TEMP_HYST -6
+#define ADM1026_FAN_CONTROL_TEMP_RANGE 20
+#define ADM1026_PWM_MAX 255
+
+/*
+ * Conversions. Rounding and limit checking is only done on the TO_REG
+ * variants. Note that you should be a bit careful with which arguments
+ * these macros are called: arguments may be evaluated more than once.
+ */
+
+/*
+ * IN are scaled according to built-in resistors. These are the
+ * voltages corresponding to 3/4 of full scale (192 or 0xc0)
+ * NOTE: The -12V input needs an additional factor to account
+ * for the Vref pullup resistor.
+ * NEG12_OFFSET = SCALE * Vref / V-192 - Vref
+ * = 13875 * 2.50 / 1.875 - 2500
+ * = 16000
+ *
+ * The values in this table are based on Table II, page 15 of the
+ * datasheet.
+ */
+static int adm1026_scaling[] = { /* .001 Volts */
+ 2250, 2250, 2250, 2250, 2250, 2250,
+ 1875, 1875, 1875, 1875, 3000, 3330,
+ 3330, 4995, 2250, 12000, 13875
+ };
+#define NEG12_OFFSET 16000
+#define SCALE(val, from, to) (((val)*(to) + ((from)/2))/(from))
+#define INS_TO_REG(n, val) \
+ SCALE(clamp_val(val, 0, 255 * adm1026_scaling[n] / 192), \
+ adm1026_scaling[n], 192)
+#define INS_FROM_REG(n, val) (SCALE(val, 192, adm1026_scaling[n]))
+
+/*
+ * FAN speed is measured using 22.5kHz clock and counts for 2 pulses
+ * and we assume a 2 pulse-per-rev fan tach signal
+ * 22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000
+ */
+#define FAN_TO_REG(val, div) ((val) <= 0 ? 0xff : \
+ clamp_val(1350000 / ((val) * (div)), \
+ 1, 254))
+#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 0xff ? 0 : \
+ 1350000 / ((val) * (div)))
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val) >= 8 ? 3 : (val) >= 4 ? 2 : (val) >= 2 ? 1 : 0)
+
+/* Temperature is reported in 1 degC increments */
+#define TEMP_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), \
+ 1000)
+#define TEMP_FROM_REG(val) ((val) * 1000)
+#define OFFSET_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), \
+ 1000)
+#define OFFSET_FROM_REG(val) ((val) * 1000)
+
+#define PWM_TO_REG(val) (clamp_val(val, 0, 255))
+#define PWM_FROM_REG(val) (val)
+
+#define PWM_MIN_TO_REG(val) ((val) & 0xf0)
+#define PWM_MIN_FROM_REG(val) (((val) & 0xf0) + ((val) >> 4))
+
+/*
+ * Analog output is a voltage, and scaled to millivolts. The datasheet
+ * indicates that the DAC could be used to drive the fans, but in our
+ * example board (Arima HDAMA) it isn't connected to the fans at all.
+ */
+#define DAC_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, 0, 2500) * 255, \
+ 2500)
+#define DAC_FROM_REG(val) (((val) * 2500) / 255)
+
+/*
+ * Chip sampling rates
+ *
+ * Some sensors are not updated more frequently than once per second
+ * so it doesn't make sense to read them more often than that.
+ * We cache the results and return the saved data if the driver
+ * is called again before a second has elapsed.
+ *
+ * Also, there is significant configuration data for this chip
+ * So, we keep the config data up to date in the cache
+ * when it is written and only sample it once every 5 *minutes*
+ */
+#define ADM1026_DATA_INTERVAL (1 * HZ)
+#define ADM1026_CONFIG_INTERVAL (5 * 60 * HZ)
+
+/*
+ * We allow for multiple chips in a single system.
+ *
+ * For each registered ADM1026, we need to keep state information
+ * at client->data. The adm1026_data structure is dynamically
+ * allocated, when a new client structure is allocated.
+ */
+
+struct pwm_data {
+ u8 pwm;
+ u8 enable;
+ u8 auto_pwm_min;
+};
+
+struct adm1026_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_reading; /* In jiffies */
+ unsigned long last_config; /* In jiffies */
+
+ u8 in[17]; /* Register value */
+ u8 in_max[17]; /* Register value */
+ u8 in_min[17]; /* Register value */
+ s8 temp[3]; /* Register value */
+ s8 temp_min[3]; /* Register value */
+ s8 temp_max[3]; /* Register value */
+ s8 temp_tmin[3]; /* Register value */
+ s8 temp_crit[3]; /* Register value */
+ s8 temp_offset[3]; /* Register value */
+ u8 fan[8]; /* Register value */
+ u8 fan_min[8]; /* Register value */
+ u8 fan_div[8]; /* Decoded value */
+ struct pwm_data pwm1; /* Pwm control values */
+ u8 vrm; /* VRM version */
+ u8 analog_out; /* Register value (DAC) */
+ long alarms; /* Register encoding, combined */
+ long alarm_mask; /* Register encoding, combined */
+ long gpio; /* Register encoding, combined */
+ long gpio_mask; /* Register encoding, combined */
+ u8 gpio_config[17]; /* Decoded value */
+ u8 config1; /* Register value */
+ u8 config2; /* Register value */
+ u8 config3; /* Register value */
+};
+
+static int adm1026_read_value(struct i2c_client *client, u8 reg)
+{
+ int res;
+
+ if (reg < 0x80) {
+ /* "RAM" locations */
+ res = i2c_smbus_read_byte_data(client, reg) & 0xff;
+ } else {
+ /* EEPROM, do nothing */
+ res = 0;
+ }
+ return res;
+}
+
+static int adm1026_write_value(struct i2c_client *client, u8 reg, int value)
+{
+ int res;
+
+ if (reg < 0x80) {
+ /* "RAM" locations */
+ res = i2c_smbus_write_byte_data(client, reg, value);
+ } else {
+ /* EEPROM, do nothing */
+ res = 0;
+ }
+ return res;
+}
+
+static struct adm1026_data *adm1026_update_device(struct device *dev)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i;
+ long value, alarms, gpio;
+
+ mutex_lock(&data->update_lock);
+ if (!data->valid
+ || time_after(jiffies,
+ data->last_reading + ADM1026_DATA_INTERVAL)) {
+ /* Things that change quickly */
+ dev_dbg(&client->dev, "Reading sensor values\n");
+ for (i = 0; i <= 16; ++i) {
+ data->in[i] =
+ adm1026_read_value(client, ADM1026_REG_IN[i]);
+ }
+
+ for (i = 0; i <= 7; ++i) {
+ data->fan[i] =
+ adm1026_read_value(client, ADM1026_REG_FAN(i));
+ }
+
+ for (i = 0; i <= 2; ++i) {
+ /*
+ * NOTE: temp[] is s8 and we assume 2's complement
+ * "conversion" in the assignment
+ */
+ data->temp[i] =
+ adm1026_read_value(client, ADM1026_REG_TEMP[i]);
+ }
+
+ data->pwm1.pwm = adm1026_read_value(client,
+ ADM1026_REG_PWM);
+ data->analog_out = adm1026_read_value(client,
+ ADM1026_REG_DAC);
+ /* GPIO16 is MSbit of alarms, move it to gpio */
+ alarms = adm1026_read_value(client, ADM1026_REG_STATUS4);
+ gpio = alarms & 0x80 ? 0x0100 : 0; /* GPIO16 */
+ alarms &= 0x7f;
+ alarms <<= 8;
+ alarms |= adm1026_read_value(client, ADM1026_REG_STATUS3);
+ alarms <<= 8;
+ alarms |= adm1026_read_value(client, ADM1026_REG_STATUS2);
+ alarms <<= 8;
+ alarms |= adm1026_read_value(client, ADM1026_REG_STATUS1);
+ data->alarms = alarms;
+
+ /* Read the GPIO values */
+ gpio |= adm1026_read_value(client,
+ ADM1026_REG_GPIO_STATUS_8_15);
+ gpio <<= 8;
+ gpio |= adm1026_read_value(client,
+ ADM1026_REG_GPIO_STATUS_0_7);
+ data->gpio = gpio;
+
+ data->last_reading = jiffies;
+ } /* last_reading */
+
+ if (!data->valid ||
+ time_after(jiffies, data->last_config + ADM1026_CONFIG_INTERVAL)) {
+ /* Things that don't change often */
+ dev_dbg(&client->dev, "Reading config values\n");
+ for (i = 0; i <= 16; ++i) {
+ data->in_min[i] = adm1026_read_value(client,
+ ADM1026_REG_IN_MIN[i]);
+ data->in_max[i] = adm1026_read_value(client,
+ ADM1026_REG_IN_MAX[i]);
+ }
+
+ value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3)
+ | (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7)
+ << 8);
+ for (i = 0; i <= 7; ++i) {
+ data->fan_min[i] = adm1026_read_value(client,
+ ADM1026_REG_FAN_MIN(i));
+ data->fan_div[i] = DIV_FROM_REG(value & 0x03);
+ value >>= 2;
+ }
+
+ for (i = 0; i <= 2; ++i) {
+ /*
+ * NOTE: temp_xxx[] are s8 and we assume 2's
+ * complement "conversion" in the assignment
+ */
+ data->temp_min[i] = adm1026_read_value(client,
+ ADM1026_REG_TEMP_MIN[i]);
+ data->temp_max[i] = adm1026_read_value(client,
+ ADM1026_REG_TEMP_MAX[i]);
+ data->temp_tmin[i] = adm1026_read_value(client,
+ ADM1026_REG_TEMP_TMIN[i]);
+ data->temp_crit[i] = adm1026_read_value(client,
+ ADM1026_REG_TEMP_THERM[i]);
+ data->temp_offset[i] = adm1026_read_value(client,
+ ADM1026_REG_TEMP_OFFSET[i]);
+ }
+
+ /* Read the STATUS/alarm masks */
+ alarms = adm1026_read_value(client, ADM1026_REG_MASK4);
+ gpio = alarms & 0x80 ? 0x0100 : 0; /* GPIO16 */
+ alarms = (alarms & 0x7f) << 8;
+ alarms |= adm1026_read_value(client, ADM1026_REG_MASK3);
+ alarms <<= 8;
+ alarms |= adm1026_read_value(client, ADM1026_REG_MASK2);
+ alarms <<= 8;
+ alarms |= adm1026_read_value(client, ADM1026_REG_MASK1);
+ data->alarm_mask = alarms;
+
+ /* Read the GPIO values */
+ gpio |= adm1026_read_value(client,
+ ADM1026_REG_GPIO_MASK_8_15);
+ gpio <<= 8;
+ gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_0_7);
+ data->gpio_mask = gpio;
+
+ /* Read various values from CONFIG1 */
+ data->config1 = adm1026_read_value(client,
+ ADM1026_REG_CONFIG1);
+ if (data->config1 & CFG1_PWM_AFC) {
+ data->pwm1.enable = 2;
+ data->pwm1.auto_pwm_min =
+ PWM_MIN_FROM_REG(data->pwm1.pwm);
+ }
+ /* Read the GPIO config */
+ data->config2 = adm1026_read_value(client,
+ ADM1026_REG_CONFIG2);
+ data->config3 = adm1026_read_value(client,
+ ADM1026_REG_CONFIG3);
+ data->gpio_config[16] = (data->config3 >> 6) & 0x03;
+
+ value = 0;
+ for (i = 0; i <= 15; ++i) {
+ if ((i & 0x03) == 0) {
+ value = adm1026_read_value(client,
+ ADM1026_REG_GPIO_CFG_0_3 + i/4);
+ }
+ data->gpio_config[i] = value & 0x03;
+ value >>= 2;
+ }
+
+ data->last_config = jiffies;
+ } /* last_config */
+
+ data->valid = true;
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+static ssize_t in_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(nr, data->in[nr]));
+}
+static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(nr, data->in_min[nr]));
+}
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[nr] = INS_TO_REG(nr, val);
+ adm1026_write_value(client, ADM1026_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 *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(nr, data->in_max[nr]));
+}
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_max[nr] = INS_TO_REG(nr, val);
+ adm1026_write_value(client, ADM1026_REG_IN_MAX[nr], data->in_max[nr]);
+ 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);
+static SENSOR_DEVICE_ATTR_RO(in6_input, in, 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, 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, 8);
+static SENSOR_DEVICE_ATTR_RW(in8_min, in_min, 8);
+static SENSOR_DEVICE_ATTR_RW(in8_max, in_max, 8);
+static SENSOR_DEVICE_ATTR_RO(in9_input, in, 9);
+static SENSOR_DEVICE_ATTR_RW(in9_min, in_min, 9);
+static SENSOR_DEVICE_ATTR_RW(in9_max, in_max, 9);
+static SENSOR_DEVICE_ATTR_RO(in10_input, in, 10);
+static SENSOR_DEVICE_ATTR_RW(in10_min, in_min, 10);
+static SENSOR_DEVICE_ATTR_RW(in10_max, in_max, 10);
+static SENSOR_DEVICE_ATTR_RO(in11_input, in, 11);
+static SENSOR_DEVICE_ATTR_RW(in11_min, in_min, 11);
+static SENSOR_DEVICE_ATTR_RW(in11_max, in_max, 11);
+static SENSOR_DEVICE_ATTR_RO(in12_input, in, 12);
+static SENSOR_DEVICE_ATTR_RW(in12_min, in_min, 12);
+static SENSOR_DEVICE_ATTR_RW(in12_max, in_max, 12);
+static SENSOR_DEVICE_ATTR_RO(in13_input, in, 13);
+static SENSOR_DEVICE_ATTR_RW(in13_min, in_min, 13);
+static SENSOR_DEVICE_ATTR_RW(in13_max, in_max, 13);
+static SENSOR_DEVICE_ATTR_RO(in14_input, in, 14);
+static SENSOR_DEVICE_ATTR_RW(in14_min, in_min, 14);
+static SENSOR_DEVICE_ATTR_RW(in14_max, in_max, 14);
+static SENSOR_DEVICE_ATTR_RO(in15_input, in, 15);
+static SENSOR_DEVICE_ATTR_RW(in15_min, in_min, 15);
+static SENSOR_DEVICE_ATTR_RW(in15_max, in_max, 15);
+
+static ssize_t in16_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(16, data->in[16]) -
+ NEG12_OFFSET);
+}
+static ssize_t in16_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(16, data->in_min[16])
+ - NEG12_OFFSET);
+}
+static ssize_t in16_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[16] = INS_TO_REG(16,
+ clamp_val(val, INT_MIN,
+ INT_MAX - NEG12_OFFSET) +
+ NEG12_OFFSET);
+ adm1026_write_value(client, ADM1026_REG_IN_MIN[16], data->in_min[16]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t in16_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(16, data->in_max[16])
+ - NEG12_OFFSET);
+}
+static ssize_t in16_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_max[16] = INS_TO_REG(16,
+ clamp_val(val, INT_MIN,
+ INT_MAX - NEG12_OFFSET) +
+ NEG12_OFFSET);
+ adm1026_write_value(client, ADM1026_REG_IN_MAX[16], data->in_max[16]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(in16_input, in16, 16);
+static SENSOR_DEVICE_ATTR_RW(in16_min, in16_min, 16);
+static SENSOR_DEVICE_ATTR_RW(in16_max, in16_max, 16);
+
+/* Now add fan read/write functions */
+
+static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ data->fan_div[nr]));
+}
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ data->fan_div[nr]));
+}
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->fan_min[nr] = FAN_TO_REG(val, data->fan_div[nr]);
+ adm1026_write_value(client, ADM1026_REG_FAN_MIN(nr),
+ data->fan_min[nr]);
+ 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(fan2_input, fan, 1);
+static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
+static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3);
+static SENSOR_DEVICE_ATTR_RW(fan4_min, fan_min, 3);
+static SENSOR_DEVICE_ATTR_RO(fan5_input, fan, 4);
+static SENSOR_DEVICE_ATTR_RW(fan5_min, fan_min, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_input, fan, 5);
+static SENSOR_DEVICE_ATTR_RW(fan6_min, fan_min, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_input, fan, 6);
+static SENSOR_DEVICE_ATTR_RW(fan7_min, fan_min, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_input, fan, 7);
+static SENSOR_DEVICE_ATTR_RW(fan8_min, fan_min, 7);
+
+/* Adjust fan_min to account for new fan divisor */
+static void fixup_fan_min(struct device *dev, int fan, int old_div)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int new_min;
+ int new_div = data->fan_div[fan];
+
+ /* 0 and 0xff are special. Don't adjust them */
+ if (data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff)
+ return;
+
+ new_min = data->fan_min[fan] * old_div / new_div;
+ new_min = clamp_val(new_min, 1, 254);
+ data->fan_min[fan] = new_min;
+ adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min);
+}
+
+/* Now add fan_div read/write functions */
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", data->fan_div[nr]);
+}
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int orig_div, new_div;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ new_div = DIV_TO_REG(val);
+
+ mutex_lock(&data->update_lock);
+ orig_div = data->fan_div[nr];
+ data->fan_div[nr] = DIV_FROM_REG(new_div);
+
+ if (nr < 4) { /* 0 <= nr < 4 */
+ adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3,
+ (DIV_TO_REG(data->fan_div[0]) << 0) |
+ (DIV_TO_REG(data->fan_div[1]) << 2) |
+ (DIV_TO_REG(data->fan_div[2]) << 4) |
+ (DIV_TO_REG(data->fan_div[3]) << 6));
+ } else { /* 3 < nr < 8 */
+ adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7,
+ (DIV_TO_REG(data->fan_div[4]) << 0) |
+ (DIV_TO_REG(data->fan_div[5]) << 2) |
+ (DIV_TO_REG(data->fan_div[6]) << 4) |
+ (DIV_TO_REG(data->fan_div[7]) << 6));
+ }
+
+ if (data->fan_div[nr] != orig_div)
+ fixup_fan_min(dev, nr, orig_div);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+static SENSOR_DEVICE_ATTR_RW(fan3_div, fan_div, 2);
+static SENSOR_DEVICE_ATTR_RW(fan4_div, fan_div, 3);
+static SENSOR_DEVICE_ATTR_RW(fan5_div, fan_div, 4);
+static SENSOR_DEVICE_ATTR_RW(fan6_div, fan_div, 5);
+static SENSOR_DEVICE_ATTR_RW(fan7_div, fan_div, 6);
+static SENSOR_DEVICE_ATTR_RW(fan8_div, fan_div, 7);
+
+/* Temps */
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
+}
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
+}
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = TEMP_TO_REG(val);
+ adm1026_write_value(client, ADM1026_REG_TEMP_MIN[nr],
+ data->temp_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
+}
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = TEMP_TO_REG(val);
+ adm1026_write_value(client, ADM1026_REG_TEMP_MAX[nr],
+ data->temp_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
+
+static ssize_t temp_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_offset[nr]));
+}
+static ssize_t temp_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_offset[nr] = TEMP_TO_REG(val);
+ adm1026_write_value(client, ADM1026_REG_TEMP_OFFSET[nr],
+ data->temp_offset[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_offset, temp_offset, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_offset, temp_offset, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_offset, temp_offset, 2);
+
+static ssize_t temp_auto_point1_temp_hyst_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(
+ ADM1026_FAN_ACTIVATION_TEMP_HYST + data->temp_tmin[nr]));
+}
+static ssize_t temp_auto_point2_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_tmin[nr] +
+ ADM1026_FAN_CONTROL_TEMP_RANGE));
+}
+static ssize_t temp_auto_point1_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_tmin[nr]));
+}
+static ssize_t temp_auto_point1_temp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_tmin[nr] = TEMP_TO_REG(val);
+ adm1026_write_value(client, ADM1026_REG_TEMP_TMIN[nr],
+ data->temp_tmin[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_point1_temp, temp_auto_point1_temp, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_auto_point1_temp_hyst,
+ temp_auto_point1_temp_hyst, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_auto_point2_temp, temp_auto_point2_temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_point1_temp, temp_auto_point1_temp, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_auto_point1_temp_hyst,
+ temp_auto_point1_temp_hyst, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_auto_point2_temp, temp_auto_point2_temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_point1_temp, temp_auto_point1_temp, 2);
+static SENSOR_DEVICE_ATTR_RO(temp3_auto_point1_temp_hyst,
+ temp_auto_point1_temp_hyst, 2);
+static SENSOR_DEVICE_ATTR_RO(temp3_auto_point2_temp, temp_auto_point2_temp, 2);
+
+static ssize_t show_temp_crit_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", (data->config1 & CFG1_THERM_HOT) >> 4);
+}
+static ssize_t set_temp_crit_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct adm1026_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;
+
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->config1 = (data->config1 & ~CFG1_THERM_HOT) | (val << 4);
+ adm1026_write_value(client, ADM1026_REG_CONFIG1, data->config1);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static DEVICE_ATTR(temp1_crit_enable, 0644, show_temp_crit_enable,
+ set_temp_crit_enable);
+static DEVICE_ATTR(temp2_crit_enable, 0644, show_temp_crit_enable,
+ set_temp_crit_enable);
+static DEVICE_ATTR(temp3_crit_enable, 0644, show_temp_crit_enable,
+ set_temp_crit_enable);
+
+static ssize_t temp_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr]));
+}
+static ssize_t temp_crit_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_crit[nr] = TEMP_TO_REG(val);
+ adm1026_write_value(client, ADM1026_REG_TEMP_THERM[nr],
+ data->temp_crit[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp_crit, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp_crit, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_crit, temp_crit, 2);
+
+static ssize_t analog_out_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", DAC_FROM_REG(data->analog_out));
+}
+static ssize_t analog_out_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->analog_out = DAC_TO_REG(val);
+ adm1026_write_value(client, ADM1026_REG_DAC, data->analog_out);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(analog_out);
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ int vid = (data->gpio >> 11) & 0x1f;
+
+ dev_dbg(dev, "Setting VID from GPIO11-15.\n");
+ return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm));
+}
+
+static DEVICE_ATTR_RO(cpu0_vid);
+
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1026_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 adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%ld\n", data->alarms);
+}
+
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ return sprintf(buf, "%ld\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(in9_alarm, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(in11_alarm, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(in12_alarm, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(in13_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(in14_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(in15_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(in16_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 8);
+static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 9);
+static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 10);
+static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 12);
+static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 13);
+static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 14);
+static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 15);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 16);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 17);
+static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, 18);
+static SENSOR_DEVICE_ATTR_RO(fan4_alarm, alarm, 19);
+static SENSOR_DEVICE_ATTR_RO(fan5_alarm, alarm, 20);
+static SENSOR_DEVICE_ATTR_RO(fan6_alarm, alarm, 21);
+static SENSOR_DEVICE_ATTR_RO(fan7_alarm, alarm, 22);
+static SENSOR_DEVICE_ATTR_RO(fan8_alarm, alarm, 23);
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 24);
+static SENSOR_DEVICE_ATTR_RO(in10_alarm, alarm, 25);
+static SENSOR_DEVICE_ATTR_RO(in8_alarm, alarm, 26);
+
+static ssize_t alarm_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%ld\n", data->alarm_mask);
+}
+static ssize_t alarm_mask_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long mask;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->alarm_mask = val & 0x7fffffff;
+ mask = data->alarm_mask
+ | (data->gpio_mask & 0x10000 ? 0x80000000 : 0);
+ adm1026_write_value(client, ADM1026_REG_MASK1,
+ mask & 0xff);
+ mask >>= 8;
+ adm1026_write_value(client, ADM1026_REG_MASK2,
+ mask & 0xff);
+ mask >>= 8;
+ adm1026_write_value(client, ADM1026_REG_MASK3,
+ mask & 0xff);
+ mask >>= 8;
+ adm1026_write_value(client, ADM1026_REG_MASK4,
+ mask & 0xff);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(alarm_mask);
+
+static ssize_t gpio_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%ld\n", data->gpio);
+}
+static ssize_t gpio_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long gpio;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->gpio = val & 0x1ffff;
+ gpio = data->gpio;
+ adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_0_7, gpio & 0xff);
+ gpio >>= 8;
+ adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_8_15, gpio & 0xff);
+ gpio = ((gpio >> 1) & 0x80) | (data->alarms >> 24 & 0x7f);
+ adm1026_write_value(client, ADM1026_REG_STATUS4, gpio & 0xff);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(gpio);
+
+static ssize_t gpio_mask_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%ld\n", data->gpio_mask);
+}
+static ssize_t gpio_mask_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long mask;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->gpio_mask = val & 0x1ffff;
+ mask = data->gpio_mask;
+ adm1026_write_value(client, ADM1026_REG_GPIO_MASK_0_7, mask & 0xff);
+ mask >>= 8;
+ adm1026_write_value(client, ADM1026_REG_GPIO_MASK_8_15, mask & 0xff);
+ mask = ((mask >> 1) & 0x80) | (data->alarm_mask >> 24 & 0x7f);
+ adm1026_write_value(client, ADM1026_REG_MASK1, mask & 0xff);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(gpio_mask);
+
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm1.pwm));
+}
+
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ if (data->pwm1.enable == 1) {
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->pwm1.pwm = PWM_TO_REG(val);
+ adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm);
+ mutex_unlock(&data->update_lock);
+ }
+ return count;
+}
+
+static ssize_t temp1_auto_point1_pwm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1.auto_pwm_min);
+}
+
+static ssize_t temp1_auto_point1_pwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1026_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->pwm1.auto_pwm_min = clamp_val(val, 0, 255);
+ if (data->pwm1.enable == 2) { /* apply immediately */
+ data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) |
+ PWM_MIN_TO_REG(data->pwm1.auto_pwm_min));
+ adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp1_auto_point2_pwm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", ADM1026_PWM_MAX);
+}
+
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1026_data *data = adm1026_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1.enable);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1026_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int old_enable;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val >= 3)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ old_enable = data->pwm1.enable;
+ data->pwm1.enable = val;
+ data->config1 = (data->config1 & ~CFG1_PWM_AFC)
+ | ((val == 2) ? CFG1_PWM_AFC : 0);
+ adm1026_write_value(client, ADM1026_REG_CONFIG1, data->config1);
+ if (val == 2) { /* apply pwm1_auto_pwm_min to pwm1 */
+ data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) |
+ PWM_MIN_TO_REG(data->pwm1.auto_pwm_min));
+ adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm);
+ } else if (!((old_enable == 1) && (val == 1))) {
+ /* set pwm to safe value */
+ data->pwm1.pwm = 255;
+ adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* enable PWM fan control */
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR(pwm2, 0644, pwm1_show, pwm1_store);
+static DEVICE_ATTR(pwm3, 0644, pwm1_show, pwm1_store);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR(pwm2_enable, 0644, pwm1_enable_show,
+ pwm1_enable_store);
+static DEVICE_ATTR(pwm3_enable, 0644, pwm1_enable_show,
+ pwm1_enable_store);
+static DEVICE_ATTR_RW(temp1_auto_point1_pwm);
+static DEVICE_ATTR(temp2_auto_point1_pwm, 0644,
+ temp1_auto_point1_pwm_show, temp1_auto_point1_pwm_store);
+static DEVICE_ATTR(temp3_auto_point1_pwm, 0644,
+ temp1_auto_point1_pwm_show, temp1_auto_point1_pwm_store);
+
+static DEVICE_ATTR_RO(temp1_auto_point2_pwm);
+static DEVICE_ATTR(temp2_auto_point2_pwm, 0444, temp1_auto_point2_pwm_show,
+ NULL);
+static DEVICE_ATTR(temp3_auto_point2_pwm, 0444, temp1_auto_point2_pwm_show,
+ NULL);
+
+static struct attribute *adm1026_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in10_alarm.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ &sensor_dev_attr_in11_alarm.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in12_max.dev_attr.attr,
+ &sensor_dev_attr_in12_min.dev_attr.attr,
+ &sensor_dev_attr_in12_alarm.dev_attr.attr,
+ &sensor_dev_attr_in13_input.dev_attr.attr,
+ &sensor_dev_attr_in13_max.dev_attr.attr,
+ &sensor_dev_attr_in13_min.dev_attr.attr,
+ &sensor_dev_attr_in13_alarm.dev_attr.attr,
+ &sensor_dev_attr_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in14_max.dev_attr.attr,
+ &sensor_dev_attr_in14_min.dev_attr.attr,
+ &sensor_dev_attr_in14_alarm.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ &sensor_dev_attr_in15_max.dev_attr.attr,
+ &sensor_dev_attr_in15_min.dev_attr.attr,
+ &sensor_dev_attr_in15_alarm.dev_attr.attr,
+ &sensor_dev_attr_in16_input.dev_attr.attr,
+ &sensor_dev_attr_in16_max.dev_attr.attr,
+ &sensor_dev_attr_in16_min.dev_attr.attr,
+ &sensor_dev_attr_in16_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,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_div.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_div.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_div.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_div.dev_attr.attr,
+ &sensor_dev_attr_fan8_min.dev_attr.attr,
+ &sensor_dev_attr_fan8_alarm.dev_attr.attr,
+ &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_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &dev_attr_temp1_crit_enable.attr,
+ &dev_attr_temp2_crit_enable.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_alarm_mask.attr,
+ &dev_attr_gpio.attr,
+ &dev_attr_gpio_mask.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm3.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm2_enable.attr,
+ &dev_attr_pwm3_enable.attr,
+ &dev_attr_temp1_auto_point1_pwm.attr,
+ &dev_attr_temp2_auto_point1_pwm.attr,
+ &dev_attr_temp1_auto_point2_pwm.attr,
+ &dev_attr_temp2_auto_point2_pwm.attr,
+ &dev_attr_analog_out.attr,
+ NULL
+};
+
+static const struct attribute_group adm1026_group = {
+ .attrs = adm1026_attributes,
+};
+
+static struct attribute *adm1026_attributes_temp3[] = {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &dev_attr_temp3_crit_enable.attr,
+ &dev_attr_temp3_auto_point1_pwm.attr,
+ &dev_attr_temp3_auto_point2_pwm.attr,
+ NULL
+};
+
+static const struct attribute_group adm1026_group_temp3 = {
+ .attrs = adm1026_attributes_temp3,
+};
+
+static struct attribute *adm1026_attributes_in8_9[] = {
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in9_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adm1026_group_in8_9 = {
+ .attrs = adm1026_attributes_in8_9,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int adm1026_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ int company, verstep;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ /* We need to be able to do byte I/O */
+ return -ENODEV;
+ }
+
+ /* Now, we do the remaining detection. */
+
+ company = adm1026_read_value(client, ADM1026_REG_COMPANY);
+ verstep = adm1026_read_value(client, ADM1026_REG_VERSTEP);
+
+ dev_dbg(&adapter->dev,
+ "Detecting device at %d,0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+ i2c_adapter_id(client->adapter), client->addr,
+ company, verstep);
+
+ /* Determine the chip type. */
+ dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x...\n",
+ i2c_adapter_id(adapter), address);
+ if (company == ADM1026_COMPANY_ANALOG_DEV
+ && verstep == ADM1026_VERSTEP_ADM1026) {
+ /* Analog Devices ADM1026 */
+ } else if (company == ADM1026_COMPANY_ANALOG_DEV
+ && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+ dev_err(&adapter->dev,
+ "Unrecognized stepping 0x%02x. Defaulting to ADM1026.\n",
+ verstep);
+ } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+ dev_err(&adapter->dev,
+ "Found version/stepping 0x%02x. Assuming generic ADM1026.\n",
+ verstep);
+ } else {
+ dev_dbg(&adapter->dev, "Autodetection failed\n");
+ /* Not an ADM1026... */
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "adm1026", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void adm1026_print_gpio(struct i2c_client *client)
+{
+ struct adm1026_data *data = i2c_get_clientdata(client);
+ int i;
+
+ dev_dbg(&client->dev, "GPIO config is:\n");
+ for (i = 0; i <= 7; ++i) {
+ if (data->config2 & (1 << i)) {
+ dev_dbg(&client->dev, "\t%sGP%s%d\n",
+ data->gpio_config[i] & 0x02 ? "" : "!",
+ data->gpio_config[i] & 0x01 ? "OUT" : "IN",
+ i);
+ } else {
+ dev_dbg(&client->dev, "\tFAN%d\n", i);
+ }
+ }
+ for (i = 8; i <= 15; ++i) {
+ dev_dbg(&client->dev, "\t%sGP%s%d\n",
+ data->gpio_config[i] & 0x02 ? "" : "!",
+ data->gpio_config[i] & 0x01 ? "OUT" : "IN",
+ i);
+ }
+ if (data->config3 & CFG3_GPIO16_ENABLE) {
+ dev_dbg(&client->dev, "\t%sGP%s16\n",
+ data->gpio_config[16] & 0x02 ? "" : "!",
+ data->gpio_config[16] & 0x01 ? "OUT" : "IN");
+ } else {
+ /* GPIO16 is THERM */
+ dev_dbg(&client->dev, "\tTHERM\n");
+ }
+}
+
+static void adm1026_fixup_gpio(struct i2c_client *client)
+{
+ struct adm1026_data *data = i2c_get_clientdata(client);
+ int i;
+ int value;
+
+ /* Make the changes requested. */
+ /*
+ * We may need to unlock/stop monitoring or soft-reset the
+ * chip before we can make changes. This hasn't been
+ * tested much. FIXME
+ */
+
+ /* Make outputs */
+ for (i = 0; i <= 16; ++i) {
+ if (gpio_output[i] >= 0 && gpio_output[i] <= 16)
+ data->gpio_config[gpio_output[i]] |= 0x01;
+ /* if GPIO0-7 is output, it isn't a FAN tach */
+ if (gpio_output[i] >= 0 && gpio_output[i] <= 7)
+ data->config2 |= 1 << gpio_output[i];
+ }
+
+ /* Input overrides output */
+ for (i = 0; i <= 16; ++i) {
+ if (gpio_input[i] >= 0 && gpio_input[i] <= 16)
+ data->gpio_config[gpio_input[i]] &= ~0x01;
+ /* if GPIO0-7 is input, it isn't a FAN tach */
+ if (gpio_input[i] >= 0 && gpio_input[i] <= 7)
+ data->config2 |= 1 << gpio_input[i];
+ }
+
+ /* Inverted */
+ for (i = 0; i <= 16; ++i) {
+ if (gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16)
+ data->gpio_config[gpio_inverted[i]] &= ~0x02;
+ }
+
+ /* Normal overrides inverted */
+ for (i = 0; i <= 16; ++i) {
+ if (gpio_normal[i] >= 0 && gpio_normal[i] <= 16)
+ data->gpio_config[gpio_normal[i]] |= 0x02;
+ }
+
+ /* Fan overrides input and output */
+ for (i = 0; i <= 7; ++i) {
+ if (gpio_fan[i] >= 0 && gpio_fan[i] <= 7)
+ data->config2 &= ~(1 << gpio_fan[i]);
+ }
+
+ /* Write new configs to registers */
+ adm1026_write_value(client, ADM1026_REG_CONFIG2, data->config2);
+ data->config3 = (data->config3 & 0x3f)
+ | ((data->gpio_config[16] & 0x03) << 6);
+ adm1026_write_value(client, ADM1026_REG_CONFIG3, data->config3);
+ for (i = 15, value = 0; i >= 0; --i) {
+ value <<= 2;
+ value |= data->gpio_config[i] & 0x03;
+ if ((i & 0x03) == 0) {
+ adm1026_write_value(client,
+ ADM1026_REG_GPIO_CFG_0_3 + i/4,
+ value);
+ value = 0;
+ }
+ }
+
+ /* Print the new config */
+ adm1026_print_gpio(client);
+}
+
+static void adm1026_init_client(struct i2c_client *client)
+{
+ int value, i;
+ struct adm1026_data *data = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "Initializing device\n");
+ /* Read chip config */
+ data->config1 = adm1026_read_value(client, ADM1026_REG_CONFIG1);
+ data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2);
+ data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3);
+
+ /* Inform user of chip config */
+ dev_dbg(&client->dev, "ADM1026_REG_CONFIG1 is: 0x%02x\n",
+ data->config1);
+ if ((data->config1 & CFG1_MONITOR) == 0) {
+ dev_dbg(&client->dev,
+ "Monitoring not currently enabled.\n");
+ }
+ if (data->config1 & CFG1_INT_ENABLE) {
+ dev_dbg(&client->dev,
+ "SMBALERT interrupts are enabled.\n");
+ }
+ if (data->config1 & CFG1_AIN8_9) {
+ dev_dbg(&client->dev,
+ "in8 and in9 enabled. temp3 disabled.\n");
+ } else {
+ dev_dbg(&client->dev,
+ "temp3 enabled. in8 and in9 disabled.\n");
+ }
+ if (data->config1 & CFG1_THERM_HOT) {
+ dev_dbg(&client->dev,
+ "Automatic THERM, PWM, and temp limits enabled.\n");
+ }
+
+ if (data->config3 & CFG3_GPIO16_ENABLE) {
+ dev_dbg(&client->dev,
+ "GPIO16 enabled. THERM pin disabled.\n");
+ } else {
+ dev_dbg(&client->dev,
+ "THERM pin enabled. GPIO16 disabled.\n");
+ }
+ if (data->config3 & CFG3_VREF_250)
+ dev_dbg(&client->dev, "Vref is 2.50 Volts.\n");
+ else
+ dev_dbg(&client->dev, "Vref is 1.82 Volts.\n");
+ /* Read and pick apart the existing GPIO configuration */
+ value = 0;
+ for (i = 0; i <= 15; ++i) {
+ if ((i & 0x03) == 0) {
+ value = adm1026_read_value(client,
+ ADM1026_REG_GPIO_CFG_0_3 + i / 4);
+ }
+ data->gpio_config[i] = value & 0x03;
+ value >>= 2;
+ }
+ data->gpio_config[16] = (data->config3 >> 6) & 0x03;
+
+ /* ... and then print it */
+ adm1026_print_gpio(client);
+
+ /*
+ * If the user asks us to reprogram the GPIO config, then
+ * do it now.
+ */
+ if (gpio_input[0] != -1 || gpio_output[0] != -1
+ || gpio_inverted[0] != -1 || gpio_normal[0] != -1
+ || gpio_fan[0] != -1) {
+ adm1026_fixup_gpio(client);
+ }
+
+ /*
+ * WE INTENTIONALLY make no changes to the limits,
+ * offsets, pwms, fans and zones. If they were
+ * configured, we don't want to mess with them.
+ * If they weren't, the default is 100% PWM, no
+ * control and will suffice until 'sensors -s'
+ * can be run by the user. We DO set the default
+ * value for pwm1.auto_pwm_min to its maximum
+ * so that enabling automatic pwm fan control
+ * without first setting a value for pwm1.auto_pwm_min
+ * will not result in potentially dangerous fan speed decrease.
+ */
+ data->pwm1.auto_pwm_min = 255;
+ /* Start monitoring */
+ value = adm1026_read_value(client, ADM1026_REG_CONFIG1);
+ /* Set MONITOR, clear interrupt acknowledge and s/w reset */
+ value = (value | CFG1_MONITOR) & (~CFG1_INT_CLEAR & ~CFG1_RESET);
+ dev_dbg(&client->dev, "Setting CONFIG to: 0x%02x\n", value);
+ data->config1 = value;
+ adm1026_write_value(client, ADM1026_REG_CONFIG1, value);
+
+ /* initialize fan_div[] to hardware defaults */
+ value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) |
+ (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8);
+ for (i = 0; i <= 7; ++i) {
+ data->fan_div[i] = DIV_FROM_REG(value & 0x03);
+ value >>= 2;
+ }
+}
+
+static int adm1026_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct adm1026_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct adm1026_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Set the VRM version */
+ data->vrm = vid_which_vrm();
+
+ /* Initialize the ADM1026 chip */
+ adm1026_init_client(client);
+
+ /* sysfs hooks */
+ data->groups[0] = &adm1026_group;
+ if (data->config1 & CFG1_AIN8_9)
+ data->groups[1] = &adm1026_group_in8_9;
+ else
+ data->groups[1] = &adm1026_group_temp3;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adm1026_id[] = {
+ { "adm1026", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1026_id);
+
+static struct i2c_driver adm1026_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adm1026",
+ },
+ .probe_new = adm1026_probe,
+ .id_table = adm1026_id,
+ .detect = adm1026_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adm1026_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, "
+ "Justin Thiessen <jthiessen@penguincomputing.com>");
+MODULE_DESCRIPTION("ADM1026 driver");
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
new file mode 100644
index 000000000..eaf6e5e04
--- /dev/null
+++ b/drivers/hwmon/adm1029.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * adm1029.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
+ *
+ * Copyright (C) 2006 Corentin LABBE <clabbe.montjoie@gmail.com>
+ *
+ * Based on LM83 Driver by Jean Delvare <jdelvare@suse.de>
+ *
+ * Give only processor, motherboard temperatures and fan tachs
+ * Very rare chip please let me know if you use it
+ *
+ * http://www.analog.com/UploadedFiles/Data_Sheets/ADM1029.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/*
+ * Addresses to scan
+ */
+
+static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, I2C_CLIENT_END
+};
+
+/*
+ * The ADM1029 registers
+ * Manufacturer ID is 0x41 for Analog Devices
+ */
+
+#define ADM1029_REG_MAN_ID 0x0D
+#define ADM1029_REG_CHIP_ID 0x0E
+#define ADM1029_REG_CONFIG 0x01
+#define ADM1029_REG_NB_FAN_SUPPORT 0x02
+
+#define ADM1029_REG_TEMP_DEVICES_INSTALLED 0x06
+
+#define ADM1029_REG_LOCAL_TEMP 0xA0
+#define ADM1029_REG_REMOTE1_TEMP 0xA1
+#define ADM1029_REG_REMOTE2_TEMP 0xA2
+
+#define ADM1029_REG_LOCAL_TEMP_HIGH 0x90
+#define ADM1029_REG_REMOTE1_TEMP_HIGH 0x91
+#define ADM1029_REG_REMOTE2_TEMP_HIGH 0x92
+
+#define ADM1029_REG_LOCAL_TEMP_LOW 0x98
+#define ADM1029_REG_REMOTE1_TEMP_LOW 0x99
+#define ADM1029_REG_REMOTE2_TEMP_LOW 0x9A
+
+#define ADM1029_REG_FAN1 0x70
+#define ADM1029_REG_FAN2 0x71
+
+#define ADM1029_REG_FAN1_MIN 0x78
+#define ADM1029_REG_FAN2_MIN 0x79
+
+#define ADM1029_REG_FAN1_CONFIG 0x68
+#define ADM1029_REG_FAN2_CONFIG 0x69
+
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+#define DIV_FROM_REG(val) (1 << (((val) >> 6) - 1))
+
+/* Registers to be checked by adm1029_update_device() */
+static const u8 ADM1029_REG_TEMP[] = {
+ ADM1029_REG_LOCAL_TEMP,
+ ADM1029_REG_REMOTE1_TEMP,
+ ADM1029_REG_REMOTE2_TEMP,
+ ADM1029_REG_LOCAL_TEMP_HIGH,
+ ADM1029_REG_REMOTE1_TEMP_HIGH,
+ ADM1029_REG_REMOTE2_TEMP_HIGH,
+ ADM1029_REG_LOCAL_TEMP_LOW,
+ ADM1029_REG_REMOTE1_TEMP_LOW,
+ ADM1029_REG_REMOTE2_TEMP_LOW,
+};
+
+static const u8 ADM1029_REG_FAN[] = {
+ ADM1029_REG_FAN1,
+ ADM1029_REG_FAN2,
+ ADM1029_REG_FAN1_MIN,
+ ADM1029_REG_FAN2_MIN,
+};
+
+static const u8 ADM1029_REG_FAN_DIV[] = {
+ ADM1029_REG_FAN1_CONFIG,
+ ADM1029_REG_FAN2_CONFIG,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct adm1029_data {
+ struct i2c_client *client;
+ struct mutex update_lock; /* protect register access */
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values, signed for temperature, unsigned for other stuff */
+ s8 temp[ARRAY_SIZE(ADM1029_REG_TEMP)];
+ u8 fan[ARRAY_SIZE(ADM1029_REG_FAN)];
+ u8 fan_div[ARRAY_SIZE(ADM1029_REG_FAN_DIV)];
+};
+
+/*
+ * function that update the status of the chips (temperature for example)
+ */
+static struct adm1029_data *adm1029_update_device(struct device *dev)
+{
+ struct adm1029_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+ /*
+ * Use the "cache" Luke, don't recheck values
+ * if there are already checked not a long time later
+ */
+ if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
+ int nr;
+
+ dev_dbg(&client->dev, "Updating adm1029 data\n");
+
+ for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_TEMP); nr++) {
+ data->temp[nr] =
+ i2c_smbus_read_byte_data(client,
+ ADM1029_REG_TEMP[nr]);
+ }
+ for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN); nr++) {
+ data->fan[nr] =
+ i2c_smbus_read_byte_data(client,
+ ADM1029_REG_FAN[nr]);
+ }
+ for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN_DIV); nr++) {
+ data->fan_div[nr] =
+ i2c_smbus_read_byte_data(client,
+ ADM1029_REG_FAN_DIV[nr]);
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+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 adm1029_data *data = adm1029_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+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 adm1029_data *data = adm1029_update_device(dev);
+ u16 val;
+
+ if (data->fan[attr->index] == 0 ||
+ (data->fan_div[attr->index] & 0xC0) == 0 ||
+ data->fan[attr->index] == 255) {
+ return sprintf(buf, "0\n");
+ }
+
+ val = 1880 * 120 / DIV_FROM_REG(data->fan_div[attr->index])
+ / data->fan[attr->index];
+ return sprintf(buf, "%d\n", val);
+}
+
+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 adm1029_data *data = adm1029_update_device(dev);
+
+ if ((data->fan_div[attr->index] & 0xC0) == 0)
+ return sprintf(buf, "0\n");
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
+}
+
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adm1029_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ u8 reg;
+ long val;
+ int ret = kstrtol(buf, 10, &val);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+
+ /*Read actual config */
+ reg = i2c_smbus_read_byte_data(client,
+ ADM1029_REG_FAN_DIV[attr->index]);
+
+ switch (val) {
+ case 1:
+ val = 1;
+ break;
+ case 2:
+ val = 2;
+ break;
+ case 4:
+ val = 3;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ dev_err(&client->dev,
+ "fan_div value %ld not supported. Choose one of 1, 2 or 4!\n",
+ val);
+ return -EINVAL;
+ }
+ /* Update the value */
+ reg = (reg & 0x3F) | (val << 6);
+
+ /* Update the cache */
+ data->fan_div[attr->index] = reg;
+
+ /* Write value */
+ i2c_smbus_write_byte_data(client,
+ ADM1029_REG_FAN_DIV[attr->index], reg);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* Access rights on sysfs. */
+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(temp1_max, temp, 3);
+static SENSOR_DEVICE_ATTR_RO(temp2_max, temp, 4);
+static SENSOR_DEVICE_ATTR_RO(temp3_max, temp, 5);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_min, temp, 6);
+static SENSOR_DEVICE_ATTR_RO(temp2_min, temp, 7);
+static SENSOR_DEVICE_ATTR_RO(temp3_min, temp, 8);
+
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
+
+static SENSOR_DEVICE_ATTR_RO(fan1_min, fan, 2);
+static SENSOR_DEVICE_ATTR_RO(fan2_min, fan, 3);
+
+static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+
+static struct attribute *adm1029_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,
+ &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_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(adm1029);
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int adm1029_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u8 man_id, chip_id, temp_devices_installed, nb_fan_support;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /*
+ * ADM1029 doesn't have CHIP ID, check just MAN ID
+ * For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED,
+ * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values
+ * documented
+ */
+
+ man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID);
+ temp_devices_installed = i2c_smbus_read_byte_data(client,
+ ADM1029_REG_TEMP_DEVICES_INSTALLED);
+ nb_fan_support = i2c_smbus_read_byte_data(client,
+ ADM1029_REG_NB_FAN_SUPPORT);
+ /* 0x41 is Analog Devices */
+ if (man_id != 0x41 || (temp_devices_installed & 0xf9) != 0x01 ||
+ nb_fan_support != 0x03)
+ return -ENODEV;
+
+ if ((chip_id & 0xF0) != 0x00) {
+ /*
+ * There are no "official" CHIP ID, so actually
+ * we use Major/Minor revision for that
+ */
+ pr_info("Unknown major revision %x, please let us know\n",
+ chip_id);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "adm1029", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int adm1029_init_client(struct i2c_client *client)
+{
+ u8 config;
+
+ config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG);
+ if ((config & 0x10) == 0) {
+ i2c_smbus_write_byte_data(client, ADM1029_REG_CONFIG,
+ config | 0x10);
+ }
+ /* recheck config */
+ config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG);
+ if ((config & 0x10) == 0) {
+ dev_err(&client->dev, "Initialization failed!\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int adm1029_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct adm1029_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct adm1029_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /*
+ * Initialize the ADM1029 chip
+ * Check config register
+ */
+ if (adm1029_init_client(client) == 0)
+ return -ENODEV;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ adm1029_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adm1029_id[] = {
+ { "adm1029", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1029_id);
+
+static struct i2c_driver adm1029_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adm1029",
+ },
+ .probe_new = adm1029_probe,
+ .id_table = adm1029_id,
+ .detect = adm1029_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adm1029_driver);
+
+MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>");
+MODULE_DESCRIPTION("adm1029 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
new file mode 100644
index 000000000..b42797bcb
--- /dev/null
+++ b/drivers/hwmon/adm1031.c
@@ -0,0 +1,1081 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Based on lm75.c and lm85.c
+ * Supports adm1030 / adm1031
+ * Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>
+ * Reworked by Jean Delvare <jdelvare@suse.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Following macros takes channel parameter starting from 0 to 2 */
+#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr))
+#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr))
+#define ADM1031_REG_PWM (0x22)
+#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr))
+#define ADM1031_REG_FAN_FILTER (0x23)
+
+#define ADM1031_REG_TEMP_OFFSET(nr) (0x0d + (nr))
+#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr))
+#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4 * (nr))
+#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4 * (nr))
+
+#define ADM1031_REG_TEMP(nr) (0x0a + (nr))
+#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr))
+
+#define ADM1031_REG_STATUS(nr) (0x2 + (nr))
+
+#define ADM1031_REG_CONF1 0x00
+#define ADM1031_REG_CONF2 0x01
+#define ADM1031_REG_EXT_TEMP 0x06
+
+#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */
+#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */
+#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */
+
+#define ADM1031_CONF2_PWM1_ENABLE 0x01
+#define ADM1031_CONF2_PWM2_ENABLE 0x02
+#define ADM1031_CONF2_TACH1_ENABLE 0x04
+#define ADM1031_CONF2_TACH2_ENABLE 0x08
+#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
+
+#define ADM1031_UPDATE_RATE_MASK 0x1c
+#define ADM1031_UPDATE_RATE_SHIFT 2
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+enum chips { adm1030, adm1031 };
+
+typedef u8 auto_chan_table_t[8][2];
+
+/* Each client has this additional data */
+struct adm1031_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+ struct mutex update_lock;
+ int chip_type;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+ unsigned int update_interval; /* In milliseconds */
+ /*
+ * The chan_select_table contains the possible configurations for
+ * auto fan control.
+ */
+ const auto_chan_table_t *chan_select_table;
+ u16 alarm;
+ u8 conf1;
+ u8 conf2;
+ u8 fan[2];
+ u8 fan_div[2];
+ u8 fan_min[2];
+ u8 pwm[2];
+ u8 old_pwm[2];
+ s8 temp[3];
+ u8 ext_temp[3];
+ u8 auto_temp[3];
+ u8 auto_temp_min[3];
+ u8 auto_temp_off[3];
+ u8 auto_temp_max[3];
+ s8 temp_offset[3];
+ s8 temp_min[3];
+ s8 temp_max[3];
+ s8 temp_crit[3];
+};
+
+static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int
+adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static struct adm1031_data *adm1031_update_device(struct device *dev)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long next_update;
+ int chan;
+
+ mutex_lock(&data->update_lock);
+
+ next_update = data->last_updated
+ + msecs_to_jiffies(data->update_interval);
+ if (time_after(jiffies, next_update) || !data->valid) {
+
+ dev_dbg(&client->dev, "Starting adm1031 update\n");
+ for (chan = 0;
+ chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) {
+ u8 oldh, newh;
+
+ oldh =
+ adm1031_read_value(client, ADM1031_REG_TEMP(chan));
+ data->ext_temp[chan] =
+ adm1031_read_value(client, ADM1031_REG_EXT_TEMP);
+ newh =
+ adm1031_read_value(client, ADM1031_REG_TEMP(chan));
+ if (newh != oldh) {
+ data->ext_temp[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_EXT_TEMP);
+#ifdef DEBUG
+ oldh =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP(chan));
+
+ /* oldh is actually newer */
+ if (newh != oldh)
+ dev_warn(&client->dev,
+ "Remote temperature may be wrong.\n");
+#endif
+ }
+ data->temp[chan] = newh;
+
+ data->temp_offset[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP_OFFSET(chan));
+ data->temp_min[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP_MIN(chan));
+ data->temp_max[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP_MAX(chan));
+ data->temp_crit[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP_CRIT(chan));
+ data->auto_temp[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_AUTO_TEMP(chan));
+
+ }
+
+ data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
+ data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
+
+ data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0))
+ | (adm1031_read_value(client, ADM1031_REG_STATUS(1)) << 8);
+ if (data->chip_type == adm1030)
+ data->alarm &= 0xc0ff;
+
+ for (chan = 0; chan < (data->chip_type == adm1030 ? 1 : 2);
+ chan++) {
+ data->fan_div[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_FAN_DIV(chan));
+ data->fan_min[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_FAN_MIN(chan));
+ data->fan[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_FAN_SPEED(chan));
+ data->pwm[chan] =
+ (adm1031_read_value(client,
+ ADM1031_REG_PWM) >> (4 * chan)) & 0x0f;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \
+ ((val + 500) / 1000)))
+
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125)
+
+#define TEMP_OFFSET_TO_REG(val) (TEMP_TO_REG(val) & 0x8f)
+#define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \
+ (val) | 0x70 : (val))
+
+#define FAN_FROM_REG(reg, div) ((reg) ? \
+ (11250 * 60) / ((reg) * (div)) : 0)
+
+static int FAN_TO_REG(int reg, int div)
+{
+ int tmp;
+ tmp = FAN_FROM_REG(clamp_val(reg, 0, 65535), div);
+ return tmp > 255 ? 255 : tmp;
+}
+
+#define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6))
+
+#define PWM_TO_REG(val) (clamp_val((val), 0, 255) >> 4)
+#define PWM_FROM_REG(val) ((val) << 4)
+
+#define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7)
+#define FAN_CHAN_TO_REG(val, reg) \
+ (((reg) & 0x1F) | (((val) << 5) & 0xe0))
+
+#define AUTO_TEMP_MIN_TO_REG(val, reg) \
+ ((((val) / 500) & 0xf8) | ((reg) & 0x7))
+#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1 << ((reg) & 0x7)))
+#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2))
+
+#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2)
+
+#define AUTO_TEMP_OFF_FROM_REG(reg) \
+ (AUTO_TEMP_MIN_FROM_REG(reg) - 5000)
+
+#define AUTO_TEMP_MAX_FROM_REG(reg) \
+ (AUTO_TEMP_RANGE_FROM_REG(reg) + \
+ AUTO_TEMP_MIN_FROM_REG(reg))
+
+static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
+{
+ int ret;
+ int range = ((val - AUTO_TEMP_MIN_FROM_REG(reg)) * 10) / (16 - pwm);
+
+ ret = ((reg & 0xf8) |
+ (range < 10000 ? 0 :
+ range < 20000 ? 1 :
+ range < 40000 ? 2 : range < 80000 ? 3 : 4));
+ return ret;
+}
+
+/* FAN auto control */
+#define GET_FAN_AUTO_BITFIELD(data, idx) \
+ (*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx % 2]
+
+/*
+ * The tables below contains the possible values for the auto fan
+ * control bitfields. the index in the table is the register value.
+ * MSb is the auto fan control enable bit, so the four first entries
+ * in the table disables auto fan control when both bitfields are zero.
+ */
+static const auto_chan_table_t auto_channel_select_table_adm1031 = {
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 2 /* 0b010 */ , 4 /* 0b100 */ },
+ { 2 /* 0b010 */ , 2 /* 0b010 */ },
+ { 4 /* 0b100 */ , 4 /* 0b100 */ },
+ { 7 /* 0b111 */ , 7 /* 0b111 */ },
+};
+
+static const auto_chan_table_t auto_channel_select_table_adm1030 = {
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 2 /* 0b10 */ , 0 },
+ { 0xff /* invalid */ , 0 },
+ { 0xff /* invalid */ , 0 },
+ { 3 /* 0b11 */ , 0 },
+};
+
+/*
+ * That function checks if a bitfield is valid and returns the other bitfield
+ * nearest match if no exact match where found.
+ */
+static int
+get_fan_auto_nearest(struct adm1031_data *data, int chan, u8 val, u8 reg)
+{
+ int i;
+ int first_match = -1, exact_match = -1;
+ u8 other_reg_val =
+ (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1];
+
+ if (val == 0)
+ return 0;
+
+ for (i = 0; i < 8; i++) {
+ if ((val == (*data->chan_select_table)[i][chan]) &&
+ ((*data->chan_select_table)[i][chan ? 0 : 1] ==
+ other_reg_val)) {
+ /* We found an exact match */
+ exact_match = i;
+ break;
+ } else if (val == (*data->chan_select_table)[i][chan] &&
+ first_match == -1) {
+ /*
+ * Save the first match in case of an exact match has
+ * not been found
+ */
+ first_match = i;
+ }
+ }
+
+ if (exact_match >= 0)
+ return exact_match;
+ else if (first_match >= 0)
+ return first_match;
+
+ return -EINVAL;
+}
+
+static ssize_t fan_auto_channel_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr));
+}
+
+static ssize_t
+fan_auto_channel_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ u8 reg;
+ int ret;
+ u8 old_fan_mode;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ old_fan_mode = data->conf1;
+
+ mutex_lock(&data->update_lock);
+
+ ret = get_fan_auto_nearest(data, nr, val, data->conf1);
+ if (ret < 0) {
+ mutex_unlock(&data->update_lock);
+ return ret;
+ }
+ reg = ret;
+ data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);
+ if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) ^
+ (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) {
+ if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
+ /*
+ * Switch to Auto Fan Mode
+ * Save PWM registers
+ * Set PWM registers to 33% Both
+ */
+ data->old_pwm[0] = data->pwm[0];
+ data->old_pwm[1] = data->pwm[1];
+ adm1031_write_value(client, ADM1031_REG_PWM, 0x55);
+ } else {
+ /* Switch to Manual Mode */
+ data->pwm[0] = data->old_pwm[0];
+ data->pwm[1] = data->old_pwm[1];
+ /* Restore PWM registers */
+ adm1031_write_value(client, ADM1031_REG_PWM,
+ data->pwm[0] | (data->pwm[1] << 4));
+ }
+ }
+ data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);
+ adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(auto_fan1_channel, fan_auto_channel, 0);
+static SENSOR_DEVICE_ATTR_RW(auto_fan2_channel, fan_auto_channel, 1);
+
+/* Auto Temps */
+static ssize_t auto_temp_off_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t auto_temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t
+auto_temp_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, 0, 127000);
+ mutex_lock(&data->update_lock);
+ data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]);
+ adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
+ data->auto_temp[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t auto_temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t
+auto_temp_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, 0, 127000);
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr],
+ data->pwm[nr]);
+ adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
+ data->temp_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(auto_temp1_off, auto_temp_off, 0);
+static SENSOR_DEVICE_ATTR_RW(auto_temp1_min, auto_temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(auto_temp1_max, auto_temp_max, 0);
+static SENSOR_DEVICE_ATTR_RO(auto_temp2_off, auto_temp_off, 1);
+static SENSOR_DEVICE_ATTR_RW(auto_temp2_min, auto_temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(auto_temp2_max, auto_temp_max, 1);
+static SENSOR_DEVICE_ATTR_RO(auto_temp3_off, auto_temp_off, 2);
+static SENSOR_DEVICE_ATTR_RW(auto_temp3_min, auto_temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(auto_temp3_max, auto_temp_max, 2);
+
+/* pwm */
+static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
+}
+static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret, reg;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) &&
+ (((val>>4) & 0xf) != 5)) {
+ /* In automatic mode, the only PWM accepted is 33% */
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+ data->pwm[nr] = PWM_TO_REG(val);
+ reg = adm1031_read_value(client, ADM1031_REG_PWM);
+ adm1031_write_value(client, ADM1031_REG_PWM,
+ nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf)
+ : (data->pwm[nr] & 0xf) | (reg & 0xf0));
+ 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(auto_fan1_min_pwm, pwm, 0);
+static SENSOR_DEVICE_ATTR_RW(auto_fan2_min_pwm, pwm, 1);
+
+/* Fans */
+
+/*
+ * That function checks the cases where the fan reading is not
+ * relevant. It is used to provide 0 as fan reading when the fan is
+ * not supposed to run
+ */
+static int trust_fan_readings(struct adm1031_data *data, int chan)
+{
+ int res = 0;
+
+ if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
+ switch (data->conf1 & 0x60) {
+ case 0x00:
+ /*
+ * remote temp1 controls fan1,
+ * remote temp2 controls fan2
+ */
+ res = data->temp[chan+1] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]);
+ break;
+ case 0x20: /* remote temp1 controls both fans */
+ res =
+ data->temp[1] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]);
+ break;
+ case 0x40: /* remote temp2 controls both fans */
+ res =
+ data->temp[2] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]);
+ break;
+ case 0x60: /* max controls both fans */
+ res =
+ data->temp[0] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0])
+ || data->temp[1] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1])
+ || (data->chip_type == adm1031
+ && data->temp[2] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]));
+ break;
+ }
+ } else {
+ res = data->pwm[chan] > 0;
+ }
+ return res;
+}
+
+static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ int value;
+
+ value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr]));
+}
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ FAN_FROM_REG(data->fan_min[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr])));
+}
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ if (val) {
+ data->fan_min[nr] =
+ FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr]));
+ } else {
+ data->fan_min[nr] = 0xff;
+ }
+ adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ u8 tmp;
+ int old_div;
+ int new_min;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ tmp = val == 8 ? 0xc0 :
+ val == 4 ? 0x80 :
+ val == 2 ? 0x40 :
+ val == 1 ? 0x00 :
+ 0xff;
+ if (tmp == 0xff)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ /* Get fresh readings */
+ data->fan_div[nr] = adm1031_read_value(client,
+ ADM1031_REG_FAN_DIV(nr));
+ data->fan_min[nr] = adm1031_read_value(client,
+ ADM1031_REG_FAN_MIN(nr));
+
+ /* Write the new clock divider and fan min */
+ old_div = FAN_DIV_FROM_REG(data->fan_div[nr]);
+ data->fan_div[nr] = tmp | (0x3f & data->fan_div[nr]);
+ new_min = data->fan_min[nr] * old_div / val;
+ data->fan_min[nr] = new_min > 0xff ? 0xff : new_min;
+
+ adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr),
+ data->fan_div[nr]);
+ adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr),
+ data->fan_min[nr]);
+
+ /* Invalidate the cache: fan speed is no longer valid */
+ data->valid = false;
+ 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_RW(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_RW(fan2_div, fan_div, 1);
+
+/* Temps */
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ int ext;
+ ext = nr == 0 ?
+ ((data->ext_temp[nr] >> 6) & 0x3) * 2 :
+ (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
+ return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext));
+}
+static ssize_t temp_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ TEMP_OFFSET_FROM_REG(data->temp_offset[nr]));
+}
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
+}
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
+}
+static ssize_t temp_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr]));
+}
+static ssize_t temp_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, -15000, 15000);
+ mutex_lock(&data->update_lock);
+ data->temp_offset[nr] = TEMP_OFFSET_TO_REG(val);
+ adm1031_write_value(client, ADM1031_REG_TEMP_OFFSET(nr),
+ data->temp_offset[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, -55000, 127000);
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = TEMP_TO_REG(val);
+ adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr),
+ data->temp_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, -55000, 127000);
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = TEMP_TO_REG(val);
+ adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr),
+ data->temp_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t temp_crit_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, -55000, 127000);
+ mutex_lock(&data->update_lock);
+ data->temp_crit[nr] = TEMP_TO_REG(val);
+ adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr),
+ data->temp_crit[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_offset, temp_offset, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp_crit, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_offset, temp_offset, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp_crit, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_offset, temp_offset, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_crit, temp_crit, 2);
+
+/* Alarms */
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarm);
+}
+
+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 adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", (data->alarm >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(fan1_fault, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 8);
+static SENSOR_DEVICE_ATTR_RO(fan2_fault, alarm, 9);
+static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, 10);
+static SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 12);
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 13);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 14);
+
+/* Update Interval */
+static const unsigned int update_intervals[] = {
+ 16000, 8000, 4000, 2000, 1000, 500, 250, 125,
+};
+
+static ssize_t update_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->update_interval);
+}
+
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm1031_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+ int i, err;
+ u8 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ /*
+ * Find the nearest update interval from the table.
+ * Use it to determine the matching update rate.
+ */
+ for (i = 0; i < ARRAY_SIZE(update_intervals) - 1; i++) {
+ if (val >= update_intervals[i])
+ break;
+ }
+ /* if not found, we point to the last entry (lowest update interval) */
+
+ /* set the new update rate while preserving other settings */
+ reg = adm1031_read_value(client, ADM1031_REG_FAN_FILTER);
+ reg &= ~ADM1031_UPDATE_RATE_MASK;
+ reg |= i << ADM1031_UPDATE_RATE_SHIFT;
+ adm1031_write_value(client, ADM1031_REG_FAN_FILTER, reg);
+
+ mutex_lock(&data->update_lock);
+ data->update_interval = update_intervals[i];
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(update_interval);
+
+static struct attribute *adm1031_attributes[] = {
+ &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_fan1_fault.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_auto_fan1_channel.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &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_auto_temp1_off.dev_attr.attr,
+ &sensor_dev_attr_auto_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_auto_temp1_max.dev_attr.attr,
+
+ &sensor_dev_attr_auto_temp2_off.dev_attr.attr,
+ &sensor_dev_attr_auto_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_auto_temp2_max.dev_attr.attr,
+
+ &sensor_dev_attr_auto_fan1_min_pwm.dev_attr.attr,
+
+ &dev_attr_update_interval.attr,
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group adm1031_group = {
+ .attrs = adm1031_attributes,
+};
+
+static struct attribute *adm1031_attributes_opt[] = {
+ &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,
+ &sensor_dev_attr_fan2_fault.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_auto_fan2_channel.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &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_auto_temp3_off.dev_attr.attr,
+ &sensor_dev_attr_auto_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_auto_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_auto_fan2_min_pwm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adm1031_group_opt = {
+ .attrs = adm1031_attributes_opt,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int adm1031_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *name;
+ int id, co;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ id = i2c_smbus_read_byte_data(client, 0x3d);
+ co = i2c_smbus_read_byte_data(client, 0x3e);
+
+ if (!((id == 0x31 || id == 0x30) && co == 0x41))
+ return -ENODEV;
+ name = (id == 0x30) ? "adm1030" : "adm1031";
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void adm1031_init_client(struct i2c_client *client)
+{
+ unsigned int read_val;
+ unsigned int mask;
+ int i;
+ struct adm1031_data *data = i2c_get_clientdata(client);
+
+ mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
+ if (data->chip_type == adm1031) {
+ mask |= (ADM1031_CONF2_PWM2_ENABLE |
+ ADM1031_CONF2_TACH2_ENABLE);
+ }
+ /* Initialize the ADM1031 chip (enables fan speed reading ) */
+ read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
+ if ((read_val | mask) != read_val)
+ adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask);
+
+ read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
+ if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
+ adm1031_write_value(client, ADM1031_REG_CONF1,
+ read_val | ADM1031_CONF1_MONITOR_ENABLE);
+ }
+
+ /* Read the chip's update rate */
+ mask = ADM1031_UPDATE_RATE_MASK;
+ read_val = adm1031_read_value(client, ADM1031_REG_FAN_FILTER);
+ i = (read_val & mask) >> ADM1031_UPDATE_RATE_SHIFT;
+ /* Save it as update interval */
+ data->update_interval = update_intervals[i];
+}
+
+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;
+ struct adm1031_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct adm1031_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ data->chip_type = i2c_match_id(adm1031_id, client)->driver_data;
+ mutex_init(&data->update_lock);
+
+ if (data->chip_type == adm1030)
+ data->chan_select_table = &auto_channel_select_table_adm1030;
+ else
+ data->chan_select_table = &auto_channel_select_table_adm1031;
+
+ /* Initialize the ADM1031 chip */
+ adm1031_init_client(client);
+
+ /* sysfs hooks */
+ data->groups[0] = &adm1031_group;
+ if (data->chip_type == adm1031)
+ data->groups[1] = &adm1031_group_opt;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adm1031_id[] = {
+ { "adm1030", adm1030 },
+ { "adm1031", adm1031 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1031_id);
+
+static struct i2c_driver adm1031_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adm1031",
+ },
+ .probe_new = adm1031_probe,
+ .id_table = adm1031_id,
+ .detect = adm1031_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adm1031_driver);
+
+MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");
+MODULE_DESCRIPTION("ADM1031/ADM1030 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c
new file mode 100644
index 000000000..0c5dbc5e3
--- /dev/null
+++ b/drivers/hwmon/adm1177.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADM1177 Hot Swap Controller and Digital Power Monitor with Soft Start Pin
+ *
+ * Copyright 2015-2019 Analog Devices Inc.
+ */
+
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+/* Command Byte Operations */
+#define ADM1177_CMD_V_CONT BIT(0)
+#define ADM1177_CMD_I_CONT BIT(2)
+#define ADM1177_CMD_VRANGE BIT(4)
+
+/* Extended Register */
+#define ADM1177_REG_ALERT_TH 2
+
+#define ADM1177_BITS 12
+
+/**
+ * struct adm1177_state - driver instance specific data
+ * @client: pointer to i2c client
+ * @reg: regulator info for the power supply of the device
+ * @r_sense_uohm: current sense resistor value
+ * @alert_threshold_ua: current limit for shutdown
+ * @vrange_high: internal voltage divider
+ */
+struct adm1177_state {
+ struct i2c_client *client;
+ struct regulator *reg;
+ u32 r_sense_uohm;
+ u32 alert_threshold_ua;
+ bool vrange_high;
+};
+
+static int adm1177_read_raw(struct adm1177_state *st, u8 num, u8 *data)
+{
+ return i2c_master_recv(st->client, data, num);
+}
+
+static int adm1177_write_cmd(struct adm1177_state *st, u8 cmd)
+{
+ return i2c_smbus_write_byte(st->client, cmd);
+}
+
+static int adm1177_write_alert_thr(struct adm1177_state *st,
+ u32 alert_threshold_ua)
+{
+ u64 val;
+ int ret;
+
+ val = 0xFFULL * alert_threshold_ua * st->r_sense_uohm;
+ val = div_u64(val, 105840000U);
+ val = div_u64(val, 1000U);
+ if (val > 0xFF)
+ val = 0xFF;
+
+ ret = i2c_smbus_write_byte_data(st->client, ADM1177_REG_ALERT_TH,
+ val);
+ if (ret)
+ return ret;
+
+ st->alert_threshold_ua = alert_threshold_ua;
+ return 0;
+}
+
+static int adm1177_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct adm1177_state *st = dev_get_drvdata(dev);
+ u8 data[3];
+ long dummy;
+ int ret;
+
+ switch (type) {
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ ret = adm1177_read_raw(st, 3, data);
+ if (ret < 0)
+ return ret;
+ dummy = (data[1] << 4) | (data[2] & 0xF);
+ /*
+ * convert to milliamperes
+ * ((105.84mV / 4096) x raw) / senseResistor(ohm)
+ */
+ *val = div_u64((105840000ull * dummy),
+ 4096 * st->r_sense_uohm);
+ return 0;
+ case hwmon_curr_max_alarm:
+ *val = st->alert_threshold_ua;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ case hwmon_in:
+ ret = adm1177_read_raw(st, 3, data);
+ if (ret < 0)
+ return ret;
+ dummy = (data[0] << 4) | (data[2] >> 4);
+ /*
+ * convert to millivolts based on resistor devision
+ * (V_fullscale / 4096) * raw
+ */
+ if (st->vrange_high)
+ dummy *= 26350;
+ else
+ dummy *= 6650;
+
+ *val = DIV_ROUND_CLOSEST(dummy, 4096);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int adm1177_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct adm1177_state *st = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_max_alarm:
+ adm1177_write_alert_thr(st, val);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t adm1177_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct adm1177_state *st = data;
+
+ switch (type) {
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ return 0444;
+ }
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ if (st->r_sense_uohm)
+ return 0444;
+ return 0;
+ case hwmon_curr_max_alarm:
+ if (st->r_sense_uohm)
+ return 0644;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *adm1177_info[] = {
+ HWMON_CHANNEL_INFO(curr,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops adm1177_hwmon_ops = {
+ .is_visible = adm1177_is_visible,
+ .read = adm1177_read,
+ .write = adm1177_write,
+};
+
+static const struct hwmon_chip_info adm1177_chip_info = {
+ .ops = &adm1177_hwmon_ops,
+ .info = adm1177_info,
+};
+
+static void adm1177_remove(void *data)
+{
+ struct adm1177_state *st = data;
+
+ regulator_disable(st->reg);
+}
+
+static int adm1177_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct adm1177_state *st;
+ u32 alert_threshold_ua;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->client = client;
+
+ st->reg = devm_regulator_get_optional(&client->dev, "vref");
+ if (IS_ERR(st->reg)) {
+ if (PTR_ERR(st->reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ st->reg = NULL;
+ } else {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(&client->dev, adm1177_remove,
+ st);
+ if (ret)
+ return ret;
+ }
+
+ if (device_property_read_u32(dev, "shunt-resistor-micro-ohms",
+ &st->r_sense_uohm))
+ st->r_sense_uohm = 0;
+ if (device_property_read_u32(dev, "adi,shutdown-threshold-microamp",
+ &alert_threshold_ua)) {
+ if (st->r_sense_uohm)
+ /*
+ * set maximum default value from datasheet based on
+ * shunt-resistor
+ */
+ alert_threshold_ua = div_u64(105840000000,
+ st->r_sense_uohm);
+ else
+ alert_threshold_ua = 0;
+ }
+ st->vrange_high = device_property_read_bool(dev,
+ "adi,vrange-high-enable");
+ if (alert_threshold_ua && st->r_sense_uohm)
+ adm1177_write_alert_thr(st, alert_threshold_ua);
+
+ ret = adm1177_write_cmd(st, ADM1177_CMD_V_CONT |
+ ADM1177_CMD_I_CONT |
+ (st->vrange_high ? 0 : ADM1177_CMD_VRANGE));
+ if (ret)
+ return ret;
+
+ hwmon_dev =
+ devm_hwmon_device_register_with_info(dev, client->name, st,
+ &adm1177_chip_info, NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adm1177_id[] = {
+ {"adm1177", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adm1177_id);
+
+static const struct of_device_id adm1177_dt_ids[] = {
+ { .compatible = "adi,adm1177" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, adm1177_dt_ids);
+
+static struct i2c_driver adm1177_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adm1177",
+ .of_match_table = adm1177_dt_ids,
+ },
+ .probe_new = adm1177_probe,
+ .id_table = adm1177_id,
+};
+module_i2c_driver(adm1177_driver);
+
+MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADM1177 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
new file mode 100644
index 000000000..40e3558d3
--- /dev/null
+++ b/drivers/hwmon/adm9240.c
@@ -0,0 +1,833 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * adm9240.c Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ *
+ * Copyright (C) 1999 Frodo Looijaard <frodol@dds.nl>
+ * Philip Edelbrock <phil@netroedge.com>
+ * Copyright (C) 2003 Michiel Rook <michiel@grendelproject.nl>
+ * Copyright (C) 2005 Grant Coady <gcoady.lk@gmail.com> with valuable
+ * guidance from Jean Delvare
+ *
+ * Driver supports Analog Devices ADM9240
+ * Dallas Semiconductor DS1780
+ * National Semiconductor LM81
+ *
+ * ADM9240 is the reference, DS1780 and LM81 are register compatibles
+ *
+ * Voltage Six inputs are scaled by chip, VID also reported
+ * Temperature Chip temperature to 0.5'C, maximum and max_hysteris
+ * Fans 2 fans, low speed alarm, automatic fan clock divider
+ * Alarms 16-bit map of active alarms
+ * Analog Out 0..1250 mV output
+ *
+ * Chassis Intrusion: clear CI latch with 'echo 0 > intrusion0_alarm'
+ *
+ * Test hardware: Intel SE440BX-2 desktop motherboard --Grant
+ *
+ * LM81 extended temp reading not implemented
+ */
+
+#include <linux/bits.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
+ I2C_CLIENT_END };
+
+enum chips { adm9240, ds1780, lm81 };
+
+/* ADM9240 registers */
+#define ADM9240_REG_MAN_ID 0x3e
+#define ADM9240_REG_DIE_REV 0x3f
+#define ADM9240_REG_CONFIG 0x40
+
+#define ADM9240_REG_IN(nr) (0x20 + (nr)) /* 0..5 */
+#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define ADM9240_REG_FAN(nr) (0x28 + (nr)) /* 0..1 */
+#define ADM9240_REG_FAN_MIN(nr) (0x3b + (nr))
+#define ADM9240_REG_INT(nr) (0x41 + (nr))
+#define ADM9240_REG_INT_MASK(nr) (0x43 + (nr))
+#define ADM9240_REG_TEMP 0x27
+#define ADM9240_REG_TEMP_MAX(nr) (0x39 + (nr)) /* 0, 1 = high, hyst */
+#define ADM9240_REG_ANALOG_OUT 0x19
+#define ADM9240_REG_CHASSIS_CLEAR 0x46
+#define ADM9240_REG_VID_FAN_DIV 0x47
+#define ADM9240_REG_I2C_ADDR 0x48
+#define ADM9240_REG_VID4 0x49
+#define ADM9240_REG_TEMP_CONF 0x4b
+
+/* generalised scaling with integer rounding */
+static inline int SCALE(long val, int mul, int div)
+{
+ if (val < 0)
+ return (val * mul - div / 2) / div;
+ else
+ return (val * mul + div / 2) / div;
+}
+
+/* adm9240 internally scales voltage measurements */
+static const u16 nom_mv[] = { 2500, 2700, 3300, 5000, 12000, 2700 };
+
+static inline unsigned int IN_FROM_REG(u8 reg, int n)
+{
+ return SCALE(reg, nom_mv[n], 192);
+}
+
+static inline u8 IN_TO_REG(unsigned long val, int n)
+{
+ val = clamp_val(val, 0, nom_mv[n] * 255 / 192);
+ return SCALE(val, 192, nom_mv[n]);
+}
+
+/* temperature range: -40..125, 127 disables temperature alarm */
+static inline s8 TEMP_TO_REG(long val)
+{
+ val = clamp_val(val, -40000, 127000);
+ return SCALE(val, 1, 1000);
+}
+
+/* two fans, each with low fan speed limit */
+static inline unsigned int FAN_FROM_REG(u8 reg, u8 div)
+{
+ if (!reg) /* error */
+ return -1;
+
+ if (reg == 255)
+ return 0;
+
+ return SCALE(1350000, 1, reg * div);
+}
+
+/* analog out 0..1250mV */
+static inline u8 AOUT_TO_REG(unsigned long val)
+{
+ val = clamp_val(val, 0, 1250);
+ return SCALE(val, 255, 1250);
+}
+
+static inline unsigned int AOUT_FROM_REG(u8 reg)
+{
+ return SCALE(reg, 1250, 255);
+}
+
+/* per client data */
+struct adm9240_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex update_lock;
+
+ u8 fan_div[2]; /* rw fan1_div, read-only accessor */
+ u8 vrm; /* -- vrm set on startup, no accessor */
+};
+
+/* write new fan div, callers must hold data->update_lock */
+static int adm9240_write_fan_div(struct adm9240_data *data, int channel, u8 fan_div)
+{
+ unsigned int reg, old, shift = (channel + 2) * 2;
+ int err;
+
+ err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &reg);
+ if (err < 0)
+ return err;
+ old = (reg >> shift) & 3;
+ reg &= ~(3 << shift);
+ reg |= (fan_div << shift);
+ 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));
+
+ return 0;
+}
+
+/*
+ * set fan speed low limit:
+ *
+ * - value is zero: disable fan speed low limit alarm
+ *
+ * - value is below fan speed measurement range: enable fan speed low
+ * limit alarm to be asserted while fan speed too slow to measure
+ *
+ * - otherwise: select fan clock divider to suit fan speed low limit,
+ * measurement code may adjust registers to ensure fan speed reading
+ */
+static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long val)
+{
+ u8 new_div;
+ u8 fan_min;
+ int err;
+
+ mutex_lock(&data->update_lock);
+
+ if (!val) {
+ 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;
+ 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;
+
+ new_div = 0;
+ while (new_min > 192 && new_div < 3) {
+ new_div++;
+ new_min /= 2;
+ }
+ if (!new_min) /* keep > 0 */
+ new_min++;
+
+ fan_min = new_min;
+
+ 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[channel]) {
+ data->fan_div[channel] = new_div;
+ adm9240_write_fan_div(data, channel, new_div);
+ }
+ err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min);
+
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ 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, &regval);
+ if (err < 0)
+ return err;
+ vid = regval & 0x0f;
+ err = regmap_read(data->regmap, ADM9240_REG_VID4, &regval);
+ 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);
+
+static ssize_t aout_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adm9240_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err;
+
+ err = regmap_read(data->regmap, ADM9240_REG_ANALOG_OUT, &regval);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d\n", AOUT_FROM_REG(regval));
+}
+
+static ssize_t aout_output_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adm9240_data *data = dev_get_drvdata(dev);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ 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 struct attribute *adm9240_attrs[] = {
+ &dev_attr_aout_output.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(adm9240);
+
+/*** sensor chip detect and driver install ***/
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int adm9240_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ const char *name = "";
+ int address = new_client->addr;
+ u8 man_id, die_rev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* verify chip: reg address should match i2c 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)
+ name = "adm9240";
+ else if (man_id == 0xda)
+ name = "ds1780";
+ else if (man_id == 0x01)
+ name = "lm81";
+ else
+ return -ENODEV;
+
+ /* successful detect, print chip info */
+ die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV);
+ dev_info(&adapter->dev, "found %s revision %u\n",
+ man_id == 0x23 ? "ADM9240" :
+ man_id == 0xda ? "DS1780" : "LM81", die_rev);
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int adm9240_init_client(struct adm9240_data *data)
+{
+ 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(data->dev, "Using VRM: %d.%d\n", data->vrm / 10,
+ data->vrm % 10);
+
+ if (conf & 1) { /* measurement cycle running: report state */
+
+ 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++) {
+ 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;
+ }
+
+ /* start measurement cycle */
+ err = regmap_write(data->regmap, ADM9240_REG_CONFIG, 1);
+ if (err < 0)
+ return err;
+
+ 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, &regval);
+ 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), &regs, 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), &regval);
+ 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, &regval);
+ if (err < 0)
+ return err;
+ *val = !!(regval & BIT(channel));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ err = regmap_read(data->regmap, reg, &regval);
+ if (err < 0)
+ return err;
+ *val = IN_FROM_REG(regval, channel);
+ return 0;
+}
+
+static int adm9240_in_write(struct device *dev, u32 attr, int channel, long val)
+{
+ 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), &regval);
+ 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), &regval);
+ 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), &regval);
+ 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, &regval);
+ if (err < 0)
+ return err;
+ temp = regval << 1;
+ err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, &regval);
+ 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), &regval);
+ 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), &regval);
+ if (err < 0)
+ return err;
+ *val = (s8)regval * 1000;
+ break;
+ case hwmon_temp_alarm:
+ err = regmap_read(data->regmap, ADM9240_REG_INT(0), &regval);
+ 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;
+
+ 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);
+
+ err = adm9240_init_client(data);
+ if (err < 0)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+ &adm9240_chip_info,
+ adm9240_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adm9240_id[] = {
+ { "adm9240", adm9240 },
+ { "ds1780", ds1780 },
+ { "lm81", lm81 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm9240_id);
+
+static struct i2c_driver adm9240_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adm9240",
+ },
+ .probe_new = adm9240_probe,
+ .id_table = adm9240_id,
+ .detect = adm9240_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adm9240_driver);
+
+MODULE_AUTHOR("Michiel Rook <michiel@grendelproject.nl>, "
+ "Grant Coady <gcoady.lk@gmail.com> and others");
+MODULE_DESCRIPTION("ADM9240/DS1780/LM81 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
new file mode 100644
index 000000000..7246198f0
--- /dev/null
+++ b/drivers/hwmon/ads7828.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ads7828.c - driver for TI ADS7828 8-channel A/D converter and compatibles
+ * (C) 2007 EADS Astrium
+ *
+ * This driver is based on the lm75 and other lm_sensors/hwmon drivers
+ *
+ * Written by Steve Hardy <shardy@redhat.com>
+ *
+ * ADS7830 support, by Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
+ *
+ * For further information, see the Documentation/hwmon/ads7828.rst file.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/ads7828.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+/* The ADS7828 registers */
+#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
+#define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */
+#define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */
+#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */
+#define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */
+#define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */
+
+/* List of supported devices */
+enum ads7828_chips { ads7828, ads7830 };
+
+/* Client specific data */
+struct ads7828_data {
+ struct regmap *regmap;
+ u8 cmd_byte; /* Command byte without channel bits */
+ unsigned int lsb_resol; /* Resolution of the ADC sample LSB */
+};
+
+/* Command byte C2,C1,C0 - see datasheet */
+static inline u8 ads7828_cmd_byte(u8 cmd, int ch)
+{
+ return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4);
+}
+
+/* sysfs callback function */
+static ssize_t ads7828_in_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ads7828_data *data = dev_get_drvdata(dev);
+ u8 cmd = ads7828_cmd_byte(data->cmd_byte, attr->index);
+ unsigned int regval;
+ int err;
+
+ err = regmap_read(data->regmap, cmd, &regval);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%d\n",
+ DIV_ROUND_CLOSEST(regval * data->lsb_resol, 1000));
+}
+
+static SENSOR_DEVICE_ATTR_RO(in0_input, ads7828_in, 0);
+static SENSOR_DEVICE_ATTR_RO(in1_input, ads7828_in, 1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, ads7828_in, 2);
+static SENSOR_DEVICE_ATTR_RO(in3_input, ads7828_in, 3);
+static SENSOR_DEVICE_ATTR_RO(in4_input, ads7828_in, 4);
+static SENSOR_DEVICE_ATTR_RO(in5_input, ads7828_in, 5);
+static SENSOR_DEVICE_ATTR_RO(in6_input, ads7828_in, 6);
+static SENSOR_DEVICE_ATTR_RO(in7_input, ads7828_in, 7);
+
+static struct attribute *ads7828_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,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(ads7828);
+
+static const struct regmap_config ads2828_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+};
+
+static const struct regmap_config ads2830_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+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);
+ struct ads7828_data *data;
+ struct device *hwmon_dev;
+ unsigned int vref_mv = ADS7828_INT_VREF_MV;
+ unsigned int vref_uv;
+ bool diff_input = false;
+ bool ext_vref = false;
+ unsigned int regval;
+ enum ads7828_chips chip;
+ struct regulator *reg;
+
+ data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (pdata) {
+ diff_input = pdata->diff_input;
+ ext_vref = pdata->ext_vref;
+ if (ext_vref && pdata->vref_mv)
+ vref_mv = pdata->vref_mv;
+ } else if (dev->of_node) {
+ diff_input = of_property_read_bool(dev->of_node,
+ "ti,differential-input");
+ reg = devm_regulator_get_optional(dev, "vref");
+ if (!IS_ERR(reg)) {
+ vref_uv = regulator_get_voltage(reg);
+ vref_mv = DIV_ROUND_CLOSEST(vref_uv, 1000);
+ if (vref_mv < ADS7828_EXT_VREF_MV_MIN ||
+ vref_mv > ADS7828_EXT_VREF_MV_MAX)
+ return -EINVAL;
+ ext_vref = true;
+ }
+ }
+
+ if (client->dev.of_node)
+ chip = (enum ads7828_chips)
+ of_device_get_match_data(&client->dev);
+ else
+ 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,
+ ADS7828_EXT_VREF_MV_MAX);
+
+ /* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */
+ if (chip == ads7828) {
+ data->lsb_resol = DIV_ROUND_CLOSEST(vref_mv * 1000, 4096);
+ data->regmap = devm_regmap_init_i2c(client,
+ &ads2828_regmap_config);
+ } else {
+ data->lsb_resol = DIV_ROUND_CLOSEST(vref_mv * 1000, 256);
+ data->regmap = devm_regmap_init_i2c(client,
+ &ads2830_regmap_config);
+ }
+
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ data->cmd_byte = ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3;
+ if (!diff_input)
+ data->cmd_byte |= ADS7828_CMD_SD_SE;
+
+ /*
+ * Datasheet specifies internal reference voltage is disabled by
+ * default. The internal reference voltage needs to be enabled and
+ * voltage needs to settle before getting valid ADC data. So perform a
+ * dummy read to enable the internal reference voltage.
+ */
+ if (!ext_vref)
+ regmap_read(data->regmap, data->cmd_byte, &regval);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ ads7828_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ads7828_device_ids[] = {
+ { "ads7828", ads7828 },
+ { "ads7830", ads7830 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ads7828_device_ids);
+
+static const struct of_device_id __maybe_unused ads7828_of_match[] = {
+ {
+ .compatible = "ti,ads7828",
+ .data = (void *)ads7828
+ },
+ {
+ .compatible = "ti,ads7830",
+ .data = (void *)ads7830
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads7828_of_match);
+
+static struct i2c_driver ads7828_driver = {
+ .driver = {
+ .name = "ads7828",
+ .of_match_table = of_match_ptr(ads7828_of_match),
+ },
+
+ .id_table = ads7828_device_ids,
+ .probe_new = ads7828_probe,
+};
+
+module_i2c_driver(ads7828_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>");
+MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter and compatibles");
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
new file mode 100644
index 000000000..5434c3796
--- /dev/null
+++ b/drivers/hwmon/ads7871.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ads7871 - driver for TI ADS7871 A/D converter
+ *
+ * Copyright (c) 2010 Paul Thomas <pthomas8589@gmail.com>
+ *
+ * You need to have something like this in struct spi_board_info
+ * {
+ * .modalias = "ads7871",
+ * .max_speed_hz = 2*1000*1000,
+ * .chip_select = 0,
+ * .bus_num = 1,
+ * },
+ */
+
+/*From figure 18 in the datasheet*/
+/*Register addresses*/
+#define REG_LS_BYTE 0 /*A/D Output Data, LS Byte*/
+#define REG_MS_BYTE 1 /*A/D Output Data, MS Byte*/
+#define REG_PGA_VALID 2 /*PGA Valid Register*/
+#define REG_AD_CONTROL 3 /*A/D Control Register*/
+#define REG_GAIN_MUX 4 /*Gain/Mux Register*/
+#define REG_IO_STATE 5 /*Digital I/O State Register*/
+#define REG_IO_CONTROL 6 /*Digital I/O Control Register*/
+#define REG_OSC_CONTROL 7 /*Rev/Oscillator Control Register*/
+#define REG_SER_CONTROL 24 /*Serial Interface Control Register*/
+#define REG_ID 31 /*ID Register*/
+
+/*
+ * From figure 17 in the datasheet
+ * These bits get ORed with the address to form
+ * the instruction byte
+ */
+/*Instruction Bit masks*/
+#define INST_MODE_BM (1 << 7)
+#define INST_READ_BM (1 << 6)
+#define INST_16BIT_BM (1 << 5)
+
+/*From figure 18 in the datasheet*/
+/*bit masks for Rev/Oscillator Control Register*/
+#define MUX_CNV_BV 7
+#define MUX_CNV_BM (1 << MUX_CNV_BV)
+#define MUX_M3_BM (1 << 3) /*M3 selects single ended*/
+#define MUX_G_BV 4 /*allows for reg = (gain << MUX_G_BV) | ...*/
+
+/*From figure 18 in the datasheet*/
+/*bit masks for Rev/Oscillator Control Register*/
+#define OSC_OSCR_BM (1 << 5)
+#define OSC_OSCE_BM (1 << 4)
+#define OSC_REFE_BM (1 << 3)
+#define OSC_BUFE_BM (1 << 2)
+#define OSC_R2V_BM (1 << 1)
+#define OSC_RBG_BM (1 << 0)
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#define DEVICE_NAME "ads7871"
+
+struct ads7871_data {
+ struct spi_device *spi;
+};
+
+static int ads7871_read_reg8(struct spi_device *spi, int reg)
+{
+ int ret;
+ reg = reg | INST_READ_BM;
+ ret = spi_w8r8(spi, reg);
+ return ret;
+}
+
+static int ads7871_read_reg16(struct spi_device *spi, int reg)
+{
+ int ret;
+ reg = reg | INST_READ_BM | INST_16BIT_BM;
+ ret = spi_w8r16(spi, reg);
+ return ret;
+}
+
+static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val)
+{
+ u8 tmp[2] = {reg, val};
+ return spi_write(spi, tmp, sizeof(tmp));
+}
+
+static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct ads7871_data *pdata = dev_get_drvdata(dev);
+ struct spi_device *spi = pdata->spi;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int ret, val, i = 0;
+ uint8_t channel, mux_cnv;
+
+ channel = attr->index;
+ /*
+ * TODO: add support for conversions
+ * other than single ended with a gain of 1
+ */
+ /*MUX_M3_BM forces single ended*/
+ /*This is also where the gain of the PGA would be set*/
+ ads7871_write_reg8(spi, REG_GAIN_MUX,
+ (MUX_CNV_BM | MUX_M3_BM | channel));
+
+ ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
+ mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
+ /*
+ * on 400MHz arm9 platform the conversion
+ * is already done when we do this test
+ */
+ while ((i < 2) && mux_cnv) {
+ i++;
+ ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
+ mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
+ msleep_interruptible(1);
+ }
+
+ if (mux_cnv == 0) {
+ val = ads7871_read_reg16(spi, REG_LS_BYTE);
+ /*result in volts*10000 = (val/8192)*2.5*10000*/
+ val = ((val >> 2) * 25000) / 8192;
+ return sprintf(buf, "%d\n", val);
+ } else {
+ return -1;
+ }
+}
+
+static SENSOR_DEVICE_ATTR_RO(in0_input, voltage, 0);
+static SENSOR_DEVICE_ATTR_RO(in1_input, voltage, 1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, voltage, 2);
+static SENSOR_DEVICE_ATTR_RO(in3_input, voltage, 3);
+static SENSOR_DEVICE_ATTR_RO(in4_input, voltage, 4);
+static SENSOR_DEVICE_ATTR_RO(in5_input, voltage, 5);
+static SENSOR_DEVICE_ATTR_RO(in6_input, voltage, 6);
+static SENSOR_DEVICE_ATTR_RO(in7_input, voltage, 7);
+
+static struct attribute *ads7871_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,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(ads7871);
+
+static int ads7871_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ int ret;
+ uint8_t val;
+ struct ads7871_data *pdata;
+ struct device *hwmon_dev;
+
+ /* Configure the SPI bus */
+ spi->mode = (SPI_MODE_0);
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ ads7871_write_reg8(spi, REG_SER_CONTROL, 0);
+ ads7871_write_reg8(spi, REG_AD_CONTROL, 0);
+
+ val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM);
+ ads7871_write_reg8(spi, REG_OSC_CONTROL, val);
+ ret = ads7871_read_reg8(spi, REG_OSC_CONTROL);
+
+ dev_dbg(dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret);
+ /*
+ * because there is no other error checking on an SPI bus
+ * we need to make sure we really have a chip
+ */
+ if (val != ret)
+ return -ENODEV;
+
+ pdata = devm_kzalloc(dev, sizeof(struct ads7871_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->spi = spi;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, spi->modalias,
+ pdata,
+ ads7871_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct spi_driver ads7871_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ },
+ .probe = ads7871_probe,
+};
+
+module_spi_driver(ads7871_driver);
+
+MODULE_AUTHOR("Paul Thomas <pthomas8589@gmail.com>");
+MODULE_DESCRIPTION("TI ADS7871 A/D driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
new file mode 100644
index 000000000..067865f48
--- /dev/null
+++ b/drivers/hwmon/adt7310.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ADT7310/ADT7310 digital temperature sensor driver
+ *
+ * Copyright 2012-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
+#include "adt7x10.h"
+
+#define ADT7310_STATUS 0
+#define ADT7310_CONFIG 1
+#define ADT7310_TEMPERATURE 2
+#define ADT7310_ID 3
+#define ADT7310_T_CRIT 4
+#define ADT7310_T_HYST 5
+#define ADT7310_T_ALARM_HIGH 6
+#define ADT7310_T_ALARM_LOW 7
+
+static const u8 adt7310_reg_table[] = {
+ [ADT7X10_TEMPERATURE] = ADT7310_TEMPERATURE,
+ [ADT7X10_STATUS] = ADT7310_STATUS,
+ [ADT7X10_CONFIG] = ADT7310_CONFIG,
+ [ADT7X10_T_ALARM_HIGH] = ADT7310_T_ALARM_HIGH,
+ [ADT7X10_T_ALARM_LOW] = ADT7310_T_ALARM_LOW,
+ [ADT7X10_T_CRIT] = ADT7310_T_CRIT,
+ [ADT7X10_T_HYST] = ADT7310_T_HYST,
+ [ADT7X10_ID] = ADT7310_ID,
+};
+
+#define ADT7310_CMD_REG_OFFSET 3
+#define ADT7310_CMD_READ 0x40
+
+#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
+
+static int adt7310_spi_read_word(struct spi_device *spi, u8 reg)
+{
+ return spi_w8r16be(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+}
+
+static int adt7310_spi_write_word(struct spi_device *spi, u8 reg, u16 data)
+{
+ u8 buf[3];
+
+ buf[0] = AD7310_COMMAND(reg);
+ put_unaligned_be16(data, &buf[1]);
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int adt7310_spi_read_byte(struct spi_device *spi, u8 reg)
+{
+ return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+}
+
+static int adt7310_spi_write_byte(struct spi_device *spi, u8 reg, u8 data)
+{
+ u8 buf[2];
+
+ buf[0] = AD7310_COMMAND(reg);
+ buf[1] = data;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+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_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ 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 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)
+{
+ 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[] = {
+ { "adt7310", 0 },
+ { "adt7320", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adt7310_id);
+
+static struct spi_driver adt7310_driver = {
+ .driver = {
+ .name = "adt7310",
+ .pm = pm_sleep_ptr(&adt7x10_dev_pm_ops),
+ },
+ .probe = adt7310_spi_probe,
+ .id_table = adt7310_id,
+};
+module_spi_driver(adt7310_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7310/ADT7320 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
new file mode 100644
index 000000000..0cebf6777
--- /dev/null
+++ b/drivers/hwmon/adt7410.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ADT7410/ADT7420 digital temperature sensor driver
+ *
+ * Copyright 2012-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "adt7x10.h"
+
+static bool adt7410_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADT7X10_TEMPERATURE:
+ case ADT7X10_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adt7410_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ int regval;
+
+ 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_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ 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 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)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init(&client->dev, NULL, client,
+ &adt7410_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return adt7x10_probe(&client->dev, client->name, client->irq, regmap);
+}
+
+static const struct i2c_device_id adt7410_ids[] = {
+ { "adt7410", 0 },
+ { "adt7420", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adt7410_ids);
+
+static struct i2c_driver adt7410_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adt7410",
+ .pm = pm_sleep_ptr(&adt7x10_dev_pm_ops),
+ },
+ .probe_new = adt7410_i2c_probe,
+ .id_table = adt7410_ids,
+ .address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
+};
+module_i2c_driver(adt7410_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7410/AD7420 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
new file mode 100644
index 000000000..bf5c5618f
--- /dev/null
+++ b/drivers/hwmon/adt7411.c
@@ -0,0 +1,720 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the ADT7411 (I2C/SPI 8 channel 10 bit ADC & temperature-sensor)
+ *
+ * Copyright (C) 2008, 2010 Pengutronix
+ *
+ * TODO: SPI, use power-down mode for suspend?, interrupt handling?
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/slab.h>
+
+#define ADT7411_REG_STAT_1 0x00
+#define ADT7411_STAT_1_INT_TEMP_HIGH BIT(0)
+#define ADT7411_STAT_1_INT_TEMP_LOW BIT(1)
+#define ADT7411_STAT_1_EXT_TEMP_HIGH_AIN1 BIT(2)
+#define ADT7411_STAT_1_EXT_TEMP_LOW BIT(3)
+#define ADT7411_STAT_1_EXT_TEMP_FAULT BIT(4)
+#define ADT7411_STAT_1_AIN2 BIT(5)
+#define ADT7411_STAT_1_AIN3 BIT(6)
+#define ADT7411_STAT_1_AIN4 BIT(7)
+#define ADT7411_REG_STAT_2 0x01
+#define ADT7411_STAT_2_AIN5 BIT(0)
+#define ADT7411_STAT_2_AIN6 BIT(1)
+#define ADT7411_STAT_2_AIN7 BIT(2)
+#define ADT7411_STAT_2_AIN8 BIT(3)
+#define ADT7411_STAT_2_VDD BIT(4)
+#define ADT7411_REG_INT_TEMP_VDD_LSB 0x03
+#define ADT7411_REG_EXT_TEMP_AIN14_LSB 0x04
+#define ADT7411_REG_VDD_MSB 0x06
+#define ADT7411_REG_INT_TEMP_MSB 0x07
+#define ADT7411_REG_EXT_TEMP_AIN1_MSB 0x08
+
+#define ADT7411_REG_CFG1 0x18
+#define ADT7411_CFG1_START_MONITOR BIT(0)
+#define ADT7411_CFG1_RESERVED_BIT1 BIT(1)
+#define ADT7411_CFG1_EXT_TDM BIT(2)
+#define ADT7411_CFG1_RESERVED_BIT3 BIT(3)
+
+#define ADT7411_REG_CFG2 0x19
+#define ADT7411_CFG2_DISABLE_AVG BIT(5)
+
+#define ADT7411_REG_CFG3 0x1a
+#define ADT7411_CFG3_ADC_CLK_225 BIT(0)
+#define ADT7411_CFG3_RESERVED_BIT1 BIT(1)
+#define ADT7411_CFG3_RESERVED_BIT2 BIT(2)
+#define ADT7411_CFG3_RESERVED_BIT3 BIT(3)
+#define ADT7411_CFG3_REF_VDD BIT(4)
+
+#define ADT7411_REG_VDD_HIGH 0x23
+#define ADT7411_REG_VDD_LOW 0x24
+#define ADT7411_REG_TEMP_HIGH(nr) (0x25 + 2 * (nr))
+#define ADT7411_REG_TEMP_LOW(nr) (0x26 + 2 * (nr))
+#define ADT7411_REG_IN_HIGH(nr) ((nr) > 1 \
+ ? 0x2b + 2 * ((nr)-2) \
+ : 0x27)
+#define ADT7411_REG_IN_LOW(nr) ((nr) > 1 \
+ ? 0x2c + 2 * ((nr)-2) \
+ : 0x28)
+
+#define ADT7411_REG_DEVICE_ID 0x4d
+#define ADT7411_REG_MANUFACTURER_ID 0x4e
+
+#define ADT7411_DEVICE_ID 0x2
+#define ADT7411_MANUFACTURER_ID 0x41
+
+static const unsigned short normal_i2c[] = { 0x48, 0x4a, 0x4b, I2C_CLIENT_END };
+
+static const u8 adt7411_in_alarm_reg[] = {
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_1,
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_2,
+ ADT7411_REG_STAT_2,
+};
+
+static const u8 adt7411_in_alarm_bits[] = {
+ ADT7411_STAT_2_VDD,
+ ADT7411_STAT_1_EXT_TEMP_HIGH_AIN1,
+ ADT7411_STAT_1_AIN2,
+ ADT7411_STAT_1_AIN3,
+ ADT7411_STAT_1_AIN4,
+ ADT7411_STAT_2_AIN5,
+ ADT7411_STAT_2_AIN6,
+ ADT7411_STAT_2_AIN7,
+ ADT7411_STAT_2_AIN8,
+};
+
+struct adt7411_data {
+ struct mutex device_lock; /* for "atomic" device accesses */
+ struct mutex update_lock;
+ unsigned long next_update;
+ long vref_cached;
+ struct i2c_client *client;
+ bool use_ext_temp;
+};
+
+/*
+ * When reading a register containing (up to 4) lsb, all associated
+ * msb-registers get locked by the hardware. After _one_ of those msb is read,
+ * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb
+ * is protected here with a mutex, too.
+ */
+static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg,
+ u8 msb_reg, u8 lsb_shift)
+{
+ struct adt7411_data *data = i2c_get_clientdata(client);
+ int val, tmp;
+
+ mutex_lock(&data->device_lock);
+
+ val = i2c_smbus_read_byte_data(client, lsb_reg);
+ if (val < 0)
+ goto exit_unlock;
+
+ tmp = (val >> lsb_shift) & 3;
+ val = i2c_smbus_read_byte_data(client, msb_reg);
+
+ if (val >= 0)
+ val = (val << 2) | tmp;
+
+ exit_unlock:
+ mutex_unlock(&data->device_lock);
+
+ return val;
+}
+
+static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit,
+ bool flag)
+{
+ struct adt7411_data *data = i2c_get_clientdata(client);
+ int ret, val;
+
+ mutex_lock(&data->device_lock);
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ goto exit_unlock;
+
+ if (flag)
+ val = ret | bit;
+ else
+ val = ret & ~bit;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ exit_unlock:
+ mutex_unlock(&data->device_lock);
+ return ret;
+}
+
+static ssize_t adt7411_show_bit(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret = i2c_smbus_read_byte_data(client, attr2->index);
+
+ return ret < 0 ? ret : sprintf(buf, "%u\n", !!(ret & attr2->nr));
+}
+
+static ssize_t adt7411_set_bit(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute_2 *s_attr2 = to_sensor_dev_attr_2(attr);
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+ unsigned long flag;
+
+ ret = kstrtoul(buf, 0, &flag);
+ if (ret || flag > 1)
+ return -EINVAL;
+
+ ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag);
+
+ /* force update */
+ mutex_lock(&data->update_lock);
+ data->next_update = jiffies;
+ mutex_unlock(&data->update_lock);
+
+ return ret < 0 ? ret : count;
+}
+
+#define ADT7411_BIT_ATTR(__name, __reg, __bit) \
+ SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \
+ adt7411_set_bit, __bit, __reg)
+
+static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG);
+static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225);
+static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD);
+
+static struct attribute *adt7411_attrs[] = {
+ &sensor_dev_attr_no_average.dev_attr.attr,
+ &sensor_dev_attr_fast_sampling.dev_attr.attr,
+ &sensor_dev_attr_adc_ref_vdd.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(adt7411);
+
+static int adt7411_read_in_alarm(struct device *dev, int channel, long *val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, adt7411_in_alarm_reg[channel]);
+ if (ret < 0)
+ return ret;
+ *val = !!(ret & adt7411_in_alarm_bits[channel]);
+ return 0;
+}
+
+static int adt7411_read_in_vdd(struct device *dev, u32 attr, long *val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ switch (attr) {
+ case hwmon_in_input:
+ ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
+ ADT7411_REG_VDD_MSB, 2);
+ if (ret < 0)
+ return ret;
+ *val = ret * 7000 / 1024;
+ return 0;
+ case hwmon_in_min:
+ ret = i2c_smbus_read_byte_data(client, ADT7411_REG_VDD_LOW);
+ if (ret < 0)
+ return ret;
+ *val = ret * 7000 / 256;
+ return 0;
+ case hwmon_in_max:
+ ret = i2c_smbus_read_byte_data(client, ADT7411_REG_VDD_HIGH);
+ if (ret < 0)
+ return ret;
+ *val = ret * 7000 / 256;
+ return 0;
+ case hwmon_in_alarm:
+ return adt7411_read_in_alarm(dev, 0, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int adt7411_update_vref(struct device *dev)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int val;
+
+ if (time_after_eq(jiffies, data->next_update)) {
+ val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3);
+ if (val < 0)
+ return val;
+
+ if (val & ADT7411_CFG3_REF_VDD) {
+ val = adt7411_read_in_vdd(dev, hwmon_in_input,
+ &data->vref_cached);
+ if (val < 0)
+ return val;
+ } else {
+ data->vref_cached = 2250;
+ }
+
+ data->next_update = jiffies + HZ;
+ }
+
+ return 0;
+}
+
+static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ int ret;
+ int reg, lsb_reg, lsb_shift;
+ int nr = channel - 1;
+
+ mutex_lock(&data->update_lock);
+ ret = adt7411_update_vref(dev);
+ if (ret < 0)
+ goto exit_unlock;
+
+ switch (attr) {
+ case hwmon_in_input:
+ lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2);
+ lsb_shift = 2 * (nr & 0x03);
+ ret = adt7411_read_10_bit(client, lsb_reg,
+ ADT7411_REG_EXT_TEMP_AIN1_MSB + nr,
+ lsb_shift);
+ if (ret < 0)
+ goto exit_unlock;
+ *val = ret * data->vref_cached / 1024;
+ ret = 0;
+ break;
+ case hwmon_in_min:
+ case hwmon_in_max:
+ reg = (attr == hwmon_in_min)
+ ? ADT7411_REG_IN_LOW(channel)
+ : ADT7411_REG_IN_HIGH(channel);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ goto exit_unlock;
+ *val = ret * data->vref_cached / 256;
+ ret = 0;
+ break;
+ case hwmon_in_alarm:
+ ret = adt7411_read_in_alarm(dev, channel, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ exit_unlock:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int adt7411_read_in(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ if (channel == 0)
+ return adt7411_read_in_vdd(dev, attr, val);
+ else
+ return adt7411_read_in_chan(dev, attr, channel, val);
+}
+
+
+static int adt7411_read_temp_alarm(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret, bit;
+
+ ret = i2c_smbus_read_byte_data(client, ADT7411_REG_STAT_1);
+ if (ret < 0)
+ return ret;
+
+ switch (attr) {
+ case hwmon_temp_min_alarm:
+ bit = channel ? ADT7411_STAT_1_EXT_TEMP_LOW
+ : ADT7411_STAT_1_INT_TEMP_LOW;
+ break;
+ case hwmon_temp_max_alarm:
+ bit = channel ? ADT7411_STAT_1_EXT_TEMP_HIGH_AIN1
+ : ADT7411_STAT_1_INT_TEMP_HIGH;
+ break;
+ case hwmon_temp_fault:
+ bit = ADT7411_STAT_1_EXT_TEMP_FAULT;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ *val = !!(ret & bit);
+ return 0;
+}
+
+static int adt7411_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret, reg, regl, regh;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ regl = channel ? ADT7411_REG_EXT_TEMP_AIN14_LSB :
+ ADT7411_REG_INT_TEMP_VDD_LSB;
+ regh = channel ? ADT7411_REG_EXT_TEMP_AIN1_MSB :
+ ADT7411_REG_INT_TEMP_MSB;
+ ret = adt7411_read_10_bit(client, regl, regh, 0);
+ if (ret < 0)
+ return ret;
+ ret = ret & 0x200 ? ret - 0x400 : ret; /* 10 bit signed */
+ *val = ret * 250;
+ return 0;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ reg = (attr == hwmon_temp_min)
+ ? ADT7411_REG_TEMP_LOW(channel)
+ : ADT7411_REG_TEMP_HIGH(channel);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ return ret;
+ ret = ret & 0x80 ? ret - 0x100 : ret; /* 8 bit signed */
+ *val = ret * 1000;
+ return 0;
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_fault:
+ return adt7411_read_temp_alarm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int adt7411_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_in:
+ return adt7411_read_in(dev, attr, channel, val);
+ case hwmon_temp:
+ return adt7411_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int adt7411_write_in_vdd(struct device *dev, u32 attr, long val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int reg;
+
+ val = clamp_val(val, 0, 255 * 7000 / 256);
+ val = DIV_ROUND_CLOSEST(val * 256, 7000);
+
+ switch (attr) {
+ case hwmon_in_min:
+ reg = ADT7411_REG_VDD_LOW;
+ break;
+ case hwmon_in_max:
+ reg = ADT7411_REG_VDD_HIGH;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adt7411_write_in_chan(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret, reg;
+
+ mutex_lock(&data->update_lock);
+ ret = adt7411_update_vref(dev);
+ if (ret < 0)
+ goto exit_unlock;
+ val = clamp_val(val, 0, 255 * data->vref_cached / 256);
+ val = DIV_ROUND_CLOSEST(val * 256, data->vref_cached);
+
+ switch (attr) {
+ case hwmon_in_min:
+ reg = ADT7411_REG_IN_LOW(channel);
+ break;
+ case hwmon_in_max:
+ reg = ADT7411_REG_IN_HIGH(channel);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto exit_unlock;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ exit_unlock:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int adt7411_write_in(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ if (channel == 0)
+ return adt7411_write_in_vdd(dev, attr, val);
+ else
+ return adt7411_write_in_chan(dev, attr, channel, val);
+}
+
+static int adt7411_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int reg;
+
+ val = clamp_val(val, -128000, 127000);
+ val = DIV_ROUND_CLOSEST(val, 1000);
+
+ switch (attr) {
+ case hwmon_temp_min:
+ reg = ADT7411_REG_TEMP_LOW(channel);
+ break;
+ case hwmon_temp_max:
+ reg = ADT7411_REG_TEMP_HIGH(channel);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adt7411_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_in:
+ return adt7411_write_in(dev, attr, channel, val);
+ case hwmon_temp:
+ return adt7411_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t adt7411_is_visible(const void *_data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct adt7411_data *data = _data;
+ bool visible;
+
+ switch (type) {
+ case hwmon_in:
+ visible = channel == 0 || channel >= 3 || !data->use_ext_temp;
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_alarm:
+ return visible ? S_IRUGO : 0;
+ case hwmon_in_min:
+ case hwmon_in_max:
+ return visible ? S_IRUGO | S_IWUSR : 0;
+ }
+ break;
+ case hwmon_temp:
+ visible = channel == 0 || data->use_ext_temp;
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_fault:
+ return visible ? S_IRUGO : 0;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ return visible ? S_IRUGO | S_IWUSR : 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int adt7411_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int val;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID);
+ if (val < 0 || val != ADT7411_MANUFACTURER_ID) {
+ dev_dbg(&client->dev,
+ "Wrong manufacturer ID. Got %d, expected %d\n",
+ val, ADT7411_MANUFACTURER_ID);
+ return -ENODEV;
+ }
+
+ val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID);
+ if (val < 0 || val != ADT7411_DEVICE_ID) {
+ dev_dbg(&client->dev,
+ "Wrong device ID. Got %d, expected %d\n",
+ val, ADT7411_DEVICE_ID);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "adt7411", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int adt7411_init_device(struct adt7411_data *data)
+{
+ int ret;
+ u8 val;
+
+ ret = i2c_smbus_read_byte_data(data->client, ADT7411_REG_CFG3);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * We must only write zero to bit 1 and bit 2 and only one to bit 3
+ * according to the datasheet.
+ */
+ val = ret;
+ val &= ~(ADT7411_CFG3_RESERVED_BIT1 | ADT7411_CFG3_RESERVED_BIT2);
+ val |= ADT7411_CFG3_RESERVED_BIT3;
+
+ ret = i2c_smbus_write_byte_data(data->client, ADT7411_REG_CFG3, val);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, ADT7411_REG_CFG1);
+ if (ret < 0)
+ return ret;
+
+ data->use_ext_temp = ret & ADT7411_CFG1_EXT_TDM;
+
+ /*
+ * We must only write zero to bit 1 and only one to bit 3 according to
+ * the datasheet.
+ */
+ val = ret;
+ val &= ~ADT7411_CFG1_RESERVED_BIT1;
+ val |= ADT7411_CFG1_RESERVED_BIT3;
+
+ /* enable monitoring */
+ val |= ADT7411_CFG1_START_MONITOR;
+
+ return i2c_smbus_write_byte_data(data->client, ADT7411_REG_CFG1, val);
+}
+
+static const struct hwmon_channel_info *adt7411_info[] = {
+ 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_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(temp,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX | HWMON_T_MAX_ALARM,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_FAULT),
+ NULL
+};
+
+static const struct hwmon_ops adt7411_hwmon_ops = {
+ .is_visible = adt7411_is_visible,
+ .read = adt7411_read,
+ .write = adt7411_write,
+};
+
+static const struct hwmon_chip_info adt7411_chip_info = {
+ .ops = &adt7411_hwmon_ops,
+ .info = adt7411_info,
+};
+
+static int adt7411_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct adt7411_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ mutex_init(&data->device_lock);
+ mutex_init(&data->update_lock);
+
+ ret = adt7411_init_device(data);
+ if (ret < 0)
+ return ret;
+
+ /* force update on first occasion */
+ data->next_update = jiffies;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &adt7411_chip_info,
+ adt7411_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adt7411_id[] = {
+ { "adt7411", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adt7411_id);
+
+static struct i2c_driver adt7411_driver = {
+ .driver = {
+ .name = "adt7411",
+ },
+ .probe_new = adt7411_probe,
+ .id_table = adt7411_id,
+ .detect = adt7411_detect,
+ .address_list = normal_i2c,
+ .class = I2C_CLASS_HWMON,
+};
+
+module_i2c_driver(adt7411_driver);
+
+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
new file mode 100644
index 000000000..9c0235849
--- /dev/null
+++ b/drivers/hwmon/adt7462.c
@@ -0,0 +1,1832 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A hwmon driver for the Analog Devices ADT7462
+ * Copyright (C) 2008 IBM
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END };
+
+/* ADT7462 registers */
+#define ADT7462_REG_DEVICE 0x3D
+#define ADT7462_REG_VENDOR 0x3E
+#define ADT7462_REG_REVISION 0x3F
+
+#define ADT7462_REG_MIN_TEMP_BASE_ADDR 0x44
+#define ADT7462_REG_MIN_TEMP_MAX_ADDR 0x47
+#define ADT7462_REG_MAX_TEMP_BASE_ADDR 0x48
+#define ADT7462_REG_MAX_TEMP_MAX_ADDR 0x4B
+#define ADT7462_REG_TEMP_BASE_ADDR 0x88
+#define ADT7462_REG_TEMP_MAX_ADDR 0x8F
+
+#define ADT7462_REG_FAN_BASE_ADDR 0x98
+#define ADT7462_REG_FAN_MAX_ADDR 0x9F
+#define ADT7462_REG_FAN2_BASE_ADDR 0xA2
+#define ADT7462_REG_FAN2_MAX_ADDR 0xA9
+#define ADT7462_REG_FAN_ENABLE 0x07
+#define ADT7462_REG_FAN_MIN_BASE_ADDR 0x78
+#define ADT7462_REG_FAN_MIN_MAX_ADDR 0x7F
+
+#define ADT7462_REG_CFG2 0x02
+#define ADT7462_FSPD_MASK 0x20
+
+#define ADT7462_REG_PWM_BASE_ADDR 0xAA
+#define ADT7462_REG_PWM_MAX_ADDR 0xAD
+#define ADT7462_REG_PWM_MIN_BASE_ADDR 0x28
+#define ADT7462_REG_PWM_MIN_MAX_ADDR 0x2B
+#define ADT7462_REG_PWM_MAX 0x2C
+#define ADT7462_REG_PWM_TEMP_MIN_BASE_ADDR 0x5C
+#define ADT7462_REG_PWM_TEMP_MIN_MAX_ADDR 0x5F
+#define ADT7462_REG_PWM_TEMP_RANGE_BASE_ADDR 0x60
+#define ADT7462_REG_PWM_TEMP_RANGE_MAX_ADDR 0x63
+#define ADT7462_PWM_HYST_MASK 0x0F
+#define ADT7462_PWM_RANGE_MASK 0xF0
+#define ADT7462_PWM_RANGE_SHIFT 4
+#define ADT7462_REG_PWM_CFG_BASE_ADDR 0x21
+#define ADT7462_REG_PWM_CFG_MAX_ADDR 0x24
+#define ADT7462_PWM_CHANNEL_MASK 0xE0
+#define ADT7462_PWM_CHANNEL_SHIFT 5
+
+#define ADT7462_REG_PIN_CFG_BASE_ADDR 0x10
+#define ADT7462_REG_PIN_CFG_MAX_ADDR 0x13
+#define ADT7462_PIN7_INPUT 0x01 /* cfg0 */
+#define ADT7462_DIODE3_INPUT 0x20
+#define ADT7462_DIODE1_INPUT 0x40
+#define ADT7462_VID_INPUT 0x80
+#define ADT7462_PIN22_INPUT 0x04 /* cfg1 */
+#define ADT7462_PIN21_INPUT 0x08
+#define ADT7462_PIN19_INPUT 0x10
+#define ADT7462_PIN15_INPUT 0x20
+#define ADT7462_PIN13_INPUT 0x40
+#define ADT7462_PIN8_INPUT 0x80
+#define ADT7462_PIN23_MASK 0x03
+#define ADT7462_PIN23_SHIFT 0
+#define ADT7462_PIN26_MASK 0x0C /* cfg2 */
+#define ADT7462_PIN26_SHIFT 2
+#define ADT7462_PIN25_MASK 0x30
+#define ADT7462_PIN25_SHIFT 4
+#define ADT7462_PIN24_MASK 0xC0
+#define ADT7462_PIN24_SHIFT 6
+#define ADT7462_PIN26_VOLT_INPUT 0x08
+#define ADT7462_PIN25_VOLT_INPUT 0x20
+#define ADT7462_PIN28_SHIFT 4 /* cfg3 */
+#define ADT7462_PIN28_VOLT 0x5
+
+#define ADT7462_REG_ALARM1 0xB8
+#define ADT7462_LT_ALARM 0x02
+#define ADT7462_R1T_ALARM 0x04
+#define ADT7462_R2T_ALARM 0x08
+#define ADT7462_R3T_ALARM 0x10
+#define ADT7462_REG_ALARM2 0xBB
+#define ADT7462_V0_ALARM 0x01
+#define ADT7462_V1_ALARM 0x02
+#define ADT7462_V2_ALARM 0x04
+#define ADT7462_V3_ALARM 0x08
+#define ADT7462_V4_ALARM 0x10
+#define ADT7462_V5_ALARM 0x20
+#define ADT7462_V6_ALARM 0x40
+#define ADT7462_V7_ALARM 0x80
+#define ADT7462_REG_ALARM3 0xBC
+#define ADT7462_V8_ALARM 0x08
+#define ADT7462_V9_ALARM 0x10
+#define ADT7462_V10_ALARM 0x20
+#define ADT7462_V11_ALARM 0x40
+#define ADT7462_V12_ALARM 0x80
+#define ADT7462_REG_ALARM4 0xBD
+#define ADT7462_F0_ALARM 0x01
+#define ADT7462_F1_ALARM 0x02
+#define ADT7462_F2_ALARM 0x04
+#define ADT7462_F3_ALARM 0x08
+#define ADT7462_F4_ALARM 0x10
+#define ADT7462_F5_ALARM 0x20
+#define ADT7462_F6_ALARM 0x40
+#define ADT7462_F7_ALARM 0x80
+#define ADT7462_ALARM1 0x0000
+#define ADT7462_ALARM2 0x0100
+#define ADT7462_ALARM3 0x0200
+#define ADT7462_ALARM4 0x0300
+#define ADT7462_ALARM_REG_SHIFT 8
+#define ADT7462_ALARM_FLAG_MASK 0x0F
+
+#define ADT7462_TEMP_COUNT 4
+#define ADT7462_TEMP_REG(x) (ADT7462_REG_TEMP_BASE_ADDR + ((x) * 2))
+#define ADT7462_TEMP_MIN_REG(x) (ADT7462_REG_MIN_TEMP_BASE_ADDR + (x))
+#define ADT7462_TEMP_MAX_REG(x) (ADT7462_REG_MAX_TEMP_BASE_ADDR + (x))
+#define TEMP_FRAC_OFFSET 6
+
+#define ADT7462_FAN_COUNT 8
+#define ADT7462_REG_FAN_MIN(x) (ADT7462_REG_FAN_MIN_BASE_ADDR + (x))
+
+#define ADT7462_PWM_COUNT 4
+#define ADT7462_REG_PWM(x) (ADT7462_REG_PWM_BASE_ADDR + (x))
+#define ADT7462_REG_PWM_MIN(x) (ADT7462_REG_PWM_MIN_BASE_ADDR + (x))
+#define ADT7462_REG_PWM_TMIN(x) \
+ (ADT7462_REG_PWM_TEMP_MIN_BASE_ADDR + (x))
+#define ADT7462_REG_PWM_TRANGE(x) \
+ (ADT7462_REG_PWM_TEMP_RANGE_BASE_ADDR + (x))
+
+#define ADT7462_PIN_CFG_REG_COUNT 4
+#define ADT7462_REG_PIN_CFG(x) (ADT7462_REG_PIN_CFG_BASE_ADDR + (x))
+#define ADT7462_REG_PWM_CFG(x) (ADT7462_REG_PWM_CFG_BASE_ADDR + (x))
+
+#define ADT7462_ALARM_REG_COUNT 4
+
+/*
+ * The chip can measure 13 different voltage sources:
+ *
+ * 1. +12V1 (pin 7)
+ * 2. Vccp1/+2.5V/+1.8V/+1.5V (pin 23)
+ * 3. +12V3 (pin 22)
+ * 4. +5V (pin 21)
+ * 5. +1.25V/+0.9V (pin 19)
+ * 6. +2.5V/+1.8V (pin 15)
+ * 7. +3.3v (pin 13)
+ * 8. +12V2 (pin 8)
+ * 9. Vbatt/FSB_Vtt (pin 26)
+ * A. +3.3V/+1.2V1 (pin 25)
+ * B. Vccp2/+2.5V/+1.8V/+1.5V (pin 24)
+ * C. +1.5V ICH (only if BOTH pin 28/29 are set to +1.5V)
+ * D. +1.5V 3GPIO (only if BOTH pin 28/29 are set to +1.5V)
+ *
+ * Each of these 13 has a factor to convert raw to voltage. Even better,
+ * the pins can be connected to other sensors (tach/gpio/hot/etc), which
+ * makes the bookkeeping tricky.
+ *
+ * Some, but not all, of these voltages have low/high limits.
+ */
+#define ADT7462_VOLT_COUNT 13
+
+#define ADT7462_VENDOR 0x41
+#define ADT7462_DEVICE 0x62
+/* datasheet only mentions a revision 4 */
+#define ADT7462_REVISION 0x04
+
+/* How often do we reread sensors values? (In jiffies) */
+#define SENSOR_REFRESH_INTERVAL (2 * HZ)
+
+/* How often do we reread sensor limit values? (In jiffies) */
+#define LIMIT_REFRESH_INTERVAL (60 * HZ)
+
+/* datasheet says to divide this number by the fan reading to get fan rpm */
+#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x))
+#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM
+#define FAN_PERIOD_INVALID 65535
+#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+
+#define MASK_AND_SHIFT(value, prefix) \
+ (((value) & prefix##_MASK) >> prefix##_SHIFT)
+
+struct adt7462_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ char sensors_valid;
+ char limits_valid;
+ unsigned long sensors_last_updated; /* In jiffies */
+ unsigned long limits_last_updated; /* In jiffies */
+
+ u8 temp[ADT7462_TEMP_COUNT];
+ /* bits 6-7 are quarter pieces of temp */
+ u8 temp_frac[ADT7462_TEMP_COUNT];
+ u8 temp_min[ADT7462_TEMP_COUNT];
+ u8 temp_max[ADT7462_TEMP_COUNT];
+ u16 fan[ADT7462_FAN_COUNT];
+ u8 fan_enabled;
+ u8 fan_min[ADT7462_FAN_COUNT];
+ u8 cfg2;
+ u8 pwm[ADT7462_PWM_COUNT];
+ u8 pin_cfg[ADT7462_PIN_CFG_REG_COUNT];
+ u8 voltages[ADT7462_VOLT_COUNT];
+ u8 volt_max[ADT7462_VOLT_COUNT];
+ u8 volt_min[ADT7462_VOLT_COUNT];
+ u8 pwm_min[ADT7462_PWM_COUNT];
+ u8 pwm_tmin[ADT7462_PWM_COUNT];
+ u8 pwm_trange[ADT7462_PWM_COUNT];
+ u8 pwm_max; /* only one per chip */
+ u8 pwm_cfg[ADT7462_PWM_COUNT];
+ u8 alarms[ADT7462_ALARM_REG_COUNT];
+};
+
+/*
+ * 16-bit registers on the ADT7462 are low-byte first. The data sheet says
+ * that the low byte must be read before the high byte.
+ */
+static inline int adt7462_read_word_data(struct i2c_client *client, u8 reg)
+{
+ u16 foo;
+ foo = i2c_smbus_read_byte_data(client, reg);
+ foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8);
+ return foo;
+}
+
+/* For some reason these registers are not contiguous. */
+static int ADT7462_REG_FAN(int fan)
+{
+ if (fan < 4)
+ return ADT7462_REG_FAN_BASE_ADDR + (2 * fan);
+ return ADT7462_REG_FAN2_BASE_ADDR + (2 * (fan - 4));
+}
+
+/* Voltage registers are scattered everywhere */
+static int ADT7462_REG_VOLT_MAX(struct adt7462_data *data, int which)
+{
+ switch (which) {
+ case 0:
+ if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT))
+ return 0x7C;
+ break;
+ case 1:
+ return 0x69;
+ case 2:
+ if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT))
+ return 0x7F;
+ break;
+ case 3:
+ if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT))
+ return 0x7E;
+ break;
+ case 4:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT))
+ return 0x4B;
+ break;
+ case 5:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT))
+ return 0x49;
+ break;
+ case 6:
+ if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT))
+ return 0x68;
+ break;
+ case 7:
+ if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT))
+ return 0x7D;
+ break;
+ case 8:
+ if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT))
+ return 0x6C;
+ break;
+ case 9:
+ if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT))
+ return 0x6B;
+ break;
+ case 10:
+ return 0x6A;
+ case 11:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return 0x50;
+ break;
+ case 12:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return 0x4C;
+ break;
+ }
+ return 0;
+}
+
+static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which)
+{
+ switch (which) {
+ case 0:
+ if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT))
+ return 0x6D;
+ break;
+ case 1:
+ return 0x72;
+ case 2:
+ if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT))
+ return 0x6F;
+ break;
+ case 3:
+ if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT))
+ return 0x71;
+ break;
+ case 4:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT))
+ return 0x47;
+ break;
+ case 5:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT))
+ return 0x45;
+ break;
+ case 6:
+ if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT))
+ return 0x70;
+ break;
+ case 7:
+ if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT))
+ return 0x6E;
+ break;
+ case 8:
+ if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT))
+ return 0x75;
+ break;
+ case 9:
+ if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT))
+ return 0x74;
+ break;
+ case 10:
+ return 0x73;
+ case 11:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return 0x76;
+ break;
+ case 12:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return 0x77;
+ break;
+ }
+ return 0;
+}
+
+static int ADT7462_REG_VOLT(struct adt7462_data *data, int which)
+{
+ switch (which) {
+ case 0:
+ if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT))
+ return 0xA3;
+ break;
+ case 1:
+ return 0x90;
+ case 2:
+ if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT))
+ return 0xA9;
+ break;
+ case 3:
+ if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT))
+ return 0xA7;
+ break;
+ case 4:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT))
+ return 0x8F;
+ break;
+ case 5:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT))
+ return 0x8B;
+ break;
+ case 6:
+ if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT))
+ return 0x96;
+ break;
+ case 7:
+ if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT))
+ return 0xA5;
+ break;
+ case 8:
+ if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT))
+ return 0x93;
+ break;
+ case 9:
+ if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT))
+ return 0x92;
+ break;
+ case 10:
+ return 0x91;
+ case 11:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return 0x94;
+ break;
+ case 12:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return 0x95;
+ break;
+ }
+ return 0;
+}
+
+/* Provide labels for sysfs */
+static const char *voltage_label(struct adt7462_data *data, int which)
+{
+ switch (which) {
+ case 0:
+ if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT))
+ return "+12V1";
+ break;
+ case 1:
+ switch (MASK_AND_SHIFT(data->pin_cfg[1], ADT7462_PIN23)) {
+ case 0:
+ return "Vccp1";
+ case 1:
+ return "+2.5V";
+ case 2:
+ return "+1.8V";
+ case 3:
+ return "+1.5V";
+ }
+ fallthrough;
+ case 2:
+ if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT))
+ return "+12V3";
+ break;
+ case 3:
+ if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT))
+ return "+5V";
+ break;
+ case 4:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) {
+ if (data->pin_cfg[1] & ADT7462_PIN19_INPUT)
+ return "+0.9V";
+ return "+1.25V";
+ }
+ break;
+ case 5:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) {
+ if (data->pin_cfg[1] & ADT7462_PIN19_INPUT)
+ return "+1.8V";
+ return "+2.5V";
+ }
+ break;
+ case 6:
+ if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT))
+ return "+3.3V";
+ break;
+ case 7:
+ if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT))
+ return "+12V2";
+ break;
+ case 8:
+ switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN26)) {
+ case 0:
+ return "Vbatt";
+ case 1:
+ return "FSB_Vtt";
+ }
+ break;
+ case 9:
+ switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN25)) {
+ case 0:
+ return "+3.3V";
+ case 1:
+ return "+1.2V1";
+ }
+ break;
+ case 10:
+ switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN24)) {
+ case 0:
+ return "Vccp2";
+ case 1:
+ return "+2.5V";
+ case 2:
+ return "+1.8V";
+ case 3:
+ return "+1.5";
+ }
+ fallthrough;
+ case 11:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return "+1.5V ICH";
+ break;
+ case 12:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return "+1.5V 3GPIO";
+ break;
+ }
+ return "N/A";
+}
+
+/* Multipliers are actually in uV, not mV. */
+static int voltage_multiplier(struct adt7462_data *data, int which)
+{
+ switch (which) {
+ case 0:
+ if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT))
+ return 62500;
+ break;
+ case 1:
+ switch (MASK_AND_SHIFT(data->pin_cfg[1], ADT7462_PIN23)) {
+ case 0:
+ if (data->pin_cfg[0] & ADT7462_VID_INPUT)
+ return 12500;
+ return 6250;
+ case 1:
+ return 13000;
+ case 2:
+ return 9400;
+ case 3:
+ return 7800;
+ }
+ fallthrough;
+ case 2:
+ if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT))
+ return 62500;
+ break;
+ case 3:
+ if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT))
+ return 26000;
+ break;
+ case 4:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) {
+ if (data->pin_cfg[1] & ADT7462_PIN19_INPUT)
+ return 4690;
+ return 6500;
+ }
+ break;
+ case 5:
+ if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) {
+ if (data->pin_cfg[1] & ADT7462_PIN15_INPUT)
+ return 9400;
+ return 13000;
+ }
+ break;
+ case 6:
+ if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT))
+ return 17200;
+ break;
+ case 7:
+ if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT))
+ return 62500;
+ break;
+ case 8:
+ switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN26)) {
+ case 0:
+ return 15600;
+ case 1:
+ return 6250;
+ }
+ break;
+ case 9:
+ switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN25)) {
+ case 0:
+ return 17200;
+ case 1:
+ return 6250;
+ }
+ break;
+ case 10:
+ switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN24)) {
+ case 0:
+ return 6250;
+ case 1:
+ return 13000;
+ case 2:
+ return 9400;
+ case 3:
+ return 7800;
+ }
+ fallthrough;
+ case 11:
+ case 12:
+ if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT ==
+ ADT7462_PIN28_VOLT &&
+ !(data->pin_cfg[0] & ADT7462_VID_INPUT))
+ return 7800;
+ }
+ return 0;
+}
+
+static int temp_enabled(struct adt7462_data *data, int which)
+{
+ switch (which) {
+ case 0:
+ case 2:
+ return 1;
+ case 1:
+ if (data->pin_cfg[0] & ADT7462_DIODE1_INPUT)
+ return 1;
+ break;
+ case 3:
+ if (data->pin_cfg[0] & ADT7462_DIODE3_INPUT)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+static const char *temp_label(struct adt7462_data *data, int which)
+{
+ switch (which) {
+ case 0:
+ return "local";
+ case 1:
+ if (data->pin_cfg[0] & ADT7462_DIODE1_INPUT)
+ return "remote1";
+ break;
+ case 2:
+ return "remote2";
+ case 3:
+ if (data->pin_cfg[0] & ADT7462_DIODE3_INPUT)
+ return "remote3";
+ break;
+ }
+ return "N/A";
+}
+
+/* Map Trange register values to mC */
+#define NUM_TRANGE_VALUES 16
+static const int trange_values[NUM_TRANGE_VALUES] = {
+ 2000,
+ 2500,
+ 3300,
+ 4000,
+ 5000,
+ 6700,
+ 8000,
+ 10000,
+ 13300,
+ 16000,
+ 20000,
+ 26700,
+ 32000,
+ 40000,
+ 53300,
+ 80000
+};
+
+static int find_trange_value(int trange)
+{
+ int i;
+
+ for (i = 0; i < NUM_TRANGE_VALUES; i++)
+ if (trange_values[i] == trange)
+ return i;
+
+ return -EINVAL;
+}
+
+static struct adt7462_data *adt7462_update_device(struct device *dev)
+{
+ struct adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long local_jiffies = jiffies;
+ int i;
+
+ mutex_lock(&data->lock);
+ if (time_before(local_jiffies, data->sensors_last_updated +
+ SENSOR_REFRESH_INTERVAL)
+ && data->sensors_valid)
+ goto no_sensor_update;
+
+ for (i = 0; i < ADT7462_TEMP_COUNT; i++) {
+ /*
+ * Reading the fractional register locks the integral
+ * register until both have been read.
+ */
+ data->temp_frac[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_TEMP_REG(i));
+ data->temp[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_TEMP_REG(i) + 1);
+ }
+
+ for (i = 0; i < ADT7462_FAN_COUNT; i++)
+ data->fan[i] = adt7462_read_word_data(client,
+ ADT7462_REG_FAN(i));
+
+ data->fan_enabled = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_FAN_ENABLE);
+
+ for (i = 0; i < ADT7462_PWM_COUNT; i++)
+ data->pwm[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_PWM(i));
+
+ for (i = 0; i < ADT7462_PIN_CFG_REG_COUNT; i++)
+ data->pin_cfg[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_PIN_CFG(i));
+
+ for (i = 0; i < ADT7462_VOLT_COUNT; i++) {
+ int reg = ADT7462_REG_VOLT(data, i);
+ if (!reg)
+ data->voltages[i] = 0;
+ else
+ data->voltages[i] = i2c_smbus_read_byte_data(client,
+ reg);
+ }
+
+ data->alarms[0] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM1);
+ data->alarms[1] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM2);
+ data->alarms[2] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM3);
+ data->alarms[3] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM4);
+
+ data->sensors_last_updated = local_jiffies;
+ data->sensors_valid = 1;
+
+no_sensor_update:
+ if (time_before(local_jiffies, data->limits_last_updated +
+ LIMIT_REFRESH_INTERVAL)
+ && data->limits_valid)
+ goto out;
+
+ for (i = 0; i < ADT7462_TEMP_COUNT; i++) {
+ data->temp_min[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_TEMP_MIN_REG(i));
+ data->temp_max[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_TEMP_MAX_REG(i));
+ }
+
+ for (i = 0; i < ADT7462_FAN_COUNT; i++)
+ data->fan_min[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_FAN_MIN(i));
+
+ for (i = 0; i < ADT7462_VOLT_COUNT; i++) {
+ int reg = ADT7462_REG_VOLT_MAX(data, i);
+ data->volt_max[i] =
+ (reg ? i2c_smbus_read_byte_data(client, reg) : 0);
+
+ reg = ADT7462_REG_VOLT_MIN(data, i);
+ data->volt_min[i] =
+ (reg ? i2c_smbus_read_byte_data(client, reg) : 0);
+ }
+
+ for (i = 0; i < ADT7462_PWM_COUNT; i++) {
+ data->pwm_min[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_PWM_MIN(i));
+ data->pwm_tmin[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_PWM_TMIN(i));
+ data->pwm_trange[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_PWM_TRANGE(i));
+ data->pwm_cfg[i] = i2c_smbus_read_byte_data(client,
+ ADT7462_REG_PWM_CFG(i));
+ }
+
+ data->pwm_max = i2c_smbus_read_byte_data(client, ADT7462_REG_PWM_MAX);
+
+ data->cfg2 = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2);
+
+ data->limits_last_updated = local_jiffies;
+ data->limits_valid = 1;
+
+out:
+ mutex_unlock(&data->lock);
+ return data;
+}
+
+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 adt7462_data *data = adt7462_update_device(dev);
+
+ if (!temp_enabled(data, attr->index))
+ return sprintf(buf, "0\n");
+
+ return sprintf(buf, "%d\n", 1000 * (data->temp_min[attr->index] - 64));
+}
+
+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 adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+
+ if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index))
+ return -EINVAL;
+
+ temp = clamp_val(temp, -64000, 191000);
+ temp = DIV_ROUND_CLOSEST(temp, 1000) + 64;
+
+ mutex_lock(&data->lock);
+ data->temp_min[attr->index] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_TEMP_MIN_REG(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+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 adt7462_data *data = adt7462_update_device(dev);
+
+ if (!temp_enabled(data, attr->index))
+ return sprintf(buf, "0\n");
+
+ return sprintf(buf, "%d\n", 1000 * (data->temp_max[attr->index] - 64));
+}
+
+static ssize_t temp_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 adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+
+ if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index))
+ return -EINVAL;
+
+ temp = clamp_val(temp, -64000, 191000);
+ temp = DIV_ROUND_CLOSEST(temp, 1000) + 64;
+
+ mutex_lock(&data->lock);
+ data->temp_max[attr->index] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_TEMP_MAX_REG(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+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 adt7462_data *data = adt7462_update_device(dev);
+ u8 frac = data->temp_frac[attr->index] >> TEMP_FRAC_OFFSET;
+
+ if (!temp_enabled(data, attr->index))
+ return sprintf(buf, "0\n");
+
+ return sprintf(buf, "%d\n", 1000 * (data->temp[attr->index] - 64) +
+ 250 * frac);
+}
+
+static ssize_t temp_label_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+
+ return sprintf(buf, "%s\n", temp_label(data, attr->index));
+}
+
+static ssize_t volt_max_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+ int x = voltage_multiplier(data, attr->index);
+
+ x *= data->volt_max[attr->index];
+ x /= 1000; /* convert from uV to mV */
+
+ return sprintf(buf, "%d\n", x);
+}
+
+static ssize_t volt_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 adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int x = voltage_multiplier(data, attr->index);
+ long temp;
+
+ if (kstrtol(buf, 10, &temp) || !x)
+ return -EINVAL;
+
+ temp = clamp_val(temp, 0, 255 * x / 1000);
+ temp *= 1000; /* convert mV to uV */
+ temp = DIV_ROUND_CLOSEST(temp, x);
+
+ mutex_lock(&data->lock);
+ data->volt_max[attr->index] = temp;
+ i2c_smbus_write_byte_data(client,
+ ADT7462_REG_VOLT_MAX(data, attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t volt_min_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+ int x = voltage_multiplier(data, attr->index);
+
+ x *= data->volt_min[attr->index];
+ x /= 1000; /* convert from uV to mV */
+
+ return sprintf(buf, "%d\n", x);
+}
+
+static ssize_t volt_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 adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int x = voltage_multiplier(data, attr->index);
+ long temp;
+
+ if (kstrtol(buf, 10, &temp) || !x)
+ return -EINVAL;
+
+ temp = clamp_val(temp, 0, 255 * x / 1000);
+ temp *= 1000; /* convert mV to uV */
+ temp = DIV_ROUND_CLOSEST(temp, x);
+
+ mutex_lock(&data->lock);
+ data->volt_min[attr->index] = temp;
+ i2c_smbus_write_byte_data(client,
+ ADT7462_REG_VOLT_MIN(data, attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t voltage_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+ int x = voltage_multiplier(data, attr->index);
+
+ x *= data->voltages[attr->index];
+ x /= 1000; /* convert from uV to mV */
+
+ return sprintf(buf, "%d\n", x);
+}
+
+static ssize_t voltage_label_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+
+ return sprintf(buf, "%s\n", voltage_label(data, attr->index));
+}
+
+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 adt7462_data *data = adt7462_update_device(dev);
+ int reg = attr->index >> ADT7462_ALARM_REG_SHIFT;
+ int mask = attr->index & ADT7462_ALARM_FLAG_MASK;
+
+ if (data->alarms[reg] & mask)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static int fan_enabled(struct adt7462_data *data, int fan)
+{
+ return data->fan_enabled & (1 << fan);
+}
+
+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 adt7462_data *data = adt7462_update_device(dev);
+ u16 temp;
+
+ /* Only the MSB of the min fan period is stored... */
+ temp = data->fan_min[attr->index];
+ temp <<= 8;
+
+ if (!fan_enabled(data, attr->index) ||
+ !FAN_DATA_VALID(temp))
+ return sprintf(buf, "0\n");
+
+ return sprintf(buf, "%d\n", FAN_PERIOD_TO_RPM(temp));
+}
+
+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 adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+
+ if (kstrtol(buf, 10, &temp) || !temp ||
+ !fan_enabled(data, attr->index))
+ return -EINVAL;
+
+ temp = FAN_RPM_TO_PERIOD(temp);
+ temp >>= 8;
+ temp = clamp_val(temp, 1, 255);
+
+ mutex_lock(&data->lock);
+ data->fan_min[attr->index] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_REG_FAN_MIN(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ 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 adt7462_data *data = adt7462_update_device(dev);
+
+ if (!fan_enabled(data, attr->index) ||
+ !FAN_DATA_VALID(data->fan[attr->index]))
+ return sprintf(buf, "0\n");
+
+ return sprintf(buf, "%d\n",
+ FAN_PERIOD_TO_RPM(data->fan[attr->index]));
+}
+
+static ssize_t force_pwm_max_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7462_data *data = adt7462_update_device(dev);
+ return sprintf(buf, "%d\n", (data->cfg2 & ADT7462_FSPD_MASK ? 1 : 0));
+}
+
+static ssize_t force_pwm_max_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+ u8 reg;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ reg = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2);
+ if (temp)
+ reg |= ADT7462_FSPD_MASK;
+ else
+ reg &= ~ADT7462_FSPD_MASK;
+ data->cfg2 = reg;
+ i2c_smbus_write_byte_data(client, ADT7462_REG_CFG2, 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 adt7462_data *data = adt7462_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 adt7462_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, ADT7462_REG_PWM(attr->index), temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t pwm_max_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7462_data *data = adt7462_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm_max);
+}
+
+static ssize_t pwm_max_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7462_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_max = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_MAX, temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t pwm_min_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm_min[attr->index]);
+}
+
+static ssize_t pwm_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 adt7462_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_min[attr->index] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_MIN(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t pwm_hyst_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+ return sprintf(buf, "%d\n", 1000 *
+ (data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK));
+}
+
+static ssize_t pwm_hyst_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 adt7462_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, 15000);
+ temp = DIV_ROUND_CLOSEST(temp, 1000);
+
+ /* package things up */
+ temp &= ADT7462_PWM_HYST_MASK;
+ temp |= data->pwm_trange[attr->index] & ADT7462_PWM_RANGE_MASK;
+
+ mutex_lock(&data->lock);
+ data->pwm_trange[attr->index] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TRANGE(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t pwm_tmax_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+
+ /* tmax = tmin + trange */
+ int trange = trange_values[data->pwm_trange[attr->index] >>
+ ADT7462_PWM_RANGE_SHIFT];
+ int tmin = (data->pwm_tmin[attr->index] - 64) * 1000;
+
+ return sprintf(buf, "%d\n", tmin + trange);
+}
+
+static ssize_t pwm_tmax_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int temp;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int tmin, trange_value;
+ long trange;
+
+ if (kstrtol(buf, 10, &trange))
+ return -EINVAL;
+
+ /* trange = tmax - tmin */
+ tmin = (data->pwm_tmin[attr->index] - 64) * 1000;
+ trange_value = find_trange_value(trange - tmin);
+ if (trange_value < 0)
+ return trange_value;
+
+ temp = trange_value << ADT7462_PWM_RANGE_SHIFT;
+ temp |= data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK;
+
+ mutex_lock(&data->lock);
+ data->pwm_trange[attr->index] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TRANGE(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t pwm_tmin_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+ return sprintf(buf, "%d\n", 1000 * (data->pwm_tmin[attr->index] - 64));
+}
+
+static ssize_t pwm_tmin_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 adt7462_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, -64000, 191000);
+ temp = DIV_ROUND_CLOSEST(temp, 1000) + 64;
+
+ mutex_lock(&data->lock);
+ data->pwm_tmin[attr->index] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_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 adt7462_data *data = adt7462_update_device(dev);
+ int cfg = data->pwm_cfg[attr->index] >> ADT7462_PWM_CHANNEL_SHIFT;
+
+ switch (cfg) {
+ case 4: /* off */
+ return sprintf(buf, "0\n");
+ case 7: /* manual */
+ return sprintf(buf, "1\n");
+ default: /* automatic */
+ return sprintf(buf, "2\n");
+ }
+}
+
+static void set_pwm_channel(struct i2c_client *client,
+ struct adt7462_data *data,
+ int which,
+ int value)
+{
+ int temp = data->pwm_cfg[which] & ~ADT7462_PWM_CHANNEL_MASK;
+ temp |= value << ADT7462_PWM_CHANNEL_SHIFT;
+
+ mutex_lock(&data->lock);
+ data->pwm_cfg[which] = temp;
+ i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_CFG(which), temp);
+ mutex_unlock(&data->lock);
+}
+
+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 adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ switch (temp) {
+ case 0: /* off */
+ set_pwm_channel(client, data, attr->index, 4);
+ return count;
+ case 1: /* manual */
+ set_pwm_channel(client, data, attr->index, 7);
+ return count;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t pwm_auto_temp_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct adt7462_data *data = adt7462_update_device(dev);
+ int channel = data->pwm_cfg[attr->index] >> ADT7462_PWM_CHANNEL_SHIFT;
+
+ switch (channel) {
+ case 0: /* temp[1234] only */
+ case 1:
+ case 2:
+ case 3:
+ return sprintf(buf, "%d\n", (1 << channel));
+ case 5: /* temp1 & temp4 */
+ return sprintf(buf, "9\n");
+ case 6:
+ return sprintf(buf, "15\n");
+ default:
+ return sprintf(buf, "0\n");
+ }
+}
+
+static int cvt_auto_temp(int input)
+{
+ if (input == 0xF)
+ return 6;
+ if (input == 0x9)
+ return 5;
+ if (input < 1 || !is_power_of_2(input))
+ return -EINVAL;
+ return ilog2(input);
+}
+
+static ssize_t pwm_auto_temp_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 adt7462_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ temp = cvt_auto_temp(temp);
+ if (temp < 0)
+ return temp;
+
+ set_pwm_channel(client, data, attr->index, temp);
+
+ return count;
+}
+
+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(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_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(temp1_label, temp_label, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_label, temp_label, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_label, temp_label, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_label, temp_label, 3);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm,
+ ADT7462_ALARM1 | ADT7462_LT_ALARM);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm,
+ ADT7462_ALARM1 | ADT7462_R1T_ALARM);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm,
+ ADT7462_ALARM1 | ADT7462_R2T_ALARM);
+static SENSOR_DEVICE_ATTR_RO(temp4_alarm, alarm,
+ ADT7462_ALARM1 | ADT7462_R3T_ALARM);
+
+static SENSOR_DEVICE_ATTR_RW(in1_max, volt_max, 0);
+static SENSOR_DEVICE_ATTR_RW(in2_max, volt_max, 1);
+static SENSOR_DEVICE_ATTR_RW(in3_max, volt_max, 2);
+static SENSOR_DEVICE_ATTR_RW(in4_max, volt_max, 3);
+static SENSOR_DEVICE_ATTR_RW(in5_max, volt_max, 4);
+static SENSOR_DEVICE_ATTR_RW(in6_max, volt_max, 5);
+static SENSOR_DEVICE_ATTR_RW(in7_max, volt_max, 6);
+static SENSOR_DEVICE_ATTR_RW(in8_max, volt_max, 7);
+static SENSOR_DEVICE_ATTR_RW(in9_max, volt_max, 8);
+static SENSOR_DEVICE_ATTR_RW(in10_max, volt_max, 9);
+static SENSOR_DEVICE_ATTR_RW(in11_max, volt_max, 10);
+static SENSOR_DEVICE_ATTR_RW(in12_max, volt_max, 11);
+static SENSOR_DEVICE_ATTR_RW(in13_max, volt_max, 12);
+
+static SENSOR_DEVICE_ATTR_RW(in1_min, volt_min, 0);
+static SENSOR_DEVICE_ATTR_RW(in2_min, volt_min, 1);
+static SENSOR_DEVICE_ATTR_RW(in3_min, volt_min, 2);
+static SENSOR_DEVICE_ATTR_RW(in4_min, volt_min, 3);
+static SENSOR_DEVICE_ATTR_RW(in5_min, volt_min, 4);
+static SENSOR_DEVICE_ATTR_RW(in6_min, volt_min, 5);
+static SENSOR_DEVICE_ATTR_RW(in7_min, volt_min, 6);
+static SENSOR_DEVICE_ATTR_RW(in8_min, volt_min, 7);
+static SENSOR_DEVICE_ATTR_RW(in9_min, volt_min, 8);
+static SENSOR_DEVICE_ATTR_RW(in10_min, volt_min, 9);
+static SENSOR_DEVICE_ATTR_RW(in11_min, volt_min, 10);
+static SENSOR_DEVICE_ATTR_RW(in12_min, volt_min, 11);
+static SENSOR_DEVICE_ATTR_RW(in13_min, volt_min, 12);
+
+static SENSOR_DEVICE_ATTR_RO(in1_input, voltage, 0);
+static SENSOR_DEVICE_ATTR_RO(in2_input, voltage, 1);
+static SENSOR_DEVICE_ATTR_RO(in3_input, voltage, 2);
+static SENSOR_DEVICE_ATTR_RO(in4_input, voltage, 3);
+static SENSOR_DEVICE_ATTR_RO(in5_input, voltage, 4);
+static SENSOR_DEVICE_ATTR_RO(in6_input, voltage, 5);
+static SENSOR_DEVICE_ATTR_RO(in7_input, voltage, 6);
+static SENSOR_DEVICE_ATTR_RO(in8_input, voltage, 7);
+static SENSOR_DEVICE_ATTR_RO(in9_input, voltage, 8);
+static SENSOR_DEVICE_ATTR_RO(in10_input, voltage, 9);
+static SENSOR_DEVICE_ATTR_RO(in11_input, voltage, 10);
+static SENSOR_DEVICE_ATTR_RO(in12_input, voltage, 11);
+static SENSOR_DEVICE_ATTR_RO(in13_input, voltage, 12);
+
+static SENSOR_DEVICE_ATTR_RO(in1_label, voltage_label, 0);
+static SENSOR_DEVICE_ATTR_RO(in2_label, voltage_label, 1);
+static SENSOR_DEVICE_ATTR_RO(in3_label, voltage_label, 2);
+static SENSOR_DEVICE_ATTR_RO(in4_label, voltage_label, 3);
+static SENSOR_DEVICE_ATTR_RO(in5_label, voltage_label, 4);
+static SENSOR_DEVICE_ATTR_RO(in6_label, voltage_label, 5);
+static SENSOR_DEVICE_ATTR_RO(in7_label, voltage_label, 6);
+static SENSOR_DEVICE_ATTR_RO(in8_label, voltage_label, 7);
+static SENSOR_DEVICE_ATTR_RO(in9_label, voltage_label, 8);
+static SENSOR_DEVICE_ATTR_RO(in10_label, voltage_label, 9);
+static SENSOR_DEVICE_ATTR_RO(in11_label, voltage_label, 10);
+static SENSOR_DEVICE_ATTR_RO(in12_label, voltage_label, 11);
+static SENSOR_DEVICE_ATTR_RO(in13_label, voltage_label, 12);
+
+static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V0_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V7_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V2_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V6_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V5_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V4_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V3_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in8_alarm, alarm,
+ ADT7462_ALARM2 | ADT7462_V1_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in9_alarm, alarm,
+ ADT7462_ALARM3 | ADT7462_V10_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in10_alarm, alarm,
+ ADT7462_ALARM3 | ADT7462_V9_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in11_alarm, alarm,
+ ADT7462_ALARM3 | ADT7462_V8_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in12_alarm, alarm,
+ ADT7462_ALARM3 | ADT7462_V11_ALARM);
+static SENSOR_DEVICE_ATTR_RO(in13_alarm, alarm,
+ ADT7462_ALARM3 | ADT7462_V12_ALARM);
+
+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_RW(fan5_min, fan_min, 4);
+static SENSOR_DEVICE_ATTR_RW(fan6_min, fan_min, 5);
+static SENSOR_DEVICE_ATTR_RW(fan7_min, fan_min, 6);
+static SENSOR_DEVICE_ATTR_RW(fan8_min, fan_min, 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(fan5_input, fan, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_input, fan, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_input, fan, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_input, fan, 7);
+
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F0_ALARM);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F1_ALARM);
+static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F2_ALARM);
+static SENSOR_DEVICE_ATTR_RO(fan4_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F3_ALARM);
+static SENSOR_DEVICE_ATTR_RO(fan5_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F4_ALARM);
+static SENSOR_DEVICE_ATTR_RO(fan6_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F5_ALARM);
+static SENSOR_DEVICE_ATTR_RO(fan7_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F6_ALARM);
+static SENSOR_DEVICE_ATTR_RO(fan8_alarm, alarm,
+ ADT7462_ALARM4 | ADT7462_F7_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 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);
+static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point1_pwm, pwm_min, 3);
+
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm_max, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point2_pwm, pwm_max, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point2_pwm, pwm_max, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point2_pwm, pwm_max, 3);
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_point1_hyst, pwm_hyst, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_point1_hyst, pwm_hyst, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_point1_hyst, pwm_hyst, 2);
+static SENSOR_DEVICE_ATTR_RW(temp4_auto_point1_hyst, pwm_hyst, 3);
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_point2_hyst, pwm_hyst, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_point2_hyst, pwm_hyst, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_point2_hyst, pwm_hyst, 2);
+static SENSOR_DEVICE_ATTR_RW(temp4_auto_point2_hyst, pwm_hyst, 3);
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_point1_temp, pwm_tmin, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_point1_temp, pwm_tmin, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_point1_temp, pwm_tmin, 2);
+static SENSOR_DEVICE_ATTR_RW(temp4_auto_point1_temp, pwm_tmin, 3);
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_point2_temp, pwm_tmax, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_point2_temp, pwm_tmax, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_point2_temp, pwm_tmax, 2);
+static SENSOR_DEVICE_ATTR_RW(temp4_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);
+static SENSOR_DEVICE_ATTR_RW(pwm4_auto_channels_temp, pwm_auto_temp, 3);
+
+static struct attribute *adt7462_attrs[] = {
+ &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_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_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_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_label.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_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ &sensor_dev_attr_in12_max.dev_attr.attr,
+ &sensor_dev_attr_in13_max.dev_attr.attr,
+
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ &sensor_dev_attr_in12_min.dev_attr.attr,
+ &sensor_dev_attr_in13_min.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_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,
+
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+ &sensor_dev_attr_in9_alarm.dev_attr.attr,
+ &sensor_dev_attr_in10_alarm.dev_attr.attr,
+ &sensor_dev_attr_in11_alarm.dev_attr.attr,
+ &sensor_dev_attr_in12_alarm.dev_attr.attr,
+ &sensor_dev_attr_in13_alarm.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_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan8_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_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_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_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan7_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan8_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_force_pwm_max.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.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,
+ &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_auto_point1_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_auto_point1_hyst.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_auto_point2_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point2_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_auto_point2_hyst.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp4_auto_point1_temp.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp4_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,
+ &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(adt7462);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int adt7462_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int vendor, device, revision;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR);
+ if (vendor != ADT7462_VENDOR)
+ return -ENODEV;
+
+ device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE);
+ if (device != ADT7462_DEVICE)
+ return -ENODEV;
+
+ revision = i2c_smbus_read_byte_data(client, ADT7462_REG_REVISION);
+ if (revision != ADT7462_REVISION)
+ return -ENODEV;
+
+ strscpy(info->type, "adt7462", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int adt7462_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct adt7462_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct adt7462_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+
+ dev_info(&client->dev, "%s chip found\n", client->name);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ adt7462_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id adt7462_id[] = {
+ { "adt7462", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adt7462_id);
+
+static struct i2c_driver adt7462_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adt7462",
+ },
+ .probe_new = adt7462_probe,
+ .id_table = adt7462_id,
+ .detect = adt7462_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adt7462_driver);
+
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
+MODULE_DESCRIPTION("ADT7462 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
new file mode 100644
index 000000000..927f8df05
--- /dev/null
+++ b/drivers/hwmon/adt7470.c
@@ -0,0 +1,1328 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A hwmon driver for the Analog Devices ADT7470
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#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>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+
+/* ADT7470 registers */
+#define ADT7470_REG_BASE_ADDR 0x20
+#define ADT7470_REG_TEMP_BASE_ADDR 0x20
+#define ADT7470_REG_TEMP_MAX_ADDR 0x29
+#define ADT7470_REG_FAN_BASE_ADDR 0x2A
+#define ADT7470_REG_FAN_MAX_ADDR 0x31
+#define ADT7470_REG_PWM_BASE_ADDR 0x32
+#define ADT7470_REG_PWM_MAX_ADDR 0x35
+#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
+#define ADT7470_R3T_ALARM 0x04
+#define ADT7470_R4T_ALARM 0x08
+#define ADT7470_R5T_ALARM 0x10
+#define ADT7470_R6T_ALARM 0x20
+#define ADT7470_R7T_ALARM 0x40
+#define ADT7470_OOL_ALARM 0x80
+#define ADT7470_REG_ALARM2 0x42
+#define ADT7470_R8T_ALARM 0x01
+#define ADT7470_R9T_ALARM 0x02
+#define ADT7470_R10T_ALARM 0x04
+#define ADT7470_FAN1_ALARM 0x10
+#define ADT7470_FAN2_ALARM 0x20
+#define ADT7470_FAN3_ALARM 0x40
+#define ADT7470_FAN4_ALARM 0x80
+#define ADT7470_REG_TEMP_LIMITS_BASE_ADDR 0x44
+#define ADT7470_REG_TEMP_LIMITS_MAX_ADDR 0x57
+#define ADT7470_REG_FAN_MIN_BASE_ADDR 0x58
+#define ADT7470_REG_FAN_MIN_MAX_ADDR 0x5F
+#define ADT7470_REG_FAN_MAX_BASE_ADDR 0x60
+#define ADT7470_REG_FAN_MAX_MAX_ADDR 0x67
+#define ADT7470_REG_PWM_CFG_BASE_ADDR 0x68
+#define ADT7470_REG_PWM12_CFG 0x68
+#define ADT7470_PWM2_AUTO_MASK 0x40
+#define ADT7470_PWM1_AUTO_MASK 0x80
+#define ADT7470_PWM_AUTO_MASK 0xC0
+#define ADT7470_REG_PWM34_CFG 0x69
+#define ADT7470_PWM3_AUTO_MASK 0x40
+#define ADT7470_PWM4_AUTO_MASK 0x80
+#define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A
+#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
+#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
+#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
+#define ADT7470_REG_CFG_2 0x74
+#define ADT7470_REG_ACOUSTICS12 0x75
+#define ADT7470_REG_ACOUSTICS34 0x76
+#define ADT7470_REG_DEVICE 0x3D
+#define ADT7470_REG_VENDOR 0x3E
+#define ADT7470_REG_REVISION 0x3F
+#define ADT7470_REG_ALARM1_MASK 0x72
+#define ADT7470_REG_ALARM2_MASK 0x73
+#define ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR 0x7C
+#define ADT7470_REG_PWM_AUTO_TEMP_MAX_ADDR 0x7D
+#define ADT7470_REG_MAX_ADDR 0x81
+
+#define ADT7470_TEMP_COUNT 10
+#define ADT7470_TEMP_REG(x) (ADT7470_REG_TEMP_BASE_ADDR + (x))
+#define ADT7470_TEMP_MIN_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2))
+#define ADT7470_TEMP_MAX_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + \
+ ((x) * 2) + 1)
+
+#define ADT7470_FAN_COUNT 4
+#define ADT7470_REG_FAN(x) (ADT7470_REG_FAN_BASE_ADDR + ((x) * 2))
+#define ADT7470_REG_FAN_MIN(x) (ADT7470_REG_FAN_MIN_BASE_ADDR + ((x) * 2))
+#define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2))
+
+#define ADT7470_PWM_COUNT 4
+#define ADT7470_REG_PWM(x) (ADT7470_REG_PWM_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_TMIN(x) (ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_CFG(x) (ADT7470_REG_PWM_CFG_BASE_ADDR + ((x) / 2))
+#define ADT7470_REG_PWM_AUTO_TEMP(x) (ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR + \
+ ((x) / 2))
+
+#define ALARM2(x) ((x) << 8)
+
+#define ADT7470_VENDOR 0x41
+#define ADT7470_DEVICE 0x70
+/* datasheet only mentions a revision 2 */
+#define ADT7470_REVISION 0x02
+
+/* "all temps" according to hwmon sysfs interface spec */
+#define ADT7470_PWM_ALL_TEMPS 0x3FF
+
+/* How often do we reread sensors values? (In jiffies) */
+#define SENSOR_REFRESH_INTERVAL (5 * HZ)
+
+/* How often do we reread sensor limit values? (In jiffies) */
+#define LIMIT_REFRESH_INTERVAL (60 * HZ)
+
+/* Wait at least 200ms per sensor for 10 sensors */
+#define TEMP_COLLECTION_TIME 2000
+
+/* auto update thing won't fire more than every 2s */
+#define AUTO_UPDATE_INTERVAL 2000
+
+/* datasheet says to divide this number by the fan reading to get fan rpm */
+#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x))
+#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM
+#define FAN_PERIOD_INVALID 65535
+#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+
+/* Config registers 1 and 2 include fields for selecting the PWM frequency */
+#define ADT7470_CFG_LF 0x40
+#define ADT7470_FREQ_MASK 0x70
+#define ADT7470_FREQ_SHIFT 4
+
+struct adt7470_data {
+ struct regmap *regmap;
+ struct mutex lock;
+ char sensors_valid;
+ char limits_valid;
+ unsigned long sensors_last_updated; /* In jiffies */
+ unsigned long limits_last_updated; /* In jiffies */
+
+ int num_temp_sensors; /* -1 = probe */
+ int temperatures_probed;
+
+ s8 temp[ADT7470_TEMP_COUNT];
+ s8 temp_min[ADT7470_TEMP_COUNT];
+ s8 temp_max[ADT7470_TEMP_COUNT];
+ u16 fan[ADT7470_FAN_COUNT];
+ u16 fan_min[ADT7470_FAN_COUNT];
+ u16 fan_max[ADT7470_FAN_COUNT];
+ u16 alarm;
+ u16 alarms_mask;
+ u8 force_pwm_max;
+ u8 pwm[ADT7470_PWM_COUNT];
+ u8 pwm_max[ADT7470_PWM_COUNT];
+ u8 pwm_automatic[ADT7470_PWM_COUNT];
+ u8 pwm_min[ADT7470_PWM_COUNT];
+ s8 pwm_tmin[ADT7470_PWM_COUNT];
+ u8 pwm_auto_temp[ADT7470_PWM_COUNT];
+
+ struct task_struct *auto_update;
+ unsigned int auto_update_interval;
+};
+
+/*
+ * 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 adt7470_data *data, unsigned int reg,
+ unsigned int *val)
+{
+ u8 regval[2];
+ int err;
+
+ err = regmap_bulk_read(data->regmap, reg, &regval, 2);
+ if (err < 0)
+ return err;
+
+ *val = regval[0] | (regval[1] << 8);
+
+ return 0;
+}
+
+static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg,
+ unsigned int val)
+{
+ u8 regval[2];
+
+ regval[0] = val & 0xFF;
+ regval[1] = val >> 8;
+
+ return regmap_bulk_write(data->regmap, reg, &regval, 2);
+}
+
+/* Probe for temperature sensors. Assumes lock is held */
+static int adt7470_read_temperatures(struct adt7470_data *data)
+{
+ unsigned long res;
+ unsigned int pwm_cfg[2];
+ int err;
+ int i;
+ u8 pwm[ADT7470_FAN_COUNT];
+
+ /* save pwm[1-4] config register */
+ 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 */
+ 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 */
+ 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 */
+ err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
+ ADT7470_PWM_COUNT);
+ if (err < 0)
+ return err;
+
+ /* start reading temperature sensors */
+ 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 ?
+ data->num_temp_sensors * 200 :
+ TEMP_COLLECTION_TIME));
+
+ /* done reading temperature sensors */
+ 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 */
+ 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++) {
+ if (data->temp[i])
+ data->num_temp_sensors = i + 1;
+ }
+ data->temperatures_probed = 1;
+ return 0;
+}
+
+static int adt7470_update_thread(void *p)
+{
+ struct i2c_client *client = p;
+ struct adt7470_data *data = i2c_get_clientdata(client);
+
+ while (!kthread_should_stop()) {
+ mutex_lock(&data->lock);
+ adt7470_read_temperatures(data);
+ mutex_unlock(&data->lock);
+
+ if (kthread_should_stop())
+ break;
+
+ schedule_timeout_interruptible(msecs_to_jiffies(data->auto_update_interval));
+ }
+
+ return 0;
+}
+
+static int adt7470_update_sensors(struct adt7470_data *data)
+{
+ unsigned int val;
+ int err;
+ int i;
+
+ if (!data->temperatures_probed)
+ err = adt7470_read_temperatures(data);
+ else
+ 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++) {
+ err = adt7470_read_word_data(data, ADT7470_REG_FAN(i), &val);
+ if (err < 0)
+ return err;
+ data->fan[i] = val;
+ }
+
+ err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &data->pwm[0], ADT7470_PWM_COUNT);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ADT7470_PWM_COUNT; i++) {
+ unsigned int mask;
+
+ if (i % 2)
+ mask = ADT7470_PWM2_AUTO_MASK;
+ else
+ mask = ADT7470_PWM1_AUTO_MASK;
+
+ err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(i), &val);
+ if (err < 0)
+ return err;
+ data->pwm_automatic[i] = !!(val & mask);
+
+ 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] = val >> 4;
+ else
+ data->pwm_auto_temp[i] = val & 0xF;
+ }
+
+ 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);
+ }
+
+ err = adt7470_read_word_data(data, ADT7470_REG_ALARM1_MASK, &val);
+ if (err < 0)
+ return err;
+ data->alarms_mask = val;
+
+ return 0;
+}
+
+static int adt7470_update_limits(struct adt7470_data *data)
+{
+ unsigned int val;
+ int err;
+ int i;
+
+ for (i = 0; i < ADT7470_TEMP_COUNT; 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++) {
+ 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++) {
+ 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;
+ }
+
+ 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 err < 0 ? ERR_PTR(err) : data;
+}
+
+static ssize_t auto_update_interval_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->auto_update_interval);
+}
+
+static ssize_t auto_update_interval_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ long temp;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ temp = clamp_val(temp, 0, 60000);
+
+ mutex_lock(&data->lock);
+ data->auto_update_interval = temp;
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t num_temp_sensors_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->num_temp_sensors);
+}
+
+static ssize_t num_temp_sensors_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ long temp;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ temp = clamp_val(temp, -1, 10);
+
+ mutex_lock(&data->lock);
+ data->num_temp_sensors = temp;
+ if (temp < 0)
+ data->temperatures_probed = 0;
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static int adt7470_temp_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_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;
+ }
+
+ return 0;
+}
+
+static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ int err;
+
+ val = clamp_val(val, -128000, 127000);
+ val = DIV_ROUND_CLOSEST(val, 1000);
+
+ 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;
+ }
+
+ return err;
+}
+
+static ssize_t alarm_mask_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, "%x\n", data->alarms_mask);
+}
+
+static ssize_t alarm_mask_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ long mask;
+ int err;
+
+ if (kstrtoul(buf, 0, &mask))
+ return -EINVAL;
+
+ if (mask & ~0xffff)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ data->alarms_mask = mask;
+ err = adt7470_write_word_data(data, ADT7470_REG_ALARM1_MASK, mask);
+ mutex_unlock(&data->lock);
+
+ return err < 0 ? err : count;
+}
+
+static int adt7470_fan_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_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;
+ }
+
+ return 0;
+}
+
+static int adt7470_fan_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ int err;
+
+ if (val <= 0)
+ return -EINVAL;
+
+ val = FAN_RPM_TO_PERIOD(val);
+ val = clamp_val(val, 1, 65534);
+
+ 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;
+ }
+
+ 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);
+}
+
+static ssize_t force_pwm_max_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ long temp;
+ int err;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ data->force_pwm_max = temp;
+ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+ ADT7470_FSPD_MASK,
+ temp ? ADT7470_FSPD_MASK : 0);
+ mutex_unlock(&data->lock);
+
+ return err < 0 ? err : count;
+}
+
+/* These are the valid PWM frequencies to the nearest Hz */
+static const int adt7470_freq_map[] = {
+ 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+};
+
+static int pwm1_freq_get(struct device *dev)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ unsigned int cfg_reg_1, cfg_reg_2;
+ int index;
+ int err;
+
+ mutex_lock(&data->lock);
+ 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;
+ if (!(cfg_reg_1 & ADT7470_CFG_LF))
+ index += 8;
+ if (index >= ARRAY_SIZE(adt7470_freq_map))
+ index = ARRAY_SIZE(adt7470_freq_map) - 1;
+
+ return adt7470_freq_map[index];
+
+out:
+ mutex_unlock(&data->lock);
+ return err;
+}
+
+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);
+ unsigned int low_freq = ADT7470_CFG_LF;
+ int index;
+ int err;
+
+ /* Round the user value given to the closest available frequency */
+ index = find_closest(freq, adt7470_freq_map,
+ ARRAY_SIZE(adt7470_freq_map));
+
+ if (index >= 8) {
+ index -= 8;
+ low_freq = 0;
+ }
+
+ mutex_lock(&data->lock);
+ /* Configuration Register 1 */
+ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
+ ADT7470_CFG_LF, low_freq);
+ if (err < 0)
+ goto out;
+
+ /* Configuration Register 2 */
+ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
+ ADT7470_FREQ_MASK,
+ index << ADT7470_FREQ_SHIFT);
+out:
+ mutex_unlock(&data->lock);
+
+ 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,
+ 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 (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->pwm_max[attr->index]);
+}
+
+static ssize_t pwm_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);
+ long temp;
+ int err;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ temp = clamp_val(temp, 0, 255);
+
+ mutex_lock(&data->lock);
+ data->pwm_max[attr->index] = temp;
+ err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return err < 0 ? err : count;
+}
+
+static ssize_t pwm_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);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->pwm_min[attr->index]);
+}
+
+static ssize_t pwm_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);
+ long temp;
+ int err;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ temp = clamp_val(temp, 0, 255);
+
+ mutex_lock(&data->lock);
+ data->pwm_min[attr->index] = temp;
+ err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return err < 0 ? err : count;
+}
+
+static ssize_t pwm_tmax_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 (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]));
+}
+
+static ssize_t pwm_tmin_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 (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]);
+}
+
+static ssize_t pwm_tmin_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);
+ long temp;
+ int err;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ temp = clamp_val(temp, -128000, 127000);
+ temp = DIV_ROUND_CLOSEST(temp, 1000);
+
+ mutex_lock(&data->lock);
+ data->pwm_tmin[attr->index] = temp;
+ err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index),
+ temp);
+ mutex_unlock(&data->lock);
+
+ return err < 0 ? err : count;
+}
+
+static ssize_t pwm_auto_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);
+ 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
+ return sprintf(buf, "%d\n", ADT7470_PWM_ALL_TEMPS);
+}
+
+static int cvt_auto_temp(int input)
+{
+ if (input == ADT7470_PWM_ALL_TEMPS)
+ return 0;
+ if (input < 1 || !is_power_of_2(input))
+ return -EINVAL;
+ return ilog2(input) + 1;
+}
+
+static ssize_t pwm_auto_temp_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);
+ int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index);
+ unsigned int mask, val;
+ long temp;
+ int err;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ temp = cvt_auto_temp(temp);
+ if (temp < 0)
+ return temp;
+
+ mutex_lock(&data->lock);
+ data->pwm_automatic[attr->index] = temp;
+
+ if (!(attr->index % 2)) {
+ mask = 0xF0;
+ val = (temp << 4) & 0xF0;
+ } else {
+ mask = 0x0F;
+ val = temp & 0x0F;
+ }
+
+ err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
+ mutex_unlock(&data->lock);
+
+ 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(force_pwm_max, force_pwm_max, 0);
+
+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);
+static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point1_pwm, pwm_min, 3);
+
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm_max, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point2_pwm, pwm_max, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point2_pwm, pwm_max, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point2_pwm, pwm_max, 3);
+
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_tmin, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_temp, pwm_tmin, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_temp, pwm_tmin, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point1_temp, pwm_tmin, 3);
+
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point2_temp, pwm_tmax, 0);
+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_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);
+static SENSOR_DEVICE_ATTR_RW(pwm4_auto_channels_temp, pwm_auto_temp, 3);
+
+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_force_pwm_max.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,
+ &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+ &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_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,
+ &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr,
+ NULL
+};
+
+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)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int vendor, device, revision;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
+ if (vendor != ADT7470_VENDOR)
+ return -ENODEV;
+
+ device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
+ if (device != ADT7470_DEVICE)
+ return -ENODEV;
+
+ revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION);
+ if (revision != ADT7470_REVISION)
+ return -ENODEV;
+
+ strscpy(info->type, "adt7470", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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)
+{
+ 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)
+ return -ENOMEM;
+
+ 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);
+ mutex_init(&data->lock);
+
+ dev_info(&client->dev, "%s chip found\n", client->name);
+
+ /* Initialize the ADT7470 chip */
+ 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_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))
+ return PTR_ERR(data->auto_update);
+
+ return 0;
+}
+
+static void adt7470_remove(struct i2c_client *client)
+{
+ struct adt7470_data *data = i2c_get_clientdata(client);
+
+ kthread_stop(data->auto_update);
+}
+
+static const struct i2c_device_id adt7470_id[] = {
+ { "adt7470", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adt7470_id);
+
+static struct i2c_driver adt7470_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adt7470",
+ },
+ .probe_new = adt7470_probe,
+ .remove = adt7470_remove,
+ .id_table = adt7470_id,
+ .detect = adt7470_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adt7470_driver);
+
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
+MODULE_DESCRIPTION("ADT7470 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
new file mode 100644
index 000000000..6a6ebcc89
--- /dev/null
+++ b/drivers/hwmon/adt7475.c
@@ -0,0 +1,2022 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * adt7475 - Thermal sensor driver for the ADT7475 chip and derivatives
+ * Copyright (C) 2007-2008, Advanced Micro Devices, Inc.
+ * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
+ * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2009 Jean Delvare <jdelvare@suse.de>
+ *
+ * Derived from the lm83 driver by Jean Delvare
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#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 */
+
+#define INPUT 0
+#define MIN 1
+#define MAX 2
+#define CONTROL 3
+#define OFFSET 3
+#define AUTOMIN 4
+#define THERM 5
+#define HYSTERSIS 6
+
+/*
+ * These are unique identifiers for the sysfs functions - unlike the
+ * numbers above, these are not also indexes into an array
+ */
+
+#define ALARM 9
+#define FAULT 10
+
+/* 7475 Common Registers */
+
+#define REG_DEVREV2 0x12 /* ADT7490 only */
+
+#define REG_VTT 0x1E /* ADT7490 only */
+#define REG_EXTEND3 0x1F /* ADT7490 only */
+
+#define REG_VOLTAGE_BASE 0x20
+#define REG_TEMP_BASE 0x25
+#define REG_TACH_BASE 0x28
+#define REG_PWM_BASE 0x30
+#define REG_PWM_MAX_BASE 0x38
+
+#define REG_DEVID 0x3D
+#define REG_VENDID 0x3E
+#define REG_DEVID2 0x3F
+
+#define REG_CONFIG1 0x40
+
+#define REG_STATUS1 0x41
+#define REG_STATUS2 0x42
+
+#define REG_VID 0x43 /* ADT7476 only */
+
+#define REG_VOLTAGE_MIN_BASE 0x44
+#define REG_VOLTAGE_MAX_BASE 0x45
+
+#define REG_TEMP_MIN_BASE 0x4E
+#define REG_TEMP_MAX_BASE 0x4F
+
+#define REG_TACH_MIN_BASE 0x54
+
+#define REG_PWM_CONFIG_BASE 0x5C
+
+#define REG_TEMP_TRANGE_BASE 0x5F
+
+#define REG_ENHANCE_ACOUSTICS1 0x62
+#define REG_ENHANCE_ACOUSTICS2 0x63
+
+#define REG_PWM_MIN_BASE 0x64
+
+#define REG_TEMP_TMIN_BASE 0x67
+#define REG_TEMP_THERM_BASE 0x6A
+
+#define REG_REMOTE1_HYSTERSIS 0x6D
+#define REG_REMOTE2_HYSTERSIS 0x6E
+
+#define REG_TEMP_OFFSET_BASE 0x70
+
+#define REG_CONFIG2 0x73
+
+#define REG_EXTEND1 0x76
+#define REG_EXTEND2 0x77
+
+#define REG_CONFIG3 0x78
+#define REG_CONFIG5 0x7C
+#define REG_CONFIG4 0x7D
+
+#define REG_STATUS4 0x81 /* ADT7490 only */
+
+#define REG_VTT_MIN 0x84 /* ADT7490 only */
+#define REG_VTT_MAX 0x86 /* ADT7490 only */
+
+#define VID_VIDSEL 0x80 /* ADT7476 only */
+
+#define CONFIG2_ATTN 0x20
+
+#define CONFIG3_SMBALERT 0x01
+#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
+
+#define CONFIG5_TWOSCOMP 0x01
+#define CONFIG5_TEMPOFFSET 0x02
+#define CONFIG5_VIDGPIO 0x10 /* ADT7476 only */
+
+/* ADT7475 Settings */
+
+#define ADT7475_VOLTAGE_COUNT 5 /* Not counting Vtt */
+#define ADT7475_TEMP_COUNT 3
+#define ADT7475_TACH_COUNT 4
+#define ADT7475_PWM_COUNT 3
+
+/* Macro to read the registers */
+
+#define adt7475_read(reg) i2c_smbus_read_byte_data(client, (reg))
+
+/* Macros to easily index the registers */
+
+#define TACH_REG(idx) (REG_TACH_BASE + ((idx) * 2))
+#define TACH_MIN_REG(idx) (REG_TACH_MIN_BASE + ((idx) * 2))
+
+#define PWM_REG(idx) (REG_PWM_BASE + (idx))
+#define PWM_MAX_REG(idx) (REG_PWM_MAX_BASE + (idx))
+#define PWM_MIN_REG(idx) (REG_PWM_MIN_BASE + (idx))
+#define PWM_CONFIG_REG(idx) (REG_PWM_CONFIG_BASE + (idx))
+
+#define VOLTAGE_REG(idx) (REG_VOLTAGE_BASE + (idx))
+#define VOLTAGE_MIN_REG(idx) (REG_VOLTAGE_MIN_BASE + ((idx) * 2))
+#define VOLTAGE_MAX_REG(idx) (REG_VOLTAGE_MAX_BASE + ((idx) * 2))
+
+#define TEMP_REG(idx) (REG_TEMP_BASE + (idx))
+#define TEMP_MIN_REG(idx) (REG_TEMP_MIN_BASE + ((idx) * 2))
+#define TEMP_MAX_REG(idx) (REG_TEMP_MAX_BASE + ((idx) * 2))
+#define TEMP_TMIN_REG(idx) (REG_TEMP_TMIN_BASE + (idx))
+#define TEMP_THERM_REG(idx) (REG_TEMP_THERM_BASE + (idx))
+#define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx))
+#define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx))
+
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+enum chips { adt7473, adt7475, adt7476, adt7490 };
+
+static const struct i2c_device_id adt7475_id[] = {
+ { "adt7473", adt7473 },
+ { "adt7475", adt7475 },
+ { "adt7476", adt7476 },
+ { "adt7490", adt7490 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adt7475_id);
+
+static const struct of_device_id __maybe_unused adt7475_of_match[] = {
+ {
+ .compatible = "adi,adt7473",
+ .data = (void *)adt7473
+ },
+ {
+ .compatible = "adi,adt7475",
+ .data = (void *)adt7475
+ },
+ {
+ .compatible = "adi,adt7476",
+ .data = (void *)adt7476
+ },
+ {
+ .compatible = "adi,adt7490",
+ .data = (void *)adt7490
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adt7475_of_match);
+
+struct adt7475_data {
+ struct i2c_client *client;
+ struct mutex lock;
+
+ unsigned long measure_updated;
+ bool valid;
+
+ u8 config2;
+ u8 config4;
+ u8 config5;
+ u8 has_voltage;
+ u8 bypass_attn; /* Bypass voltage attenuator */
+ u8 has_pwm2:1;
+ u8 has_fan4:1;
+ u8 has_vid:1;
+ u32 alarms;
+ u16 voltage[3][6];
+ u16 temp[7][3];
+ u16 tach[2][4];
+ u8 pwm[4][3];
+ u8 range[3];
+ u8 pwmctl[3];
+ u8 pwmchan[3];
+ u8 enh_acoustics[2];
+
+ u8 vid;
+ u8 vrm;
+ const struct attribute_group *groups[9];
+};
+
+static struct i2c_driver adt7475_driver;
+static struct adt7475_data *adt7475_update_device(struct device *dev);
+static void adt7475_read_hystersis(struct i2c_client *client);
+static void adt7475_read_pwm(struct i2c_client *client, int index);
+
+/* Given a temp value, convert it to register value */
+
+static inline u16 temp2reg(struct adt7475_data *data, long val)
+{
+ u16 ret;
+
+ if (!(data->config5 & CONFIG5_TWOSCOMP)) {
+ val = clamp_val(val, -64000, 191000);
+ ret = (val + 64500) / 1000;
+ } else {
+ val = clamp_val(val, -128000, 127000);
+ if (val < -500)
+ ret = (256500 + val) / 1000;
+ else
+ ret = (val + 500) / 1000;
+ }
+
+ return ret << 2;
+}
+
+/* Given a register value, convert it to a real temp value */
+
+static inline int reg2temp(struct adt7475_data *data, u16 reg)
+{
+ if (data->config5 & CONFIG5_TWOSCOMP) {
+ if (reg >= 512)
+ return (reg - 1024) * 250;
+ else
+ return reg * 250;
+ } else
+ return (reg - 256) * 250;
+}
+
+static inline int tach2rpm(u16 tach)
+{
+ if (tach == 0 || tach == 0xFFFF)
+ return 0;
+
+ return (90000 * 60) / tach;
+}
+
+static inline u16 rpm2tach(unsigned long rpm)
+{
+ if (rpm == 0)
+ return 0;
+
+ return clamp_val((90000 * 60) / rpm, 1, 0xFFFF);
+}
+
+/* Scaling factors for voltage inputs, taken from the ADT7490 datasheet */
+static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 1][2] = {
+ { 45, 94 }, /* +2.5V */
+ { 175, 525 }, /* Vccp */
+ { 68, 71 }, /* Vcc */
+ { 93, 47 }, /* +5V */
+ { 120, 20 }, /* +12V */
+ { 45, 45 }, /* Vtt */
+};
+
+static inline int reg2volt(int channel, u16 reg, u8 bypass_attn)
+{
+ const int *r = adt7473_in_scaling[channel];
+
+ if (bypass_attn & (1 << channel))
+ return DIV_ROUND_CLOSEST(reg * 2250, 1024);
+ return DIV_ROUND_CLOSEST(reg * (r[0] + r[1]) * 2250, r[1] * 1024);
+}
+
+static inline u16 volt2reg(int channel, long volt, u8 bypass_attn)
+{
+ const int *r = adt7473_in_scaling[channel];
+ long reg;
+
+ if (bypass_attn & (1 << channel))
+ reg = DIV_ROUND_CLOSEST(volt * 1024, 2250);
+ else
+ reg = DIV_ROUND_CLOSEST(volt * r[1] * 1024,
+ (r[0] + r[1]) * 2250);
+ return clamp_val(reg, 0, 1023) & (0xff << 2);
+}
+
+static int adt7475_read_word(struct i2c_client *client, int reg)
+{
+ int val1, val2;
+
+ val1 = i2c_smbus_read_byte_data(client, reg);
+ if (val1 < 0)
+ return val1;
+ val2 = i2c_smbus_read_byte_data(client, reg + 1);
+ if (val2 < 0)
+ return val2;
+
+ return val1 | (val2 << 8);
+}
+
+static void adt7475_write_word(struct i2c_client *client, int reg, u16 val)
+{
+ i2c_smbus_write_byte_data(client, reg + 1, val >> 8);
+ i2c_smbus_write_byte_data(client, reg, val & 0xFF);
+}
+
+static ssize_t voltage_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ unsigned short val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ switch (sattr->nr) {
+ case ALARM:
+ return sprintf(buf, "%d\n",
+ (data->alarms >> sattr->index) & 1);
+ default:
+ val = data->voltage[sattr->nr][sattr->index];
+ return sprintf(buf, "%d\n",
+ reg2volt(sattr->index, val, data->bypass_attn));
+ }
+}
+
+static ssize_t voltage_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned char reg;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ data->voltage[sattr->nr][sattr->index] =
+ volt2reg(sattr->index, val, data->bypass_attn);
+
+ if (sattr->index < ADT7475_VOLTAGE_COUNT) {
+ if (sattr->nr == MIN)
+ reg = VOLTAGE_MIN_REG(sattr->index);
+ else
+ reg = VOLTAGE_MAX_REG(sattr->index);
+ } else {
+ if (sattr->nr == MIN)
+ reg = REG_VTT_MIN;
+ else
+ reg = REG_VTT_MAX;
+ }
+
+ i2c_smbus_write_byte_data(client, reg,
+ data->voltage[sattr->nr][sattr->index] >> 2);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int out;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ switch (sattr->nr) {
+ case HYSTERSIS:
+ mutex_lock(&data->lock);
+ out = data->temp[sattr->nr][sattr->index];
+ if (sattr->index != 1)
+ out = (out >> 4) & 0xF;
+ else
+ out = (out & 0xF);
+ /*
+ * Show the value as an absolute number tied to
+ * THERM
+ */
+ out = reg2temp(data, data->temp[THERM][sattr->index]) -
+ out * 1000;
+ mutex_unlock(&data->lock);
+ break;
+
+ case OFFSET:
+ /*
+ * Offset is always 2's complement, regardless of the
+ * setting in CONFIG5
+ */
+ mutex_lock(&data->lock);
+ out = (s8)data->temp[sattr->nr][sattr->index];
+ if (data->config5 & CONFIG5_TEMPOFFSET)
+ out *= 1000;
+ else
+ out *= 500;
+ mutex_unlock(&data->lock);
+ break;
+
+ case ALARM:
+ out = (data->alarms >> (sattr->index + 4)) & 1;
+ break;
+
+ case FAULT:
+ /* Note - only for remote1 and remote2 */
+ out = !!(data->alarms & (sattr->index ? 0x8000 : 0x4000));
+ break;
+
+ default:
+ /* All other temp values are in the configured format */
+ out = reg2temp(data, data->temp[sattr->nr][sattr->index]);
+ }
+
+ return sprintf(buf, "%d\n", out);
+}
+
+static ssize_t temp_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned char reg = 0;
+ u8 out;
+ int temp;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ /* We need the config register in all cases for temp <-> reg conv. */
+ data->config5 = adt7475_read(REG_CONFIG5);
+
+ switch (sattr->nr) {
+ case OFFSET:
+ if (data->config5 & CONFIG5_TEMPOFFSET) {
+ val = clamp_val(val, -63000, 127000);
+ out = data->temp[OFFSET][sattr->index] = val / 1000;
+ } else {
+ val = clamp_val(val, -63000, 64000);
+ out = data->temp[OFFSET][sattr->index] = val / 500;
+ }
+ break;
+
+ case HYSTERSIS:
+ /*
+ * The value will be given as an absolute value, turn it
+ * into an offset based on THERM
+ */
+
+ /* Read fresh THERM and HYSTERSIS values from the chip */
+ data->temp[THERM][sattr->index] =
+ adt7475_read(TEMP_THERM_REG(sattr->index)) << 2;
+ adt7475_read_hystersis(client);
+
+ temp = reg2temp(data, data->temp[THERM][sattr->index]);
+ val = clamp_val(val, temp - 15000, temp);
+ val = (temp - val) / 1000;
+
+ if (sattr->index != 1) {
+ data->temp[HYSTERSIS][sattr->index] &= 0x0F;
+ data->temp[HYSTERSIS][sattr->index] |= (val & 0xF) << 4;
+ } else {
+ data->temp[HYSTERSIS][sattr->index] &= 0xF0;
+ data->temp[HYSTERSIS][sattr->index] |= (val & 0xF);
+ }
+
+ out = data->temp[HYSTERSIS][sattr->index];
+ break;
+
+ default:
+ data->temp[sattr->nr][sattr->index] = temp2reg(data, val);
+
+ /*
+ * We maintain an extra 2 digits of precision for simplicity
+ * - shift those back off before writing the value
+ */
+ out = (u8) (data->temp[sattr->nr][sattr->index] >> 2);
+ }
+
+ switch (sattr->nr) {
+ case MIN:
+ reg = TEMP_MIN_REG(sattr->index);
+ break;
+ case MAX:
+ reg = TEMP_MAX_REG(sattr->index);
+ break;
+ case OFFSET:
+ reg = TEMP_OFFSET_REG(sattr->index);
+ break;
+ case AUTOMIN:
+ reg = TEMP_TMIN_REG(sattr->index);
+ break;
+ case THERM:
+ reg = TEMP_THERM_REG(sattr->index);
+ break;
+ case HYSTERSIS:
+ if (sattr->index != 2)
+ reg = REG_REMOTE1_HYSTERSIS;
+ else
+ reg = REG_REMOTE2_HYSTERSIS;
+
+ break;
+ }
+
+ i2c_smbus_write_byte_data(client, reg, out);
+
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+/* Assuming CONFIG6[SLOW] is 0 */
+static const int ad7475_st_map[] = {
+ 37500, 18800, 12500, 7500, 4700, 3100, 1600, 800,
+};
+
+static ssize_t temp_st_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ long val;
+
+ switch (sattr->index) {
+ case 0:
+ val = data->enh_acoustics[0] & 0xf;
+ break;
+ case 1:
+ val = data->enh_acoustics[1] & 0xf;
+ break;
+ case 2:
+ default:
+ val = (data->enh_acoustics[1] >> 4) & 0xf;
+ break;
+ }
+
+ if (val & 0x8)
+ return sprintf(buf, "%d\n", ad7475_st_map[val & 0x7]);
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t temp_st_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned char reg;
+ int shift, idx;
+ ulong val;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ switch (sattr->index) {
+ case 0:
+ reg = REG_ENHANCE_ACOUSTICS1;
+ shift = 0;
+ idx = 0;
+ break;
+ case 1:
+ reg = REG_ENHANCE_ACOUSTICS2;
+ shift = 0;
+ idx = 1;
+ break;
+ case 2:
+ default:
+ reg = REG_ENHANCE_ACOUSTICS2;
+ shift = 4;
+ idx = 1;
+ break;
+ }
+
+ if (val > 0) {
+ val = find_closest_descending(val, ad7475_st_map,
+ ARRAY_SIZE(ad7475_st_map));
+ val |= 0x8;
+ }
+
+ mutex_lock(&data->lock);
+
+ data->enh_acoustics[idx] &= ~(0xf << shift);
+ data->enh_acoustics[idx] |= (val << shift);
+
+ i2c_smbus_write_byte_data(client, reg, data->enh_acoustics[idx]);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/*
+ * Table of autorange values - the user will write the value in millidegrees,
+ * and we'll convert it
+ */
+static const int autorange_table[] = {
+ 2000, 2500, 3330, 4000, 5000, 6670, 8000,
+ 10000, 13330, 16000, 20000, 26670, 32000, 40000,
+ 53330, 80000
+};
+
+static ssize_t point2_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int out, val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->lock);
+ out = (data->range[sattr->index] >> 4) & 0x0F;
+ val = reg2temp(data, data->temp[AUTOMIN][sattr->index]);
+ mutex_unlock(&data->lock);
+
+ return sprintf(buf, "%d\n", val + autorange_table[out]);
+}
+
+static ssize_t point2_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int temp;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ /* Get a fresh copy of the needed registers */
+ data->config5 = adt7475_read(REG_CONFIG5);
+ data->temp[AUTOMIN][sattr->index] =
+ adt7475_read(TEMP_TMIN_REG(sattr->index)) << 2;
+ data->range[sattr->index] =
+ adt7475_read(TEMP_TRANGE_REG(sattr->index));
+
+ /*
+ * The user will write an absolute value, so subtract the start point
+ * to figure the range
+ */
+ temp = reg2temp(data, data->temp[AUTOMIN][sattr->index]);
+ val = clamp_val(val, temp + autorange_table[0],
+ temp + autorange_table[ARRAY_SIZE(autorange_table) - 1]);
+ val -= temp;
+
+ /* Find the nearest table entry to what the user wrote */
+ val = find_closest(val, autorange_table, ARRAY_SIZE(autorange_table));
+
+ data->range[sattr->index] &= ~0xF0;
+ data->range[sattr->index] |= val << 4;
+
+ i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(sattr->index),
+ data->range[sattr->index]);
+
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+static ssize_t tach_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int out;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (sattr->nr == ALARM)
+ out = (data->alarms >> (sattr->index + 10)) & 1;
+ else
+ out = tach2rpm(data->tach[sattr->nr][sattr->index]);
+
+ return sprintf(buf, "%d\n", out);
+}
+
+static ssize_t tach_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ data->tach[MIN][sattr->index] = rpm2tach(val);
+
+ adt7475_write_word(client, TACH_MIN_REG(sattr->index),
+ data->tach[MIN][sattr->index]);
+
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_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->pwm[sattr->nr][sattr->index]);
+}
+
+static ssize_t pwmchan_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_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->pwmchan[sattr->index]);
+}
+
+static ssize_t pwmctrl_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_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->pwmctl[sattr->index]);
+}
+
+static ssize_t pwm_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned char reg = 0;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ switch (sattr->nr) {
+ case INPUT:
+ /* Get a fresh value for CONTROL */
+ data->pwm[CONTROL][sattr->index] =
+ adt7475_read(PWM_CONFIG_REG(sattr->index));
+
+ /*
+ * If we are not in manual mode, then we shouldn't allow
+ * the user to set the pwm speed
+ */
+ if (((data->pwm[CONTROL][sattr->index] >> 5) & 7) != 7) {
+ mutex_unlock(&data->lock);
+ return count;
+ }
+
+ reg = PWM_REG(sattr->index);
+ break;
+
+ case MIN:
+ reg = PWM_MIN_REG(sattr->index);
+ break;
+
+ case MAX:
+ reg = PWM_MAX_REG(sattr->index);
+ break;
+ }
+
+ data->pwm[sattr->nr][sattr->index] = clamp_val(val, 0, 0xFF);
+ i2c_smbus_write_byte_data(client, reg,
+ data->pwm[sattr->nr][sattr->index]);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t stall_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct adt7475_data *data = dev_get_drvdata(dev);
+
+ u8 mask = BIT(5 + sattr->index);
+
+ return sprintf(buf, "%d\n", !!(data->enh_acoustics[0] & mask));
+}
+
+static ssize_t stall_disable_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ u8 mask = BIT(5 + sattr->index);
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ data->enh_acoustics[0] &= ~mask;
+ if (val)
+ data->enh_acoustics[0] |= mask;
+
+ i2c_smbus_write_byte_data(client, REG_ENHANCE_ACOUSTICS1,
+ data->enh_acoustics[0]);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/* Called by set_pwmctrl and set_pwmchan */
+
+static int hw_set_pwm(struct i2c_client *client, int index,
+ unsigned int pwmctl, unsigned int pwmchan)
+{
+ struct adt7475_data *data = i2c_get_clientdata(client);
+ long val = 0;
+
+ switch (pwmctl) {
+ case 0:
+ val = 0x03; /* Run at full speed */
+ break;
+ case 1:
+ val = 0x07; /* Manual mode */
+ break;
+ case 2:
+ switch (pwmchan) {
+ case 1:
+ /* Remote1 controls PWM */
+ val = 0x00;
+ break;
+ case 2:
+ /* local controls PWM */
+ val = 0x01;
+ break;
+ case 4:
+ /* remote2 controls PWM */
+ val = 0x02;
+ break;
+ case 6:
+ /* local/remote2 control PWM */
+ val = 0x05;
+ break;
+ case 7:
+ /* All three control PWM */
+ val = 0x06;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data->pwmctl[index] = pwmctl;
+ data->pwmchan[index] = pwmchan;
+
+ data->pwm[CONTROL][index] &= ~0xE0;
+ data->pwm[CONTROL][index] |= (val & 7) << 5;
+
+ i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index),
+ data->pwm[CONTROL][index]);
+
+ return 0;
+}
+
+static ssize_t pwmchan_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int r;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ /* Read Modify Write PWM values */
+ adt7475_read_pwm(client, sattr->index);
+ r = hw_set_pwm(client, sattr->index, data->pwmctl[sattr->index], val);
+ if (r)
+ count = r;
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t pwmctrl_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int r;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ /* Read Modify Write PWM values */
+ adt7475_read_pwm(client, sattr->index);
+ r = hw_set_pwm(client, sattr->index, val, data->pwmchan[sattr->index]);
+ if (r)
+ count = r;
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/* List of frequencies for the PWM */
+static const int pwmfreq_table[] = {
+ 11, 14, 22, 29, 35, 44, 58, 88, 22500
+};
+
+static ssize_t pwmfreq_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int idx;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ idx = clamp_val(data->range[sattr->index] & 0xf, 0,
+ ARRAY_SIZE(pwmfreq_table) - 1);
+
+ return sprintf(buf, "%d\n", pwmfreq_table[idx]);
+}
+
+static ssize_t pwmfreq_store(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 adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int out;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ out = find_closest(val, pwmfreq_table, ARRAY_SIZE(pwmfreq_table));
+
+ mutex_lock(&data->lock);
+
+ data->range[sattr->index] =
+ adt7475_read(TEMP_TRANGE_REG(sattr->index));
+ data->range[sattr->index] &= ~0xf;
+ data->range[sattr->index] |= out;
+
+ i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(sattr->index),
+ data->range[sattr->index]);
+
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+static ssize_t pwm_use_point2_pwm_at_crit_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", !!(data->config4 & CONFIG4_MAXDUTY));
+}
+
+static ssize_t pwm_use_point2_pwm_at_crit_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ data->config4 = i2c_smbus_read_byte_data(client, REG_CONFIG4);
+ if (val)
+ data->config4 |= CONFIG4_MAXDUTY;
+ else
+ data->config4 &= ~CONFIG4_MAXDUTY;
+ i2c_smbus_write_byte_data(client, REG_CONFIG4, data->config4);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t vrm_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", (int)data->vrm);
+}
+
+static ssize_t vrm_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ long val;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ data->vrm = val;
+
+ return count;
+}
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static SENSOR_DEVICE_ATTR_2_RO(in0_input, voltage, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in0_max, voltage, MAX, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in0_min, voltage, MIN, 0);
+static SENSOR_DEVICE_ATTR_2_RO(in0_alarm, voltage, ALARM, 0);
+static SENSOR_DEVICE_ATTR_2_RO(in1_input, voltage, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in1_max, voltage, MAX, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in1_min, voltage, MIN, 1);
+static SENSOR_DEVICE_ATTR_2_RO(in1_alarm, voltage, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2_RO(in2_input, voltage, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in2_max, voltage, MAX, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in2_min, voltage, MIN, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in2_alarm, voltage, ALARM, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in3_input, voltage, INPUT, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in3_max, voltage, MAX, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in3_min, voltage, MIN, 3);
+static SENSOR_DEVICE_ATTR_2_RO(in3_alarm, voltage, ALARM, 3);
+static SENSOR_DEVICE_ATTR_2_RO(in4_input, voltage, INPUT, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in4_max, voltage, MAX, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in4_min, voltage, MIN, 4);
+static SENSOR_DEVICE_ATTR_2_RO(in4_alarm, voltage, ALARM, 8);
+static SENSOR_DEVICE_ATTR_2_RO(in5_input, voltage, INPUT, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in5_max, voltage, MAX, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in5_min, voltage, MIN, 5);
+static SENSOR_DEVICE_ATTR_2_RO(in5_alarm, voltage, ALARM, 31);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_alarm, temp, ALARM, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_fault, temp, FAULT, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, MAX, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, MIN, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_offset, temp, OFFSET, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point1_temp, temp, AUTOMIN, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, point2, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_crit, temp, THERM, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_crit_hyst, temp, HYSTERSIS, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_smoothing, temp_st, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_alarm, temp, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, MAX, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_min, temp, MIN, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_offset, temp, OFFSET, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point1_temp, temp, AUTOMIN, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point2_temp, point2, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_crit, temp, THERM, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_crit_hyst, temp, HYSTERSIS, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_smoothing, temp_st, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_alarm, temp, ALARM, 2);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_fault, temp, FAULT, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, MAX, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_min, temp, MIN, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_offset, temp, OFFSET, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_point1_temp, temp, AUTOMIN, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_point2_temp, point2, 0, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_crit, temp, THERM, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_crit_hyst, temp, HYSTERSIS, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_smoothing, temp_st, 0, 2);
+static SENSOR_DEVICE_ATTR_2_RO(fan1_input, tach, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2_RW(fan1_min, tach, MIN, 0);
+static SENSOR_DEVICE_ATTR_2_RO(fan1_alarm, tach, ALARM, 0);
+static SENSOR_DEVICE_ATTR_2_RO(fan2_input, tach, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2_RW(fan2_min, tach, MIN, 1);
+static SENSOR_DEVICE_ATTR_2_RO(fan2_alarm, tach, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2_RO(fan3_input, tach, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2_RW(fan3_min, tach, MIN, 2);
+static SENSOR_DEVICE_ATTR_2_RO(fan3_alarm, tach, ALARM, 2);
+static SENSOR_DEVICE_ATTR_2_RO(fan4_input, tach, INPUT, 3);
+static SENSOR_DEVICE_ATTR_2_RW(fan4_min, tach, MIN, 3);
+static SENSOR_DEVICE_ATTR_2_RO(fan4_alarm, tach, ALARM, 3);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1, pwm, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_freq, pwmfreq, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_enable, pwmctrl, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_channels_temp, pwmchan, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_pwm, pwm, MIN, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_pwm, pwm, MAX, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_stall_disable, stall_disable, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2, pwm, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_freq, pwmfreq, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_enable, pwmctrl, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_channels_temp, pwmchan, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_pwm, pwm, MIN, 1);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_pwm, pwm, MAX, 1);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_stall_disable, stall_disable, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3, pwm, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_freq, pwmfreq, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_enable, pwmctrl, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_channels_temp, pwmchan, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point1_pwm, pwm, MIN, 2);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point2_pwm, pwm, MAX, 2);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_stall_disable, stall_disable, 0, 2);
+
+/* Non-standard name, might need revisiting */
+static DEVICE_ATTR_RW(pwm_use_point2_pwm_at_crit);
+
+static DEVICE_ATTR_RW(vrm);
+static DEVICE_ATTR_RO(cpu0_vid);
+
+static struct attribute *adt7475_attrs[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_smoothing.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_smoothing.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_smoothing.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.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_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_stall_disable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_stall_disable.dev_attr.attr,
+ &dev_attr_pwm_use_point2_pwm_at_crit.attr,
+ NULL,
+};
+
+static struct attribute *fan4_attrs[] = {
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *pwm2_attrs[] = {
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_stall_disable.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in0_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in3_attrs[] = {
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in4_attrs[] = {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in5_attrs[] = {
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *vid_attrs[] = {
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
+static const struct attribute_group fan4_attr_group = { .attrs = fan4_attrs };
+static const struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs };
+static const struct attribute_group in0_attr_group = { .attrs = in0_attrs };
+static const struct attribute_group in3_attr_group = { .attrs = in3_attrs };
+static const struct attribute_group in4_attr_group = { .attrs = in4_attrs };
+static const struct attribute_group in5_attr_group = { .attrs = in5_attrs };
+static const struct attribute_group vid_attr_group = { .attrs = vid_attrs };
+
+static int adt7475_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int vendid, devid, devid2;
+ const char *name;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ vendid = adt7475_read(REG_VENDID);
+ devid2 = adt7475_read(REG_DEVID2);
+ if (vendid != 0x41 || /* Analog Devices */
+ (devid2 & 0xf8) != 0x68)
+ return -ENODEV;
+
+ devid = adt7475_read(REG_DEVID);
+ if (devid == 0x73)
+ name = "adt7473";
+ else if (devid == 0x75 && client->addr == 0x2e)
+ name = "adt7475";
+ else if (devid == 0x76)
+ name = "adt7476";
+ else if ((devid2 & 0xfc) == 0x6c)
+ name = "adt7490";
+ else {
+ dev_dbg(&adapter->dev,
+ "Couldn't detect an ADT7473/75/76/90 part at "
+ "0x%02x\n", (unsigned int)client->addr);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int adt7475_update_limits(struct i2c_client *client)
+{
+ struct adt7475_data *data = i2c_get_clientdata(client);
+ int i;
+ int ret;
+
+ ret = adt7475_read(REG_CONFIG4);
+ if (ret < 0)
+ return ret;
+ data->config4 = ret;
+
+ ret = adt7475_read(REG_CONFIG5);
+ if (ret < 0)
+ return ret;
+ data->config5 = ret;
+
+ for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
+ if (!(data->has_voltage & (1 << i)))
+ continue;
+ /* Adjust values so they match the input precision */
+ ret = adt7475_read(VOLTAGE_MIN_REG(i));
+ if (ret < 0)
+ return ret;
+ data->voltage[MIN][i] = ret << 2;
+
+ ret = adt7475_read(VOLTAGE_MAX_REG(i));
+ if (ret < 0)
+ return ret;
+ data->voltage[MAX][i] = ret << 2;
+ }
+
+ if (data->has_voltage & (1 << 5)) {
+ ret = adt7475_read(REG_VTT_MIN);
+ if (ret < 0)
+ return ret;
+ data->voltage[MIN][5] = ret << 2;
+
+ ret = adt7475_read(REG_VTT_MAX);
+ if (ret < 0)
+ return ret;
+ data->voltage[MAX][5] = ret << 2;
+ }
+
+ for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
+ /* Adjust values so they match the input precision */
+ ret = adt7475_read(TEMP_MIN_REG(i));
+ if (ret < 0)
+ return ret;
+ data->temp[MIN][i] = ret << 2;
+
+ ret = adt7475_read(TEMP_MAX_REG(i));
+ if (ret < 0)
+ return ret;
+ data->temp[MAX][i] = ret << 2;
+
+ ret = adt7475_read(TEMP_TMIN_REG(i));
+ if (ret < 0)
+ return ret;
+ data->temp[AUTOMIN][i] = ret << 2;
+
+ ret = adt7475_read(TEMP_THERM_REG(i));
+ if (ret < 0)
+ return ret;
+ data->temp[THERM][i] = ret << 2;
+
+ ret = adt7475_read(TEMP_OFFSET_REG(i));
+ if (ret < 0)
+ return ret;
+ data->temp[OFFSET][i] = ret;
+ }
+ adt7475_read_hystersis(client);
+
+ for (i = 0; i < ADT7475_TACH_COUNT; i++) {
+ if (i == 3 && !data->has_fan4)
+ continue;
+ ret = adt7475_read_word(client, TACH_MIN_REG(i));
+ if (ret < 0)
+ return ret;
+ data->tach[MIN][i] = ret;
+ }
+
+ for (i = 0; i < ADT7475_PWM_COUNT; i++) {
+ if (i == 1 && !data->has_pwm2)
+ continue;
+ ret = adt7475_read(PWM_MAX_REG(i));
+ if (ret < 0)
+ return ret;
+ data->pwm[MAX][i] = ret;
+
+ ret = adt7475_read(PWM_MIN_REG(i));
+ if (ret < 0)
+ return ret;
+ data->pwm[MIN][i] = ret;
+ /* Set the channel and control information */
+ adt7475_read_pwm(client, i);
+ }
+
+ ret = adt7475_read(TEMP_TRANGE_REG(0));
+ if (ret < 0)
+ return ret;
+ data->range[0] = ret;
+
+ ret = adt7475_read(TEMP_TRANGE_REG(1));
+ if (ret < 0)
+ return ret;
+ data->range[1] = ret;
+
+ ret = adt7475_read(TEMP_TRANGE_REG(2));
+ if (ret < 0)
+ return ret;
+ data->range[2] = ret;
+
+ return 0;
+}
+
+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 = device_property_read_u32_array(&client->dev,
+ "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[] = {
+ [adt7473] = "ADT7473",
+ [adt7475] = "ADT7475",
+ [adt7476] = "ADT7476",
+ [adt7490] = "ADT7490",
+ };
+
+ struct adt7475_data *data;
+ struct device *hwmon_dev;
+ int i, ret = 0, revision, group_num = 0;
+ 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)
+ return -ENOMEM;
+
+ mutex_init(&data->lock);
+ data->client = client;
+ i2c_set_clientdata(client, data);
+
+ if (client->dev.of_node)
+ chip = (enum chips)of_device_get_match_data(&client->dev);
+ else
+ chip = id->driver_data;
+
+ /* Initialize device-specific values */
+ switch (chip) {
+ case adt7476:
+ data->has_voltage = 0x0e; /* in1 to in3 */
+ revision = adt7475_read(REG_DEVID2) & 0x07;
+ break;
+ case adt7490:
+ data->has_voltage = 0x3e; /* in1 to in5 */
+ revision = adt7475_read(REG_DEVID2) & 0x03;
+ if (revision == 0x03)
+ revision += adt7475_read(REG_DEVREV2);
+ break;
+ default:
+ data->has_voltage = 0x06; /* in1, in2 */
+ 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))
+ data->has_pwm2 = 1;
+ /* Meaning of this bit is inverted for the ADT7473-1 */
+ if (id->driver_data == adt7473 && revision >= 1)
+ data->has_pwm2 = !data->has_pwm2;
+
+ data->config4 = adt7475_read(REG_CONFIG4);
+ /* Pin TACH4 may alternatively be used for THERM */
+ if ((data->config4 & CONFIG4_PINFUNC) == 0x0)
+ data->has_fan4 = 1;
+
+ /*
+ * THERM configuration is more complex on the ADT7476 and ADT7490,
+ * because 2 different pins (TACH4 and +2.5 Vin) can be used for
+ * this function
+ */
+ if (id->driver_data == adt7490) {
+ if ((data->config4 & CONFIG4_PINFUNC) == 0x1 &&
+ !(config3 & CONFIG3_THERM))
+ data->has_fan4 = 1;
+ }
+ if (id->driver_data == adt7476 || id->driver_data == adt7490) {
+ if (!(config3 & CONFIG3_THERM) ||
+ (data->config4 & CONFIG4_PINFUNC) == 0x1)
+ data->has_voltage |= (1 << 0); /* in0 */
+ }
+
+ /*
+ * On the ADT7476, the +12V input pin may instead be used as VID5,
+ * and VID pins may alternatively be used as GPIO
+ */
+ if (id->driver_data == adt7476) {
+ u8 vid = adt7475_read(REG_VID);
+ if (!(vid & VID_VIDSEL))
+ data->has_voltage |= (1 << 4); /* in4 */
+
+ data->has_vid = !(adt7475_read(REG_CONFIG5) & CONFIG5_VIDGPIO);
+ }
+
+ /* Voltage attenuators can be bypassed, globally or individually */
+ 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) |
+ ((data->config4 & CONFIG4_ATTN_IN43) >> 3);
+ }
+ data->bypass_attn &= data->has_voltage;
+
+ /*
+ * Call adt7475_read_pwm for all pwm's as this will reprogram any
+ * pwm's which are disabled to manual mode with 0% duty cycle
+ */
+ 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:
+ case adt7476:
+ i2c_smbus_write_byte_data(client, REG_CONFIG1,
+ adt7475_read(REG_CONFIG1) | 0x01);
+ break;
+ default:
+ break;
+ }
+
+ data->groups[group_num++] = &adt7475_attr_group;
+
+ /* Features that can be disabled individually */
+ if (data->has_fan4) {
+ data->groups[group_num++] = &fan4_attr_group;
+ }
+ if (data->has_pwm2) {
+ data->groups[group_num++] = &pwm2_attr_group;
+ }
+ if (data->has_voltage & (1 << 0)) {
+ data->groups[group_num++] = &in0_attr_group;
+ }
+ if (data->has_voltage & (1 << 3)) {
+ data->groups[group_num++] = &in3_attr_group;
+ }
+ if (data->has_voltage & (1 << 4)) {
+ data->groups[group_num++] = &in4_attr_group;
+ }
+ if (data->has_voltage & (1 << 5)) {
+ data->groups[group_num++] = &in5_attr_group;
+ }
+ if (data->has_vid) {
+ data->vrm = vid_which_vrm();
+ data->groups[group_num] = &vid_attr_group;
+ }
+
+ /* register device with all the acquired attributes */
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ data->groups);
+
+ if (IS_ERR(hwmon_dev)) {
+ ret = PTR_ERR(hwmon_dev);
+ return ret;
+ }
+
+ dev_info(&client->dev, "%s device, revision %d\n",
+ names[id->driver_data], revision);
+ if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2)
+ dev_info(&client->dev, "Optional features:%s%s%s%s%s\n",
+ (data->has_voltage & (1 << 0)) ? " in0" : "",
+ (data->has_voltage & (1 << 4)) ? " in4" : "",
+ data->has_fan4 ? " fan4" : "",
+ data->has_pwm2 ? " pwm2" : "",
+ data->has_vid ? " vid" : "");
+ if (data->bypass_attn)
+ dev_info(&client->dev, "Bypassing attenuators on:%s%s%s%s\n",
+ (data->bypass_attn & (1 << 0)) ? " in0" : "",
+ (data->bypass_attn & (1 << 1)) ? " in1" : "",
+ (data->bypass_attn & (1 << 3)) ? " in3" : "",
+ (data->bypass_attn & (1 << 4)) ? " in4" : "");
+
+ /* Limits and settings, should never change update more than once */
+ ret = adt7475_update_limits(client);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct i2c_driver adt7475_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adt7475",
+ .of_match_table = of_match_ptr(adt7475_of_match),
+ },
+ .probe_new = adt7475_probe,
+ .id_table = adt7475_id,
+ .detect = adt7475_detect,
+ .address_list = normal_i2c,
+};
+
+static void adt7475_read_hystersis(struct i2c_client *client)
+{
+ struct adt7475_data *data = i2c_get_clientdata(client);
+
+ data->temp[HYSTERSIS][0] = (u16) adt7475_read(REG_REMOTE1_HYSTERSIS);
+ data->temp[HYSTERSIS][1] = data->temp[HYSTERSIS][0];
+ data->temp[HYSTERSIS][2] = (u16) adt7475_read(REG_REMOTE2_HYSTERSIS);
+}
+
+static void adt7475_read_pwm(struct i2c_client *client, int index)
+{
+ struct adt7475_data *data = i2c_get_clientdata(client);
+ unsigned int v;
+
+ data->pwm[CONTROL][index] = adt7475_read(PWM_CONFIG_REG(index));
+
+ /*
+ * Figure out the internal value for pwmctrl and pwmchan
+ * based on the current settings
+ */
+ v = (data->pwm[CONTROL][index] >> 5) & 7;
+
+ if (v == 3)
+ data->pwmctl[index] = 0;
+ else if (v == 7)
+ data->pwmctl[index] = 1;
+ else if (v == 4) {
+ /*
+ * The fan is disabled - we don't want to
+ * support that, so change to manual mode and
+ * set the duty cycle to 0 instead
+ */
+ data->pwm[INPUT][index] = 0;
+ data->pwm[CONTROL][index] &= ~0xE0;
+ data->pwm[CONTROL][index] |= (7 << 5);
+
+ i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index),
+ data->pwm[INPUT][index]);
+
+ i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index),
+ data->pwm[CONTROL][index]);
+
+ data->pwmctl[index] = 1;
+ } else {
+ data->pwmctl[index] = 2;
+
+ switch (v) {
+ case 0:
+ data->pwmchan[index] = 1;
+ break;
+ case 1:
+ data->pwmchan[index] = 2;
+ break;
+ case 2:
+ data->pwmchan[index] = 4;
+ break;
+ case 5:
+ data->pwmchan[index] = 6;
+ break;
+ case 6:
+ data->pwmchan[index] = 7;
+ break;
+ }
+ }
+}
+
+static int adt7475_update_measure(struct device *dev)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u16 ext;
+ int i;
+ int ret;
+
+ ret = adt7475_read(REG_STATUS2);
+ if (ret < 0)
+ return ret;
+ data->alarms = ret << 8;
+
+ ret = adt7475_read(REG_STATUS1);
+ if (ret < 0)
+ return ret;
+ data->alarms |= ret;
+
+ ret = adt7475_read(REG_EXTEND2);
+ if (ret < 0)
+ return ret;
+
+ ext = (ret << 8);
+
+ ret = adt7475_read(REG_EXTEND1);
+ if (ret < 0)
+ return ret;
+
+ ext |= ret;
+
+ for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
+ if (!(data->has_voltage & (1 << i)))
+ continue;
+ ret = adt7475_read(VOLTAGE_REG(i));
+ if (ret < 0)
+ return ret;
+ data->voltage[INPUT][i] =
+ (ret << 2) |
+ ((ext >> (i * 2)) & 3);
+ }
+
+ for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
+ ret = adt7475_read(TEMP_REG(i));
+ if (ret < 0)
+ return ret;
+ data->temp[INPUT][i] =
+ (ret << 2) |
+ ((ext >> ((i + 5) * 2)) & 3);
+ }
+
+ if (data->has_voltage & (1 << 5)) {
+ ret = adt7475_read(REG_STATUS4);
+ if (ret < 0)
+ return ret;
+ data->alarms |= ret << 24;
+
+ ret = adt7475_read(REG_EXTEND3);
+ if (ret < 0)
+ return ret;
+ ext = ret;
+
+ ret = adt7475_read(REG_VTT);
+ if (ret < 0)
+ return ret;
+ data->voltage[INPUT][5] = ret << 2 |
+ ((ext >> 4) & 3);
+ }
+
+ for (i = 0; i < ADT7475_TACH_COUNT; i++) {
+ if (i == 3 && !data->has_fan4)
+ continue;
+ ret = adt7475_read_word(client, TACH_REG(i));
+ if (ret < 0)
+ return ret;
+ data->tach[INPUT][i] = ret;
+ }
+
+ /* Updated by hw when in auto mode */
+ for (i = 0; i < ADT7475_PWM_COUNT; i++) {
+ if (i == 1 && !data->has_pwm2)
+ continue;
+ ret = adt7475_read(PWM_REG(i));
+ if (ret < 0)
+ return ret;
+ data->pwm[INPUT][i] = ret;
+ }
+
+ if (data->has_vid) {
+ ret = adt7475_read(REG_VID);
+ if (ret < 0)
+ return ret;
+ data->vid = ret & 0x3f;
+ }
+
+ return 0;
+}
+
+static struct adt7475_data *adt7475_update_device(struct device *dev)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ /* Measurement values update every 2 seconds */
+ if (time_after(jiffies, data->measure_updated + HZ * 2) ||
+ !data->valid) {
+ ret = adt7475_update_measure(dev);
+ if (ret) {
+ data->valid = false;
+ mutex_unlock(&data->lock);
+ return ERR_PTR(ret);
+ }
+ data->measure_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return data;
+}
+
+module_i2c_driver(adt7475_driver);
+
+MODULE_AUTHOR("Advanced Micro Devices, Inc");
+MODULE_DESCRIPTION("adt7475 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
new file mode 100644
index 000000000..da67734ed
--- /dev/null
+++ b/drivers/hwmon/adt7x10.c
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * adt7x10.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * This driver handles the ADT7410 and compatible digital temperature sensors.
+ * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
+ * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
+ * 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/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+
+#include "adt7x10.h"
+
+/*
+ * ADT7X10 status
+ */
+#define ADT7X10_STAT_T_LOW (1 << 4)
+#define ADT7X10_STAT_T_HIGH (1 << 5)
+#define ADT7X10_STAT_T_CRIT (1 << 6)
+#define ADT7X10_STAT_NOT_RDY (1 << 7)
+
+/*
+ * ADT7X10 config
+ */
+#define ADT7X10_FAULT_QUEUE_MASK (1 << 0 | 1 << 1)
+#define ADT7X10_CT_POLARITY (1 << 2)
+#define ADT7X10_INT_POLARITY (1 << 3)
+#define ADT7X10_EVENT_MODE (1 << 4)
+#define ADT7X10_MODE_MASK (1 << 5 | 1 << 6)
+#define ADT7X10_FULL (0 << 5 | 0 << 6)
+#define ADT7X10_PD (1 << 5 | 1 << 6)
+#define ADT7X10_RESOLUTION (1 << 7)
+
+/*
+ * ADT7X10 masks
+ */
+#define ADT7X10_T13_VALUE_MASK 0xFFF8
+#define ADT7X10_T_HYST_MASK 0xF
+
+/* straight from the datasheet */
+#define ADT7X10_TEMP_MIN (-55000)
+#define ADT7X10_TEMP_MAX 150000
+
+/* Each client has this additional data */
+struct adt7x10_data {
+ struct regmap *regmap;
+ struct mutex update_lock;
+ u8 config;
+ u8 oldconfig;
+ bool valid; /* true if temperature valid */
+};
+
+enum {
+ adt7x10_temperature = 0,
+ adt7x10_t_alarm_high,
+ adt7x10_t_alarm_low,
+ adt7x10_t_crit,
+};
+
+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;
+ struct adt7x10_data *d = dev_get_drvdata(dev);
+ unsigned int status;
+ int ret;
+
+ ret = regmap_read(d->regmap, ADT7X10_STATUS, &status);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (status & ADT7X10_STAT_T_HIGH)
+ hwmon_notify_event(dev, hwmon_temp, hwmon_temp_max_alarm, 0);
+ if (status & ADT7X10_STAT_T_LOW)
+ hwmon_notify_event(dev, hwmon_temp, hwmon_temp_min_alarm, 0);
+ if (status & ADT7X10_STAT_T_CRIT)
+ hwmon_notify_event(dev, hwmon_temp, hwmon_temp_crit_alarm, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int adt7x10_temp_ready(struct regmap *regmap)
+{
+ unsigned int status;
+ int i, ret;
+
+ for (i = 0; i < 6; i++) {
+ ret = regmap_read(regmap, ADT7X10_STATUS, &status);
+ if (ret < 0)
+ return ret;
+ if (!(status & ADT7X10_STAT_NOT_RDY))
+ return 0;
+ msleep(60);
+ }
+ return -ETIMEDOUT;
+}
+
+static s16 ADT7X10_TEMP_TO_REG(long temp)
+{
+ return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN,
+ ADT7X10_TEMP_MAX) * 128, 1000);
+}
+
+static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg)
+{
+ /* in 13 bit mode, bits 0-2 are status flags - mask them out */
+ if (!(data->config & ADT7X10_RESOLUTION))
+ reg &= ADT7X10_T13_VALUE_MASK;
+ /*
+ * temperature is stored in twos complement format, in steps of
+ * 1/128°C
+ */
+ return DIV_ROUND_CLOSEST(reg * 1000, 128);
+}
+
+/*-----------------------------------------------------------------------*/
+
+static int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val)
+{
+ unsigned int regval;
+ int 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);
+
+ ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &regval);
+ if (ret)
+ return ret;
+
+ *val = ADT7X10_REG_TO_TEMP(data, regval);
+ return 0;
+}
+
+static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp)
+{
+ int ret;
+
+ mutex_lock(&data->update_lock);
+ ret = regmap_write(data->regmap, ADT7X10_REG_TEMP[index],
+ ADT7X10_TEMP_TO_REG(temp));
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val)
+{
+ 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 = (hyst & ADT7X10_T_HYST_MASK) * 1000;
+
+ /*
+ * hysteresis is stored as a 4 bit offset in the device, convert it
+ * to an absolute value
+ */
+ /* min has positive offset, others have negative */
+ if (index == adt7x10_t_alarm_low)
+ hyst = -hyst;
+
+ *val = ADT7X10_REG_TO_TEMP(data, temp) - hyst;
+ return 0;
+}
+
+static int adt7x10_hyst_write(struct adt7x10_data *data, long hyst)
+{
+ unsigned int regval;
+ int limit, ret;
+
+ mutex_lock(&data->update_lock);
+
+ /* convert absolute hysteresis value to a 4 bit delta value */
+ ret = regmap_read(data->regmap, ADT7X10_T_ALARM_HIGH, &regval);
+ if (ret < 0)
+ goto abort;
+
+ 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 int adt7x10_alarm_read(struct adt7x10_data *data, int index, long *val)
+{
+ unsigned int status;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADT7X10_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ *val = !!(status & index);
+
+ return 0;
+}
+
+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);
+
+ 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 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 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,
+ 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->regmap = regmap;
+
+ dev_set_drvdata(dev, data);
+ mutex_init(&data->update_lock);
+
+ /* configure as specified */
+ ret = regmap_read(regmap, ADT7X10_CONFIG, &config);
+ if (ret < 0) {
+ dev_dbg(dev, "Can't read config? %d\n", ret);
+ return ret;
+ }
+ data->oldconfig = config;
+
+ /*
+ * Set to 16 bit resolution, continous conversion and comparator mode.
+ */
+ data->config = data->oldconfig;
+ data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY |
+ ADT7X10_INT_POLARITY);
+ data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE;
+
+ if (data->config != data->oldconfig) {
+ ret = regmap_write(regmap, ADT7X10_CONFIG, data->config);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(dev, adt7x10_restore_config, data);
+ if (ret)
+ return ret;
+ }
+ dev_dbg(dev, "Config %02x\n", data->config);
+
+ 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 = devm_request_threaded_irq(dev, irq, NULL,
+ adt7x10_irq_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev_name(dev), hdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adt7x10_probe);
+
+static int adt7x10_suspend(struct device *dev)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+
+ 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 regmap_write(data->regmap, ADT7X10_CONFIG, data->config);
+}
+
+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");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
new file mode 100644
index 000000000..46caf3e21
--- /dev/null
+++ b/drivers/hwmon/adt7x10.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __HWMON_ADT7X10_H__
+#define __HWMON_ADT7X10_H__
+
+#include <linux/types.h>
+#include <linux/pm.h>
+
+/* ADT7410 registers definition */
+#define ADT7X10_TEMPERATURE 0
+#define ADT7X10_STATUS 2
+#define ADT7X10_CONFIG 3
+#define ADT7X10_T_ALARM_HIGH 4
+#define ADT7X10_T_ALARM_LOW 6
+#define ADT7X10_T_CRIT 8
+#define ADT7X10_T_HYST 0xA
+#define ADT7X10_ID 0xB
+
+struct device;
+
+int adt7x10_probe(struct device *dev, const char *name, int irq,
+ struct regmap *regmap);
+
+extern const struct dev_pm_ops adt7x10_dev_pm_ops;
+
+#endif
diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c
new file mode 100644
index 000000000..2d9770cb4
--- /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
new file mode 100644
index 000000000..3bfd12ff4
--- /dev/null
+++ b/drivers/hwmon/amc6821.c
@@ -0,0 +1,952 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * amc6821.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si>
+ *
+ * Based on max6650.c:
+ * Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
+ */
+
+#include <linux/kernel.h> /* Needed for KERN_INFO */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/*
+ * Addresses to scan.
+ */
+
+static const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e,
+ 0x4c, 0x4d, 0x4e, I2C_CLIENT_END};
+
+/*
+ * Insmod parameters
+ */
+
+static int pwminv; /*Inverted PWM output. */
+module_param(pwminv, int, 0444);
+
+static int init = 1; /*Power-on initialization.*/
+module_param(init, int, 0444);
+
+enum chips { amc6821 };
+
+#define AMC6821_REG_DEV_ID 0x3D
+#define AMC6821_REG_COMP_ID 0x3E
+#define AMC6821_REG_CONF1 0x00
+#define AMC6821_REG_CONF2 0x01
+#define AMC6821_REG_CONF3 0x3F
+#define AMC6821_REG_CONF4 0x04
+#define AMC6821_REG_STAT1 0x02
+#define AMC6821_REG_STAT2 0x03
+#define AMC6821_REG_TDATA_LOW 0x08
+#define AMC6821_REG_TDATA_HI 0x09
+#define AMC6821_REG_LTEMP_HI 0x0A
+#define AMC6821_REG_RTEMP_HI 0x0B
+#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15
+#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14
+#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19
+#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18
+#define AMC6821_REG_LTEMP_CRIT 0x1B
+#define AMC6821_REG_RTEMP_CRIT 0x1D
+#define AMC6821_REG_PSV_TEMP 0x1C
+#define AMC6821_REG_DCY 0x22
+#define AMC6821_REG_LTEMP_FAN_CTRL 0x24
+#define AMC6821_REG_RTEMP_FAN_CTRL 0x25
+#define AMC6821_REG_DCY_LOW_TEMP 0x21
+
+#define AMC6821_REG_TACH_LLIMITL 0x10
+#define AMC6821_REG_TACH_LLIMITH 0x11
+#define AMC6821_REG_TACH_HLIMITL 0x12
+#define AMC6821_REG_TACH_HLIMITH 0x13
+
+#define AMC6821_CONF1_START 0x01
+#define AMC6821_CONF1_FAN_INT_EN 0x02
+#define AMC6821_CONF1_FANIE 0x04
+#define AMC6821_CONF1_PWMINV 0x08
+#define AMC6821_CONF1_FAN_FAULT_EN 0x10
+#define AMC6821_CONF1_FDRC0 0x20
+#define AMC6821_CONF1_FDRC1 0x40
+#define AMC6821_CONF1_THERMOVIE 0x80
+
+#define AMC6821_CONF2_PWM_EN 0x01
+#define AMC6821_CONF2_TACH_MODE 0x02
+#define AMC6821_CONF2_TACH_EN 0x04
+#define AMC6821_CONF2_RTFIE 0x08
+#define AMC6821_CONF2_LTOIE 0x10
+#define AMC6821_CONF2_RTOIE 0x20
+#define AMC6821_CONF2_PSVIE 0x40
+#define AMC6821_CONF2_RST 0x80
+
+#define AMC6821_CONF3_THERM_FAN_EN 0x80
+#define AMC6821_CONF3_REV_MASK 0x0F
+
+#define AMC6821_CONF4_OVREN 0x10
+#define AMC6821_CONF4_TACH_FAST 0x20
+#define AMC6821_CONF4_PSPR 0x40
+#define AMC6821_CONF4_MODE 0x80
+
+#define AMC6821_STAT1_RPM_ALARM 0x01
+#define AMC6821_STAT1_FANS 0x02
+#define AMC6821_STAT1_RTH 0x04
+#define AMC6821_STAT1_RTL 0x08
+#define AMC6821_STAT1_R_THERM 0x10
+#define AMC6821_STAT1_RTF 0x20
+#define AMC6821_STAT1_LTH 0x40
+#define AMC6821_STAT1_LTL 0x80
+
+#define AMC6821_STAT2_RTC 0x08
+#define AMC6821_STAT2_LTC 0x10
+#define AMC6821_STAT2_LPSV 0x20
+#define AMC6821_STAT2_L_THERM 0x40
+#define AMC6821_STAT2_THERM_IN 0x80
+
+enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX,
+ IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN,
+ IDX_TEMP2_MAX, IDX_TEMP2_CRIT,
+ TEMP_IDX_LEN, };
+
+static const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI,
+ AMC6821_REG_LTEMP_LIMIT_MIN,
+ AMC6821_REG_LTEMP_LIMIT_MAX,
+ AMC6821_REG_LTEMP_CRIT,
+ AMC6821_REG_RTEMP_HI,
+ AMC6821_REG_RTEMP_LIMIT_MIN,
+ AMC6821_REG_RTEMP_LIMIT_MAX,
+ AMC6821_REG_RTEMP_CRIT, };
+
+enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX,
+ FAN1_IDX_LEN, };
+
+static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW,
+ AMC6821_REG_TACH_LLIMITL,
+ AMC6821_REG_TACH_HLIMITL, };
+
+
+static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI,
+ AMC6821_REG_TACH_LLIMITH,
+ AMC6821_REG_TACH_HLIMITH, };
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct amc6821_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 */
+ int temp[TEMP_IDX_LEN];
+
+ u16 fan[FAN1_IDX_LEN];
+ u8 fan1_div;
+
+ u8 pwm1;
+ u8 temp1_auto_point_temp[3];
+ u8 temp2_auto_point_temp[3];
+ u8 pwm1_auto_point_pwm[3];
+ u8 pwm1_enable;
+ u8 pwm1_auto_channels_temp;
+
+ u8 stat1;
+ u8 stat2;
+};
+
+static struct amc6821_data *amc6821_update_device(struct device *dev)
+{
+ struct amc6821_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int timeout = HZ;
+ u8 reg;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + timeout) ||
+ !data->valid) {
+
+ for (i = 0; i < TEMP_IDX_LEN; i++)
+ data->temp[i] = (int8_t)i2c_smbus_read_byte_data(
+ client, temp_reg[i]);
+
+ data->stat1 = i2c_smbus_read_byte_data(client,
+ AMC6821_REG_STAT1);
+ data->stat2 = i2c_smbus_read_byte_data(client,
+ AMC6821_REG_STAT2);
+
+ data->pwm1 = i2c_smbus_read_byte_data(client,
+ AMC6821_REG_DCY);
+ for (i = 0; i < FAN1_IDX_LEN; i++) {
+ data->fan[i] = i2c_smbus_read_byte_data(
+ client,
+ fan_reg_low[i]);
+ data->fan[i] += i2c_smbus_read_byte_data(
+ client,
+ fan_reg_hi[i]) << 8;
+ }
+ data->fan1_div = i2c_smbus_read_byte_data(client,
+ AMC6821_REG_CONF4);
+ data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 : 2;
+
+ data->pwm1_auto_point_pwm[0] = 0;
+ data->pwm1_auto_point_pwm[2] = 255;
+ data->pwm1_auto_point_pwm[1] = i2c_smbus_read_byte_data(client,
+ AMC6821_REG_DCY_LOW_TEMP);
+
+ data->temp1_auto_point_temp[0] =
+ i2c_smbus_read_byte_data(client,
+ AMC6821_REG_PSV_TEMP);
+ data->temp2_auto_point_temp[0] =
+ data->temp1_auto_point_temp[0];
+ reg = i2c_smbus_read_byte_data(client,
+ AMC6821_REG_LTEMP_FAN_CTRL);
+ data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1;
+ reg &= 0x07;
+ reg = 0x20 >> reg;
+ if (reg > 0)
+ data->temp1_auto_point_temp[2] =
+ data->temp1_auto_point_temp[1] +
+ (data->pwm1_auto_point_pwm[2] -
+ data->pwm1_auto_point_pwm[1]) / reg;
+ else
+ data->temp1_auto_point_temp[2] = 255;
+
+ reg = i2c_smbus_read_byte_data(client,
+ AMC6821_REG_RTEMP_FAN_CTRL);
+ data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1;
+ reg &= 0x07;
+ reg = 0x20 >> reg;
+ if (reg > 0)
+ data->temp2_auto_point_temp[2] =
+ data->temp2_auto_point_temp[1] +
+ (data->pwm1_auto_point_pwm[2] -
+ data->pwm1_auto_point_pwm[1]) / reg;
+ else
+ data->temp2_auto_point_temp[2] = 255;
+
+ reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+ reg = (reg >> 5) & 0x3;
+ switch (reg) {
+ case 0: /*open loop: software sets pwm1*/
+ data->pwm1_auto_channels_temp = 0;
+ data->pwm1_enable = 1;
+ break;
+ case 2: /*closed loop: remote T (temp2)*/
+ data->pwm1_auto_channels_temp = 2;
+ data->pwm1_enable = 2;
+ break;
+ case 3: /*closed loop: local and remote T (temp2)*/
+ data->pwm1_auto_channels_temp = 3;
+ data->pwm1_enable = 3;
+ break;
+ case 1: /*
+ * semi-open loop: software sets rpm, chip controls
+ * pwm1, currently not implemented
+ */
+ data->pwm1_auto_channels_temp = 0;
+ data->pwm1_enable = 0;
+ break;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", data->temp[ix] * 1000);
+}
+
+static ssize_t temp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct amc6821_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ix = to_sensor_dev_attr(attr)->index;
+ long val;
+
+ int ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ val = clamp_val(val / 1000, -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->temp[ix] = val;
+ if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp[ix])) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+ u8 flag;
+
+ switch (ix) {
+ case IDX_TEMP1_MIN:
+ flag = data->stat1 & AMC6821_STAT1_LTL;
+ break;
+ case IDX_TEMP1_MAX:
+ flag = data->stat1 & AMC6821_STAT1_LTH;
+ break;
+ case IDX_TEMP1_CRIT:
+ flag = data->stat2 & AMC6821_STAT2_LTC;
+ break;
+ case IDX_TEMP2_MIN:
+ flag = data->stat1 & AMC6821_STAT1_RTL;
+ break;
+ case IDX_TEMP2_MAX:
+ flag = data->stat1 & AMC6821_STAT1_RTH;
+ break;
+ case IDX_TEMP2_CRIT:
+ flag = data->stat2 & AMC6821_STAT2_RTC;
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
+ return -EINVAL;
+ }
+ if (flag)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+static ssize_t temp2_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ if (data->stat1 & AMC6821_STAT1_RTF)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1);
+}
+
+static ssize_t pwm1_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf,
+ size_t count)
+{
+ struct amc6821_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->pwm1 = clamp_val(val , 0, 255);
+ i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_enable);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct amc6821_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int config = kstrtol(buf, 10, &val);
+ if (config)
+ return config;
+
+ mutex_lock(&data->update_lock);
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+ if (config < 0) {
+ dev_err(&client->dev,
+ "Error reading configuration register, aborting.\n");
+ count = config;
+ goto unlock;
+ }
+
+ switch (val) {
+ case 1:
+ config &= ~AMC6821_CONF1_FDRC0;
+ config &= ~AMC6821_CONF1_FDRC1;
+ break;
+ case 2:
+ config &= ~AMC6821_CONF1_FDRC0;
+ config |= AMC6821_CONF1_FDRC1;
+ break;
+ case 3:
+ config |= AMC6821_CONF1_FDRC0;
+ config |= AMC6821_CONF1_FDRC1;
+ break;
+ default:
+ count = -EINVAL;
+ goto unlock;
+ }
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) {
+ dev_err(&client->dev,
+ "Configuration register write error, aborting.\n");
+ count = -EIO;
+ }
+unlock:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm1_auto_channels_temp_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp);
+}
+
+static ssize_t temp_auto_point_temp_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ int ix = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ struct amc6821_data *data = amc6821_update_device(dev);
+ switch (nr) {
+ case 1:
+ return sprintf(buf, "%d\n",
+ data->temp1_auto_point_temp[ix] * 1000);
+ case 2:
+ return sprintf(buf, "%d\n",
+ data->temp2_auto_point_temp[ix] * 1000);
+ default:
+ dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
+ return -EINVAL;
+ }
+}
+
+static ssize_t pwm1_auto_point_pwm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ int ix = to_sensor_dev_attr(devattr)->index;
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]);
+}
+
+static inline ssize_t set_slope_register(struct i2c_client *client,
+ u8 reg,
+ u8 dpwm,
+ u8 *ptemp)
+{
+ int dt;
+ u8 tmp;
+
+ dt = ptemp[2]-ptemp[1];
+ for (tmp = 4; tmp > 0; tmp--) {
+ if (dt * (0x20 >> tmp) >= dpwm)
+ break;
+ }
+ tmp |= (ptemp[1] & 0x7C) << 1;
+ if (i2c_smbus_write_byte_data(client,
+ reg, tmp)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static ssize_t temp_auto_point_temp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ struct i2c_client *client = data->client;
+ int ix = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ u8 *ptemp;
+ u8 reg;
+ int dpwm;
+ long val;
+ int ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ switch (nr) {
+ case 1:
+ ptemp = data->temp1_auto_point_temp;
+ reg = AMC6821_REG_LTEMP_FAN_CTRL;
+ break;
+ case 2:
+ ptemp = data->temp2_auto_point_temp;
+ reg = AMC6821_REG_RTEMP_FAN_CTRL;
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->valid = false;
+
+ switch (ix) {
+ case 0:
+ ptemp[0] = clamp_val(val / 1000, 0,
+ data->temp1_auto_point_temp[1]);
+ ptemp[0] = clamp_val(ptemp[0], 0,
+ data->temp2_auto_point_temp[1]);
+ ptemp[0] = clamp_val(ptemp[0], 0, 63);
+ if (i2c_smbus_write_byte_data(
+ client,
+ AMC6821_REG_PSV_TEMP,
+ ptemp[0])) {
+ dev_err(&client->dev,
+ "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ goto EXIT;
+ case 1:
+ ptemp[1] = clamp_val(val / 1000, (ptemp[0] & 0x7C) + 4, 124);
+ ptemp[1] &= 0x7C;
+ ptemp[2] = clamp_val(ptemp[2], ptemp[1] + 1, 255);
+ break;
+ case 2:
+ ptemp[2] = clamp_val(val / 1000, ptemp[1]+1, 255);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
+ count = -EINVAL;
+ goto EXIT;
+ }
+ dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];
+ if (set_slope_register(client, reg, dpwm, ptemp))
+ count = -EIO;
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm1_auto_point_pwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct amc6821_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int dpwm;
+ long val;
+ int ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->pwm1_auto_point_pwm[1] = clamp_val(val, 0, 254);
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP,
+ data->pwm1_auto_point_pwm[1])) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ goto EXIT;
+ }
+ dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];
+ if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL, dpwm,
+ data->temp1_auto_point_temp)) {
+ count = -EIO;
+ goto EXIT;
+ }
+ if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL, dpwm,
+ data->temp2_auto_point_temp)) {
+ count = -EIO;
+ goto EXIT;
+ }
+
+EXIT:
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+ if (0 == data->fan[ix])
+ return sprintf(buf, "0");
+ return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix]));
+}
+
+static ssize_t fan1_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ if (data->stat1 & AMC6821_STAT1_FANS)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+static ssize_t fan_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct amc6821_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int ix = to_sensor_dev_attr(attr)->index;
+ int ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ val = 1 > val ? 0xFFFF : 6000000/val;
+
+ mutex_lock(&data->update_lock);
+ data->fan[ix] = (u16) clamp_val(val, 1, 0xFFFF);
+ if (i2c_smbus_write_byte_data(client, fan_reg_low[ix],
+ data->fan[ix] & 0xFF)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ goto EXIT;
+ }
+ if (i2c_smbus_write_byte_data(client,
+ fan_reg_hi[ix], data->fan[ix] >> 8)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan1_div_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->fan1_div);
+}
+
+static ssize_t fan1_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct amc6821_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int config = kstrtol(buf, 10, &val);
+ if (config)
+ return config;
+
+ mutex_lock(&data->update_lock);
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+ if (config < 0) {
+ dev_err(&client->dev,
+ "Error reading configuration register, aborting.\n");
+ count = config;
+ goto EXIT;
+ }
+ switch (val) {
+ case 2:
+ config &= ~AMC6821_CONF4_PSPR;
+ data->fan1_div = 2;
+ break;
+ case 4:
+ config |= AMC6821_CONF4_PSPR;
+ data->fan1_div = 4;
+ break;
+ default:
+ count = -EINVAL;
+ goto EXIT;
+ }
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) {
+ dev_err(&client->dev,
+ "Configuration register write error, aborting.\n");
+ count = -EIO;
+ }
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, IDX_TEMP1_INPUT);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, IDX_TEMP1_MIN);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, IDX_TEMP1_MAX);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, IDX_TEMP1_CRIT);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_alarm, IDX_TEMP1_MIN);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_alarm, IDX_TEMP1_MAX);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, temp_alarm, IDX_TEMP1_CRIT);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, IDX_TEMP2_INPUT);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, IDX_TEMP2_MIN);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, IDX_TEMP2_MAX);
+static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, IDX_TEMP2_CRIT);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp2_fault, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_alarm, IDX_TEMP2_MIN);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_alarm, IDX_TEMP2_MAX);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, temp_alarm, IDX_TEMP2_CRIT);
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, IDX_FAN1_INPUT);
+static SENSOR_DEVICE_ATTR_RW(fan1_min, fan, IDX_FAN1_MIN);
+static SENSOR_DEVICE_ATTR_RW(fan1_max, fan, IDX_FAN1_MAX);
+static SENSOR_DEVICE_ATTR_RO(fan1_fault, fan1_fault, 0);
+static SENSOR_DEVICE_ATTR_RW(fan1_div, fan1_div, 0);
+
+static SENSOR_DEVICE_ATTR_RW(pwm1, pwm1, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm1_enable, 0);
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm1_auto_point_pwm, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm1_auto_point_pwm, 1);
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm1_auto_point_pwm, 2);
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_channels_temp, pwm1_auto_channels_temp,
+ 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_auto_point1_temp, temp_auto_point_temp,
+ 1, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, temp_auto_point_temp,
+ 1, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point3_temp, temp_auto_point_temp,
+ 1, 2);
+
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point1_temp, temp_auto_point_temp,
+ 2, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point2_temp, temp_auto_point_temp,
+ 2, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point3_temp, temp_auto_point_temp,
+ 2, 2);
+
+static struct attribute *amc6821_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,
+ &sensor_dev_attr_temp1_crit.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,
+ &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_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_max.dev_attr.attr,
+ &sensor_dev_attr_fan1_fault.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point3_temp.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(amc6821);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int amc6821_detect(
+ struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ int dev_id, comp_id;
+
+ dev_dbg(&adapter->dev, "amc6821_detect called.\n");
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_dbg(&adapter->dev,
+ "amc6821: I2C bus doesn't support byte mode, "
+ "skipping.\n");
+ return -ENODEV;
+ }
+
+ dev_id = i2c_smbus_read_byte_data(client, AMC6821_REG_DEV_ID);
+ comp_id = i2c_smbus_read_byte_data(client, AMC6821_REG_COMP_ID);
+ if (dev_id != 0x21 || comp_id != 0x49) {
+ dev_dbg(&adapter->dev,
+ "amc6821: detection failed at 0x%02x.\n",
+ address);
+ return -ENODEV;
+ }
+
+ /*
+ * Bit 7 of the address register is ignored, so we can check the
+ * ID registers again
+ */
+ dev_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_DEV_ID);
+ comp_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_COMP_ID);
+ if (dev_id != 0x21 || comp_id != 0x49) {
+ dev_dbg(&adapter->dev,
+ "amc6821: detection failed at 0x%02x.\n",
+ address);
+ return -ENODEV;
+ }
+
+ dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address);
+ strscpy(info->type, "amc6821", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int amc6821_init_client(struct i2c_client *client)
+{
+ int config;
+ int err = -EIO;
+
+ if (init) {
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+
+ if (config < 0) {
+ dev_err(&client->dev,
+ "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ config |= AMC6821_CONF4_MODE;
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4,
+ config)) {
+ dev_err(&client->dev,
+ "Configuration register write error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3);
+
+ if (config < 0) {
+ dev_err(&client->dev,
+ "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ dev_info(&client->dev, "Revision %d\n", config & 0x0f);
+
+ config &= ~AMC6821_CONF3_THERM_FAN_EN;
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3,
+ config)) {
+ dev_err(&client->dev,
+ "Configuration register write error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2);
+
+ if (config < 0) {
+ dev_err(&client->dev,
+ "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF2_RTFIE;
+ config &= ~AMC6821_CONF2_LTOIE;
+ config &= ~AMC6821_CONF2_RTOIE;
+ if (i2c_smbus_write_byte_data(client,
+ AMC6821_REG_CONF2, config)) {
+ dev_err(&client->dev,
+ "Configuration register write error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+
+ if (config < 0) {
+ dev_err(&client->dev,
+ "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF1_THERMOVIE;
+ config &= ~AMC6821_CONF1_FANIE;
+ config |= AMC6821_CONF1_START;
+ if (pwminv)
+ config |= AMC6821_CONF1_PWMINV;
+ else
+ config &= ~AMC6821_CONF1_PWMINV;
+
+ if (i2c_smbus_write_byte_data(
+ client, AMC6821_REG_CONF1, config)) {
+ dev_err(&client->dev,
+ "Configuration register write error, aborting.\n");
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int amc6821_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct amc6821_data *data;
+ struct device *hwmon_dev;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /*
+ * Initialize the amc6821 chip
+ */
+ err = amc6821_init_client(client);
+ if (err)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ amc6821_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id amc6821_id[] = {
+ { "amc6821", amc6821 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, amc6821_id);
+
+static struct i2c_driver amc6821_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "amc6821",
+ },
+ .probe_new = amc6821_probe,
+ .id_table = amc6821_id,
+ .detect = amc6821_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(amc6821_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("T. Mertelj <tomaz.mertelj@guest.arnes.si>");
+MODULE_DESCRIPTION("Texas Instruments amc6821 hwmon driver");
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
new file mode 100644
index 000000000..fc6d6a905
--- /dev/null
+++ b/drivers/hwmon/applesmc.c
@@ -0,0 +1,1419 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/hwmon/applesmc.c - driver for Apple's SMC (accelerometer, temperature
+ * sensors, fan control, keyboard backlight control) used in Intel-based Apple
+ * computers.
+ *
+ * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ *
+ * Based on hdaps.c driver:
+ * Copyright (C) 2005 Robert Love <rml@novell.com>
+ * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net>
+ *
+ * Fan control based on smcFanControl:
+ * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/dmi.h>
+#include <linux/mutex.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#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
+/* command/status port used by Apple SMC */
+#define APPLESMC_CMD_PORT 0x304
+
+#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
+
+#define APPLESMC_MAX_DATA_LENGTH 32
+
+/* 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
+#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12
+#define APPLESMC_GET_KEY_TYPE_CMD 0x13
+
+#define KEY_COUNT_KEY "#KEY" /* r-o ui32 */
+
+#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */
+#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */
+#define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */
+
+#define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */
+
+#define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */
+#define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */
+#define MOTION_SENSOR_Z_KEY "MO_Z" /* r-o sp78 (2 bytes) */
+#define MOTION_SENSOR_KEY "MOCN" /* r/w ui16 */
+
+#define FANS_COUNT "FNum" /* r-o ui8 */
+#define FANS_MANUAL "FS! " /* r-w ui16 */
+#define FAN_ID_FMT "F%dID" /* r-o char[16] */
+
+#define TEMP_SENSOR_TYPE "sp78"
+
+/* List of keys used to read/write fan speeds */
+static const char *const fan_speed_fmt[] = {
+ "F%dAc", /* actual speed */
+ "F%dMn", /* minimum speed (rw) */
+ "F%dMx", /* maximum speed */
+ "F%dSf", /* safe speed - not all models */
+ "F%dTg", /* target speed (manual: rw) */
+};
+
+#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */
+#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */
+
+#define APPLESMC_POLL_INTERVAL 50 /* msecs */
+#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */
+#define APPLESMC_INPUT_FLAT 4
+
+#define to_index(attr) (to_sensor_dev_attr(attr)->index & 0xffff)
+#define to_option(attr) (to_sensor_dev_attr(attr)->index >> 16)
+
+/* Dynamic device node attributes */
+struct applesmc_dev_attr {
+ struct sensor_device_attribute sda; /* hwmon attributes */
+ char name[32]; /* room for node file name */
+};
+
+/* Dynamic device node group */
+struct applesmc_node_group {
+ char *format; /* format string */
+ void *show; /* show function */
+ void *store; /* store function */
+ int option; /* function argument */
+ struct applesmc_dev_attr *nodes; /* dynamic node array */
+};
+
+/* AppleSMC entry - cached register information */
+struct applesmc_entry {
+ char key[5]; /* four-letter key code */
+ u8 valid; /* set when entry is successfully read once */
+ u8 len; /* bounded by APPLESMC_MAX_DATA_LENGTH */
+ char type[5]; /* four-letter type code */
+ u8 flags; /* 0x10: func; 0x40: write; 0x80: read */
+};
+
+/* Register lookup and registers common to all SMCs */
+static struct applesmc_registers {
+ struct mutex mutex; /* register read/write mutex */
+ unsigned int key_count; /* number of SMC registers */
+ unsigned int fan_count; /* number of fans */
+ unsigned int temp_count; /* number of temperature registers */
+ unsigned int temp_begin; /* temperature lower index bound */
+ unsigned int temp_end; /* temperature upper index bound */
+ unsigned int index_count; /* size of temperature index array */
+ int num_light_sensors; /* number of light sensors */
+ bool has_accelerometer; /* has motion sensor */
+ bool has_key_backlight; /* has keyboard backlight */
+ bool init_complete; /* true when fully initialized */
+ struct applesmc_entry *cache; /* cached key entries */
+ const char **index; /* temperature key index */
+} smcreg = {
+ .mutex = __MUTEX_INITIALIZER(smcreg.mutex),
+};
+
+static const int debug;
+static struct platform_device *pdev;
+static s16 rest_x;
+static s16 rest_y;
+static u8 backlight_state[2];
+
+static struct device *hwmon_dev;
+static struct input_dev *applesmc_idev;
+
+/*
+ * Last index written to key_at_index sysfs file, and value to use for all other
+ * key_at_index_* sysfs files.
+ */
+static unsigned int key_at_index;
+
+static struct workqueue_struct *applesmc_led_wq;
+
+/*
+ * 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_status(u8 val, u8 mask)
+{
+ u8 status;
+ int us;
+ int i;
+
+ us = APPLESMC_MIN_WAIT;
+ for (i = 0; i < 24 ; i++) {
+ status = inb(APPLESMC_CMD_PORT);
+ if ((status & mask) == val)
+ return 0;
+ usleep_range(us, us * 2);
+ if (i > 9)
+ us <<= 1;
+ }
+ return -EIO;
+}
+
+/* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */
+
+static int send_byte(u8 cmd, u16 port)
+{
+ 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);
+ return 0;
+}
+
+/* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */
+
+static int send_command(u8 cmd)
+{
+ 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)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ if (send_byte(key[i], APPLESMC_DATA_PORT))
+ return -EIO;
+ return 0;
+}
+
+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);
+ return -EIO;
+ }
+
+ /* This has no effect on newer (2012) SMCs */
+ if (send_byte(len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: read len fail\n", key);
+ return -EIO;
+ }
+
+ for (i = 0; i < len; i++) {
+ 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;
+ }
+ buffer[i] = inb(APPLESMC_DATA_PORT);
+ }
+
+ /* Read the data port until bit0 is cleared */
+ for (i = 0; i < 16; i++) {
+ udelay(APPLESMC_MIN_WAIT);
+ status = inb(APPLESMC_CMD_PORT);
+ 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 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);
+ return -EIO;
+ }
+
+ if (send_byte(len, APPLESMC_DATA_PORT)) {
+ pr_warn("%.4s: write len fail\n", key);
+ return -EIO;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (send_byte(buffer[i], APPLESMC_DATA_PORT)) {
+ pr_warn("%s: write data fail\n", key);
+ return -EIO;
+ }
+ }
+
+ return wait_status(0, SMC_STATUS_BUSY);
+}
+
+static int read_register_count(unsigned int *count)
+{
+ __be32 be;
+ int ret;
+
+ ret = read_smc(APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4);
+ if (ret)
+ return ret;
+
+ *count = be32_to_cpu(be);
+ return 0;
+}
+
+/*
+ * Serialized I/O
+ *
+ * Returns zero on success or a negative error on failure.
+ * All functions below are concurrency safe - callers should NOT hold lock.
+ */
+
+static int applesmc_read_entry(const struct applesmc_entry *entry,
+ u8 *buf, u8 len)
+{
+ int ret;
+
+ if (entry->len != len)
+ return -EINVAL;
+ mutex_lock(&smcreg.mutex);
+ ret = read_smc(APPLESMC_READ_CMD, entry->key, buf, len);
+ mutex_unlock(&smcreg.mutex);
+
+ return ret;
+}
+
+static int applesmc_write_entry(const struct applesmc_entry *entry,
+ const u8 *buf, u8 len)
+{
+ int ret;
+
+ if (entry->len != len)
+ return -EINVAL;
+ mutex_lock(&smcreg.mutex);
+ ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buf, len);
+ mutex_unlock(&smcreg.mutex);
+ return ret;
+}
+
+static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
+{
+ struct applesmc_entry *cache = &smcreg.cache[index];
+ u8 key[4], info[6];
+ __be32 be;
+ int ret = 0;
+
+ if (cache->valid)
+ return cache;
+
+ mutex_lock(&smcreg.mutex);
+
+ if (cache->valid)
+ goto out;
+ be = cpu_to_be32(index);
+ ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4);
+ if (ret)
+ goto out;
+ ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6);
+ if (ret)
+ goto out;
+
+ memcpy(cache->key, key, 4);
+ cache->len = info[0];
+ memcpy(cache->type, &info[1], 4);
+ cache->flags = info[5];
+ cache->valid = true;
+
+out:
+ mutex_unlock(&smcreg.mutex);
+ if (ret)
+ return ERR_PTR(ret);
+ return cache;
+}
+
+static int applesmc_get_lower_bound(unsigned int *lo, const char *key)
+{
+ int begin = 0, end = smcreg.key_count;
+ const struct applesmc_entry *entry;
+
+ while (begin != end) {
+ int middle = begin + (end - begin) / 2;
+ entry = applesmc_get_entry_by_index(middle);
+ if (IS_ERR(entry)) {
+ *lo = 0;
+ return PTR_ERR(entry);
+ }
+ if (strcmp(entry->key, key) < 0)
+ begin = middle + 1;
+ else
+ end = middle;
+ }
+
+ *lo = begin;
+ return 0;
+}
+
+static int applesmc_get_upper_bound(unsigned int *hi, const char *key)
+{
+ int begin = 0, end = smcreg.key_count;
+ const struct applesmc_entry *entry;
+
+ while (begin != end) {
+ int middle = begin + (end - begin) / 2;
+ entry = applesmc_get_entry_by_index(middle);
+ if (IS_ERR(entry)) {
+ *hi = smcreg.key_count;
+ return PTR_ERR(entry);
+ }
+ if (strcmp(key, entry->key) < 0)
+ end = middle;
+ else
+ begin = middle + 1;
+ }
+
+ *hi = begin;
+ return 0;
+}
+
+static const struct applesmc_entry *applesmc_get_entry_by_key(const char *key)
+{
+ int begin, end;
+ int ret;
+
+ ret = applesmc_get_lower_bound(&begin, key);
+ if (ret)
+ return ERR_PTR(ret);
+ ret = applesmc_get_upper_bound(&end, key);
+ if (ret)
+ return ERR_PTR(ret);
+ if (end - begin != 1)
+ return ERR_PTR(-EINVAL);
+
+ return applesmc_get_entry_by_index(begin);
+}
+
+static int applesmc_read_key(const char *key, u8 *buffer, u8 len)
+{
+ const struct applesmc_entry *entry;
+
+ entry = applesmc_get_entry_by_key(key);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ return applesmc_read_entry(entry, buffer, len);
+}
+
+static int applesmc_write_key(const char *key, const u8 *buffer, u8 len)
+{
+ const struct applesmc_entry *entry;
+
+ entry = applesmc_get_entry_by_key(key);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ return applesmc_write_entry(entry, buffer, len);
+}
+
+static int applesmc_has_key(const char *key, bool *value)
+{
+ const struct applesmc_entry *entry;
+
+ entry = applesmc_get_entry_by_key(key);
+ if (IS_ERR(entry) && PTR_ERR(entry) != -EINVAL)
+ return PTR_ERR(entry);
+
+ *value = !IS_ERR(entry);
+ return 0;
+}
+
+/*
+ * applesmc_read_s16 - Read 16-bit signed big endian register
+ */
+static int applesmc_read_s16(const char *key, s16 *value)
+{
+ u8 buffer[2];
+ int ret;
+
+ ret = applesmc_read_key(key, buffer, 2);
+ if (ret)
+ return ret;
+
+ *value = ((s16)buffer[0] << 8) | buffer[1];
+ return 0;
+}
+
+/*
+ * applesmc_device_init - initialize the accelerometer. Can sleep.
+ */
+static void applesmc_device_init(void)
+{
+ int total;
+ u8 buffer[2];
+
+ if (!smcreg.has_accelerometer)
+ return;
+
+ for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
+ if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
+ (buffer[0] != 0x00 || buffer[1] != 0x00))
+ return;
+ buffer[0] = 0xe0;
+ buffer[1] = 0x00;
+ applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
+ msleep(INIT_WAIT_MSECS);
+ }
+
+ pr_warn("failed to init the device\n");
+}
+
+static int applesmc_init_index(struct applesmc_registers *s)
+{
+ const struct applesmc_entry *entry;
+ unsigned int i;
+
+ if (s->index)
+ return 0;
+
+ s->index = kcalloc(s->temp_count, sizeof(s->index[0]), GFP_KERNEL);
+ if (!s->index)
+ return -ENOMEM;
+
+ for (i = s->temp_begin; i < s->temp_end; i++) {
+ entry = applesmc_get_entry_by_index(i);
+ if (IS_ERR(entry))
+ continue;
+ if (strcmp(entry->type, TEMP_SENSOR_TYPE))
+ continue;
+ s->index[s->index_count++] = entry->key;
+ }
+
+ return 0;
+}
+
+/*
+ * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent.
+ */
+static int applesmc_init_smcreg_try(void)
+{
+ struct applesmc_registers *s = &smcreg;
+ bool left_light_sensor = false, right_light_sensor = false;
+ unsigned int count;
+ u8 tmp[1];
+ int ret;
+
+ if (s->init_complete)
+ return 0;
+
+ ret = read_register_count(&count);
+ if (ret)
+ return ret;
+
+ if (s->cache && s->key_count != count) {
+ pr_warn("key count changed from %d to %d\n",
+ s->key_count, count);
+ kfree(s->cache);
+ s->cache = NULL;
+ }
+ s->key_count = count;
+
+ if (!s->cache)
+ s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL);
+ if (!s->cache)
+ return -ENOMEM;
+
+ ret = applesmc_read_key(FANS_COUNT, tmp, 1);
+ if (ret)
+ return ret;
+ s->fan_count = tmp[0];
+ if (s->fan_count > 10)
+ s->fan_count = 10;
+
+ ret = applesmc_get_lower_bound(&s->temp_begin, "T");
+ if (ret)
+ return ret;
+ ret = applesmc_get_lower_bound(&s->temp_end, "U");
+ if (ret)
+ return ret;
+ s->temp_count = s->temp_end - s->temp_begin;
+
+ ret = applesmc_init_index(s);
+ if (ret)
+ return ret;
+
+ ret = applesmc_has_key(LIGHT_SENSOR_LEFT_KEY, &left_light_sensor);
+ if (ret)
+ return ret;
+ ret = applesmc_has_key(LIGHT_SENSOR_RIGHT_KEY, &right_light_sensor);
+ if (ret)
+ return ret;
+ ret = applesmc_has_key(MOTION_SENSOR_KEY, &s->has_accelerometer);
+ if (ret)
+ return ret;
+ ret = applesmc_has_key(BACKLIGHT_KEY, &s->has_key_backlight);
+ if (ret)
+ return ret;
+
+ s->num_light_sensors = left_light_sensor + right_light_sensor;
+ s->init_complete = true;
+
+ pr_info("key=%d fan=%d temp=%d index=%d acc=%d lux=%d kbd=%d\n",
+ s->key_count, s->fan_count, s->temp_count, s->index_count,
+ s->has_accelerometer,
+ s->num_light_sensors,
+ s->has_key_backlight);
+
+ return 0;
+}
+
+static void applesmc_destroy_smcreg(void)
+{
+ kfree(smcreg.index);
+ smcreg.index = NULL;
+ kfree(smcreg.cache);
+ smcreg.cache = NULL;
+ smcreg.init_complete = false;
+}
+
+/*
+ * applesmc_init_smcreg - Initialize register cache.
+ *
+ * Retries until initialization is successful, or the operation times out.
+ *
+ */
+static int applesmc_init_smcreg(void)
+{
+ int ms, ret;
+
+ for (ms = 0; ms < INIT_TIMEOUT_MSECS; ms += INIT_WAIT_MSECS) {
+ ret = applesmc_init_smcreg_try();
+ if (!ret) {
+ if (ms)
+ pr_info("init_smcreg() took %d ms\n", ms);
+ return 0;
+ }
+ msleep(INIT_WAIT_MSECS);
+ }
+
+ applesmc_destroy_smcreg();
+
+ return ret;
+}
+
+/* Device model stuff */
+static int applesmc_probe(struct platform_device *dev)
+{
+ int ret;
+
+ ret = applesmc_init_smcreg();
+ if (ret)
+ return ret;
+
+ applesmc_device_init();
+
+ return 0;
+}
+
+/* Synchronize device with memorized backlight state */
+static int applesmc_pm_resume(struct device *dev)
+{
+ if (smcreg.has_key_backlight)
+ applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
+ return 0;
+}
+
+/* Reinitialize device on resume from hibernation */
+static int applesmc_pm_restore(struct device *dev)
+{
+ applesmc_device_init();
+ return applesmc_pm_resume(dev);
+}
+
+static const struct dev_pm_ops applesmc_pm_ops = {
+ .resume = applesmc_pm_resume,
+ .restore = applesmc_pm_restore,
+};
+
+static struct platform_driver applesmc_driver = {
+ .probe = applesmc_probe,
+ .driver = {
+ .name = "applesmc",
+ .pm = &applesmc_pm_ops,
+ },
+};
+
+/*
+ * applesmc_calibrate - Set our "resting" values. Callers must
+ * hold applesmc_lock.
+ */
+static void applesmc_calibrate(void)
+{
+ applesmc_read_s16(MOTION_SENSOR_X_KEY, &rest_x);
+ applesmc_read_s16(MOTION_SENSOR_Y_KEY, &rest_y);
+ rest_x = -rest_x;
+}
+
+static void applesmc_idev_poll(struct input_dev *idev)
+{
+ s16 x, y;
+
+ if (applesmc_read_s16(MOTION_SENSOR_X_KEY, &x))
+ return;
+ if (applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y))
+ return;
+
+ x = -x;
+ input_report_abs(idev, ABS_X, x - rest_x);
+ input_report_abs(idev, ABS_Y, y - rest_y);
+ input_sync(idev);
+}
+
+/* Sysfs Files */
+
+static ssize_t applesmc_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "applesmc\n");
+}
+
+static ssize_t applesmc_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ s16 x, y, z;
+
+ ret = applesmc_read_s16(MOTION_SENSOR_X_KEY, &x);
+ if (ret)
+ goto out;
+ ret = applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y);
+ if (ret)
+ goto out;
+ ret = applesmc_read_s16(MOTION_SENSOR_Z_KEY, &z);
+ if (ret)
+ goto out;
+
+out:
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "(%d,%d,%d)\n", x, y, z);
+}
+
+static ssize_t applesmc_light_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ const struct applesmc_entry *entry;
+ static int data_length;
+ int ret;
+ u8 left = 0, right = 0;
+ u8 buffer[10];
+
+ if (!data_length) {
+ entry = applesmc_get_entry_by_key(LIGHT_SENSOR_LEFT_KEY);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+ if (entry->len > 10)
+ return -ENXIO;
+ data_length = entry->len;
+ pr_info("light sensor data length set to %d\n", data_length);
+ }
+
+ 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;
+ right = buffer[2];
+
+out:
+ if (ret)
+ return ret;
+
+ return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right);
+}
+
+/* Displays sensor key as label */
+static ssize_t applesmc_show_sensor_label(struct device *dev,
+ struct device_attribute *devattr, char *sysfsbuf)
+{
+ const char *key = smcreg.index[to_index(devattr)];
+
+ return sysfs_emit(sysfsbuf, "%s\n", key);
+}
+
+/* Displays degree Celsius * 1000 */
+static ssize_t applesmc_show_temperature(struct device *dev,
+ struct device_attribute *devattr, char *sysfsbuf)
+{
+ const char *key = smcreg.index[to_index(devattr)];
+ int ret;
+ s16 value;
+ int temp;
+
+ ret = applesmc_read_s16(key, &value);
+ if (ret)
+ return ret;
+
+ temp = 250 * (value >> 6);
+
+ return sysfs_emit(sysfsbuf, "%d\n", temp);
+}
+
+static ssize_t applesmc_show_fan_speed(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ int ret;
+ unsigned int speed = 0;
+ char newkey[5];
+ u8 buffer[2];
+
+ scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
+ to_index(attr));
+
+ ret = applesmc_read_key(newkey, buffer, 2);
+ if (ret)
+ return ret;
+
+ speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+ return sysfs_emit(sysfsbuf, "%u\n", speed);
+}
+
+static ssize_t applesmc_store_fan_speed(struct device *dev,
+ struct device_attribute *attr,
+ const char *sysfsbuf, size_t count)
+{
+ int ret;
+ unsigned long speed;
+ char newkey[5];
+ u8 buffer[2];
+
+ if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000)
+ return -EINVAL; /* Bigger than a 14-bit value */
+
+ scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
+ to_index(attr));
+
+ buffer[0] = (speed >> 6) & 0xff;
+ buffer[1] = (speed << 2) & 0xff;
+ ret = applesmc_write_key(newkey, buffer, 2);
+
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static ssize_t applesmc_show_fan_manual(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ int ret;
+ u16 manual = 0;
+ u8 buffer[2];
+
+ ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+ if (ret)
+ return ret;
+
+ 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,
+ struct device_attribute *attr,
+ const char *sysfsbuf, size_t count)
+{
+ int ret;
+ u8 buffer[2];
+ unsigned long input;
+ u16 val;
+
+ if (kstrtoul(sysfsbuf, 10, &input) < 0)
+ return -EINVAL;
+
+ ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+ if (ret)
+ goto out;
+
+ val = (buffer[0] << 8 | buffer[1]);
+
+ if (input)
+ val = val | (0x01 << to_index(attr));
+ else
+ val = val & ~(0x01 << to_index(attr));
+
+ buffer[0] = (val >> 8) & 0xFF;
+ buffer[1] = val & 0xFF;
+
+ ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+
+out:
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static ssize_t applesmc_show_fan_position(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ int ret;
+ char newkey[5];
+ u8 buffer[17];
+
+ scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr));
+
+ ret = applesmc_read_key(newkey, buffer, 16);
+ buffer[16] = 0;
+
+ if (ret)
+ return ret;
+
+ return sysfs_emit(sysfsbuf, "%s\n", buffer + 4);
+}
+
+static ssize_t applesmc_calibrate_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ return sysfs_emit(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
+}
+
+static ssize_t applesmc_calibrate_store(struct device *dev,
+ struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+ applesmc_calibrate();
+
+ return count;
+}
+
+static void applesmc_backlight_set(struct work_struct *work)
+{
+ applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
+}
+static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
+
+static void applesmc_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ int ret;
+
+ backlight_state[0] = value;
+ ret = queue_work(applesmc_led_wq, &backlight_work);
+
+ if (debug && (!ret))
+ dev_dbg(led_cdev->dev, "work was already on the queue.\n");
+}
+
+static ssize_t applesmc_key_count_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ int ret;
+ u8 buffer[4];
+ u32 count;
+
+ ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
+ if (ret)
+ return ret;
+
+ 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,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ const struct applesmc_entry *entry;
+ int ret;
+
+ entry = applesmc_get_entry_by_index(key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+ ret = applesmc_read_entry(entry, sysfsbuf, entry->len);
+ if (ret)
+ return ret;
+
+ return entry->len;
+}
+
+static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ const struct applesmc_entry *entry;
+
+ entry = applesmc_get_entry_by_index(key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ return sysfs_emit(sysfsbuf, "%d\n", entry->len);
+}
+
+static ssize_t applesmc_key_at_index_type_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ const struct applesmc_entry *entry;
+
+ entry = applesmc_get_entry_by_index(key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ return sysfs_emit(sysfsbuf, "%s\n", entry->type);
+}
+
+static ssize_t applesmc_key_at_index_name_show(struct device *dev,
+ struct device_attribute *attr, char *sysfsbuf)
+{
+ const struct applesmc_entry *entry;
+
+ entry = applesmc_get_entry_by_index(key_at_index);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ 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 sysfs_emit(sysfsbuf, "%d\n", key_at_index);
+}
+
+static ssize_t applesmc_key_at_index_store(struct device *dev,
+ struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+ unsigned long newkey;
+
+ if (kstrtoul(sysfsbuf, 10, &newkey) < 0
+ || newkey >= smcreg.key_count)
+ return -EINVAL;
+
+ key_at_index = newkey;
+ return count;
+}
+
+static struct led_classdev applesmc_backlight = {
+ .name = "smc::kbd_backlight",
+ .default_trigger = "nand-disk",
+ .brightness_set = applesmc_brightness_set,
+};
+
+static struct applesmc_node_group info_group[] = {
+ { "name", applesmc_name_show },
+ { "key_count", applesmc_key_count_show },
+ { "key_at_index", applesmc_key_at_index_show, applesmc_key_at_index_store },
+ { "key_at_index_name", applesmc_key_at_index_name_show },
+ { "key_at_index_type", applesmc_key_at_index_type_show },
+ { "key_at_index_data_length", applesmc_key_at_index_data_length_show },
+ { "key_at_index_data", applesmc_key_at_index_read_show },
+ { }
+};
+
+static struct applesmc_node_group accelerometer_group[] = {
+ { "position", applesmc_position_show },
+ { "calibrate", applesmc_calibrate_show, applesmc_calibrate_store },
+ { }
+};
+
+static struct applesmc_node_group light_sensor_group[] = {
+ { "light", applesmc_light_show },
+ { }
+};
+
+static struct applesmc_node_group fan_group[] = {
+ { "fan%d_label", applesmc_show_fan_position },
+ { "fan%d_input", applesmc_show_fan_speed, NULL, 0 },
+ { "fan%d_min", applesmc_show_fan_speed, applesmc_store_fan_speed, 1 },
+ { "fan%d_max", applesmc_show_fan_speed, NULL, 2 },
+ { "fan%d_safe", applesmc_show_fan_speed, NULL, 3 },
+ { "fan%d_output", applesmc_show_fan_speed, applesmc_store_fan_speed, 4 },
+ { "fan%d_manual", applesmc_show_fan_manual, applesmc_store_fan_manual },
+ { }
+};
+
+static struct applesmc_node_group temp_group[] = {
+ { "temp%d_label", applesmc_show_sensor_label },
+ { "temp%d_input", applesmc_show_temperature },
+ { }
+};
+
+/* Module stuff */
+
+/*
+ * applesmc_destroy_nodes - remove files and free associated memory
+ */
+static void applesmc_destroy_nodes(struct applesmc_node_group *groups)
+{
+ struct applesmc_node_group *grp;
+ struct applesmc_dev_attr *node;
+
+ for (grp = groups; grp->nodes; grp++) {
+ for (node = grp->nodes; node->sda.dev_attr.attr.name; node++)
+ sysfs_remove_file(&pdev->dev.kobj,
+ &node->sda.dev_attr.attr);
+ kfree(grp->nodes);
+ grp->nodes = NULL;
+ }
+}
+
+/*
+ * applesmc_create_nodes - create a two-dimensional group of sysfs files
+ */
+static int applesmc_create_nodes(struct applesmc_node_group *groups, int num)
+{
+ struct applesmc_node_group *grp;
+ struct applesmc_dev_attr *node;
+ struct attribute *attr;
+ int ret, i;
+
+ for (grp = groups; grp->format; grp++) {
+ grp->nodes = kcalloc(num + 1, sizeof(*node), GFP_KERNEL);
+ if (!grp->nodes) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < num; i++) {
+ node = &grp->nodes[i];
+ scnprintf(node->name, sizeof(node->name), grp->format,
+ i + 1);
+ node->sda.index = (grp->option << 16) | (i & 0xffff);
+ node->sda.dev_attr.show = grp->show;
+ node->sda.dev_attr.store = grp->store;
+ attr = &node->sda.dev_attr.attr;
+ sysfs_attr_init(attr);
+ attr->name = node->name;
+ attr->mode = 0444 | (grp->store ? 0200 : 0);
+ ret = sysfs_create_file(&pdev->dev.kobj, attr);
+ if (ret) {
+ attr->name = NULL;
+ goto out;
+ }
+ }
+ }
+
+ return 0;
+out:
+ applesmc_destroy_nodes(groups);
+ return ret;
+}
+
+/* Create accelerometer resources */
+static int applesmc_create_accelerometer(void)
+{
+ int ret;
+
+ if (!smcreg.has_accelerometer)
+ return 0;
+
+ ret = applesmc_create_nodes(accelerometer_group, 1);
+ if (ret)
+ goto out;
+
+ applesmc_idev = input_allocate_device();
+ if (!applesmc_idev) {
+ ret = -ENOMEM;
+ goto out_sysfs;
+ }
+
+ /* initial calibrate for the input device */
+ applesmc_calibrate();
+
+ /* initialize the input device */
+ applesmc_idev->name = "applesmc";
+ applesmc_idev->id.bustype = BUS_HOST;
+ applesmc_idev->dev.parent = &pdev->dev;
+ input_set_abs_params(applesmc_idev, ABS_X,
+ -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+ input_set_abs_params(applesmc_idev, ABS_Y,
+ -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+ ret = input_setup_polling(applesmc_idev, applesmc_idev_poll);
+ if (ret)
+ goto out_idev;
+
+ input_set_poll_interval(applesmc_idev, APPLESMC_POLL_INTERVAL);
+
+ ret = input_register_device(applesmc_idev);
+ if (ret)
+ goto out_idev;
+
+ return 0;
+
+out_idev:
+ input_free_device(applesmc_idev);
+
+out_sysfs:
+ applesmc_destroy_nodes(accelerometer_group);
+
+out:
+ pr_warn("driver init failed (ret=%d)!\n", ret);
+ return ret;
+}
+
+/* Release all resources used by the accelerometer */
+static void applesmc_release_accelerometer(void)
+{
+ if (!smcreg.has_accelerometer)
+ return;
+ input_unregister_device(applesmc_idev);
+ applesmc_destroy_nodes(accelerometer_group);
+}
+
+static int applesmc_create_light_sensor(void)
+{
+ if (!smcreg.num_light_sensors)
+ return 0;
+ return applesmc_create_nodes(light_sensor_group, 1);
+}
+
+static void applesmc_release_light_sensor(void)
+{
+ if (!smcreg.num_light_sensors)
+ return;
+ applesmc_destroy_nodes(light_sensor_group);
+}
+
+static int applesmc_create_key_backlight(void)
+{
+ if (!smcreg.has_key_backlight)
+ return 0;
+ applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
+ if (!applesmc_led_wq)
+ return -ENOMEM;
+ return led_classdev_register(&pdev->dev, &applesmc_backlight);
+}
+
+static void applesmc_release_key_backlight(void)
+{
+ if (!smcreg.has_key_backlight)
+ return;
+ led_classdev_unregister(&applesmc_backlight);
+ destroy_workqueue(applesmc_led_wq);
+}
+
+static int applesmc_dmi_match(const struct dmi_system_id *id)
+{
+ return 1;
+}
+
+/*
+ * Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
+ * So we need to put "Apple MacBook Pro" before "Apple MacBook".
+ */
+static const struct dmi_system_id applesmc_whitelist[] __initconst = {
+ { applesmc_dmi_match, "Apple MacBook Air", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
+ },
+ { applesmc_dmi_match, "Apple MacBook Pro", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro") },
+ },
+ { applesmc_dmi_match, "Apple MacBook", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBook") },
+ },
+ { applesmc_dmi_match, "Apple Macmini", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Macmini") },
+ },
+ { applesmc_dmi_match, "Apple MacPro", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
+ },
+ { applesmc_dmi_match, "Apple iMac", {
+ 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 }
+};
+
+static int __init applesmc_init(void)
+{
+ int ret;
+
+ if (!dmi_check_system(applesmc_whitelist)) {
+ pr_warn("supported laptop not found!\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+ "applesmc")) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ ret = platform_driver_register(&applesmc_driver);
+ if (ret)
+ goto out_region;
+
+ pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
+ NULL, 0);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ goto out_driver;
+ }
+
+ /* create register cache */
+ ret = applesmc_init_smcreg();
+ if (ret)
+ goto out_device;
+
+ ret = applesmc_create_nodes(info_group, 1);
+ if (ret)
+ goto out_smcreg;
+
+ ret = applesmc_create_nodes(fan_group, smcreg.fan_count);
+ if (ret)
+ goto out_info;
+
+ ret = applesmc_create_nodes(temp_group, smcreg.index_count);
+ if (ret)
+ goto out_fans;
+
+ ret = applesmc_create_accelerometer();
+ if (ret)
+ goto out_temperature;
+
+ ret = applesmc_create_light_sensor();
+ if (ret)
+ goto out_accelerometer;
+
+ ret = applesmc_create_key_backlight();
+ if (ret)
+ goto out_light_sysfs;
+
+ hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(hwmon_dev)) {
+ ret = PTR_ERR(hwmon_dev);
+ goto out_light_ledclass;
+ }
+
+ return 0;
+
+out_light_ledclass:
+ applesmc_release_key_backlight();
+out_light_sysfs:
+ applesmc_release_light_sensor();
+out_accelerometer:
+ applesmc_release_accelerometer();
+out_temperature:
+ applesmc_destroy_nodes(temp_group);
+out_fans:
+ applesmc_destroy_nodes(fan_group);
+out_info:
+ applesmc_destroy_nodes(info_group);
+out_smcreg:
+ applesmc_destroy_smcreg();
+out_device:
+ platform_device_unregister(pdev);
+out_driver:
+ platform_driver_unregister(&applesmc_driver);
+out_region:
+ release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+out:
+ pr_warn("driver init failed (ret=%d)!\n", ret);
+ return ret;
+}
+
+static void __exit applesmc_exit(void)
+{
+ hwmon_device_unregister(hwmon_dev);
+ applesmc_release_key_backlight();
+ applesmc_release_light_sensor();
+ applesmc_release_accelerometer();
+ applesmc_destroy_nodes(temp_group);
+ applesmc_destroy_nodes(fan_group);
+ applesmc_destroy_nodes(info_group);
+ applesmc_destroy_smcreg();
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&applesmc_driver);
+ release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+}
+
+module_init(applesmc_init);
+module_exit(applesmc_exit);
+
+MODULE_AUTHOR("Nicolas Boichat");
+MODULE_DESCRIPTION("Apple SMC");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);
diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
new file mode 100644
index 000000000..8c7796d3f
--- /dev/null
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -0,0 +1,1059 @@
+// 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/delay.h>
+#include <linux/hid.h>
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/ktime.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
+
+#define CTRL_REPORT_DELAY 200 /* ms */
+
+/* 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;
+
+ ktime_t last_ctrl_report_op;
+ int ctrl_report_delay; /* Delay between two ctrl report operations, in ms */
+
+ 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);
+}
+
+static void aqc_delay_ctrl_report(struct aqc_data *priv)
+{
+ /*
+ * If previous read or write is too close to this one, delay the current operation
+ * to give the device enough time to process the previous one.
+ */
+ if (priv->ctrl_report_delay) {
+ s64 delta = ktime_ms_delta(ktime_get(), priv->last_ctrl_report_op);
+
+ if (delta < priv->ctrl_report_delay)
+ msleep(priv->ctrl_report_delay - delta);
+ }
+}
+
+/* Expects the mutex to be locked */
+static int aqc_get_ctrl_data(struct aqc_data *priv)
+{
+ int ret;
+
+ aqc_delay_ctrl_report(priv);
+
+ 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;
+
+ priv->last_ctrl_report_op = ktime_get();
+
+ return ret;
+}
+
+/* Expects the mutex to be locked */
+static int aqc_send_ctrl_data(struct aqc_data *priv)
+{
+ int ret;
+ u16 checksum;
+
+ aqc_delay_ctrl_report(priv);
+
+ /* 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)
+ goto record_access_and_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);
+
+record_access_and_ret:
+ priv->last_ctrl_report_op = ktime_get();
+
+ 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->ctrl_report_delay = CTRL_REPORT_DELAY;
+
+ 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->ctrl_report_delay = CTRL_REPORT_DELAY;
+
+ 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->ctrl_report_delay = CTRL_REPORT_DELAY;
+ 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
new file mode 100644
index 000000000..63b5b2d6e
--- /dev/null
+++ b/drivers/hwmon/as370-hwmon.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics AS370 SoC Hardware Monitoring Driver
+ *
+ * Copyright (C) 2018 Synaptics Incorporated
+ * Author: Jisheng Zhang <jszhang@kernel.org>
+ */
+
+#include <linux/bitops.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#define CTRL 0x0
+#define PD BIT(0)
+#define EN BIT(1)
+#define T_SEL BIT(2)
+#define V_SEL BIT(3)
+#define NMOS_SEL BIT(8)
+#define PMOS_SEL BIT(9)
+#define STS 0x4
+#define BN_MASK GENMASK(11, 0)
+#define EOC BIT(12)
+
+struct as370_hwmon {
+ void __iomem *base;
+};
+
+static void init_pvt(struct as370_hwmon *hwmon)
+{
+ u32 val;
+ void __iomem *addr = hwmon->base + CTRL;
+
+ val = PD;
+ writel_relaxed(val, addr);
+ val |= T_SEL;
+ writel_relaxed(val, addr);
+ val |= EN;
+ writel_relaxed(val, addr);
+ val &= ~PD;
+ writel_relaxed(val, addr);
+}
+
+static int as370_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ int val;
+ struct as370_hwmon *hwmon = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ val = readl_relaxed(hwmon->base + STS) & BN_MASK;
+ *temp = DIV_ROUND_CLOSEST(val * 251802, 4096) - 85525;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static umode_t
+as370_hwmon_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 *as370_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops as370_hwmon_ops = {
+ .is_visible = as370_hwmon_is_visible,
+ .read = as370_hwmon_read,
+};
+
+static const struct hwmon_chip_info as370_chip_info = {
+ .ops = &as370_hwmon_ops,
+ .info = as370_hwmon_info,
+};
+
+static int as370_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *hwmon_dev;
+ struct as370_hwmon *hwmon;
+ struct device *dev = &pdev->dev;
+
+ hwmon = devm_kzalloc(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);
+
+ init_pvt(hwmon);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev,
+ "as370",
+ hwmon,
+ &as370_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct of_device_id as370_hwmon_match[] = {
+ { .compatible = "syna,as370-hwmon" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, as370_hwmon_match);
+
+static struct platform_driver as370_hwmon_driver = {
+ .probe = as370_hwmon_probe,
+ .driver = {
+ .name = "as370-hwmon",
+ .of_match_table = as370_hwmon_match,
+ },
+};
+module_platform_driver(as370_hwmon_driver);
+
+MODULE_AUTHOR("Jisheng Zhang<jszhang@kernel.org>");
+MODULE_DESCRIPTION("Synaptics AS370 SoC hardware monitor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
new file mode 100644
index 000000000..ce4da8367
--- /dev/null
+++ b/drivers/hwmon/asb100.c
@@ -0,0 +1,1008 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * asb100.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ *
+ * Copyright (C) 2004 Mark M. Hoffman <mhoffman@lightlink.com>
+ *
+ * (derived from w83781d.c)
+ *
+ * Copyright (C) 1998 - 2003 Frodo Looijaard <frodol@dds.nl>,
+ * Philip Edelbrock <phil@netroedge.com>, and
+ * Mark Studebaker <mdsxyz123@yahoo.com>
+ */
+
+/*
+ * This driver supports the hardware sensor chips: Asus ASB100 and
+ * ASB100-A "BACH".
+ *
+ * ASB100-A supports pwm1, while plain ASB100 does not. There is no known
+ * way for the driver to tell which one is there.
+ *
+ * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ * asb100 7 3 1 4 0x31 0x0694 yes no
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include "lm75.h"
+
+/* I2C addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
+
+static unsigned short force_subclients[4];
+module_param_array(force_subclients, short, NULL, 0);
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+/* Voltage IN registers 0-6 */
+#define ASB100_REG_IN(nr) (0x20 + (nr))
+#define ASB100_REG_IN_MAX(nr) (0x2b + (nr * 2))
+#define ASB100_REG_IN_MIN(nr) (0x2c + (nr * 2))
+
+/* FAN IN registers 1-3 */
+#define ASB100_REG_FAN(nr) (0x28 + (nr))
+#define ASB100_REG_FAN_MIN(nr) (0x3b + (nr))
+
+/* TEMPERATURE registers 1-4 */
+static const u16 asb100_reg_temp[] = {0, 0x27, 0x150, 0x250, 0x17};
+static const u16 asb100_reg_temp_max[] = {0, 0x39, 0x155, 0x255, 0x18};
+static const u16 asb100_reg_temp_hyst[] = {0, 0x3a, 0x153, 0x253, 0x19};
+
+#define ASB100_REG_TEMP(nr) (asb100_reg_temp[nr])
+#define ASB100_REG_TEMP_MAX(nr) (asb100_reg_temp_max[nr])
+#define ASB100_REG_TEMP_HYST(nr) (asb100_reg_temp_hyst[nr])
+
+#define ASB100_REG_TEMP2_CONFIG 0x0152
+#define ASB100_REG_TEMP3_CONFIG 0x0252
+
+
+#define ASB100_REG_CONFIG 0x40
+#define ASB100_REG_ALARM1 0x41
+#define ASB100_REG_ALARM2 0x42
+#define ASB100_REG_SMIM1 0x43
+#define ASB100_REG_SMIM2 0x44
+#define ASB100_REG_VID_FANDIV 0x47
+#define ASB100_REG_I2C_ADDR 0x48
+#define ASB100_REG_CHIPID 0x49
+#define ASB100_REG_I2C_SUBADDR 0x4a
+#define ASB100_REG_PIN 0x4b
+#define ASB100_REG_IRQ 0x4c
+#define ASB100_REG_BANK 0x4e
+#define ASB100_REG_CHIPMAN 0x4f
+
+#define ASB100_REG_WCHIPID 0x58
+
+/* bit 7 -> enable, bits 0-3 -> duty cycle */
+#define ASB100_REG_PWM1 0x59
+
+/*
+ * CONVERSIONS
+ * Rounding and limit checking is only done on the TO_REG variants.
+ */
+
+/* These constants are a guess, consistent w/ w83781d */
+#define ASB100_IN_MIN 0
+#define ASB100_IN_MAX 4080
+
+/*
+ * IN: 1/1000 V (0V to 4.08V)
+ * REG: 16mV/bit
+ */
+static u8 IN_TO_REG(unsigned val)
+{
+ unsigned nval = clamp_val(val, ASB100_IN_MIN, ASB100_IN_MAX);
+ return (nval + 8) / 16;
+}
+
+static unsigned IN_FROM_REG(u8 reg)
+{
+ return reg * 16;
+}
+
+static u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == -1)
+ return 0;
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+static int FAN_FROM_REG(u8 val, int div)
+{
+ return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div);
+}
+
+/* These constants are a guess, consistent w/ w83781d */
+#define ASB100_TEMP_MIN -128000
+#define ASB100_TEMP_MAX 127000
+
+/*
+ * TEMP: 0.001C/bit (-128C to +127C)
+ * REG: 1C/bit, two's complement
+ */
+static u8 TEMP_TO_REG(long temp)
+{
+ int ntemp = clamp_val(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX);
+ ntemp += (ntemp < 0 ? -500 : 500);
+ return (u8)(ntemp / 1000);
+}
+
+static int TEMP_FROM_REG(u8 reg)
+{
+ return (s8)reg * 1000;
+}
+
+/*
+ * PWM: 0 - 255 per sensors documentation
+ * REG: (6.25% duty cycle per bit)
+ */
+static u8 ASB100_PWM_TO_REG(int pwm)
+{
+ pwm = clamp_val(pwm, 0, 255);
+ return (u8)(pwm / 16);
+}
+
+static int ASB100_PWM_FROM_REG(u8 reg)
+{
+ return reg * 16;
+}
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+/*
+ * FAN DIV: 1, 2, 4, or 8 (defaults to 2)
+ * REG: 0, 1, 2, or 3 (respectively) (defaults to 1)
+ */
+static u8 DIV_TO_REG(long val)
+{
+ return val == 8 ? 3 : val == 4 ? 2 : val == 1 ? 0 : 1;
+}
+
+/*
+ * For each registered client, we need to keep some data in memory. That
+ * data is pointed to by client->data. The structure itself is
+ * dynamically allocated, at the same time the client itself is allocated.
+ */
+struct asb100_data {
+ struct device *hwmon_dev;
+ struct mutex lock;
+
+ struct mutex update_lock;
+ unsigned long last_updated; /* In jiffies */
+
+ /* array of 2 pointers to subclients */
+ struct i2c_client *lm75[2];
+
+ 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 */
+ u8 fan[3]; /* Register value */
+ u8 fan_min[3]; /* Register value */
+ u16 temp[4]; /* Register value (0 and 3 are u8 only) */
+ u16 temp_max[4]; /* Register value (0 and 3 are u8 only) */
+ u16 temp_hyst[4]; /* Register value (0 and 3 are u8 only) */
+ u8 fan_div[3]; /* Register encoding, right justified */
+ u8 pwm; /* Register encoding */
+ u8 vid; /* Register encoding, combined */
+ u32 alarms; /* Register encoding, combined */
+ u8 vrm;
+};
+
+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);
+static int asb100_detect(struct i2c_client *client,
+ struct i2c_board_info *info);
+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);
+
+static const struct i2c_device_id asb100_id[] = {
+ { "asb100", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, asb100_id);
+
+static struct i2c_driver asb100_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "asb100",
+ },
+ .probe_new = asb100_probe,
+ .remove = asb100_remove,
+ .id_table = asb100_id,
+ .detect = asb100_detect,
+ .address_list = normal_i2c,
+};
+
+/* 7 Voltages */
+#define show_in_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ struct asb100_data *data = asb100_update_device(dev); \
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \
+}
+
+show_in_reg(in)
+show_in_reg(in_min)
+show_in_reg(in_max)
+
+#define set_in_reg(REG, reg) \
+static ssize_t set_in_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct asb100_data *data = i2c_get_clientdata(client); \
+ unsigned long val; \
+ int err = kstrtoul(buf, 10, &val); \
+ if (err) \
+ return err; \
+ mutex_lock(&data->update_lock); \
+ data->in_##reg[nr] = IN_TO_REG(val); \
+ asb100_write_value(client, ASB100_REG_IN_##REG(nr), \
+ data->in_##reg[nr]); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+set_in_reg(MIN, min)
+set_in_reg(MAX, max)
+
+#define sysfs_in(offset) \
+static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
+ show_in, NULL, offset); \
+static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
+ show_in_min, set_in_min, offset); \
+static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
+ show_in_max, set_in_max, offset)
+
+sysfs_in(0);
+sysfs_in(1);
+sysfs_in(2);
+sysfs_in(3);
+sysfs_in(4);
+sysfs_in(5);
+sysfs_in(6);
+
+/* 3 Fans */
+static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
+}
+
+static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct asb100_data *data = i2c_get_clientdata(client);
+ 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]));
+ asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct asb100_data *data = i2c_get_clientdata(client);
+ unsigned long min;
+ int reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ min = FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+ data->fan_div[nr] = DIV_TO_REG(val);
+
+ switch (nr) {
+ case 0: /* fan 1 */
+ reg = asb100_read_value(client, ASB100_REG_VID_FANDIV);
+ reg = (reg & 0xcf) | (data->fan_div[0] << 4);
+ asb100_write_value(client, ASB100_REG_VID_FANDIV, reg);
+ break;
+
+ case 1: /* fan 2 */
+ reg = asb100_read_value(client, ASB100_REG_VID_FANDIV);
+ reg = (reg & 0x3f) | (data->fan_div[1] << 6);
+ asb100_write_value(client, ASB100_REG_VID_FANDIV, reg);
+ break;
+
+ case 2: /* fan 3 */
+ reg = asb100_read_value(client, ASB100_REG_PIN);
+ reg = (reg & 0x3f) | (data->fan_div[2] << 6);
+ asb100_write_value(client, ASB100_REG_PIN, reg);
+ break;
+ }
+
+ data->fan_min[nr] =
+ FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+#define sysfs_fan(offset) \
+static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
+ show_fan, NULL, offset - 1); \
+static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
+ show_fan_min, set_fan_min, offset - 1); \
+static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
+ show_fan_div, set_fan_div, offset - 1)
+
+sysfs_fan(1);
+sysfs_fan(2);
+sysfs_fan(3);
+
+/* 4 Temp. Sensors */
+static int sprintf_temp_from_reg(u16 reg, char *buf, int nr)
+{
+ int ret = 0;
+
+ switch (nr) {
+ case 1: case 2:
+ ret = sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(reg));
+ break;
+ case 0: case 3: default:
+ ret = sprintf(buf, "%d\n", TEMP_FROM_REG(reg));
+ break;
+ }
+ return ret;
+}
+
+#define show_temp_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ struct asb100_data *data = asb100_update_device(dev); \
+ return sprintf_temp_from_reg(data->reg[nr], buf, nr); \
+}
+
+show_temp_reg(temp);
+show_temp_reg(temp_max);
+show_temp_reg(temp_hyst);
+
+#define set_temp_reg(REG, reg) \
+static ssize_t set_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct asb100_data *data = i2c_get_clientdata(client); \
+ long val; \
+ int err = kstrtol(buf, 10, &val); \
+ if (err) \
+ return err; \
+ mutex_lock(&data->update_lock); \
+ switch (nr) { \
+ case 1: case 2: \
+ data->reg[nr] = LM75_TEMP_TO_REG(val); \
+ break; \
+ case 0: case 3: default: \
+ data->reg[nr] = TEMP_TO_REG(val); \
+ break; \
+ } \
+ asb100_write_value(client, ASB100_REG_TEMP_##REG(nr+1), \
+ data->reg[nr]); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+set_temp_reg(MAX, temp_max);
+set_temp_reg(HYST, temp_hyst);
+
+#define sysfs_temp(num) \
+static SENSOR_DEVICE_ATTR(temp##num##_input, S_IRUGO, \
+ show_temp, NULL, num - 1); \
+static SENSOR_DEVICE_ATTR(temp##num##_max, S_IRUGO | S_IWUSR, \
+ show_temp_max, set_temp_max, num - 1); \
+static SENSOR_DEVICE_ATTR(temp##num##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_temp_hyst, set_temp_hyst, num - 1)
+
+sysfs_temp(1);
+sysfs_temp(2);
+sysfs_temp(3);
+sysfs_temp(4);
+
+/* VID */
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static DEVICE_ATTR_RO(cpu0_vid);
+
+/* VRM */
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asb100_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct asb100_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;
+}
+
+/* Alarms */
+static DEVICE_ATTR_RW(vrm);
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13);
+
+/* 1 PWM */
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%d\n", ASB100_PWM_FROM_REG(data->pwm & 0x0f));
+}
+
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct asb100_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->pwm &= 0x80; /* keep the enable bit */
+ data->pwm |= (0x0f & ASB100_PWM_TO_REG(val));
+ asb100_write_value(client, ASB100_REG_PWM1, data->pwm);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asb100_data *data = asb100_update_device(dev);
+ return sprintf(buf, "%d\n", (data->pwm & 0x80) ? 1 : 0);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct asb100_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->pwm &= 0x0f; /* keep the duty cycle bits */
+ data->pwm |= (val ? 0x80 : 0x00);
+ asb100_write_value(client, ASB100_REG_PWM1, data->pwm);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RW(pwm1_enable);
+
+static struct attribute *asb100_attributes[] = {
+ &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_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.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_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.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_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.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_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+
+ NULL
+};
+
+static const struct attribute_group asb100_group = {
+ .attrs = asb100_attributes,
+};
+
+static int asb100_detect_subclients(struct i2c_client *client)
+{
+ int i, id, err;
+ int address = client->addr;
+ unsigned short sc_addr[2];
+ struct asb100_data *data = i2c_get_clientdata(client);
+ struct i2c_adapter *adapter = client->adapter;
+
+ id = i2c_adapter_id(adapter);
+
+ if (force_subclients[0] == id && force_subclients[1] == address) {
+ for (i = 2; i <= 3; i++) {
+ if (force_subclients[i] < 0x48 ||
+ force_subclients[i] > 0x4f) {
+ dev_err(&client->dev,
+ "invalid subclient address %d; must be 0x48-0x4f\n",
+ force_subclients[i]);
+ err = -ENODEV;
+ goto ERROR_SC_2;
+ }
+ }
+ asb100_write_value(client, ASB100_REG_I2C_SUBADDR,
+ (force_subclients[2] & 0x07) |
+ ((force_subclients[3] & 0x07) << 4));
+ sc_addr[0] = force_subclients[2];
+ sc_addr[1] = force_subclients[3];
+ } else {
+ int val = asb100_read_value(client, ASB100_REG_I2C_SUBADDR);
+ sc_addr[0] = 0x48 + (val & 0x07);
+ sc_addr[1] = 0x48 + ((val >> 4) & 0x07);
+ }
+
+ if (sc_addr[0] == sc_addr[1]) {
+ dev_err(&client->dev,
+ "duplicate addresses 0x%x for subclients\n",
+ sc_addr[0]);
+ err = -ENODEV;
+ goto ERROR_SC_2;
+ }
+
+ data->lm75[0] = i2c_new_dummy_device(adapter, sc_addr[0]);
+ if (IS_ERR(data->lm75[0])) {
+ dev_err(&client->dev,
+ "subclient %d registration at address 0x%x failed.\n",
+ 1, sc_addr[0]);
+ err = PTR_ERR(data->lm75[0]);
+ goto ERROR_SC_2;
+ }
+
+ data->lm75[1] = i2c_new_dummy_device(adapter, sc_addr[1]);
+ if (IS_ERR(data->lm75[1])) {
+ dev_err(&client->dev,
+ "subclient %d registration at address 0x%x failed.\n",
+ 2, sc_addr[1]);
+ err = PTR_ERR(data->lm75[1]);
+ goto ERROR_SC_3;
+ }
+
+ return 0;
+
+/* Undo inits in case of errors */
+ERROR_SC_3:
+ i2c_unregister_device(data->lm75[0]);
+ERROR_SC_2:
+ return err;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int asb100_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int val1, val2;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_debug("detect failed, smbus byte data not supported!\n");
+ return -ENODEV;
+ }
+
+ val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK);
+ val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
+
+ /* If we're in bank 0 */
+ if ((!(val1 & 0x07)) &&
+ /* Check for ASB100 ID (low byte) */
+ (((!(val1 & 0x80)) && (val2 != 0x94)) ||
+ /* Check for ASB100 ID (high byte ) */
+ ((val1 & 0x80) && (val2 != 0x06)))) {
+ pr_debug("detect failed, bad chip id 0x%02x!\n", val2);
+ return -ENODEV;
+ }
+
+ /* Put it now into bank 0 and Vendor ID High Byte */
+ i2c_smbus_write_byte_data(client, ASB100_REG_BANK,
+ (i2c_smbus_read_byte_data(client, ASB100_REG_BANK) & 0x78)
+ | 0x80);
+
+ /* Determine the chip type. */
+ val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID);
+ val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
+
+ if (val1 != 0x31 || val2 != 0x06)
+ return -ENODEV;
+
+ strscpy(info->type, "asb100", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int asb100_probe(struct i2c_client *client)
+{
+ int err;
+ struct asb100_data *data;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct asb100_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
+
+ /* Attach secondary lm75 clients */
+ err = asb100_detect_subclients(client);
+ if (err)
+ return err;
+
+ /* Initialize the chip */
+ asb100_init_client(client);
+
+ /* A few vars need to be filled upon startup */
+ data->fan_min[0] = asb100_read_value(client, ASB100_REG_FAN_MIN(0));
+ data->fan_min[1] = asb100_read_value(client, ASB100_REG_FAN_MIN(1));
+ data->fan_min[2] = asb100_read_value(client, ASB100_REG_FAN_MIN(2));
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &asb100_group);
+ if (err)
+ goto ERROR3;
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto ERROR4;
+ }
+
+ return 0;
+
+ERROR4:
+ sysfs_remove_group(&client->dev.kobj, &asb100_group);
+ERROR3:
+ i2c_unregister_device(data->lm75[1]);
+ i2c_unregister_device(data->lm75[0]);
+ return err;
+}
+
+static void asb100_remove(struct i2c_client *client)
+{
+ struct asb100_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &asb100_group);
+
+ i2c_unregister_device(data->lm75[1]);
+ i2c_unregister_device(data->lm75[0]);
+}
+
+/*
+ * The SMBus locks itself, usually, but nothing may access the chip between
+ * bank switches.
+ */
+static int asb100_read_value(struct i2c_client *client, u16 reg)
+{
+ struct asb100_data *data = i2c_get_clientdata(client);
+ struct i2c_client *cl;
+ int res, bank;
+
+ mutex_lock(&data->lock);
+
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank);
+
+ if (bank == 0 || bank > 2) {
+ res = i2c_smbus_read_byte_data(client, reg & 0xff);
+ } else {
+ /* switch to subclient */
+ cl = data->lm75[bank - 1];
+
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x50: /* TEMP */
+ res = i2c_smbus_read_word_swapped(cl, 0);
+ break;
+ case 0x52: /* CONFIG */
+ res = i2c_smbus_read_byte_data(cl, 1);
+ break;
+ case 0x53: /* HYST */
+ res = i2c_smbus_read_word_swapped(cl, 2);
+ break;
+ case 0x55: /* MAX */
+ default:
+ res = i2c_smbus_read_word_swapped(cl, 3);
+ break;
+ }
+ }
+
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0);
+
+ mutex_unlock(&data->lock);
+
+ return res;
+}
+
+static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value)
+{
+ struct asb100_data *data = i2c_get_clientdata(client);
+ struct i2c_client *cl;
+ int bank;
+
+ mutex_lock(&data->lock);
+
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank);
+
+ if (bank == 0 || bank > 2) {
+ i2c_smbus_write_byte_data(client, reg & 0xff, value & 0xff);
+ } else {
+ /* switch to subclient */
+ cl = data->lm75[bank - 1];
+
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x52: /* CONFIG */
+ i2c_smbus_write_byte_data(cl, 1, value & 0xff);
+ break;
+ case 0x53: /* HYST */
+ i2c_smbus_write_word_swapped(cl, 2, value);
+ break;
+ case 0x55: /* MAX */
+ i2c_smbus_write_word_swapped(cl, 3, value);
+ break;
+ }
+ }
+
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0);
+
+ mutex_unlock(&data->lock);
+}
+
+static void asb100_init_client(struct i2c_client *client)
+{
+ struct asb100_data *data = i2c_get_clientdata(client);
+
+ data->vrm = vid_which_vrm();
+
+ /* Start monitoring */
+ asb100_write_value(client, ASB100_REG_CONFIG,
+ (asb100_read_value(client, ASB100_REG_CONFIG) & 0xf7) | 0x01);
+}
+
+static struct asb100_data *asb100_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct asb100_data *data = i2c_get_clientdata(client);
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+
+ dev_dbg(&client->dev, "starting device update...\n");
+
+ /* 7 voltage inputs */
+ for (i = 0; i < 7; i++) {
+ data->in[i] = asb100_read_value(client,
+ ASB100_REG_IN(i));
+ data->in_min[i] = asb100_read_value(client,
+ ASB100_REG_IN_MIN(i));
+ data->in_max[i] = asb100_read_value(client,
+ ASB100_REG_IN_MAX(i));
+ }
+
+ /* 3 fan inputs */
+ for (i = 0; i < 3; i++) {
+ data->fan[i] = asb100_read_value(client,
+ ASB100_REG_FAN(i));
+ data->fan_min[i] = asb100_read_value(client,
+ ASB100_REG_FAN_MIN(i));
+ }
+
+ /* 4 temperature inputs */
+ for (i = 1; i <= 4; i++) {
+ data->temp[i-1] = asb100_read_value(client,
+ ASB100_REG_TEMP(i));
+ data->temp_max[i-1] = asb100_read_value(client,
+ ASB100_REG_TEMP_MAX(i));
+ data->temp_hyst[i-1] = asb100_read_value(client,
+ ASB100_REG_TEMP_HYST(i));
+ }
+
+ /* VID and fan divisors */
+ i = asb100_read_value(client, ASB100_REG_VID_FANDIV);
+ data->vid = i & 0x0f;
+ data->vid |= (asb100_read_value(client,
+ ASB100_REG_CHIPID) & 0x01) << 4;
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = (i >> 6) & 0x03;
+ data->fan_div[2] = (asb100_read_value(client,
+ ASB100_REG_PIN) >> 6) & 0x03;
+
+ /* PWM */
+ data->pwm = asb100_read_value(client, ASB100_REG_PWM1);
+
+ /* alarms */
+ data->alarms = asb100_read_value(client, ASB100_REG_ALARM1) +
+ (asb100_read_value(client, ASB100_REG_ALARM2) << 8);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+
+ dev_dbg(&client->dev, "... device update complete\n");
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+module_i2c_driver(asb100_driver);
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
+MODULE_DESCRIPTION("ASB100 Bach driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
new file mode 100644
index 000000000..545954545
--- /dev/null
+++ b/drivers/hwmon/asc7621.c
@@ -0,0 +1,1231 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * asc7621.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
+ * Copyright (c) 2007, 2010 George Joseph <george.joseph@fairview5.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = {
+ 0x2c, 0x2d, 0x2e, I2C_CLIENT_END
+};
+
+enum asc7621_type {
+ asc7621,
+ asc7621a
+};
+
+#define INTERVAL_HIGH (HZ + HZ / 2)
+#define INTERVAL_LOW (1 * 60 * HZ)
+#define PRI_NONE 0
+#define PRI_LOW 1
+#define PRI_HIGH 2
+#define FIRST_CHIP asc7621
+#define LAST_CHIP asc7621a
+
+struct asc7621_chip {
+ char *name;
+ enum asc7621_type chip_type;
+ u8 company_reg;
+ u8 company_id;
+ u8 verstep_reg;
+ u8 verstep_id;
+ const unsigned short *addresses;
+};
+
+static struct asc7621_chip asc7621_chips[] = {
+ {
+ .name = "asc7621",
+ .chip_type = asc7621,
+ .company_reg = 0x3e,
+ .company_id = 0x61,
+ .verstep_reg = 0x3f,
+ .verstep_id = 0x6c,
+ .addresses = normal_i2c,
+ },
+ {
+ .name = "asc7621a",
+ .chip_type = asc7621a,
+ .company_reg = 0x3e,
+ .company_id = 0x61,
+ .verstep_reg = 0x3f,
+ .verstep_id = 0x6d,
+ .addresses = normal_i2c,
+ },
+};
+
+/*
+ * Defines the highest register to be used, not the count.
+ * The actual count will probably be smaller because of gaps
+ * in the implementation (unused register locations).
+ * This define will safely set the array size of both the parameter
+ * and data arrays.
+ * This comes from the data sheet register description table.
+ */
+#define LAST_REGISTER 0xff
+
+struct asc7621_data {
+ struct i2c_client client;
+ struct device *class_dev;
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_high_reading; /* In jiffies */
+ unsigned long last_low_reading; /* In jiffies */
+ /*
+ * Registers we care about occupy the corresponding index
+ * in the array. Registers we don't care about are left
+ * at 0.
+ */
+ u8 reg[LAST_REGISTER + 1];
+};
+
+/*
+ * Macro to get the parent asc7621_param structure
+ * from a sensor_device_attribute passed into the
+ * show/store functions.
+ */
+#define to_asc7621_param(_sda) \
+ container_of(_sda, struct asc7621_param, sda)
+
+/*
+ * Each parameter to be retrieved needs an asc7621_param structure
+ * allocated. It contains the sensor_device_attribute structure
+ * and the control info needed to retrieve the value from the register map.
+ */
+struct asc7621_param {
+ struct sensor_device_attribute sda;
+ u8 priority;
+ u8 msb[3];
+ u8 lsb[3];
+ u8 mask[3];
+ u8 shift[3];
+};
+
+/*
+ * This is the map that ultimately indicates whether we'll be
+ * retrieving a register value or not, and at what frequency.
+ */
+static u8 asc7621_register_priorities[255];
+
+static struct asc7621_data *asc7621_update_device(struct device *dev);
+
+static inline u8 read_byte(struct i2c_client *client, u8 reg)
+{
+ int res = i2c_smbus_read_byte_data(client, reg);
+ if (res < 0) {
+ dev_err(&client->dev,
+ "Unable to read from register 0x%02x.\n", reg);
+ return 0;
+ }
+ return res & 0xff;
+}
+
+static inline int write_byte(struct i2c_client *client, u8 reg, u8 data)
+{
+ int res = i2c_smbus_write_byte_data(client, reg, data);
+ if (res < 0) {
+ dev_err(&client->dev,
+ "Unable to write value 0x%02x to register 0x%02x.\n",
+ data, reg);
+ }
+ return res;
+}
+
+/*
+ * Data Handlers
+ * Each function handles the formatting, storage
+ * and retrieval of like parameters.
+ */
+
+#define SETUP_SHOW_DATA_PARAM(d, a) \
+ struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
+ struct asc7621_data *data = asc7621_update_device(d); \
+ struct asc7621_param *param = to_asc7621_param(sda)
+
+#define SETUP_STORE_DATA_PARAM(d, a) \
+ struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
+ struct i2c_client *client = to_i2c_client(d); \
+ struct asc7621_data *data = i2c_get_clientdata(client); \
+ struct asc7621_param *param = to_asc7621_param(sda)
+
+/*
+ * u8 is just what it sounds like...an unsigned byte with no
+ * special formatting.
+ */
+static ssize_t show_u8(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+
+ return sprintf(buf, "%u\n", data->reg[param->msb[0]]);
+}
+
+static ssize_t store_u8(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ reqval = clamp_val(reqval, 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->reg[param->msb[0]] = reqval;
+ write_byte(client, param->msb[0], reqval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Many of the config values occupy only a few bits of a register.
+ */
+static ssize_t show_bitmask(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+
+ return sprintf(buf, "%u\n",
+ (data->reg[param->msb[0]] >> param->
+ shift[0]) & param->mask[0]);
+}
+
+static ssize_t store_bitmask(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+ u8 currval;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ reqval = clamp_val(reqval, 0, param->mask[0]);
+
+ reqval = (reqval & param->mask[0]) << param->shift[0];
+
+ mutex_lock(&data->update_lock);
+ currval = read_byte(client, param->msb[0]);
+ reqval |= (currval & ~(param->mask[0] << param->shift[0]));
+ data->reg[param->msb[0]] = reqval;
+ write_byte(client, param->msb[0], reqval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * 16 bit fan rpm values
+ * reported by the device as the number of 11.111us periods (90khz)
+ * between full fan rotations. Therefore...
+ * RPM = (90000 * 60) / register value
+ */
+static ssize_t show_fan16(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u16 regval;
+
+ mutex_lock(&data->update_lock);
+ regval = (data->reg[param->msb[0]] << 8) | data->reg[param->lsb[0]];
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%u\n",
+ (regval == 0 ? -1 : (regval) ==
+ 0xffff ? 0 : 5400000 / regval));
+}
+
+static ssize_t store_fan16(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ /*
+ * If a minimum RPM of zero is requested, then we set the register to
+ * 0xffff. This value allows the fan to be stopped completely without
+ * generating an alarm.
+ */
+ reqval =
+ (reqval <= 0 ? 0xffff : clamp_val(5400000 / reqval, 0, 0xfffe));
+
+ mutex_lock(&data->update_lock);
+ data->reg[param->msb[0]] = (reqval >> 8) & 0xff;
+ data->reg[param->lsb[0]] = reqval & 0xff;
+ write_byte(client, param->msb[0], data->reg[param->msb[0]]);
+ write_byte(client, param->lsb[0], data->reg[param->lsb[0]]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * Voltages are scaled in the device so that the nominal voltage
+ * is 3/4ths of the 0-255 range (i.e. 192).
+ * If all voltages are 'normal' then all voltage registers will
+ * read 0xC0.
+ *
+ * The data sheet provides us with the 3/4 scale value for each voltage
+ * which is stored in in_scaling. The sda->index parameter value provides
+ * the index into in_scaling.
+ *
+ * NOTE: The chip expects the first 2 inputs be 2.5 and 2.25 volts
+ * respectively. That doesn't mean that's what the motherboard provides. :)
+ */
+
+static const int asc7621_in_scaling[] = {
+ 2500, 2250, 3300, 5000, 12000
+};
+
+static ssize_t show_in10(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u16 regval;
+ u8 nr = sda->index;
+
+ mutex_lock(&data->update_lock);
+ regval = (data->reg[param->msb[0]] << 8) | (data->reg[param->lsb[0]]);
+ mutex_unlock(&data->update_lock);
+
+ /* The LSB value is a 2-bit scaling of the MSB's LSbit value. */
+ regval = (regval >> 6) * asc7621_in_scaling[nr] / (0xc0 << 2);
+
+ return sprintf(buf, "%u\n", regval);
+}
+
+/* 8 bit voltage values (the mins and maxs) */
+static ssize_t show_in8(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 nr = sda->index;
+
+ return sprintf(buf, "%u\n",
+ ((data->reg[param->msb[0]] *
+ asc7621_in_scaling[nr]) / 0xc0));
+}
+
+static ssize_t store_in8(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+ u8 nr = sda->index;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ reqval = clamp_val(reqval, 0, 0xffff);
+
+ reqval = reqval * 0xc0 / asc7621_in_scaling[nr];
+
+ reqval = clamp_val(reqval, 0, 0xff);
+
+ mutex_lock(&data->update_lock);
+ data->reg[param->msb[0]] = reqval;
+ write_byte(client, param->msb[0], reqval);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_temp8(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+
+ return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000);
+}
+
+static ssize_t store_temp8(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+ s8 temp;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ reqval = clamp_val(reqval, -127000, 127000);
+
+ temp = reqval / 1000;
+
+ mutex_lock(&data->update_lock);
+ data->reg[param->msb[0]] = temp;
+ write_byte(client, param->msb[0], temp);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Temperatures that occupy 2 bytes always have the whole
+ * number of degrees in the MSB with some part of the LSB
+ * indicating fractional degrees.
+ */
+
+/* mmmmmmmm.llxxxxxx */
+static ssize_t show_temp10(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 msb, lsb;
+ int temp;
+
+ mutex_lock(&data->update_lock);
+ msb = data->reg[param->msb[0]];
+ lsb = (data->reg[param->lsb[0]] >> 6) & 0x03;
+ temp = (((s8) msb) * 1000) + (lsb * 250);
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+/* mmmmmm.ll */
+static ssize_t show_temp62(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 regval = data->reg[param->msb[0]];
+ int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250);
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t store_temp62(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval, i, f;
+ s8 temp;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ reqval = clamp_val(reqval, -32000, 31750);
+ i = reqval / 1000;
+ f = reqval - (i * 1000);
+ temp = i << 2;
+ temp |= f / 250;
+
+ mutex_lock(&data->update_lock);
+ data->reg[param->msb[0]] = temp;
+ write_byte(client, param->msb[0], temp);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * The aSC7621 doesn't provide an "auto_point2". Instead, you
+ * specify the auto_point1 and a range. To keep with the sysfs
+ * hwmon specs, we synthesize the auto_point_2 from them.
+ */
+
+static const u32 asc7621_range_map[] = {
+ 2000, 2500, 3330, 4000, 5000, 6670, 8000, 10000,
+ 13330, 16000, 20000, 26670, 32000, 40000, 53330, 80000,
+};
+
+static ssize_t show_ap2_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ long auto_point1;
+ u8 regval;
+ int temp;
+
+ mutex_lock(&data->update_lock);
+ auto_point1 = ((s8) data->reg[param->msb[1]]) * 1000;
+ regval =
+ ((data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]);
+ temp = auto_point1 + asc7621_range_map[clamp_val(regval, 0, 15)];
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%d\n", temp);
+
+}
+
+static ssize_t store_ap2_temp(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval, auto_point1;
+ int i;
+ u8 currval, newval = 0;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ auto_point1 = data->reg[param->msb[1]] * 1000;
+ reqval = clamp_val(reqval, auto_point1 + 2000, auto_point1 + 80000);
+
+ for (i = ARRAY_SIZE(asc7621_range_map) - 1; i >= 0; i--) {
+ if (reqval >= auto_point1 + asc7621_range_map[i]) {
+ newval = i;
+ break;
+ }
+ }
+
+ newval = (newval & param->mask[0]) << param->shift[0];
+ currval = read_byte(client, param->msb[0]);
+ newval |= (currval & ~(param->mask[0] << param->shift[0]));
+ data->reg[param->msb[0]] = newval;
+ write_byte(client, param->msb[0], newval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_pwm_ac(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 config, altbit, regval;
+ static const u8 map[] = {
+ 0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
+ 0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f
+ };
+
+ mutex_lock(&data->update_lock);
+ config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+ altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1];
+ regval = config | (altbit << 3);
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%u\n", map[clamp_val(regval, 0, 15)]);
+}
+
+static ssize_t store_pwm_ac(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ unsigned long reqval;
+ u8 currval, config, altbit, newval;
+ static const u16 map[] = {
+ 0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06,
+ 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
+ };
+
+ if (kstrtoul(buf, 10, &reqval))
+ return -EINVAL;
+
+ if (reqval > 31)
+ return -EINVAL;
+
+ reqval = map[reqval];
+ if (reqval == 0xff)
+ return -EINVAL;
+
+ config = reqval & 0x07;
+ altbit = (reqval >> 3) & 0x01;
+
+ config = (config & param->mask[0]) << param->shift[0];
+ altbit = (altbit & param->mask[1]) << param->shift[1];
+
+ mutex_lock(&data->update_lock);
+ currval = read_byte(client, param->msb[0]);
+ newval = config | (currval & ~(param->mask[0] << param->shift[0]));
+ newval = altbit | (newval & ~(param->mask[1] << param->shift[1]));
+ data->reg[param->msb[0]] = newval;
+ write_byte(client, param->msb[0], newval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_pwm_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 config, altbit, minoff, val, newval;
+
+ mutex_lock(&data->update_lock);
+ config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+ altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1];
+ minoff = (data->reg[param->msb[2]] >> param->shift[2]) & param->mask[2];
+ mutex_unlock(&data->update_lock);
+
+ val = config | (altbit << 3);
+
+ if (val == 3 || val >= 10)
+ newval = 255;
+ else if (val == 4)
+ newval = 0;
+ else if (val == 7)
+ newval = 1;
+ else if (minoff == 1)
+ newval = 2;
+ else
+ newval = 3;
+
+ return sprintf(buf, "%u\n", newval);
+}
+
+static ssize_t store_pwm_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+ u8 currval, config, altbit, newval, minoff = 255;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ switch (reqval) {
+ case 0:
+ newval = 0x04;
+ break;
+ case 1:
+ newval = 0x07;
+ break;
+ case 2:
+ newval = 0x00;
+ minoff = 1;
+ break;
+ case 3:
+ newval = 0x00;
+ minoff = 0;
+ break;
+ case 255:
+ newval = 0x03;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ config = newval & 0x07;
+ altbit = (newval >> 3) & 0x01;
+
+ mutex_lock(&data->update_lock);
+ config = (config & param->mask[0]) << param->shift[0];
+ altbit = (altbit & param->mask[1]) << param->shift[1];
+ currval = read_byte(client, param->msb[0]);
+ newval = config | (currval & ~(param->mask[0] << param->shift[0]));
+ newval = altbit | (newval & ~(param->mask[1] << param->shift[1]));
+ data->reg[param->msb[0]] = newval;
+ write_byte(client, param->msb[0], newval);
+ if (minoff < 255) {
+ minoff = (minoff & param->mask[2]) << param->shift[2];
+ currval = read_byte(client, param->msb[2]);
+ newval =
+ minoff | (currval & ~(param->mask[2] << param->shift[2]));
+ data->reg[param->msb[2]] = newval;
+ write_byte(client, param->msb[2], newval);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static const u32 asc7621_pwm_freq_map[] = {
+ 10, 15, 23, 30, 38, 47, 62, 94,
+ 23000, 24000, 25000, 26000, 27000, 28000, 29000, 30000
+};
+
+static ssize_t show_pwm_freq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 regval =
+ (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+
+ regval = clamp_val(regval, 0, 15);
+
+ return sprintf(buf, "%u\n", asc7621_pwm_freq_map[regval]);
+}
+
+static ssize_t store_pwm_freq(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ unsigned long reqval;
+ u8 currval, newval = 255;
+ int i;
+
+ if (kstrtoul(buf, 10, &reqval))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(asc7621_pwm_freq_map); i++) {
+ if (reqval == asc7621_pwm_freq_map[i]) {
+ newval = i;
+ break;
+ }
+ }
+ if (newval == 255)
+ return -EINVAL;
+
+ newval = (newval & param->mask[0]) << param->shift[0];
+
+ mutex_lock(&data->update_lock);
+ currval = read_byte(client, param->msb[0]);
+ newval |= (currval & ~(param->mask[0] << param->shift[0]));
+ data->reg[param->msb[0]] = newval;
+ write_byte(client, param->msb[0], newval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static const u32 asc7621_pwm_auto_spinup_map[] = {
+ 0, 100, 250, 400, 700, 1000, 2000, 4000
+};
+
+static ssize_t show_pwm_ast(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 regval =
+ (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+
+ regval = clamp_val(regval, 0, 7);
+
+ return sprintf(buf, "%u\n", asc7621_pwm_auto_spinup_map[regval]);
+
+}
+
+static ssize_t store_pwm_ast(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+ u8 currval, newval = 255;
+ u32 i;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(asc7621_pwm_auto_spinup_map); i++) {
+ if (reqval == asc7621_pwm_auto_spinup_map[i]) {
+ newval = i;
+ break;
+ }
+ }
+ if (newval == 255)
+ return -EINVAL;
+
+ newval = (newval & param->mask[0]) << param->shift[0];
+
+ mutex_lock(&data->update_lock);
+ currval = read_byte(client, param->msb[0]);
+ newval |= (currval & ~(param->mask[0] << param->shift[0]));
+ data->reg[param->msb[0]] = newval;
+ write_byte(client, param->msb[0], newval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static const u32 asc7621_temp_smoothing_time_map[] = {
+ 35000, 17600, 11800, 7000, 4400, 3000, 1600, 800
+};
+
+static ssize_t show_temp_st(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ SETUP_SHOW_DATA_PARAM(dev, attr);
+ u8 regval =
+ (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+ regval = clamp_val(regval, 0, 7);
+
+ return sprintf(buf, "%u\n", asc7621_temp_smoothing_time_map[regval]);
+}
+
+static ssize_t store_temp_st(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ SETUP_STORE_DATA_PARAM(dev, attr);
+ long reqval;
+ u8 currval, newval = 255;
+ u32 i;
+
+ if (kstrtol(buf, 10, &reqval))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(asc7621_temp_smoothing_time_map); i++) {
+ if (reqval == asc7621_temp_smoothing_time_map[i]) {
+ newval = i;
+ break;
+ }
+ }
+
+ if (newval == 255)
+ return -EINVAL;
+
+ newval = (newval & param->mask[0]) << param->shift[0];
+
+ mutex_lock(&data->update_lock);
+ currval = read_byte(client, param->msb[0]);
+ newval |= (currval & ~(param->mask[0] << param->shift[0]));
+ data->reg[param->msb[0]] = newval;
+ write_byte(client, param->msb[0], newval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * End of data handlers
+ *
+ * These defines do nothing more than make the table easier
+ * to read when wrapped at column 80.
+ */
+
+/*
+ * Creates a variable length array inititalizer.
+ * VAA(1,3,5,7) would produce {1,3,5,7}
+ */
+#define VAA(args...) {args}
+
+#define PREAD(name, n, pri, rm, rl, m, s, r) \
+ {.sda = SENSOR_ATTR(name, S_IRUGO, show_##r, NULL, n), \
+ .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \
+ .shift[0] = s,}
+
+#define PWRITE(name, n, pri, rm, rl, m, s, r) \
+ {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \
+ .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \
+ .shift[0] = s,}
+
+/*
+ * PWRITEM assumes that the initializers for the .msb, .lsb, .mask and .shift
+ * were created using the VAA macro.
+ */
+#define PWRITEM(name, n, pri, rm, rl, m, s, r) \
+ {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \
+ .priority = pri, .msb = rm, .lsb = rl, .mask = m, .shift = s,}
+
+static struct asc7621_param asc7621_params[] = {
+ PREAD(in0_input, 0, PRI_HIGH, 0x20, 0x13, 0, 0, in10),
+ PREAD(in1_input, 1, PRI_HIGH, 0x21, 0x18, 0, 0, in10),
+ PREAD(in2_input, 2, PRI_HIGH, 0x22, 0x11, 0, 0, in10),
+ PREAD(in3_input, 3, PRI_HIGH, 0x23, 0x12, 0, 0, in10),
+ PREAD(in4_input, 4, PRI_HIGH, 0x24, 0x14, 0, 0, in10),
+
+ PWRITE(in0_min, 0, PRI_LOW, 0x44, 0, 0, 0, in8),
+ PWRITE(in1_min, 1, PRI_LOW, 0x46, 0, 0, 0, in8),
+ PWRITE(in2_min, 2, PRI_LOW, 0x48, 0, 0, 0, in8),
+ PWRITE(in3_min, 3, PRI_LOW, 0x4a, 0, 0, 0, in8),
+ PWRITE(in4_min, 4, PRI_LOW, 0x4c, 0, 0, 0, in8),
+
+ PWRITE(in0_max, 0, PRI_LOW, 0x45, 0, 0, 0, in8),
+ PWRITE(in1_max, 1, PRI_LOW, 0x47, 0, 0, 0, in8),
+ PWRITE(in2_max, 2, PRI_LOW, 0x49, 0, 0, 0, in8),
+ PWRITE(in3_max, 3, PRI_LOW, 0x4b, 0, 0, 0, in8),
+ PWRITE(in4_max, 4, PRI_LOW, 0x4d, 0, 0, 0, in8),
+
+ PREAD(in0_alarm, 0, PRI_HIGH, 0x41, 0, 0x01, 0, bitmask),
+ PREAD(in1_alarm, 1, PRI_HIGH, 0x41, 0, 0x01, 1, bitmask),
+ PREAD(in2_alarm, 2, PRI_HIGH, 0x41, 0, 0x01, 2, bitmask),
+ PREAD(in3_alarm, 3, PRI_HIGH, 0x41, 0, 0x01, 3, bitmask),
+ PREAD(in4_alarm, 4, PRI_HIGH, 0x42, 0, 0x01, 0, bitmask),
+
+ PREAD(fan1_input, 0, PRI_HIGH, 0x29, 0x28, 0, 0, fan16),
+ PREAD(fan2_input, 1, PRI_HIGH, 0x2b, 0x2a, 0, 0, fan16),
+ PREAD(fan3_input, 2, PRI_HIGH, 0x2d, 0x2c, 0, 0, fan16),
+ PREAD(fan4_input, 3, PRI_HIGH, 0x2f, 0x2e, 0, 0, fan16),
+
+ PWRITE(fan1_min, 0, PRI_LOW, 0x55, 0x54, 0, 0, fan16),
+ PWRITE(fan2_min, 1, PRI_LOW, 0x57, 0x56, 0, 0, fan16),
+ PWRITE(fan3_min, 2, PRI_LOW, 0x59, 0x58, 0, 0, fan16),
+ PWRITE(fan4_min, 3, PRI_LOW, 0x5b, 0x5a, 0, 0, fan16),
+
+ PREAD(fan1_alarm, 0, PRI_HIGH, 0x42, 0, 0x01, 2, bitmask),
+ PREAD(fan2_alarm, 1, PRI_HIGH, 0x42, 0, 0x01, 3, bitmask),
+ PREAD(fan3_alarm, 2, PRI_HIGH, 0x42, 0, 0x01, 4, bitmask),
+ PREAD(fan4_alarm, 3, PRI_HIGH, 0x42, 0, 0x01, 5, bitmask),
+
+ PREAD(temp1_input, 0, PRI_HIGH, 0x25, 0x10, 0, 0, temp10),
+ PREAD(temp2_input, 1, PRI_HIGH, 0x26, 0x15, 0, 0, temp10),
+ PREAD(temp3_input, 2, PRI_HIGH, 0x27, 0x16, 0, 0, temp10),
+ PREAD(temp4_input, 3, PRI_HIGH, 0x33, 0x17, 0, 0, temp10),
+ PREAD(temp5_input, 4, PRI_HIGH, 0xf7, 0xf6, 0, 0, temp10),
+ PREAD(temp6_input, 5, PRI_HIGH, 0xf9, 0xf8, 0, 0, temp10),
+ PREAD(temp7_input, 6, PRI_HIGH, 0xfb, 0xfa, 0, 0, temp10),
+ PREAD(temp8_input, 7, PRI_HIGH, 0xfd, 0xfc, 0, 0, temp10),
+
+ PWRITE(temp1_min, 0, PRI_LOW, 0x4e, 0, 0, 0, temp8),
+ PWRITE(temp2_min, 1, PRI_LOW, 0x50, 0, 0, 0, temp8),
+ PWRITE(temp3_min, 2, PRI_LOW, 0x52, 0, 0, 0, temp8),
+ PWRITE(temp4_min, 3, PRI_LOW, 0x34, 0, 0, 0, temp8),
+
+ PWRITE(temp1_max, 0, PRI_LOW, 0x4f, 0, 0, 0, temp8),
+ PWRITE(temp2_max, 1, PRI_LOW, 0x51, 0, 0, 0, temp8),
+ PWRITE(temp3_max, 2, PRI_LOW, 0x53, 0, 0, 0, temp8),
+ PWRITE(temp4_max, 3, PRI_LOW, 0x35, 0, 0, 0, temp8),
+
+ PREAD(temp1_alarm, 0, PRI_HIGH, 0x41, 0, 0x01, 4, bitmask),
+ PREAD(temp2_alarm, 1, PRI_HIGH, 0x41, 0, 0x01, 5, bitmask),
+ PREAD(temp3_alarm, 2, PRI_HIGH, 0x41, 0, 0x01, 6, bitmask),
+ PREAD(temp4_alarm, 3, PRI_HIGH, 0x43, 0, 0x01, 0, bitmask),
+
+ PWRITE(temp1_source, 0, PRI_LOW, 0x02, 0, 0x07, 4, bitmask),
+ PWRITE(temp2_source, 1, PRI_LOW, 0x02, 0, 0x07, 0, bitmask),
+ PWRITE(temp3_source, 2, PRI_LOW, 0x03, 0, 0x07, 4, bitmask),
+ PWRITE(temp4_source, 3, PRI_LOW, 0x03, 0, 0x07, 0, bitmask),
+
+ PWRITE(temp1_smoothing_enable, 0, PRI_LOW, 0x62, 0, 0x01, 3, bitmask),
+ PWRITE(temp2_smoothing_enable, 1, PRI_LOW, 0x63, 0, 0x01, 7, bitmask),
+ PWRITE(temp3_smoothing_enable, 2, PRI_LOW, 0x63, 0, 0x01, 3, bitmask),
+ PWRITE(temp4_smoothing_enable, 3, PRI_LOW, 0x3c, 0, 0x01, 3, bitmask),
+
+ PWRITE(temp1_smoothing_time, 0, PRI_LOW, 0x62, 0, 0x07, 0, temp_st),
+ PWRITE(temp2_smoothing_time, 1, PRI_LOW, 0x63, 0, 0x07, 4, temp_st),
+ PWRITE(temp3_smoothing_time, 2, PRI_LOW, 0x63, 0, 0x07, 0, temp_st),
+ PWRITE(temp4_smoothing_time, 3, PRI_LOW, 0x3c, 0, 0x07, 0, temp_st),
+
+ PWRITE(temp1_auto_point1_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4,
+ bitmask),
+ PWRITE(temp2_auto_point1_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0,
+ bitmask),
+ PWRITE(temp3_auto_point1_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4,
+ bitmask),
+ PWRITE(temp4_auto_point1_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0,
+ bitmask),
+
+ PREAD(temp1_auto_point2_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4,
+ bitmask),
+ PREAD(temp2_auto_point2_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0,
+ bitmask),
+ PREAD(temp3_auto_point2_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4,
+ bitmask),
+ PREAD(temp4_auto_point2_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0,
+ bitmask),
+
+ PWRITE(temp1_auto_point1_temp, 0, PRI_LOW, 0x67, 0, 0, 0, temp8),
+ PWRITE(temp2_auto_point1_temp, 1, PRI_LOW, 0x68, 0, 0, 0, temp8),
+ PWRITE(temp3_auto_point1_temp, 2, PRI_LOW, 0x69, 0, 0, 0, temp8),
+ PWRITE(temp4_auto_point1_temp, 3, PRI_LOW, 0x3b, 0, 0, 0, temp8),
+
+ PWRITEM(temp1_auto_point2_temp, 0, PRI_LOW, VAA(0x5f, 0x67), VAA(0),
+ VAA(0x0f), VAA(4), ap2_temp),
+ PWRITEM(temp2_auto_point2_temp, 1, PRI_LOW, VAA(0x60, 0x68), VAA(0),
+ VAA(0x0f), VAA(4), ap2_temp),
+ PWRITEM(temp3_auto_point2_temp, 2, PRI_LOW, VAA(0x61, 0x69), VAA(0),
+ VAA(0x0f), VAA(4), ap2_temp),
+ PWRITEM(temp4_auto_point2_temp, 3, PRI_LOW, VAA(0x3c, 0x3b), VAA(0),
+ VAA(0x0f), VAA(4), ap2_temp),
+
+ PWRITE(temp1_crit, 0, PRI_LOW, 0x6a, 0, 0, 0, temp8),
+ PWRITE(temp2_crit, 1, PRI_LOW, 0x6b, 0, 0, 0, temp8),
+ PWRITE(temp3_crit, 2, PRI_LOW, 0x6c, 0, 0, 0, temp8),
+ PWRITE(temp4_crit, 3, PRI_LOW, 0x3d, 0, 0, 0, temp8),
+
+ PWRITE(temp5_enable, 4, PRI_LOW, 0x0e, 0, 0x01, 0, bitmask),
+ PWRITE(temp6_enable, 5, PRI_LOW, 0x0e, 0, 0x01, 1, bitmask),
+ PWRITE(temp7_enable, 6, PRI_LOW, 0x0e, 0, 0x01, 2, bitmask),
+ PWRITE(temp8_enable, 7, PRI_LOW, 0x0e, 0, 0x01, 3, bitmask),
+
+ PWRITE(remote1_offset, 0, PRI_LOW, 0x1c, 0, 0, 0, temp62),
+ PWRITE(remote2_offset, 1, PRI_LOW, 0x1d, 0, 0, 0, temp62),
+
+ PWRITE(pwm1, 0, PRI_HIGH, 0x30, 0, 0, 0, u8),
+ PWRITE(pwm2, 1, PRI_HIGH, 0x31, 0, 0, 0, u8),
+ PWRITE(pwm3, 2, PRI_HIGH, 0x32, 0, 0, 0, u8),
+
+ PWRITE(pwm1_invert, 0, PRI_LOW, 0x5c, 0, 0x01, 4, bitmask),
+ PWRITE(pwm2_invert, 1, PRI_LOW, 0x5d, 0, 0x01, 4, bitmask),
+ PWRITE(pwm3_invert, 2, PRI_LOW, 0x5e, 0, 0x01, 4, bitmask),
+
+ PWRITEM(pwm1_enable, 0, PRI_LOW, VAA(0x5c, 0x5c, 0x62), VAA(0, 0, 0),
+ VAA(0x07, 0x01, 0x01), VAA(5, 3, 5), pwm_enable),
+ PWRITEM(pwm2_enable, 1, PRI_LOW, VAA(0x5d, 0x5d, 0x62), VAA(0, 0, 0),
+ VAA(0x07, 0x01, 0x01), VAA(5, 3, 6), pwm_enable),
+ PWRITEM(pwm3_enable, 2, PRI_LOW, VAA(0x5e, 0x5e, 0x62), VAA(0, 0, 0),
+ VAA(0x07, 0x01, 0x01), VAA(5, 3, 7), pwm_enable),
+
+ PWRITEM(pwm1_auto_channels, 0, PRI_LOW, VAA(0x5c, 0x5c), VAA(0, 0),
+ VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
+ PWRITEM(pwm2_auto_channels, 1, PRI_LOW, VAA(0x5d, 0x5d), VAA(0, 0),
+ VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
+ PWRITEM(pwm3_auto_channels, 2, PRI_LOW, VAA(0x5e, 0x5e), VAA(0, 0),
+ VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
+
+ PWRITE(pwm1_auto_point1_pwm, 0, PRI_LOW, 0x64, 0, 0, 0, u8),
+ PWRITE(pwm2_auto_point1_pwm, 1, PRI_LOW, 0x65, 0, 0, 0, u8),
+ PWRITE(pwm3_auto_point1_pwm, 2, PRI_LOW, 0x66, 0, 0, 0, u8),
+
+ PWRITE(pwm1_auto_point2_pwm, 0, PRI_LOW, 0x38, 0, 0, 0, u8),
+ PWRITE(pwm2_auto_point2_pwm, 1, PRI_LOW, 0x39, 0, 0, 0, u8),
+ PWRITE(pwm3_auto_point2_pwm, 2, PRI_LOW, 0x3a, 0, 0, 0, u8),
+
+ PWRITE(pwm1_freq, 0, PRI_LOW, 0x5f, 0, 0x0f, 0, pwm_freq),
+ PWRITE(pwm2_freq, 1, PRI_LOW, 0x60, 0, 0x0f, 0, pwm_freq),
+ PWRITE(pwm3_freq, 2, PRI_LOW, 0x61, 0, 0x0f, 0, pwm_freq),
+
+ PREAD(pwm1_auto_zone_assigned, 0, PRI_LOW, 0, 0, 0x03, 2, bitmask),
+ PREAD(pwm2_auto_zone_assigned, 1, PRI_LOW, 0, 0, 0x03, 4, bitmask),
+ PREAD(pwm3_auto_zone_assigned, 2, PRI_LOW, 0, 0, 0x03, 6, bitmask),
+
+ PWRITE(pwm1_auto_spinup_time, 0, PRI_LOW, 0x5c, 0, 0x07, 0, pwm_ast),
+ PWRITE(pwm2_auto_spinup_time, 1, PRI_LOW, 0x5d, 0, 0x07, 0, pwm_ast),
+ PWRITE(pwm3_auto_spinup_time, 2, PRI_LOW, 0x5e, 0, 0x07, 0, pwm_ast),
+
+ PWRITE(peci_enable, 0, PRI_LOW, 0x40, 0, 0x01, 4, bitmask),
+ PWRITE(peci_avg, 0, PRI_LOW, 0x36, 0, 0x07, 0, bitmask),
+ PWRITE(peci_domain, 0, PRI_LOW, 0x36, 0, 0x01, 3, bitmask),
+ PWRITE(peci_legacy, 0, PRI_LOW, 0x36, 0, 0x01, 4, bitmask),
+ PWRITE(peci_diode, 0, PRI_LOW, 0x0e, 0, 0x07, 4, bitmask),
+ PWRITE(peci_4domain, 0, PRI_LOW, 0x0e, 0, 0x01, 4, bitmask),
+
+};
+
+static struct asc7621_data *asc7621_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct asc7621_data *data = i2c_get_clientdata(client);
+ int i;
+
+/*
+ * The asc7621 chips guarantee consistent reads of multi-byte values
+ * regardless of the order of the reads. No special logic is needed
+ * so we can just read the registers in whatever order they appear
+ * in the asc7621_params array.
+ */
+
+ mutex_lock(&data->update_lock);
+
+ /* Read all the high priority registers */
+
+ if (!data->valid ||
+ time_after(jiffies, data->last_high_reading + INTERVAL_HIGH)) {
+
+ for (i = 0; i < ARRAY_SIZE(asc7621_register_priorities); i++) {
+ if (asc7621_register_priorities[i] == PRI_HIGH) {
+ data->reg[i] =
+ i2c_smbus_read_byte_data(client, i) & 0xff;
+ }
+ }
+ data->last_high_reading = jiffies;
+ } /* last_reading */
+
+ /* Read all the low priority registers. */
+
+ if (!data->valid ||
+ time_after(jiffies, data->last_low_reading + INTERVAL_LOW)) {
+
+ for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+ if (asc7621_register_priorities[i] == PRI_LOW) {
+ data->reg[i] =
+ i2c_smbus_read_byte_data(client, i) & 0xff;
+ }
+ }
+ data->last_low_reading = jiffies;
+ } /* last_reading */
+
+ data->valid = true;
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Standard detection and initialization below
+ *
+ * Helper function that checks if an address is valid
+ * for a particular chip.
+ */
+
+static inline int valid_address_for_chip(int chip_type, int address)
+{
+ int i;
+
+ for (i = 0; asc7621_chips[chip_type].addresses[i] != I2C_CLIENT_END;
+ i++) {
+ if (asc7621_chips[chip_type].addresses[i] == address)
+ return 1;
+ }
+ return 0;
+}
+
+static void asc7621_init_client(struct i2c_client *client)
+{
+ int value;
+
+ /* Warn if part was not "READY" */
+
+ value = read_byte(client, 0x40);
+
+ if (value & 0x02) {
+ dev_err(&client->dev,
+ "Client (%d,0x%02x) config is locked.\n",
+ i2c_adapter_id(client->adapter), client->addr);
+ }
+ if (!(value & 0x04)) {
+ dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n",
+ i2c_adapter_id(client->adapter), client->addr);
+ }
+
+/*
+ * Start monitoring
+ *
+ * Try to clear LOCK, Set START, save everything else
+ */
+ value = (value & ~0x02) | 0x01;
+ write_byte(client, 0x40, value & 0xff);
+
+}
+
+static int
+asc7621_probe(struct i2c_client *client)
+{
+ struct asc7621_data *data;
+ int i, err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct asc7621_data),
+ GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the asc7621 chip */
+ asc7621_init_client(client);
+
+ /* Create the sysfs entries */
+ for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+ err =
+ device_create_file(&client->dev,
+ &(asc7621_params[i].sda.dev_attr));
+ if (err)
+ goto exit_remove;
+ }
+
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+ device_remove_file(&client->dev,
+ &(asc7621_params[i].sda.dev_attr));
+ }
+
+ return err;
+}
+
+static int asc7621_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int company, verstep, chip_index;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ for (chip_index = FIRST_CHIP; chip_index <= LAST_CHIP; chip_index++) {
+
+ if (!valid_address_for_chip(chip_index, client->addr))
+ continue;
+
+ company = read_byte(client,
+ asc7621_chips[chip_index].company_reg);
+ verstep = read_byte(client,
+ asc7621_chips[chip_index].verstep_reg);
+
+ if (company == asc7621_chips[chip_index].company_id &&
+ verstep == asc7621_chips[chip_index].verstep_id) {
+ strscpy(info->type, asc7621_chips[chip_index].name,
+ I2C_NAME_SIZE);
+
+ dev_info(&adapter->dev, "Matched %s at 0x%02x\n",
+ asc7621_chips[chip_index].name, client->addr);
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static void asc7621_remove(struct i2c_client *client)
+{
+ struct asc7621_data *data = i2c_get_clientdata(client);
+ int i;
+
+ hwmon_device_unregister(data->class_dev);
+
+ for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+ device_remove_file(&client->dev,
+ &(asc7621_params[i].sda.dev_attr));
+ }
+}
+
+static const struct i2c_device_id asc7621_id[] = {
+ {"asc7621", asc7621},
+ {"asc7621a", asc7621a},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, asc7621_id);
+
+static struct i2c_driver asc7621_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "asc7621",
+ },
+ .probe_new = asc7621_probe,
+ .remove = asc7621_remove,
+ .id_table = asc7621_id,
+ .detect = asc7621_detect,
+ .address_list = normal_i2c,
+};
+
+static int __init sm_asc7621_init(void)
+{
+ int i, j;
+/*
+ * Collect all the registers needed into a single array.
+ * This way, if a register isn't actually used for anything,
+ * we don't retrieve it.
+ */
+
+ for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+ for (j = 0; j < ARRAY_SIZE(asc7621_params[i].msb); j++)
+ asc7621_register_priorities[asc7621_params[i].msb[j]] =
+ asc7621_params[i].priority;
+ for (j = 0; j < ARRAY_SIZE(asc7621_params[i].lsb); j++)
+ asc7621_register_priorities[asc7621_params[i].lsb[j]] =
+ asc7621_params[i].priority;
+ }
+ return i2c_add_driver(&asc7621_driver);
+}
+
+static void __exit sm_asc7621_exit(void)
+{
+ i2c_del_driver(&asc7621_driver);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("George Joseph");
+MODULE_DESCRIPTION("Andigilog aSC7621 and aSC7621a driver");
+
+module_init(sm_asc7621_init);
+module_exit(sm_asc7621_exit);
diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
new file mode 100644
index 000000000..d11f674e3
--- /dev/null
+++ b/drivers/hwmon/aspeed-pwm-tacho.c
@@ -0,0 +1,972 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2016 Google, Inc
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+/* ASPEED PWM & FAN Tach Register Definition */
+#define ASPEED_PTCR_CTRL 0x00
+#define ASPEED_PTCR_CLK_CTRL 0x04
+#define ASPEED_PTCR_DUTY0_CTRL 0x08
+#define ASPEED_PTCR_DUTY1_CTRL 0x0c
+#define ASPEED_PTCR_TYPEM_CTRL 0x10
+#define ASPEED_PTCR_TYPEM_CTRL1 0x14
+#define ASPEED_PTCR_TYPEN_CTRL 0x18
+#define ASPEED_PTCR_TYPEN_CTRL1 0x1c
+#define ASPEED_PTCR_TACH_SOURCE 0x20
+#define ASPEED_PTCR_TRIGGER 0x28
+#define ASPEED_PTCR_RESULT 0x2c
+#define ASPEED_PTCR_INTR_CTRL 0x30
+#define ASPEED_PTCR_INTR_STS 0x34
+#define ASPEED_PTCR_TYPEM_LIMIT 0x38
+#define ASPEED_PTCR_TYPEN_LIMIT 0x3C
+#define ASPEED_PTCR_CTRL_EXT 0x40
+#define ASPEED_PTCR_CLK_CTRL_EXT 0x44
+#define ASPEED_PTCR_DUTY2_CTRL 0x48
+#define ASPEED_PTCR_DUTY3_CTRL 0x4c
+#define ASPEED_PTCR_TYPEO_CTRL 0x50
+#define ASPEED_PTCR_TYPEO_CTRL1 0x54
+#define ASPEED_PTCR_TACH_SOURCE_EXT 0x60
+#define ASPEED_PTCR_TYPEO_LIMIT 0x78
+
+/* ASPEED_PTCR_CTRL : 0x00 - General Control Register */
+#define ASPEED_PTCR_CTRL_SET_PWMD_TYPE_PART1 15
+#define ASPEED_PTCR_CTRL_SET_PWMD_TYPE_PART2 6
+#define ASPEED_PTCR_CTRL_SET_PWMD_TYPE_MASK (BIT(7) | BIT(15))
+
+#define ASPEED_PTCR_CTRL_SET_PWMC_TYPE_PART1 14
+#define ASPEED_PTCR_CTRL_SET_PWMC_TYPE_PART2 5
+#define ASPEED_PTCR_CTRL_SET_PWMC_TYPE_MASK (BIT(6) | BIT(14))
+
+#define ASPEED_PTCR_CTRL_SET_PWMB_TYPE_PART1 13
+#define ASPEED_PTCR_CTRL_SET_PWMB_TYPE_PART2 4
+#define ASPEED_PTCR_CTRL_SET_PWMB_TYPE_MASK (BIT(5) | BIT(13))
+
+#define ASPEED_PTCR_CTRL_SET_PWMA_TYPE_PART1 12
+#define ASPEED_PTCR_CTRL_SET_PWMA_TYPE_PART2 3
+#define ASPEED_PTCR_CTRL_SET_PWMA_TYPE_MASK (BIT(4) | BIT(12))
+
+#define ASPEED_PTCR_CTRL_FAN_NUM_EN(x) BIT(16 + (x))
+
+#define ASPEED_PTCR_CTRL_PWMD_EN BIT(11)
+#define ASPEED_PTCR_CTRL_PWMC_EN BIT(10)
+#define ASPEED_PTCR_CTRL_PWMB_EN BIT(9)
+#define ASPEED_PTCR_CTRL_PWMA_EN BIT(8)
+
+#define ASPEED_PTCR_CTRL_CLK_SRC BIT(1)
+#define ASPEED_PTCR_CTRL_CLK_EN BIT(0)
+
+/* ASPEED_PTCR_CLK_CTRL : 0x04 - Clock Control Register */
+/* TYPE N */
+#define ASPEED_PTCR_CLK_CTRL_TYPEN_MASK GENMASK(31, 16)
+#define ASPEED_PTCR_CLK_CTRL_TYPEN_UNIT 24
+#define ASPEED_PTCR_CLK_CTRL_TYPEN_H 20
+#define ASPEED_PTCR_CLK_CTRL_TYPEN_L 16
+/* TYPE M */
+#define ASPEED_PTCR_CLK_CTRL_TYPEM_MASK GENMASK(15, 0)
+#define ASPEED_PTCR_CLK_CTRL_TYPEM_UNIT 8
+#define ASPEED_PTCR_CLK_CTRL_TYPEM_H 4
+#define ASPEED_PTCR_CLK_CTRL_TYPEM_L 0
+
+/*
+ * ASPEED_PTCR_DUTY_CTRL/1/2/3 : 0x08/0x0C/0x48/0x4C - PWM-FAN duty control
+ * 0/1/2/3 register
+ */
+#define DUTY_CTRL_PWM2_FALL_POINT 24
+#define DUTY_CTRL_PWM2_RISE_POINT 16
+#define DUTY_CTRL_PWM2_RISE_FALL_MASK GENMASK(31, 16)
+#define DUTY_CTRL_PWM1_FALL_POINT 8
+#define DUTY_CTRL_PWM1_RISE_POINT 0
+#define DUTY_CTRL_PWM1_RISE_FALL_MASK GENMASK(15, 0)
+
+/* ASPEED_PTCR_TYPEM_CTRL : 0x10/0x18/0x50 - Type M/N/O Ctrl 0 Register */
+#define TYPE_CTRL_FAN_MASK (GENMASK(5, 1) | GENMASK(31, 16))
+#define TYPE_CTRL_FAN1_MASK GENMASK(31, 0)
+#define TYPE_CTRL_FAN_PERIOD 16
+#define TYPE_CTRL_FAN_MODE 4
+#define TYPE_CTRL_FAN_DIVISION 1
+#define TYPE_CTRL_FAN_TYPE_EN 1
+
+/* ASPEED_PTCR_TACH_SOURCE : 0x20/0x60 - Tach Source Register */
+/* bit [0,1] at 0x20, bit [2] at 0x60 */
+#define TACH_PWM_SOURCE_BIT01(x) ((x) * 2)
+#define TACH_PWM_SOURCE_BIT2(x) ((x) * 2)
+#define TACH_PWM_SOURCE_MASK_BIT01(x) (0x3 << ((x) * 2))
+#define TACH_PWM_SOURCE_MASK_BIT2(x) BIT((x) * 2)
+
+/* ASPEED_PTCR_RESULT : 0x2c - Result Register */
+#define RESULT_STATUS_MASK BIT(31)
+#define RESULT_VALUE_MASK 0xfffff
+
+/* ASPEED_PTCR_CTRL_EXT : 0x40 - General Control Extension #1 Register */
+#define ASPEED_PTCR_CTRL_SET_PWMH_TYPE_PART1 15
+#define ASPEED_PTCR_CTRL_SET_PWMH_TYPE_PART2 6
+#define ASPEED_PTCR_CTRL_SET_PWMH_TYPE_MASK (BIT(7) | BIT(15))
+
+#define ASPEED_PTCR_CTRL_SET_PWMG_TYPE_PART1 14
+#define ASPEED_PTCR_CTRL_SET_PWMG_TYPE_PART2 5
+#define ASPEED_PTCR_CTRL_SET_PWMG_TYPE_MASK (BIT(6) | BIT(14))
+
+#define ASPEED_PTCR_CTRL_SET_PWMF_TYPE_PART1 13
+#define ASPEED_PTCR_CTRL_SET_PWMF_TYPE_PART2 4
+#define ASPEED_PTCR_CTRL_SET_PWMF_TYPE_MASK (BIT(5) | BIT(13))
+
+#define ASPEED_PTCR_CTRL_SET_PWME_TYPE_PART1 12
+#define ASPEED_PTCR_CTRL_SET_PWME_TYPE_PART2 3
+#define ASPEED_PTCR_CTRL_SET_PWME_TYPE_MASK (BIT(4) | BIT(12))
+
+#define ASPEED_PTCR_CTRL_PWMH_EN BIT(11)
+#define ASPEED_PTCR_CTRL_PWMG_EN BIT(10)
+#define ASPEED_PTCR_CTRL_PWMF_EN BIT(9)
+#define ASPEED_PTCR_CTRL_PWME_EN BIT(8)
+
+/* ASPEED_PTCR_CLK_EXT_CTRL : 0x44 - Clock Control Extension #1 Register */
+/* TYPE O */
+#define ASPEED_PTCR_CLK_CTRL_TYPEO_MASK GENMASK(15, 0)
+#define ASPEED_PTCR_CLK_CTRL_TYPEO_UNIT 8
+#define ASPEED_PTCR_CLK_CTRL_TYPEO_H 4
+#define ASPEED_PTCR_CLK_CTRL_TYPEO_L 0
+
+#define PWM_MAX 255
+
+#define BOTH_EDGES 0x02 /* 10b */
+
+#define M_PWM_DIV_H 0x00
+#define M_PWM_DIV_L 0x05
+#define M_PWM_PERIOD 0x5F
+#define M_TACH_CLK_DIV 0x00
+/*
+ * 5:4 Type N fan tach mode selection bit:
+ * 00: falling
+ * 01: rising
+ * 10: both
+ * 11: reserved.
+ */
+#define M_TACH_MODE 0x02 /* 10b */
+#define M_TACH_UNIT 0x0420
+#define INIT_FAN_CTRL 0xFF
+
+/* How long we sleep in us while waiting for an RPM result. */
+#define ASPEED_RPM_STATUS_SLEEP_USEC 500
+
+#define MAX_CDEV_NAME_LEN 16
+
+struct aspeed_cooling_device {
+ char name[16];
+ struct aspeed_pwm_tacho_data *priv;
+ struct thermal_cooling_device *tcdev;
+ int pwm_port;
+ u8 *cooling_levels;
+ u8 max_state;
+ u8 cur_state;
+};
+
+struct aspeed_pwm_tacho_data {
+ struct regmap *regmap;
+ struct reset_control *rst;
+ unsigned long clk_freq;
+ bool pwm_present[8];
+ bool fan_tach_present[16];
+ u8 type_pwm_clock_unit[3];
+ u8 type_pwm_clock_division_h[3];
+ u8 type_pwm_clock_division_l[3];
+ u8 type_fan_tach_clock_division[3];
+ u8 type_fan_tach_mode[3];
+ u16 type_fan_tach_unit[3];
+ u8 pwm_port_type[8];
+ u8 pwm_port_fan_ctrl[8];
+ u8 fan_tach_ch_source[16];
+ struct aspeed_cooling_device *cdev[8];
+ const struct attribute_group *groups[3];
+};
+
+enum type { TYPEM, TYPEN, TYPEO };
+
+struct type_params {
+ u32 l_value;
+ u32 h_value;
+ u32 unit_value;
+ u32 clk_ctrl_mask;
+ u32 clk_ctrl_reg;
+ u32 ctrl_reg;
+ u32 ctrl_reg1;
+};
+
+static const struct type_params type_params[] = {
+ [TYPEM] = {
+ .l_value = ASPEED_PTCR_CLK_CTRL_TYPEM_L,
+ .h_value = ASPEED_PTCR_CLK_CTRL_TYPEM_H,
+ .unit_value = ASPEED_PTCR_CLK_CTRL_TYPEM_UNIT,
+ .clk_ctrl_mask = ASPEED_PTCR_CLK_CTRL_TYPEM_MASK,
+ .clk_ctrl_reg = ASPEED_PTCR_CLK_CTRL,
+ .ctrl_reg = ASPEED_PTCR_TYPEM_CTRL,
+ .ctrl_reg1 = ASPEED_PTCR_TYPEM_CTRL1,
+ },
+ [TYPEN] = {
+ .l_value = ASPEED_PTCR_CLK_CTRL_TYPEN_L,
+ .h_value = ASPEED_PTCR_CLK_CTRL_TYPEN_H,
+ .unit_value = ASPEED_PTCR_CLK_CTRL_TYPEN_UNIT,
+ .clk_ctrl_mask = ASPEED_PTCR_CLK_CTRL_TYPEN_MASK,
+ .clk_ctrl_reg = ASPEED_PTCR_CLK_CTRL,
+ .ctrl_reg = ASPEED_PTCR_TYPEN_CTRL,
+ .ctrl_reg1 = ASPEED_PTCR_TYPEN_CTRL1,
+ },
+ [TYPEO] = {
+ .l_value = ASPEED_PTCR_CLK_CTRL_TYPEO_L,
+ .h_value = ASPEED_PTCR_CLK_CTRL_TYPEO_H,
+ .unit_value = ASPEED_PTCR_CLK_CTRL_TYPEO_UNIT,
+ .clk_ctrl_mask = ASPEED_PTCR_CLK_CTRL_TYPEO_MASK,
+ .clk_ctrl_reg = ASPEED_PTCR_CLK_CTRL_EXT,
+ .ctrl_reg = ASPEED_PTCR_TYPEO_CTRL,
+ .ctrl_reg1 = ASPEED_PTCR_TYPEO_CTRL1,
+ }
+};
+
+enum pwm_port { PWMA, PWMB, PWMC, PWMD, PWME, PWMF, PWMG, PWMH };
+
+struct pwm_port_params {
+ u32 pwm_en;
+ u32 ctrl_reg;
+ u32 type_part1;
+ u32 type_part2;
+ u32 type_mask;
+ u32 duty_ctrl_rise_point;
+ u32 duty_ctrl_fall_point;
+ u32 duty_ctrl_reg;
+ u32 duty_ctrl_rise_fall_mask;
+};
+
+static const struct pwm_port_params pwm_port_params[] = {
+ [PWMA] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWMA_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWMA_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWMA_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWMA_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM1_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM1_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY0_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM1_RISE_FALL_MASK,
+ },
+ [PWMB] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWMB_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWMB_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWMB_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWMB_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM2_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM2_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY0_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM2_RISE_FALL_MASK,
+ },
+ [PWMC] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWMC_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWMC_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWMC_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWMC_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM1_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM1_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY1_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM1_RISE_FALL_MASK,
+ },
+ [PWMD] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWMD_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWMD_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWMD_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWMD_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM2_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM2_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY1_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM2_RISE_FALL_MASK,
+ },
+ [PWME] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWME_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL_EXT,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWME_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWME_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWME_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM1_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM1_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY2_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM1_RISE_FALL_MASK,
+ },
+ [PWMF] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWMF_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL_EXT,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWMF_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWMF_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWMF_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM2_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM2_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY2_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM2_RISE_FALL_MASK,
+ },
+ [PWMG] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWMG_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL_EXT,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWMG_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWMG_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWMG_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM1_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM1_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY3_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM1_RISE_FALL_MASK,
+ },
+ [PWMH] = {
+ .pwm_en = ASPEED_PTCR_CTRL_PWMH_EN,
+ .ctrl_reg = ASPEED_PTCR_CTRL_EXT,
+ .type_part1 = ASPEED_PTCR_CTRL_SET_PWMH_TYPE_PART1,
+ .type_part2 = ASPEED_PTCR_CTRL_SET_PWMH_TYPE_PART2,
+ .type_mask = ASPEED_PTCR_CTRL_SET_PWMH_TYPE_MASK,
+ .duty_ctrl_rise_point = DUTY_CTRL_PWM2_RISE_POINT,
+ .duty_ctrl_fall_point = DUTY_CTRL_PWM2_FALL_POINT,
+ .duty_ctrl_reg = ASPEED_PTCR_DUTY3_CTRL,
+ .duty_ctrl_rise_fall_mask = DUTY_CTRL_PWM2_RISE_FALL_MASK,
+ }
+};
+
+static int regmap_aspeed_pwm_tacho_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ void __iomem *regs = (void __iomem *)context;
+
+ writel(val, regs + reg);
+ return 0;
+}
+
+static int regmap_aspeed_pwm_tacho_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ void __iomem *regs = (void __iomem *)context;
+
+ *val = readl(regs + reg);
+ return 0;
+}
+
+static const struct regmap_config aspeed_pwm_tacho_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = ASPEED_PTCR_TYPEO_LIMIT,
+ .reg_write = regmap_aspeed_pwm_tacho_reg_write,
+ .reg_read = regmap_aspeed_pwm_tacho_reg_read,
+ .fast_io = true,
+};
+
+static void aspeed_set_clock_enable(struct regmap *regmap, bool val)
+{
+ regmap_update_bits(regmap, ASPEED_PTCR_CTRL,
+ ASPEED_PTCR_CTRL_CLK_EN,
+ val ? ASPEED_PTCR_CTRL_CLK_EN : 0);
+}
+
+static void aspeed_set_clock_source(struct regmap *regmap, int val)
+{
+ regmap_update_bits(regmap, ASPEED_PTCR_CTRL,
+ ASPEED_PTCR_CTRL_CLK_SRC,
+ val ? ASPEED_PTCR_CTRL_CLK_SRC : 0);
+}
+
+static void aspeed_set_pwm_clock_values(struct regmap *regmap, u8 type,
+ u8 div_high, u8 div_low, u8 unit)
+{
+ u32 reg_value = ((div_high << type_params[type].h_value) |
+ (div_low << type_params[type].l_value) |
+ (unit << type_params[type].unit_value));
+
+ regmap_update_bits(regmap, type_params[type].clk_ctrl_reg,
+ type_params[type].clk_ctrl_mask, reg_value);
+}
+
+static void aspeed_set_pwm_port_enable(struct regmap *regmap, u8 pwm_port,
+ bool enable)
+{
+ regmap_update_bits(regmap, pwm_port_params[pwm_port].ctrl_reg,
+ pwm_port_params[pwm_port].pwm_en,
+ enable ? pwm_port_params[pwm_port].pwm_en : 0);
+}
+
+static void aspeed_set_pwm_port_type(struct regmap *regmap,
+ u8 pwm_port, u8 type)
+{
+ u32 reg_value = (type & 0x1) << pwm_port_params[pwm_port].type_part1;
+
+ reg_value |= (type & 0x2) << pwm_port_params[pwm_port].type_part2;
+
+ regmap_update_bits(regmap, pwm_port_params[pwm_port].ctrl_reg,
+ pwm_port_params[pwm_port].type_mask, reg_value);
+}
+
+static void aspeed_set_pwm_port_duty_rising_falling(struct regmap *regmap,
+ u8 pwm_port, u8 rising,
+ u8 falling)
+{
+ u32 reg_value = (rising <<
+ pwm_port_params[pwm_port].duty_ctrl_rise_point);
+ reg_value |= (falling <<
+ pwm_port_params[pwm_port].duty_ctrl_fall_point);
+
+ regmap_update_bits(regmap, pwm_port_params[pwm_port].duty_ctrl_reg,
+ pwm_port_params[pwm_port].duty_ctrl_rise_fall_mask,
+ reg_value);
+}
+
+static void aspeed_set_tacho_type_enable(struct regmap *regmap, u8 type,
+ bool enable)
+{
+ regmap_update_bits(regmap, type_params[type].ctrl_reg,
+ TYPE_CTRL_FAN_TYPE_EN,
+ enable ? TYPE_CTRL_FAN_TYPE_EN : 0);
+}
+
+static void aspeed_set_tacho_type_values(struct regmap *regmap, u8 type,
+ u8 mode, u16 unit, u8 division)
+{
+ u32 reg_value = ((mode << TYPE_CTRL_FAN_MODE) |
+ (unit << TYPE_CTRL_FAN_PERIOD) |
+ (division << TYPE_CTRL_FAN_DIVISION));
+
+ regmap_update_bits(regmap, type_params[type].ctrl_reg,
+ TYPE_CTRL_FAN_MASK, reg_value);
+ regmap_update_bits(regmap, type_params[type].ctrl_reg1,
+ TYPE_CTRL_FAN1_MASK, unit << 16);
+}
+
+static void aspeed_set_fan_tach_ch_enable(struct regmap *regmap, u8 fan_tach_ch,
+ bool enable)
+{
+ regmap_update_bits(regmap, ASPEED_PTCR_CTRL,
+ ASPEED_PTCR_CTRL_FAN_NUM_EN(fan_tach_ch),
+ enable ?
+ ASPEED_PTCR_CTRL_FAN_NUM_EN(fan_tach_ch) : 0);
+}
+
+static void aspeed_set_fan_tach_ch_source(struct regmap *regmap, u8 fan_tach_ch,
+ u8 fan_tach_ch_source)
+{
+ u32 reg_value1 = ((fan_tach_ch_source & 0x3) <<
+ TACH_PWM_SOURCE_BIT01(fan_tach_ch));
+ u32 reg_value2 = (((fan_tach_ch_source & 0x4) >> 2) <<
+ TACH_PWM_SOURCE_BIT2(fan_tach_ch));
+
+ regmap_update_bits(regmap, ASPEED_PTCR_TACH_SOURCE,
+ TACH_PWM_SOURCE_MASK_BIT01(fan_tach_ch),
+ reg_value1);
+
+ regmap_update_bits(regmap, ASPEED_PTCR_TACH_SOURCE_EXT,
+ TACH_PWM_SOURCE_MASK_BIT2(fan_tach_ch),
+ reg_value2);
+}
+
+static void aspeed_set_pwm_port_fan_ctrl(struct aspeed_pwm_tacho_data *priv,
+ u8 index, u8 fan_ctrl)
+{
+ u16 period, dc_time_on;
+
+ period = priv->type_pwm_clock_unit[priv->pwm_port_type[index]];
+ period += 1;
+ dc_time_on = (fan_ctrl * period) / PWM_MAX;
+
+ if (dc_time_on == 0) {
+ aspeed_set_pwm_port_enable(priv->regmap, index, false);
+ } else {
+ if (dc_time_on == period)
+ dc_time_on = 0;
+
+ aspeed_set_pwm_port_duty_rising_falling(priv->regmap, index, 0,
+ dc_time_on);
+ aspeed_set_pwm_port_enable(priv->regmap, index, true);
+ }
+}
+
+static u32 aspeed_get_fan_tach_ch_measure_period(struct aspeed_pwm_tacho_data
+ *priv, u8 type)
+{
+ u32 clk;
+ u16 tacho_unit;
+ u8 clk_unit, div_h, div_l, tacho_div;
+
+ clk = priv->clk_freq;
+ clk_unit = priv->type_pwm_clock_unit[type];
+ div_h = priv->type_pwm_clock_division_h[type];
+ div_h = 0x1 << div_h;
+ div_l = priv->type_pwm_clock_division_l[type];
+ if (div_l == 0)
+ div_l = 1;
+ else
+ div_l = div_l * 2;
+
+ tacho_unit = priv->type_fan_tach_unit[type];
+ tacho_div = priv->type_fan_tach_clock_division[type];
+
+ tacho_div = 0x4 << (tacho_div * 2);
+ return clk / (clk_unit * div_h * div_l * tacho_div * tacho_unit);
+}
+
+static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tacho_data *priv,
+ u8 fan_tach_ch)
+{
+ u32 raw_data, tach_div, clk_source, msec, usec, val;
+ u8 fan_tach_ch_source, type, mode, both;
+ int ret;
+
+ regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0);
+ regmap_write(priv->regmap, ASPEED_PTCR_TRIGGER, 0x1 << fan_tach_ch);
+
+ fan_tach_ch_source = priv->fan_tach_ch_source[fan_tach_ch];
+ type = priv->pwm_port_type[fan_tach_ch_source];
+
+ msec = (1000 / aspeed_get_fan_tach_ch_measure_period(priv, type));
+ usec = msec * 1000;
+
+ ret = regmap_read_poll_timeout(
+ priv->regmap,
+ ASPEED_PTCR_RESULT,
+ val,
+ (val & RESULT_STATUS_MASK),
+ ASPEED_RPM_STATUS_SLEEP_USEC,
+ usec);
+
+ /* return -ETIMEDOUT if we didn't get an answer. */
+ if (ret)
+ return ret;
+
+ raw_data = val & RESULT_VALUE_MASK;
+ tach_div = priv->type_fan_tach_clock_division[type];
+ /*
+ * We need the mode to determine if the raw_data is double (from
+ * counting both edges).
+ */
+ mode = priv->type_fan_tach_mode[type];
+ both = (mode & BOTH_EDGES) ? 1 : 0;
+
+ tach_div = (0x4 << both) << (tach_div * 2);
+ clk_source = priv->clk_freq;
+
+ if (raw_data == 0)
+ return 0;
+
+ return (clk_source * 60) / (2 * raw_data * tach_div);
+}
+
+static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int index = sensor_attr->index;
+ int ret;
+ struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
+ long fan_ctrl;
+
+ ret = kstrtol(buf, 10, &fan_ctrl);
+ if (ret != 0)
+ return ret;
+
+ if (fan_ctrl < 0 || fan_ctrl > PWM_MAX)
+ return -EINVAL;
+
+ if (priv->pwm_port_fan_ctrl[index] == fan_ctrl)
+ return count;
+
+ priv->pwm_port_fan_ctrl[index] = fan_ctrl;
+ aspeed_set_pwm_port_fan_ctrl(priv, index, fan_ctrl);
+
+ return count;
+}
+
+static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int index = sensor_attr->index;
+ struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", priv->pwm_port_fan_ctrl[index]);
+}
+
+static ssize_t rpm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int index = sensor_attr->index;
+ int rpm;
+ struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
+
+ rpm = aspeed_get_fan_tach_ch_rpm(priv, index);
+ if (rpm < 0)
+ return rpm;
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static umode_t pwm_is_visible(struct kobject *kobj,
+ struct attribute *a, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
+
+ if (!priv->pwm_present[index])
+ return 0;
+ return a->mode;
+}
+
+static umode_t fan_dev_is_visible(struct kobject *kobj,
+ struct attribute *a, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
+
+ if (!priv->fan_tach_present[index])
+ return 0;
+ return a->mode;
+}
+
+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 SENSOR_DEVICE_ATTR_RW(pwm5, pwm, 4);
+static SENSOR_DEVICE_ATTR_RW(pwm6, pwm, 5);
+static SENSOR_DEVICE_ATTR_RW(pwm7, pwm, 6);
+static SENSOR_DEVICE_ATTR_RW(pwm8, pwm, 7);
+static struct attribute *pwm_dev_attrs[] = {
+ &sensor_dev_attr_pwm1.dev_attr.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_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm7.dev_attr.attr,
+ &sensor_dev_attr_pwm8.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group pwm_dev_group = {
+ .attrs = pwm_dev_attrs,
+ .is_visible = pwm_is_visible,
+};
+
+static SENSOR_DEVICE_ATTR_RO(fan1_input, rpm, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, rpm, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, rpm, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_input, rpm, 3);
+static SENSOR_DEVICE_ATTR_RO(fan5_input, rpm, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_input, rpm, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_input, rpm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_input, rpm, 7);
+static SENSOR_DEVICE_ATTR_RO(fan9_input, rpm, 8);
+static SENSOR_DEVICE_ATTR_RO(fan10_input, rpm, 9);
+static SENSOR_DEVICE_ATTR_RO(fan11_input, rpm, 10);
+static SENSOR_DEVICE_ATTR_RO(fan12_input, rpm, 11);
+static SENSOR_DEVICE_ATTR_RO(fan13_input, rpm, 12);
+static SENSOR_DEVICE_ATTR_RO(fan14_input, rpm, 13);
+static SENSOR_DEVICE_ATTR_RO(fan15_input, rpm, 14);
+static SENSOR_DEVICE_ATTR_RO(fan16_input, rpm, 15);
+static struct attribute *fan_dev_attrs[] = {
+ &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_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+ &sensor_dev_attr_fan9_input.dev_attr.attr,
+ &sensor_dev_attr_fan10_input.dev_attr.attr,
+ &sensor_dev_attr_fan11_input.dev_attr.attr,
+ &sensor_dev_attr_fan12_input.dev_attr.attr,
+ &sensor_dev_attr_fan13_input.dev_attr.attr,
+ &sensor_dev_attr_fan14_input.dev_attr.attr,
+ &sensor_dev_attr_fan15_input.dev_attr.attr,
+ &sensor_dev_attr_fan16_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group fan_dev_group = {
+ .attrs = fan_dev_attrs,
+ .is_visible = fan_dev_is_visible,
+};
+
+/*
+ * The clock type is type M :
+ * The PWM frequency = 24MHz / (type M clock division L bit *
+ * type M clock division H bit * (type M PWM period bit + 1))
+ */
+static void aspeed_create_type(struct aspeed_pwm_tacho_data *priv)
+{
+ priv->type_pwm_clock_division_h[TYPEM] = M_PWM_DIV_H;
+ priv->type_pwm_clock_division_l[TYPEM] = M_PWM_DIV_L;
+ priv->type_pwm_clock_unit[TYPEM] = M_PWM_PERIOD;
+ aspeed_set_pwm_clock_values(priv->regmap, TYPEM, M_PWM_DIV_H,
+ M_PWM_DIV_L, M_PWM_PERIOD);
+ aspeed_set_tacho_type_enable(priv->regmap, TYPEM, true);
+ priv->type_fan_tach_clock_division[TYPEM] = M_TACH_CLK_DIV;
+ priv->type_fan_tach_unit[TYPEM] = M_TACH_UNIT;
+ priv->type_fan_tach_mode[TYPEM] = M_TACH_MODE;
+ aspeed_set_tacho_type_values(priv->regmap, TYPEM, M_TACH_MODE,
+ M_TACH_UNIT, M_TACH_CLK_DIV);
+}
+
+static void aspeed_create_pwm_port(struct aspeed_pwm_tacho_data *priv,
+ u8 pwm_port)
+{
+ aspeed_set_pwm_port_enable(priv->regmap, pwm_port, true);
+ priv->pwm_present[pwm_port] = true;
+
+ priv->pwm_port_type[pwm_port] = TYPEM;
+ aspeed_set_pwm_port_type(priv->regmap, pwm_port, TYPEM);
+
+ priv->pwm_port_fan_ctrl[pwm_port] = INIT_FAN_CTRL;
+ aspeed_set_pwm_port_fan_ctrl(priv, pwm_port, INIT_FAN_CTRL);
+}
+
+static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tacho_data *priv,
+ u8 *fan_tach_ch,
+ int count,
+ u8 pwm_source)
+{
+ u8 val, index;
+
+ for (val = 0; val < count; val++) {
+ index = fan_tach_ch[val];
+ aspeed_set_fan_tach_ch_enable(priv->regmap, index, true);
+ priv->fan_tach_present[index] = true;
+ priv->fan_tach_ch_source[index] = pwm_source;
+ aspeed_set_fan_tach_ch_source(priv->regmap, index, pwm_source);
+ }
+}
+
+static int
+aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct aspeed_cooling_device *cdev = tcdev->devdata;
+
+ *state = cdev->max_state;
+
+ return 0;
+}
+
+static int
+aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct aspeed_cooling_device *cdev = tcdev->devdata;
+
+ *state = cdev->cur_state;
+
+ return 0;
+}
+
+static int
+aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long state)
+{
+ struct aspeed_cooling_device *cdev = tcdev->devdata;
+
+ if (state > cdev->max_state)
+ return -EINVAL;
+
+ cdev->cur_state = state;
+ cdev->priv->pwm_port_fan_ctrl[cdev->pwm_port] =
+ cdev->cooling_levels[cdev->cur_state];
+ aspeed_set_pwm_port_fan_ctrl(cdev->priv, cdev->pwm_port,
+ cdev->cooling_levels[cdev->cur_state]);
+
+ return 0;
+}
+
+static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = {
+ .get_max_state = aspeed_pwm_cz_get_max_state,
+ .get_cur_state = aspeed_pwm_cz_get_cur_state,
+ .set_cur_state = aspeed_pwm_cz_set_cur_state,
+};
+
+static int aspeed_create_pwm_cooling(struct device *dev,
+ struct device_node *child,
+ struct aspeed_pwm_tacho_data *priv,
+ u32 pwm_port, u8 num_levels)
+{
+ int ret;
+ struct aspeed_cooling_device *cdev;
+
+ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
+
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
+ if (!cdev->cooling_levels)
+ return -ENOMEM;
+
+ cdev->max_state = num_levels - 1;
+ ret = of_property_read_u8_array(child, "cooling-levels",
+ cdev->cooling_levels,
+ num_levels);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
+ return ret;
+ }
+ snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port);
+
+ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child,
+ cdev->name, cdev, &aspeed_pwm_cool_ops);
+ if (IS_ERR(cdev->tcdev))
+ return PTR_ERR(cdev->tcdev);
+
+ cdev->priv = priv;
+ cdev->pwm_port = pwm_port;
+
+ priv->cdev[pwm_port] = cdev;
+
+ return 0;
+}
+
+static int aspeed_create_fan(struct device *dev,
+ struct device_node *child,
+ struct aspeed_pwm_tacho_data *priv)
+{
+ u8 *fan_tach_ch;
+ u32 pwm_port;
+ int ret, count;
+
+ 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");
+
+ if (ret > 0) {
+ ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_port,
+ ret);
+ if (ret)
+ return ret;
+ }
+
+ count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch");
+ if (count < 1)
+ return -EINVAL;
+ fan_tach_ch = devm_kcalloc(dev, count, sizeof(*fan_tach_ch),
+ GFP_KERNEL);
+ if (!fan_tach_ch)
+ return -ENOMEM;
+ ret = of_property_read_u8_array(child, "aspeed,fan-tach-ch",
+ fan_tach_ch, count);
+ if (ret)
+ return ret;
+ aspeed_create_fan_tach_channel(priv, fan_tach_ch, count, pwm_port);
+
+ return 0;
+}
+
+static void aspeed_pwm_tacho_remove(void *data)
+{
+ struct aspeed_pwm_tacho_data *priv = data;
+
+ reset_control_assert(priv->rst);
+}
+
+static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np, *child;
+ struct aspeed_pwm_tacho_data *priv;
+ void __iomem *regs;
+ struct device *hwmon;
+ struct clk *clk;
+ int ret;
+
+ np = dev->of_node;
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->regmap = devm_regmap_init(dev, NULL, (__force void *)regs,
+ &aspeed_pwm_tacho_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(priv->rst)) {
+ dev_err(dev,
+ "missing or invalid reset controller device tree entry");
+ return PTR_ERR(priv->rst);
+ }
+ reset_control_deassert(priv->rst);
+
+ ret = devm_add_action_or_reset(dev, aspeed_pwm_tacho_remove, priv);
+ if (ret)
+ return ret;
+
+ regmap_write(priv->regmap, ASPEED_PTCR_TACH_SOURCE, 0);
+ regmap_write(priv->regmap, ASPEED_PTCR_TACH_SOURCE_EXT, 0);
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk))
+ return -ENODEV;
+ priv->clk_freq = clk_get_rate(clk);
+ aspeed_set_clock_enable(priv->regmap, true);
+ aspeed_set_clock_source(priv->regmap, 0);
+
+ aspeed_create_type(priv);
+
+ for_each_child_of_node(np, child) {
+ ret = aspeed_create_fan(dev, child, priv);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+
+ priv->groups[0] = &pwm_dev_group;
+ priv->groups[1] = &fan_dev_group;
+ priv->groups[2] = NULL;
+ hwmon = devm_hwmon_device_register_with_groups(dev,
+ "aspeed_pwm_tacho",
+ priv, priv->groups);
+ return PTR_ERR_OR_ZERO(hwmon);
+}
+
+static const struct of_device_id of_pwm_tacho_match_table[] = {
+ { .compatible = "aspeed,ast2400-pwm-tacho", },
+ { .compatible = "aspeed,ast2500-pwm-tacho", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_pwm_tacho_match_table);
+
+static struct platform_driver aspeed_pwm_tacho_driver = {
+ .probe = aspeed_pwm_tacho_probe,
+ .driver = {
+ .name = "aspeed_pwm_tacho",
+ .of_match_table = of_pwm_tacho_match_table,
+ },
+};
+
+module_platform_driver(aspeed_pwm_tacho_driver);
+
+MODULE_AUTHOR("Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>");
+MODULE_DESCRIPTION("ASPEED PWM and Fan Tacho device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
new file mode 100644
index 000000000..b4d65916b
--- /dev/null
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -0,0 +1,1043 @@
+// 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,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .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);
+ if (!ec_data->sensors)
+ return -ENOMEM;
+
+ 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_atk0110.c b/drivers/hwmon/asus_atk0110.c
new file mode 100644
index 000000000..ff64a39d5
--- /dev/null
+++ b/drivers/hwmon/asus_atk0110.c
@@ -0,0 +1,1394 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2007-2009 Luca Tettamanti <kronos.it@gmail.com>
+ *
+ * See COPYING in the top level directory of the kernel tree.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/hwmon.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dmi.h>
+#include <linux/jiffies.h>
+#include <linux/err.h>
+#include <linux/acpi.h>
+
+#define ATK_HID "ATK0110"
+
+static bool new_if;
+module_param(new_if, bool, 0);
+MODULE_PARM_DESC(new_if, "Override detection heuristic and force the use of the new ATK0110 interface");
+
+static const struct dmi_system_id __initconst atk_force_new_if[] = {
+ {
+ /* Old interface has broken MCH temp monitoring */
+ .ident = "Asus Sabertooth X58",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "SABERTOOTH X58")
+ }
+ }, {
+ /* Old interface reads the same sensor for fan0 and fan1 */
+ .ident = "Asus M5A78L",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "M5A78L")
+ }
+ },
+ { }
+};
+
+/*
+ * Minimum time between readings, enforced in order to avoid
+ * hogging the CPU.
+ */
+#define CACHE_TIME HZ
+
+#define BOARD_ID "MBIF"
+#define METHOD_ENUMERATE "GGRP"
+#define METHOD_READ "GITM"
+#define METHOD_WRITE "SITM"
+#define METHOD_OLD_READ_TMP "RTMP"
+#define METHOD_OLD_READ_VLT "RVLT"
+#define METHOD_OLD_READ_FAN "RFAN"
+#define METHOD_OLD_ENUM_TMP "TSIF"
+#define METHOD_OLD_ENUM_VLT "VSIF"
+#define METHOD_OLD_ENUM_FAN "FSIF"
+
+#define ATK_MUX_HWMON 0x00000006ULL
+#define ATK_MUX_MGMT 0x00000011ULL
+
+#define ATK_CLASS_MASK 0xff000000ULL
+#define ATK_CLASS_FREQ_CTL 0x03000000ULL
+#define ATK_CLASS_FAN_CTL 0x04000000ULL
+#define ATK_CLASS_HWMON 0x06000000ULL
+#define ATK_CLASS_MGMT 0x11000000ULL
+
+#define ATK_TYPE_MASK 0x00ff0000ULL
+#define HWMON_TYPE_VOLT 0x00020000ULL
+#define HWMON_TYPE_TEMP 0x00030000ULL
+#define HWMON_TYPE_FAN 0x00040000ULL
+
+#define ATK_ELEMENT_ID_MASK 0x0000ffffULL
+
+#define ATK_EC_ID 0x11060004ULL
+
+enum atk_pack_member {
+ HWMON_PACK_FLAGS,
+ HWMON_PACK_NAME,
+ HWMON_PACK_LIMIT1,
+ HWMON_PACK_LIMIT2,
+ HWMON_PACK_ENABLE
+};
+
+/* New package format */
+#define _HWMON_NEW_PACK_SIZE 7
+#define _HWMON_NEW_PACK_FLAGS 0
+#define _HWMON_NEW_PACK_NAME 1
+#define _HWMON_NEW_PACK_UNK1 2
+#define _HWMON_NEW_PACK_UNK2 3
+#define _HWMON_NEW_PACK_LIMIT1 4
+#define _HWMON_NEW_PACK_LIMIT2 5
+#define _HWMON_NEW_PACK_ENABLE 6
+
+/* Old package format */
+#define _HWMON_OLD_PACK_SIZE 5
+#define _HWMON_OLD_PACK_FLAGS 0
+#define _HWMON_OLD_PACK_NAME 1
+#define _HWMON_OLD_PACK_LIMIT1 2
+#define _HWMON_OLD_PACK_LIMIT2 3
+#define _HWMON_OLD_PACK_ENABLE 4
+
+
+struct atk_data {
+ struct device *hwmon_dev;
+ acpi_handle atk_handle;
+ struct acpi_device *acpi_dev;
+
+ bool old_interface;
+
+ /* old interface */
+ acpi_handle rtmp_handle;
+ acpi_handle rvlt_handle;
+ acpi_handle rfan_handle;
+ /* new interface */
+ acpi_handle enumerate_handle;
+ acpi_handle read_handle;
+ acpi_handle write_handle;
+
+ bool disable_ec;
+
+ int voltage_count;
+ int temperature_count;
+ int fan_count;
+ struct list_head sensor_list;
+ struct attribute_group attr_group;
+ const struct attribute_group *attr_groups[2];
+
+ struct {
+ struct dentry *root;
+ u32 id;
+ } debugfs;
+};
+
+
+typedef ssize_t (*sysfs_show_func)(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static const struct acpi_device_id atk_ids[] = {
+ {ATK_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, atk_ids);
+
+#define ATTR_NAME_SIZE 16 /* Worst case is "tempN_input" */
+
+struct atk_sensor_data {
+ struct list_head list;
+ struct atk_data *data;
+ struct device_attribute label_attr;
+ struct device_attribute input_attr;
+ struct device_attribute limit1_attr;
+ struct device_attribute limit2_attr;
+ char label_attr_name[ATTR_NAME_SIZE];
+ char input_attr_name[ATTR_NAME_SIZE];
+ char limit1_attr_name[ATTR_NAME_SIZE];
+ char limit2_attr_name[ATTR_NAME_SIZE];
+ u64 id;
+ u64 type;
+ u64 limit1;
+ u64 limit2;
+ u64 cached_value;
+ unsigned long last_updated; /* in jiffies */
+ bool is_valid;
+ char const *acpi_name;
+};
+
+/*
+ * Return buffer format:
+ * [0-3] "value" is valid flag
+ * [4-7] value
+ * [8- ] unknown stuff on newer mobos
+ */
+struct atk_acpi_ret_buffer {
+ u32 flags;
+ u32 value;
+ u8 data[];
+};
+
+/* Input buffer used for GITM and SITM methods */
+struct atk_acpi_input_buf {
+ u32 id;
+ u32 param1;
+ u32 param2;
+};
+
+static int atk_add(struct acpi_device *device);
+static int atk_remove(struct acpi_device *device);
+static void atk_print_sensor(struct atk_data *data, union acpi_object *obj);
+static int atk_read_value(struct atk_sensor_data *sensor, u64 *value);
+
+static struct acpi_driver atk_driver = {
+ .name = ATK_HID,
+ .class = "hwmon",
+ .ids = atk_ids,
+ .ops = {
+ .add = atk_add,
+ .remove = atk_remove,
+ },
+};
+
+#define input_to_atk_sensor(attr) \
+ container_of(attr, struct atk_sensor_data, input_attr)
+
+#define label_to_atk_sensor(attr) \
+ container_of(attr, struct atk_sensor_data, label_attr)
+
+#define limit1_to_atk_sensor(attr) \
+ container_of(attr, struct atk_sensor_data, limit1_attr)
+
+#define limit2_to_atk_sensor(attr) \
+ container_of(attr, struct atk_sensor_data, limit2_attr)
+
+static ssize_t atk_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atk_sensor_data *s = input_to_atk_sensor(attr);
+ u64 value;
+ int err;
+
+ err = atk_read_value(s, &value);
+ if (err)
+ return err;
+
+ if (s->type == HWMON_TYPE_TEMP)
+ /* ACPI returns decidegree */
+ value *= 100;
+
+ return sprintf(buf, "%llu\n", value);
+}
+
+static ssize_t atk_label_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atk_sensor_data *s = label_to_atk_sensor(attr);
+
+ return sprintf(buf, "%s\n", s->acpi_name);
+}
+
+static ssize_t atk_limit1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atk_sensor_data *s = limit1_to_atk_sensor(attr);
+ u64 value = s->limit1;
+
+ if (s->type == HWMON_TYPE_TEMP)
+ value *= 100;
+
+ return sprintf(buf, "%lld\n", value);
+}
+
+static ssize_t atk_limit2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atk_sensor_data *s = limit2_to_atk_sensor(attr);
+ u64 value = s->limit2;
+
+ if (s->type == HWMON_TYPE_TEMP)
+ value *= 100;
+
+ return sprintf(buf, "%lld\n", value);
+}
+
+static void atk_init_attribute(struct device_attribute *attr, char *name,
+ sysfs_show_func show)
+{
+ sysfs_attr_init(&attr->attr);
+ attr->attr.name = name;
+ attr->attr.mode = 0444;
+ attr->show = show;
+ attr->store = NULL;
+}
+
+
+static union acpi_object *atk_get_pack_member(struct atk_data *data,
+ union acpi_object *pack,
+ enum atk_pack_member m)
+{
+ bool old_if = data->old_interface;
+ int offset;
+
+ switch (m) {
+ case HWMON_PACK_FLAGS:
+ offset = old_if ? _HWMON_OLD_PACK_FLAGS : _HWMON_NEW_PACK_FLAGS;
+ break;
+ case HWMON_PACK_NAME:
+ offset = old_if ? _HWMON_OLD_PACK_NAME : _HWMON_NEW_PACK_NAME;
+ break;
+ case HWMON_PACK_LIMIT1:
+ offset = old_if ? _HWMON_OLD_PACK_LIMIT1 :
+ _HWMON_NEW_PACK_LIMIT1;
+ break;
+ case HWMON_PACK_LIMIT2:
+ offset = old_if ? _HWMON_OLD_PACK_LIMIT2 :
+ _HWMON_NEW_PACK_LIMIT2;
+ break;
+ case HWMON_PACK_ENABLE:
+ offset = old_if ? _HWMON_OLD_PACK_ENABLE :
+ _HWMON_NEW_PACK_ENABLE;
+ break;
+ default:
+ return NULL;
+ }
+
+ return &pack->package.elements[offset];
+}
+
+
+/*
+ * New package format is:
+ * - flag (int)
+ * class - used for de-muxing the request to the correct GITn
+ * type (volt, temp, fan)
+ * sensor id |
+ * sensor id - used for de-muxing the request _inside_ the GITn
+ * - name (str)
+ * - unknown (int)
+ * - unknown (int)
+ * - limit1 (int)
+ * - limit2 (int)
+ * - enable (int)
+ *
+ * The old package has the same format but it's missing the two unknown fields.
+ */
+static int validate_hwmon_pack(struct atk_data *data, union acpi_object *obj)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *tmp;
+ bool old_if = data->old_interface;
+ int const expected_size = old_if ? _HWMON_OLD_PACK_SIZE :
+ _HWMON_NEW_PACK_SIZE;
+
+ if (obj->type != ACPI_TYPE_PACKAGE) {
+ dev_warn(dev, "Invalid type: %d\n", obj->type);
+ return -EINVAL;
+ }
+
+ if (obj->package.count != expected_size) {
+ dev_warn(dev, "Invalid package size: %d, expected: %d\n",
+ obj->package.count, expected_size);
+ return -EINVAL;
+ }
+
+ tmp = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS);
+ if (tmp->type != ACPI_TYPE_INTEGER) {
+ dev_warn(dev, "Invalid type (flag): %d\n", tmp->type);
+ return -EINVAL;
+ }
+
+ tmp = atk_get_pack_member(data, obj, HWMON_PACK_NAME);
+ if (tmp->type != ACPI_TYPE_STRING) {
+ dev_warn(dev, "Invalid type (name): %d\n", tmp->type);
+ return -EINVAL;
+ }
+
+ /* Don't check... we don't know what they're useful for anyway */
+#if 0
+ tmp = &obj->package.elements[HWMON_PACK_UNK1];
+ if (tmp->type != ACPI_TYPE_INTEGER) {
+ dev_warn(dev, "Invalid type (unk1): %d\n", tmp->type);
+ return -EINVAL;
+ }
+
+ tmp = &obj->package.elements[HWMON_PACK_UNK2];
+ if (tmp->type != ACPI_TYPE_INTEGER) {
+ dev_warn(dev, "Invalid type (unk2): %d\n", tmp->type);
+ return -EINVAL;
+ }
+#endif
+
+ tmp = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT1);
+ if (tmp->type != ACPI_TYPE_INTEGER) {
+ dev_warn(dev, "Invalid type (limit1): %d\n", tmp->type);
+ return -EINVAL;
+ }
+
+ tmp = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT2);
+ if (tmp->type != ACPI_TYPE_INTEGER) {
+ dev_warn(dev, "Invalid type (limit2): %d\n", tmp->type);
+ return -EINVAL;
+ }
+
+ tmp = atk_get_pack_member(data, obj, HWMON_PACK_ENABLE);
+ if (tmp->type != ACPI_TYPE_INTEGER) {
+ dev_warn(dev, "Invalid type (enable): %d\n", tmp->type);
+ return -EINVAL;
+ }
+
+ atk_print_sensor(data, obj);
+
+ return 0;
+}
+
+#ifdef DEBUG
+static char const *atk_sensor_type(union acpi_object *flags)
+{
+ u64 type = flags->integer.value & ATK_TYPE_MASK;
+ char const *what;
+
+ switch (type) {
+ case HWMON_TYPE_VOLT:
+ what = "voltage";
+ break;
+ case HWMON_TYPE_TEMP:
+ what = "temperature";
+ break;
+ case HWMON_TYPE_FAN:
+ what = "fan";
+ break;
+ default:
+ what = "unknown";
+ break;
+ }
+
+ return what;
+}
+#endif
+
+static void atk_print_sensor(struct atk_data *data, union acpi_object *obj)
+{
+#ifdef DEBUG
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *flags;
+ union acpi_object *name;
+ union acpi_object *limit1;
+ union acpi_object *limit2;
+ union acpi_object *enable;
+ char const *what;
+
+ flags = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS);
+ name = atk_get_pack_member(data, obj, HWMON_PACK_NAME);
+ limit1 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT1);
+ limit2 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT2);
+ enable = atk_get_pack_member(data, obj, HWMON_PACK_ENABLE);
+
+ what = atk_sensor_type(flags);
+
+ dev_dbg(dev, "%s: %#llx %s [%llu-%llu] %s\n", what,
+ flags->integer.value,
+ name->string.pointer,
+ limit1->integer.value, limit2->integer.value,
+ enable->integer.value ? "enabled" : "disabled");
+#endif
+}
+
+static int atk_read_value_old(struct atk_sensor_data *sensor, u64 *value)
+{
+ struct atk_data *data = sensor->data;
+ struct device *dev = &data->acpi_dev->dev;
+ struct acpi_object_list params;
+ union acpi_object id;
+ acpi_status status;
+ acpi_handle method;
+
+ switch (sensor->type) {
+ case HWMON_TYPE_VOLT:
+ method = data->rvlt_handle;
+ break;
+ case HWMON_TYPE_TEMP:
+ method = data->rtmp_handle;
+ break;
+ case HWMON_TYPE_FAN:
+ method = data->rfan_handle;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ id.type = ACPI_TYPE_INTEGER;
+ id.integer.value = sensor->id;
+
+ params.count = 1;
+ params.pointer = &id;
+
+ status = acpi_evaluate_integer(method, NULL, &params, value);
+ if (status != AE_OK) {
+ dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
+ acpi_format_exception(status));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static union acpi_object *atk_ggrp(struct atk_data *data, u16 mux)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ struct acpi_buffer buf;
+ acpi_status ret;
+ struct acpi_object_list params;
+ union acpi_object id;
+ union acpi_object *pack;
+
+ id.type = ACPI_TYPE_INTEGER;
+ id.integer.value = mux;
+ params.count = 1;
+ params.pointer = &id;
+
+ buf.length = ACPI_ALLOCATE_BUFFER;
+ ret = acpi_evaluate_object(data->enumerate_handle, NULL, &params, &buf);
+ if (ret != AE_OK) {
+ dev_err(dev, "GGRP[%#x] ACPI exception: %s\n", mux,
+ acpi_format_exception(ret));
+ return ERR_PTR(-EIO);
+ }
+ pack = buf.pointer;
+ if (pack->type != ACPI_TYPE_PACKAGE) {
+ /* Execution was successful, but the id was not found */
+ ACPI_FREE(pack);
+ return ERR_PTR(-ENOENT);
+ }
+
+ if (pack->package.count < 1) {
+ dev_err(dev, "GGRP[%#x] package is too small\n", mux);
+ ACPI_FREE(pack);
+ return ERR_PTR(-EIO);
+ }
+ return pack;
+}
+
+static union acpi_object *atk_gitm(struct atk_data *data, u64 id)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ struct atk_acpi_input_buf buf;
+ union acpi_object tmp;
+ struct acpi_object_list params;
+ struct acpi_buffer ret;
+ union acpi_object *obj;
+ acpi_status status;
+
+ buf.id = id;
+ buf.param1 = 0;
+ buf.param2 = 0;
+
+ tmp.type = ACPI_TYPE_BUFFER;
+ tmp.buffer.pointer = (u8 *)&buf;
+ tmp.buffer.length = sizeof(buf);
+
+ params.count = 1;
+ params.pointer = (void *)&tmp;
+
+ ret.length = ACPI_ALLOCATE_BUFFER;
+ status = acpi_evaluate_object_typed(data->read_handle, NULL, &params,
+ &ret, ACPI_TYPE_BUFFER);
+ if (status != AE_OK) {
+ dev_warn(dev, "GITM[%#llx] ACPI exception: %s\n", id,
+ acpi_format_exception(status));
+ return ERR_PTR(-EIO);
+ }
+ obj = ret.pointer;
+
+ /* Sanity check */
+ if (obj->buffer.length < 8) {
+ dev_warn(dev, "Unexpected ASBF length: %u\n",
+ obj->buffer.length);
+ ACPI_FREE(obj);
+ return ERR_PTR(-EIO);
+ }
+ return obj;
+}
+
+static union acpi_object *atk_sitm(struct atk_data *data,
+ struct atk_acpi_input_buf *buf)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ struct acpi_object_list params;
+ union acpi_object tmp;
+ struct acpi_buffer ret;
+ union acpi_object *obj;
+ acpi_status status;
+
+ tmp.type = ACPI_TYPE_BUFFER;
+ tmp.buffer.pointer = (u8 *)buf;
+ tmp.buffer.length = sizeof(*buf);
+
+ params.count = 1;
+ params.pointer = &tmp;
+
+ ret.length = ACPI_ALLOCATE_BUFFER;
+ status = acpi_evaluate_object_typed(data->write_handle, NULL, &params,
+ &ret, ACPI_TYPE_BUFFER);
+ if (status != AE_OK) {
+ dev_warn(dev, "SITM[%#x] ACPI exception: %s\n", buf->id,
+ acpi_format_exception(status));
+ return ERR_PTR(-EIO);
+ }
+ obj = ret.pointer;
+
+ /* Sanity check */
+ if (obj->buffer.length < 8) {
+ dev_warn(dev, "Unexpected ASBF length: %u\n",
+ obj->buffer.length);
+ ACPI_FREE(obj);
+ return ERR_PTR(-EIO);
+ }
+ return obj;
+}
+
+static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value)
+{
+ struct atk_data *data = sensor->data;
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *obj;
+ struct atk_acpi_ret_buffer *buf;
+ int err = 0;
+
+ obj = atk_gitm(data, sensor->id);
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+
+ buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer;
+ if (buf->flags == 0) {
+ /*
+ * The reading is not valid, possible causes:
+ * - sensor failure
+ * - enumeration was FUBAR (and we didn't notice)
+ */
+ dev_warn(dev, "Read failed, sensor = %#llx\n", sensor->id);
+ err = -EIO;
+ goto out;
+ }
+
+ *value = buf->value;
+out:
+ ACPI_FREE(obj);
+ return err;
+}
+
+static int atk_read_value(struct atk_sensor_data *sensor, u64 *value)
+{
+ int err;
+
+ if (!sensor->is_valid ||
+ time_after(jiffies, sensor->last_updated + CACHE_TIME)) {
+ if (sensor->data->old_interface)
+ err = atk_read_value_old(sensor, value);
+ else
+ err = atk_read_value_new(sensor, value);
+
+ if (err)
+ return err;
+
+ sensor->is_valid = true;
+ sensor->last_updated = jiffies;
+ sensor->cached_value = *value;
+ } else {
+ *value = sensor->cached_value;
+ err = 0;
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int atk_debugfs_gitm_get(void *p, u64 *val)
+{
+ struct atk_data *data = p;
+ union acpi_object *ret;
+ struct atk_acpi_ret_buffer *buf;
+ int err = 0;
+
+ if (!data->read_handle)
+ return -ENODEV;
+
+ if (!data->debugfs.id)
+ return -EINVAL;
+
+ ret = atk_gitm(data, data->debugfs.id);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+
+ buf = (struct atk_acpi_ret_buffer *)ret->buffer.pointer;
+ if (buf->flags)
+ *val = buf->value;
+ else
+ err = -EIO;
+
+ ACPI_FREE(ret);
+ return err;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(atk_debugfs_gitm, atk_debugfs_gitm_get, NULL,
+ "0x%08llx\n");
+
+static int atk_acpi_print(char *buf, size_t sz, union acpi_object *obj)
+{
+ int ret = 0;
+
+ switch (obj->type) {
+ case ACPI_TYPE_INTEGER:
+ ret = snprintf(buf, sz, "0x%08llx\n", obj->integer.value);
+ break;
+ case ACPI_TYPE_STRING:
+ ret = snprintf(buf, sz, "%s\n", obj->string.pointer);
+ break;
+ }
+
+ return ret;
+}
+
+static void atk_pack_print(char *buf, size_t sz, union acpi_object *pack)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < pack->package.count; i++) {
+ union acpi_object *obj = &pack->package.elements[i];
+
+ ret = atk_acpi_print(buf, sz, obj);
+ if (ret >= sz)
+ break;
+ buf += ret;
+ sz -= ret;
+ }
+}
+
+static int atk_debugfs_ggrp_open(struct inode *inode, struct file *file)
+{
+ struct atk_data *data = inode->i_private;
+ char *buf = NULL;
+ union acpi_object *ret;
+ u8 cls;
+ int i;
+
+ if (!data->enumerate_handle)
+ return -ENODEV;
+ if (!data->debugfs.id)
+ return -EINVAL;
+
+ cls = (data->debugfs.id & 0xff000000) >> 24;
+ ret = atk_ggrp(data, cls);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+
+ for (i = 0; i < ret->package.count; i++) {
+ union acpi_object *pack = &ret->package.elements[i];
+ union acpi_object *id;
+
+ if (pack->type != ACPI_TYPE_PACKAGE)
+ continue;
+ if (!pack->package.count)
+ continue;
+ id = &pack->package.elements[0];
+ if (id->integer.value == data->debugfs.id) {
+ /* Print the package */
+ buf = kzalloc(512, GFP_KERNEL);
+ if (!buf) {
+ ACPI_FREE(ret);
+ return -ENOMEM;
+ }
+ atk_pack_print(buf, 512, pack);
+ break;
+ }
+ }
+ ACPI_FREE(ret);
+
+ if (!buf)
+ return -EINVAL;
+
+ file->private_data = buf;
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t atk_debugfs_ggrp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char *str = file->private_data;
+ size_t len = strlen(str);
+
+ return simple_read_from_buffer(buf, count, pos, str, len);
+}
+
+static int atk_debugfs_ggrp_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static const struct file_operations atk_debugfs_ggrp_fops = {
+ .read = atk_debugfs_ggrp_read,
+ .open = atk_debugfs_ggrp_open,
+ .release = atk_debugfs_ggrp_release,
+ .llseek = no_llseek,
+};
+
+static void atk_debugfs_init(struct atk_data *data)
+{
+ struct dentry *d;
+
+ data->debugfs.id = 0;
+
+ d = debugfs_create_dir("asus_atk0110", NULL);
+
+ debugfs_create_x32("id", 0600, d, &data->debugfs.id);
+ debugfs_create_file_unsafe("gitm", 0400, d, data, &atk_debugfs_gitm);
+ debugfs_create_file("ggrp", 0400, d, data, &atk_debugfs_ggrp_fops);
+
+ data->debugfs.root = d;
+}
+
+static void atk_debugfs_cleanup(struct atk_data *data)
+{
+ debugfs_remove_recursive(data->debugfs.root);
+}
+
+#else /* CONFIG_DEBUG_FS */
+
+static void atk_debugfs_init(struct atk_data *data)
+{
+}
+
+static void atk_debugfs_cleanup(struct atk_data *data)
+{
+}
+#endif
+
+static int atk_add_sensor(struct atk_data *data, union acpi_object *obj)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *flags;
+ union acpi_object *name;
+ union acpi_object *limit1;
+ union acpi_object *limit2;
+ union acpi_object *enable;
+ struct atk_sensor_data *sensor;
+ char const *base_name;
+ char const *limit1_name;
+ char const *limit2_name;
+ u64 type;
+ int err;
+ int *num;
+ int start;
+
+ if (obj->type != ACPI_TYPE_PACKAGE) {
+ /* wft is this? */
+ dev_warn(dev, "Unknown type for ACPI object: (%d)\n",
+ obj->type);
+ return -EINVAL;
+ }
+
+ err = validate_hwmon_pack(data, obj);
+ if (err)
+ return err;
+
+ /* Ok, we have a valid hwmon package */
+ type = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS)->integer.value
+ & ATK_TYPE_MASK;
+
+ switch (type) {
+ case HWMON_TYPE_VOLT:
+ base_name = "in";
+ limit1_name = "min";
+ limit2_name = "max";
+ num = &data->voltage_count;
+ start = 0;
+ break;
+ case HWMON_TYPE_TEMP:
+ base_name = "temp";
+ limit1_name = "max";
+ limit2_name = "crit";
+ num = &data->temperature_count;
+ start = 1;
+ break;
+ case HWMON_TYPE_FAN:
+ base_name = "fan";
+ limit1_name = "min";
+ limit2_name = "max";
+ num = &data->fan_count;
+ start = 1;
+ break;
+ default:
+ dev_warn(dev, "Unknown sensor type: %#llx\n", type);
+ return -EINVAL;
+ }
+
+ enable = atk_get_pack_member(data, obj, HWMON_PACK_ENABLE);
+ if (!enable->integer.value)
+ /* sensor is disabled */
+ return 0;
+
+ flags = atk_get_pack_member(data, obj, HWMON_PACK_FLAGS);
+ name = atk_get_pack_member(data, obj, HWMON_PACK_NAME);
+ limit1 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT1);
+ limit2 = atk_get_pack_member(data, obj, HWMON_PACK_LIMIT2);
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->acpi_name = devm_kstrdup(dev, name->string.pointer, GFP_KERNEL);
+ if (!sensor->acpi_name)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&sensor->list);
+ sensor->type = type;
+ sensor->data = data;
+ sensor->id = flags->integer.value;
+ sensor->limit1 = limit1->integer.value;
+ if (data->old_interface)
+ sensor->limit2 = limit2->integer.value;
+ else
+ /* The upper limit is expressed as delta from lower limit */
+ sensor->limit2 = sensor->limit1 + limit2->integer.value;
+
+ snprintf(sensor->input_attr_name, ATTR_NAME_SIZE,
+ "%s%d_input", base_name, start + *num);
+ atk_init_attribute(&sensor->input_attr,
+ sensor->input_attr_name,
+ atk_input_show);
+
+ snprintf(sensor->label_attr_name, ATTR_NAME_SIZE,
+ "%s%d_label", base_name, start + *num);
+ atk_init_attribute(&sensor->label_attr,
+ sensor->label_attr_name,
+ atk_label_show);
+
+ snprintf(sensor->limit1_attr_name, ATTR_NAME_SIZE,
+ "%s%d_%s", base_name, start + *num, limit1_name);
+ atk_init_attribute(&sensor->limit1_attr,
+ sensor->limit1_attr_name,
+ atk_limit1_show);
+
+ snprintf(sensor->limit2_attr_name, ATTR_NAME_SIZE,
+ "%s%d_%s", base_name, start + *num, limit2_name);
+ atk_init_attribute(&sensor->limit2_attr,
+ sensor->limit2_attr_name,
+ atk_limit2_show);
+
+ list_add(&sensor->list, &data->sensor_list);
+ (*num)++;
+
+ return 1;
+}
+
+static int atk_enumerate_old_hwmon(struct atk_data *data)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ struct acpi_buffer buf;
+ union acpi_object *pack;
+ acpi_status status;
+ int i, ret;
+ int count = 0;
+
+ /* Voltages */
+ buf.length = ACPI_ALLOCATE_BUFFER;
+ status = acpi_evaluate_object_typed(data->atk_handle,
+ METHOD_OLD_ENUM_VLT, NULL, &buf, ACPI_TYPE_PACKAGE);
+ if (status != AE_OK) {
+ dev_warn(dev, METHOD_OLD_ENUM_VLT ": ACPI exception: %s\n",
+ acpi_format_exception(status));
+
+ return -ENODEV;
+ }
+
+ pack = buf.pointer;
+ for (i = 1; i < pack->package.count; i++) {
+ union acpi_object *obj = &pack->package.elements[i];
+
+ ret = atk_add_sensor(data, obj);
+ if (ret > 0)
+ count++;
+ }
+ ACPI_FREE(buf.pointer);
+
+ /* Temperatures */
+ buf.length = ACPI_ALLOCATE_BUFFER;
+ status = acpi_evaluate_object_typed(data->atk_handle,
+ METHOD_OLD_ENUM_TMP, NULL, &buf, ACPI_TYPE_PACKAGE);
+ if (status != AE_OK) {
+ dev_warn(dev, METHOD_OLD_ENUM_TMP ": ACPI exception: %s\n",
+ acpi_format_exception(status));
+
+ return -ENODEV;
+ }
+
+ pack = buf.pointer;
+ for (i = 1; i < pack->package.count; i++) {
+ union acpi_object *obj = &pack->package.elements[i];
+
+ ret = atk_add_sensor(data, obj);
+ if (ret > 0)
+ count++;
+ }
+ ACPI_FREE(buf.pointer);
+
+ /* Fans */
+ buf.length = ACPI_ALLOCATE_BUFFER;
+ status = acpi_evaluate_object_typed(data->atk_handle,
+ METHOD_OLD_ENUM_FAN, NULL, &buf, ACPI_TYPE_PACKAGE);
+ if (status != AE_OK) {
+ dev_warn(dev, METHOD_OLD_ENUM_FAN ": ACPI exception: %s\n",
+ acpi_format_exception(status));
+
+ return -ENODEV;
+ }
+
+ pack = buf.pointer;
+ for (i = 1; i < pack->package.count; i++) {
+ union acpi_object *obj = &pack->package.elements[i];
+
+ ret = atk_add_sensor(data, obj);
+ if (ret > 0)
+ count++;
+ }
+ ACPI_FREE(buf.pointer);
+
+ return count;
+}
+
+static int atk_ec_present(struct atk_data *data)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *pack;
+ union acpi_object *ec;
+ int ret;
+ int i;
+
+ pack = atk_ggrp(data, ATK_MUX_MGMT);
+ if (IS_ERR(pack)) {
+ if (PTR_ERR(pack) == -ENOENT) {
+ /* The MGMT class does not exists - that's ok */
+ dev_dbg(dev, "Class %#llx not found\n", ATK_MUX_MGMT);
+ return 0;
+ }
+ return PTR_ERR(pack);
+ }
+
+ /* Search the EC */
+ ec = NULL;
+ for (i = 0; i < pack->package.count; i++) {
+ union acpi_object *obj = &pack->package.elements[i];
+ union acpi_object *id;
+
+ if (obj->type != ACPI_TYPE_PACKAGE)
+ continue;
+
+ id = &obj->package.elements[0];
+ if (id->type != ACPI_TYPE_INTEGER)
+ continue;
+
+ if (id->integer.value == ATK_EC_ID) {
+ ec = obj;
+ break;
+ }
+ }
+
+ ret = (ec != NULL);
+ if (!ret)
+ /* The system has no EC */
+ dev_dbg(dev, "EC not found\n");
+
+ ACPI_FREE(pack);
+ return ret;
+}
+
+static int atk_ec_enabled(struct atk_data *data)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *obj;
+ struct atk_acpi_ret_buffer *buf;
+ int err;
+
+ obj = atk_gitm(data, ATK_EC_ID);
+ if (IS_ERR(obj)) {
+ dev_err(dev, "Unable to query EC status\n");
+ return PTR_ERR(obj);
+ }
+ buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer;
+
+ if (buf->flags == 0) {
+ dev_err(dev, "Unable to query EC status\n");
+ err = -EIO;
+ } else {
+ err = (buf->value != 0);
+ dev_dbg(dev, "EC is %sabled\n",
+ err ? "en" : "dis");
+ }
+
+ ACPI_FREE(obj);
+ return err;
+}
+
+static int atk_ec_ctl(struct atk_data *data, int enable)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *obj;
+ struct atk_acpi_input_buf sitm;
+ struct atk_acpi_ret_buffer *ec_ret;
+ int err = 0;
+
+ sitm.id = ATK_EC_ID;
+ sitm.param1 = enable;
+ sitm.param2 = 0;
+
+ obj = atk_sitm(data, &sitm);
+ if (IS_ERR(obj)) {
+ dev_err(dev, "Failed to %sable the EC\n",
+ enable ? "en" : "dis");
+ return PTR_ERR(obj);
+ }
+ ec_ret = (struct atk_acpi_ret_buffer *)obj->buffer.pointer;
+ if (ec_ret->flags == 0) {
+ dev_err(dev, "Failed to %sable the EC\n",
+ enable ? "en" : "dis");
+ err = -EIO;
+ } else {
+ dev_info(dev, "EC %sabled\n",
+ enable ? "en" : "dis");
+ }
+
+ ACPI_FREE(obj);
+ return err;
+}
+
+static int atk_enumerate_new_hwmon(struct atk_data *data)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ union acpi_object *pack;
+ int err;
+ int i;
+
+ err = atk_ec_present(data);
+ if (err < 0)
+ return err;
+ if (err) {
+ err = atk_ec_enabled(data);
+ if (err < 0)
+ return err;
+ /* If the EC was disabled we will disable it again on unload */
+ data->disable_ec = err;
+
+ err = atk_ec_ctl(data, 1);
+ if (err) {
+ data->disable_ec = false;
+ return err;
+ }
+ }
+
+ dev_dbg(dev, "Enumerating hwmon sensors\n");
+
+ pack = atk_ggrp(data, ATK_MUX_HWMON);
+ if (IS_ERR(pack))
+ return PTR_ERR(pack);
+
+ for (i = 0; i < pack->package.count; i++) {
+ union acpi_object *obj = &pack->package.elements[i];
+
+ atk_add_sensor(data, obj);
+ }
+
+ err = data->voltage_count + data->temperature_count + data->fan_count;
+
+ ACPI_FREE(pack);
+ return err;
+}
+
+static int atk_init_attribute_groups(struct atk_data *data)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ struct atk_sensor_data *s;
+ struct attribute **attrs;
+ int i = 0;
+ int len = (data->voltage_count + data->temperature_count
+ + data->fan_count) * 4 + 1;
+
+ attrs = devm_kcalloc(dev, len, sizeof(struct attribute *), GFP_KERNEL);
+ if (!attrs)
+ return -ENOMEM;
+
+ list_for_each_entry(s, &data->sensor_list, list) {
+ attrs[i++] = &s->input_attr.attr;
+ attrs[i++] = &s->label_attr.attr;
+ attrs[i++] = &s->limit1_attr.attr;
+ attrs[i++] = &s->limit2_attr.attr;
+ }
+
+ data->attr_group.attrs = attrs;
+ data->attr_groups[0] = &data->attr_group;
+
+ return 0;
+}
+
+static int atk_register_hwmon(struct atk_data *data)
+{
+ struct device *dev = &data->acpi_dev->dev;
+
+ dev_dbg(dev, "registering hwmon device\n");
+ data->hwmon_dev = hwmon_device_register_with_groups(dev, "atk0110",
+ data,
+ data->attr_groups);
+
+ return PTR_ERR_OR_ZERO(data->hwmon_dev);
+}
+
+static int atk_probe_if(struct atk_data *data)
+{
+ struct device *dev = &data->acpi_dev->dev;
+ acpi_handle ret;
+ acpi_status status;
+ int err = 0;
+
+ /* RTMP: read temperature */
+ status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_TMP, &ret);
+ if (ACPI_SUCCESS(status))
+ data->rtmp_handle = ret;
+ else
+ dev_dbg(dev, "method " METHOD_OLD_READ_TMP " not found: %s\n",
+ acpi_format_exception(status));
+
+ /* RVLT: read voltage */
+ status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_VLT, &ret);
+ if (ACPI_SUCCESS(status))
+ data->rvlt_handle = ret;
+ else
+ dev_dbg(dev, "method " METHOD_OLD_READ_VLT " not found: %s\n",
+ acpi_format_exception(status));
+
+ /* RFAN: read fan status */
+ status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_FAN, &ret);
+ if (ACPI_SUCCESS(status))
+ data->rfan_handle = ret;
+ else
+ dev_dbg(dev, "method " METHOD_OLD_READ_FAN " not found: %s\n",
+ acpi_format_exception(status));
+
+ /* Enumeration */
+ status = acpi_get_handle(data->atk_handle, METHOD_ENUMERATE, &ret);
+ if (ACPI_SUCCESS(status))
+ data->enumerate_handle = ret;
+ else
+ dev_dbg(dev, "method " METHOD_ENUMERATE " not found: %s\n",
+ acpi_format_exception(status));
+
+ /* De-multiplexer (read) */
+ status = acpi_get_handle(data->atk_handle, METHOD_READ, &ret);
+ if (ACPI_SUCCESS(status))
+ data->read_handle = ret;
+ else
+ dev_dbg(dev, "method " METHOD_READ " not found: %s\n",
+ acpi_format_exception(status));
+
+ /* De-multiplexer (write) */
+ status = acpi_get_handle(data->atk_handle, METHOD_WRITE, &ret);
+ if (ACPI_SUCCESS(status))
+ data->write_handle = ret;
+ else
+ dev_dbg(dev, "method " METHOD_WRITE " not found: %s\n",
+ acpi_format_exception(status));
+
+ /*
+ * Check for hwmon methods: first check "old" style methods; note that
+ * both may be present: in this case we stick to the old interface;
+ * analysis of multiple DSDTs indicates that when both interfaces
+ * are present the new one (GGRP/GITM) is not functional.
+ */
+ if (new_if)
+ dev_info(dev, "Overriding interface detection\n");
+ if (data->rtmp_handle &&
+ data->rvlt_handle && data->rfan_handle && !new_if)
+ data->old_interface = true;
+ else if (data->enumerate_handle && data->read_handle &&
+ data->write_handle)
+ data->old_interface = false;
+ else
+ err = -ENODEV;
+
+ return err;
+}
+
+static int atk_add(struct acpi_device *device)
+{
+ acpi_status ret;
+ int err;
+ struct acpi_buffer buf;
+ union acpi_object *obj;
+ struct atk_data *data;
+
+ dev_dbg(&device->dev, "adding...\n");
+
+ data = devm_kzalloc(&device->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->acpi_dev = device;
+ data->atk_handle = device->handle;
+ INIT_LIST_HEAD(&data->sensor_list);
+ data->disable_ec = false;
+
+ buf.length = ACPI_ALLOCATE_BUFFER;
+ ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL,
+ &buf, ACPI_TYPE_PACKAGE);
+ if (ret != AE_OK) {
+ dev_dbg(&device->dev, "atk: method MBIF not found\n");
+ } else {
+ obj = buf.pointer;
+ if (obj->package.count >= 2) {
+ union acpi_object *id = &obj->package.elements[1];
+ if (id->type == ACPI_TYPE_STRING)
+ dev_dbg(&device->dev, "board ID = %s\n",
+ id->string.pointer);
+ }
+ ACPI_FREE(buf.pointer);
+ }
+
+ err = atk_probe_if(data);
+ if (err) {
+ dev_err(&device->dev, "No usable hwmon interface detected\n");
+ goto out;
+ }
+
+ if (data->old_interface) {
+ dev_dbg(&device->dev, "Using old hwmon interface\n");
+ err = atk_enumerate_old_hwmon(data);
+ } else {
+ dev_dbg(&device->dev, "Using new hwmon interface\n");
+ err = atk_enumerate_new_hwmon(data);
+ }
+ if (err < 0)
+ goto out;
+ if (err == 0) {
+ dev_info(&device->dev,
+ "No usable sensor detected, bailing out\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ err = atk_init_attribute_groups(data);
+ if (err)
+ goto out;
+ err = atk_register_hwmon(data);
+ if (err)
+ goto out;
+
+ atk_debugfs_init(data);
+
+ device->driver_data = data;
+ return 0;
+out:
+ if (data->disable_ec)
+ atk_ec_ctl(data, 0);
+ return err;
+}
+
+static int atk_remove(struct acpi_device *device)
+{
+ struct atk_data *data = device->driver_data;
+ dev_dbg(&device->dev, "removing...\n");
+
+ device->driver_data = NULL;
+
+ atk_debugfs_cleanup(data);
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ if (data->disable_ec) {
+ if (atk_ec_ctl(data, 0))
+ dev_err(&device->dev, "Failed to disable EC\n");
+ }
+
+ return 0;
+}
+
+static int __init atk0110_init(void)
+{
+ int ret;
+
+ /* Make sure it's safe to access the device through ACPI */
+ if (!acpi_resources_are_enforced()) {
+ pr_err("Resources not safely usable due to acpi_enforce_resources kernel parameter\n");
+ return -EBUSY;
+ }
+
+ if (dmi_check_system(atk_force_new_if))
+ new_if = true;
+
+ ret = acpi_bus_register_driver(&atk_driver);
+ if (ret)
+ pr_info("acpi_bus_register_driver failed: %d\n", ret);
+
+ return ret;
+}
+
+static void __exit atk0110_exit(void)
+{
+ acpi_bus_unregister_driver(&atk_driver);
+}
+
+module_init(atk0110_init);
+module_exit(atk0110_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/asus_wmi_sensors.c b/drivers/hwmon/asus_wmi_sensors.c
new file mode 100644
index 000000000..6e8a90817
--- /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
new file mode 100644
index 000000000..4fd8de802
--- /dev/null
+++ b/drivers/hwmon/atxp1.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * atxp1.c - kernel module for setting CPU VID and general purpose
+ * I/Os using the Attansic ATXP1 chip.
+ *
+ * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is
+ * not auto-detected by the driver and must be instantiated explicitly.
+ * See Documentation/i2c/instantiating-devices.rst for more information.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
+MODULE_VERSION("0.6.3");
+MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
+
+#define ATXP1_VID 0x00
+#define ATXP1_CVID 0x01
+#define ATXP1_GPIO1 0x06
+#define ATXP1_GPIO2 0x0a
+#define ATXP1_VIDENA 0x20
+#define ATXP1_VIDMASK 0x1f
+#define ATXP1_GPIO1MASK 0x0f
+
+struct atxp1_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ unsigned long last_updated;
+ bool valid;
+ struct {
+ u8 vid; /* VID output register */
+ u8 cpu_vid; /* VID input from CPU */
+ u8 gpio1; /* General purpose I/O register 1 */
+ u8 gpio2; /* General purpose I/O register 2 */
+ } reg;
+ u8 vrm; /* Detected CPU VRM */
+};
+
+static struct atxp1_data *atxp1_update_device(struct device *dev)
+{
+ struct atxp1_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+
+ /* Update local register data */
+ data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
+ data->reg.cpu_vid = i2c_smbus_read_byte_data(client,
+ ATXP1_CVID);
+ data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
+ data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
+
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* sys file functions for cpu0_vid */
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int size;
+ struct atxp1_data *data;
+
+ data = atxp1_update_device(dev);
+
+ size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK,
+ data->vrm));
+
+ return size;
+}
+
+static ssize_t cpu0_vid_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct atxp1_data *data = atxp1_update_device(dev);
+ struct i2c_client *client = data->client;
+ int vid, cvid;
+ unsigned long vcore;
+ int err;
+
+ err = kstrtoul(buf, 10, &vcore);
+ if (err)
+ return err;
+
+ vcore /= 25;
+ vcore *= 25;
+
+ /* Calculate VID */
+ vid = vid_to_reg(vcore, data->vrm);
+ if (vid < 0) {
+ dev_err(dev, "VID calculation failed.\n");
+ return vid;
+ }
+
+ /*
+ * If output enabled, use control register value.
+ * Otherwise original CPU VID
+ */
+ if (data->reg.vid & ATXP1_VIDENA)
+ cvid = data->reg.vid & ATXP1_VIDMASK;
+ else
+ cvid = data->reg.cpu_vid;
+
+ /* Nothing changed, aborting */
+ if (vid == cvid)
+ return count;
+
+ dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid);
+
+ /* Write every 25 mV step to increase stability */
+ if (cvid > vid) {
+ for (; cvid >= vid; cvid--)
+ i2c_smbus_write_byte_data(client,
+ ATXP1_VID, cvid | ATXP1_VIDENA);
+ } else {
+ for (; cvid <= vid; cvid++)
+ i2c_smbus_write_byte_data(client,
+ ATXP1_VID, cvid | ATXP1_VIDENA);
+ }
+
+ data->valid = false;
+
+ return count;
+}
+
+/*
+ * CPU core reference voltage
+ * unit: millivolt
+ */
+static DEVICE_ATTR_RW(cpu0_vid);
+
+/* sys file functions for GPIO1 */
+static ssize_t gpio1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int size;
+ struct atxp1_data *data;
+
+ data = atxp1_update_device(dev);
+
+ size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
+
+ return size;
+}
+
+static ssize_t gpio1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct atxp1_data *data = atxp1_update_device(dev);
+ struct i2c_client *client = data->client;
+ unsigned long value;
+ int err;
+
+ err = kstrtoul(buf, 16, &value);
+ if (err)
+ return err;
+
+ value &= ATXP1_GPIO1MASK;
+
+ if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
+ dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
+
+ i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
+
+ data->valid = false;
+ }
+
+ return count;
+}
+
+/*
+ * GPIO1 data register
+ * unit: Four bit as hex (e.g. 0x0f)
+ */
+static DEVICE_ATTR_RW(gpio1);
+
+/* sys file functions for GPIO2 */
+static ssize_t gpio2_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int size;
+ struct atxp1_data *data;
+
+ data = atxp1_update_device(dev);
+
+ size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
+
+ return size;
+}
+
+static ssize_t gpio2_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct atxp1_data *data = atxp1_update_device(dev);
+ struct i2c_client *client = data->client;
+ unsigned long value;
+ int err;
+
+ err = kstrtoul(buf, 16, &value);
+ if (err)
+ return err;
+ value &= 0xff;
+
+ if (value != data->reg.gpio2) {
+ dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value);
+
+ i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
+
+ data->valid = false;
+ }
+
+ return count;
+}
+
+/*
+ * GPIO2 data register
+ * unit: Eight bit as hex (e.g. 0xff)
+ */
+static DEVICE_ATTR_RW(gpio2);
+
+static struct attribute *atxp1_attrs[] = {
+ &dev_attr_gpio1.attr,
+ &dev_attr_gpio2.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(atxp1);
+
+static int atxp1_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct atxp1_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct atxp1_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* Get VRM */
+ data->vrm = vid_which_vrm();
+ if (data->vrm != 90 && data->vrm != 91) {
+ dev_err(dev, "atxp1: Not supporting VRM %d.%d\n",
+ data->vrm / 10, data->vrm % 10);
+ return -ENODEV;
+ }
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ atxp1_groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(dev, "Using VRM: %d.%d\n", data->vrm / 10, data->vrm % 10);
+
+ return 0;
+};
+
+static const struct i2c_device_id atxp1_id[] = {
+ { "atxp1", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, atxp1_id);
+
+static struct i2c_driver atxp1_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "atxp1",
+ },
+ .probe_new = atxp1_probe,
+ .id_table = atxp1_id,
+};
+
+module_i2c_driver(atxp1_driver);
diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c
new file mode 100644
index 000000000..25abf2808
--- /dev/null
+++ b/drivers/hwmon/axi-fan-control.c
@@ -0,0 +1,541 @@
+// 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;
+ }
+
+ 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);
+
+ if (IS_ERR(ctl->hdev))
+ return PTR_ERR(ctl->hdev);
+
+ 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;
+ }
+
+ return 0;
+}
+
+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 000000000..21ab17277
--- /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 000000000..93b8dd5e7
--- /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
new file mode 100644
index 000000000..09aab5859
--- /dev/null
+++ b/drivers/hwmon/coretemp.c
@@ -0,0 +1,787 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * coretemp.c - Linux kernel module for hardware monitoring
+ *
+ * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * Inspired from many hwmon drivers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/cpu_device_id.h>
+
+#define DRVNAME "coretemp"
+
+/*
+ * force_tjmax only matters when TjMax can't be read from the CPU itself.
+ * When set, it replaces the driver's suboptimal heuristic.
+ */
+static int force_tjmax;
+module_param_named(tjmax, force_tjmax, int, 0444);
+MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
+
+#define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */
+#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
+#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */
+#define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */
+#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */
+#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1)
+#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
+
+#ifdef CONFIG_SMP
+#define for_each_sibling(i, cpu) \
+ for_each_cpu(i, topology_sibling_cpumask(cpu))
+#else
+#define for_each_sibling(i, cpu) for (i = 0; false; )
+#endif
+
+/*
+ * Per-Core Temperature Data
+ * @last_updated: The time when the current temperature value was updated
+ * earlier (in jiffies).
+ * @cpu_core_id: The CPU Core from which temperature values should be read
+ * This value is passed as "id" field to rdmsr/wrmsr functions.
+ * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
+ * from where the temperature values should be read.
+ * @attr_size: Total number of pre-core attrs displayed in the sysfs.
+ * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
+ * Otherwise, temp_data holds coretemp data.
+ * @valid: If this is 1, the current temperature is valid.
+ */
+struct temp_data {
+ int temp;
+ int ttarget;
+ int tjmax;
+ unsigned long last_updated;
+ unsigned int cpu;
+ u32 cpu_core_id;
+ u32 status_reg;
+ int attr_size;
+ bool is_pkg_data;
+ bool valid;
+ struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
+ char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
+ struct attribute *attrs[TOTAL_ATTRS + 1];
+ struct attribute_group attr_group;
+ struct mutex update_lock;
+};
+
+/* Platform Data per Physical CPU */
+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;
+};
+
+/* Keep track of how many zone pointers we allocated in init() */
+static int max_zones __read_mostly;
+/* Array of zone pointers. Serialized by cpu hotplug lock */
+static struct platform_device **zone_devices;
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+ struct temp_data *tdata = pdata->core_data[attr->index];
+
+ if (tdata->is_pkg_data)
+ return sprintf(buf, "Package id %u\n", pdata->pkg_id);
+
+ return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
+}
+
+static ssize_t show_crit_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ u32 eax, edx;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+ struct temp_data *tdata = pdata->core_data[attr->index];
+
+ mutex_lock(&tdata->update_lock);
+ rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+ mutex_unlock(&tdata->update_lock);
+
+ return sprintf(buf, "%d\n", (eax >> 5) & 1);
+}
+
+static ssize_t show_tjmax(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax);
+}
+
+static ssize_t show_ttarget(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
+}
+
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ u32 eax, edx;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct platform_data *pdata = dev_get_drvdata(dev);
+ struct temp_data *tdata = pdata->core_data[attr->index];
+
+ mutex_lock(&tdata->update_lock);
+
+ /* Check whether the time interval has elapsed */
+ if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) {
+ rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+ /*
+ * Ignore the valid bit. In all observed cases the register
+ * value is either low or zero if the valid bit is 0.
+ * Return it instead of reporting an error which doesn't
+ * really help at all.
+ */
+ tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000;
+ tdata->valid = true;
+ tdata->last_updated = jiffies;
+ }
+
+ mutex_unlock(&tdata->update_lock);
+ return sprintf(buf, "%d\n", tdata->temp);
+}
+
+struct tjmax_pci {
+ unsigned int device;
+ int tjmax;
+};
+
+static const struct tjmax_pci tjmax_pci_table[] = {
+ { 0x0708, 110000 }, /* CE41x0 (Sodaville ) */
+ { 0x0c72, 102000 }, /* Atom S1240 (Centerton) */
+ { 0x0c73, 95000 }, /* Atom S1220 (Centerton) */
+ { 0x0c75, 95000 }, /* Atom S1260 (Centerton) */
+};
+
+struct tjmax {
+ char const *id;
+ int tjmax;
+};
+
+static const struct tjmax tjmax_table[] = {
+ { "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */
+ { "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */
+};
+
+struct tjmax_model {
+ u8 model;
+ u8 mask;
+ int tjmax;
+};
+
+#define ANY 0xff
+
+static const struct tjmax_model tjmax_model_table[] = {
+ { 0x1c, 10, 100000 }, /* D4xx, K4xx, N4xx, D5xx, K5xx, N5xx */
+ { 0x1c, ANY, 90000 }, /* Z5xx, N2xx, possibly others
+ * Note: Also matches 230 and 330,
+ * which are covered by tjmax_table
+ */
+ { 0x26, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx)
+ * Note: TjMax for E6xxT is 110C, but CPU type
+ * is undetectable by software
+ */
+ { 0x27, ANY, 90000 }, /* Atom Medfield (Z2460) */
+ { 0x35, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */
+ { 0x36, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx)
+ * Also matches S12x0 (stepping 9), covered by
+ * PCI table
+ */
+};
+
+static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
+{
+ /* The 100C is default for both mobile and non mobile CPUs */
+
+ int tjmax = 100000;
+ int tjmax_ee = 85000;
+ int usemsr_ee = 1;
+ int err;
+ u32 eax, edx;
+ int i;
+ u16 devfn = PCI_DEVFN(0, 0);
+ struct pci_dev *host_bridge = pci_get_domain_bus_and_slot(0, 0, devfn);
+
+ /*
+ * Explicit tjmax table entries override heuristics.
+ * First try PCI host bridge IDs, followed by model ID strings
+ * and model/stepping information.
+ */
+ if (host_bridge && host_bridge->vendor == PCI_VENDOR_ID_INTEL) {
+ for (i = 0; i < ARRAY_SIZE(tjmax_pci_table); i++) {
+ if (host_bridge->device == tjmax_pci_table[i].device) {
+ pci_dev_put(host_bridge);
+ return tjmax_pci_table[i].tjmax;
+ }
+ }
+ }
+ pci_dev_put(host_bridge);
+
+ for (i = 0; i < ARRAY_SIZE(tjmax_table); i++) {
+ if (strstr(c->x86_model_id, tjmax_table[i].id))
+ return tjmax_table[i].tjmax;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) {
+ const struct tjmax_model *tm = &tjmax_model_table[i];
+ if (c->x86_model == tm->model &&
+ (tm->mask == ANY || c->x86_stepping == tm->mask))
+ return tm->tjmax;
+ }
+
+ /* Early chips have no MSR for TjMax */
+
+ if (c->x86_model == 0xf && c->x86_stepping < 4)
+ usemsr_ee = 0;
+
+ if (c->x86_model > 0xe && usemsr_ee) {
+ u8 platform_id;
+
+ /*
+ * Now we can detect the mobile CPU using Intel provided table
+ * http://softwarecommunity.intel.com/Wiki/Mobility/720.htm
+ * For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU
+ */
+ err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx);
+ if (err) {
+ dev_warn(dev,
+ "Unable to access MSR 0x17, assuming desktop"
+ " CPU\n");
+ usemsr_ee = 0;
+ } else if (c->x86_model < 0x17 && !(eax & 0x10000000)) {
+ /*
+ * Trust bit 28 up to Penryn, I could not find any
+ * documentation on that; if you happen to know
+ * someone at Intel please ask
+ */
+ usemsr_ee = 0;
+ } else {
+ /* Platform ID bits 52:50 (EDX starts at bit 32) */
+ platform_id = (edx >> 18) & 0x7;
+
+ /*
+ * Mobile Penryn CPU seems to be platform ID 7 or 5
+ * (guesswork)
+ */
+ if (c->x86_model == 0x17 &&
+ (platform_id == 5 || platform_id == 7)) {
+ /*
+ * If MSR EE bit is set, set it to 90 degrees C,
+ * otherwise 105 degrees C
+ */
+ tjmax_ee = 90000;
+ tjmax = 105000;
+ }
+ }
+ }
+
+ if (usemsr_ee) {
+ err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx);
+ if (err) {
+ dev_warn(dev,
+ "Unable to access MSR 0xEE, for Tjmax, left"
+ " at default\n");
+ } else if (eax & 0x40000000) {
+ tjmax = tjmax_ee;
+ }
+ } else if (tjmax == 100000) {
+ /*
+ * If we don't use msr EE it means we are desktop CPU
+ * (with exeception of Atom)
+ */
+ dev_warn(dev, "Using relative temperature scale!\n");
+ }
+
+ return tjmax;
+}
+
+static bool cpu_has_tjmax(struct cpuinfo_x86 *c)
+{
+ u8 model = c->x86_model;
+
+ return model > 0xe &&
+ model != 0x1c &&
+ model != 0x26 &&
+ model != 0x27 &&
+ model != 0x35 &&
+ model != 0x36;
+}
+
+static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
+{
+ int err;
+ u32 eax, edx;
+ u32 val;
+
+ /*
+ * A new feature of current Intel(R) processors, the
+ * IA32_TEMPERATURE_TARGET contains the TjMax value
+ */
+ err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (err) {
+ if (cpu_has_tjmax(c))
+ dev_warn(dev, "Unable to read TjMax from CPU %u\n", id);
+ } else {
+ val = (eax >> 16) & 0xff;
+ /*
+ * If the TjMax is not plausible, an assumption
+ * will be used
+ */
+ if (val) {
+ dev_dbg(dev, "TjMax is %d degrees C\n", val);
+ return val * 1000;
+ }
+ }
+
+ if (force_tjmax) {
+ dev_notice(dev, "TjMax forced to %d degrees C by user\n",
+ force_tjmax);
+ return force_tjmax * 1000;
+ }
+
+ /*
+ * An assumption is made for early CPUs and unreadable MSR.
+ * NOTE: the calculated value may not be correct.
+ */
+ return adjust_tjmax(c, id, dev);
+}
+
+static int create_core_attrs(struct temp_data *tdata, struct device *dev,
+ int attr_no)
+{
+ int i;
+ static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev,
+ struct device_attribute *devattr, char *buf) = {
+ show_label, show_crit_alarm, show_temp, show_tjmax,
+ show_ttarget };
+ static const char *const suffixes[TOTAL_ATTRS] = {
+ "label", "crit_alarm", "input", "crit", "max"
+ };
+
+ for (i = 0; i < tdata->attr_size; i++) {
+ snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH,
+ "temp%d_%s", attr_no, suffixes[i]);
+ sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
+ tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
+ tdata->sd_attrs[i].dev_attr.attr.mode = 0444;
+ tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
+ tdata->sd_attrs[i].index = attr_no;
+ tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr;
+ }
+ tdata->attr_group.attrs = tdata->attrs;
+ return sysfs_create_group(&dev->kobj, &tdata->attr_group);
+}
+
+
+static int chk_ucode_version(unsigned int cpu)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+
+ /*
+ * Check if we have problem with errata AE18 of Core processors:
+ * Readings might stop update when processor visited too deep sleep,
+ * fixed for stepping D0 (6EC).
+ */
+ if (c->x86_model == 0xe && c->x86_stepping < 0xc && c->microcode < 0x39) {
+ pr_err("Errata AE18 not fixed, update BIOS or microcode of the CPU!\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct platform_device *coretemp_get_pdev(unsigned int cpu)
+{
+ int id = topology_logical_die_id(cpu);
+
+ if (id >= 0 && id < max_zones)
+ return zone_devices[id];
+ return NULL;
+}
+
+static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
+{
+ struct temp_data *tdata;
+
+ tdata = kzalloc(sizeof(struct temp_data), GFP_KERNEL);
+ if (!tdata)
+ return NULL;
+
+ tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
+ MSR_IA32_THERM_STATUS;
+ tdata->is_pkg_data = pkg_flag;
+ tdata->cpu = cpu;
+ tdata->cpu_core_id = topology_core_id(cpu);
+ tdata->attr_size = MAX_CORE_ATTRS;
+ mutex_init(&tdata->update_lock);
+ return tdata;
+}
+
+static int create_core_data(struct platform_device *pdev, unsigned int cpu,
+ int pkg_flag)
+{
+ struct temp_data *tdata;
+ struct platform_data *pdata = platform_get_drvdata(pdev);
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ u32 eax, edx;
+ int err, index, attr_no;
+
+ /*
+ * Find attr number for sysfs:
+ * We map the attr number to core id of the CPU
+ * The attr number is always core id + 2
+ * The Pkgtemp will always show up as temp1_*, if available
+ */
+ 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) {
+ err = -ERANGE;
+ goto ida_free;
+ }
+
+ tdata = init_temp_data(cpu, pkg_flag);
+ 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);
+ if (err)
+ goto exit_free;
+
+ /* We can access status register. Get Critical Temperature */
+ tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
+
+ /*
+ * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET.
+ * The target temperature is available on older CPUs but not in this
+ * register. Atoms don't have the register at all.
+ */
+ if (c->x86_model > 0xe && c->x86_model != 0x1c) {
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET,
+ &eax, &edx);
+ if (!err) {
+ tdata->ttarget
+ = tdata->tjmax - ((eax >> 8) & 0xff) * 1000;
+ tdata->attr_size++;
+ }
+ }
+
+ pdata->core_data[attr_no] = tdata;
+
+ /* Create sysfs interfaces */
+ err = create_core_attrs(tdata, pdata->hwmon_dev, attr_no);
+ if (err)
+ goto exit_free;
+
+ return 0;
+exit_free:
+ pdata->core_data[attr_no] = NULL;
+ kfree(tdata);
+ida_free:
+ if (!pkg_flag)
+ ida_free(&pdata->ida, index);
+ return err;
+}
+
+static void
+coretemp_add_core(struct platform_device *pdev, unsigned int cpu, int pkg_flag)
+{
+ if (create_core_data(pdev, cpu, pkg_flag))
+ dev_err(&pdev->dev, "Adding Core %u failed\n", cpu);
+}
+
+static void coretemp_remove_core(struct platform_data *pdata, int indx)
+{
+ struct temp_data *tdata = pdata->core_data[indx];
+
+ /* if we errored on add then this is already gone */
+ if (!tdata)
+ return;
+
+ /* Remove the sysfs attributes */
+ sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group);
+
+ 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_device_add(int zoneid)
+{
+ struct platform_device *pdev;
+ struct platform_data *pdata;
+ int err;
+
+ /* Initialize the per-zone data structures */
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->pkg_id = zoneid;
+ ida_init(&pdata->ida);
+
+ pdev = platform_device_alloc(DRVNAME, zoneid);
+ if (!pdev) {
+ err = -ENOMEM;
+ goto err_free_pdata;
+ }
+
+ err = platform_device_add(pdev);
+ if (err)
+ goto err_put_dev;
+
+ platform_set_drvdata(pdev, pdata);
+ zone_devices[zoneid] = pdev;
+ return 0;
+
+err_put_dev:
+ platform_device_put(pdev);
+err_free_pdata:
+ kfree(pdata);
+ return err;
+}
+
+static void coretemp_device_remove(int zoneid)
+{
+ struct platform_device *pdev = zone_devices[zoneid];
+ struct platform_data *pdata = platform_get_drvdata(pdev);
+
+ ida_destroy(&pdata->ida);
+ kfree(pdata);
+ platform_device_unregister(pdev);
+}
+
+static int coretemp_cpu_online(unsigned int cpu)
+{
+ struct platform_device *pdev = coretemp_get_pdev(cpu);
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ struct platform_data *pdata;
+
+ /*
+ * Don't execute this on resume as the offline callback did
+ * not get executed on suspend.
+ */
+ if (cpuhp_tasks_frozen)
+ return 0;
+
+ /*
+ * CPUID.06H.EAX[0] indicates whether the CPU has thermal
+ * sensors. We check this bit only, all the early CPUs
+ * without thermal sensors will be filtered out.
+ */
+ if (!cpu_has(c, X86_FEATURE_DTHERM))
+ return -ENODEV;
+
+ pdata = platform_get_drvdata(pdev);
+ if (!pdata->hwmon_dev) {
+ struct device *hwmon;
+
+ /* Check the microcode version of the CPU */
+ if (chk_ucode_version(cpu))
+ return -EINVAL;
+
+ /*
+ * Alright, we have DTS support.
+ * We are bringing the _first_ core in this pkg
+ * online. So, initialize per-pkg data structures and
+ * then bring this core online.
+ */
+ hwmon = hwmon_device_register_with_groups(&pdev->dev, DRVNAME,
+ pdata, NULL);
+ if (IS_ERR(hwmon))
+ return PTR_ERR(hwmon);
+ pdata->hwmon_dev = hwmon;
+
+ /*
+ * Check whether pkgtemp support is available.
+ * If so, add interfaces for pkgtemp.
+ */
+ if (cpu_has(c, X86_FEATURE_PTS))
+ coretemp_add_core(pdev, cpu, 1);
+ }
+
+ /*
+ * Check whether a thread sibling is already online. If not add the
+ * interface for this CPU core.
+ */
+ if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu)))
+ coretemp_add_core(pdev, cpu, 0);
+
+ cpumask_set_cpu(cpu, &pdata->cpumask);
+ return 0;
+}
+
+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 i, indx = -1, target;
+
+ /* No need to tear down any interfaces for suspend */
+ if (cpuhp_tasks_frozen)
+ return 0;
+
+ /* If the physical CPU device does not exist, just return */
+ pd = platform_get_drvdata(pdev);
+ if (!pd->hwmon_dev)
+ return 0;
+
+ 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;
+
+ tdata = pd->core_data[indx];
+
+ cpumask_clear_cpu(cpu, &pd->cpumask);
+
+ /*
+ * If this is the last thread sibling, remove the CPU core
+ * interface, If there is still a sibling online, transfer the
+ * target cpu of that core interface to it.
+ */
+ target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu));
+ if (target >= nr_cpu_ids) {
+ coretemp_remove_core(pd, indx);
+ } else if (tdata && tdata->cpu == cpu) {
+ mutex_lock(&tdata->update_lock);
+ tdata->cpu = target;
+ mutex_unlock(&tdata->update_lock);
+ }
+
+ /*
+ * If all cores in this pkg are offline, remove the interface.
+ */
+ tdata = pd->core_data[PKG_SYSFS_ATTR_NO];
+ if (cpumask_empty(&pd->cpumask)) {
+ if (tdata)
+ coretemp_remove_core(pd, PKG_SYSFS_ATTR_NO);
+ hwmon_device_unregister(pd->hwmon_dev);
+ pd->hwmon_dev = NULL;
+ return 0;
+ }
+
+ /*
+ * Check whether this core is the target for the package
+ * interface. We need to assign it to some other cpu.
+ */
+ if (tdata && tdata->cpu == cpu) {
+ target = cpumask_first(&pd->cpumask);
+ mutex_lock(&tdata->update_lock);
+ tdata->cpu = target;
+ mutex_unlock(&tdata->update_lock);
+ }
+ return 0;
+}
+static const struct x86_cpu_id __initconst coretemp_ids[] = {
+ X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL),
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, coretemp_ids);
+
+static enum cpuhp_state coretemp_hp_online;
+
+static int __init coretemp_init(void)
+{
+ int i, err;
+
+ /*
+ * CPUID.06H.EAX[0] indicates whether the CPU has thermal
+ * sensors. We check this bit only, all the early CPUs
+ * without thermal sensors will be filtered out.
+ */
+ if (!x86_match_cpu(coretemp_ids))
+ return -ENODEV;
+
+ max_zones = topology_max_packages() * topology_max_die_per_package();
+ zone_devices = kcalloc(max_zones, sizeof(struct platform_device *),
+ GFP_KERNEL);
+ if (!zone_devices)
+ return -ENOMEM;
+
+ for (i = 0; i < max_zones; i++) {
+ err = coretemp_device_add(i);
+ if (err)
+ goto outzone;
+ }
+
+ err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online",
+ coretemp_cpu_online, coretemp_cpu_offline);
+ if (err < 0)
+ goto outzone;
+ coretemp_hp_online = err;
+ return 0;
+
+outzone:
+ while (i--)
+ coretemp_device_remove(i);
+ kfree(zone_devices);
+ return err;
+}
+module_init(coretemp_init)
+
+static void __exit coretemp_exit(void)
+{
+ int i;
+
+ cpuhp_remove_state(coretemp_hp_online);
+ for (i = 0; i < max_zones; i++)
+ coretemp_device_remove(i);
+ kfree(zone_devices);
+}
+module_exit(coretemp_exit)
+
+MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
+MODULE_DESCRIPTION("Intel Core temperature monitor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c
new file mode 100644
index 000000000..fa6aa4fc8
--- /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 000000000..ec7f27a6c
--- /dev/null
+++ b/drivers/hwmon/corsair-psu.c
@@ -0,0 +1,860 @@
+// 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
+};
+
+static int __init corsair_init(void)
+{
+ return hid_register_driver(&corsairpsu_driver);
+}
+
+static void __exit corsair_exit(void)
+{
+ hid_unregister_driver(&corsairpsu_driver);
+}
+
+/*
+ * With module_init() the driver would load before the HID bus when
+ * built-in, so use late_initcall() instead.
+ */
+late_initcall(corsair_init);
+module_exit(corsair_exit);
+
+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
new file mode 100644
index 000000000..ed6c5df94
--- /dev/null
+++ b/drivers/hwmon/da9052-hwmon.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HWMON Driver for Dialog DA9052
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/regulator/consumer.h>
+
+struct da9052_hwmon {
+ struct da9052 *da9052;
+ struct mutex hwmon_lock;
+ bool tsi_as_adc;
+ int tsiref_mv;
+ struct regulator *tsiref;
+ struct completion tsidone;
+};
+
+static const char * const input_names[] = {
+ [DA9052_ADC_VDDOUT] = "VDDOUT",
+ [DA9052_ADC_ICH] = "CHARGING CURRENT",
+ [DA9052_ADC_TBAT] = "BATTERY TEMP",
+ [DA9052_ADC_VBAT] = "BATTERY VOLTAGE",
+ [DA9052_ADC_IN4] = "ADC IN4",
+ [DA9052_ADC_IN5] = "ADC IN5",
+ [DA9052_ADC_IN6] = "ADC IN6",
+ [DA9052_ADC_TSI_XP] = "ADC TS X+",
+ [DA9052_ADC_TSI_YP] = "ADC TS Y+",
+ [DA9052_ADC_TSI_XN] = "ADC TS X-",
+ [DA9052_ADC_TSI_YN] = "ADC TS Y-",
+ [DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP",
+ [DA9052_ADC_VBBAT] = "BACK-UP BATTERY VOLTAGE",
+};
+
+/* Conversion function for VDDOUT and VBAT */
+static inline int volt_reg_to_mv(int value)
+{
+ return DIV_ROUND_CLOSEST(value * 2000, 1023) + 2500;
+}
+
+/* Conversion function for ADC channels 4, 5 and 6 */
+static inline int input_reg_to_mv(int value)
+{
+ return DIV_ROUND_CLOSEST(value * 2500, 1023);
+}
+
+/* Conversion function for VBBAT */
+static inline int vbbat_reg_to_mv(int value)
+{
+ return DIV_ROUND_CLOSEST(value * 5000, 1023);
+}
+
+static inline int input_tsireg_to_mv(struct da9052_hwmon *hwmon, int value)
+{
+ return DIV_ROUND_CLOSEST(value * hwmon->tsiref_mv, 1023);
+}
+
+static inline int da9052_enable_vddout_channel(struct da9052 *da9052)
+{
+ return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
+ DA9052_ADCCONT_AUTOVDDEN,
+ DA9052_ADCCONT_AUTOVDDEN);
+}
+
+static inline int da9052_disable_vddout_channel(struct da9052 *da9052)
+{
+ return da9052_reg_update(da9052, DA9052_ADC_CONT_REG,
+ DA9052_ADCCONT_AUTOVDDEN, 0);
+}
+
+static ssize_t da9052_vddout_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret, vdd;
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ ret = da9052_enable_vddout_channel(hwmon->da9052);
+ if (ret < 0)
+ goto hwmon_err;
+
+ vdd = da9052_reg_read(hwmon->da9052, DA9052_VDD_RES_REG);
+ if (vdd < 0) {
+ ret = vdd;
+ goto hwmon_err_release;
+ }
+
+ ret = da9052_disable_vddout_channel(hwmon->da9052);
+ if (ret < 0)
+ goto hwmon_err;
+
+ mutex_unlock(&hwmon->hwmon_lock);
+ return sprintf(buf, "%d\n", volt_reg_to_mv(vdd));
+
+hwmon_err_release:
+ da9052_disable_vddout_channel(hwmon->da9052);
+hwmon_err:
+ mutex_unlock(&hwmon->hwmon_lock);
+ return ret;
+}
+
+static ssize_t da9052_ich_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret;
+
+ ret = da9052_reg_read(hwmon->da9052, DA9052_ICHG_AV_REG);
+ if (ret < 0)
+ return ret;
+
+ /* Equivalent to 3.9mA/bit in register ICHG_AV */
+ return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret * 39, 10));
+}
+
+static ssize_t da9052_tbat_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", da9052_adc_read_temp(hwmon->da9052));
+}
+
+static ssize_t da9052_vbat_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret;
+
+ ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBAT);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", volt_reg_to_mv(ret));
+}
+
+static ssize_t da9052_misc_channel_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(devattr)->index;
+ int ret;
+
+ ret = da9052_adc_manual_read(hwmon->da9052, channel);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", input_reg_to_mv(ret));
+}
+
+static int da9052_request_tsi_read(struct da9052_hwmon *hwmon, int channel)
+{
+ u8 val = DA9052_TSICONTB_TSIMAN;
+
+ switch (channel) {
+ case DA9052_ADC_TSI_XP:
+ val |= DA9052_TSICONTB_TSIMUX_XP;
+ break;
+ case DA9052_ADC_TSI_YP:
+ val |= DA9052_TSICONTB_TSIMUX_YP;
+ break;
+ case DA9052_ADC_TSI_XN:
+ val |= DA9052_TSICONTB_TSIMUX_XN;
+ break;
+ case DA9052_ADC_TSI_YN:
+ val |= DA9052_TSICONTB_TSIMUX_YN;
+ break;
+ }
+
+ return da9052_reg_write(hwmon->da9052, DA9052_TSI_CONT_B_REG, val);
+}
+
+static int da9052_get_tsi_result(struct da9052_hwmon *hwmon, int channel)
+{
+ u8 regs[3];
+ int msb, lsb, err;
+
+ /* block read to avoid separation of MSB and LSB */
+ err = da9052_group_read(hwmon->da9052, DA9052_TSI_X_MSB_REG,
+ ARRAY_SIZE(regs), regs);
+ if (err)
+ return err;
+
+ switch (channel) {
+ case DA9052_ADC_TSI_XP:
+ case DA9052_ADC_TSI_XN:
+ msb = regs[0] << DA9052_TSILSB_TSIXL_BITS;
+ lsb = regs[2] & DA9052_TSILSB_TSIXL;
+ lsb >>= DA9052_TSILSB_TSIXL_SHIFT;
+ break;
+ case DA9052_ADC_TSI_YP:
+ case DA9052_ADC_TSI_YN:
+ msb = regs[1] << DA9052_TSILSB_TSIYL_BITS;
+ lsb = regs[2] & DA9052_TSILSB_TSIYL;
+ lsb >>= DA9052_TSILSB_TSIYL_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return msb | lsb;
+}
+
+
+static ssize_t __da9052_read_tsi(struct device *dev, int channel)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret;
+
+ reinit_completion(&hwmon->tsidone);
+
+ ret = da9052_request_tsi_read(hwmon, channel);
+ if (ret < 0)
+ return ret;
+
+ /* Wait for an conversion done interrupt */
+ if (!wait_for_completion_timeout(&hwmon->tsidone,
+ msecs_to_jiffies(500)))
+ return -ETIMEDOUT;
+
+ return da9052_get_tsi_result(hwmon, channel);
+}
+
+static ssize_t da9052_tsi_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(devattr)->index;
+ int ret;
+
+ mutex_lock(&hwmon->da9052->auxadc_lock);
+ ret = __da9052_read_tsi(dev, channel);
+ mutex_unlock(&hwmon->da9052->auxadc_lock);
+
+ if (ret < 0)
+ return ret;
+ else
+ return sprintf(buf, "%d\n", input_tsireg_to_mv(hwmon, ret));
+}
+
+static ssize_t da9052_tjunc_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int tjunc;
+ int toffset;
+
+ tjunc = da9052_reg_read(hwmon->da9052, DA9052_TJUNC_RES_REG);
+ if (tjunc < 0)
+ return tjunc;
+
+ toffset = da9052_reg_read(hwmon->da9052, DA9052_T_OFFSET_REG);
+ if (toffset < 0)
+ return toffset;
+
+ /*
+ * Degrees celsius = 1.708 * (TJUNC_RES - T_OFFSET) - 108.8
+ * T_OFFSET is a trim value used to improve accuracy of the result
+ */
+ return sprintf(buf, "%d\n", 1708 * (tjunc - toffset) - 108800);
+}
+
+static ssize_t da9052_vbbat_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret;
+
+ ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBBAT);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret));
+}
+
+static ssize_t label_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n",
+ input_names[to_sensor_dev_attr(devattr)->index]);
+}
+
+static umode_t da9052_channel_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(dattr);
+
+ if (!hwmon->tsi_as_adc) {
+ switch (sattr->index) {
+ case DA9052_ADC_TSI_XP:
+ case DA9052_ADC_TSI_YP:
+ case DA9052_ADC_TSI_XN:
+ case DA9052_ADC_TSI_YN:
+ return 0;
+ }
+ }
+
+ return attr->mode;
+}
+
+static SENSOR_DEVICE_ATTR_RO(in0_input, da9052_vddout, DA9052_ADC_VDDOUT);
+static SENSOR_DEVICE_ATTR_RO(in0_label, label, DA9052_ADC_VDDOUT);
+static SENSOR_DEVICE_ATTR_RO(in3_input, da9052_vbat, DA9052_ADC_VBAT);
+static SENSOR_DEVICE_ATTR_RO(in3_label, label, DA9052_ADC_VBAT);
+static SENSOR_DEVICE_ATTR_RO(in4_input, da9052_misc_channel, DA9052_ADC_IN4);
+static SENSOR_DEVICE_ATTR_RO(in4_label, label, DA9052_ADC_IN4);
+static SENSOR_DEVICE_ATTR_RO(in5_input, da9052_misc_channel, DA9052_ADC_IN5);
+static SENSOR_DEVICE_ATTR_RO(in5_label, label, DA9052_ADC_IN5);
+static SENSOR_DEVICE_ATTR_RO(in6_input, da9052_misc_channel, DA9052_ADC_IN6);
+static SENSOR_DEVICE_ATTR_RO(in6_label, label, DA9052_ADC_IN6);
+static SENSOR_DEVICE_ATTR_RO(in9_input, da9052_vbbat, DA9052_ADC_VBBAT);
+static SENSOR_DEVICE_ATTR_RO(in9_label, label, DA9052_ADC_VBBAT);
+
+static SENSOR_DEVICE_ATTR_RO(in70_input, da9052_tsi, DA9052_ADC_TSI_XP);
+static SENSOR_DEVICE_ATTR_RO(in70_label, label, DA9052_ADC_TSI_XP);
+static SENSOR_DEVICE_ATTR_RO(in71_input, da9052_tsi, DA9052_ADC_TSI_XN);
+static SENSOR_DEVICE_ATTR_RO(in71_label, label, DA9052_ADC_TSI_XN);
+static SENSOR_DEVICE_ATTR_RO(in72_input, da9052_tsi, DA9052_ADC_TSI_YP);
+static SENSOR_DEVICE_ATTR_RO(in72_label, label, DA9052_ADC_TSI_YP);
+static SENSOR_DEVICE_ATTR_RO(in73_input, da9052_tsi, DA9052_ADC_TSI_YN);
+static SENSOR_DEVICE_ATTR_RO(in73_label, label, DA9052_ADC_TSI_YN);
+
+static SENSOR_DEVICE_ATTR_RO(curr1_input, da9052_ich, DA9052_ADC_ICH);
+static SENSOR_DEVICE_ATTR_RO(curr1_label, label, DA9052_ADC_ICH);
+
+static SENSOR_DEVICE_ATTR_RO(temp2_input, da9052_tbat, DA9052_ADC_TBAT);
+static SENSOR_DEVICE_ATTR_RO(temp2_label, label, DA9052_ADC_TBAT);
+static SENSOR_DEVICE_ATTR_RO(temp8_input, da9052_tjunc, DA9052_ADC_TJUNC);
+static SENSOR_DEVICE_ATTR_RO(temp8_label, label, DA9052_ADC_TJUNC);
+
+static struct attribute *da9052_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_label.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_label.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_label.dev_attr.attr,
+ &sensor_dev_attr_in70_input.dev_attr.attr,
+ &sensor_dev_attr_in70_label.dev_attr.attr,
+ &sensor_dev_attr_in71_input.dev_attr.attr,
+ &sensor_dev_attr_in71_label.dev_attr.attr,
+ &sensor_dev_attr_in72_input.dev_attr.attr,
+ &sensor_dev_attr_in72_label.dev_attr.attr,
+ &sensor_dev_attr_in73_input.dev_attr.attr,
+ &sensor_dev_attr_in73_label.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_label.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp8_input.dev_attr.attr,
+ &sensor_dev_attr_temp8_label.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group da9052_group = {
+ .attrs = da9052_attrs,
+ .is_visible = da9052_channel_is_visible,
+};
+__ATTRIBUTE_GROUPS(da9052);
+
+static irqreturn_t da9052_tsi_datardy_irq(int irq, void *data)
+{
+ struct da9052_hwmon *hwmon = data;
+
+ complete(&hwmon->tsidone);
+ return IRQ_HANDLED;
+}
+
+static int da9052_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9052_hwmon *hwmon;
+ struct device *hwmon_dev;
+ int err;
+
+ hwmon = devm_kzalloc(dev, sizeof(struct da9052_hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hwmon);
+
+ mutex_init(&hwmon->hwmon_lock);
+ hwmon->da9052 = dev_get_drvdata(pdev->dev.parent);
+
+ init_completion(&hwmon->tsidone);
+
+ hwmon->tsi_as_adc =
+ device_property_read_bool(pdev->dev.parent, "dlg,tsi-as-adc");
+
+ if (hwmon->tsi_as_adc) {
+ hwmon->tsiref = devm_regulator_get(pdev->dev.parent, "tsiref");
+ if (IS_ERR(hwmon->tsiref)) {
+ err = PTR_ERR(hwmon->tsiref);
+ dev_err(&pdev->dev, "failed to get tsiref: %d", err);
+ return err;
+ }
+
+ err = regulator_enable(hwmon->tsiref);
+ if (err)
+ return err;
+
+ hwmon->tsiref_mv = regulator_get_voltage(hwmon->tsiref);
+ if (hwmon->tsiref_mv < 0) {
+ err = hwmon->tsiref_mv;
+ goto exit_regulator;
+ }
+
+ /* convert from microvolt (DT) to millivolt (hwmon) */
+ hwmon->tsiref_mv /= 1000;
+
+ /* TSIREF limits from datasheet */
+ if (hwmon->tsiref_mv < 1800 || hwmon->tsiref_mv > 2600) {
+ dev_err(hwmon->da9052->dev, "invalid TSIREF voltage: %d",
+ hwmon->tsiref_mv);
+ err = -ENXIO;
+ goto exit_regulator;
+ }
+
+ /* disable touchscreen features */
+ da9052_reg_write(hwmon->da9052, DA9052_TSI_CONT_A_REG, 0x00);
+
+ /* Sample every 1ms */
+ da9052_reg_update(hwmon->da9052, DA9052_ADC_CONT_REG,
+ DA9052_ADCCONT_ADCMODE,
+ DA9052_ADCCONT_ADCMODE);
+
+ err = da9052_request_irq(hwmon->da9052, DA9052_IRQ_TSIREADY,
+ "tsiready-irq", da9052_tsi_datardy_irq,
+ hwmon);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register TSIRDY IRQ: %d",
+ err);
+ goto exit_regulator;
+ }
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9052",
+ hwmon,
+ da9052_groups);
+ err = PTR_ERR_OR_ZERO(hwmon_dev);
+ if (err)
+ goto exit_irq;
+
+ return 0;
+
+exit_irq:
+ if (hwmon->tsi_as_adc)
+ da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon);
+exit_regulator:
+ if (hwmon->tsiref)
+ regulator_disable(hwmon->tsiref);
+
+ return err;
+}
+
+static int da9052_hwmon_remove(struct platform_device *pdev)
+{
+ struct da9052_hwmon *hwmon = platform_get_drvdata(pdev);
+
+ if (hwmon->tsi_as_adc) {
+ da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon);
+ regulator_disable(hwmon->tsiref);
+ }
+
+ return 0;
+}
+
+static struct platform_driver da9052_hwmon_driver = {
+ .probe = da9052_hwmon_probe,
+ .remove = da9052_hwmon_remove,
+ .driver = {
+ .name = "da9052-hwmon",
+ },
+};
+
+module_platform_driver(da9052_hwmon_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 HWMON driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-hwmon");
diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c
new file mode 100644
index 000000000..7652d553b
--- /dev/null
+++ b/drivers/hwmon/da9055-hwmon.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HWMON Driver for Dialog DA9055
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+
+#define DA9055_ADCIN_DIV 102
+#define DA9055_VSYS_DIV 85
+
+#define DA9055_ADC_VSYS 0
+#define DA9055_ADC_ADCIN1 1
+#define DA9055_ADC_ADCIN2 2
+#define DA9055_ADC_ADCIN3 3
+#define DA9055_ADC_TJUNC 4
+
+struct da9055_hwmon {
+ struct da9055 *da9055;
+ struct mutex hwmon_lock;
+ struct mutex irq_lock;
+ struct completion done;
+};
+
+static const char * const input_names[] = {
+ [DA9055_ADC_VSYS] = "VSYS",
+ [DA9055_ADC_ADCIN1] = "ADC IN1",
+ [DA9055_ADC_ADCIN2] = "ADC IN2",
+ [DA9055_ADC_ADCIN3] = "ADC IN3",
+ [DA9055_ADC_TJUNC] = "CHIP TEMP",
+};
+
+static const u8 chan_mux[DA9055_ADC_TJUNC + 1] = {
+ [DA9055_ADC_VSYS] = DA9055_ADC_MUX_VSYS,
+ [DA9055_ADC_ADCIN1] = DA9055_ADC_MUX_ADCIN1,
+ [DA9055_ADC_ADCIN2] = DA9055_ADC_MUX_ADCIN2,
+ [DA9055_ADC_ADCIN3] = DA9055_ADC_MUX_ADCIN3,
+ [DA9055_ADC_TJUNC] = DA9055_ADC_MUX_T_SENSE,
+};
+
+static int da9055_adc_manual_read(struct da9055_hwmon *hwmon,
+ unsigned char channel)
+{
+ int ret;
+ unsigned short calc_data;
+ unsigned short data;
+ unsigned char mux_sel;
+ struct da9055 *da9055 = hwmon->da9055;
+
+ if (channel > DA9055_ADC_TJUNC)
+ return -EINVAL;
+
+ mutex_lock(&hwmon->irq_lock);
+
+ /* Selects desired MUX for manual conversion */
+ mux_sel = chan_mux[channel] | DA9055_ADC_MAN_CONV;
+
+ ret = da9055_reg_write(da9055, DA9055_REG_ADC_MAN, mux_sel);
+ if (ret < 0)
+ goto err;
+
+ /* Wait for an interrupt */
+ if (!wait_for_completion_timeout(&hwmon->done,
+ msecs_to_jiffies(500))) {
+ dev_err(da9055->dev,
+ "timeout waiting for ADC conversion interrupt\n");
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_H);
+ if (ret < 0)
+ goto err;
+
+ calc_data = (unsigned short)ret;
+ data = calc_data << 2;
+
+ ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_L);
+ if (ret < 0)
+ goto err;
+
+ calc_data = (unsigned short)(ret & DA9055_ADC_LSB_MASK);
+ data |= calc_data;
+
+ ret = data;
+
+err:
+ mutex_unlock(&hwmon->irq_lock);
+ return ret;
+}
+
+static irqreturn_t da9055_auxadc_irq(int irq, void *irq_data)
+{
+ struct da9055_hwmon *hwmon = irq_data;
+
+ complete(&hwmon->done);
+
+ return IRQ_HANDLED;
+}
+
+/* Conversion function for VSYS and ADCINx */
+static inline int volt_reg_to_mv(int value, int channel)
+{
+ if (channel == DA9055_ADC_VSYS)
+ return DIV_ROUND_CLOSEST(value * 1000, DA9055_VSYS_DIV) + 2500;
+ else
+ return DIV_ROUND_CLOSEST(value * 1000, DA9055_ADCIN_DIV);
+}
+
+static int da9055_enable_auto_mode(struct da9055 *da9055, int channel)
+{
+
+ return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel,
+ 1 << channel);
+
+}
+
+static int da9055_disable_auto_mode(struct da9055 *da9055, int channel)
+{
+
+ return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel, 0);
+}
+
+static ssize_t da9055_auto_ch_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct da9055_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret, adc;
+ int channel = to_sensor_dev_attr(devattr)->index;
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ ret = da9055_enable_auto_mode(hwmon->da9055, channel);
+ if (ret < 0)
+ goto hwmon_err;
+
+ usleep_range(10000, 10500);
+
+ adc = da9055_reg_read(hwmon->da9055, DA9055_REG_VSYS_RES + channel);
+ if (adc < 0) {
+ ret = adc;
+ goto hwmon_err_release;
+ }
+
+ ret = da9055_disable_auto_mode(hwmon->da9055, channel);
+ if (ret < 0)
+ goto hwmon_err;
+
+ mutex_unlock(&hwmon->hwmon_lock);
+
+ return sprintf(buf, "%d\n", volt_reg_to_mv(adc, channel));
+
+hwmon_err_release:
+ da9055_disable_auto_mode(hwmon->da9055, channel);
+hwmon_err:
+ mutex_unlock(&hwmon->hwmon_lock);
+ return ret;
+}
+
+static ssize_t da9055_tjunc_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct da9055_hwmon *hwmon = dev_get_drvdata(dev);
+ int tjunc;
+ int toffset;
+
+ tjunc = da9055_adc_manual_read(hwmon, DA9055_ADC_TJUNC);
+ if (tjunc < 0)
+ return tjunc;
+
+ toffset = da9055_reg_read(hwmon->da9055, DA9055_REG_T_OFFSET);
+ if (toffset < 0)
+ return toffset;
+
+ /*
+ * Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332
+ * T_OFFSET is a trim value used to improve accuracy of the result
+ */
+ return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(-4084 * (tjunc - toffset)
+ + 3076332, 10000));
+}
+
+static ssize_t label_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n",
+ input_names[to_sensor_dev_attr(devattr)->index]);
+}
+
+static SENSOR_DEVICE_ATTR_RO(in0_input, da9055_auto_ch, DA9055_ADC_VSYS);
+static SENSOR_DEVICE_ATTR_RO(in0_label, label, DA9055_ADC_VSYS);
+static SENSOR_DEVICE_ATTR_RO(in1_input, da9055_auto_ch, DA9055_ADC_ADCIN1);
+static SENSOR_DEVICE_ATTR_RO(in1_label, label, DA9055_ADC_ADCIN1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, da9055_auto_ch, DA9055_ADC_ADCIN2);
+static SENSOR_DEVICE_ATTR_RO(in2_label, label, DA9055_ADC_ADCIN2);
+static SENSOR_DEVICE_ATTR_RO(in3_input, da9055_auto_ch, DA9055_ADC_ADCIN3);
+static SENSOR_DEVICE_ATTR_RO(in3_label, label, DA9055_ADC_ADCIN3);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, da9055_tjunc, DA9055_ADC_TJUNC);
+static SENSOR_DEVICE_ATTR_RO(temp1_label, label, DA9055_ADC_TJUNC);
+
+static struct attribute *da9055_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_label.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(da9055);
+
+static int da9055_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9055_hwmon *hwmon;
+ struct device *hwmon_dev;
+ int hwmon_irq, ret;
+
+ hwmon = devm_kzalloc(dev, sizeof(struct da9055_hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ mutex_init(&hwmon->hwmon_lock);
+ mutex_init(&hwmon->irq_lock);
+
+ init_completion(&hwmon->done);
+ hwmon->da9055 = dev_get_drvdata(pdev->dev.parent);
+
+ hwmon_irq = platform_get_irq_byname(pdev, "HWMON");
+ if (hwmon_irq < 0)
+ return hwmon_irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, hwmon_irq,
+ NULL, da9055_auxadc_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "adc-irq", hwmon);
+ if (ret != 0) {
+ dev_err(hwmon->da9055->dev, "DA9055 ADC IRQ failed ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9055",
+ hwmon,
+ da9055_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver da9055_hwmon_driver = {
+ .probe = da9055_hwmon_probe,
+ .driver = {
+ .name = "da9055-hwmon",
+ },
+};
+
+module_platform_driver(da9055_hwmon_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9055 HWMON driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-hwmon");
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
new file mode 100644
index 000000000..1572b5416
--- /dev/null
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -0,0 +1,1468 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
+ *
+ * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org>
+ *
+ * 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@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/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/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>
+
+#define I8K_SMM_FN_STATUS 0x0025
+#define I8K_SMM_POWER_STATUS 0x0069
+#define I8K_SMM_SET_FAN 0x01a3
+#define I8K_SMM_GET_FAN 0x00a3
+#define I8K_SMM_GET_SPEED 0x02a3
+#define I8K_SMM_GET_FAN_TYPE 0x03a3
+#define I8K_SMM_GET_NOM_SPEED 0x04a3
+#define I8K_SMM_GET_TEMP 0x10a3
+#define I8K_SMM_GET_TEMP_TYPE 0x11a3
+#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_RPM_THRESHOLD 1000
+#define I8K_MAX_TEMP 127
+
+#define I8K_FN_NONE 0x00
+#define I8K_FN_UP 0x01
+#define I8K_FN_DOWN 0x02
+#define I8K_FN_MUTE 0x04
+#define I8K_FN_MASK 0x07
+#define I8K_FN_SHIFT 8
+
+#define I8K_POWER_AC 0x05
+#define I8K_POWER_BATTERY 0x01
+
+#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@kernel.org>");
+MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i8k");
+
+static bool force;
+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);
+MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
+
+#if IS_ENABLED(CONFIG_I8K)
+static bool restricted = true;
+module_param(restricted, bool, 0);
+MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)");
+
+static bool power_status;
+module_param(power_status, bool, 0600);
+MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)");
+#endif
+
+static uint fan_mult;
+module_param(fan_mult, uint, 0);
+MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");
+
+static uint fan_max;
+module_param(fan_max, uint, 0);
+MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
+
+struct smm_regs {
+ unsigned int eax;
+ 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 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);
+
+ return dmi_data && *dmi_data ? dmi_data : "?";
+}
+
+/*
+ * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
+ */
+static int i8k_smm_func(void *par)
+{
+ ktime_t calltime = ktime_get();
+ struct smm_regs *regs = par;
+ int eax = regs->eax;
+ int ebx = regs->ebx;
+ unsigned char carry;
+ long long duration;
+
+ /* SMM requires CPU 0 */
+ if (smp_processor_id() != 0)
+ return -EBUSY;
+
+ 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 0;
+}
+
+/*
+ * Call the System Management Mode BIOS.
+ */
+static int i8k_smm(struct smm_regs *regs)
+{
+ int ret;
+
+ cpus_read_lock();
+ ret = smp_call_on_cpu(0, i8k_smm_func, regs, true);
+ cpus_read_unlock();
+
+ return ret;
+}
+
+/*
+ * Read the fan status.
+ */
+static int i8k_get_fan_status(const struct dell_smm_data *data, u8 fan)
+{
+ struct smm_regs regs = {
+ .eax = I8K_SMM_GET_FAN,
+ .ebx = fan,
+ };
+
+ if (data->disallow_fan_support)
+ return -EINVAL;
+
+ return i8k_smm(&regs) ? : regs.eax & 0xff;
+}
+
+/*
+ * Read the fan speed in RPM.
+ */
+static int i8k_get_fan_speed(const struct dell_smm_data *data, u8 fan)
+{
+ struct smm_regs regs = {
+ .eax = I8K_SMM_GET_SPEED,
+ .ebx = fan,
+ };
+
+ if (data->disallow_fan_support)
+ return -EINVAL;
+
+ return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
+}
+
+/*
+ * Read the fan type.
+ */
+static int _i8k_get_fan_type(const struct dell_smm_data *data, u8 fan)
+{
+ struct smm_regs regs = {
+ .eax = I8K_SMM_GET_FAN_TYPE,
+ .ebx = fan,
+ };
+
+ if (data->disallow_fan_support || data->disallow_fan_type_call)
+ return -EINVAL;
+
+ return i8k_smm(&regs) ? : regs.eax & 0xff;
+}
+
+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 */
+ if (data->fan_type[fan] == INT_MIN)
+ data->fan_type[fan] = _i8k_get_fan_type(data, fan);
+
+ return data->fan_type[fan];
+}
+
+/*
+ * Read the fan nominal rpm for specific fan 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,
+ .ebx = fan | (speed << 8),
+ };
+
+ if (data->disallow_fan_support)
+ return -EINVAL;
+
+ return i8k_smm(&regs) ? : (regs.eax & 0xffff);
+}
+
+/*
+ * Enable or disable automatic BIOS fan control support
+ */
+static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable)
+{
+ struct smm_regs regs = { };
+
+ if (data->disallow_fan_support)
+ return -EINVAL;
+
+ regs.eax = enable ? data->auto_fan : data->manual_fan;
+ return i8k_smm(&regs);
+}
+
+/*
+ * Set the fan speed (off, low, high, ...).
+ */
+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 (data->disallow_fan_support)
+ return -EINVAL;
+
+ speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed);
+ regs.ebx = fan | (speed << 8);
+
+ return i8k_smm(&regs);
+}
+
+static int __init i8k_get_temp_type(u8 sensor)
+{
+ struct smm_regs regs = {
+ .eax = I8K_SMM_GET_TEMP_TYPE,
+ .ebx = sensor,
+ };
+
+ return i8k_smm(&regs) ? : regs.eax & 0xff;
+}
+
+/*
+ * Read the cpu temperature.
+ */
+static int _i8k_get_temp(u8 sensor)
+{
+ struct smm_regs regs = {
+ .eax = I8K_SMM_GET_TEMP,
+ .ebx = sensor,
+ };
+
+ return i8k_smm(&regs) ? : regs.eax & 0xff;
+}
+
+static int i8k_get_temp(u8 sensor)
+{
+ int temp = _i8k_get_temp(sensor);
+
+ /*
+ * Sometimes the temperature sensor returns 0x99, which is out of range.
+ * In this case we retry (once) before returning an error.
+ # 1003655137 00000058 00005a4b
+ # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
+ # 1003655139 00000054 00005c52
+ */
+ if (temp == 0x99) {
+ msleep(100);
+ temp = _i8k_get_temp(sensor);
+ }
+ /*
+ * Return -ENODATA for all invalid temperatures.
+ *
+ * Known instances are the 0x99 value as seen above as well as
+ * 0xc1 (193), which may be returned when trying to read the GPU
+ * temperature if the system supports a GPU and it is currently
+ * turned off.
+ */
+ if (temp > I8K_MAX_TEMP)
+ return -ENODATA;
+
+ return temp;
+}
+
+static int __init i8k_get_dell_signature(int req_fn)
+{
+ struct smm_regs regs = { .eax = req_fn, };
+ int rc;
+
+ rc = i8k_smm(&regs);
+ if (rc < 0)
+ return rc;
+
+ return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
+}
+
+#if IS_ENABLED(CONFIG_I8K)
+
+/*
+ * Read the Fn key status.
+ */
+static int i8k_get_fn_status(void)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
+ int rc;
+
+ rc = i8k_smm(&regs);
+ if (rc < 0)
+ return rc;
+
+ switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
+ case I8K_FN_UP:
+ return I8K_VOL_UP;
+ case I8K_FN_DOWN:
+ return I8K_VOL_DOWN;
+ case I8K_FN_MUTE:
+ return I8K_VOL_MUTE;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Read the power status.
+ */
+static int i8k_get_power_status(void)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
+ int rc;
+
+ rc = i8k_smm(&regs);
+ if (rc < 0)
+ return rc;
+
+ return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
+}
+
+/*
+ * Procfs interface
+ */
+
+static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ 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(data->bios_version[0]) || !isdigit(data->bios_version[1]) ||
+ !isdigit(data->bios_version[2]))
+ return -EINVAL;
+
+ 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;
+
+ 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;
+
+ case I8K_POWER_STATUS:
+ val = i8k_get_power_status();
+ break;
+
+ case I8K_GET_TEMP:
+ val = i8k_get_temp(0);
+ break;
+
+ case I8K_GET_SPEED:
+ if (copy_from_user(&val, argp, sizeof(int)))
+ return -EFAULT;
+
+ 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;
+
+ if (val > U8_MAX || val < 0)
+ return -EINVAL;
+
+ val = i8k_get_fan_status(data, val);
+ break;
+
+ case I8K_SET_FAN:
+ if (restricted && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ 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;
+
+ 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 -ENOIOCTLCMD;
+ }
+
+ if (val < 0)
+ return val;
+
+ if (copy_to_user(argp, &val, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * 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(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 */
+ else
+ ac_power = -1;
+
+ /*
+ * Info:
+ *
+ * 1) Format version (this will change if format changes)
+ * 2) BIOS version
+ * 3) BIOS machine ID
+ * 4) Cpu temperature
+ * 5) Left fan status
+ * 6) Right fan status
+ * 7) Left fan speed
+ * 8) Right fan speed
+ * 9) AC power
+ * 10) Fn Key status
+ */
+ seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
+ I8K_PROC_FMT,
+ 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);
+
+ return 0;
+}
+
+static int i8k_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, i8k_proc_show, pde_data(inode));
+}
+
+static const struct proc_ops i8k_proc_ops = {
+ .proc_open = i8k_open_fs,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+ .proc_ioctl = i8k_ioctl,
+};
+
+static void i8k_exit_procfs(void *param)
+{
+ remove_proc_entry("i8k", NULL);
+}
+
+static void __init i8k_init_procfs(struct device *dev)
+{
+ 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 void __init i8k_init_procfs(struct device *dev)
+{
+}
+
+#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;
+}
+
+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;
+
+ ret = i8k_get_fan_status(cdata->data, cdata->fan_num);
+ if (ret < 0)
+ return ret;
+
+ *state = ret;
+
+ return 0;
+}
+
+static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state)
+{
+ struct dell_smm_cooling_data *cdata = dev->devdata;
+ struct dell_smm_data *data = cdata->data;
+ int ret;
+
+ 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 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)
+{
+ 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;
+
+ 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 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)
+{
+ bool dock = false;
+ int type = i8k_get_fan_type(data, channel);
+
+ if (type < 0)
+ return ERR_PTR(type);
+
+ if (type & 0x10) {
+ dock = true;
+ type &= 0x0F;
+ }
+
+ if (type >= ARRAY_SIZE(fan_labels))
+ type = ARRAY_SIZE(fan_labels) - 1;
+
+ return dock ? docking_labels[type] : fan_labels[type];
+}
+
+static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ 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;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long val)
+{
+ struct dell_smm_data *data = dev_get_drvdata(dev);
+ unsigned long pwm;
+ bool enable;
+ int err;
+
+ 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(&data->i8k_mutex);
+ err = i8k_set_fan(data, channel, pwm);
+ mutex_unlock(&data->i8k_mutex);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+ case hwmon_pwm_enable:
+ if (!val)
+ return -EINVAL;
+
+ if (val == 1)
+ enable = false;
+ else
+ enable = true;
+
+ mutex_lock(&data->i8k_mutex);
+ err = i8k_enable_fan_auto_mode(data, enable);
+ mutex_unlock(&data->i8k_mutex);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+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 const struct hwmon_chip_info dell_smm_chip_info = {
+ .ops = &dell_smm_ops,
+ .info = dell_smm_info,
+};
+
+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;
+ }
+
+ kfree(name);
+
+ return ret;
+}
+
+static int __init dell_smm_init_hwmon(struct device *dev)
+{
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ 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 {
+ uint fan_mult;
+ uint fan_max;
+};
+
+enum i8k_configs {
+ DELL_LATITUDE_D520,
+ DELL_PRECISION_490,
+ DELL_STUDIO,
+ DELL_XPS,
+};
+
+/*
+ * 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,
+ },
+ [DELL_PRECISION_490] = {
+ .fan_mult = 1,
+ .fan_max = I8K_FAN_TURBO,
+ },
+ [DELL_STUDIO] = {
+ .fan_mult = 1,
+ .fan_max = I8K_FAN_HIGH,
+ },
+ [DELL_XPS] = {
+ .fan_mult = 1,
+ .fan_max = I8K_FAN_HIGH,
+ },
+};
+
+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"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
+ },
+ },
+ {
+ .ident = "Dell Latitude",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
+ },
+ },
+ {
+ .ident = "Dell Inspiron 2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
+ },
+ },
+ {
+ .ident = "Dell Latitude D520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"),
+ },
+ .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
+ },
+ {
+ .ident = "Dell Latitude 2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
+ },
+ },
+ { /* UK Inspiron 6400 */
+ .ident = "Dell Inspiron 3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
+ },
+ },
+ {
+ .ident = "Dell Inspiron 3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
+ },
+ },
+ {
+ .ident = "Dell Precision 490",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "Precision WorkStation 490"),
+ },
+ .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490],
+ },
+ {
+ .ident = "Dell Precision",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
+ },
+ },
+ {
+ .ident = "Dell Vostro",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
+ },
+ },
+ {
+ .ident = "Dell Studio",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
+ },
+ .driver_data = (void *)&i8k_config_data[DELL_STUDIO],
+ },
+ {
+ .ident = "Dell XPS M140",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
+ },
+ .driver_data = (void *)&i8k_config_data[DELL_XPS],
+ },
+ {
+ .ident = "Dell XPS",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS"),
+ },
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
+
+/*
+ * On some machines once I8K_SMM_GET_FAN_TYPE is issued then CPU fan speed
+ * randomly going up and down due to bug in Dell SMM or BIOS. Here is blacklist
+ * of affected Dell machines for which we disallow I8K_SMM_GET_FAN_TYPE call.
+ * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=100121
+ */
+static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst = {
+ {
+ .ident = "Dell Studio XPS 8000",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"),
+ },
+ },
+ {
+ .ident = "Dell Studio XPS 8100",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"),
+ },
+ },
+ {
+ .ident = "Dell Inspiron 580",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ 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"),
+ },
+ },
+ { }
+};
+
+/*
+ * On some machines all fan related SMM functions implemented by Dell BIOS
+ * firmware freeze kernel for about 500ms. Until Dell fixes these problems fan
+ * support for affected blacklisted Dell machines stay disabled.
+ * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751
+ */
+static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = {
+ {
+ .ident = "Dell Inspiron 7720",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
+ },
+ },
+ {
+ .ident = "Dell Vostro 3360",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
+ },
+ },
+ {
+ .ident = "Dell XPS13 9333",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ 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"),
+ },
+ },
+ { }
+};
+
+struct i8k_fan_control_data {
+ unsigned int manual_fan;
+ unsigned int auto_fan;
+};
+
+enum i8k_fan_controls {
+ I8K_FAN_34A3_35A3,
+};
+
+static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = {
+ [I8K_FAN_34A3_35A3] = {
+ .manual_fan = 0x34a3,
+ .auto_fan = 0x35a3,
+ },
+};
+
+static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
+ {
+ .ident = "Dell Latitude 5480",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5480"),
+ },
+ .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
+ },
+ {
+ .ident = "Dell Latitude E6440",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"),
+ },
+ .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],
+ },
+ { }
+};
+
+static int __init dell_smm_probe(struct platform_device *pdev)
+{
+ struct dell_smm_data *data;
+ const struct dmi_system_id *id, *fan_control;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->i8k_mutex);
+ platform_set_drvdata(pdev, data);
+
+ if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
+ 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)) {
+ 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");
+ }
+ }
+
+ 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
+ * Values specified in module parameters override values from dmi
+ */
+ 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;
+ }
+
+ /* 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 *control = fan_control->driver_data;
+
+ 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");
+ }
+
+ 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)
+{
+ /*
+ * Get DMI information
+ */
+ if (!dmi_check_system(i8k_dmi_table)) {
+ if (!ignore_dmi && !force)
+ 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));
+ }
+
+ /*
+ * 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;
+ }
+
+ 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)
+{
+ platform_device_unregister(dell_smm_device);
+ platform_driver_unregister(&dell_smm_driver);
+}
+
+module_init(i8k_init);
+module_exit(i8k_exit);
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
new file mode 100644
index 000000000..66c48f70f
--- /dev/null
+++ b/drivers/hwmon/dme1737.c
@@ -0,0 +1,2787 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x, SCH5027,
+ * and SCH5127 Super-I/O chips integrated hardware monitoring
+ * features.
+ * Copyright (c) 2007, 2008, 2009, 2010 Juerg Haefliger <juergh@gmail.com>
+ *
+ * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
+ * the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus
+ * if a SCH311x or SCH5127 chip is found. Both types of chips have very
+ * similar hardware monitoring capabilities but differ in the way they can be
+ * accessed.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.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/io.h>
+
+/* ISA device, if found */
+static struct platform_device *pdev;
+
+/* Module load parameters */
+static bool force_start;
+module_param(force_start, bool, 0);
+MODULE_PARM_DESC(force_start, "Force the chip to start monitoring inputs");
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static bool probe_all_addr;
+module_param(probe_all_addr, bool, 0);
+MODULE_PARM_DESC(probe_all_addr,
+ "Include probing of non-standard LPC addresses");
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
+
+enum chips { dme1737, sch5027, sch311x, sch5127 };
+
+#define DO_REPORT "Please report to the driver maintainer."
+
+/* ---------------------------------------------------------------------
+ * Registers
+ *
+ * The sensors are defined as follows:
+ *
+ * Voltages Temperatures
+ * -------- ------------
+ * in0 +5VTR (+5V stdby) temp1 Remote diode 1
+ * in1 Vccp (proc core) temp2 Internal temp
+ * in2 VCC (internal +3.3V) temp3 Remote diode 2
+ * in3 +5V
+ * in4 +12V
+ * in5 VTR (+3.3V stby)
+ * in6 Vbat
+ * in7 Vtrip (sch5127 only)
+ *
+ * --------------------------------------------------------------------- */
+
+/* Voltages (in) numbered 0-7 (ix) */
+#define DME1737_REG_IN(ix) ((ix) < 5 ? 0x20 + (ix) : \
+ (ix) < 7 ? 0x94 + (ix) : \
+ 0x1f)
+#define DME1737_REG_IN_MIN(ix) ((ix) < 5 ? 0x44 + (ix) * 2 \
+ : 0x91 + (ix) * 2)
+#define DME1737_REG_IN_MAX(ix) ((ix) < 5 ? 0x45 + (ix) * 2 \
+ : 0x92 + (ix) * 2)
+
+/* Temperatures (temp) numbered 0-2 (ix) */
+#define DME1737_REG_TEMP(ix) (0x25 + (ix))
+#define DME1737_REG_TEMP_MIN(ix) (0x4e + (ix) * 2)
+#define DME1737_REG_TEMP_MAX(ix) (0x4f + (ix) * 2)
+#define DME1737_REG_TEMP_OFFSET(ix) ((ix) == 0 ? 0x1f \
+ : 0x1c + (ix))
+
+/*
+ * Voltage and temperature LSBs
+ * The LSBs (4 bits each) are stored in 5 registers with the following layouts:
+ * IN_TEMP_LSB(0) = [in5, in6]
+ * IN_TEMP_LSB(1) = [temp3, temp1]
+ * IN_TEMP_LSB(2) = [in4, temp2]
+ * IN_TEMP_LSB(3) = [in3, in0]
+ * IN_TEMP_LSB(4) = [in2, in1]
+ * IN_TEMP_LSB(5) = [res, in7]
+ */
+#define DME1737_REG_IN_TEMP_LSB(ix) (0x84 + (ix))
+static const u8 DME1737_REG_IN_LSB[] = {3, 4, 4, 3, 2, 0, 0, 5};
+static const u8 DME1737_REG_IN_LSB_SHL[] = {4, 4, 0, 0, 0, 0, 4, 4};
+static const u8 DME1737_REG_TEMP_LSB[] = {1, 2, 1};
+static const u8 DME1737_REG_TEMP_LSB_SHL[] = {4, 4, 0};
+
+/* Fans numbered 0-5 (ix) */
+#define DME1737_REG_FAN(ix) ((ix) < 4 ? 0x28 + (ix) * 2 \
+ : 0xa1 + (ix) * 2)
+#define DME1737_REG_FAN_MIN(ix) ((ix) < 4 ? 0x54 + (ix) * 2 \
+ : 0xa5 + (ix) * 2)
+#define DME1737_REG_FAN_OPT(ix) ((ix) < 4 ? 0x90 + (ix) \
+ : 0xb2 + (ix))
+#define DME1737_REG_FAN_MAX(ix) (0xb4 + (ix)) /* only for fan[4-5] */
+
+/* PWMs numbered 0-2, 4-5 (ix) */
+#define DME1737_REG_PWM(ix) ((ix) < 3 ? 0x30 + (ix) \
+ : 0xa1 + (ix))
+#define DME1737_REG_PWM_CONFIG(ix) (0x5c + (ix)) /* only for pwm[0-2] */
+#define DME1737_REG_PWM_MIN(ix) (0x64 + (ix)) /* only for pwm[0-2] */
+#define DME1737_REG_PWM_FREQ(ix) ((ix) < 3 ? 0x5f + (ix) \
+ : 0xa3 + (ix))
+/*
+ * The layout of the ramp rate registers is different from the other pwm
+ * registers. The bits for the 3 PWMs are stored in 2 registers:
+ * PWM_RR(0) = [OFF3, OFF2, OFF1, RES, RR1E, RR1-2, RR1-1, RR1-0]
+ * PWM_RR(1) = [RR2E, RR2-2, RR2-1, RR2-0, RR3E, RR3-2, RR3-1, RR3-0]
+ */
+#define DME1737_REG_PWM_RR(ix) (0x62 + (ix)) /* only for pwm[0-2] */
+
+/* Thermal zones 0-2 */
+#define DME1737_REG_ZONE_LOW(ix) (0x67 + (ix))
+#define DME1737_REG_ZONE_ABS(ix) (0x6a + (ix))
+/*
+ * The layout of the hysteresis registers is different from the other zone
+ * registers. The bits for the 3 zones are stored in 2 registers:
+ * ZONE_HYST(0) = [H1-3, H1-2, H1-1, H1-0, H2-3, H2-2, H2-1, H2-0]
+ * ZONE_HYST(1) = [H3-3, H3-2, H3-1, H3-0, RES, RES, RES, RES]
+ */
+#define DME1737_REG_ZONE_HYST(ix) (0x6d + (ix))
+
+/*
+ * Alarm registers and bit mapping
+ * The 3 8-bit alarm registers will be concatenated to a single 32-bit
+ * alarm value [0, ALARM3, ALARM2, ALARM1].
+ */
+#define DME1737_REG_ALARM1 0x41
+#define DME1737_REG_ALARM2 0x42
+#define DME1737_REG_ALARM3 0x83
+static const u8 DME1737_BIT_ALARM_IN[] = {0, 1, 2, 3, 8, 16, 17, 18};
+static const u8 DME1737_BIT_ALARM_TEMP[] = {4, 5, 6};
+static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
+
+/* Miscellaneous registers */
+#define DME1737_REG_DEVICE 0x3d
+#define DME1737_REG_COMPANY 0x3e
+#define DME1737_REG_VERSTEP 0x3f
+#define DME1737_REG_CONFIG 0x40
+#define DME1737_REG_CONFIG2 0x7f
+#define DME1737_REG_VID 0x43
+#define DME1737_REG_TACH_PWM 0x81
+
+/* ---------------------------------------------------------------------
+ * Misc defines
+ * --------------------------------------------------------------------- */
+
+/* Chip identification */
+#define DME1737_COMPANY_SMSC 0x5c
+#define DME1737_VERSTEP 0x88
+#define DME1737_VERSTEP_MASK 0xf8
+#define SCH311X_DEVICE 0x8c
+#define SCH5027_VERSTEP 0x69
+#define SCH5127_DEVICE 0x8e
+
+/* Device ID values (global configuration register index 0x20) */
+#define DME1737_ID_1 0x77
+#define DME1737_ID_2 0x78
+#define SCH3112_ID 0x7c
+#define SCH3114_ID 0x7d
+#define SCH3116_ID 0x7f
+#define SCH5027_ID 0x89
+#define SCH5127_ID 0x86
+
+/* Length of ISA address segment */
+#define DME1737_EXTENT 2
+
+/* chip-dependent features */
+#define HAS_TEMP_OFFSET (1 << 0) /* bit 0 */
+#define HAS_VID (1 << 1) /* bit 1 */
+#define HAS_ZONE3 (1 << 2) /* bit 2 */
+#define HAS_ZONE_HYST (1 << 3) /* bit 3 */
+#define HAS_PWM_MIN (1 << 4) /* bit 4 */
+#define HAS_FAN(ix) (1 << ((ix) + 5)) /* bits 5-10 */
+#define HAS_PWM(ix) (1 << ((ix) + 11)) /* bits 11-16 */
+#define HAS_IN7 (1 << 17) /* bit 17 */
+
+/* ---------------------------------------------------------------------
+ * Data structures and manipulation thereof
+ * --------------------------------------------------------------------- */
+
+struct dme1737_data {
+ struct i2c_client *client; /* for I2C devices only */
+ struct device *hwmon_dev;
+ const char *name;
+ unsigned int addr; /* for ISA devices only */
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_update; /* in jiffies */
+ unsigned long last_vbat; /* in jiffies */
+ enum chips type;
+ const int *in_nominal; /* pointer to IN_NOMINAL array */
+
+ u8 vid;
+ u8 pwm_rr_en;
+ u32 has_features;
+
+ /* Register values */
+ u16 in[8];
+ u8 in_min[8];
+ u8 in_max[8];
+ s16 temp[3];
+ s8 temp_min[3];
+ s8 temp_max[3];
+ s8 temp_offset[3];
+ u8 config;
+ u8 config2;
+ u8 vrm;
+ u16 fan[6];
+ u16 fan_min[6];
+ u8 fan_max[2];
+ u8 fan_opt[6];
+ u8 pwm[6];
+ u8 pwm_min[3];
+ u8 pwm_config[3];
+ u8 pwm_acz[3];
+ u8 pwm_freq[6];
+ u8 pwm_rr[2];
+ s8 zone_low[3];
+ s8 zone_abs[3];
+ u8 zone_hyst[2];
+ u32 alarms;
+};
+
+/* Nominal voltage values */
+static const int IN_NOMINAL_DME1737[] = {5000, 2250, 3300, 5000, 12000, 3300,
+ 3300};
+static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
+ 3300};
+static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
+ 3300};
+static const int IN_NOMINAL_SCH5127[] = {2500, 2250, 3300, 1125, 1125, 3300,
+ 3300, 1500};
+#define IN_NOMINAL(type) ((type) == sch311x ? IN_NOMINAL_SCH311x : \
+ (type) == sch5027 ? IN_NOMINAL_SCH5027 : \
+ (type) == sch5127 ? IN_NOMINAL_SCH5127 : \
+ IN_NOMINAL_DME1737)
+
+/*
+ * Voltage input
+ * Voltage inputs have 16 bits resolution, limit values have 8 bits
+ * resolution.
+ */
+static inline int IN_FROM_REG(int reg, int nominal, int res)
+{
+ return (reg * nominal + (3 << (res - 3))) / (3 << (res - 2));
+}
+
+static inline int IN_TO_REG(long val, int nominal)
+{
+ val = clamp_val(val, 0, 255 * nominal / 192);
+ return DIV_ROUND_CLOSEST(val * 192, nominal);
+}
+
+/*
+ * Temperature input
+ * The register values represent temperatures in 2's complement notation from
+ * -127 degrees C to +127 degrees C. Temp inputs have 16 bits resolution, limit
+ * values have 8 bits resolution.
+ */
+static inline int TEMP_FROM_REG(int reg, int res)
+{
+ return (reg * 1000) >> (res - 8);
+}
+
+static inline int TEMP_TO_REG(long val)
+{
+ val = clamp_val(val, -128000, 127000);
+ return DIV_ROUND_CLOSEST(val, 1000);
+}
+
+/* Temperature range */
+static const int TEMP_RANGE[] = {2000, 2500, 3333, 4000, 5000, 6666, 8000,
+ 10000, 13333, 16000, 20000, 26666, 32000,
+ 40000, 53333, 80000};
+
+static inline int TEMP_RANGE_FROM_REG(int reg)
+{
+ return TEMP_RANGE[(reg >> 4) & 0x0f];
+}
+
+static int TEMP_RANGE_TO_REG(long val, int reg)
+{
+ int i;
+
+ for (i = 15; i > 0; i--) {
+ if (val > (TEMP_RANGE[i] + TEMP_RANGE[i - 1] + 1) / 2)
+ break;
+ }
+
+ return (reg & 0x0f) | (i << 4);
+}
+
+/*
+ * Temperature hysteresis
+ * Register layout:
+ * reg[0] = [H1-3, H1-2, H1-1, H1-0, H2-3, H2-2, H2-1, H2-0]
+ * reg[1] = [H3-3, H3-2, H3-1, H3-0, xxxx, xxxx, xxxx, xxxx]
+ */
+static inline int TEMP_HYST_FROM_REG(int reg, int ix)
+{
+ return (((ix == 1) ? reg : reg >> 4) & 0x0f) * 1000;
+}
+
+static inline int TEMP_HYST_TO_REG(int temp, long hyst, int ix, int reg)
+{
+ hyst = clamp_val(hyst, temp - 15000, temp);
+ hyst = DIV_ROUND_CLOSEST(temp - hyst, 1000);
+
+ return (ix == 1) ? (reg & 0xf0) | hyst : (reg & 0x0f) | (hyst << 4);
+}
+
+/* Fan input RPM */
+static inline int FAN_FROM_REG(int reg, int tpc)
+{
+ if (tpc)
+ return tpc * reg;
+ else
+ return (reg == 0 || reg == 0xffff) ? 0 : 90000 * 60 / reg;
+}
+
+static inline int FAN_TO_REG(long val, int tpc)
+{
+ if (tpc) {
+ return clamp_val(val / tpc, 0, 0xffff);
+ } else {
+ return (val <= 0) ? 0xffff :
+ clamp_val(90000 * 60 / val, 0, 0xfffe);
+ }
+}
+
+/*
+ * Fan TPC (tach pulse count)
+ * Converts a register value to a TPC multiplier or returns 0 if the tachometer
+ * is configured in legacy (non-tpc) mode
+ */
+static inline int FAN_TPC_FROM_REG(int reg)
+{
+ return (reg & 0x20) ? 0 : 60 >> (reg & 0x03);
+}
+
+/*
+ * Fan type
+ * The type of a fan is expressed in number of pulses-per-revolution that it
+ * emits
+ */
+static inline int FAN_TYPE_FROM_REG(int reg)
+{
+ int edge = (reg >> 1) & 0x03;
+
+ return (edge > 0) ? 1 << (edge - 1) : 0;
+}
+
+static inline int FAN_TYPE_TO_REG(long val, int reg)
+{
+ int edge = (val == 4) ? 3 : val;
+
+ return (reg & 0xf9) | (edge << 1);
+}
+
+/* Fan max RPM */
+static const int FAN_MAX[] = {0x54, 0x38, 0x2a, 0x21, 0x1c, 0x18, 0x15, 0x12,
+ 0x11, 0x0f, 0x0e};
+
+static int FAN_MAX_FROM_REG(int reg)
+{
+ int i;
+
+ for (i = 10; i > 0; i--) {
+ if (reg == FAN_MAX[i])
+ break;
+ }
+
+ return 1000 + i * 500;
+}
+
+static int FAN_MAX_TO_REG(long val)
+{
+ int i;
+
+ for (i = 10; i > 0; i--) {
+ if (val > (1000 + (i - 1) * 500))
+ break;
+ }
+
+ return FAN_MAX[i];
+}
+
+/*
+ * PWM enable
+ * Register to enable mapping:
+ * 000: 2 fan on zone 1 auto
+ * 001: 2 fan on zone 2 auto
+ * 010: 2 fan on zone 3 auto
+ * 011: 0 fan full on
+ * 100: -1 fan disabled
+ * 101: 2 fan on hottest of zones 2,3 auto
+ * 110: 2 fan on hottest of zones 1,2,3 auto
+ * 111: 1 fan in manual mode
+ */
+static inline int PWM_EN_FROM_REG(int reg)
+{
+ static const int en[] = {2, 2, 2, 0, -1, 2, 2, 1};
+
+ return en[(reg >> 5) & 0x07];
+}
+
+static inline int PWM_EN_TO_REG(int val, int reg)
+{
+ int en = (val == 1) ? 7 : 3;
+
+ return (reg & 0x1f) | ((en & 0x07) << 5);
+}
+
+/*
+ * PWM auto channels zone
+ * Register to auto channels zone mapping (ACZ is a bitfield with bit x
+ * corresponding to zone x+1):
+ * 000: 001 fan on zone 1 auto
+ * 001: 010 fan on zone 2 auto
+ * 010: 100 fan on zone 3 auto
+ * 011: 000 fan full on
+ * 100: 000 fan disabled
+ * 101: 110 fan on hottest of zones 2,3 auto
+ * 110: 111 fan on hottest of zones 1,2,3 auto
+ * 111: 000 fan in manual mode
+ */
+static inline int PWM_ACZ_FROM_REG(int reg)
+{
+ static const int acz[] = {1, 2, 4, 0, 0, 6, 7, 0};
+
+ return acz[(reg >> 5) & 0x07];
+}
+
+static inline int PWM_ACZ_TO_REG(long val, int reg)
+{
+ int acz = (val == 4) ? 2 : val - 1;
+
+ return (reg & 0x1f) | ((acz & 0x07) << 5);
+}
+
+/* PWM frequency */
+static const int PWM_FREQ[] = {11, 15, 22, 29, 35, 44, 59, 88,
+ 15000, 20000, 30000, 25000, 0, 0, 0, 0};
+
+static inline int PWM_FREQ_FROM_REG(int reg)
+{
+ return PWM_FREQ[reg & 0x0f];
+}
+
+static int PWM_FREQ_TO_REG(long val, int reg)
+{
+ int i;
+
+ /* the first two cases are special - stupid chip design! */
+ if (val > 27500) {
+ i = 10;
+ } else if (val > 22500) {
+ i = 11;
+ } else {
+ for (i = 9; i > 0; i--) {
+ if (val > (PWM_FREQ[i] + PWM_FREQ[i - 1] + 1) / 2)
+ break;
+ }
+ }
+
+ return (reg & 0xf0) | i;
+}
+
+/*
+ * PWM ramp rate
+ * Register layout:
+ * reg[0] = [OFF3, OFF2, OFF1, RES, RR1-E, RR1-2, RR1-1, RR1-0]
+ * reg[1] = [RR2-E, RR2-2, RR2-1, RR2-0, RR3-E, RR3-2, RR3-1, RR3-0]
+ */
+static const u8 PWM_RR[] = {206, 104, 69, 41, 26, 18, 10, 5};
+
+static inline int PWM_RR_FROM_REG(int reg, int ix)
+{
+ int rr = (ix == 1) ? reg >> 4 : reg;
+
+ return (rr & 0x08) ? PWM_RR[rr & 0x07] : 0;
+}
+
+static int PWM_RR_TO_REG(long val, int ix, int reg)
+{
+ int i;
+
+ for (i = 0; i < 7; i++) {
+ if (val > (PWM_RR[i] + PWM_RR[i + 1] + 1) / 2)
+ break;
+ }
+
+ return (ix == 1) ? (reg & 0x8f) | (i << 4) : (reg & 0xf8) | i;
+}
+
+/* PWM ramp rate enable */
+static inline int PWM_RR_EN_FROM_REG(int reg, int ix)
+{
+ return PWM_RR_FROM_REG(reg, ix) ? 1 : 0;
+}
+
+static inline int PWM_RR_EN_TO_REG(long val, int ix, int reg)
+{
+ int en = (ix == 1) ? 0x80 : 0x08;
+
+ return val ? reg | en : reg & ~en;
+}
+
+/*
+ * PWM min/off
+ * The PWM min/off bits are part of the PMW ramp rate register 0 (see above for
+ * the register layout).
+ */
+static inline int PWM_OFF_FROM_REG(int reg, int ix)
+{
+ return (reg >> (ix + 5)) & 0x01;
+}
+
+static inline int PWM_OFF_TO_REG(int val, int ix, int reg)
+{
+ return (reg & ~(1 << (ix + 5))) | ((val & 0x01) << (ix + 5));
+}
+
+/* ---------------------------------------------------------------------
+ * Device I/O access
+ *
+ * ISA access is performed through an index/data register pair and needs to
+ * be protected by a mutex during runtime (not required for initialization).
+ * We use data->update_lock for this and need to ensure that we acquire it
+ * before calling dme1737_read or dme1737_write.
+ * --------------------------------------------------------------------- */
+
+static u8 dme1737_read(const struct dme1737_data *data, u8 reg)
+{
+ struct i2c_client *client = data->client;
+ s32 val;
+
+ if (client) { /* I2C device */
+ val = i2c_smbus_read_byte_data(client, reg);
+
+ if (val < 0) {
+ dev_warn(&client->dev,
+ "Read from register 0x%02x failed! %s\n",
+ reg, DO_REPORT);
+ }
+ } else { /* ISA device */
+ outb(reg, data->addr);
+ val = inb(data->addr + 1);
+ }
+
+ return val;
+}
+
+static s32 dme1737_write(const struct dme1737_data *data, u8 reg, u8 val)
+{
+ struct i2c_client *client = data->client;
+ s32 res = 0;
+
+ if (client) { /* I2C device */
+ res = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (res < 0) {
+ dev_warn(&client->dev,
+ "Write to register 0x%02x failed! %s\n",
+ reg, DO_REPORT);
+ }
+ } else { /* ISA device */
+ outb(reg, data->addr);
+ outb(val, data->addr + 1);
+ }
+
+ return res;
+}
+
+static struct dme1737_data *dme1737_update_device(struct device *dev)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ int ix;
+ u8 lsb[6];
+
+ mutex_lock(&data->update_lock);
+
+ /* Enable a Vbat monitoring cycle every 10 mins */
+ if (time_after(jiffies, data->last_vbat + 600 * HZ) || !data->valid) {
+ dme1737_write(data, DME1737_REG_CONFIG, dme1737_read(data,
+ DME1737_REG_CONFIG) | 0x10);
+ data->last_vbat = jiffies;
+ }
+
+ /* Sample register contents every 1 sec */
+ if (time_after(jiffies, data->last_update + HZ) || !data->valid) {
+ if (data->has_features & HAS_VID) {
+ data->vid = dme1737_read(data, DME1737_REG_VID) &
+ 0x3f;
+ }
+
+ /* In (voltage) registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
+ /*
+ * Voltage inputs are stored as 16 bit values even
+ * though they have only 12 bits resolution. This is
+ * to make it consistent with the temp inputs.
+ */
+ if (ix == 7 && !(data->has_features & HAS_IN7))
+ continue;
+ data->in[ix] = dme1737_read(data,
+ DME1737_REG_IN(ix)) << 8;
+ data->in_min[ix] = dme1737_read(data,
+ DME1737_REG_IN_MIN(ix));
+ data->in_max[ix] = dme1737_read(data,
+ DME1737_REG_IN_MAX(ix));
+ }
+
+ /* Temp registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) {
+ /*
+ * Temp inputs are stored as 16 bit values even
+ * though they have only 12 bits resolution. This is
+ * to take advantage of implicit conversions between
+ * register values (2's complement) and temp values
+ * (signed decimal).
+ */
+ data->temp[ix] = dme1737_read(data,
+ DME1737_REG_TEMP(ix)) << 8;
+ data->temp_min[ix] = dme1737_read(data,
+ DME1737_REG_TEMP_MIN(ix));
+ data->temp_max[ix] = dme1737_read(data,
+ DME1737_REG_TEMP_MAX(ix));
+ if (data->has_features & HAS_TEMP_OFFSET) {
+ data->temp_offset[ix] = dme1737_read(data,
+ DME1737_REG_TEMP_OFFSET(ix));
+ }
+ }
+
+ /*
+ * In and temp LSB registers
+ * The LSBs are latched when the MSBs are read, so the order in
+ * which the registers are read (MSB first, then LSB) is
+ * important!
+ */
+ for (ix = 0; ix < ARRAY_SIZE(lsb); ix++) {
+ if (ix == 5 && !(data->has_features & HAS_IN7))
+ continue;
+ lsb[ix] = dme1737_read(data,
+ DME1737_REG_IN_TEMP_LSB(ix));
+ }
+ for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
+ if (ix == 7 && !(data->has_features & HAS_IN7))
+ continue;
+ data->in[ix] |= (lsb[DME1737_REG_IN_LSB[ix]] <<
+ DME1737_REG_IN_LSB_SHL[ix]) & 0xf0;
+ }
+ for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) {
+ data->temp[ix] |= (lsb[DME1737_REG_TEMP_LSB[ix]] <<
+ DME1737_REG_TEMP_LSB_SHL[ix]) & 0xf0;
+ }
+
+ /* Fan registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
+ /*
+ * Skip reading registers if optional fans are not
+ * present
+ */
+ if (!(data->has_features & HAS_FAN(ix)))
+ continue;
+ data->fan[ix] = dme1737_read(data,
+ DME1737_REG_FAN(ix));
+ data->fan[ix] |= dme1737_read(data,
+ DME1737_REG_FAN(ix) + 1) << 8;
+ data->fan_min[ix] = dme1737_read(data,
+ DME1737_REG_FAN_MIN(ix));
+ data->fan_min[ix] |= dme1737_read(data,
+ DME1737_REG_FAN_MIN(ix) + 1) << 8;
+ data->fan_opt[ix] = dme1737_read(data,
+ DME1737_REG_FAN_OPT(ix));
+ /* fan_max exists only for fan[5-6] */
+ if (ix > 3) {
+ data->fan_max[ix - 4] = dme1737_read(data,
+ DME1737_REG_FAN_MAX(ix));
+ }
+ }
+
+ /* PWM registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->pwm); ix++) {
+ /*
+ * Skip reading registers if optional PWMs are not
+ * present
+ */
+ if (!(data->has_features & HAS_PWM(ix)))
+ continue;
+ data->pwm[ix] = dme1737_read(data,
+ DME1737_REG_PWM(ix));
+ data->pwm_freq[ix] = dme1737_read(data,
+ DME1737_REG_PWM_FREQ(ix));
+ /* pwm_config and pwm_min exist only for pwm[1-3] */
+ if (ix < 3) {
+ data->pwm_config[ix] = dme1737_read(data,
+ DME1737_REG_PWM_CONFIG(ix));
+ data->pwm_min[ix] = dme1737_read(data,
+ DME1737_REG_PWM_MIN(ix));
+ }
+ }
+ for (ix = 0; ix < ARRAY_SIZE(data->pwm_rr); ix++) {
+ data->pwm_rr[ix] = dme1737_read(data,
+ DME1737_REG_PWM_RR(ix));
+ }
+
+ /* Thermal zone registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) {
+ /* Skip reading registers if zone3 is not present */
+ if ((ix == 2) && !(data->has_features & HAS_ZONE3))
+ continue;
+ /* sch5127 zone2 registers are special */
+ if ((ix == 1) && (data->type == sch5127)) {
+ data->zone_low[1] = dme1737_read(data,
+ DME1737_REG_ZONE_LOW(2));
+ data->zone_abs[1] = dme1737_read(data,
+ DME1737_REG_ZONE_ABS(2));
+ } else {
+ data->zone_low[ix] = dme1737_read(data,
+ DME1737_REG_ZONE_LOW(ix));
+ data->zone_abs[ix] = dme1737_read(data,
+ DME1737_REG_ZONE_ABS(ix));
+ }
+ }
+ if (data->has_features & HAS_ZONE_HYST) {
+ for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
+ data->zone_hyst[ix] = dme1737_read(data,
+ DME1737_REG_ZONE_HYST(ix));
+ }
+ }
+
+ /* Alarm registers */
+ data->alarms = dme1737_read(data,
+ DME1737_REG_ALARM1);
+ /*
+ * Bit 7 tells us if the other alarm registers are non-zero and
+ * therefore also need to be read
+ */
+ if (data->alarms & 0x80) {
+ data->alarms |= dme1737_read(data,
+ DME1737_REG_ALARM2) << 8;
+ data->alarms |= dme1737_read(data,
+ DME1737_REG_ALARM3) << 16;
+ }
+
+ /*
+ * The ISA chips require explicit clearing of alarm bits.
+ * Don't worry, an alarm will come back if the condition
+ * that causes it still exists
+ */
+ if (!data->client) {
+ if (data->alarms & 0xff0000)
+ dme1737_write(data, DME1737_REG_ALARM3, 0xff);
+ if (data->alarms & 0xff00)
+ dme1737_write(data, DME1737_REG_ALARM2, 0xff);
+ if (data->alarms & 0xff)
+ dme1737_write(data, DME1737_REG_ALARM1, 0xff);
+ }
+
+ data->last_update = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* ---------------------------------------------------------------------
+ * Voltage sysfs attributes
+ * ix = [0-7]
+ * --------------------------------------------------------------------- */
+
+#define SYS_IN_INPUT 0
+#define SYS_IN_MIN 1
+#define SYS_IN_MAX 2
+#define SYS_IN_ALARM 3
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dme1737_data *data = dme1737_update_device(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SYS_IN_INPUT:
+ res = IN_FROM_REG(data->in[ix], data->in_nominal[ix], 16);
+ break;
+ case SYS_IN_MIN:
+ res = IN_FROM_REG(data->in_min[ix], data->in_nominal[ix], 8);
+ break;
+ case SYS_IN_MAX:
+ res = IN_FROM_REG(data->in_max[ix], data->in_nominal[ix], 8);
+ break;
+ case SYS_IN_ALARM:
+ res = (data->alarms >> DME1737_BIT_ALARM_IN[ix]) & 0x01;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SYS_IN_MIN:
+ data->in_min[ix] = IN_TO_REG(val, data->in_nominal[ix]);
+ dme1737_write(data, DME1737_REG_IN_MIN(ix),
+ data->in_min[ix]);
+ break;
+ case SYS_IN_MAX:
+ data->in_max[ix] = IN_TO_REG(val, data->in_nominal[ix]);
+ dme1737_write(data, DME1737_REG_IN_MAX(ix),
+ data->in_max[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Temperature sysfs attributes
+ * ix = [0-2]
+ * --------------------------------------------------------------------- */
+
+#define SYS_TEMP_INPUT 0
+#define SYS_TEMP_MIN 1
+#define SYS_TEMP_MAX 2
+#define SYS_TEMP_OFFSET 3
+#define SYS_TEMP_ALARM 4
+#define SYS_TEMP_FAULT 5
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dme1737_data *data = dme1737_update_device(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SYS_TEMP_INPUT:
+ res = TEMP_FROM_REG(data->temp[ix], 16);
+ break;
+ case SYS_TEMP_MIN:
+ res = TEMP_FROM_REG(data->temp_min[ix], 8);
+ break;
+ case SYS_TEMP_MAX:
+ res = TEMP_FROM_REG(data->temp_max[ix], 8);
+ break;
+ case SYS_TEMP_OFFSET:
+ res = TEMP_FROM_REG(data->temp_offset[ix], 8);
+ break;
+ case SYS_TEMP_ALARM:
+ res = (data->alarms >> DME1737_BIT_ALARM_TEMP[ix]) & 0x01;
+ break;
+ case SYS_TEMP_FAULT:
+ res = (((u16)data->temp[ix] & 0xff00) == 0x8000);
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SYS_TEMP_MIN:
+ data->temp_min[ix] = TEMP_TO_REG(val);
+ dme1737_write(data, DME1737_REG_TEMP_MIN(ix),
+ data->temp_min[ix]);
+ break;
+ case SYS_TEMP_MAX:
+ data->temp_max[ix] = TEMP_TO_REG(val);
+ dme1737_write(data, DME1737_REG_TEMP_MAX(ix),
+ data->temp_max[ix]);
+ break;
+ case SYS_TEMP_OFFSET:
+ data->temp_offset[ix] = TEMP_TO_REG(val);
+ dme1737_write(data, DME1737_REG_TEMP_OFFSET(ix),
+ data->temp_offset[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Zone sysfs attributes
+ * ix = [0-2]
+ * --------------------------------------------------------------------- */
+
+#define SYS_ZONE_AUTO_CHANNELS_TEMP 0
+#define SYS_ZONE_AUTO_POINT1_TEMP_HYST 1
+#define SYS_ZONE_AUTO_POINT1_TEMP 2
+#define SYS_ZONE_AUTO_POINT2_TEMP 3
+#define SYS_ZONE_AUTO_POINT3_TEMP 4
+
+static ssize_t show_zone(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dme1737_data *data = dme1737_update_device(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SYS_ZONE_AUTO_CHANNELS_TEMP:
+ /* check config2 for non-standard temp-to-zone mapping */
+ if ((ix == 1) && (data->config2 & 0x02))
+ res = 4;
+ else
+ res = 1 << ix;
+ break;
+ case SYS_ZONE_AUTO_POINT1_TEMP_HYST:
+ res = TEMP_FROM_REG(data->zone_low[ix], 8) -
+ TEMP_HYST_FROM_REG(data->zone_hyst[ix == 2], ix);
+ break;
+ case SYS_ZONE_AUTO_POINT1_TEMP:
+ res = TEMP_FROM_REG(data->zone_low[ix], 8);
+ break;
+ case SYS_ZONE_AUTO_POINT2_TEMP:
+ /* pwm_freq holds the temp range bits in the upper nibble */
+ res = TEMP_FROM_REG(data->zone_low[ix], 8) +
+ TEMP_RANGE_FROM_REG(data->pwm_freq[ix]);
+ break;
+ case SYS_ZONE_AUTO_POINT3_TEMP:
+ res = TEMP_FROM_REG(data->zone_abs[ix], 8);
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_zone(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val;
+ int temp;
+ int err;
+ u8 reg;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SYS_ZONE_AUTO_POINT1_TEMP_HYST:
+ /* Refresh the cache */
+ data->zone_low[ix] = dme1737_read(data,
+ DME1737_REG_ZONE_LOW(ix));
+ /* Modify the temp hyst value */
+ temp = TEMP_FROM_REG(data->zone_low[ix], 8);
+ reg = dme1737_read(data, DME1737_REG_ZONE_HYST(ix == 2));
+ data->zone_hyst[ix == 2] = TEMP_HYST_TO_REG(temp, val, ix, reg);
+ dme1737_write(data, DME1737_REG_ZONE_HYST(ix == 2),
+ data->zone_hyst[ix == 2]);
+ break;
+ case SYS_ZONE_AUTO_POINT1_TEMP:
+ data->zone_low[ix] = TEMP_TO_REG(val);
+ dme1737_write(data, DME1737_REG_ZONE_LOW(ix),
+ data->zone_low[ix]);
+ break;
+ case SYS_ZONE_AUTO_POINT2_TEMP:
+ /* Refresh the cache */
+ data->zone_low[ix] = dme1737_read(data,
+ DME1737_REG_ZONE_LOW(ix));
+ /*
+ * Modify the temp range value (which is stored in the upper
+ * nibble of the pwm_freq register)
+ */
+ temp = TEMP_FROM_REG(data->zone_low[ix], 8);
+ val = clamp_val(val, temp, temp + 80000);
+ reg = dme1737_read(data, DME1737_REG_PWM_FREQ(ix));
+ data->pwm_freq[ix] = TEMP_RANGE_TO_REG(val - temp, reg);
+ dme1737_write(data, DME1737_REG_PWM_FREQ(ix),
+ data->pwm_freq[ix]);
+ break;
+ case SYS_ZONE_AUTO_POINT3_TEMP:
+ data->zone_abs[ix] = TEMP_TO_REG(val);
+ dme1737_write(data, DME1737_REG_ZONE_ABS(ix),
+ data->zone_abs[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Fan sysfs attributes
+ * ix = [0-5]
+ * --------------------------------------------------------------------- */
+
+#define SYS_FAN_INPUT 0
+#define SYS_FAN_MIN 1
+#define SYS_FAN_MAX 2
+#define SYS_FAN_ALARM 3
+#define SYS_FAN_TYPE 4
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dme1737_data *data = dme1737_update_device(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SYS_FAN_INPUT:
+ res = FAN_FROM_REG(data->fan[ix],
+ ix < 4 ? 0 :
+ FAN_TPC_FROM_REG(data->fan_opt[ix]));
+ break;
+ case SYS_FAN_MIN:
+ res = FAN_FROM_REG(data->fan_min[ix],
+ ix < 4 ? 0 :
+ FAN_TPC_FROM_REG(data->fan_opt[ix]));
+ break;
+ case SYS_FAN_MAX:
+ /* only valid for fan[5-6] */
+ res = FAN_MAX_FROM_REG(data->fan_max[ix - 4]);
+ break;
+ case SYS_FAN_ALARM:
+ res = (data->alarms >> DME1737_BIT_ALARM_FAN[ix]) & 0x01;
+ break;
+ case SYS_FAN_TYPE:
+ /* only valid for fan[1-4] */
+ res = FAN_TYPE_FROM_REG(data->fan_opt[ix]);
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SYS_FAN_MIN:
+ if (ix < 4) {
+ data->fan_min[ix] = FAN_TO_REG(val, 0);
+ } else {
+ /* Refresh the cache */
+ data->fan_opt[ix] = dme1737_read(data,
+ DME1737_REG_FAN_OPT(ix));
+ /* Modify the fan min value */
+ data->fan_min[ix] = FAN_TO_REG(val,
+ FAN_TPC_FROM_REG(data->fan_opt[ix]));
+ }
+ dme1737_write(data, DME1737_REG_FAN_MIN(ix),
+ data->fan_min[ix] & 0xff);
+ dme1737_write(data, DME1737_REG_FAN_MIN(ix) + 1,
+ data->fan_min[ix] >> 8);
+ break;
+ case SYS_FAN_MAX:
+ /* Only valid for fan[5-6] */
+ data->fan_max[ix - 4] = FAN_MAX_TO_REG(val);
+ dme1737_write(data, DME1737_REG_FAN_MAX(ix),
+ data->fan_max[ix - 4]);
+ break;
+ case SYS_FAN_TYPE:
+ /* Only valid for fan[1-4] */
+ if (!(val == 1 || val == 2 || val == 4)) {
+ count = -EINVAL;
+ dev_warn(dev,
+ "Fan type value %ld not supported. Choose one of 1, 2, or 4.\n",
+ val);
+ goto exit;
+ }
+ data->fan_opt[ix] = FAN_TYPE_TO_REG(val, dme1737_read(data,
+ DME1737_REG_FAN_OPT(ix)));
+ dme1737_write(data, DME1737_REG_FAN_OPT(ix),
+ data->fan_opt[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+exit:
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * PWM sysfs attributes
+ * ix = [0-4]
+ * --------------------------------------------------------------------- */
+
+#define SYS_PWM 0
+#define SYS_PWM_FREQ 1
+#define SYS_PWM_ENABLE 2
+#define SYS_PWM_RAMP_RATE 3
+#define SYS_PWM_AUTO_CHANNELS_ZONE 4
+#define SYS_PWM_AUTO_PWM_MIN 5
+#define SYS_PWM_AUTO_POINT1_PWM 6
+#define SYS_PWM_AUTO_POINT2_PWM 7
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dme1737_data *data = dme1737_update_device(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SYS_PWM:
+ if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 0)
+ res = 255;
+ else
+ res = data->pwm[ix];
+ break;
+ case SYS_PWM_FREQ:
+ res = PWM_FREQ_FROM_REG(data->pwm_freq[ix]);
+ break;
+ case SYS_PWM_ENABLE:
+ if (ix >= 3)
+ res = 1; /* pwm[5-6] hard-wired to manual mode */
+ else
+ res = PWM_EN_FROM_REG(data->pwm_config[ix]);
+ break;
+ case SYS_PWM_RAMP_RATE:
+ /* Only valid for pwm[1-3] */
+ res = PWM_RR_FROM_REG(data->pwm_rr[ix > 0], ix);
+ break;
+ case SYS_PWM_AUTO_CHANNELS_ZONE:
+ /* Only valid for pwm[1-3] */
+ if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2)
+ res = PWM_ACZ_FROM_REG(data->pwm_config[ix]);
+ else
+ res = data->pwm_acz[ix];
+ break;
+ case SYS_PWM_AUTO_PWM_MIN:
+ /* Only valid for pwm[1-3] */
+ if (PWM_OFF_FROM_REG(data->pwm_rr[0], ix))
+ res = data->pwm_min[ix];
+ else
+ res = 0;
+ break;
+ case SYS_PWM_AUTO_POINT1_PWM:
+ /* Only valid for pwm[1-3] */
+ res = data->pwm_min[ix];
+ break;
+ case SYS_PWM_AUTO_POINT2_PWM:
+ /* Only valid for pwm[1-3] */
+ res = 255; /* hard-wired */
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static struct attribute *dme1737_pwm_chmod_attr[];
+static void dme1737_chmod_file(struct device*, struct attribute*, umode_t);
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2
+ *sensor_attr_2 = to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SYS_PWM:
+ data->pwm[ix] = clamp_val(val, 0, 255);
+ dme1737_write(data, DME1737_REG_PWM(ix), data->pwm[ix]);
+ break;
+ case SYS_PWM_FREQ:
+ data->pwm_freq[ix] = PWM_FREQ_TO_REG(val, dme1737_read(data,
+ DME1737_REG_PWM_FREQ(ix)));
+ dme1737_write(data, DME1737_REG_PWM_FREQ(ix),
+ data->pwm_freq[ix]);
+ break;
+ case SYS_PWM_ENABLE:
+ /* Only valid for pwm[1-3] */
+ if (val < 0 || val > 2) {
+ count = -EINVAL;
+ dev_warn(dev,
+ "PWM enable %ld not supported. Choose one of 0, 1, or 2.\n",
+ val);
+ goto exit;
+ }
+ /* Refresh the cache */
+ data->pwm_config[ix] = dme1737_read(data,
+ DME1737_REG_PWM_CONFIG(ix));
+ if (val == PWM_EN_FROM_REG(data->pwm_config[ix])) {
+ /* Bail out if no change */
+ goto exit;
+ }
+ /* Do some housekeeping if we are currently in auto mode */
+ if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) {
+ /* Save the current zone channel assignment */
+ data->pwm_acz[ix] = PWM_ACZ_FROM_REG(
+ data->pwm_config[ix]);
+ /* Save the current ramp rate state and disable it */
+ data->pwm_rr[ix > 0] = dme1737_read(data,
+ DME1737_REG_PWM_RR(ix > 0));
+ data->pwm_rr_en &= ~(1 << ix);
+ if (PWM_RR_EN_FROM_REG(data->pwm_rr[ix > 0], ix)) {
+ data->pwm_rr_en |= (1 << ix);
+ data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(0, ix,
+ data->pwm_rr[ix > 0]);
+ dme1737_write(data,
+ DME1737_REG_PWM_RR(ix > 0),
+ data->pwm_rr[ix > 0]);
+ }
+ }
+ /* Set the new PWM mode */
+ switch (val) {
+ case 0:
+ /* Change permissions of pwm[ix] to read-only */
+ dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
+ S_IRUGO);
+ /* Turn fan fully on */
+ data->pwm_config[ix] = PWM_EN_TO_REG(0,
+ data->pwm_config[ix]);
+ dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
+ data->pwm_config[ix]);
+ break;
+ case 1:
+ /* Turn on manual mode */
+ data->pwm_config[ix] = PWM_EN_TO_REG(1,
+ data->pwm_config[ix]);
+ dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
+ data->pwm_config[ix]);
+ /* Change permissions of pwm[ix] to read-writeable */
+ dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
+ S_IRUGO | S_IWUSR);
+ break;
+ case 2:
+ /* Change permissions of pwm[ix] to read-only */
+ dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
+ S_IRUGO);
+ /*
+ * Turn on auto mode using the saved zone channel
+ * assignment
+ */
+ data->pwm_config[ix] = PWM_ACZ_TO_REG(
+ data->pwm_acz[ix],
+ data->pwm_config[ix]);
+ dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
+ data->pwm_config[ix]);
+ /* Enable PWM ramp rate if previously enabled */
+ if (data->pwm_rr_en & (1 << ix)) {
+ data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(1, ix,
+ dme1737_read(data,
+ DME1737_REG_PWM_RR(ix > 0)));
+ dme1737_write(data,
+ DME1737_REG_PWM_RR(ix > 0),
+ data->pwm_rr[ix > 0]);
+ }
+ break;
+ }
+ break;
+ case SYS_PWM_RAMP_RATE:
+ /* Only valid for pwm[1-3] */
+ /* Refresh the cache */
+ data->pwm_config[ix] = dme1737_read(data,
+ DME1737_REG_PWM_CONFIG(ix));
+ data->pwm_rr[ix > 0] = dme1737_read(data,
+ DME1737_REG_PWM_RR(ix > 0));
+ /* Set the ramp rate value */
+ if (val > 0) {
+ data->pwm_rr[ix > 0] = PWM_RR_TO_REG(val, ix,
+ data->pwm_rr[ix > 0]);
+ }
+ /*
+ * Enable/disable the feature only if the associated PWM
+ * output is in automatic mode.
+ */
+ if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) {
+ data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(val > 0, ix,
+ data->pwm_rr[ix > 0]);
+ }
+ dme1737_write(data, DME1737_REG_PWM_RR(ix > 0),
+ data->pwm_rr[ix > 0]);
+ break;
+ case SYS_PWM_AUTO_CHANNELS_ZONE:
+ /* Only valid for pwm[1-3] */
+ if (!(val == 1 || val == 2 || val == 4 ||
+ val == 6 || val == 7)) {
+ count = -EINVAL;
+ dev_warn(dev,
+ "PWM auto channels zone %ld not supported. Choose one of 1, 2, 4, 6, "
+ "or 7.\n", val);
+ goto exit;
+ }
+ /* Refresh the cache */
+ data->pwm_config[ix] = dme1737_read(data,
+ DME1737_REG_PWM_CONFIG(ix));
+ if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) {
+ /*
+ * PWM is already in auto mode so update the temp
+ * channel assignment
+ */
+ data->pwm_config[ix] = PWM_ACZ_TO_REG(val,
+ data->pwm_config[ix]);
+ dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
+ data->pwm_config[ix]);
+ } else {
+ /*
+ * PWM is not in auto mode so we save the temp
+ * channel assignment for later use
+ */
+ data->pwm_acz[ix] = val;
+ }
+ break;
+ case SYS_PWM_AUTO_PWM_MIN:
+ /* Only valid for pwm[1-3] */
+ /* Refresh the cache */
+ data->pwm_min[ix] = dme1737_read(data,
+ DME1737_REG_PWM_MIN(ix));
+ /*
+ * There are only 2 values supported for the auto_pwm_min
+ * value: 0 or auto_point1_pwm. So if the temperature drops
+ * below the auto_point1_temp_hyst value, the fan either turns
+ * off or runs at auto_point1_pwm duty-cycle.
+ */
+ if (val > ((data->pwm_min[ix] + 1) / 2)) {
+ data->pwm_rr[0] = PWM_OFF_TO_REG(1, ix,
+ dme1737_read(data,
+ DME1737_REG_PWM_RR(0)));
+ } else {
+ data->pwm_rr[0] = PWM_OFF_TO_REG(0, ix,
+ dme1737_read(data,
+ DME1737_REG_PWM_RR(0)));
+ }
+ dme1737_write(data, DME1737_REG_PWM_RR(0),
+ data->pwm_rr[0]);
+ break;
+ case SYS_PWM_AUTO_POINT1_PWM:
+ /* Only valid for pwm[1-3] */
+ data->pwm_min[ix] = clamp_val(val, 0, 255);
+ dme1737_write(data, DME1737_REG_PWM_MIN(ix),
+ data->pwm_min[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown function %d.\n", fn);
+ }
+exit:
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Miscellaneous sysfs attributes
+ * --------------------------------------------------------------------- */
+
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct dme1737_data *data = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dme1737_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 ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dme1737_data *data = dme1737_update_device(dev);
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+/* ---------------------------------------------------------------------
+ * Sysfs device attribute defines and structs
+ * --------------------------------------------------------------------- */
+
+/* Voltages 0-7 */
+
+#define SENSOR_DEVICE_ATTR_IN(ix) \
+static SENSOR_DEVICE_ATTR_2(in##ix##_input, S_IRUGO, \
+ show_in, NULL, SYS_IN_INPUT, ix); \
+static SENSOR_DEVICE_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SYS_IN_MIN, ix); \
+static SENSOR_DEVICE_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SYS_IN_MAX, ix); \
+static SENSOR_DEVICE_ATTR_2(in##ix##_alarm, S_IRUGO, \
+ show_in, NULL, SYS_IN_ALARM, ix)
+
+SENSOR_DEVICE_ATTR_IN(0);
+SENSOR_DEVICE_ATTR_IN(1);
+SENSOR_DEVICE_ATTR_IN(2);
+SENSOR_DEVICE_ATTR_IN(3);
+SENSOR_DEVICE_ATTR_IN(4);
+SENSOR_DEVICE_ATTR_IN(5);
+SENSOR_DEVICE_ATTR_IN(6);
+SENSOR_DEVICE_ATTR_IN(7);
+
+/* Temperatures 1-3 */
+
+#define SENSOR_DEVICE_ATTR_TEMP(ix) \
+static SENSOR_DEVICE_ATTR_2(temp##ix##_input, S_IRUGO, \
+ show_temp, NULL, SYS_TEMP_INPUT, ix-1); \
+static SENSOR_DEVICE_ATTR_2(temp##ix##_min, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SYS_TEMP_MIN, ix-1); \
+static SENSOR_DEVICE_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SYS_TEMP_MAX, ix-1); \
+static SENSOR_DEVICE_ATTR_2(temp##ix##_offset, S_IRUGO, \
+ show_temp, set_temp, SYS_TEMP_OFFSET, ix-1); \
+static SENSOR_DEVICE_ATTR_2(temp##ix##_alarm, S_IRUGO, \
+ show_temp, NULL, SYS_TEMP_ALARM, ix-1); \
+static SENSOR_DEVICE_ATTR_2(temp##ix##_fault, S_IRUGO, \
+ show_temp, NULL, SYS_TEMP_FAULT, ix-1)
+
+SENSOR_DEVICE_ATTR_TEMP(1);
+SENSOR_DEVICE_ATTR_TEMP(2);
+SENSOR_DEVICE_ATTR_TEMP(3);
+
+/* Zones 1-3 */
+
+#define SENSOR_DEVICE_ATTR_ZONE(ix) \
+static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_channels_temp, S_IRUGO, \
+ show_zone, NULL, SYS_ZONE_AUTO_CHANNELS_TEMP, ix-1); \
+static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point1_temp_hyst, S_IRUGO, \
+ show_zone, set_zone, SYS_ZONE_AUTO_POINT1_TEMP_HYST, ix-1); \
+static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point1_temp, S_IRUGO, \
+ show_zone, set_zone, SYS_ZONE_AUTO_POINT1_TEMP, ix-1); \
+static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point2_temp, S_IRUGO, \
+ show_zone, set_zone, SYS_ZONE_AUTO_POINT2_TEMP, ix-1); \
+static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point3_temp, S_IRUGO, \
+ show_zone, set_zone, SYS_ZONE_AUTO_POINT3_TEMP, ix-1)
+
+SENSOR_DEVICE_ATTR_ZONE(1);
+SENSOR_DEVICE_ATTR_ZONE(2);
+SENSOR_DEVICE_ATTR_ZONE(3);
+
+/* Fans 1-4 */
+
+#define SENSOR_DEVICE_ATTR_FAN_1TO4(ix) \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_input, S_IRUGO, \
+ show_fan, NULL, SYS_FAN_INPUT, ix-1); \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SYS_FAN_MIN, ix-1); \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_alarm, S_IRUGO, \
+ show_fan, NULL, SYS_FAN_ALARM, ix-1); \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_type, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SYS_FAN_TYPE, ix-1)
+
+SENSOR_DEVICE_ATTR_FAN_1TO4(1);
+SENSOR_DEVICE_ATTR_FAN_1TO4(2);
+SENSOR_DEVICE_ATTR_FAN_1TO4(3);
+SENSOR_DEVICE_ATTR_FAN_1TO4(4);
+
+/* Fans 5-6 */
+
+#define SENSOR_DEVICE_ATTR_FAN_5TO6(ix) \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_input, S_IRUGO, \
+ show_fan, NULL, SYS_FAN_INPUT, ix-1); \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SYS_FAN_MIN, ix-1); \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_alarm, S_IRUGO, \
+ show_fan, NULL, SYS_FAN_ALARM, ix-1); \
+static SENSOR_DEVICE_ATTR_2(fan##ix##_max, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SYS_FAN_MAX, ix-1)
+
+SENSOR_DEVICE_ATTR_FAN_5TO6(5);
+SENSOR_DEVICE_ATTR_FAN_5TO6(6);
+
+/* PWMs 1-3 */
+
+#define SENSOR_DEVICE_ATTR_PWM_1TO3(ix) \
+static SENSOR_DEVICE_ATTR_2(pwm##ix, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_freq, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM_FREQ, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_enable, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM_ENABLE, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_ramp_rate, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM_RAMP_RATE, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_channels_zone, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM_AUTO_CHANNELS_ZONE, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_pwm_min, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM_AUTO_PWM_MIN, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_point1_pwm, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM_AUTO_POINT1_PWM, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_point2_pwm, S_IRUGO, \
+ show_pwm, NULL, SYS_PWM_AUTO_POINT2_PWM, ix-1)
+
+SENSOR_DEVICE_ATTR_PWM_1TO3(1);
+SENSOR_DEVICE_ATTR_PWM_1TO3(2);
+SENSOR_DEVICE_ATTR_PWM_1TO3(3);
+
+/* PWMs 5-6 */
+
+#define SENSOR_DEVICE_ATTR_PWM_5TO6(ix) \
+static SENSOR_DEVICE_ATTR_2(pwm##ix, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_freq, S_IRUGO, \
+ show_pwm, set_pwm, SYS_PWM_FREQ, ix-1); \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_enable, S_IRUGO, \
+ show_pwm, NULL, SYS_PWM_ENABLE, ix-1)
+
+SENSOR_DEVICE_ATTR_PWM_5TO6(5);
+SENSOR_DEVICE_ATTR_PWM_5TO6(6);
+
+/* Misc */
+
+static DEVICE_ATTR_RW(vrm);
+static DEVICE_ATTR_RO(cpu0_vid);
+static DEVICE_ATTR_RO(name); /* for ISA devices */
+
+/*
+ * This struct holds all the attributes that are always present and need to be
+ * created unconditionally. The attributes that need modification of their
+ * permissions are created read-only and write permissions are added or removed
+ * on the fly when required
+ */
+static struct attribute *dme1737_attr[] = {
+ /* Voltages */
+ &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,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ /* Temperatures */
+ &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_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.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_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.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_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ /* Zones */
+ &sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_group = {
+ .attrs = dme1737_attr,
+};
+
+/*
+ * The following struct holds temp offset attributes, which are not available
+ * in all chips. The following chips support them:
+ * DME1737, SCH311x
+ */
+static struct attribute *dme1737_temp_offset_attr[] = {
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_temp_offset_group = {
+ .attrs = dme1737_temp_offset_attr,
+};
+
+/*
+ * The following struct holds VID related attributes, which are not available
+ * in all chips. The following chips support them:
+ * DME1737
+ */
+static struct attribute *dme1737_vid_attr[] = {
+ &dev_attr_vrm.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_vid_group = {
+ .attrs = dme1737_vid_attr,
+};
+
+/*
+ * The following struct holds temp zone 3 related attributes, which are not
+ * available in all chips. The following chips support them:
+ * DME1737, SCH311x, SCH5027
+ */
+static struct attribute *dme1737_zone3_attr[] = {
+ &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_zone3_group = {
+ .attrs = dme1737_zone3_attr,
+};
+
+
+/*
+ * The following struct holds temp zone hysteresis related attributes, which
+ * are not available in all chips. The following chips support them:
+ * DME1737, SCH311x
+ */
+static struct attribute *dme1737_zone_hyst_attr[] = {
+ &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_zone_hyst_group = {
+ .attrs = dme1737_zone_hyst_attr,
+};
+
+/*
+ * The following struct holds voltage in7 related attributes, which
+ * are not available in all chips. The following chips support them:
+ * SCH5127
+ */
+static struct attribute *dme1737_in7_attr[] = {
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_in7_group = {
+ .attrs = dme1737_in7_attr,
+};
+
+/*
+ * The following structs hold the PWM attributes, some of which are optional.
+ * Their creation depends on the chip configuration which is determined during
+ * module load.
+ */
+static struct attribute *dme1737_pwm1_attr[] = {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm2_attr[] = {
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm3_attr[] = {
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm5_attr[] = {
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm5_enable.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm6_attr[] = {
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm6_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm6_enable.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_pwm_group[] = {
+ { .attrs = dme1737_pwm1_attr },
+ { .attrs = dme1737_pwm2_attr },
+ { .attrs = dme1737_pwm3_attr },
+ { .attrs = NULL },
+ { .attrs = dme1737_pwm5_attr },
+ { .attrs = dme1737_pwm6_attr },
+};
+
+/*
+ * The following struct holds auto PWM min attributes, which are not available
+ * in all chips. Their creation depends on the chip type which is determined
+ * during module load.
+ */
+static struct attribute *dme1737_auto_pwm_min_attr[] = {
+ &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
+};
+
+/*
+ * The following structs hold the fan attributes, some of which are optional.
+ * Their creation depends on the chip configuration which is determined during
+ * module load.
+ */
+static struct attribute *dme1737_fan1_attr[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_type.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_fan2_attr[] = {
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_type.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_fan3_attr[] = {
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_type.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_fan4_attr[] = {
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan4_type.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_fan5_attr[] = {
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan5_max.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_fan6_attr[] = {
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan6_max.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_fan_group[] = {
+ { .attrs = dme1737_fan1_attr },
+ { .attrs = dme1737_fan2_attr },
+ { .attrs = dme1737_fan3_attr },
+ { .attrs = dme1737_fan4_attr },
+ { .attrs = dme1737_fan5_attr },
+ { .attrs = dme1737_fan6_attr },
+};
+
+/*
+ * The permissions of the following zone attributes are changed to read-
+ * writeable if the chip is *not* locked. Otherwise they stay read-only.
+ */
+static struct attribute *dme1737_zone_chmod_attr[] = {
+ &sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_zone_chmod_group = {
+ .attrs = dme1737_zone_chmod_attr,
+};
+
+
+/*
+ * The permissions of the following zone 3 attributes are changed to read-
+ * writeable if the chip is *not* locked. Otherwise they stay read-only.
+ */
+static struct attribute *dme1737_zone3_chmod_attr[] = {
+ &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_zone3_chmod_group = {
+ .attrs = dme1737_zone3_chmod_attr,
+};
+
+/*
+ * The permissions of the following PWM attributes are changed to read-
+ * writeable if the chip is *not* locked and the respective PWM is available.
+ * Otherwise they stay read-only.
+ */
+static struct attribute *dme1737_pwm1_chmod_attr[] = {
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm2_chmod_attr[] = {
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm3_chmod_attr[] = {
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm5_chmod_attr[] = {
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_freq.dev_attr.attr,
+ NULL
+};
+static struct attribute *dme1737_pwm6_chmod_attr[] = {
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm6_freq.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dme1737_pwm_chmod_group[] = {
+ { .attrs = dme1737_pwm1_chmod_attr },
+ { .attrs = dme1737_pwm2_chmod_attr },
+ { .attrs = dme1737_pwm3_chmod_attr },
+ { .attrs = NULL },
+ { .attrs = dme1737_pwm5_chmod_attr },
+ { .attrs = dme1737_pwm6_chmod_attr },
+};
+
+/*
+ * Pwm[1-3] are read-writeable if the associated pwm is in manual mode and the
+ * chip is not locked. Otherwise they are read-only.
+ */
+static struct attribute *dme1737_pwm_chmod_attr[] = {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+};
+
+/* ---------------------------------------------------------------------
+ * Super-IO functions
+ * --------------------------------------------------------------------- */
+
+static inline void dme1737_sio_enter(int sio_cip)
+{
+ outb(0x55, sio_cip);
+}
+
+static inline void dme1737_sio_exit(int sio_cip)
+{
+ outb(0xaa, sio_cip);
+}
+
+static inline int dme1737_sio_inb(int sio_cip, int reg)
+{
+ outb(reg, sio_cip);
+ return inb(sio_cip + 1);
+}
+
+static inline void dme1737_sio_outb(int sio_cip, int reg, int val)
+{
+ outb(reg, sio_cip);
+ outb(val, sio_cip + 1);
+}
+
+/* ---------------------------------------------------------------------
+ * Device initialization
+ * --------------------------------------------------------------------- */
+
+static int dme1737_i2c_get_features(int, struct dme1737_data*);
+
+static void dme1737_chmod_file(struct device *dev,
+ struct attribute *attr, umode_t mode)
+{
+ if (sysfs_chmod_file(&dev->kobj, attr, mode)) {
+ dev_warn(dev, "Failed to change permissions of %s.\n",
+ attr->name);
+ }
+}
+
+static void dme1737_chmod_group(struct device *dev,
+ const struct attribute_group *group,
+ umode_t mode)
+{
+ struct attribute **attr;
+
+ for (attr = group->attrs; *attr; attr++)
+ dme1737_chmod_file(dev, *attr, mode);
+}
+
+static void dme1737_remove_files(struct device *dev)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ int ix;
+
+ for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
+ if (data->has_features & HAS_FAN(ix)) {
+ sysfs_remove_group(&dev->kobj,
+ &dme1737_fan_group[ix]);
+ }
+ }
+
+ for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
+ if (data->has_features & HAS_PWM(ix)) {
+ sysfs_remove_group(&dev->kobj,
+ &dme1737_pwm_group[ix]);
+ if ((data->has_features & HAS_PWM_MIN) && ix < 3) {
+ sysfs_remove_file(&dev->kobj,
+ dme1737_auto_pwm_min_attr[ix]);
+ }
+ }
+ }
+
+ if (data->has_features & HAS_TEMP_OFFSET)
+ sysfs_remove_group(&dev->kobj, &dme1737_temp_offset_group);
+ if (data->has_features & HAS_VID)
+ sysfs_remove_group(&dev->kobj, &dme1737_vid_group);
+ if (data->has_features & HAS_ZONE3)
+ sysfs_remove_group(&dev->kobj, &dme1737_zone3_group);
+ if (data->has_features & HAS_ZONE_HYST)
+ sysfs_remove_group(&dev->kobj, &dme1737_zone_hyst_group);
+ if (data->has_features & HAS_IN7)
+ sysfs_remove_group(&dev->kobj, &dme1737_in7_group);
+ sysfs_remove_group(&dev->kobj, &dme1737_group);
+
+ if (!data->client)
+ sysfs_remove_file(&dev->kobj, &dev_attr_name.attr);
+}
+
+static int dme1737_create_files(struct device *dev)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ int err, ix;
+
+ /* Create a name attribute for ISA devices */
+ if (!data->client) {
+ err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr);
+ if (err)
+ goto exit;
+ }
+
+ /* Create standard sysfs attributes */
+ err = sysfs_create_group(&dev->kobj, &dme1737_group);
+ if (err)
+ goto exit_remove;
+
+ /* Create chip-dependent sysfs attributes */
+ if (data->has_features & HAS_TEMP_OFFSET) {
+ err = sysfs_create_group(&dev->kobj,
+ &dme1737_temp_offset_group);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->has_features & HAS_VID) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_vid_group);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->has_features & HAS_ZONE3) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_zone3_group);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->has_features & HAS_ZONE_HYST) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_zone_hyst_group);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->has_features & HAS_IN7) {
+ err = sysfs_create_group(&dev->kobj, &dme1737_in7_group);
+ if (err)
+ goto exit_remove;
+ }
+
+ /* Create fan sysfs attributes */
+ for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
+ if (data->has_features & HAS_FAN(ix)) {
+ err = sysfs_create_group(&dev->kobj,
+ &dme1737_fan_group[ix]);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
+ /* Create PWM sysfs attributes */
+ for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
+ if (data->has_features & HAS_PWM(ix)) {
+ err = sysfs_create_group(&dev->kobj,
+ &dme1737_pwm_group[ix]);
+ if (err)
+ goto exit_remove;
+ if ((data->has_features & HAS_PWM_MIN) && (ix < 3)) {
+ err = sysfs_create_file(&dev->kobj,
+ dme1737_auto_pwm_min_attr[ix]);
+ if (err)
+ goto exit_remove;
+ }
+ }
+ }
+
+ /*
+ * Inform if the device is locked. Otherwise change the permissions of
+ * selected attributes from read-only to read-writeable.
+ */
+ if (data->config & 0x02) {
+ dev_info(dev,
+ "Device is locked. Some attributes will be read-only.\n");
+ } else {
+ /* Change permissions of zone sysfs attributes */
+ dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
+ S_IRUGO | S_IWUSR);
+
+ /* Change permissions of chip-dependent sysfs attributes */
+ if (data->has_features & HAS_TEMP_OFFSET) {
+ dme1737_chmod_group(dev, &dme1737_temp_offset_group,
+ S_IRUGO | S_IWUSR);
+ }
+ if (data->has_features & HAS_ZONE3) {
+ dme1737_chmod_group(dev, &dme1737_zone3_chmod_group,
+ S_IRUGO | S_IWUSR);
+ }
+ if (data->has_features & HAS_ZONE_HYST) {
+ dme1737_chmod_group(dev, &dme1737_zone_hyst_group,
+ S_IRUGO | S_IWUSR);
+ }
+
+ /* Change permissions of PWM sysfs attributes */
+ for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) {
+ if (data->has_features & HAS_PWM(ix)) {
+ dme1737_chmod_group(dev,
+ &dme1737_pwm_chmod_group[ix],
+ S_IRUGO | S_IWUSR);
+ if ((data->has_features & HAS_PWM_MIN) &&
+ ix < 3) {
+ dme1737_chmod_file(dev,
+ dme1737_auto_pwm_min_attr[ix],
+ S_IRUGO | S_IWUSR);
+ }
+ }
+ }
+
+ /* Change permissions of pwm[1-3] if in manual mode */
+ for (ix = 0; ix < 3; ix++) {
+ if ((data->has_features & HAS_PWM(ix)) &&
+ (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
+ dme1737_chmod_file(dev,
+ dme1737_pwm_chmod_attr[ix],
+ S_IRUGO | S_IWUSR);
+ }
+ }
+ }
+
+ return 0;
+
+exit_remove:
+ dme1737_remove_files(dev);
+exit:
+ return err;
+}
+
+static int dme1737_init_device(struct device *dev)
+{
+ struct dme1737_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ix;
+ u8 reg;
+
+ /* Point to the right nominal voltages array */
+ data->in_nominal = IN_NOMINAL(data->type);
+
+ data->config = dme1737_read(data, DME1737_REG_CONFIG);
+ /* Inform if part is not monitoring/started */
+ if (!(data->config & 0x01)) {
+ if (!force_start) {
+ dev_err(dev,
+ "Device is not monitoring. Use the force_start load parameter to override.\n");
+ return -EFAULT;
+ }
+
+ /* Force monitoring */
+ data->config |= 0x01;
+ dme1737_write(data, DME1737_REG_CONFIG, data->config);
+ }
+ /* Inform if part is not ready */
+ if (!(data->config & 0x04)) {
+ dev_err(dev, "Device is not ready.\n");
+ return -EFAULT;
+ }
+
+ /*
+ * Determine which optional fan and pwm features are enabled (only
+ * valid for I2C devices)
+ */
+ if (client) { /* I2C chip */
+ data->config2 = dme1737_read(data, DME1737_REG_CONFIG2);
+ /* Check if optional fan3 input is enabled */
+ if (data->config2 & 0x04)
+ data->has_features |= HAS_FAN(2);
+
+ /*
+ * Fan4 and pwm3 are only available if the client's I2C address
+ * is the default 0x2e. Otherwise the I/Os associated with
+ * these functions are used for addr enable/select.
+ */
+ if (client->addr == 0x2e)
+ data->has_features |= HAS_FAN(3) | HAS_PWM(2);
+
+ /*
+ * Determine which of the optional fan[5-6] and pwm[5-6]
+ * features are enabled. For this, we need to query the runtime
+ * registers through the Super-IO LPC interface. Try both
+ * config ports 0x2e and 0x4e.
+ */
+ if (dme1737_i2c_get_features(0x2e, data) &&
+ dme1737_i2c_get_features(0x4e, data)) {
+ dev_warn(dev,
+ "Failed to query Super-IO for optional features.\n");
+ }
+ }
+
+ /* Fan[1-2] and pwm[1-2] are present in all chips */
+ data->has_features |= HAS_FAN(0) | HAS_FAN(1) | HAS_PWM(0) | HAS_PWM(1);
+
+ /* Chip-dependent features */
+ switch (data->type) {
+ case dme1737:
+ data->has_features |= HAS_TEMP_OFFSET | HAS_VID | HAS_ZONE3 |
+ HAS_ZONE_HYST | HAS_PWM_MIN;
+ break;
+ case sch311x:
+ data->has_features |= HAS_TEMP_OFFSET | HAS_ZONE3 |
+ HAS_ZONE_HYST | HAS_PWM_MIN | HAS_FAN(2) | HAS_PWM(2);
+ break;
+ case sch5027:
+ data->has_features |= HAS_ZONE3;
+ break;
+ case sch5127:
+ data->has_features |= HAS_FAN(2) | HAS_PWM(2) | HAS_IN7;
+ break;
+ default:
+ break;
+ }
+
+ dev_info(dev,
+ "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
+ (data->has_features & HAS_PWM(2)) ? "yes" : "no",
+ (data->has_features & HAS_PWM(4)) ? "yes" : "no",
+ (data->has_features & HAS_PWM(5)) ? "yes" : "no",
+ (data->has_features & HAS_FAN(2)) ? "yes" : "no",
+ (data->has_features & HAS_FAN(3)) ? "yes" : "no",
+ (data->has_features & HAS_FAN(4)) ? "yes" : "no",
+ (data->has_features & HAS_FAN(5)) ? "yes" : "no");
+
+ reg = dme1737_read(data, DME1737_REG_TACH_PWM);
+ /* Inform if fan-to-pwm mapping differs from the default */
+ if (client && reg != 0xa4) { /* I2C chip */
+ dev_warn(dev,
+ "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, fan4->pwm%d. %s\n",
+ (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
+ ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1,
+ DO_REPORT);
+ } else if (!client && reg != 0x24) { /* ISA chip */
+ dev_warn(dev,
+ "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. %s\n",
+ (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
+ ((reg >> 4) & 0x03) + 1, DO_REPORT);
+ }
+
+ /*
+ * Switch pwm[1-3] to manual mode if they are currently disabled and
+ * set the duty-cycles to 0% (which is identical to the PWMs being
+ * disabled).
+ */
+ if (!(data->config & 0x02)) {
+ for (ix = 0; ix < 3; ix++) {
+ data->pwm_config[ix] = dme1737_read(data,
+ DME1737_REG_PWM_CONFIG(ix));
+ if ((data->has_features & HAS_PWM(ix)) &&
+ (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
+ dev_info(dev,
+ "Switching pwm%d to manual mode.\n",
+ ix + 1);
+ data->pwm_config[ix] = PWM_EN_TO_REG(1,
+ data->pwm_config[ix]);
+ dme1737_write(data, DME1737_REG_PWM(ix), 0);
+ dme1737_write(data,
+ DME1737_REG_PWM_CONFIG(ix),
+ data->pwm_config[ix]);
+ }
+ }
+ }
+
+ /* Initialize the default PWM auto channels zone (acz) assignments */
+ data->pwm_acz[0] = 1; /* pwm1 -> zone1 */
+ data->pwm_acz[1] = 2; /* pwm2 -> zone2 */
+ data->pwm_acz[2] = 4; /* pwm3 -> zone3 */
+
+ /* Set VRM */
+ if (data->has_features & HAS_VID)
+ data->vrm = vid_which_vrm();
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * I2C device detection and registration
+ * --------------------------------------------------------------------- */
+
+static struct i2c_driver dme1737_i2c_driver;
+
+static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
+{
+ int err = 0, reg;
+ u16 addr;
+
+ dme1737_sio_enter(sio_cip);
+
+ /*
+ * Check device ID
+ * We currently know about two kinds of DME1737 and SCH5027.
+ */
+ reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
+ if (!(reg == DME1737_ID_1 || reg == DME1737_ID_2 ||
+ reg == SCH5027_ID)) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /* Select logical device A (runtime registers) */
+ dme1737_sio_outb(sio_cip, 0x07, 0x0a);
+
+ /* Get the base address of the runtime registers */
+ addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
+ dme1737_sio_inb(sio_cip, 0x61);
+ if (!addr) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /*
+ * Read the runtime registers to determine which optional features
+ * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set
+ * to '10' if the respective feature is enabled.
+ */
+ if ((inb(addr + 0x43) & 0x0c) == 0x08) /* fan6 */
+ data->has_features |= HAS_FAN(5);
+ if ((inb(addr + 0x44) & 0x0c) == 0x08) /* pwm6 */
+ data->has_features |= HAS_PWM(5);
+ if ((inb(addr + 0x45) & 0x0c) == 0x08) /* fan5 */
+ data->has_features |= HAS_FAN(4);
+ if ((inb(addr + 0x46) & 0x0c) == 0x08) /* pwm5 */
+ data->has_features |= HAS_PWM(4);
+
+exit:
+ dme1737_sio_exit(sio_cip);
+
+ return err;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int dme1737_i2c_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct device *dev = &adapter->dev;
+ u8 company, verstep = 0;
+ const char *name;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY);
+ verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP);
+
+ if (company == DME1737_COMPANY_SMSC &&
+ verstep == SCH5027_VERSTEP) {
+ name = "sch5027";
+ } else if (company == DME1737_COMPANY_SMSC &&
+ (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
+ name = "dme1737";
+ } else {
+ return -ENODEV;
+ }
+
+ dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
+ verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737",
+ client->addr, verstep);
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct dme1737_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->type = i2c_match_id(dme1737_id, client)->driver_data;
+ data->client = client;
+ data->name = client->name;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the DME1737 chip */
+ err = dme1737_init_device(dev);
+ if (err) {
+ dev_err(dev, "Failed to initialize device.\n");
+ return err;
+ }
+
+ /* Create sysfs files */
+ err = dme1737_create_files(dev);
+ if (err) {
+ dev_err(dev, "Failed to create sysfs files.\n");
+ return err;
+ }
+
+ /* Register device */
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_err(dev, "Failed to register device.\n");
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ dme1737_remove_files(dev);
+ return err;
+}
+
+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);
+}
+
+static const struct i2c_device_id dme1737_id[] = {
+ { "dme1737", dme1737 },
+ { "sch5027", sch5027 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dme1737_id);
+
+static struct i2c_driver dme1737_i2c_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "dme1737",
+ },
+ .probe_new = dme1737_i2c_probe,
+ .remove = dme1737_i2c_remove,
+ .id_table = dme1737_id,
+ .detect = dme1737_i2c_detect,
+ .address_list = normal_i2c,
+};
+
+/* ---------------------------------------------------------------------
+ * ISA device detection and registration
+ * --------------------------------------------------------------------- */
+
+static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr)
+{
+ int err = 0, reg;
+ unsigned short base_addr;
+
+ dme1737_sio_enter(sio_cip);
+
+ /*
+ * Check device ID
+ * We currently know about SCH3112, SCH3114, SCH3116, and SCH5127
+ */
+ reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
+ if (!(reg == SCH3112_ID || reg == SCH3114_ID || reg == SCH3116_ID ||
+ reg == SCH5127_ID)) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /* Select logical device A (runtime registers) */
+ dme1737_sio_outb(sio_cip, 0x07, 0x0a);
+
+ /* Get the base address of the runtime registers */
+ base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
+ dme1737_sio_inb(sio_cip, 0x61);
+ if (!base_addr) {
+ pr_err("Base address not set\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /*
+ * Access to the hwmon registers is through an index/data register
+ * pair located at offset 0x70/0x71.
+ */
+ *addr = base_addr + 0x70;
+
+exit:
+ dme1737_sio_exit(sio_cip);
+ return err;
+}
+
+static int __init dme1737_isa_device_add(unsigned short addr)
+{
+ struct resource res = {
+ .start = addr,
+ .end = addr + DME1737_EXTENT - 1,
+ .name = "dme1737",
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc("dme1737", addr);
+ if (!pdev) {
+ pr_err("Failed to allocate device\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Failed to add device resource (err = %d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Failed to add device (err = %d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+ pdev = NULL;
+exit:
+ return err;
+}
+
+static int dme1737_isa_probe(struct platform_device *pdev)
+{
+ u8 company, device;
+ struct resource *res;
+ struct dme1737_data *data;
+ struct device *dev = &pdev->dev;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(dev, res->start, DME1737_EXTENT, "dme1737")) {
+ dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+ (unsigned short)res->start,
+ (unsigned short)res->start + DME1737_EXTENT - 1);
+ return -EBUSY;
+ }
+
+ data = devm_kzalloc(dev, sizeof(struct dme1737_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = res->start;
+ platform_set_drvdata(pdev, data);
+
+ /* Skip chip detection if module is loaded with force_id parameter */
+ switch (force_id) {
+ case SCH3112_ID:
+ case SCH3114_ID:
+ case SCH3116_ID:
+ data->type = sch311x;
+ break;
+ case SCH5127_ID:
+ data->type = sch5127;
+ break;
+ default:
+ company = dme1737_read(data, DME1737_REG_COMPANY);
+ device = dme1737_read(data, DME1737_REG_DEVICE);
+
+ if ((company == DME1737_COMPANY_SMSC) &&
+ (device == SCH311X_DEVICE)) {
+ data->type = sch311x;
+ } else if ((company == DME1737_COMPANY_SMSC) &&
+ (device == SCH5127_DEVICE)) {
+ data->type = sch5127;
+ } else {
+ return -ENODEV;
+ }
+ }
+
+ if (data->type == sch5127)
+ data->name = "sch5127";
+ else
+ data->name = "sch311x";
+
+ /* Initialize the mutex */
+ mutex_init(&data->update_lock);
+
+ dev_info(dev, "Found a %s chip at 0x%04x\n",
+ data->type == sch5127 ? "SCH5127" : "SCH311x", data->addr);
+
+ /* Initialize the chip */
+ err = dme1737_init_device(dev);
+ if (err) {
+ dev_err(dev, "Failed to initialize device.\n");
+ return err;
+ }
+
+ /* Create sysfs files */
+ err = dme1737_create_files(dev);
+ if (err) {
+ dev_err(dev, "Failed to create sysfs files.\n");
+ return err;
+ }
+
+ /* Register device */
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_err(dev, "Failed to register device.\n");
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ dme1737_remove_files(dev);
+ return err;
+}
+
+static int dme1737_isa_remove(struct platform_device *pdev)
+{
+ struct dme1737_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ dme1737_remove_files(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver dme1737_isa_driver = {
+ .driver = {
+ .name = "dme1737",
+ },
+ .probe = dme1737_isa_probe,
+ .remove = dme1737_isa_remove,
+};
+
+/* ---------------------------------------------------------------------
+ * Module initialization and cleanup
+ * --------------------------------------------------------------------- */
+
+static int __init dme1737_init(void)
+{
+ int err;
+ unsigned short addr;
+
+ err = i2c_add_driver(&dme1737_i2c_driver);
+ if (err)
+ goto exit;
+
+ if (dme1737_isa_detect(0x2e, &addr) &&
+ dme1737_isa_detect(0x4e, &addr) &&
+ (!probe_all_addr ||
+ (dme1737_isa_detect(0x162e, &addr) &&
+ dme1737_isa_detect(0x164e, &addr)))) {
+ /* Return 0 if we didn't find an ISA device */
+ return 0;
+ }
+
+ err = platform_driver_register(&dme1737_isa_driver);
+ if (err)
+ goto exit_del_i2c_driver;
+
+ /* Sets global pdev as a side effect */
+ err = dme1737_isa_device_add(addr);
+ if (err)
+ goto exit_del_isa_driver;
+
+ return 0;
+
+exit_del_isa_driver:
+ platform_driver_unregister(&dme1737_isa_driver);
+exit_del_i2c_driver:
+ i2c_del_driver(&dme1737_i2c_driver);
+exit:
+ return err;
+}
+
+static void __exit dme1737_exit(void)
+{
+ if (pdev) {
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&dme1737_isa_driver);
+ }
+
+ i2c_del_driver(&dme1737_i2c_driver);
+}
+
+MODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>");
+MODULE_DESCRIPTION("DME1737 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(dme1737_init);
+module_exit(dme1737_exit);
diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c
new file mode 100644
index 000000000..5bac2b0fc
--- /dev/null
+++ b/drivers/hwmon/drivetemp.c
@@ -0,0 +1,624 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hwmon client for disk and solid state drives with temperature sensors
+ * Copyright (C) 2019 Zodiac Inflight Innovations
+ *
+ * With input from:
+ * Hwmon client for S.M.A.R.T. hard disk drives with temperature sensors.
+ * (C) 2018 Linus Walleij
+ *
+ * hwmon: Driver for SCSI/ATA temperature sensors
+ * by Constantin Baranov <const@mimas.ru>, submitted September 2009
+ *
+ * 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
+ * for ATA drives is the SCT Command Transport feature set as specified in
+ * ATA8-ACS.
+ * It can be used to read the current drive temperature, temperature limits,
+ * and historic minimum and maximum temperatures. The SCT Command Transport
+ * feature set is documented in "AT Attachment 8 - ATA/ATAPI Command Set
+ * (ATA8-ACS)".
+ *
+ * If the SCT Command Transport feature set is not available, drive temperatures
+ * may be readable through SMART attributes. Since SMART attributes are not well
+ * defined, this method is only used as fallback mechanism.
+ *
+ * There are three SMART attributes which may report drive temperatures.
+ * Those are defined as follows (from
+ * http://www.cropel.com/library/smart-attribute-list.aspx).
+ *
+ * 190 Temperature Temperature, monitored by a sensor somewhere inside
+ * the drive. Raw value typicaly holds the actual
+ * temperature (hexadecimal) in its rightmost two digits.
+ *
+ * 194 Temperature Temperature, monitored by a sensor somewhere inside
+ * the drive. Raw value typicaly holds the actual
+ * temperature (hexadecimal) in its rightmost two digits.
+ *
+ * 231 Temperature Temperature, monitored by a sensor somewhere inside
+ * the drive. Raw value typicaly holds the actual
+ * temperature (hexadecimal) in its rightmost two digits.
+ *
+ * Wikipedia defines attributes a bit differently.
+ *
+ * 190 Temperature Value is equal to (100-temp. °C), allowing manufacturer
+ * Difference or to set a minimum threshold which corresponds to a
+ * Airflow maximum temperature. This also follows the convention of
+ * Temperature 100 being a best-case value and lower values being
+ * undesirable. However, some older drives may instead
+ * report raw Temperature (identical to 0xC2) or
+ * Temperature minus 50 here.
+ * 194 Temperature or Indicates the device temperature, if the appropriate
+ * Temperature sensor is fitted. Lowest byte of the raw value contains
+ * Celsius the exact temperature value (Celsius degrees).
+ * 231 Life Left Indicates the approximate SSD life left, in terms of
+ * (SSDs) or program/erase cycles or available reserved blocks.
+ * Temperature A normalized value of 100 represents a new drive, with
+ * a threshold value at 10 indicating a need for
+ * replacement. A value of 0 may mean that the drive is
+ * operating in read-only mode to allow data recovery.
+ * Previously (pre-2010) occasionally used for Drive
+ * Temperature (more typically reported at 0xC2).
+ *
+ * Common denominator is that the first raw byte reports the temperature
+ * in degrees C on almost all drives. Some drives may report a fractional
+ * temperature in the second raw byte.
+ *
+ * Known exceptions (from libatasmart):
+ * - SAMSUNG SV0412H and SAMSUNG SV1204H) report the temperature in 10th
+ * degrees C in the first two raw bytes.
+ * - A few Maxtor drives report an unknown or bad value in attribute 194.
+ * - Certain Apple SSD drives report an unknown value in attribute 190.
+ * Only certain firmware versions are affected.
+ *
+ * Those exceptions affect older ATA drives and are currently ignored.
+ * Also, the second raw byte (possibly reporting the fractional temperature)
+ * is currently ignored.
+ *
+ * Many drives also report temperature limits in additional SMART data raw
+ * bytes. The format of those is not well defined and varies widely.
+ * The driver does not currently attempt to report those limits.
+ *
+ * According to data in smartmontools, attribute 231 is rarely used to report
+ * drive temperatures. At the same time, several drives report SSD life left
+ * in attribute 231, but do not support temperature sensors. For this reason,
+ * attribute 231 is currently ignored.
+ *
+ * Following above definitions, temperatures are reported as follows.
+ * If SCT Command Transport is supported, it is used to read the
+ * temperature and, if available, temperature limits.
+ * - Otherwise, if SMART attribute 194 is supported, it is used to read
+ * the temperature.
+ * - Otherwise, if SMART attribute 190 is supported, it is used to read
+ * the temperature.
+ */
+
+#include <linux/ata.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_proto.h>
+
+struct drivetemp_data {
+ struct list_head list; /* list of instantiated devices */
+ struct mutex lock; /* protect data buffer accesses */
+ struct scsi_device *sdev; /* SCSI device */
+ struct device *dev; /* instantiating device */
+ struct device *hwdev; /* hardware monitoring device */
+ u8 smartdata[ATA_SECT_SIZE]; /* local buffer */
+ int (*get_temp)(struct drivetemp_data *st, u32 attr, long *val);
+ bool have_temp_lowest; /* lowest temp in SCT status */
+ bool have_temp_highest; /* highest temp in SCT status */
+ bool have_temp_min; /* have min temp */
+ bool have_temp_max; /* have max temp */
+ bool have_temp_lcrit; /* have lower critical limit */
+ bool have_temp_crit; /* have critical limit */
+ int temp_min; /* min temp */
+ int temp_max; /* max temp */
+ int temp_lcrit; /* lower critical limit */
+ int temp_crit; /* critical limit */
+};
+
+static LIST_HEAD(drivetemp_devlist);
+
+#define ATA_MAX_SMART_ATTRS 30
+#define SMART_TEMP_PROP_190 190
+#define SMART_TEMP_PROP_194 194
+
+#define SCT_STATUS_REQ_ADDR 0xe0
+#define SCT_STATUS_VERSION_LOW 0 /* log byte offsets */
+#define SCT_STATUS_VERSION_HIGH 1
+#define SCT_STATUS_TEMP 200
+#define SCT_STATUS_TEMP_LOWEST 201
+#define SCT_STATUS_TEMP_HIGHEST 202
+#define SCT_READ_LOG_ADDR 0xe1
+#define SMART_READ_LOG 0xd5
+#define SMART_WRITE_LOG 0xd6
+
+#define INVALID_TEMP 0x80
+
+#define temp_is_valid(temp) ((temp) != INVALID_TEMP)
+#define temp_from_sct(temp) (((s8)(temp)) * 1000)
+
+static inline bool ata_id_smart_supported(u16 *id)
+{
+ return id[ATA_ID_COMMAND_SET_1] & BIT(0);
+}
+
+static inline bool ata_id_smart_enabled(u16 *id)
+{
+ return id[ATA_ID_CFS_ENABLE_1] & BIT(0);
+}
+
+static int drivetemp_scsi_command(struct drivetemp_data *st,
+ u8 ata_command, u8 feature,
+ u8 lba_low, u8 lba_mid, u8 lba_high)
+{
+ u8 scsi_cmd[MAX_COMMAND_SIZE];
+ int data_dir;
+
+ memset(scsi_cmd, 0, sizeof(scsi_cmd));
+ scsi_cmd[0] = ATA_16;
+ if (ata_command == ATA_CMD_SMART && feature == SMART_WRITE_LOG) {
+ scsi_cmd[1] = (5 << 1); /* PIO Data-out */
+ /*
+ * No off.line or cc, write to dev, block count in sector count
+ * field.
+ */
+ scsi_cmd[2] = 0x06;
+ data_dir = DMA_TO_DEVICE;
+ } else {
+ scsi_cmd[1] = (4 << 1); /* PIO Data-in */
+ /*
+ * No off.line or cc, read from dev, block count in sector count
+ * field.
+ */
+ scsi_cmd[2] = 0x0e;
+ data_dir = DMA_FROM_DEVICE;
+ }
+ scsi_cmd[4] = feature;
+ scsi_cmd[6] = 1; /* 1 sector */
+ scsi_cmd[8] = lba_low;
+ scsi_cmd[10] = lba_mid;
+ scsi_cmd[12] = lba_high;
+ scsi_cmd[14] = ata_command;
+
+ return scsi_execute_req(st->sdev, scsi_cmd, data_dir,
+ st->smartdata, ATA_SECT_SIZE, NULL, HZ, 5,
+ NULL);
+}
+
+static int drivetemp_ata_command(struct drivetemp_data *st, u8 feature,
+ u8 select)
+{
+ return drivetemp_scsi_command(st, ATA_CMD_SMART, feature, select,
+ ATA_SMART_LBAM_PASS, ATA_SMART_LBAH_PASS);
+}
+
+static int drivetemp_get_smarttemp(struct drivetemp_data *st, u32 attr,
+ long *temp)
+{
+ u8 *buf = st->smartdata;
+ bool have_temp = false;
+ u8 temp_raw;
+ u8 csum;
+ int err;
+ int i;
+
+ err = drivetemp_ata_command(st, ATA_SMART_READ_VALUES, 0);
+ if (err)
+ return err;
+
+ /* Checksum the read value table */
+ csum = 0;
+ for (i = 0; i < ATA_SECT_SIZE; i++)
+ csum += buf[i];
+ if (csum) {
+ dev_dbg(&st->sdev->sdev_gendev,
+ "checksum error reading SMART values\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < ATA_MAX_SMART_ATTRS; i++) {
+ u8 *attr = buf + i * 12;
+ int id = attr[2];
+
+ if (!id)
+ continue;
+
+ if (id == SMART_TEMP_PROP_190) {
+ temp_raw = attr[7];
+ have_temp = true;
+ }
+ if (id == SMART_TEMP_PROP_194) {
+ temp_raw = attr[7];
+ have_temp = true;
+ break;
+ }
+ }
+
+ if (have_temp) {
+ *temp = temp_raw * 1000;
+ return 0;
+ }
+
+ return -ENXIO;
+}
+
+static int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val)
+{
+ u8 *buf = st->smartdata;
+ int err;
+
+ err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR);
+ if (err)
+ 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:
+ err = -EINVAL;
+ break;
+ }
+ 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;
+ u8 *buf = st->smartdata;
+ struct scsi_vpd *vpd;
+ bool is_ata, is_sata;
+ bool have_sct_data_table;
+ bool have_sct_temp;
+ bool have_smart;
+ bool have_sct;
+ u16 *ata_id;
+ u16 version;
+ long temp;
+ int err;
+
+ /* SCSI-ATA Translation present? */
+ rcu_read_lock();
+ vpd = rcu_dereference(sdev->vpd_pg89);
+
+ /*
+ * Verify that ATA IDENTIFY DEVICE data is included in ATA Information
+ * VPD and that the drive implements the SATA protocol.
+ */
+ if (!vpd || vpd->len < 572 || vpd->data[56] != ATA_CMD_ID_ATA ||
+ vpd->data[36] != 0x34) {
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+ ata_id = (u16 *)&vpd->data[60];
+ is_ata = ata_id_is_ata(ata_id);
+ is_sata = ata_id_is_sata(ata_id);
+ have_sct = ata_id_sct_supported(ata_id);
+ have_sct_data_table = ata_id_sct_data_tables(ata_id);
+ have_smart = ata_id_smart_supported(ata_id) &&
+ ata_id_smart_enabled(ata_id);
+
+ rcu_read_unlock();
+
+ /* 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;
+
+ err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR);
+ if (err)
+ goto skip_sct;
+
+ version = (buf[SCT_STATUS_VERSION_HIGH] << 8) |
+ buf[SCT_STATUS_VERSION_LOW];
+ if (version != 2 && version != 3)
+ goto skip_sct;
+
+ have_sct_temp = temp_is_valid(buf[SCT_STATUS_TEMP]);
+ if (!have_sct_temp)
+ goto skip_sct;
+
+ st->have_temp_lowest = temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST]);
+ st->have_temp_highest = temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]);
+
+ if (!have_sct_data_table)
+ goto skip_sct_data;
+
+ /* Request and read temperature history table */
+ memset(buf, '\0', sizeof(st->smartdata));
+ buf[0] = 5; /* data table command */
+ buf[2] = 1; /* read table */
+ buf[4] = 2; /* temperature history table */
+
+ err = drivetemp_ata_command(st, SMART_WRITE_LOG, SCT_STATUS_REQ_ADDR);
+ if (err)
+ goto skip_sct_data;
+
+ err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_READ_LOG_ADDR);
+ if (err)
+ goto skip_sct_data;
+
+ /*
+ * Temperature limits per AT Attachment 8 -
+ * ATA/ATAPI Command Set (ATA8-ACS)
+ */
+ st->have_temp_max = temp_is_valid(buf[6]);
+ st->have_temp_crit = temp_is_valid(buf[7]);
+ st->have_temp_min = temp_is_valid(buf[8]);
+ st->have_temp_lcrit = temp_is_valid(buf[9]);
+
+ st->temp_max = temp_from_sct(buf[6]);
+ st->temp_crit = temp_from_sct(buf[7]);
+ st->temp_min = temp_from_sct(buf[8]);
+ st->temp_lcrit = temp_from_sct(buf[9]);
+
+skip_sct_data:
+ if (have_sct_temp) {
+ st->get_temp = drivetemp_get_scttemp;
+ return 0;
+ }
+skip_sct:
+ if (!have_smart)
+ return -ENODEV;
+ st->get_temp = drivetemp_get_smarttemp;
+ return drivetemp_get_smarttemp(st, hwmon_temp_input, &temp);
+}
+
+static int drivetemp_identify(struct drivetemp_data *st)
+{
+ struct scsi_device *sdev = st->sdev;
+
+ /* Bail out immediately if there is no inquiry data */
+ if (!sdev->inquiry || sdev->inquiry_len < 16)
+ return -ENODEV;
+
+ /* Disk device? */
+ if (sdev->type != TYPE_DISK && sdev->type != TYPE_ZBC)
+ return -ENODEV;
+
+ return drivetemp_identify_sata(st);
+}
+
+static int drivetemp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct drivetemp_data *st = dev_get_drvdata(dev);
+ int err = 0;
+
+ if (type != hwmon_temp)
+ return -EINVAL;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_lowest:
+ case hwmon_temp_highest:
+ mutex_lock(&st->lock);
+ err = st->get_temp(st, attr, val);
+ mutex_unlock(&st->lock);
+ break;
+ case hwmon_temp_lcrit:
+ *val = st->temp_lcrit;
+ break;
+ case hwmon_temp_min:
+ *val = st->temp_min;
+ break;
+ case hwmon_temp_max:
+ *val = st->temp_max;
+ break;
+ case hwmon_temp_crit:
+ *val = st->temp_crit;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+static umode_t drivetemp_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct drivetemp_data *st = data;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ case hwmon_temp_lowest:
+ if (st->have_temp_lowest)
+ return 0444;
+ break;
+ case hwmon_temp_highest:
+ if (st->have_temp_highest)
+ return 0444;
+ break;
+ case hwmon_temp_min:
+ if (st->have_temp_min)
+ return 0444;
+ break;
+ case hwmon_temp_max:
+ if (st->have_temp_max)
+ return 0444;
+ break;
+ case hwmon_temp_lcrit:
+ if (st->have_temp_lcrit)
+ return 0444;
+ break;
+ case hwmon_temp_crit:
+ if (st->have_temp_crit)
+ return 0444;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *drivetemp_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT |
+ HWMON_T_LOWEST | HWMON_T_HIGHEST |
+ HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_LCRIT | HWMON_T_CRIT),
+ NULL
+};
+
+static const struct hwmon_ops drivetemp_ops = {
+ .is_visible = drivetemp_is_visible,
+ .read = drivetemp_read,
+};
+
+static const struct hwmon_chip_info drivetemp_chip_info = {
+ .ops = &drivetemp_ops,
+ .info = drivetemp_info,
+};
+
+/*
+ * The device argument points to sdev->sdev_dev. Its parent is
+ * sdev->sdev_gendev, which we can use to get the scsi_device pointer.
+ */
+static int drivetemp_add(struct device *dev, struct class_interface *intf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev->parent);
+ struct drivetemp_data *st;
+ int err;
+
+ st = kzalloc(sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->sdev = sdev;
+ st->dev = dev;
+ mutex_init(&st->lock);
+
+ if (drivetemp_identify(st)) {
+ err = -ENODEV;
+ goto abort;
+ }
+
+ st->hwdev = hwmon_device_register_with_info(dev->parent, "drivetemp",
+ st, &drivetemp_chip_info,
+ NULL);
+ if (IS_ERR(st->hwdev)) {
+ err = PTR_ERR(st->hwdev);
+ goto abort;
+ }
+
+ list_add(&st->list, &drivetemp_devlist);
+ return 0;
+
+abort:
+ kfree(st);
+ return err;
+}
+
+static void drivetemp_remove(struct device *dev, struct class_interface *intf)
+{
+ struct drivetemp_data *st, *tmp;
+
+ list_for_each_entry_safe(st, tmp, &drivetemp_devlist, list) {
+ if (st->dev == dev) {
+ list_del(&st->list);
+ hwmon_device_unregister(st->hwdev);
+ kfree(st);
+ break;
+ }
+ }
+}
+
+static struct class_interface drivetemp_interface = {
+ .add_dev = drivetemp_add,
+ .remove_dev = drivetemp_remove,
+};
+
+static int __init drivetemp_init(void)
+{
+ return scsi_register_interface(&drivetemp_interface);
+}
+
+static void __exit drivetemp_exit(void)
+{
+ scsi_unregister_interface(&drivetemp_interface);
+}
+
+module_init(drivetemp_init);
+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
new file mode 100644
index 000000000..0886abf6e
--- /dev/null
+++ b/drivers/hwmon/ds1621.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ds1621.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Christian W. Zuckschwerdt <zany@triq.net> 2000-11-23
+ * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
+ * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
+ * the help of Jean Delvare <jdelvare@suse.de>
+ *
+ * The DS1621 device is a digital temperature/thermometer with 9-bit
+ * resolution, a thermal alarm output (Tout), and user-defined minimum
+ * and maximum temperature thresholds (TH and TL).
+ *
+ * The DS1625, DS1631, DS1721, and DS1731 are pin compatible with the DS1621
+ * and similar in operation, with slight variations as noted in the device
+ * datasheets (please refer to www.maximintegrated.com for specific
+ * device information).
+ *
+ * Since the DS1621 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/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/kernel.h>
+
+/* Supported devices */
+enum chips { ds1621, ds1625, ds1631, ds1721, ds1731 };
+
+/* Insmod parameters */
+static int polarity = -1;
+module_param(polarity, int, 0);
+MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low");
+
+/*
+ * The Configuration/Status register
+ *
+ * - DS1621:
+ * 7 6 5 4 3 2 1 0
+ * |Done|THF |TLF |NVB | X | X |POL |1SHOT|
+ *
+ * - DS1625:
+ * 7 6 5 4 3 2 1 0
+ * |Done|THF |TLF |NVB | 1 | 0 |POL |1SHOT|
+ *
+ * - DS1631, DS1731:
+ * 7 6 5 4 3 2 1 0
+ * |Done|THF |TLF |NVB | R1 | R0 |POL |1SHOT|
+ *
+ * - DS1721:
+ * 7 6 5 4 3 2 1 0
+ * |Done| X | X | U | R1 | R0 |POL |1SHOT|
+ *
+ * Where:
+ * - 'X' is Reserved
+ * - 'U' is Undefined
+ */
+#define DS1621_REG_CONFIG_NVB 0x10
+#define DS1621_REG_CONFIG_RESOL 0x0C
+#define DS1621_REG_CONFIG_POLARITY 0x02
+#define DS1621_REG_CONFIG_1SHOT 0x01
+#define DS1621_REG_CONFIG_DONE 0x80
+
+#define DS1621_REG_CONFIG_RESOL_SHIFT 2
+
+/* ds1721 conversion rates: {C/LSB, time(ms), resolution bit setting} */
+static const unsigned short ds1721_convrates[] = {
+ 94, /* 9-bits (0.5, 93.75, RES[0..1] = 0 */
+ 188, /* 10-bits (0.25, 187.5, RES[0..1] = 1 */
+ 375, /* 11-bits (0.125, 375, RES[0..1] = 2 */
+ 750, /* 12-bits (0.0625, 750, RES[0..1] = 3 */
+};
+
+#define DS1621_CONVERSION_MAX 750
+#define DS1625_CONVERSION_MAX 500
+
+#define DS1621_TEMP_MAX 125000
+#define DS1621_TEMP_MIN (-55000)
+
+/* The DS1621 temperature registers */
+static const u8 DS1621_REG_TEMP[3] = {
+ 0xAA, /* input, word, RO */
+ 0xA2, /* min, word, RW */
+ 0xA1, /* max, word, RW */
+};
+#define DS1621_REG_CONF 0xAC /* byte, RW */
+#define DS1621_COM_START 0xEE /* no data */
+#define DS1721_COM_START 0x51 /* no data */
+#define DS1621_COM_STOP 0x22 /* no data */
+
+/* The DS1621 configuration register */
+#define DS1621_ALARM_TEMP_HIGH 0x40
+#define DS1621_ALARM_TEMP_LOW 0x20
+
+/* Conversions */
+#define ALARMS_FROM_REG(val) ((val) & \
+ (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
+
+/* Each client has this additional data */
+struct ds1621_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+ enum chips kind; /* device type */
+
+ u16 temp[3]; /* Register values, word */
+ u8 conf; /* Register encoding, combined */
+ u8 zbits; /* Resolution encoded as number of
+ * zero bits */
+ u16 update_interval; /* Conversion rate in milliseconds */
+};
+
+static inline int DS1621_TEMP_FROM_REG(u16 reg)
+{
+ return DIV_ROUND_CLOSEST(((s16)reg / 16) * 625, 10);
+}
+
+/*
+ * TEMP: 0.001C/bit (-55C to +125C)
+ * REG:
+ * - 1621, 1625: 0.5C/bit, 7 zero-bits
+ * - 1631, 1721, 1731: 0.0625C/bit, 4 zero-bits
+ */
+static inline u16 DS1621_TEMP_TO_REG(long temp, u8 zbits)
+{
+ temp = clamp_val(temp, DS1621_TEMP_MIN, DS1621_TEMP_MAX);
+ temp = DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits;
+ return temp;
+}
+
+static void ds1621_init_client(struct ds1621_data *data,
+ struct i2c_client *client)
+{
+ u8 conf, new_conf, sreg, resol;
+
+ new_conf = conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
+ /* switch to continuous conversion mode */
+ new_conf &= ~DS1621_REG_CONFIG_1SHOT;
+
+ /* setup output polarity */
+ if (polarity == 0)
+ new_conf &= ~DS1621_REG_CONFIG_POLARITY;
+ else if (polarity == 1)
+ new_conf |= DS1621_REG_CONFIG_POLARITY;
+
+ if (conf != new_conf)
+ i2c_smbus_write_byte_data(client, DS1621_REG_CONF, new_conf);
+
+ switch (data->kind) {
+ case ds1625:
+ data->update_interval = DS1625_CONVERSION_MAX;
+ data->zbits = 7;
+ sreg = DS1621_COM_START;
+ break;
+ case ds1631:
+ case ds1721:
+ case ds1731:
+ resol = (new_conf & DS1621_REG_CONFIG_RESOL) >>
+ DS1621_REG_CONFIG_RESOL_SHIFT;
+ data->update_interval = ds1721_convrates[resol];
+ data->zbits = 7 - resol;
+ sreg = DS1721_COM_START;
+ break;
+ default:
+ data->update_interval = DS1621_CONVERSION_MAX;
+ data->zbits = 7;
+ sreg = DS1621_COM_START;
+ break;
+ }
+
+ /* start conversion */
+ i2c_smbus_write_byte(client, sreg);
+}
+
+static struct ds1621_data *ds1621_update_client(struct device *dev)
+{
+ struct ds1621_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 new_conf;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + data->update_interval) ||
+ !data->valid) {
+ int i;
+
+ dev_dbg(&client->dev, "Starting ds1621 update\n");
+
+ data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
+
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++)
+ data->temp[i] = i2c_smbus_read_word_swapped(client,
+ DS1621_REG_TEMP[i]);
+
+ /* reset alarms if necessary */
+ new_conf = data->conf;
+ if (data->temp[0] > data->temp[1]) /* input > min */
+ new_conf &= ~DS1621_ALARM_TEMP_LOW;
+ if (data->temp[0] < data->temp[2]) /* input < max */
+ new_conf &= ~DS1621_ALARM_TEMP_HIGH;
+ if (data->conf != new_conf)
+ i2c_smbus_write_byte_data(client, DS1621_REG_CONF,
+ new_conf);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ds1621_data *data = ds1621_update_client(dev);
+ return sprintf(buf, "%d\n",
+ DS1621_TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static ssize_t temp_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 ds1621_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->temp[attr->index] = DS1621_TEMP_TO_REG(val, data->zbits);
+ i2c_smbus_write_word_swapped(data->client, DS1621_REG_TEMP[attr->index],
+ data->temp[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct ds1621_data *data = ds1621_update_client(dev);
+ return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->conf));
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ds1621_data *data = ds1621_update_client(dev);
+ return sprintf(buf, "%d\n", !!(data->conf & attr->index));
+}
+
+static ssize_t update_interval_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct ds1621_data *data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%hu\n", data->update_interval);
+}
+
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct ds1621_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long convrate;
+ s32 err;
+ int resol = 0;
+
+ err = kstrtoul(buf, 10, &convrate);
+ if (err)
+ return err;
+
+ /* Convert rate into resolution bits */
+ while (resol < (ARRAY_SIZE(ds1721_convrates) - 1) &&
+ convrate > ds1721_convrates[resol])
+ resol++;
+
+ mutex_lock(&data->update_lock);
+ data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
+ data->conf &= ~DS1621_REG_CONFIG_RESOL;
+ data->conf |= (resol << DS1621_REG_CONFIG_RESOL_SHIFT);
+ i2c_smbus_write_byte_data(client, DS1621_REG_CONF, data->conf);
+ data->update_interval = ds1721_convrates[resol];
+ data->zbits = 7 - resol;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static DEVICE_ATTR_RO(alarms);
+static DEVICE_ATTR_RW(update_interval);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 2);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, DS1621_ALARM_TEMP_LOW);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, DS1621_ALARM_TEMP_HIGH);
+
+static struct attribute *ds1621_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_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_update_interval.attr,
+ NULL
+};
+
+static umode_t ds1621_attribute_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct ds1621_data *data = dev_get_drvdata(dev);
+
+ if (attr == &dev_attr_update_interval.attr)
+ if (data->kind == ds1621 || data->kind == ds1625)
+ /* shhh, we're hiding update_interval */
+ return 0;
+ return attr->mode;
+}
+
+static const struct attribute_group ds1621_group = {
+ .attrs = ds1621_attributes,
+ .is_visible = ds1621_attribute_visible
+};
+__ATTRIBUTE_GROUPS(ds1621);
+
+static const struct i2c_device_id ds1621_id[];
+
+static int ds1621_probe(struct i2c_client *client)
+{
+ struct ds1621_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct ds1621_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->update_lock);
+
+ data->kind = i2c_match_id(ds1621_id, client)->driver_data;
+ data->client = client;
+
+ /* Initialize the DS1621 chip */
+ ds1621_init_client(data, client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ ds1621_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ds1621_id[] = {
+ { "ds1621", ds1621 },
+ { "ds1625", ds1625 },
+ { "ds1631", ds1631 },
+ { "ds1721", ds1721 },
+ { "ds1731", ds1731 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds1621_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ds1621_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "ds1621",
+ },
+ .probe_new = ds1621_probe,
+ .id_table = ds1621_id,
+};
+
+module_i2c_driver(ds1621_driver);
+
+MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
+MODULE_DESCRIPTION("DS1621 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
new file mode 100644
index 000000000..82d7c3d58
--- /dev/null
+++ b/drivers/hwmon/ds620.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ds620.c - Support for temperature sensor and thermostat DS620
+ *
+ * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
+ *
+ * based on ds1621.c by Christian W. Zuckschwerdt <zany@triq.net>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/platform_data/ds620.h>
+
+/*
+ * Many DS620 constants specified below
+ * 15 14 13 12 11 10 09 08
+ * |Done|NVB |THF |TLF |R1 |R0 |AUTOC|1SHOT|
+ *
+ * 07 06 05 04 03 02 01 00
+ * |PO2 |PO1 |A2 |A1 |A0 | | | |
+ */
+#define DS620_REG_CONFIG_DONE 0x8000
+#define DS620_REG_CONFIG_NVB 0x4000
+#define DS620_REG_CONFIG_THF 0x2000
+#define DS620_REG_CONFIG_TLF 0x1000
+#define DS620_REG_CONFIG_R1 0x0800
+#define DS620_REG_CONFIG_R0 0x0400
+#define DS620_REG_CONFIG_AUTOC 0x0200
+#define DS620_REG_CONFIG_1SHOT 0x0100
+#define DS620_REG_CONFIG_PO2 0x0080
+#define DS620_REG_CONFIG_PO1 0x0040
+#define DS620_REG_CONFIG_A2 0x0020
+#define DS620_REG_CONFIG_A1 0x0010
+#define DS620_REG_CONFIG_A0 0x0008
+
+/* The DS620 registers */
+static const u8 DS620_REG_TEMP[3] = {
+ 0xAA, /* input, word, RO */
+ 0xA2, /* min, word, RW */
+ 0xA0, /* max, word, RW */
+};
+
+#define DS620_REG_CONF 0xAC /* word, RW */
+#define DS620_COM_START 0x51 /* no data */
+#define DS620_COM_STOP 0x22 /* no data */
+
+/* Each client has this additional data */
+struct ds620_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ s16 temp[3]; /* Register values, word */
+};
+
+static void ds620_init_client(struct i2c_client *client)
+{
+ struct ds620_platform_data *ds620_info = dev_get_platdata(&client->dev);
+ u16 conf, new_conf;
+
+ new_conf = conf =
+ i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
+
+ /* switch to continuous conversion mode */
+ new_conf &= ~DS620_REG_CONFIG_1SHOT;
+ /* already high at power-on, but don't trust the BIOS! */
+ new_conf |= DS620_REG_CONFIG_PO2;
+ /* thermostat mode according to platform data */
+ if (ds620_info && ds620_info->pomode == 1)
+ new_conf &= ~DS620_REG_CONFIG_PO1; /* PO_LOW */
+ else if (ds620_info && ds620_info->pomode == 2)
+ new_conf |= DS620_REG_CONFIG_PO1; /* PO_HIGH */
+ else
+ new_conf &= ~DS620_REG_CONFIG_PO2; /* always low */
+ /* with highest precision */
+ new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0;
+
+ if (conf != new_conf)
+ i2c_smbus_write_word_swapped(client, DS620_REG_CONF, new_conf);
+
+ /* start conversion */
+ i2c_smbus_write_byte(client, DS620_COM_START);
+}
+
+static struct ds620_data *ds620_update_client(struct device *dev)
+{
+ struct ds620_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct ds620_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ int i;
+ int res;
+
+ dev_dbg(&client->dev, "Starting ds620 update\n");
+
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ res = i2c_smbus_read_word_swapped(client,
+ DS620_REG_TEMP[i]);
+ if (res < 0) {
+ ret = ERR_PTR(res);
+ goto abort;
+ }
+
+ data->temp[i] = res;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ds620_data *data = ds620_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", ((data->temp[attr->index] / 8) * 625) / 10);
+}
+
+static ssize_t temp_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int res;
+ long val;
+
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ds620_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ res = kstrtol(buf, 10, &val);
+
+ if (res)
+ return res;
+
+ val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8;
+
+ mutex_lock(&data->update_lock);
+ data->temp[attr->index] = val;
+ i2c_smbus_write_word_swapped(client, DS620_REG_TEMP[attr->index],
+ data->temp[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ds620_data *data = ds620_update_client(dev);
+ struct i2c_client *client;
+ u16 conf, new_conf;
+ int res;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ client = data->client;
+
+ /* reset alarms if necessary */
+ res = i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
+ if (res < 0)
+ return res;
+
+ new_conf = conf = res;
+ new_conf &= ~attr->index;
+ if (conf != new_conf) {
+ res = i2c_smbus_write_word_swapped(client, DS620_REG_CONF,
+ new_conf);
+ if (res < 0)
+ return res;
+ }
+
+ return sprintf(buf, "%d\n", !!(conf & attr->index));
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 2);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, DS620_REG_CONFIG_TLF);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, DS620_REG_CONFIG_THF);
+
+static struct attribute *ds620_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,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(ds620);
+
+static int ds620_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct ds620_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct ds620_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the DS620 chip */
+ ds620_init_client(client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, ds620_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ds620_id[] = {
+ {"ds620", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ds620_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ds620_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "ds620",
+ },
+ .probe_new = ds620_probe,
+ .id_table = ds620_id,
+};
+
+module_i2c_driver(ds620_driver);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("DS620 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
new file mode 100644
index 000000000..61d59189a
--- /dev/null
+++ b/drivers/hwmon/emc1403.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * emc1403.c - SMSC Thermal Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#define THERMAL_PID_REG 0xfd
+#define THERMAL_SMSC_ID_REG 0xfe
+#define THERMAL_REVISION_REG 0xff
+
+enum emc1403_chip { emc1402, emc1403, emc1404 };
+
+struct thermal_data {
+ struct regmap *regmap;
+ struct mutex mutex;
+ const struct attribute_group *groups[4];
+};
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+ struct thermal_data *data = dev_get_drvdata(dev);
+ unsigned int val;
+ int retval;
+
+ retval = regmap_read(data->regmap, sda->index, &val);
+ if (retval < 0)
+ return retval;
+ return sprintf(buf, "%d000\n", val);
+}
+
+static ssize_t bit_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
+ struct thermal_data *data = dev_get_drvdata(dev);
+ unsigned int val;
+ int retval;
+
+ retval = regmap_read(data->regmap, sda->nr, &val);
+ if (retval < 0)
+ return retval;
+ return sprintf(buf, "%d\n", !!(val & sda->index));
+}
+
+static ssize_t temp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+ struct thermal_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int retval;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+ retval = regmap_write(data->regmap, sda->index,
+ DIV_ROUND_CLOSEST(val, 1000));
+ if (retval < 0)
+ return retval;
+ return count;
+}
+
+static ssize_t bit_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
+ struct thermal_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int retval;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ retval = regmap_update_bits(data->regmap, sda->nr, sda->index,
+ val ? sda->index : 0);
+ if (retval < 0)
+ return retval;
+ return count;
+}
+
+static ssize_t show_hyst_common(struct device *dev,
+ struct device_attribute *attr, char *buf,
+ bool is_min)
+{
+ struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+ struct thermal_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int limit;
+ unsigned int hyst;
+ int retval;
+
+ retval = regmap_read(regmap, sda->index, &limit);
+ if (retval < 0)
+ return retval;
+
+ retval = regmap_read(regmap, 0x21, &hyst);
+ if (retval < 0)
+ return retval;
+
+ return sprintf(buf, "%d000\n", is_min ? limit + hyst : limit - hyst);
+}
+
+static ssize_t hyst_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_hyst_common(dev, attr, buf, false);
+}
+
+static ssize_t min_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return show_hyst_common(dev, attr, buf, true);
+}
+
+static ssize_t hyst_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+ struct thermal_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int limit;
+ int retval;
+ int hyst;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ retval = regmap_read(regmap, sda->index, &limit);
+ if (retval < 0)
+ goto fail;
+
+ hyst = limit * 1000 - val;
+ hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 255);
+ retval = regmap_write(regmap, 0x21, hyst);
+ if (retval == 0)
+ retval = count;
+fail:
+ mutex_unlock(&data->mutex);
+ return retval;
+}
+
+/*
+ * Sensors. We pass the actual i2c register to the methods.
+ */
+
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, 0x06);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 0x05);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, 0x20);
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0x00);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_min_alarm, bit, 0x36, 0x01);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_max_alarm, bit, 0x35, 0x01);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_crit_alarm, bit, 0x37, 0x01);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_hyst, min_hyst, 0x06);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, hyst, 0x05);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, hyst, 0x20);
+
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, 0x08);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, 0x07);
+static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, 0x19);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 0x01);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_fault, bit, 0x1b, 0x02);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_min_alarm, bit, 0x36, 0x02);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_max_alarm, bit, 0x35, 0x02);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_crit_alarm, bit, 0x37, 0x02);
+static SENSOR_DEVICE_ATTR_RO(temp2_min_hyst, min_hyst, 0x08);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_hyst, hyst, 0x07);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, hyst, 0x19);
+
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp, 0x16);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp, 0x15);
+static SENSOR_DEVICE_ATTR_RW(temp3_crit, temp, 0x1A);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 0x23);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_fault, bit, 0x1b, 0x04);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_min_alarm, bit, 0x36, 0x04);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_max_alarm, bit, 0x35, 0x04);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_crit_alarm, bit, 0x37, 0x04);
+static SENSOR_DEVICE_ATTR_RO(temp3_min_hyst, min_hyst, 0x16);
+static SENSOR_DEVICE_ATTR_RO(temp3_max_hyst, hyst, 0x15);
+static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, hyst, 0x1A);
+
+static SENSOR_DEVICE_ATTR_RW(temp4_min, temp, 0x2D);
+static SENSOR_DEVICE_ATTR_RW(temp4_max, temp, 0x2C);
+static SENSOR_DEVICE_ATTR_RW(temp4_crit, temp, 0x30);
+static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 0x2A);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_fault, bit, 0x1b, 0x08);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_min_alarm, bit, 0x36, 0x08);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_max_alarm, bit, 0x35, 0x08);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_crit_alarm, bit, 0x37, 0x08);
+static SENSOR_DEVICE_ATTR_RO(temp4_min_hyst, min_hyst, 0x2D);
+static SENSOR_DEVICE_ATTR_RO(temp4_max_hyst, hyst, 0x2C);
+static SENSOR_DEVICE_ATTR_RO(temp4_crit_hyst, hyst, 0x30);
+
+static SENSOR_DEVICE_ATTR_2_RW(power_state, bit, 0x03, 0x40);
+
+static struct attribute *emc1402_attrs[] = {
+ &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_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.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_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+
+ &sensor_dev_attr_power_state.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group emc1402_group = {
+ .attrs = emc1402_attrs,
+};
+
+static struct attribute *emc1403_attrs[] = {
+ &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,
+
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.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_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group emc1403_group = {
+ .attrs = emc1403_attrs,
+};
+
+static struct attribute *emc1404_attrs[] = {
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_hyst.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group emc1404_group = {
+ .attrs = emc1404_attrs,
+};
+
+/*
+ * EMC14x2 uses a different register and different bits to report alarm and
+ * fault status. For simplicity, provide a separate attribute group for this
+ * chip series.
+ * Since we can not re-use the same attribute names, create a separate attribute
+ * array.
+ */
+static struct sensor_device_attribute_2 emc1402_alarms[] = {
+ SENSOR_ATTR_2_RO(temp1_min_alarm, bit, 0x02, 0x20),
+ SENSOR_ATTR_2_RO(temp1_max_alarm, bit, 0x02, 0x40),
+ SENSOR_ATTR_2_RO(temp1_crit_alarm, bit, 0x02, 0x01),
+
+ SENSOR_ATTR_2_RO(temp2_fault, bit, 0x02, 0x04),
+ SENSOR_ATTR_2_RO(temp2_min_alarm, bit, 0x02, 0x08),
+ SENSOR_ATTR_2_RO(temp2_max_alarm, bit, 0x02, 0x10),
+ SENSOR_ATTR_2_RO(temp2_crit_alarm, bit, 0x02, 0x02),
+};
+
+static struct attribute *emc1402_alarm_attrs[] = {
+ &emc1402_alarms[0].dev_attr.attr,
+ &emc1402_alarms[1].dev_attr.attr,
+ &emc1402_alarms[2].dev_attr.attr,
+ &emc1402_alarms[3].dev_attr.attr,
+ &emc1402_alarms[4].dev_attr.attr,
+ &emc1402_alarms[5].dev_attr.attr,
+ &emc1402_alarms[6].dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group emc1402_alarm_group = {
+ .attrs = emc1402_alarm_attrs,
+};
+
+static int emc1403_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int id;
+ /* Check if thermal chip is SMSC and EMC1403 or EMC1423 */
+
+ id = i2c_smbus_read_byte_data(client, THERMAL_SMSC_ID_REG);
+ if (id != 0x5d)
+ return -ENODEV;
+
+ id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
+ switch (id) {
+ case 0x20:
+ strscpy(info->type, "emc1402", I2C_NAME_SIZE);
+ break;
+ case 0x21:
+ strscpy(info->type, "emc1403", I2C_NAME_SIZE);
+ break;
+ case 0x22:
+ strscpy(info->type, "emc1422", I2C_NAME_SIZE);
+ break;
+ case 0x23:
+ strscpy(info->type, "emc1423", I2C_NAME_SIZE);
+ break;
+ case 0x25:
+ strscpy(info->type, "emc1404", I2C_NAME_SIZE);
+ break;
+ case 0x27:
+ strscpy(info->type, "emc1424", I2C_NAME_SIZE);
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG);
+ if (id < 0x01 || id > 0x04)
+ return -ENODEV;
+
+ return 0;
+}
+
+static bool emc1403_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00: /* internal diode high byte */
+ case 0x01: /* external diode 1 high byte */
+ case 0x02: /* status */
+ case 0x10: /* external diode 1 low byte */
+ case 0x1b: /* external diode fault */
+ case 0x23: /* external diode 2 high byte */
+ case 0x24: /* external diode 2 low byte */
+ case 0x29: /* internal diode low byte */
+ case 0x2a: /* externl diode 3 high byte */
+ case 0x2b: /* external diode 3 low byte */
+ case 0x35: /* high limit status */
+ case 0x36: /* low limit status */
+ case 0x37: /* therm limit status */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config emc1403_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = emc1403_regmap_is_volatile,
+};
+
+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);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &emc1403_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ mutex_init(&data->mutex);
+
+ switch (id->driver_data) {
+ case emc1404:
+ data->groups[2] = &emc1404_group;
+ fallthrough;
+ case emc1403:
+ data->groups[1] = &emc1403_group;
+ fallthrough;
+ case emc1402:
+ data->groups[0] = &emc1402_group;
+ }
+
+ if (id->driver_data == emc1402)
+ data->groups[1] = &emc1402_alarm_group;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ data->groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(&client->dev, "%s Thermal chip found\n", id->name);
+ return 0;
+}
+
+static const unsigned short emc1403_address_list[] = {
+ 0x18, 0x1c, 0x29, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END
+};
+
+/* Last digit of chip name indicates number of channels */
+static const struct i2c_device_id emc1403_idtable[] = {
+ { "emc1402", emc1402 },
+ { "emc1403", emc1403 },
+ { "emc1404", emc1404 },
+ { "emc1412", emc1402 },
+ { "emc1413", emc1403 },
+ { "emc1414", emc1404 },
+ { "emc1422", emc1402 },
+ { "emc1423", emc1403 },
+ { "emc1424", emc1404 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
+
+static struct i2c_driver sensor_emc1403 = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "emc1403",
+ },
+ .detect = emc1403_detect,
+ .probe_new = emc1403_probe,
+ .id_table = emc1403_idtable,
+ .address_list = emc1403_address_list,
+};
+
+module_i2c_driver(sensor_emc1403);
+
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
+MODULE_DESCRIPTION("emc1403 Thermal Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c
new file mode 100644
index 000000000..361cf9292
--- /dev/null
+++ b/drivers/hwmon/emc2103.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * emc2103.c - Support for SMSC EMC2103
+ * Copyright (c) 2010 SMSC
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x2E, I2C_CLIENT_END };
+
+static const u8 REG_TEMP[4] = { 0x00, 0x02, 0x04, 0x06 };
+static const u8 REG_TEMP_MIN[4] = { 0x3c, 0x38, 0x39, 0x3a };
+static const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 };
+
+#define REG_CONF1 0x20
+#define REG_TEMP_MAX_ALARM 0x24
+#define REG_TEMP_MIN_ALARM 0x25
+#define REG_FAN_CONF1 0x42
+#define REG_FAN_TARGET_LO 0x4c
+#define REG_FAN_TARGET_HI 0x4d
+#define REG_FAN_TACH_HI 0x4e
+#define REG_FAN_TACH_LO 0x4f
+#define REG_PRODUCT_ID 0xfd
+#define REG_MFG_ID 0xfe
+
+/* equation 4 from datasheet: rpm = (3932160 * multipler) / count */
+#define FAN_RPM_FACTOR 3932160
+
+/*
+ * 2103-2 and 2103-4's 3rd temperature sensor can be connected to two diodes
+ * in anti-parallel mode, and in this configuration both can be read
+ * independently (so we have 4 temperature inputs). The device can't
+ * detect if it's connected in this mode, so we have to manually enable
+ * it. Default is to leave the device in the state it's already in (-1).
+ * This parameter allows APD mode to be optionally forced on or off
+ */
+static int apd = -1;
+module_param(apd, bint, 0);
+MODULE_PARM_DESC(apd, "Set to zero to disable anti-parallel diode mode");
+
+struct temperature {
+ s8 degrees;
+ u8 fraction; /* 0-7 multiples of 0.125 */
+};
+
+struct emc2103_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[4];
+ struct mutex update_lock;
+ bool valid; /* registers are valid */
+ bool fan_rpm_control;
+ int temp_count; /* num of temp sensors */
+ unsigned long last_updated; /* in jiffies */
+ struct temperature temp[4]; /* internal + 3 external */
+ s8 temp_min[4]; /* no fractional part */
+ s8 temp_max[4]; /* no fractional part */
+ u8 temp_min_alarm;
+ u8 temp_max_alarm;
+ u8 fan_multiplier;
+ u16 fan_tach;
+ u16 fan_target;
+};
+
+static int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output)
+{
+ int status = i2c_smbus_read_byte_data(client, i2c_reg);
+ if (status < 0) {
+ dev_warn(&client->dev, "reg 0x%02x, err %d\n",
+ i2c_reg, status);
+ } else {
+ *output = status;
+ }
+ return status;
+}
+
+static void read_temp_from_i2c(struct i2c_client *client, u8 i2c_reg,
+ struct temperature *temp)
+{
+ u8 degrees, fractional;
+
+ if (read_u8_from_i2c(client, i2c_reg, &degrees) < 0)
+ return;
+
+ if (read_u8_from_i2c(client, i2c_reg + 1, &fractional) < 0)
+ return;
+
+ temp->degrees = degrees;
+ temp->fraction = (fractional & 0xe0) >> 5;
+}
+
+static void read_fan_from_i2c(struct i2c_client *client, u16 *output,
+ u8 hi_addr, u8 lo_addr)
+{
+ u8 high_byte, lo_byte;
+
+ if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0)
+ return;
+
+ if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0)
+ return;
+
+ *output = ((u16)high_byte << 5) | (lo_byte >> 3);
+}
+
+static void write_fan_target_to_i2c(struct i2c_client *client, u16 new_target)
+{
+ u8 high_byte = (new_target & 0x1fe0) >> 5;
+ u8 low_byte = (new_target & 0x001f) << 3;
+ i2c_smbus_write_byte_data(client, REG_FAN_TARGET_LO, low_byte);
+ i2c_smbus_write_byte_data(client, REG_FAN_TARGET_HI, high_byte);
+}
+
+static void read_fan_config_from_i2c(struct i2c_client *client)
+
+{
+ struct emc2103_data *data = i2c_get_clientdata(client);
+ u8 conf1;
+
+ if (read_u8_from_i2c(client, REG_FAN_CONF1, &conf1) < 0)
+ return;
+
+ data->fan_multiplier = 1 << ((conf1 & 0x60) >> 5);
+ data->fan_rpm_control = (conf1 & 0x80) != 0;
+}
+
+static struct emc2103_data *emc2103_update_device(struct device *dev)
+{
+ struct emc2103_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ int i;
+
+ for (i = 0; i < data->temp_count; i++) {
+ read_temp_from_i2c(client, REG_TEMP[i], &data->temp[i]);
+ read_u8_from_i2c(client, REG_TEMP_MIN[i],
+ &data->temp_min[i]);
+ read_u8_from_i2c(client, REG_TEMP_MAX[i],
+ &data->temp_max[i]);
+ }
+
+ read_u8_from_i2c(client, REG_TEMP_MIN_ALARM,
+ &data->temp_min_alarm);
+ read_u8_from_i2c(client, REG_TEMP_MAX_ALARM,
+ &data->temp_max_alarm);
+
+ read_fan_from_i2c(client, &data->fan_tach,
+ REG_FAN_TACH_HI, REG_FAN_TACH_LO);
+ read_fan_from_i2c(client, &data->fan_target,
+ REG_FAN_TARGET_HI, REG_FAN_TARGET_LO);
+ read_fan_config_from_i2c(client);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t
+temp_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp[nr].degrees * 1000
+ + data->temp[nr].fraction * 125;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+temp_min_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp_min[nr] * 1000;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+temp_max_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int millidegrees = data->temp_max[nr] * 1000;
+ return sprintf(buf, "%d\n", millidegrees);
+}
+
+static ssize_t
+temp_fault_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool fault = (data->temp[nr].degrees == -128);
+ return sprintf(buf, "%d\n", fault ? 1 : 0);
+}
+
+static ssize_t
+temp_min_alarm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool alarm = data->temp_min_alarm & (1 << nr);
+ return sprintf(buf, "%d\n", alarm ? 1 : 0);
+}
+
+static ssize_t
+temp_max_alarm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool alarm = data->temp_max_alarm & (1 << nr);
+ return sprintf(buf, "%d\n", alarm ? 1 : 0);
+}
+
+static ssize_t temp_min_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+
+ int result = kstrtol(buf, 10, &val);
+ if (result < 0)
+ return result;
+
+ val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = val;
+ i2c_smbus_write_byte_data(client, REG_TEMP_MIN[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t temp_max_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct emc2103_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+
+ int result = kstrtol(buf, 10, &val);
+ if (result < 0)
+ return result;
+
+ val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = val;
+ i2c_smbus_write_byte_data(client, REG_TEMP_MAX[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+fan1_input_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int rpm = 0;
+ if (data->fan_tach != 0)
+ rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach;
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t
+fan1_div_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int fan_div = 8 / data->fan_multiplier;
+ return sprintf(buf, "%d\n", fan_div);
+}
+
+/*
+ * Note: we also update the fan target here, because its value is
+ * determined in part by the fan clock divider. This follows the principle
+ * of least surprise; the user doesn't expect the fan target to change just
+ * because the divider changed.
+ */
+static ssize_t fan1_div_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ struct i2c_client *client = data->client;
+ int new_range_bits, old_div = 8 / data->fan_multiplier;
+ long new_div;
+
+ int status = kstrtol(buf, 10, &new_div);
+ if (status < 0)
+ return status;
+
+ if (new_div == old_div) /* No change */
+ return count;
+
+ switch (new_div) {
+ case 1:
+ new_range_bits = 3;
+ break;
+ case 2:
+ new_range_bits = 2;
+ break;
+ case 4:
+ new_range_bits = 1;
+ break;
+ case 8:
+ new_range_bits = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+
+ status = i2c_smbus_read_byte_data(client, REG_FAN_CONF1);
+ if (status < 0) {
+ dev_dbg(&client->dev, "reg 0x%02x, err %d\n",
+ REG_FAN_CONF1, status);
+ mutex_unlock(&data->update_lock);
+ return status;
+ }
+ status &= 0x9F;
+ status |= (new_range_bits << 5);
+ i2c_smbus_write_byte_data(client, REG_FAN_CONF1, status);
+
+ data->fan_multiplier = 8 / new_div;
+
+ /* update fan target if high byte is not disabled */
+ if ((data->fan_target & 0x1fe0) != 0x1fe0) {
+ u16 new_target = (data->fan_target * old_div) / new_div;
+ data->fan_target = min(new_target, (u16)0x1fff);
+ write_fan_target_to_i2c(client, data->fan_target);
+ }
+
+ /* invalidate data to force re-read from hardware */
+ data->valid = false;
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+fan1_target_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ int rpm = 0;
+
+ /* high byte of 0xff indicates disabled so return 0 */
+ if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0))
+ rpm = (FAN_RPM_FACTOR * data->fan_multiplier)
+ / data->fan_target;
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t fan1_target_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ struct i2c_client *client = data->client;
+ unsigned long rpm_target;
+
+ int result = kstrtoul(buf, 10, &rpm_target);
+ if (result < 0)
+ return result;
+
+ /* Datasheet states 16384 as maximum RPM target (table 3.2) */
+ rpm_target = clamp_val(rpm_target, 0, 16384);
+
+ mutex_lock(&data->update_lock);
+
+ if (rpm_target == 0)
+ data->fan_target = 0x1fff;
+ else
+ data->fan_target = clamp_val(
+ (FAN_RPM_FACTOR * data->fan_multiplier) / rpm_target,
+ 0, 0x1fff);
+
+ write_fan_target_to_i2c(client, data->fan_target);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+fan1_fault_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0);
+ return sprintf(buf, "%d\n", fault ? 1 : 0);
+}
+
+static ssize_t
+pwm1_enable_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct emc2103_data *data = emc2103_update_device(dev);
+ return sprintf(buf, "%d\n", data->fan_rpm_control ? 3 : 0);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ struct emc2103_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long new_value;
+ u8 conf_reg;
+
+ int result = kstrtol(buf, 10, &new_value);
+ if (result < 0)
+ return result;
+
+ mutex_lock(&data->update_lock);
+ switch (new_value) {
+ case 0:
+ data->fan_rpm_control = false;
+ break;
+ case 3:
+ data->fan_rpm_control = true;
+ break;
+ default:
+ count = -EINVAL;
+ goto err;
+ }
+
+ result = read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg);
+ if (result < 0) {
+ count = result;
+ goto err;
+ }
+
+ if (data->fan_rpm_control)
+ conf_reg |= 0x80;
+ else
+ conf_reg &= ~0x80;
+
+ i2c_smbus_write_byte_data(client, REG_FAN_CONF1, conf_reg);
+err:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0);
+
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1);
+
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2);
+static SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2);
+
+static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
+static SENSOR_DEVICE_ATTR_RW(temp4_min, temp_min, 3);
+static SENSOR_DEVICE_ATTR_RW(temp4_max, temp_max, 3);
+static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3);
+static SENSOR_DEVICE_ATTR_RO(temp4_min_alarm, temp_min_alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, temp_max_alarm, 3);
+
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR_RW(fan1_div);
+static DEVICE_ATTR_RW(fan1_target);
+static DEVICE_ATTR_RO(fan1_fault);
+
+static DEVICE_ATTR_RW(pwm1_enable);
+
+/* sensors present on all models */
+static struct attribute *emc2103_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_fault.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_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_target.attr,
+ &dev_attr_fan1_fault.attr,
+ &dev_attr_pwm1_enable.attr,
+ NULL
+};
+
+/* extra temperature sensors only present on 2103-2 and 2103-4 */
+static struct attribute *emc2103_attributes_temp3[] = {
+ &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_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ NULL
+};
+
+/* extra temperature sensors only present on 2103-2 and 2103-4 in APD mode */
+static struct attribute *emc2103_attributes_temp4[] = {
+ &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_fault.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 emc2103_group = {
+ .attrs = emc2103_attributes,
+};
+
+static const struct attribute_group emc2103_temp3_group = {
+ .attrs = emc2103_attributes_temp3,
+};
+
+static const struct attribute_group emc2103_temp4_group = {
+ .attrs = emc2103_attributes_temp4,
+};
+
+static int
+emc2103_probe(struct i2c_client *client)
+{
+ struct emc2103_data *data;
+ struct device *hwmon_dev;
+ int status, idx = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct emc2103_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* 2103-2 and 2103-4 have 3 external diodes, 2103-1 has 1 */
+ status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID);
+ if (status == 0x24) {
+ /* 2103-1 only has 1 external diode */
+ data->temp_count = 2;
+ } else {
+ /* 2103-2 and 2103-4 have 3 or 4 external diodes */
+ status = i2c_smbus_read_byte_data(client, REG_CONF1);
+ if (status < 0) {
+ dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_CONF1,
+ status);
+ return status;
+ }
+
+ /* detect current state of hardware */
+ data->temp_count = (status & 0x01) ? 4 : 3;
+
+ /* force APD state if module parameter is set */
+ if (apd == 0) {
+ /* force APD mode off */
+ data->temp_count = 3;
+ status &= ~(0x01);
+ i2c_smbus_write_byte_data(client, REG_CONF1, status);
+ } else if (apd == 1) {
+ /* force APD mode on */
+ data->temp_count = 4;
+ status |= 0x01;
+ i2c_smbus_write_byte_data(client, REG_CONF1, status);
+ }
+ }
+
+ /* sysfs hooks */
+ data->groups[idx++] = &emc2103_group;
+ if (data->temp_count >= 3)
+ data->groups[idx++] = &emc2103_temp3_group;
+ if (data->temp_count == 4)
+ data->groups[idx++] = &emc2103_temp4_group;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ data->groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(&client->dev, "%s: sensor '%s'\n",
+ dev_name(hwmon_dev), client->name);
+
+ return 0;
+}
+
+static const struct i2c_device_id emc2103_ids[] = {
+ { "emc2103", 0, },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, emc2103_ids);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int
+emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ int manufacturer, product;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ manufacturer = i2c_smbus_read_byte_data(new_client, REG_MFG_ID);
+ if (manufacturer != 0x5D)
+ return -ENODEV;
+
+ product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID);
+ if ((product != 0x24) && (product != 0x26))
+ return -ENODEV;
+
+ strscpy(info->type, "emc2103", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static struct i2c_driver emc2103_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "emc2103",
+ },
+ .probe_new = emc2103_probe,
+ .id_table = emc2103_ids,
+ .detect = emc2103_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(emc2103_driver);
+
+MODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>");
+MODULE_DESCRIPTION("SMSC EMC2103 hwmon driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c
new file mode 100644
index 000000000..e42ae43f3
--- /dev/null
+++ b/drivers/hwmon/emc2305.c
@@ -0,0 +1,626 @@
+// 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_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 emc2305_data *data, int cdev_idx, unsigned long state)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+ u8 val, i;
+
+ 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 int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ int cdev_idx, ret;
+ struct emc2305_data *data = cdev->devdata;
+
+ if (state > data->max_state)
+ return -EINVAL;
+
+ cdev_idx = emc2305_get_cdev_idx(cdev);
+ if (cdev_idx < 0)
+ return cdev_idx;
+
+ /* Save thermal state. */
+ data->cdev_data[cdev_idx].last_thermal_state = state;
+ ret = __emc2305_set_cur_state(data, cdev_idx, state);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct thermal_cooling_device_ops emc2305_cooling_ops = {
+ .get_max_state = emc2305_get_max_state,
+ .get_cur_state = emc2305_get_cur_state,
+ .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_idx,
+ 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;
+ 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;
+
+ 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
new file mode 100644
index 000000000..bcd93f0fe
--- /dev/null
+++ b/drivers/hwmon/emc6w201.c
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * emc6w201.c - Hardware monitoring driver for the SMSC EMC6W201
+ * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/*
+ * Addresses to scan
+ */
+
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+/*
+ * The EMC6W201 registers
+ */
+
+#define EMC6W201_REG_IN(nr) (0x20 + (nr))
+#define EMC6W201_REG_TEMP(nr) (0x26 + (nr))
+#define EMC6W201_REG_FAN(nr) (0x2C + (nr) * 2)
+#define EMC6W201_REG_COMPANY 0x3E
+#define EMC6W201_REG_VERSTEP 0x3F
+#define EMC6W201_REG_CONFIG 0x40
+#define EMC6W201_REG_IN_LOW(nr) (0x4A + (nr) * 2)
+#define EMC6W201_REG_IN_HIGH(nr) (0x4B + (nr) * 2)
+#define EMC6W201_REG_TEMP_LOW(nr) (0x56 + (nr) * 2)
+#define EMC6W201_REG_TEMP_HIGH(nr) (0x57 + (nr) * 2)
+#define EMC6W201_REG_FAN_MIN(nr) (0x62 + (nr) * 2)
+
+enum subfeature { input, min, max };
+
+/*
+ * Per-device data
+ */
+
+struct emc6w201_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ u8 in[3][6];
+ s8 temp[3][6];
+ u16 fan[2][5];
+};
+
+/*
+ * Combine LSB and MSB registers in a single value
+ * Locking: must be called with data->update_lock held
+ */
+static u16 emc6w201_read16(struct i2c_client *client, u8 reg)
+{
+ int lsb, msb;
+
+ lsb = i2c_smbus_read_byte_data(client, reg);
+ msb = i2c_smbus_read_byte_data(client, reg + 1);
+ if (unlikely(lsb < 0 || msb < 0)) {
+ dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n",
+ 16, "read", reg);
+ return 0xFFFF; /* Arbitrary value */
+ }
+
+ return (msb << 8) | lsb;
+}
+
+/*
+ * Write 16-bit value to LSB and MSB registers
+ * Locking: must be called with data->update_lock held
+ */
+static int emc6w201_write16(struct i2c_client *client, u8 reg, u16 val)
+{
+ int err;
+
+ err = i2c_smbus_write_byte_data(client, reg, val & 0xff);
+ if (likely(!err))
+ err = i2c_smbus_write_byte_data(client, reg + 1, val >> 8);
+ if (unlikely(err < 0))
+ dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n",
+ 16, "write", reg);
+
+ return err;
+}
+
+/* Read 8-bit value from register */
+static u8 emc6w201_read8(struct i2c_client *client, u8 reg)
+{
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, reg);
+ if (unlikely(val < 0)) {
+ dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n",
+ 8, "read", reg);
+ return 0x00; /* Arbitrary value */
+ }
+
+ return val;
+}
+
+/* Write 8-bit value to register */
+static int emc6w201_write8(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (unlikely(err < 0))
+ dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n",
+ 8, "write", reg);
+
+ return err;
+}
+
+static struct emc6w201_data *emc6w201_update_device(struct device *dev)
+{
+ struct emc6w201_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ for (nr = 0; nr < 6; nr++) {
+ data->in[input][nr] =
+ emc6w201_read8(client,
+ EMC6W201_REG_IN(nr));
+ data->in[min][nr] =
+ emc6w201_read8(client,
+ EMC6W201_REG_IN_LOW(nr));
+ data->in[max][nr] =
+ emc6w201_read8(client,
+ EMC6W201_REG_IN_HIGH(nr));
+ }
+
+ for (nr = 0; nr < 6; nr++) {
+ data->temp[input][nr] =
+ emc6w201_read8(client,
+ EMC6W201_REG_TEMP(nr));
+ data->temp[min][nr] =
+ emc6w201_read8(client,
+ EMC6W201_REG_TEMP_LOW(nr));
+ data->temp[max][nr] =
+ emc6w201_read8(client,
+ EMC6W201_REG_TEMP_HIGH(nr));
+ }
+
+ for (nr = 0; nr < 5; nr++) {
+ data->fan[input][nr] =
+ emc6w201_read16(client,
+ EMC6W201_REG_FAN(nr));
+ data->fan[min][nr] =
+ emc6w201_read16(client,
+ EMC6W201_REG_FAN_MIN(nr));
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs callback functions
+ */
+
+static const s16 nominal_mv[6] = { 2500, 1500, 3300, 5000, 1500, 1500 };
+
+static ssize_t in_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct emc6w201_data *data = emc6w201_update_device(dev);
+ int sf = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+
+ return sprintf(buf, "%u\n",
+ (unsigned)data->in[sf][nr] * nominal_mv[nr] / 0xC0);
+}
+
+static ssize_t in_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct emc6w201_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int sf = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int err;
+ long val;
+ u8 reg;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(val, 0, 255 * nominal_mv[nr] / 192);
+ val = DIV_ROUND_CLOSEST(val * 192, nominal_mv[nr]);
+ reg = (sf == min) ? EMC6W201_REG_IN_LOW(nr)
+ : EMC6W201_REG_IN_HIGH(nr);
+
+ mutex_lock(&data->update_lock);
+ data->in[sf][nr] = val;
+ err = emc6w201_write8(client, reg, data->in[sf][nr]);
+ mutex_unlock(&data->update_lock);
+
+ return err < 0 ? err : count;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct emc6w201_data *data = emc6w201_update_device(dev);
+ int sf = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+
+ return sprintf(buf, "%d\n", (int)data->temp[sf][nr] * 1000);
+}
+
+static ssize_t temp_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf,
+ size_t count)
+{
+ struct emc6w201_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int sf = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int err;
+ long val;
+ u8 reg;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(val, -127000, 127000);
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ reg = (sf == min) ? EMC6W201_REG_TEMP_LOW(nr)
+ : EMC6W201_REG_TEMP_HIGH(nr);
+
+ mutex_lock(&data->update_lock);
+ data->temp[sf][nr] = val;
+ err = emc6w201_write8(client, reg, data->temp[sf][nr]);
+ mutex_unlock(&data->update_lock);
+
+ return err < 0 ? err : count;
+}
+
+static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct emc6w201_data *data = emc6w201_update_device(dev);
+ int sf = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ unsigned rpm;
+
+ if (data->fan[sf][nr] == 0 || data->fan[sf][nr] == 0xFFFF)
+ rpm = 0;
+ else
+ rpm = 5400000U / data->fan[sf][nr];
+
+ return sprintf(buf, "%u\n", rpm);
+}
+
+static ssize_t fan_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct emc6w201_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int sf = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int err;
+ unsigned long val;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val == 0) {
+ val = 0xFFFF;
+ } else {
+ val = DIV_ROUND_CLOSEST(5400000U, val);
+ val = clamp_val(val, 0, 0xFFFE);
+ }
+
+ mutex_lock(&data->update_lock);
+ data->fan[sf][nr] = val;
+ err = emc6w201_write16(client, EMC6W201_REG_FAN_MIN(nr),
+ data->fan[sf][nr]);
+ mutex_unlock(&data->update_lock);
+
+ return err < 0 ? err : count;
+}
+
+static SENSOR_DEVICE_ATTR_2_RO(in0_input, in, 0, input);
+static SENSOR_DEVICE_ATTR_2_RW(in0_min, in, 0, min);
+static SENSOR_DEVICE_ATTR_2_RW(in0_max, in, 0, max);
+static SENSOR_DEVICE_ATTR_2_RO(in1_input, in, 1, input);
+static SENSOR_DEVICE_ATTR_2_RW(in1_min, in, 1, min);
+static SENSOR_DEVICE_ATTR_2_RW(in1_max, in, 1, max);
+static SENSOR_DEVICE_ATTR_2_RO(in2_input, in, 2, input);
+static SENSOR_DEVICE_ATTR_2_RW(in2_min, in, 2, min);
+static SENSOR_DEVICE_ATTR_2_RW(in2_max, in, 2, max);
+static SENSOR_DEVICE_ATTR_2_RO(in3_input, in, 3, input);
+static SENSOR_DEVICE_ATTR_2_RW(in3_min, in, 3, min);
+static SENSOR_DEVICE_ATTR_2_RW(in3_max, in, 3, max);
+static SENSOR_DEVICE_ATTR_2_RO(in4_input, in, 4, input);
+static SENSOR_DEVICE_ATTR_2_RW(in4_min, in, 4, min);
+static SENSOR_DEVICE_ATTR_2_RW(in4_max, in, 4, max);
+static SENSOR_DEVICE_ATTR_2_RO(in5_input, in, 5, input);
+static SENSOR_DEVICE_ATTR_2_RW(in5_min, in, 5, min);
+static SENSOR_DEVICE_ATTR_2_RW(in5_max, in, 5, max);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0, input);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, 0, min);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, 0, max);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, 1, input);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_min, temp, 1, min);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, 1, max);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, 2, input);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_min, temp, 2, min);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, 2, max);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_input, temp, 3, input);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_min, temp, 3, min);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_max, temp, 3, max);
+static SENSOR_DEVICE_ATTR_2_RO(temp5_input, temp, 4, input);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_min, temp, 4, min);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_max, temp, 4, max);
+static SENSOR_DEVICE_ATTR_2_RO(temp6_input, temp, 5, input);
+static SENSOR_DEVICE_ATTR_2_RW(temp6_min, temp, 5, min);
+static SENSOR_DEVICE_ATTR_2_RW(temp6_max, temp, 5, max);
+
+static SENSOR_DEVICE_ATTR_2_RO(fan1_input, fan, 0, input);
+static SENSOR_DEVICE_ATTR_2_RW(fan1_min, fan, 0, min);
+static SENSOR_DEVICE_ATTR_2_RO(fan2_input, fan, 1, input);
+static SENSOR_DEVICE_ATTR_2_RW(fan2_min, fan, 1, min);
+static SENSOR_DEVICE_ATTR_2_RO(fan3_input, fan, 2, input);
+static SENSOR_DEVICE_ATTR_2_RW(fan3_min, fan, 2, min);
+static SENSOR_DEVICE_ATTR_2_RO(fan4_input, fan, 3, input);
+static SENSOR_DEVICE_ATTR_2_RW(fan4_min, fan, 3, min);
+static SENSOR_DEVICE_ATTR_2_RO(fan5_input, fan, 4, input);
+static SENSOR_DEVICE_ATTR_2_RW(fan5_min, fan, 4, min);
+
+static struct attribute *emc6w201_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_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.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_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.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_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.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_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.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_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_min.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(emc6w201);
+
+/*
+ * Driver interface
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int emc6w201_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int company, verstep, config;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Identification */
+ company = i2c_smbus_read_byte_data(client, EMC6W201_REG_COMPANY);
+ if (company != 0x5C)
+ return -ENODEV;
+ verstep = i2c_smbus_read_byte_data(client, EMC6W201_REG_VERSTEP);
+ if (verstep < 0 || (verstep & 0xF0) != 0xB0)
+ return -ENODEV;
+ if ((verstep & 0x0F) > 2) {
+ dev_dbg(&client->dev, "Unknown EMC6W201 stepping %d\n",
+ verstep & 0x0F);
+ return -ENODEV;
+ }
+
+ /* Check configuration */
+ config = i2c_smbus_read_byte_data(client, EMC6W201_REG_CONFIG);
+ if (config < 0 || (config & 0xF4) != 0x04)
+ return -ENODEV;
+ if (!(config & 0x01)) {
+ dev_err(&client->dev, "Monitoring not enabled\n");
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "emc6w201", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int emc6w201_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct emc6w201_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct emc6w201_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ emc6w201_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id emc6w201_id[] = {
+ { "emc6w201", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, emc6w201_id);
+
+static struct i2c_driver emc6w201_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "emc6w201",
+ },
+ .probe_new = emc6w201_probe,
+ .id_table = emc6w201_id,
+ .detect = emc6w201_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(emc6w201_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("SMSC EMC6W201 hardware monitoring driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
new file mode 100644
index 000000000..7f20edb06
--- /dev/null
+++ b/drivers/hwmon/f71805f.c
@@ -0,0 +1,1649 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * f71805f.c - driver for the Fintek F71805F/FG and F71872F/FG Super-I/O
+ * chips integrated hardware monitoring features
+ * Copyright (C) 2005-2006 Jean Delvare <jdelvare@suse.de>
+ *
+ * The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates
+ * complete hardware monitoring features: voltage, fan and temperature
+ * sensors, and manual and automatic fan speed control.
+ *
+ * The F71872F/FG is almost the same, with two more voltages monitored,
+ * and 6 VID inputs.
+ *
+ * The F71806F/FG is essentially the same as the F71872F/FG. It even has
+ * the same chip ID, so the driver can't differentiate between.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.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 <linux/sysfs.h>
+#include <linux/ioport.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static struct platform_device *pdev;
+
+#define DRVNAME "f71805f"
+enum kinds { f71805f, f71872f };
+
+/*
+ * Super-I/O constants and functions
+ */
+
+#define F71805F_LD_HWM 0x04
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_DEVREV 0x22 /* Device revision */
+#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
+#define SIO_REG_FNSEL1 0x29 /* Multi Function Select 1 (F71872F) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_FINTEK_ID 0x1934
+#define SIO_F71805F_ID 0x0406
+#define SIO_F71872F_ID 0x0341
+
+static inline int
+superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static int
+superio_inw(int base, int reg)
+{
+ int val;
+ outb(reg++, base);
+ val = inb(base + 1) << 8;
+ outb(reg, base);
+ val |= inb(base + 1);
+ return val;
+}
+
+static inline void
+superio_select(int base, int ld)
+{
+ outb(SIO_REG_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline int
+superio_enter(int base)
+{
+ if (!request_muxed_region(base, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x87, base);
+ outb(0x87, base);
+
+ return 0;
+}
+
+static inline void
+superio_exit(int base)
+{
+ outb(0xaa, base);
+ release_region(base, 2);
+}
+
+/*
+ * ISA constants
+ */
+
+#define REGION_LENGTH 8
+#define ADDR_REG_OFFSET 5
+#define DATA_REG_OFFSET 6
+
+/*
+ * Registers
+ */
+
+/* in nr from 0 to 10 (8-bit values) */
+#define F71805F_REG_IN(nr) (0x10 + (nr))
+#define F71805F_REG_IN_HIGH(nr) ((nr) < 10 ? 0x40 + 2 * (nr) : 0x2E)
+#define F71805F_REG_IN_LOW(nr) ((nr) < 10 ? 0x41 + 2 * (nr) : 0x2F)
+/* fan nr from 0 to 2 (12-bit values, two registers) */
+#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr))
+#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr))
+#define F71805F_REG_FAN_TARGET(nr) (0x69 + 16 * (nr))
+#define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr))
+#define F71805F_REG_PWM_FREQ(nr) (0x63 + 16 * (nr))
+#define F71805F_REG_PWM_DUTY(nr) (0x6B + 16 * (nr))
+/* temp nr from 0 to 2 (8-bit values) */
+#define F71805F_REG_TEMP(nr) (0x1B + (nr))
+#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr))
+#define F71805F_REG_TEMP_HYST(nr) (0x55 + 2 * (nr))
+#define F71805F_REG_TEMP_MODE 0x01
+/* pwm/fan pwmnr from 0 to 2, auto point apnr from 0 to 2 */
+/* map Fintek numbers to our numbers as follows: 9->0, 5->1, 1->2 */
+#define F71805F_REG_PWM_AUTO_POINT_TEMP(pwmnr, apnr) \
+ (0xA0 + 0x10 * (pwmnr) + (2 - (apnr)))
+#define F71805F_REG_PWM_AUTO_POINT_FAN(pwmnr, apnr) \
+ (0xA4 + 0x10 * (pwmnr) + \
+ 2 * (2 - (apnr)))
+
+#define F71805F_REG_START 0x00
+/* status nr from 0 to 2 */
+#define F71805F_REG_STATUS(nr) (0x36 + (nr))
+
+/* individual register bits */
+#define FAN_CTRL_DC_MODE 0x10
+#define FAN_CTRL_LATCH_FULL 0x08
+#define FAN_CTRL_MODE_MASK 0x03
+#define FAN_CTRL_MODE_SPEED 0x00
+#define FAN_CTRL_MODE_TEMPERATURE 0x01
+#define FAN_CTRL_MODE_MANUAL 0x02
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct f71805f_auto_point {
+ u8 temp[3];
+ u16 fan[3];
+};
+
+struct f71805f_data {
+ unsigned short addr;
+ const char *name;
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+ unsigned long last_limits; /* In jiffies */
+
+ /* Register values */
+ u8 in[11];
+ u8 in_high[11];
+ u8 in_low[11];
+ u16 has_in;
+ u16 fan[3];
+ u16 fan_low[3];
+ u16 fan_target[3];
+ u8 fan_ctrl[3];
+ u8 pwm[3];
+ u8 pwm_freq[3];
+ u8 temp[3];
+ u8 temp_high[3];
+ u8 temp_hyst[3];
+ u8 temp_mode;
+ unsigned long alarms;
+ struct f71805f_auto_point auto_points[3];
+};
+
+struct f71805f_sio_data {
+ enum kinds kind;
+ u8 fnsel1;
+};
+
+static inline long in_from_reg(u8 reg)
+{
+ return reg * 8;
+}
+
+/* The 2 least significant bits are not used */
+static inline u8 in_to_reg(long val)
+{
+ if (val <= 0)
+ return 0;
+ if (val >= 2016)
+ return 0xfc;
+ return ((val + 16) / 32) << 2;
+}
+
+/* in0 is downscaled by a factor 2 internally */
+static inline long in0_from_reg(u8 reg)
+{
+ return reg * 16;
+}
+
+static inline u8 in0_to_reg(long val)
+{
+ if (val <= 0)
+ return 0;
+ if (val >= 4032)
+ return 0xfc;
+ return ((val + 32) / 64) << 2;
+}
+
+/* The 4 most significant bits are not used */
+static inline long fan_from_reg(u16 reg)
+{
+ reg &= 0xfff;
+ if (!reg || reg == 0xfff)
+ return 0;
+ return 1500000 / reg;
+}
+
+static inline u16 fan_to_reg(long rpm)
+{
+ /*
+ * If the low limit is set below what the chip can measure,
+ * store the largest possible 12-bit value in the registers,
+ * so that no alarm will ever trigger.
+ */
+ if (rpm < 367)
+ return 0xfff;
+ return 1500000 / rpm;
+}
+
+static inline unsigned long pwm_freq_from_reg(u8 reg)
+{
+ unsigned long clock = (reg & 0x80) ? 48000000UL : 1000000UL;
+
+ reg &= 0x7f;
+ if (reg == 0)
+ reg++;
+ return clock / (reg << 8);
+}
+
+static inline u8 pwm_freq_to_reg(unsigned long val)
+{
+ if (val >= 187500) /* The highest we can do */
+ return 0x80;
+ if (val >= 1475) /* Use 48 MHz clock */
+ return 0x80 | (48000000UL / (val << 8));
+ if (val < 31) /* The lowest we can do */
+ return 0x7f;
+ else /* Use 1 MHz clock */
+ return 1000000UL / (val << 8);
+}
+
+static inline int pwm_mode_from_reg(u8 reg)
+{
+ return !(reg & FAN_CTRL_DC_MODE);
+}
+
+static inline long temp_from_reg(u8 reg)
+{
+ return reg * 1000;
+}
+
+static inline u8 temp_to_reg(long val)
+{
+ if (val <= 0)
+ return 0;
+ if (val >= 1000 * 0xff)
+ return 0xff;
+ return (val + 500) / 1000;
+}
+
+/*
+ * Device I/O access
+ */
+
+/* Must be called with data->update_lock held, except during initialization */
+static u8 f71805f_read8(struct f71805f_data *data, u8 reg)
+{
+ outb(reg, data->addr + ADDR_REG_OFFSET);
+ return inb(data->addr + DATA_REG_OFFSET);
+}
+
+/* Must be called with data->update_lock held, except during initialization */
+static void f71805f_write8(struct f71805f_data *data, u8 reg, u8 val)
+{
+ outb(reg, data->addr + ADDR_REG_OFFSET);
+ outb(val, data->addr + DATA_REG_OFFSET);
+}
+
+/*
+ * It is important to read the MSB first, because doing so latches the
+ * value of the LSB, so we are sure both bytes belong to the same value.
+ * Must be called with data->update_lock held, except during initialization
+ */
+static u16 f71805f_read16(struct f71805f_data *data, u8 reg)
+{
+ u16 val;
+
+ outb(reg, data->addr + ADDR_REG_OFFSET);
+ val = inb(data->addr + DATA_REG_OFFSET) << 8;
+ outb(++reg, data->addr + ADDR_REG_OFFSET);
+ val |= inb(data->addr + DATA_REG_OFFSET);
+
+ return val;
+}
+
+/* Must be called with data->update_lock held, except during initialization */
+static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val)
+{
+ outb(reg, data->addr + ADDR_REG_OFFSET);
+ outb(val >> 8, data->addr + DATA_REG_OFFSET);
+ outb(++reg, data->addr + ADDR_REG_OFFSET);
+ outb(val & 0xff, data->addr + DATA_REG_OFFSET);
+}
+
+static struct f71805f_data *f71805f_update_device(struct device *dev)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ int nr, apnr;
+
+ mutex_lock(&data->update_lock);
+
+ /* Limit registers cache is refreshed after 60 seconds */
+ if (time_after(jiffies, data->last_updated + 60 * HZ)
+ || !data->valid) {
+ for (nr = 0; nr < 11; nr++) {
+ if (!(data->has_in & (1 << nr)))
+ continue;
+ data->in_high[nr] = f71805f_read8(data,
+ F71805F_REG_IN_HIGH(nr));
+ data->in_low[nr] = f71805f_read8(data,
+ F71805F_REG_IN_LOW(nr));
+ }
+ for (nr = 0; nr < 3; nr++) {
+ data->fan_low[nr] = f71805f_read16(data,
+ F71805F_REG_FAN_LOW(nr));
+ data->fan_target[nr] = f71805f_read16(data,
+ F71805F_REG_FAN_TARGET(nr));
+ data->pwm_freq[nr] = f71805f_read8(data,
+ F71805F_REG_PWM_FREQ(nr));
+ }
+ for (nr = 0; nr < 3; nr++) {
+ data->temp_high[nr] = f71805f_read8(data,
+ F71805F_REG_TEMP_HIGH(nr));
+ data->temp_hyst[nr] = f71805f_read8(data,
+ F71805F_REG_TEMP_HYST(nr));
+ }
+ data->temp_mode = f71805f_read8(data, F71805F_REG_TEMP_MODE);
+ for (nr = 0; nr < 3; nr++) {
+ for (apnr = 0; apnr < 3; apnr++) {
+ data->auto_points[nr].temp[apnr] =
+ f71805f_read8(data,
+ F71805F_REG_PWM_AUTO_POINT_TEMP(nr,
+ apnr));
+ data->auto_points[nr].fan[apnr] =
+ f71805f_read16(data,
+ F71805F_REG_PWM_AUTO_POINT_FAN(nr,
+ apnr));
+ }
+ }
+
+ data->last_limits = jiffies;
+ }
+
+ /* Measurement registers cache is refreshed after 1 second */
+ if (time_after(jiffies, data->last_updated + HZ)
+ || !data->valid) {
+ for (nr = 0; nr < 11; nr++) {
+ if (!(data->has_in & (1 << nr)))
+ continue;
+ data->in[nr] = f71805f_read8(data,
+ F71805F_REG_IN(nr));
+ }
+ for (nr = 0; nr < 3; nr++) {
+ data->fan[nr] = f71805f_read16(data,
+ F71805F_REG_FAN(nr));
+ data->fan_ctrl[nr] = f71805f_read8(data,
+ F71805F_REG_FAN_CTRL(nr));
+ data->pwm[nr] = f71805f_read8(data,
+ F71805F_REG_PWM_DUTY(nr));
+ }
+ for (nr = 0; nr < 3; nr++) {
+ data->temp[nr] = f71805f_read8(data,
+ F71805F_REG_TEMP(nr));
+ }
+ data->alarms = f71805f_read8(data, F71805F_REG_STATUS(0))
+ + (f71805f_read8(data, F71805F_REG_STATUS(1)) << 8)
+ + (f71805f_read8(data, F71805F_REG_STATUS(2)) << 16);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs interface
+ */
+
+static ssize_t show_in0(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", in0_from_reg(data->in[nr]));
+}
+
+static ssize_t show_in0_max(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", in0_from_reg(data->in_high[nr]));
+}
+
+static ssize_t show_in0_min(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", in0_from_reg(data->in_low[nr]));
+}
+
+static ssize_t set_in0_max(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_high[nr] = in0_to_reg(val);
+ f71805f_write8(data, F71805F_REG_IN_HIGH(nr), data->in_high[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_in0_min(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_low[nr] = in0_to_reg(val);
+ f71805f_write8(data, F71805F_REG_IN_LOW(nr), data->in_low[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", in_from_reg(data->in[nr]));
+}
+
+static ssize_t show_in_max(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", in_from_reg(data->in_high[nr]));
+}
+
+static ssize_t show_in_min(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", in_from_reg(data->in_low[nr]));
+}
+
+static ssize_t set_in_max(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_high[nr] = in_to_reg(val);
+ f71805f_write8(data, F71805F_REG_IN_HIGH(nr), data->in_high[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_in_min(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_low[nr] = in_to_reg(val);
+ f71805f_write8(data, F71805F_REG_IN_LOW(nr), data->in_low[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", fan_from_reg(data->fan[nr]));
+}
+
+static ssize_t show_fan_min(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", fan_from_reg(data->fan_low[nr]));
+}
+
+static ssize_t show_fan_target(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", fan_from_reg(data->fan_target[nr]));
+}
+
+static ssize_t set_fan_min(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->fan_low[nr] = fan_to_reg(val);
+ f71805f_write16(data, F71805F_REG_FAN_LOW(nr), data->fan_low[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_fan_target(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->fan_target[nr] = fan_to_reg(val);
+ f71805f_write16(data, F71805F_REG_FAN_TARGET(nr),
+ data->fan_target[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%d\n", (int)data->pwm[nr]);
+}
+
+static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ int mode;
+
+ switch (data->fan_ctrl[nr] & FAN_CTRL_MODE_MASK) {
+ case FAN_CTRL_MODE_SPEED:
+ mode = 3;
+ break;
+ case FAN_CTRL_MODE_TEMPERATURE:
+ mode = 2;
+ break;
+ default: /* MANUAL */
+ mode = 1;
+ }
+
+ return sprintf(buf, "%d\n", mode);
+}
+
+static ssize_t show_pwm_freq(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%lu\n", pwm_freq_from_reg(data->pwm_freq[nr]));
+}
+
+static ssize_t show_pwm_mode(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%d\n", pwm_mode_from_reg(data->fan_ctrl[nr]));
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = val;
+ f71805f_write8(data, F71805F_REG_PWM_DUTY(nr), data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct attribute *f71805f_attr_pwm[];
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ u8 reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val < 1 || val > 3)
+ return -EINVAL;
+
+ if (val > 1) { /* Automatic mode, user can't set PWM value */
+ if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr],
+ S_IRUGO))
+ dev_dbg(dev, "chmod -w pwm%d failed\n", nr + 1);
+ }
+
+ mutex_lock(&data->update_lock);
+ reg = f71805f_read8(data, F71805F_REG_FAN_CTRL(nr))
+ & ~FAN_CTRL_MODE_MASK;
+ switch (val) {
+ case 1:
+ reg |= FAN_CTRL_MODE_MANUAL;
+ break;
+ case 2:
+ reg |= FAN_CTRL_MODE_TEMPERATURE;
+ break;
+ case 3:
+ reg |= FAN_CTRL_MODE_SPEED;
+ break;
+ }
+ data->fan_ctrl[nr] = reg;
+ f71805f_write8(data, F71805F_REG_FAN_CTRL(nr), reg);
+ mutex_unlock(&data->update_lock);
+
+ if (val == 1) { /* Manual mode, user can set PWM value */
+ if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr],
+ S_IRUGO | S_IWUSR))
+ dev_dbg(dev, "chmod +w pwm%d failed\n", nr + 1);
+ }
+
+ return count;
+}
+
+static ssize_t set_pwm_freq(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->pwm_freq[nr] = pwm_freq_to_reg(val);
+ f71805f_write8(data, F71805F_REG_PWM_FREQ(nr), data->pwm_freq[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int pwmnr = attr->nr;
+ int apnr = attr->index;
+
+ return sprintf(buf, "%ld\n",
+ temp_from_reg(data->auto_points[pwmnr].temp[apnr]));
+}
+
+static ssize_t set_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int pwmnr = attr->nr;
+ int apnr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->auto_points[pwmnr].temp[apnr] = temp_to_reg(val);
+ f71805f_write8(data, F71805F_REG_PWM_AUTO_POINT_TEMP(pwmnr, apnr),
+ data->auto_points[pwmnr].temp[apnr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_pwm_auto_point_fan(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int pwmnr = attr->nr;
+ int apnr = attr->index;
+
+ return sprintf(buf, "%ld\n",
+ fan_from_reg(data->auto_points[pwmnr].fan[apnr]));
+}
+
+static ssize_t set_pwm_auto_point_fan(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int pwmnr = attr->nr;
+ int apnr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->auto_points[pwmnr].fan[apnr] = fan_to_reg(val);
+ f71805f_write16(data, F71805F_REG_PWM_AUTO_POINT_FAN(pwmnr, apnr),
+ data->auto_points[pwmnr].fan[apnr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg(data->temp[nr]));
+}
+
+static ssize_t show_temp_max(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg(data->temp_high[nr]));
+}
+
+static ssize_t show_temp_hyst(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg(data->temp_hyst[nr]));
+}
+
+static ssize_t show_temp_type(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+
+ /* 3 is diode, 4 is thermistor */
+ return sprintf(buf, "%u\n", (data->temp_mode & (1 << nr)) ? 3 : 4);
+}
+
+static ssize_t set_temp_max(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_high[nr] = temp_to_reg(val);
+ f71805f_write8(data, F71805F_REG_TEMP_HIGH(nr), data->temp_high[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_temp_hyst(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_hyst[nr] = temp_to_reg(val);
+ f71805f_write8(data, F71805F_REG_TEMP_HYST(nr), data->temp_hyst[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t alarms_in_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+
+ return sprintf(buf, "%lu\n", data->alarms & 0x7ff);
+}
+
+static ssize_t alarms_fan_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+
+ return sprintf(buf, "%lu\n", (data->alarms >> 16) & 0x07);
+}
+
+static ssize_t alarms_temp_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+
+ return sprintf(buf, "%lu\n", (data->alarms >> 11) & 0x07);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%lu\n", (data->alarms >> bitnr) & 1);
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL, 0);
+static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR,
+ show_in0_max, set_in0_max, 0);
+static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR,
+ show_in0_min, set_in0_min, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 1);
+static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 2);
+static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 3);
+static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 4);
+static SENSOR_DEVICE_ATTR(in4_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in5_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 5);
+static SENSOR_DEVICE_ATTR(in5_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in6_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 6);
+static SENSOR_DEVICE_ATTR(in6_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in7_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 7);
+static SENSOR_DEVICE_ATTR(in7_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 8);
+static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 8);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in0, NULL, 9);
+static SENSOR_DEVICE_ATTR(in9_max, S_IRUGO | S_IWUSR,
+ show_in0_max, set_in0_max, 9);
+static SENSOR_DEVICE_ATTR(in9_min, S_IRUGO | S_IWUSR,
+ show_in0_min, set_in0_min, 9);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in0, NULL, 10);
+static SENSOR_DEVICE_ATTR(in10_max, S_IRUGO | S_IWUSR,
+ show_in0_max, set_in0_max, 10);
+static SENSOR_DEVICE_ATTR(in10_min, S_IRUGO | S_IWUSR,
+ show_in0_min, set_in0_min, 10);
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR,
+ show_fan_target, set_fan_target, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan2_target, S_IRUGO | S_IWUSR,
+ show_fan_target, set_fan_target, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 2);
+static SENSOR_DEVICE_ATTR(fan3_target, S_IRUGO | S_IWUSR,
+ show_fan_target, set_fan_target, 2);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);
+
+/*
+ * pwm (value) files are created read-only, write permission is
+ * then added or removed dynamically as needed
+ */
+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR,
+ show_pwm_freq, set_pwm_freq, 0);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO, show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm2_freq, S_IRUGO | S_IWUSR,
+ show_pwm_freq, set_pwm_freq, 1);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IRUGO, show_pwm_mode, NULL, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO, show_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm3_freq, S_IRUGO | S_IWUSR,
+ show_pwm_freq, set_pwm_freq, 2);
+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IRUGO, show_pwm_mode, NULL, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 0, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 1, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp,
+ 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_fan, S_IRUGO | S_IWUSR,
+ show_pwm_auto_point_fan, set_pwm_auto_point_fan,
+ 2, 2);
+
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 16);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 17);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 18);
+static DEVICE_ATTR_RO(alarms_in);
+static DEVICE_ATTR_RO(alarms_fan);
+static DEVICE_ATTR_RO(alarms_temp);
+
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *f71805f_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_target.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_target.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_target.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_fan.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_fan.dev_attr.attr,
+
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &dev_attr_alarms_in.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,
+ &dev_attr_alarms_temp.attr,
+ &dev_attr_alarms_fan.attr,
+
+ &dev_attr_name.attr,
+ NULL
+};
+
+static const struct attribute_group f71805f_group = {
+ .attrs = f71805f_attributes,
+};
+
+static struct attribute *f71805f_attributes_optin[4][5] = {
+ {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in9_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in10_alarm.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group f71805f_group_optin[4] = {
+ { .attrs = f71805f_attributes_optin[0] },
+ { .attrs = f71805f_attributes_optin[1] },
+ { .attrs = f71805f_attributes_optin[2] },
+ { .attrs = f71805f_attributes_optin[3] },
+};
+
+/*
+ * We don't include pwm_freq files in the arrays above, because they must be
+ * created conditionally (only if pwm_mode is 1 == PWM)
+ */
+static struct attribute *f71805f_attributes_pwm_freq[] = {
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group f71805f_group_pwm_freq = {
+ .attrs = f71805f_attributes_pwm_freq,
+};
+
+/* We also need an indexed access to pwmN files to toggle writability */
+static struct attribute *f71805f_attr_pwm[] = {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+};
+
+/*
+ * Device registration and initialization
+ */
+
+static void f71805f_init_device(struct f71805f_data *data)
+{
+ u8 reg;
+ int i;
+
+ reg = f71805f_read8(data, F71805F_REG_START);
+ if ((reg & 0x41) != 0x01) {
+ pr_debug("Starting monitoring operations\n");
+ f71805f_write8(data, F71805F_REG_START, (reg | 0x01) & ~0x40);
+ }
+
+ /*
+ * Fan monitoring can be disabled. If it is, we won't be polling
+ * the register values, and won't create the related sysfs files.
+ */
+ for (i = 0; i < 3; i++) {
+ data->fan_ctrl[i] = f71805f_read8(data,
+ F71805F_REG_FAN_CTRL(i));
+ /*
+ * Clear latch full bit, else "speed mode" fan speed control
+ * doesn't work
+ */
+ if (data->fan_ctrl[i] & FAN_CTRL_LATCH_FULL) {
+ data->fan_ctrl[i] &= ~FAN_CTRL_LATCH_FULL;
+ f71805f_write8(data, F71805F_REG_FAN_CTRL(i),
+ data->fan_ctrl[i]);
+ }
+ }
+}
+
+static int f71805f_probe(struct platform_device *pdev)
+{
+ struct f71805f_sio_data *sio_data = dev_get_platdata(&pdev->dev);
+ struct f71805f_data *data;
+ struct resource *res;
+ int i, err;
+
+ static const char * const names[] = {
+ "f71805f",
+ "f71872f",
+ };
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct f71805f_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start + ADDR_REG_OFFSET, 2,
+ DRVNAME)) {
+ dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n",
+ (unsigned long)(res->start + ADDR_REG_OFFSET),
+ (unsigned long)(res->start + ADDR_REG_OFFSET + 1));
+ return -EBUSY;
+ }
+ data->addr = res->start;
+ data->name = names[sio_data->kind];
+ mutex_init(&data->update_lock);
+
+ platform_set_drvdata(pdev, data);
+
+ /* Some voltage inputs depend on chip model and configuration */
+ switch (sio_data->kind) {
+ case f71805f:
+ data->has_in = 0x1ff;
+ break;
+ case f71872f:
+ data->has_in = 0x6ef;
+ if (sio_data->fnsel1 & 0x01)
+ data->has_in |= (1 << 4); /* in4 */
+ if (sio_data->fnsel1 & 0x02)
+ data->has_in |= (1 << 8); /* in8 */
+ break;
+ }
+
+ /* Initialize the F71805F chip */
+ f71805f_init_device(data);
+
+ /* Register sysfs interface files */
+ err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group);
+ if (err)
+ return err;
+ if (data->has_in & (1 << 4)) { /* in4 */
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &f71805f_group_optin[0]);
+ if (err)
+ goto exit_remove_files;
+ }
+ if (data->has_in & (1 << 8)) { /* in8 */
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &f71805f_group_optin[1]);
+ if (err)
+ goto exit_remove_files;
+ }
+ if (data->has_in & (1 << 9)) { /* in9 (F71872F/FG only) */
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &f71805f_group_optin[2]);
+ if (err)
+ goto exit_remove_files;
+ }
+ if (data->has_in & (1 << 10)) { /* in9 (F71872F/FG only) */
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &f71805f_group_optin[3]);
+ if (err)
+ goto exit_remove_files;
+ }
+ for (i = 0; i < 3; i++) {
+ /* If control mode is PWM, create pwm_freq file */
+ if (!(data->fan_ctrl[i] & FAN_CTRL_DC_MODE)) {
+ err = sysfs_create_file(&pdev->dev.kobj,
+ f71805f_attributes_pwm_freq[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+ /* If PWM is in manual mode, add write permission */
+ if (data->fan_ctrl[i] & FAN_CTRL_MODE_MANUAL) {
+ err = sysfs_chmod_file(&pdev->dev.kobj,
+ f71805f_attr_pwm[i],
+ S_IRUGO | S_IWUSR);
+ if (err) {
+ dev_err(&pdev->dev, "chmod +w pwm%d failed\n",
+ i + 1);
+ goto exit_remove_files;
+ }
+ }
+ }
+
+ 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_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
+ for (i = 0; i < 4; i++)
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
+ return err;
+}
+
+static int f71805f_remove(struct platform_device *pdev)
+{
+ struct f71805f_data *data = platform_get_drvdata(pdev);
+ int i;
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
+ for (i = 0; i < 4; i++)
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
+
+ return 0;
+}
+
+static struct platform_driver f71805f_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = f71805f_probe,
+ .remove = f71805f_remove,
+};
+
+static int __init f71805f_device_add(unsigned short address,
+ const struct f71805f_sio_data *sio_data)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + REGION_LENGTH - 1,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ res.name = pdev->name;
+ 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;
+ }
+
+ err = platform_device_add_data(pdev, sio_data,
+ sizeof(struct f71805f_sio_data));
+ if (err) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int __init f71805f_find(int sioaddr, unsigned short *address,
+ struct f71805f_sio_data *sio_data)
+{
+ int err;
+ u16 devid;
+
+ static const char * const names[] = {
+ "F71805F/FG",
+ "F71872F/FG or F71806F/FG",
+ };
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ err = -ENODEV;
+ devid = superio_inw(sioaddr, SIO_REG_MANID);
+ if (devid != SIO_FINTEK_ID)
+ goto exit;
+
+ devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_F71805F_ID:
+ sio_data->kind = f71805f;
+ break;
+ case SIO_F71872F_ID:
+ sio_data->kind = f71872f;
+ sio_data->fnsel1 = superio_inb(sioaddr, SIO_REG_FNSEL1);
+ break;
+ default:
+ pr_info("Unsupported Fintek device, skipping\n");
+ goto exit;
+ }
+
+ superio_select(sioaddr, F71805F_LD_HWM);
+ if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
+ pr_warn("Device not activated, skipping\n");
+ goto exit;
+ }
+
+ *address = superio_inw(sioaddr, SIO_REG_ADDR);
+ if (*address == 0) {
+ pr_warn("Base address not set, skipping\n");
+ goto exit;
+ }
+ *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */
+
+ err = 0;
+ pr_info("Found %s chip at %#x, revision %u\n",
+ names[sio_data->kind], *address,
+ superio_inb(sioaddr, SIO_REG_DEVREV));
+
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+static int __init f71805f_init(void)
+{
+ int err;
+ unsigned short address;
+ struct f71805f_sio_data sio_data;
+
+ if (f71805f_find(0x2e, &address, &sio_data)
+ && f71805f_find(0x4e, &address, &sio_data))
+ return -ENODEV;
+
+ err = platform_driver_register(&f71805f_driver);
+ if (err)
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ err = f71805f_device_add(address, &sio_data);
+ if (err)
+ goto exit_driver;
+
+ return 0;
+
+exit_driver:
+ platform_driver_unregister(&f71805f_driver);
+exit:
+ return err;
+}
+
+static void __exit f71805f_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&f71805f_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver");
+
+module_init(f71805f_init);
+module_exit(f71805f_exit);
diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c
new file mode 100644
index 000000000..27207ec6f
--- /dev/null
+++ b/drivers/hwmon/f71882fg.c
@@ -0,0 +1,2705 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
+ * Copyright (C) 2007-2011 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.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 <linux/io.h>
+#include <linux/acpi.h>
+
+#define DRVNAME "f71882fg"
+
+#define SIO_F71858FG_LD_HWM 0x02 /* Hardware monitor logical device */
+#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */
+#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_DEVREV 0x22 /* Device revision */
+#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
+#define SIO_F71808E_ID 0x0901 /* Chipset ID */
+#define SIO_F71808A_ID 0x1001 /* Chipset ID */
+#define SIO_F71858_ID 0x0507 /* Chipset ID */
+#define SIO_F71862_ID 0x0601 /* Chipset ID */
+#define SIO_F71868_ID 0x1106 /* Chipset ID */
+#define SIO_F71869_ID 0x0814 /* Chipset ID */
+#define SIO_F71869A_ID 0x1007 /* Chipset ID */
+#define SIO_F71882_ID 0x0541 /* Chipset ID */
+#define SIO_F71889_ID 0x0723 /* Chipset ID */
+#define SIO_F71889E_ID 0x0909 /* Chipset ID */
+#define SIO_F71889A_ID 0x1005 /* Chipset ID */
+#define SIO_F8000_ID 0x0581 /* Chipset ID */
+#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
+#define DATA_REG_OFFSET 6
+
+#define F71882FG_REG_IN_STATUS 0x12 /* f7188x only */
+#define F71882FG_REG_IN_BEEP 0x13 /* f7188x only */
+#define F71882FG_REG_IN(nr) (0x20 + (nr))
+#define F71882FG_REG_IN1_HIGH 0x32 /* f7188x only */
+
+#define F81866_REG_IN_STATUS 0x16 /* F81866 only */
+#define F81866_REG_IN_BEEP 0x17 /* F81866 only */
+#define F81866_REG_IN1_HIGH 0x3a /* F81866 only */
+
+#define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr)))
+#define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr)))
+#define F71882FG_REG_FAN_FULL_SPEED(nr) (0xA4 + (16 * (nr)))
+#define F71882FG_REG_FAN_STATUS 0x92
+#define F71882FG_REG_FAN_BEEP 0x93
+
+#define F71882FG_REG_TEMP(nr) (0x70 + 2 * (nr))
+#define F71882FG_REG_TEMP_OVT(nr) (0x80 + 2 * (nr))
+#define F71882FG_REG_TEMP_HIGH(nr) (0x81 + 2 * (nr))
+#define F71882FG_REG_TEMP_STATUS 0x62
+#define F71882FG_REG_TEMP_BEEP 0x63
+#define F71882FG_REG_TEMP_CONFIG 0x69
+#define F71882FG_REG_TEMP_HYST(nr) (0x6C + (nr))
+#define F71882FG_REG_TEMP_TYPE 0x6B
+#define F71882FG_REG_TEMP_DIODE_OPEN 0x6F
+
+#define F71882FG_REG_PWM(nr) (0xA3 + (16 * (nr)))
+#define F71882FG_REG_PWM_TYPE 0x94
+#define F71882FG_REG_PWM_ENABLE 0x96
+
+#define F71882FG_REG_FAN_HYST(nr) (0x98 + (nr))
+
+#define F71882FG_REG_FAN_FAULT_T 0x9F
+#define F71882FG_FAN_NEG_TEMP_EN 0x20
+#define F71882FG_FAN_PROG_SEL 0x80
+
+#define F71882FG_REG_POINT_PWM(pwm, point) (0xAA + (point) + (16 * (pwm)))
+#define F71882FG_REG_POINT_TEMP(pwm, point) (0xA6 + (point) + (16 * (pwm)))
+#define F71882FG_REG_POINT_MAPPING(nr) (0xAF + 16 * (nr))
+
+#define F71882FG_REG_START 0x01
+
+#define F71882FG_MAX_INS 11
+
+#define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+enum chips { f71808e, f71808a, f71858fg, f71862fg, f71868a, f71869, f71869a,
+ f71882fg, f71889fg, f71889ed, f71889a, f8000, f81768d, f81865f,
+ f81866a};
+
+static const char *const f71882fg_names[] = {
+ "f71808e",
+ "f71808a",
+ "f71858fg",
+ "f71862fg",
+ "f71868a",
+ "f71869", /* Both f71869f and f71869e, reg. compatible and same id */
+ "f71869a",
+ "f71882fg",
+ "f71889fg", /* f81801u too, same id */
+ "f71889ed",
+ "f71889a",
+ "f8000",
+ "f81768d",
+ "f81865f",
+ "f81866a",
+};
+
+static const char f71882fg_has_in[][F71882FG_MAX_INS] = {
+ [f71808e] = { 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0 },
+ [f71808a] = { 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0 },
+ [f71858fg] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [f71862fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ [f71868a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+ [f71869] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ [f71869a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ [f71882fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ [f71889fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ [f71889ed] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ [f71889a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
+ [f8000] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ [f81768d] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ [f81865f] = { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ [f81866a] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
+};
+
+static const char f71882fg_has_in1_alarm[] = {
+ [f71808e] = 0,
+ [f71808a] = 0,
+ [f71858fg] = 0,
+ [f71862fg] = 0,
+ [f71868a] = 0,
+ [f71869] = 0,
+ [f71869a] = 0,
+ [f71882fg] = 1,
+ [f71889fg] = 1,
+ [f71889ed] = 1,
+ [f71889a] = 1,
+ [f8000] = 0,
+ [f81768d] = 1,
+ [f81865f] = 1,
+ [f81866a] = 1,
+};
+
+static const char f71882fg_fan_has_beep[] = {
+ [f71808e] = 0,
+ [f71808a] = 0,
+ [f71858fg] = 0,
+ [f71862fg] = 1,
+ [f71868a] = 1,
+ [f71869] = 1,
+ [f71869a] = 1,
+ [f71882fg] = 1,
+ [f71889fg] = 1,
+ [f71889ed] = 1,
+ [f71889a] = 1,
+ [f8000] = 0,
+ [f81768d] = 1,
+ [f81865f] = 1,
+ [f81866a] = 1,
+};
+
+static const char f71882fg_nr_fans[] = {
+ [f71808e] = 3,
+ [f71808a] = 2, /* +1 fan which is monitor + simple pwm only */
+ [f71858fg] = 3,
+ [f71862fg] = 3,
+ [f71868a] = 3,
+ [f71869] = 3,
+ [f71869a] = 3,
+ [f71882fg] = 4,
+ [f71889fg] = 3,
+ [f71889ed] = 3,
+ [f71889a] = 3,
+ [f8000] = 3, /* +1 fan which is monitor only */
+ [f81768d] = 3,
+ [f81865f] = 2,
+ [f81866a] = 3,
+};
+
+static const char f71882fg_temp_has_beep[] = {
+ [f71808e] = 0,
+ [f71808a] = 1,
+ [f71858fg] = 0,
+ [f71862fg] = 1,
+ [f71868a] = 1,
+ [f71869] = 1,
+ [f71869a] = 1,
+ [f71882fg] = 1,
+ [f71889fg] = 1,
+ [f71889ed] = 1,
+ [f71889a] = 1,
+ [f8000] = 0,
+ [f81768d] = 1,
+ [f81865f] = 1,
+ [f81866a] = 1,
+};
+
+static const char f71882fg_nr_temps[] = {
+ [f71808e] = 2,
+ [f71808a] = 2,
+ [f71858fg] = 3,
+ [f71862fg] = 3,
+ [f71868a] = 3,
+ [f71869] = 3,
+ [f71869a] = 3,
+ [f71882fg] = 3,
+ [f71889fg] = 3,
+ [f71889ed] = 3,
+ [f71889a] = 3,
+ [f8000] = 3,
+ [f81768d] = 3,
+ [f81865f] = 2,
+ [f81866a] = 3,
+};
+
+static struct platform_device *f71882fg_pdev;
+
+struct f71882fg_sio_data {
+ enum chips type;
+};
+
+struct f71882fg_data {
+ unsigned short addr;
+ enum chips type;
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ int temp_start; /* temp numbering start (0 or 1) */
+ 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 */
+
+ /* Register Values */
+ u8 in[F71882FG_MAX_INS];
+ u8 in1_max;
+ u8 in_status;
+ u8 in_beep;
+ u16 fan[4];
+ u16 fan_target[4];
+ u16 fan_full_speed[4];
+ u8 fan_status;
+ u8 fan_beep;
+ /*
+ * Note: all models have max 3 temperature channels, but on some
+ * they are addressed as 0-2 and on others as 1-3, so for coding
+ * convenience we reserve space for 4 channels
+ */
+ u16 temp[4];
+ u8 temp_ovt[4];
+ u8 temp_high[4];
+ u8 temp_hyst[2]; /* 2 hysts stored per reg */
+ u8 temp_type[4];
+ u8 temp_status;
+ u8 temp_beep;
+ u8 temp_diode_open;
+ u8 temp_config;
+ u8 pwm[4];
+ u8 pwm_enable;
+ u8 pwm_auto_point_hyst[2];
+ u8 pwm_auto_point_mapping[4];
+ u8 pwm_auto_point_pwm[4][5];
+ s8 pwm_auto_point_temp[4][4];
+};
+
+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)
+{
+ 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_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)
+{
+ 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_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
+ * temperature indexes start at 0 (the others start at 1)
+ */
+static struct sensor_device_attribute_2 f71858fg_temp_attr[] = {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+ SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 0),
+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 0),
+ SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0),
+ SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 0),
+ SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 0),
+ SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
+ SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 1),
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 1),
+ SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 1),
+ SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 1),
+ SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 2),
+ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 2),
+ SENSOR_ATTR_2(temp3_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2),
+ SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 2),
+ SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 2),
+ SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+ 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),
+ SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 1),
+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 1),
+ /*
+ * Should really be temp1_max_alarm, but older versions did not handle
+ * the max and crit alarms separately and lm_sensors v2 depends on the
+ * presence of temp#_alarm files. The same goes for temp2/3 _alarm.
+ */
+ SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1),
+ SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 1),
+ SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 1),
+ SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+ SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1),
+ SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
+}, {
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 2),
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 2),
+ /* Should be temp2_max_alarm, see temp1_alarm note */
+ SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2),
+ SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 2),
+ SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 2),
+ SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+ SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
+}, {
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 3),
+ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+ store_temp_max_hyst, 0, 3),
+ /* Should be temp3_max_alarm, see temp1_alarm note */
+ SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3),
+ SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 3),
+ SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
+ 0, 3),
+ SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 7),
+ SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3),
+ 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,
+ store_temp_beep, 0, 1),
+ SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 5),
+}, {
+ SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 2),
+ SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 6),
+}, {
+ SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 3),
+ SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 7),
+} };
+
+static struct sensor_device_attribute_2 f81866_temp_beep_attr[3][2] = { {
+ SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 0),
+ SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 4),
+}, {
+ SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 1),
+ SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 5),
+}, {
+ SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 2),
+ SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+ store_temp_beep, 0, 6),
+} };
+
+/*
+ * Temp attr for the f8000
+ * Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max)
+ * is used as hysteresis value to clear alarms
+ * Also like the f71858fg its temperature indexes start at 0
+ */
+static struct sensor_device_attribute_2 f8000_temp_attr[] = {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+ SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 0),
+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 0),
+ SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
+ SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 1),
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 1),
+ SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
+ SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit,
+ store_temp_crit, 0, 2),
+ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
+ store_temp_max, 0, 2),
+ SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
+ 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),
+ SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
+ SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
+ SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3),
+ SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4),
+ SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5),
+ SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6),
+ SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7),
+ SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8),
+ SENSOR_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 0, 9),
+ 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,
+ 0, 1),
+ SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep,
+ 0, 1),
+ 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 */
+ if (fan_from_reg(data->fan_full_speed[nr]))
+ val = 255 * fan_from_reg(data->fan_target[nr])
+ / fan_from_reg(data->fan_full_speed[nr]);
+ else
+ val = 0;
+ }
+ 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),
+ SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 0),
+ SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0),
+ SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0),
+ SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+ store_pwm_enable, 0, 0),
+ SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR,
+ show_pwm_interpolate, store_pwm_interpolate, 0, 0),
+}, {
+ SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1),
+ SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 1),
+ SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1),
+ SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1),
+ SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+ store_pwm_enable, 0, 1),
+ SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR,
+ show_pwm_interpolate, store_pwm_interpolate, 0, 1),
+}, {
+ SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
+ SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 2),
+ SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
+ SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
+ SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+ store_pwm_enable, 0, 2),
+ SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR,
+ show_pwm_interpolate, store_pwm_interpolate, 0, 2),
+}, {
+ SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+ SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 3),
+ SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
+ SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
+ SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+ store_pwm_enable, 0, 3),
+ SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR,
+ 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),
+ SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
+ SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR,
+ 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,
+ store_fan_beep, 0, 0),
+ SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ store_fan_beep, 0, 1),
+ SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ store_fan_beep, 0, 2),
+ SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ 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
+ */
+static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 0),
+}, {
+ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 1),
+}, {
+ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+} };
+
+/*
+ * PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the
+ * pwm setting when the temperature is above the pwmX_auto_point1_temp can be
+ * programmed instead of being hardcoded to 0xff
+ */
+static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 0),
+ SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 0),
+}, {
+ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 1),
+ SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 1),
+}, {
+ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 2),
+ SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+} };
+
+/* PWM attr for the standard models */
+static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 0),
+ SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 2, 0),
+ SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 3, 0),
+ SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 1, 0),
+ SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 2, 0),
+ SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 1, 0),
+ SENSOR_ATTR_2(pwm1_auto_point3_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 2, 0),
+ SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 0),
+}, {
+ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 1),
+ SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 2, 1),
+ SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 3, 1),
+ SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 1, 1),
+ SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 2, 1),
+ SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 1),
+ SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 1, 1),
+ SENSOR_ATTR_2(pwm2_auto_point3_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 2, 1),
+ SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 1),
+}, {
+ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 2),
+ SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 2, 2),
+ SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 3, 2),
+ SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 1, 2),
+ SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 2, 2),
+ SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 2),
+ SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 2),
+ SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 1, 2),
+ SENSOR_ATTR_2(pwm3_auto_point3_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 2, 2),
+ SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+}, {
+ SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 3),
+ SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 3),
+ SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 3),
+ SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 2, 3),
+ SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 3, 3),
+ SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 3),
+ SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 3),
+ SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 1, 3),
+ SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 2, 3),
+ SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 3),
+ SENSOR_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 3),
+ SENSOR_ATTR_2(pwm4_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 1, 3),
+ SENSOR_ATTR_2(pwm4_auto_point3_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 2, 3),
+ SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 3),
+} };
+
+/* Fan attr specific to the f8000 (4th fan input can only measure speed) */
+static struct sensor_device_attribute_2 f8000_fan_attr[] = {
+ SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+};
+
+/*
+ * PWM attr for the f8000, zones mapped to temp instead of to pwm!
+ * Also the register block at offset A0 maps to TEMP1 (so our temp2, as the
+ * F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0
+ */
+static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 0),
+ SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 2),
+ SENSOR_ATTR_2(temp1_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 2),
+ SENSOR_ATTR_2(temp1_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 2, 2),
+ SENSOR_ATTR_2(temp1_auto_point4_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 3, 2),
+ SENSOR_ATTR_2(temp1_auto_point5_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 2),
+ SENSOR_ATTR_2(temp1_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 2),
+ SENSOR_ATTR_2(temp1_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 1, 2),
+ SENSOR_ATTR_2(temp1_auto_point3_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 2, 2),
+ SENSOR_ATTR_2(temp1_auto_point4_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 2),
+ SENSOR_ATTR_2(temp1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 2),
+ SENSOR_ATTR_2(temp1_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 1, 2),
+ SENSOR_ATTR_2(temp1_auto_point3_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 2, 2),
+ SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+}, {
+ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 1),
+ SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 0),
+ SENSOR_ATTR_2(temp2_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 0),
+ SENSOR_ATTR_2(temp2_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 2, 0),
+ SENSOR_ATTR_2(temp2_auto_point4_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 3, 0),
+ SENSOR_ATTR_2(temp2_auto_point5_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 0),
+ SENSOR_ATTR_2(temp2_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 0),
+ SENSOR_ATTR_2(temp2_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 1, 0),
+ SENSOR_ATTR_2(temp2_auto_point3_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 2, 0),
+ SENSOR_ATTR_2(temp2_auto_point4_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 0),
+ SENSOR_ATTR_2(temp2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 0),
+ SENSOR_ATTR_2(temp2_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 1, 0),
+ SENSOR_ATTR_2(temp2_auto_point3_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 2, 0),
+ SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 0),
+}, {
+ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 2),
+ SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 0, 1),
+ SENSOR_ATTR_2(temp3_auto_point2_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 1, 1),
+ SENSOR_ATTR_2(temp3_auto_point3_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 2, 1),
+ SENSOR_ATTR_2(temp3_auto_point4_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 3, 1),
+ SENSOR_ATTR_2(temp3_auto_point5_pwm, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
+ 4, 1),
+ SENSOR_ATTR_2(temp3_auto_point1_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 0, 1),
+ SENSOR_ATTR_2(temp3_auto_point2_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 1, 1),
+ SENSOR_ATTR_2(temp3_auto_point3_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 2, 1),
+ SENSOR_ATTR_2(temp3_auto_point4_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp, store_pwm_auto_point_temp,
+ 3, 1),
+ SENSOR_ATTR_2(temp3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_temp_hyst,
+ store_pwm_auto_point_temp_hyst,
+ 0, 1),
+ SENSOR_ATTR_2(temp3_auto_point2_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 1, 1),
+ SENSOR_ATTR_2(temp3_auto_point3_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 2, 1),
+ SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO,
+ show_pwm_auto_point_temp_hyst, NULL, 3, 1),
+} };
+
+/* Super I/O functions */
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static int superio_inw(int base, int reg)
+{
+ int val;
+ val = superio_inb(base, reg) << 8;
+ val |= superio_inb(base, reg + 1);
+ return val;
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident */
+ if (!request_muxed_region(base, 2, DRVNAME)) {
+ pr_err("I/O address 0x%04x already in use\n", base);
+ return -EBUSY;
+ }
+
+ /* according to the datasheet the key must be send twice! */
+ outb(SIO_UNLOCK_KEY, base);
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_REG_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+static int f71882fg_create_sysfs_files(struct platform_device *pdev,
+ struct sensor_device_attribute_2 *attr, int count)
+{
+ int err, i;
+
+ for (i = 0; i < count; i++) {
+ err = device_create_file(&pdev->dev, &attr[i].dev_attr);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static void f71882fg_remove_sysfs_files(struct platform_device *pdev,
+ struct sensor_device_attribute_2 *attr, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ device_remove_file(&pdev->dev, &attr[i].dev_attr);
+}
+
+static int f71882fg_create_fan_sysfs_files(
+ struct platform_device *pdev, int idx)
+{
+ struct f71882fg_data *data = platform_get_drvdata(pdev);
+ int err;
+
+ /* Sanity check the pwm setting */
+ err = 0;
+ switch (data->type) {
+ case f71858fg:
+ if (((data->pwm_enable >> (idx * 2)) & 3) == 3)
+ err = 1;
+ break;
+ case f71862fg:
+ if (((data->pwm_enable >> (idx * 2)) & 1) != 1)
+ err = 1;
+ break;
+ case f8000:
+ if (idx == 2)
+ err = data->pwm_enable & 0x20;
+ break;
+ default:
+ break;
+ }
+ if (err) {
+ dev_err(&pdev->dev,
+ "Invalid (reserved) pwm settings: 0x%02x, "
+ "skipping fan %d\n",
+ (data->pwm_enable >> (idx * 2)) & 3, idx + 1);
+ return 0; /* This is a non fatal condition */
+ }
+
+ err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[idx][0],
+ ARRAY_SIZE(fxxxx_fan_attr[0]));
+ if (err)
+ return err;
+
+ if (f71882fg_fan_has_beep[data->type]) {
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_fan_beep_attr[idx],
+ 1);
+ if (err)
+ return err;
+ }
+
+ dev_info(&pdev->dev, "Fan: %d is in %s mode\n", idx + 1,
+ (data->pwm_enable & (1 << (2 * idx))) ? "duty-cycle" : "RPM");
+
+ /* Check for unsupported auto pwm settings */
+ switch (data->type) {
+ case f71808e:
+ case f71808a:
+ case f71869:
+ case f71869a:
+ case f71889fg:
+ case f71889ed:
+ case f71889a:
+ data->pwm_auto_point_mapping[idx] =
+ f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(idx));
+ if ((data->pwm_auto_point_mapping[idx] & 0x80) ||
+ (data->pwm_auto_point_mapping[idx] & 3) == 0) {
+ dev_warn(&pdev->dev,
+ "Auto pwm controlled by raw digital "
+ "data, disabling pwm auto_point "
+ "sysfs attributes for fan %d\n", idx + 1);
+ return 0; /* This is a non fatal condition */
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (data->type) {
+ case f71862fg:
+ err = f71882fg_create_sysfs_files(pdev,
+ &f71862fg_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(f71862fg_auto_pwm_attr[0]));
+ break;
+ case f71808e:
+ case f71869:
+ err = f71882fg_create_sysfs_files(pdev,
+ &f71869_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(f71869_auto_pwm_attr[0]));
+ break;
+ case f8000:
+ err = f71882fg_create_sysfs_files(pdev,
+ &f8000_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(f8000_auto_pwm_attr[0]));
+ break;
+ default:
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_auto_pwm_attr[idx][0],
+ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]));
+ }
+
+ 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;
+ struct f71882fg_sio_data *sio_data = dev_get_platdata(&pdev->dev);
+ int nr_fans = f71882fg_nr_fans[sio_data->type];
+ int nr_temps = f71882fg_nr_temps[sio_data->type];
+ int err, i;
+ int size;
+ u8 start_reg, reg;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct f71882fg_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+ data->type = sio_data->type;
+ data->temp_start =
+ (data->type == f71858fg || data->type == f8000 ||
+ data->type == f81866a) ? 0 : 1;
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ start_reg = f71882fg_read8(data, F71882FG_REG_START);
+ if (start_reg & 0x04) {
+ dev_warn(&pdev->dev, "Hardware monitor is powered down\n");
+ return -ENODEV;
+ }
+ if (!(start_reg & 0x03)) {
+ dev_warn(&pdev->dev, "Hardware monitoring not activated\n");
+ return -ENODEV;
+ }
+
+ /* Register sysfs interface files */
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
+ goto exit_unregister_sysfs;
+
+ if (start_reg & 0x01) {
+ switch (data->type) {
+ case f71858fg:
+ data->temp_config =
+ f71882fg_read8(data, F71882FG_REG_TEMP_CONFIG);
+ if (data->temp_config & 0x10)
+ /*
+ * The f71858fg temperature alarms behave as
+ * the f8000 alarms in this mode
+ */
+ err = f71882fg_create_sysfs_files(pdev,
+ f8000_temp_attr,
+ ARRAY_SIZE(f8000_temp_attr));
+ else
+ err = f71882fg_create_sysfs_files(pdev,
+ f71858fg_temp_attr,
+ ARRAY_SIZE(f71858fg_temp_attr));
+ break;
+ case f8000:
+ err = f71882fg_create_sysfs_files(pdev,
+ f8000_temp_attr,
+ ARRAY_SIZE(f8000_temp_attr));
+ break;
+ case f81866a:
+ err = f71882fg_create_sysfs_files(pdev,
+ f71858fg_temp_attr,
+ ARRAY_SIZE(f71858fg_temp_attr));
+ break;
+ default:
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_temp_attr[0][0],
+ ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps);
+ }
+ if (err)
+ goto exit_unregister_sysfs;
+
+ if (f71882fg_temp_has_beep[data->type]) {
+ if (data->type == f81866a) {
+ size = ARRAY_SIZE(f81866_temp_beep_attr[0]);
+ err = f71882fg_create_sysfs_files(pdev,
+ &f81866_temp_beep_attr[0][0],
+ size * nr_temps);
+
+ } else {
+ size = ARRAY_SIZE(fxxxx_temp_beep_attr[0]);
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_temp_beep_attr[0][0],
+ size * nr_temps);
+ }
+ if (err)
+ goto exit_unregister_sysfs;
+ }
+
+ for (i = 0; i < F71882FG_MAX_INS; i++) {
+ if (f71882fg_has_in[data->type][i]) {
+ err = device_create_file(&pdev->dev,
+ &fxxxx_in_attr[i].dev_attr);
+ if (err)
+ goto exit_unregister_sysfs;
+ }
+ }
+ if (f71882fg_has_in1_alarm[data->type]) {
+ err = f71882fg_create_sysfs_files(pdev,
+ fxxxx_in1_alarm_attr,
+ ARRAY_SIZE(fxxxx_in1_alarm_attr));
+ if (err)
+ goto exit_unregister_sysfs;
+ }
+ }
+
+ if (start_reg & 0x02) {
+ switch (data->type) {
+ case f71808e:
+ case f71808a:
+ case f71869:
+ case f71869a:
+ /* These always have signed auto point temps */
+ data->auto_point_temp_signed = 1;
+ fallthrough; /* to select correct fan/pwm reg bank! */
+ case f71889fg:
+ case f71889ed:
+ case f71889a:
+ reg = f71882fg_read8(data, F71882FG_REG_FAN_FAULT_T);
+ if (reg & F71882FG_FAN_NEG_TEMP_EN)
+ data->auto_point_temp_signed = 1;
+ /* Ensure banked pwm registers point to right bank */
+ reg &= ~F71882FG_FAN_PROG_SEL;
+ f71882fg_write8(data, F71882FG_REG_FAN_FAULT_T, reg);
+ break;
+ default:
+ break;
+ }
+
+ data->pwm_enable =
+ f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
+
+ for (i = 0; i < nr_fans; i++) {
+ err = f71882fg_create_fan_sysfs_files(pdev, i);
+ if (err)
+ goto exit_unregister_sysfs;
+ }
+
+ /* Some types have 1 extra fan with limited functionality */
+ switch (data->type) {
+ case f71808a:
+ err = f71882fg_create_sysfs_files(pdev,
+ f71808a_fan3_attr,
+ ARRAY_SIZE(f71808a_fan3_attr));
+ break;
+ case f8000:
+ err = f71882fg_create_sysfs_files(pdev,
+ f8000_fan_attr,
+ ARRAY_SIZE(f8000_fan_attr));
+ break;
+ default:
+ break;
+ }
+ if (err)
+ goto exit_unregister_sysfs;
+ }
+
+ 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 exit_unregister_sysfs;
+ }
+
+ return 0;
+
+exit_unregister_sysfs:
+ f71882fg_remove(pdev); /* Will unregister the sysfs files for us */
+ return err; /* f71882fg_remove() also frees our data */
+}
+
+static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data)
+{
+ u16 devid;
+ unsigned short address;
+ int err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ devid = superio_inw(sioaddr, SIO_REG_MANID);
+ if (devid != SIO_FINTEK_ID) {
+ pr_debug("Not a Fintek device\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_F71808E_ID:
+ sio_data->type = f71808e;
+ break;
+ case SIO_F71808A_ID:
+ sio_data->type = f71808a;
+ break;
+ case SIO_F71858_ID:
+ case SIO_F71858AD_ID:
+ sio_data->type = f71858fg;
+ break;
+ case SIO_F71862_ID:
+ sio_data->type = f71862fg;
+ break;
+ case SIO_F71868_ID:
+ sio_data->type = f71868a;
+ break;
+ case SIO_F71869_ID:
+ sio_data->type = f71869;
+ break;
+ case SIO_F71869A_ID:
+ sio_data->type = f71869a;
+ break;
+ case SIO_F71882_ID:
+ sio_data->type = f71882fg;
+ break;
+ case SIO_F71889_ID:
+ sio_data->type = f71889fg;
+ break;
+ case SIO_F71889E_ID:
+ sio_data->type = f71889ed;
+ break;
+ case SIO_F71889A_ID:
+ sio_data->type = f71889a;
+ break;
+ case SIO_F8000_ID:
+ sio_data->type = f8000;
+ break;
+ case SIO_F81768D_ID:
+ sio_data->type = f81768d;
+ break;
+ case SIO_F81865_ID:
+ sio_data->type = f81865f;
+ break;
+ case SIO_F81866_ID:
+ case SIO_F81966_ID:
+ sio_data->type = f81866a;
+ break;
+ default:
+ pr_info("Unsupported Fintek device: %04x\n",
+ (unsigned int)devid);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ if (sio_data->type == f71858fg)
+ superio_select(sioaddr, SIO_F71858FG_LD_HWM);
+ else
+ superio_select(sioaddr, SIO_F71882FG_LD_HWM);
+
+ if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
+ pr_warn("Device not activated\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ address = superio_inw(sioaddr, SIO_REG_ADDR);
+ if (address == 0) {
+ pr_warn("Base address not set\n");
+ err = -ENODEV;
+ goto exit;
+ }
+ address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */
+
+ err = address;
+ pr_info("Found %s chip at %#x, revision %d\n",
+ f71882fg_names[sio_data->type], (unsigned int)address,
+ (int)superio_inb(sioaddr, SIO_REG_DEVREV));
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+static int __init f71882fg_device_add(int address,
+ const struct f71882fg_sio_data *sio_data)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + REGION_LENGTH - 1,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ f71882fg_pdev = platform_device_alloc(DRVNAME, address);
+ if (!f71882fg_pdev)
+ return -ENOMEM;
+
+ res.name = f71882fg_pdev->name;
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit_device_put;
+
+ err = platform_device_add_resources(f71882fg_pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add_data(f71882fg_pdev, sio_data,
+ sizeof(struct f71882fg_sio_data));
+ if (err) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(f71882fg_pdev);
+ if (err) {
+ pr_err("Device addition failed\n");
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(f71882fg_pdev);
+
+ 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;
+ int address;
+ struct f71882fg_sio_data sio_data;
+
+ memset(&sio_data, 0, sizeof(sio_data));
+
+ address = f71882fg_find(0x2e, &sio_data);
+ if (address < 0)
+ address = f71882fg_find(0x4e, &sio_data);
+ if (address < 0)
+ return address;
+
+ err = platform_driver_register(&f71882fg_driver);
+ if (err)
+ return err;
+
+ err = f71882fg_device_add(address, &sio_data);
+ if (err)
+ goto exit_driver;
+
+ return 0;
+
+exit_driver:
+ platform_driver_unregister(&f71882fg_driver);
+ return err;
+}
+
+static void __exit f71882fg_exit(void)
+{
+ platform_device_unregister(f71882fg_pdev);
+ platform_driver_unregister(&f71882fg_driver);
+}
+
+MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver");
+MODULE_AUTHOR("Hans Edgington, Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_init(f71882fg_init);
+module_exit(f71882fg_exit);
diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c
new file mode 100644
index 000000000..64fbb8cf6
--- /dev/null
+++ b/drivers/hwmon/f75375s.c
@@ -0,0 +1,908 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * f75375s.c - driver for the Fintek F75375/SP, F75373 and
+ * F75387SG/RG hardware monitoring features
+ * Copyright (C) 2006-2007 Riku Voipio
+ *
+ * Datasheets available at:
+ *
+ * f75375:
+ * http://www.fintek.com.tw/files/productfiles/F75375_V026P.pdf
+ *
+ * f75373:
+ * http://www.fintek.com.tw/files/productfiles/F75373_V025P.pdf
+ *
+ * f75387:
+ * http://www.fintek.com.tw/files/productfiles/F75387_V027P.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/f75375s.h>
+#include <linux/slab.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END };
+
+enum chips { f75373, f75375, f75387 };
+
+/* Fintek F75375 registers */
+#define F75375_REG_CONFIG0 0x0
+#define F75375_REG_CONFIG1 0x1
+#define F75375_REG_CONFIG2 0x2
+#define F75375_REG_CONFIG3 0x3
+#define F75375_REG_ADDR 0x4
+#define F75375_REG_INTR 0x31
+#define F75375_CHIP_ID 0x5A
+#define F75375_REG_VERSION 0x5C
+#define F75375_REG_VENDOR 0x5D
+#define F75375_REG_FAN_TIMER 0x60
+
+#define F75375_REG_VOLT(nr) (0x10 + (nr))
+#define F75375_REG_VOLT_HIGH(nr) (0x20 + (nr) * 2)
+#define F75375_REG_VOLT_LOW(nr) (0x21 + (nr) * 2)
+
+#define F75375_REG_TEMP(nr) (0x14 + (nr))
+#define F75387_REG_TEMP11_LSB(nr) (0x1a + (nr))
+#define F75375_REG_TEMP_HIGH(nr) (0x28 + (nr) * 2)
+#define F75375_REG_TEMP_HYST(nr) (0x29 + (nr) * 2)
+
+#define F75375_REG_FAN(nr) (0x16 + (nr) * 2)
+#define F75375_REG_FAN_MIN(nr) (0x2C + (nr) * 2)
+#define F75375_REG_FAN_FULL(nr) (0x70 + (nr) * 0x10)
+#define F75375_REG_FAN_PWM_DUTY(nr) (0x76 + (nr) * 0x10)
+#define F75375_REG_FAN_PWM_CLOCK(nr) (0x7D + (nr) * 0x10)
+
+#define F75375_REG_FAN_EXP(nr) (0x74 + (nr) * 0x10)
+#define F75375_REG_FAN_B_TEMP(nr, step) ((0xA0 + (nr) * 0x10) + (step))
+#define F75375_REG_FAN_B_SPEED(nr, step) \
+ ((0xA5 + (nr) * 0x10) + (step) * 2)
+
+#define F75375_REG_PWM1_RAISE_DUTY 0x69
+#define F75375_REG_PWM2_RAISE_DUTY 0x6A
+#define F75375_REG_PWM1_DROP_DUTY 0x6B
+#define F75375_REG_PWM2_DROP_DUTY 0x6C
+
+#define F75375_FAN_CTRL_LINEAR(nr) (4 + nr)
+#define F75387_FAN_CTRL_LINEAR(nr) (1 + ((nr) * 4))
+#define FAN_CTRL_MODE(nr) (4 + ((nr) * 2))
+#define F75387_FAN_DUTY_MODE(nr) (2 + ((nr) * 4))
+#define F75387_FAN_MANU_MODE(nr) ((nr) * 4)
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct f75375_data {
+ unsigned short addr;
+ struct device *hwmon_dev;
+
+ const char *name;
+ int kind;
+ struct mutex update_lock; /* protect register access */
+ bool valid;
+ unsigned long last_updated; /* In jiffies */
+ unsigned long last_limits; /* In jiffies */
+
+ /* Register values */
+ u8 in[4];
+ u8 in_max[4];
+ u8 in_min[4];
+ u16 fan[2];
+ u16 fan_min[2];
+ u16 fan_max[2];
+ u16 fan_target[2];
+ u8 fan_timer;
+ u8 pwm[2];
+ u8 pwm_mode[2];
+ u8 pwm_enable[2];
+ /*
+ * f75387: For remote temperature reading, it uses signed 11-bit
+ * values with LSB = 0.125 degree Celsius, left-justified in 16-bit
+ * registers. For original 8-bit temp readings, the LSB just is 0.
+ */
+ s16 temp11[2];
+ s8 temp_high[2];
+ s8 temp_max_hyst[2];
+};
+
+static int f75375_detect(struct i2c_client *client,
+ struct i2c_board_info *info);
+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 },
+ { "f75375", f75375 },
+ { "f75387", f75387 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, f75375_id);
+
+static struct i2c_driver f75375_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "f75375",
+ },
+ .probe_new = f75375_probe,
+ .remove = f75375_remove,
+ .id_table = f75375_id,
+ .detect = f75375_detect,
+ .address_list = normal_i2c,
+};
+
+static inline int f75375_read8(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* in most cases, should be called while holding update_lock */
+static inline u16 f75375_read16(struct i2c_client *client, u8 reg)
+{
+ return (i2c_smbus_read_byte_data(client, reg) << 8)
+ | i2c_smbus_read_byte_data(client, reg + 1);
+}
+
+static inline void f75375_write8(struct i2c_client *client, u8 reg,
+ u8 value)
+{
+ i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline void f75375_write16(struct i2c_client *client, u8 reg,
+ u16 value)
+{
+ int err = i2c_smbus_write_byte_data(client, reg, (value >> 8));
+ if (err)
+ return;
+ i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF));
+}
+
+static void f75375_write_pwm(struct i2c_client *client, int nr)
+{
+ struct f75375_data *data = i2c_get_clientdata(client);
+ if (data->kind == f75387)
+ f75375_write16(client, F75375_REG_FAN_EXP(nr), data->pwm[nr]);
+ else
+ f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr),
+ data->pwm[nr]);
+}
+
+static struct f75375_data *f75375_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ int nr;
+
+ mutex_lock(&data->update_lock);
+
+ /* Limit registers cache is refreshed after 60 seconds */
+ if (time_after(jiffies, data->last_limits + 60 * HZ)
+ || !data->valid) {
+ for (nr = 0; nr < 2; nr++) {
+ data->temp_high[nr] =
+ f75375_read8(client, F75375_REG_TEMP_HIGH(nr));
+ data->temp_max_hyst[nr] =
+ f75375_read8(client, F75375_REG_TEMP_HYST(nr));
+ data->fan_max[nr] =
+ f75375_read16(client, F75375_REG_FAN_FULL(nr));
+ data->fan_min[nr] =
+ f75375_read16(client, F75375_REG_FAN_MIN(nr));
+ data->fan_target[nr] =
+ f75375_read16(client, F75375_REG_FAN_EXP(nr));
+ }
+ for (nr = 0; nr < 4; nr++) {
+ data->in_max[nr] =
+ f75375_read8(client, F75375_REG_VOLT_HIGH(nr));
+ data->in_min[nr] =
+ f75375_read8(client, F75375_REG_VOLT_LOW(nr));
+ }
+ data->fan_timer = f75375_read8(client, F75375_REG_FAN_TIMER);
+ data->last_limits = jiffies;
+ }
+
+ /* Measurement registers cache is refreshed after 2 second */
+ if (time_after(jiffies, data->last_updated + 2 * HZ)
+ || !data->valid) {
+ for (nr = 0; nr < 2; nr++) {
+ data->pwm[nr] = f75375_read8(client,
+ F75375_REG_FAN_PWM_DUTY(nr));
+ /* assign MSB, therefore shift it by 8 bits */
+ data->temp11[nr] =
+ f75375_read8(client, F75375_REG_TEMP(nr)) << 8;
+ if (data->kind == f75387)
+ /* merge F75387's temperature LSB (11-bit) */
+ data->temp11[nr] |=
+ f75375_read8(client,
+ F75387_REG_TEMP11_LSB(nr));
+ data->fan[nr] =
+ f75375_read16(client, F75375_REG_FAN(nr));
+ }
+ for (nr = 0; nr < 4; nr++)
+ data->in[nr] =
+ f75375_read8(client, F75375_REG_VOLT(nr));
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+static inline u16 rpm_from_reg(u16 reg)
+{
+ if (reg == 0 || reg == 0xffff)
+ return 0;
+ return 1500000 / reg;
+}
+
+static inline u16 rpm_to_reg(int rpm)
+{
+ if (rpm < 367 || rpm > 0xffff)
+ return 0xffff;
+ return 1500000 / rpm;
+}
+
+static bool duty_mode_enabled(u8 pwm_enable)
+{
+ switch (pwm_enable) {
+ case 0: /* Manual, duty mode (full speed) */
+ case 1: /* Manual, duty mode */
+ case 4: /* Auto, duty mode */
+ return true;
+ case 2: /* Auto, speed mode */
+ case 3: /* Manual, speed mode */
+ return false;
+ default:
+ WARN(1, "Unexpected pwm_enable value %d\n", pwm_enable);
+ return true;
+ }
+}
+
+static bool auto_mode_enabled(u8 pwm_enable)
+{
+ switch (pwm_enable) {
+ case 0: /* Manual, duty mode (full speed) */
+ case 1: /* Manual, duty mode */
+ case 3: /* Manual, speed mode */
+ return false;
+ case 2: /* Auto, speed mode */
+ case 4: /* Auto, duty mode */
+ return true;
+ default:
+ WARN(1, "Unexpected pwm_enable value %d\n", pwm_enable);
+ return false;
+ }
+}
+
+static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->fan_min[nr] = rpm_to_reg(val);
+ f75375_write16(client, F75375_REG_FAN_MIN(nr), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t set_fan_target(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (auto_mode_enabled(data->pwm_enable[nr]))
+ return -EINVAL;
+ if (data->kind == f75387 && duty_mode_enabled(data->pwm_enable[nr]))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->fan_target[nr] = rpm_to_reg(val);
+ f75375_write16(client, F75375_REG_FAN_EXP(nr), data->fan_target[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (auto_mode_enabled(data->pwm_enable[nr]) ||
+ !duty_mode_enabled(data->pwm_enable[nr]))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = clamp_val(val, 0, 255);
+ f75375_write_pwm(client, nr);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
+ *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm_enable[nr]);
+}
+
+static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val)
+{
+ struct f75375_data *data = i2c_get_clientdata(client);
+ u8 fanmode;
+
+ if (val < 0 || val > 4)
+ return -EINVAL;
+
+ fanmode = f75375_read8(client, F75375_REG_FAN_TIMER);
+ if (data->kind == f75387) {
+ /* For now, deny dangerous toggling of duty mode */
+ if (duty_mode_enabled(data->pwm_enable[nr]) !=
+ duty_mode_enabled(val))
+ return -EOPNOTSUPP;
+ /* clear each fanX_mode bit before setting them properly */
+ fanmode &= ~(1 << F75387_FAN_DUTY_MODE(nr));
+ fanmode &= ~(1 << F75387_FAN_MANU_MODE(nr));
+ switch (val) {
+ case 0: /* full speed */
+ fanmode |= (1 << F75387_FAN_MANU_MODE(nr));
+ fanmode |= (1 << F75387_FAN_DUTY_MODE(nr));
+ data->pwm[nr] = 255;
+ break;
+ case 1: /* PWM */
+ fanmode |= (1 << F75387_FAN_MANU_MODE(nr));
+ fanmode |= (1 << F75387_FAN_DUTY_MODE(nr));
+ break;
+ case 2: /* Automatic, speed mode */
+ break;
+ case 3: /* fan speed */
+ fanmode |= (1 << F75387_FAN_MANU_MODE(nr));
+ break;
+ case 4: /* Automatic, pwm */
+ fanmode |= (1 << F75387_FAN_DUTY_MODE(nr));
+ break;
+ }
+ } else {
+ /* clear each fanX_mode bit before setting them properly */
+ fanmode &= ~(3 << FAN_CTRL_MODE(nr));
+ switch (val) {
+ case 0: /* full speed */
+ fanmode |= (3 << FAN_CTRL_MODE(nr));
+ data->pwm[nr] = 255;
+ break;
+ case 1: /* PWM */
+ fanmode |= (3 << FAN_CTRL_MODE(nr));
+ break;
+ case 2: /* AUTOMATIC*/
+ fanmode |= (1 << FAN_CTRL_MODE(nr));
+ break;
+ case 3: /* fan speed */
+ break;
+ case 4: /* Automatic pwm */
+ return -EINVAL;
+ }
+ }
+
+ f75375_write8(client, F75375_REG_FAN_TIMER, fanmode);
+ data->pwm_enable[nr] = val;
+ if (val == 0)
+ f75375_write_pwm(client, nr);
+ return 0;
+}
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ err = set_pwm_enable_direct(client, nr, val);
+ mutex_unlock(&data->update_lock);
+ return err ? err : count;
+}
+
+static ssize_t set_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+ u8 conf;
+ char reg, ctrl;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (!(val == 0 || val == 1))
+ return -EINVAL;
+
+ /* F75373 does not support DC (linear voltage) fan control mode */
+ if (data->kind == f75373 && val == 0)
+ return -EINVAL;
+
+ /* take care for different registers */
+ if (data->kind == f75387) {
+ reg = F75375_REG_FAN_TIMER;
+ ctrl = F75387_FAN_CTRL_LINEAR(nr);
+ } else {
+ reg = F75375_REG_CONFIG1;
+ ctrl = F75375_FAN_CTRL_LINEAR(nr);
+ }
+
+ mutex_lock(&data->update_lock);
+ conf = f75375_read8(client, reg);
+ conf &= ~(1 << ctrl);
+
+ if (val == 0)
+ conf |= (1 << ctrl);
+
+ f75375_write8(client, reg, conf);
+ data->pwm_mode[nr] = val;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute
+ *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm[nr]);
+}
+
+static ssize_t show_pwm_mode(struct device *dev, struct device_attribute
+ *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm_mode[nr]);
+}
+
+#define VOLT_FROM_REG(val) ((val) * 8)
+#define VOLT_TO_REG(val) ((val) / 8)
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in[nr]));
+}
+
+static ssize_t show_in_max(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in_max[nr]));
+}
+
+static ssize_t show_in_min(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in_min[nr]));
+}
+
+static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(VOLT_TO_REG(val), 0, 0xff);
+ mutex_lock(&data->update_lock);
+ data->in_max[nr] = val;
+ f75375_write8(client, F75375_REG_VOLT_HIGH(nr), data->in_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(VOLT_TO_REG(val), 0, 0xff);
+ mutex_lock(&data->update_lock);
+ data->in_min[nr] = val;
+ f75375_write8(client, F75375_REG_VOLT_LOW(nr), data->in_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+#define TEMP_FROM_REG(val) ((val) * 1000)
+#define TEMP_TO_REG(val) ((val) / 1000)
+#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
+
+static ssize_t show_temp11(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[nr]));
+}
+
+static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr]));
+}
+
+static ssize_t show_temp_max_hyst(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct f75375_data *data = f75375_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[nr]));
+}
+
+static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(TEMP_TO_REG(val), 0, 127);
+ mutex_lock(&data->update_lock);
+ data->temp_high[nr] = val;
+ f75375_write8(client, F75375_REG_TEMP_HIGH(nr), data->temp_high[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t set_temp_max_hyst(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct f75375_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(TEMP_TO_REG(val), 0, 127);
+ mutex_lock(&data->update_lock);
+ data->temp_max_hyst[nr] = val;
+ f75375_write8(client, F75375_REG_TEMP_HYST(nr),
+ data->temp_max_hyst[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+#define show_fan(thing) \
+static ssize_t show_##thing(struct device *dev, struct device_attribute *attr, \
+ char *buf)\
+{\
+ int nr = to_sensor_dev_attr(attr)->index;\
+ struct f75375_data *data = f75375_update_device(dev); \
+ return sprintf(buf, "%d\n", rpm_from_reg(data->thing[nr])); \
+}
+
+show_fan(fan);
+show_fan(fan_min);
+show_fan(fan_max);
+show_fan(fan_target);
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO|S_IWUSR,
+ show_in_max, set_in_max, 0);
+static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO|S_IWUSR,
+ show_in_min, set_in_min, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO|S_IWUSR,
+ show_in_max, set_in_max, 1);
+static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO|S_IWUSR,
+ show_in_min, set_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO|S_IWUSR,
+ show_in_max, set_in_max, 2);
+static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO|S_IWUSR,
+ show_in_min, set_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO|S_IWUSR,
+ show_in_max, set_in_max, 3);
+static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO|S_IWUSR,
+ show_in_min, set_in_min, 3);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR,
+ show_temp_max_hyst, set_temp_max_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO|S_IWUSR,
+ show_temp_max, set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR,
+ show_temp_max_hyst, set_temp_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO|S_IWUSR,
+ show_temp_max, set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO, show_fan_max, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO|S_IWUSR,
+ show_fan_min, set_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan1_target, S_IRUGO|S_IWUSR,
+ show_fan_target, set_fan_target, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_max, S_IRUGO, show_fan_max, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO|S_IWUSR,
+ show_fan_min, set_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan2_target, S_IRUGO|S_IWUSR,
+ show_fan_target, set_fan_target, 1);
+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO|S_IWUSR,
+ show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO|S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO,
+ show_pwm_mode, set_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR,
+ show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO|S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IRUGO,
+ show_pwm_mode, set_pwm_mode, 1);
+
+static struct attribute *f75375_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_max.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_target.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_max.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_target.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group f75375_group = {
+ .attrs = f75375_attributes,
+};
+
+static void f75375_init(struct i2c_client *client, struct f75375_data *data,
+ struct f75375s_platform_data *f75375s_pdata)
+{
+ int nr;
+
+ if (!f75375s_pdata) {
+ u8 conf, mode;
+ int nr;
+
+ conf = f75375_read8(client, F75375_REG_CONFIG1);
+ mode = f75375_read8(client, F75375_REG_FAN_TIMER);
+ for (nr = 0; nr < 2; nr++) {
+ if (data->kind == f75387) {
+ bool manu, duty;
+
+ if (!(mode & (1 << F75387_FAN_CTRL_LINEAR(nr))))
+ data->pwm_mode[nr] = 1;
+
+ manu = ((mode >> F75387_FAN_MANU_MODE(nr)) & 1);
+ duty = ((mode >> F75387_FAN_DUTY_MODE(nr)) & 1);
+ if (!manu && duty)
+ /* auto, pwm */
+ data->pwm_enable[nr] = 4;
+ else if (manu && !duty)
+ /* manual, speed */
+ data->pwm_enable[nr] = 3;
+ else if (!manu && !duty)
+ /* automatic, speed */
+ data->pwm_enable[nr] = 2;
+ else
+ /* manual, pwm */
+ data->pwm_enable[nr] = 1;
+ } else {
+ if (!(conf & (1 << F75375_FAN_CTRL_LINEAR(nr))))
+ data->pwm_mode[nr] = 1;
+
+ switch ((mode >> FAN_CTRL_MODE(nr)) & 3) {
+ case 0: /* speed */
+ data->pwm_enable[nr] = 3;
+ break;
+ case 1: /* automatic */
+ data->pwm_enable[nr] = 2;
+ break;
+ default: /* manual */
+ data->pwm_enable[nr] = 1;
+ break;
+ }
+ }
+ }
+ return;
+ }
+
+ set_pwm_enable_direct(client, 0, f75375s_pdata->pwm_enable[0]);
+ set_pwm_enable_direct(client, 1, f75375s_pdata->pwm_enable[1]);
+ for (nr = 0; nr < 2; nr++) {
+ if (auto_mode_enabled(f75375s_pdata->pwm_enable[nr]) ||
+ !duty_mode_enabled(f75375s_pdata->pwm_enable[nr]))
+ continue;
+ data->pwm[nr] = clamp_val(f75375s_pdata->pwm[nr], 0, 255);
+ f75375_write_pwm(client, nr);
+ }
+
+}
+
+static int f75375_probe(struct i2c_client *client)
+{
+ struct f75375_data *data;
+ struct f75375s_platform_data *f75375s_pdata =
+ dev_get_platdata(&client->dev);
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+ data = devm_kzalloc(&client->dev, sizeof(struct f75375_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ data->kind = i2c_match_id(f75375_id, client)->driver_data;
+
+ err = sysfs_create_group(&client->dev.kobj, &f75375_group);
+ if (err)
+ return err;
+
+ if (data->kind != f75373) {
+ err = sysfs_chmod_file(&client->dev.kobj,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ S_IRUGO | S_IWUSR);
+ if (err)
+ goto exit_remove;
+ err = sysfs_chmod_file(&client->dev.kobj,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ S_IRUGO | S_IWUSR);
+ if (err)
+ goto exit_remove;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ f75375_init(client, data, f75375s_pdata);
+
+ return 0;
+
+exit_remove:
+ sysfs_remove_group(&client->dev.kobj, &f75375_group);
+ return err;
+}
+
+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 if detection is successful, -ENODEV otherwise */
+static int f75375_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u16 vendid, chipid;
+ u8 version;
+ const char *name;
+
+ vendid = f75375_read16(client, F75375_REG_VENDOR);
+ chipid = f75375_read16(client, F75375_CHIP_ID);
+ if (vendid != 0x1934)
+ return -ENODEV;
+
+ if (chipid == 0x0306)
+ name = "f75375";
+ else if (chipid == 0x0204)
+ name = "f75373";
+ else if (chipid == 0x0410)
+ name = "f75387";
+ else
+ return -ENODEV;
+
+ version = f75375_read8(client, F75375_REG_VERSION);
+ dev_info(&adapter->dev, "found %s version: %02X\n", name, version);
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+module_i2c_driver(f75375_driver);
+
+MODULE_AUTHOR("Riku Voipio");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("F75373/F75375/F75387 hardware monitoring driver");
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
new file mode 100644
index 000000000..521534d5c
--- /dev/null
+++ b/drivers/hwmon/fam15h_power.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * fam15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011-2016 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <herrmann.der.user@googlemail.com>
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <asm/processor.h>
+#include <asm/msr.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP 0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP 0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE 0xe0
+#define REG_TDP_LIMIT3 0xe8
+
+#define FAM15H_MIN_NUM_ATTRS 2
+#define FAM15H_NUM_GROUPS 2
+#define MAX_CUS 8
+
+/* set maximum interval as 1 second */
+#define MAX_INTERVAL 1000
+
+#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4
+
+struct fam15h_power_data {
+ struct pci_dev *pdev;
+ unsigned int tdp_to_watts;
+ unsigned int base_tdp;
+ unsigned int processor_pwr_watts;
+ unsigned int cpu_pwr_sample_ratio;
+ const struct attribute_group *groups[FAM15H_NUM_GROUPS];
+ struct attribute_group group;
+ /* maximum accumulated power of a compute unit */
+ u64 max_cu_acc_power;
+ /* accumulated power of the compute units */
+ u64 cu_acc_power[MAX_CUS];
+ /* performance timestamp counter */
+ u64 cpu_sw_pwr_ptsc[MAX_CUS];
+ /* online/offline status of current compute unit */
+ int cu_on[MAX_CUS];
+ unsigned long power_period;
+};
+
+static bool is_carrizo_or_later(void)
+{
+ return boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60;
+}
+
+static ssize_t power1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 val, tdp_limit, running_avg_range;
+ s32 running_avg_capture;
+ u64 curr_pwr_watts;
+ struct fam15h_power_data *data = dev_get_drvdata(dev);
+ struct pci_dev *f4 = data->pdev;
+
+ pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+ REG_TDP_RUNNING_AVERAGE, &val);
+
+ /*
+ * On Carrizo and later platforms, TdpRunAvgAccCap bit field
+ * is extended to 4:31 from 4:25.
+ */
+ if (is_carrizo_or_later()) {
+ running_avg_capture = val >> 4;
+ running_avg_capture = sign_extend32(running_avg_capture, 27);
+ } else {
+ running_avg_capture = (val >> 4) & 0x3fffff;
+ running_avg_capture = sign_extend32(running_avg_capture, 21);
+ }
+
+ running_avg_range = (val & 0xf) + 1;
+
+ pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+ REG_TDP_LIMIT3, &val);
+
+ /*
+ * On Carrizo and later platforms, ApmTdpLimit bit field
+ * is extended to 16:31 from 16:28.
+ */
+ if (is_carrizo_or_later())
+ tdp_limit = val >> 16;
+ else
+ tdp_limit = (val >> 16) & 0x1fff;
+
+ curr_pwr_watts = ((u64)(tdp_limit +
+ data->base_tdp)) << running_avg_range;
+ curr_pwr_watts -= running_avg_capture;
+ curr_pwr_watts *= data->tdp_to_watts;
+
+ /*
+ * Convert to microWatt
+ *
+ * power is in Watt provided as fixed point integer with
+ * scaling factor 1/(2^16). For conversion we use
+ * (10^6)/(2^16) = 15625/(2^10)
+ */
+ curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range);
+ return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
+}
+static DEVICE_ATTR_RO(power1_input);
+
+static ssize_t power1_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->processor_pwr_watts);
+}
+static DEVICE_ATTR_RO(power1_crit);
+
+static void do_read_registers_on_cu(void *_data)
+{
+ struct fam15h_power_data *data = _data;
+ int cpu, cu;
+
+ cpu = smp_processor_id();
+
+ /*
+ * With the new x86 topology modelling, cpu core id actually
+ * is compute unit id.
+ */
+ cu = cpu_data(cpu).cpu_core_id;
+
+ rdmsrl_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]);
+ rdmsrl_safe(MSR_F15H_PTSC, &data->cpu_sw_pwr_ptsc[cu]);
+
+ data->cu_on[cu] = 1;
+}
+
+/*
+ * This function is only able to be called when CPUID
+ * Fn8000_0007:EDX[12] is set.
+ */
+static int read_registers(struct fam15h_power_data *data)
+{
+ int core, this_core;
+ cpumask_var_t mask;
+ int ret, cpu;
+
+ ret = zalloc_cpumask_var(&mask, GFP_KERNEL);
+ if (!ret)
+ return -ENOMEM;
+
+ memset(data->cu_on, 0, sizeof(int) * MAX_CUS);
+
+ cpus_read_lock();
+
+ /*
+ * Choose the first online core of each compute unit, and then
+ * read their MSR value of power and ptsc in a single IPI,
+ * because the MSR value of CPU core represent the compute
+ * unit's.
+ */
+ core = -1;
+
+ for_each_online_cpu(cpu) {
+ this_core = topology_core_id(cpu);
+
+ if (this_core == core)
+ continue;
+
+ core = this_core;
+
+ /* get any CPU on this compute unit */
+ cpumask_set_cpu(cpumask_any(topology_sibling_cpumask(cpu)), mask);
+ }
+
+ on_each_cpu_mask(mask, do_read_registers_on_cu, data, true);
+
+ cpus_read_unlock();
+ free_cpumask_var(mask);
+
+ return 0;
+}
+
+static ssize_t power1_average_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fam15h_power_data *data = dev_get_drvdata(dev);
+ u64 prev_cu_acc_power[MAX_CUS], prev_ptsc[MAX_CUS],
+ jdelta[MAX_CUS];
+ u64 tdelta, avg_acc;
+ int cu, cu_num, ret;
+ signed long leftover;
+
+ /*
+ * With the new x86 topology modelling, x86_max_cores is the
+ * compute unit number.
+ */
+ cu_num = boot_cpu_data.x86_max_cores;
+
+ ret = read_registers(data);
+ if (ret)
+ return 0;
+
+ for (cu = 0; cu < cu_num; cu++) {
+ prev_cu_acc_power[cu] = data->cu_acc_power[cu];
+ prev_ptsc[cu] = data->cpu_sw_pwr_ptsc[cu];
+ }
+
+ leftover = schedule_timeout_interruptible(msecs_to_jiffies(data->power_period));
+ if (leftover)
+ return 0;
+
+ ret = read_registers(data);
+ if (ret)
+ return 0;
+
+ for (cu = 0, avg_acc = 0; cu < cu_num; cu++) {
+ /* check if current compute unit is online */
+ if (data->cu_on[cu] == 0)
+ continue;
+
+ if (data->cu_acc_power[cu] < prev_cu_acc_power[cu]) {
+ jdelta[cu] = data->max_cu_acc_power + data->cu_acc_power[cu];
+ jdelta[cu] -= prev_cu_acc_power[cu];
+ } else {
+ jdelta[cu] = data->cu_acc_power[cu] - prev_cu_acc_power[cu];
+ }
+ tdelta = data->cpu_sw_pwr_ptsc[cu] - prev_ptsc[cu];
+ jdelta[cu] *= data->cpu_pwr_sample_ratio * 1000;
+ do_div(jdelta[cu], tdelta);
+
+ /* the unit is microWatt */
+ avg_acc += jdelta[cu];
+ }
+
+ return sprintf(buf, "%llu\n", (unsigned long long)avg_acc);
+}
+static DEVICE_ATTR_RO(power1_average);
+
+static ssize_t power1_average_interval_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lu\n", data->power_period);
+}
+
+static ssize_t power1_average_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fam15h_power_data *data = dev_get_drvdata(dev);
+ unsigned long temp;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &temp);
+ if (ret)
+ return ret;
+
+ if (temp > MAX_INTERVAL)
+ return -EINVAL;
+
+ /* the interval value should be greater than 0 */
+ if (temp <= 0)
+ return -EINVAL;
+
+ data->power_period = temp;
+
+ return count;
+}
+static DEVICE_ATTR_RW(power1_average_interval);
+
+static int fam15h_power_init_attrs(struct pci_dev *pdev,
+ struct fam15h_power_data *data)
+{
+ int n = FAM15H_MIN_NUM_ATTRS;
+ struct attribute **fam15h_power_attrs;
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+
+ if (c->x86 == 0x15 &&
+ (c->x86_model <= 0xf ||
+ (c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
+ n += 1;
+
+ /* check if processor supports accumulated power */
+ if (boot_cpu_has(X86_FEATURE_ACC_POWER))
+ n += 2;
+
+ fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
+ sizeof(*fam15h_power_attrs),
+ GFP_KERNEL);
+
+ if (!fam15h_power_attrs)
+ return -ENOMEM;
+
+ n = 0;
+ fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr;
+ if (c->x86 == 0x15 &&
+ (c->x86_model <= 0xf ||
+ (c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
+ fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
+
+ if (boot_cpu_has(X86_FEATURE_ACC_POWER)) {
+ fam15h_power_attrs[n++] = &dev_attr_power1_average.attr;
+ fam15h_power_attrs[n++] = &dev_attr_power1_average_interval.attr;
+ }
+
+ data->group.attrs = fam15h_power_attrs;
+
+ return 0;
+}
+
+static bool should_load_on_this_node(struct pci_dev *f4)
+{
+ u32 val;
+
+ pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+ REG_NORTHBRIDGE_CAP, &val);
+ if ((val & BIT(29)) && ((val >> 30) & 3))
+ return false;
+
+ return true;
+}
+
+/*
+ * Newer BKDG versions have an updated recommendation on how to properly
+ * initialize the running average range (was: 0xE, now: 0x9). This avoids
+ * counter saturations resulting in bogus power readings.
+ * We correct this value ourselves to cope with older BIOSes.
+ */
+static const struct pci_device_id affected_device[] = {
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+ { 0 }
+};
+
+static void tweak_runavg_range(struct pci_dev *pdev)
+{
+ u32 val;
+
+ /*
+ * let this quirk apply only to the current version of the
+ * northbridge, since future versions may change the behavior
+ */
+ if (!pci_match_id(affected_device, pdev))
+ return;
+
+ pci_bus_read_config_dword(pdev->bus,
+ PCI_DEVFN(PCI_SLOT(pdev->devfn), 5),
+ REG_TDP_RUNNING_AVERAGE, &val);
+ if ((val & 0xf) != 0xe)
+ return;
+
+ val &= ~0xf;
+ val |= 0x9;
+ pci_bus_write_config_dword(pdev->bus,
+ PCI_DEVFN(PCI_SLOT(pdev->devfn), 5),
+ REG_TDP_RUNNING_AVERAGE, val);
+}
+
+#ifdef CONFIG_PM
+static int fam15h_power_resume(struct pci_dev *pdev)
+{
+ tweak_runavg_range(pdev);
+ return 0;
+}
+#else
+#define fam15h_power_resume NULL
+#endif
+
+static int fam15h_power_init_data(struct pci_dev *f4,
+ struct fam15h_power_data *data)
+{
+ u32 val;
+ u64 tmp;
+ int ret;
+
+ pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+ data->base_tdp = val >> 16;
+ tmp = val & 0xffff;
+
+ pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+ REG_TDP_LIMIT3, &val);
+
+ data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+ tmp *= data->tdp_to_watts;
+
+ /* result not allowed to be >= 256W */
+ if ((tmp >> 16) >= 256)
+ dev_warn(&f4->dev,
+ "Bogus value for ProcessorPwrWatts (processor_pwr_watts>=%u)\n",
+ (unsigned int) (tmp >> 16));
+
+ /* convert to microWatt */
+ data->processor_pwr_watts = (tmp * 15625) >> 10;
+
+ ret = fam15h_power_init_attrs(f4, data);
+ if (ret)
+ return ret;
+
+
+ /* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
+ if (!boot_cpu_has(X86_FEATURE_ACC_POWER))
+ return 0;
+
+ /*
+ * determine the ratio of the compute unit power accumulator
+ * sample period to the PTSC counter period by executing CPUID
+ * Fn8000_0007:ECX
+ */
+ data->cpu_pwr_sample_ratio = cpuid_ecx(0x80000007);
+
+ if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) {
+ pr_err("Failed to read max compute unit power accumulator MSR\n");
+ return -ENODEV;
+ }
+
+ data->max_cu_acc_power = tmp;
+
+ /*
+ * Milliseconds are a reasonable interval for the measurement.
+ * But it shouldn't set too long here, because several seconds
+ * would cause the read function to hang. So set default
+ * interval as 10 ms.
+ */
+ data->power_period = 10;
+
+ return read_registers(data);
+}
+
+static int fam15h_power_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct fam15h_power_data *data;
+ struct device *dev = &pdev->dev;
+ struct device *hwmon_dev;
+ int ret;
+
+ /*
+ * though we ignore every other northbridge, we still have to
+ * do the tweaking on _each_ node in MCM processors as the counters
+ * are working hand-in-hand
+ */
+ tweak_runavg_range(pdev);
+
+ if (!should_load_on_this_node(pdev))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct fam15h_power_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = fam15h_power_init_data(pdev, data);
+ if (ret)
+ return ret;
+
+ data->pdev = pdev;
+
+ data->groups[0] = &data->group;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power",
+ data,
+ &data->groups[0]);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct pci_device_id fam15h_power_id_table[] = {
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F4) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
+
+static struct pci_driver fam15h_power_driver = {
+ .name = "fam15h_power",
+ .id_table = fam15h_power_id_table,
+ .probe = fam15h_power_probe,
+ .resume = fam15h_power_resume,
+};
+
+module_pci_driver(fam15h_power_driver);
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
new file mode 100644
index 000000000..0a77d6161
--- /dev/null
+++ b/drivers/hwmon/fschmd.c
@@ -0,0 +1,1370 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * fschmd.c
+ *
+ * Copyright (C) 2007 - 2009 Hans de Goede <hdegoede@redhat.com>
+ */
+
+/*
+ * Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes,
+ * Scylla, Heracles, Heimdall, Hades and Syleus chips
+ *
+ * Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6
+ * (candidate) fschmd drivers:
+ * Copyright (C) 2006 Thilo Cestonaro
+ * <thilo.cestonaro.external@fujitsu-siemens.com>
+ * Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch>
+ * Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de>
+ * Copyright (c) 2001 Martin Knoblauch <mkn@teraport.de, knobi@knobisoft.de>
+ * Copyright (C) 2000 Hermann Jung <hej@odn.de>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/dmi.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kref.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
+
+/* Insmod parameters */
+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) ")");
+
+enum chips { fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl };
+
+/*
+ * The FSCHMD registers and other defines
+ */
+
+/* chip identification */
+#define FSCHMD_REG_IDENT_0 0x00
+#define FSCHMD_REG_IDENT_1 0x01
+#define FSCHMD_REG_IDENT_2 0x02
+#define FSCHMD_REG_REVISION 0x03
+
+/* global control and status */
+#define FSCHMD_REG_EVENT_STATE 0x04
+#define FSCHMD_REG_CONTROL 0x05
+
+#define FSCHMD_CONTROL_ALERT_LED 0x01
+
+/* watchdog */
+static const u8 FSCHMD_REG_WDOG_CONTROL[7] = {
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x28, 0x28 };
+static const u8 FSCHMD_REG_WDOG_STATE[7] = {
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x29, 0x29 };
+static const u8 FSCHMD_REG_WDOG_PRESET[7] = {
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x2a };
+
+#define FSCHMD_WDOG_CONTROL_TRIGGER 0x10
+#define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */
+#define FSCHMD_WDOG_CONTROL_STOP 0x20
+#define FSCHMD_WDOG_CONTROL_RESOLUTION 0x40
+
+#define FSCHMD_WDOG_STATE_CARDRESET 0x02
+
+/* voltages, weird order is to keep the same order as the old drivers */
+static const u8 FSCHMD_REG_VOLT[7][6] = {
+ { 0x45, 0x42, 0x48 }, /* pos */
+ { 0x45, 0x42, 0x48 }, /* her */
+ { 0x45, 0x42, 0x48 }, /* scy */
+ { 0x45, 0x42, 0x48 }, /* hrc */
+ { 0x45, 0x42, 0x48 }, /* hmd */
+ { 0x21, 0x20, 0x22 }, /* hds */
+ { 0x21, 0x20, 0x22, 0x23, 0x24, 0x25 }, /* syl */
+};
+
+static const int FSCHMD_NO_VOLT_SENSORS[7] = { 3, 3, 3, 3, 3, 3, 6 };
+
+/*
+ * minimum pwm at which the fan is driven (pwm can be increased depending on
+ * the temp. Notice that for the scy some fans share there minimum speed.
+ * Also notice that with the scy the sensor order is different than with the
+ * other chips, this order was in the 2.4 driver and kept for consistency.
+ */
+static const u8 FSCHMD_REG_FAN_MIN[7][7] = {
+ { 0x55, 0x65 }, /* pos */
+ { 0x55, 0x65, 0xb5 }, /* her */
+ { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */
+ { 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */
+ { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hmd */
+ { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hds */
+ { 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb4 }, /* syl */
+};
+
+/* actual fan speed */
+static const u8 FSCHMD_REG_FAN_ACT[7][7] = {
+ { 0x0e, 0x6b, 0xab }, /* pos */
+ { 0x0e, 0x6b, 0xbb }, /* her */
+ { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */
+ { 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */
+ { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hmd */
+ { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hds */
+ { 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7 }, /* syl */
+};
+
+/* fan status registers */
+static const u8 FSCHMD_REG_FAN_STATE[7][7] = {
+ { 0x0d, 0x62, 0xa2 }, /* pos */
+ { 0x0d, 0x62, 0xb2 }, /* her */
+ { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */
+ { 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */
+ { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hmd */
+ { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hds */
+ { 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }, /* syl */
+};
+
+/* fan ripple / divider registers */
+static const u8 FSCHMD_REG_FAN_RIPPLE[7][7] = {
+ { 0x0f, 0x6f, 0xaf }, /* pos */
+ { 0x0f, 0x6f, 0xbf }, /* her */
+ { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */
+ { 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */
+ { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hmd */
+ { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hds */
+ { 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6 }, /* syl */
+};
+
+static const int FSCHMD_NO_FAN_SENSORS[7] = { 3, 3, 6, 4, 5, 5, 7 };
+
+/* Fan status register bitmasks */
+#define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */
+#define FSCHMD_FAN_NOT_PRESENT 0x08
+#define FSCHMD_FAN_DISABLED 0x80
+
+
+/* actual temperature registers */
+static const u8 FSCHMD_REG_TEMP_ACT[7][11] = {
+ { 0x64, 0x32, 0x35 }, /* pos */
+ { 0x64, 0x32, 0x35 }, /* her */
+ { 0x64, 0xD0, 0x32, 0x35 }, /* scy */
+ { 0x64, 0x32, 0x35 }, /* hrc */
+ { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hmd */
+ { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hds */
+ { 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, /* syl */
+ 0xb8, 0xc8, 0xd8, 0xe8, 0xf8 },
+};
+
+/* temperature state registers */
+static const u8 FSCHMD_REG_TEMP_STATE[7][11] = {
+ { 0x71, 0x81, 0x91 }, /* pos */
+ { 0x71, 0x81, 0x91 }, /* her */
+ { 0x71, 0xd1, 0x81, 0x91 }, /* scy */
+ { 0x71, 0x81, 0x91 }, /* hrc */
+ { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hmd */
+ { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hds */
+ { 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, /* syl */
+ 0xb9, 0xc9, 0xd9, 0xe9, 0xf9 },
+};
+
+/*
+ * temperature high limit registers, FSC does not document these. Proven to be
+ * there with field testing on the fscher and fschrc, already supported / used
+ * in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers
+ * at these addresses, but doesn't want to confirm they are the same as with
+ * the fscher??
+ */
+static const u8 FSCHMD_REG_TEMP_LIMIT[7][11] = {
+ { 0, 0, 0 }, /* pos */
+ { 0x76, 0x86, 0x96 }, /* her */
+ { 0x76, 0xd6, 0x86, 0x96 }, /* scy */
+ { 0x76, 0x86, 0x96 }, /* hrc */
+ { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hmd */
+ { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hds */
+ { 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, /* syl */
+ 0xba, 0xca, 0xda, 0xea, 0xfa },
+};
+
+/*
+ * These were found through experimenting with an fscher, currently they are
+ * not used, but we keep them around for future reference.
+ * On the fscsyl AUTOP1 lives at 0x#c (so 0x5c for fan1, 0x6c for fan2, etc),
+ * AUTOP2 lives at 0x#e, and 0x#1 is a bitmask defining which temps influence
+ * the fan speed.
+ * static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 };
+ * static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 };
+ */
+
+static const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 };
+
+/* temp status register bitmasks */
+#define FSCHMD_TEMP_WORKING 0x01
+#define FSCHMD_TEMP_ALERT 0x02
+#define FSCHMD_TEMP_DISABLED 0x80
+/* there only really is an alarm if the sensor is working and alert == 1 */
+#define FSCHMD_TEMP_ALARM_MASK \
+ (FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT)
+
+/*
+ * Functions declarations
+ */
+
+static int fschmd_probe(struct i2c_client *client);
+static int fschmd_detect(struct i2c_client *client,
+ struct i2c_board_info *info);
+static void fschmd_remove(struct i2c_client *client);
+static struct fschmd_data *fschmd_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id fschmd_id[] = {
+ { "fscpos", fscpos },
+ { "fscher", fscher },
+ { "fscscy", fscscy },
+ { "fschrc", fschrc },
+ { "fschmd", fschmd },
+ { "fschds", fschds },
+ { "fscsyl", fscsyl },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, fschmd_id);
+
+static struct i2c_driver fschmd_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "fschmd",
+ },
+ .probe_new = fschmd_probe,
+ .remove = fschmd_remove,
+ .id_table = fschmd_id,
+ .detect = fschmd_detect,
+ .address_list = normal_i2c,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct fschmd_data {
+ struct i2c_client *client;
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ struct mutex watchdog_lock;
+ struct list_head list; /* member of the watchdog_data_list */
+ struct kref kref;
+ struct miscdevice watchdog_miscdev;
+ enum chips kind;
+ unsigned long watchdog_is_open;
+ char watchdog_expect_close;
+ char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* register values */
+ u8 revision; /* chip revision */
+ u8 global_control; /* global control register */
+ u8 watchdog_control; /* watchdog control register */
+ u8 watchdog_state; /* watchdog status register */
+ u8 watchdog_preset; /* watchdog counter preset on trigger val */
+ u8 volt[6]; /* voltage */
+ u8 temp_act[11]; /* temperature */
+ u8 temp_status[11]; /* status of sensor */
+ u8 temp_max[11]; /* high temp limit, notice: undocumented! */
+ u8 fan_act[7]; /* fans revolutions per second */
+ u8 fan_status[7]; /* fan status */
+ u8 fan_min[7]; /* fan min value for rps */
+ u8 fan_ripple[7]; /* divider for rps */
+};
+
+/*
+ * Global variables to hold information read from special DMI tables, which are
+ * available on FSC machines with an fscher or later chip. There is no need to
+ * protect these with a lock as they are only modified from our attach function
+ * which always gets called with the i2c-core lock held and never accessed
+ * before the attach function is done with them.
+ */
+static int dmi_mult[6] = { 490, 200, 100, 100, 200, 100 };
+static int dmi_offset[6] = { 0, 0, 0, 0, 0, 0 };
+static int dmi_vref = -1;
+
+/*
+ * Somewhat ugly :( global data pointer list with all fschmd devices, so that
+ * we can find our device data as when using misc_register there is no other
+ * method to get to ones device data from the open fop.
+ */
+static LIST_HEAD(watchdog_data_list);
+/* Note this lock not only protect list access, but also data.kref access */
+static DEFINE_MUTEX(watchdog_data_mutex);
+
+/*
+ * Release our data struct when we're detached from the i2c client *and* all
+ * references to our watchdog device are released
+ */
+static void fschmd_release_resources(struct kref *ref)
+{
+ struct fschmd_data *data = container_of(ref, struct fschmd_data, kref);
+ kfree(data);
+}
+
+/*
+ * Sysfs attr show / store functions
+ */
+
+static ssize_t in_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ const int max_reading[3] = { 14200, 6600, 3300 };
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ if (data->kind == fscher || data->kind >= fschrc)
+ return sprintf(buf, "%d\n", (data->volt[index] * dmi_vref *
+ dmi_mult[index]) / 255 + dmi_offset[index]);
+ else
+ return sprintf(buf, "%d\n", (data->volt[index] *
+ max_reading[index] + 128) / 255);
+}
+
+
+#define TEMP_FROM_REG(val) (((val) - 128) * 1000)
+
+static ssize_t temp_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[index]));
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[index]));
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = dev_get_drvdata(dev);
+ long v;
+ int err;
+
+ err = kstrtol(buf, 10, &v);
+ if (err)
+ return err;
+
+ v = clamp_val(v / 1000, -128, 127) + 128;
+
+ mutex_lock(&data->update_lock);
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ FSCHMD_REG_TEMP_LIMIT[data->kind][index], v);
+ data->temp_max[index] = v;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t temp_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ /* bit 0 set means sensor working ok, so no fault! */
+ if (data->temp_status[index] & FSCHMD_TEMP_WORKING)
+ return sprintf(buf, "0\n");
+ else
+ return sprintf(buf, "1\n");
+}
+
+static ssize_t temp_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ if ((data->temp_status[index] & FSCHMD_TEMP_ALARM_MASK) ==
+ FSCHMD_TEMP_ALARM_MASK)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+
+#define RPM_FROM_REG(val) ((val) * 60)
+
+static ssize_t fan_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[index]));
+}
+
+static ssize_t fan_div_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ /* bits 2..7 reserved => mask with 3 */
+ return sprintf(buf, "%d\n", 1 << (data->fan_ripple[index] & 3));
+}
+
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ u8 reg;
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = dev_get_drvdata(dev);
+ /* supported values: 2, 4, 8 */
+ unsigned long v;
+ int err;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err)
+ return err;
+
+ switch (v) {
+ case 2:
+ v = 1;
+ break;
+ case 4:
+ v = 2;
+ break;
+ case 8:
+ v = 3;
+ break;
+ default:
+ dev_err(dev,
+ "fan_div value %lu not supported. Choose one of 2, 4 or 8!\n",
+ v);
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+
+ reg = i2c_smbus_read_byte_data(to_i2c_client(dev),
+ FSCHMD_REG_FAN_RIPPLE[data->kind][index]);
+
+ /* bits 2..7 reserved => mask with 0x03 */
+ reg &= ~0x03;
+ reg |= v;
+
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ FSCHMD_REG_FAN_RIPPLE[data->kind][index], reg);
+
+ data->fan_ripple[index] = reg;
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t fan_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ if (data->fan_status[index] & FSCHMD_FAN_ALARM)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t fan_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+
+static ssize_t pwm_auto_point1_pwm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = fschmd_update_device(dev);
+ int val = data->fan_min[index];
+
+ /* 0 = allow turning off (except on the syl), 1-255 = 50-100% */
+ if (val || data->kind == fscsyl)
+ val = val / 2 + 128;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t pwm_auto_point1_pwm_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct fschmd_data *data = dev_get_drvdata(dev);
+ unsigned long v;
+ int err;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err)
+ return err;
+
+ /* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */
+ if (v || data->kind == fscsyl) {
+ v = clamp_val(v, 128, 255);
+ v = (v - 128) * 2 + 1;
+ }
+
+ mutex_lock(&data->update_lock);
+
+ i2c_smbus_write_byte_data(to_i2c_client(dev),
+ FSCHMD_REG_FAN_MIN[data->kind][index], v);
+ data->fan_min[index] = v;
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+
+/*
+ * The FSC hwmon family has the ability to force an attached alert led to flash
+ * from software, we export this as an alert_led sysfs attr
+ */
+static ssize_t alert_led_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fschmd_data *data = fschmd_update_device(dev);
+
+ if (data->global_control & FSCHMD_CONTROL_ALERT_LED)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t alert_led_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ u8 reg;
+ struct fschmd_data *data = dev_get_drvdata(dev);
+ unsigned long v;
+ int err;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ reg = i2c_smbus_read_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL);
+
+ if (v)
+ reg |= FSCHMD_CONTROL_ALERT_LED;
+ else
+ reg &= ~FSCHMD_CONTROL_ALERT_LED;
+
+ i2c_smbus_write_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL, reg);
+
+ data->global_control = reg;
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(alert_led);
+
+static struct sensor_device_attribute fschmd_attr[] = {
+ SENSOR_ATTR_RO(in0_input, in_value, 0),
+ SENSOR_ATTR_RO(in1_input, in_value, 1),
+ SENSOR_ATTR_RO(in2_input, in_value, 2),
+ SENSOR_ATTR_RO(in3_input, in_value, 3),
+ SENSOR_ATTR_RO(in4_input, in_value, 4),
+ SENSOR_ATTR_RO(in5_input, in_value, 5),
+};
+
+static struct sensor_device_attribute fschmd_temp_attr[] = {
+ SENSOR_ATTR_RO(temp1_input, temp_value, 0),
+ SENSOR_ATTR_RW(temp1_max, temp_max, 0),
+ SENSOR_ATTR_RO(temp1_fault, temp_fault, 0),
+ SENSOR_ATTR_RO(temp1_alarm, temp_alarm, 0),
+ SENSOR_ATTR_RO(temp2_input, temp_value, 1),
+ SENSOR_ATTR_RW(temp2_max, temp_max, 1),
+ SENSOR_ATTR_RO(temp2_fault, temp_fault, 1),
+ SENSOR_ATTR_RO(temp2_alarm, temp_alarm, 1),
+ SENSOR_ATTR_RO(temp3_input, temp_value, 2),
+ SENSOR_ATTR_RW(temp3_max, temp_max, 2),
+ SENSOR_ATTR_RO(temp3_fault, temp_fault, 2),
+ SENSOR_ATTR_RO(temp3_alarm, temp_alarm, 2),
+ SENSOR_ATTR_RO(temp4_input, temp_value, 3),
+ SENSOR_ATTR_RW(temp4_max, temp_max, 3),
+ SENSOR_ATTR_RO(temp4_fault, temp_fault, 3),
+ SENSOR_ATTR_RO(temp4_alarm, temp_alarm, 3),
+ SENSOR_ATTR_RO(temp5_input, temp_value, 4),
+ SENSOR_ATTR_RW(temp5_max, temp_max, 4),
+ SENSOR_ATTR_RO(temp5_fault, temp_fault, 4),
+ SENSOR_ATTR_RO(temp5_alarm, temp_alarm, 4),
+ SENSOR_ATTR_RO(temp6_input, temp_value, 5),
+ SENSOR_ATTR_RW(temp6_max, temp_max, 5),
+ SENSOR_ATTR_RO(temp6_fault, temp_fault, 5),
+ SENSOR_ATTR_RO(temp6_alarm, temp_alarm, 5),
+ SENSOR_ATTR_RO(temp7_input, temp_value, 6),
+ SENSOR_ATTR_RW(temp7_max, temp_max, 6),
+ SENSOR_ATTR_RO(temp7_fault, temp_fault, 6),
+ SENSOR_ATTR_RO(temp7_alarm, temp_alarm, 6),
+ SENSOR_ATTR_RO(temp8_input, temp_value, 7),
+ SENSOR_ATTR_RW(temp8_max, temp_max, 7),
+ SENSOR_ATTR_RO(temp8_fault, temp_fault, 7),
+ SENSOR_ATTR_RO(temp8_alarm, temp_alarm, 7),
+ SENSOR_ATTR_RO(temp9_input, temp_value, 8),
+ SENSOR_ATTR_RW(temp9_max, temp_max, 8),
+ SENSOR_ATTR_RO(temp9_fault, temp_fault, 8),
+ SENSOR_ATTR_RO(temp9_alarm, temp_alarm, 8),
+ SENSOR_ATTR_RO(temp10_input, temp_value, 9),
+ SENSOR_ATTR_RW(temp10_max, temp_max, 9),
+ SENSOR_ATTR_RO(temp10_fault, temp_fault, 9),
+ SENSOR_ATTR_RO(temp10_alarm, temp_alarm, 9),
+ SENSOR_ATTR_RO(temp11_input, temp_value, 10),
+ SENSOR_ATTR_RW(temp11_max, temp_max, 10),
+ SENSOR_ATTR_RO(temp11_fault, temp_fault, 10),
+ SENSOR_ATTR_RO(temp11_alarm, temp_alarm, 10),
+};
+
+static struct sensor_device_attribute fschmd_fan_attr[] = {
+ SENSOR_ATTR_RO(fan1_input, fan_value, 0),
+ SENSOR_ATTR_RW(fan1_div, fan_div, 0),
+ SENSOR_ATTR_RO(fan1_alarm, fan_alarm, 0),
+ SENSOR_ATTR_RO(fan1_fault, fan_fault, 0),
+ SENSOR_ATTR_RW(pwm1_auto_point1_pwm, pwm_auto_point1_pwm, 0),
+ SENSOR_ATTR_RO(fan2_input, fan_value, 1),
+ SENSOR_ATTR_RW(fan2_div, fan_div, 1),
+ SENSOR_ATTR_RO(fan2_alarm, fan_alarm, 1),
+ SENSOR_ATTR_RO(fan2_fault, fan_fault, 1),
+ SENSOR_ATTR_RW(pwm2_auto_point1_pwm, pwm_auto_point1_pwm, 1),
+ SENSOR_ATTR_RO(fan3_input, fan_value, 2),
+ SENSOR_ATTR_RW(fan3_div, fan_div, 2),
+ SENSOR_ATTR_RO(fan3_alarm, fan_alarm, 2),
+ SENSOR_ATTR_RO(fan3_fault, fan_fault, 2),
+ SENSOR_ATTR_RW(pwm3_auto_point1_pwm, pwm_auto_point1_pwm, 2),
+ SENSOR_ATTR_RO(fan4_input, fan_value, 3),
+ SENSOR_ATTR_RW(fan4_div, fan_div, 3),
+ SENSOR_ATTR_RO(fan4_alarm, fan_alarm, 3),
+ SENSOR_ATTR_RO(fan4_fault, fan_fault, 3),
+ SENSOR_ATTR_RW(pwm4_auto_point1_pwm, pwm_auto_point1_pwm, 3),
+ SENSOR_ATTR_RO(fan5_input, fan_value, 4),
+ SENSOR_ATTR_RW(fan5_div, fan_div, 4),
+ SENSOR_ATTR_RO(fan5_alarm, fan_alarm, 4),
+ SENSOR_ATTR_RO(fan5_fault, fan_fault, 4),
+ SENSOR_ATTR_RW(pwm5_auto_point1_pwm, pwm_auto_point1_pwm, 4),
+ SENSOR_ATTR_RO(fan6_input, fan_value, 5),
+ SENSOR_ATTR_RW(fan6_div, fan_div, 5),
+ SENSOR_ATTR_RO(fan6_alarm, fan_alarm, 5),
+ SENSOR_ATTR_RO(fan6_fault, fan_fault, 5),
+ SENSOR_ATTR_RW(pwm6_auto_point1_pwm, pwm_auto_point1_pwm, 5),
+ SENSOR_ATTR_RO(fan7_input, fan_value, 6),
+ SENSOR_ATTR_RW(fan7_div, fan_div, 6),
+ SENSOR_ATTR_RO(fan7_alarm, fan_alarm, 6),
+ SENSOR_ATTR_RO(fan7_fault, fan_fault, 6),
+ SENSOR_ATTR_RW(pwm7_auto_point1_pwm, pwm_auto_point1_pwm, 6),
+};
+
+
+/*
+ * Watchdog routines
+ */
+
+static int watchdog_set_timeout(struct fschmd_data *data, int timeout)
+{
+ int ret, resolution;
+ int kind = data->kind + 1; /* 0-x array index -> 1-x module param */
+
+ /* 2 second or 60 second resolution? */
+ if (timeout <= 510 || kind == fscpos || kind == fscscy)
+ resolution = 2;
+ else
+ resolution = 60;
+
+ if (timeout < resolution || timeout > (resolution * 255))
+ return -EINVAL;
+
+ mutex_lock(&data->watchdog_lock);
+ if (!data->client) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ if (resolution == 2)
+ data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_RESOLUTION;
+ else
+ data->watchdog_control |= FSCHMD_WDOG_CONTROL_RESOLUTION;
+
+ data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
+
+ /* Write new timeout value */
+ i2c_smbus_write_byte_data(data->client,
+ FSCHMD_REG_WDOG_PRESET[data->kind], data->watchdog_preset);
+ /* Write new control register, do not trigger! */
+ i2c_smbus_write_byte_data(data->client,
+ FSCHMD_REG_WDOG_CONTROL[data->kind],
+ data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER);
+
+ ret = data->watchdog_preset * resolution;
+
+leave:
+ mutex_unlock(&data->watchdog_lock);
+ return ret;
+}
+
+static int watchdog_get_timeout(struct fschmd_data *data)
+{
+ int timeout;
+
+ mutex_lock(&data->watchdog_lock);
+ if (data->watchdog_control & FSCHMD_WDOG_CONTROL_RESOLUTION)
+ timeout = data->watchdog_preset * 60;
+ else
+ timeout = data->watchdog_preset * 2;
+ mutex_unlock(&data->watchdog_lock);
+
+ return timeout;
+}
+
+static int watchdog_trigger(struct fschmd_data *data)
+{
+ int ret = 0;
+
+ mutex_lock(&data->watchdog_lock);
+ if (!data->client) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER;
+ i2c_smbus_write_byte_data(data->client,
+ FSCHMD_REG_WDOG_CONTROL[data->kind],
+ data->watchdog_control);
+leave:
+ mutex_unlock(&data->watchdog_lock);
+ return ret;
+}
+
+static int watchdog_stop(struct fschmd_data *data)
+{
+ int ret = 0;
+
+ mutex_lock(&data->watchdog_lock);
+ if (!data->client) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED;
+ /*
+ * Don't store the stop flag in our watchdog control register copy, as
+ * its a write only bit (read always returns 0)
+ */
+ i2c_smbus_write_byte_data(data->client,
+ FSCHMD_REG_WDOG_CONTROL[data->kind],
+ data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP);
+leave:
+ mutex_unlock(&data->watchdog_lock);
+ return ret;
+}
+
+static int watchdog_open(struct inode *inode, struct file *filp)
+{
+ struct fschmd_data *pos, *data = NULL;
+ int watchdog_is_open;
+
+ /*
+ * We get called from drivers/char/misc.c with misc_mtx hold, and we
+ * call misc_register() from fschmd_probe() with watchdog_data_mutex
+ * hold, as misc_register() takes the misc_mtx lock, this is a possible
+ * deadlock, so we use mutex_trylock here.
+ */
+ if (!mutex_trylock(&watchdog_data_mutex))
+ return -ERESTARTSYS;
+ list_for_each_entry(pos, &watchdog_data_list, list) {
+ if (pos->watchdog_miscdev.minor == iminor(inode)) {
+ data = pos;
+ break;
+ }
+ }
+ /* Note we can never not have found data, so we don't check for this */
+ watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open);
+ if (!watchdog_is_open)
+ kref_get(&data->kref);
+ mutex_unlock(&watchdog_data_mutex);
+
+ if (watchdog_is_open)
+ return -EBUSY;
+
+ /* Start the watchdog */
+ watchdog_trigger(data);
+ filp->private_data = data;
+
+ return stream_open(inode, filp);
+}
+
+static int watchdog_release(struct inode *inode, struct file *filp)
+{
+ struct fschmd_data *data = filp->private_data;
+
+ if (data->watchdog_expect_close) {
+ watchdog_stop(data);
+ data->watchdog_expect_close = 0;
+ } else {
+ watchdog_trigger(data);
+ dev_crit(&data->client->dev,
+ "unexpected close, not stopping watchdog!\n");
+ }
+
+ clear_bit(0, &data->watchdog_is_open);
+
+ mutex_lock(&watchdog_data_mutex);
+ kref_put(&data->kref, fschmd_release_resources);
+ mutex_unlock(&watchdog_data_mutex);
+
+ return 0;
+}
+
+static ssize_t watchdog_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ int ret;
+ struct fschmd_data *data = filp->private_data;
+
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* Clear it in case it was set with a previous write */
+ data->watchdog_expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ data->watchdog_expect_close = 1;
+ }
+ }
+ ret = watchdog_trigger(data);
+ if (ret < 0)
+ return ret;
+ }
+ return count;
+}
+
+static long watchdog_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_CARDRESET,
+ .identity = "FSC watchdog"
+ };
+ int i, ret = 0;
+ struct fschmd_data *data = filp->private_data;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ident.firmware_version = data->revision;
+ if (!nowayout)
+ ident.options |= WDIOF_MAGICCLOSE;
+ if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
+ ret = -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int __user *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ if (data->watchdog_state & FSCHMD_WDOG_STATE_CARDRESET)
+ ret = put_user(WDIOF_CARDRESET, (int __user *)arg);
+ else
+ ret = put_user(0, (int __user *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ ret = watchdog_trigger(data);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ i = watchdog_get_timeout(data);
+ ret = put_user(i, (int __user *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(i, (int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = watchdog_set_timeout(data, i);
+ if (ret > 0)
+ ret = put_user(ret, (int __user *)arg);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(i, (int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ if (i & WDIOS_DISABLECARD)
+ ret = watchdog_stop(data);
+ else if (i & WDIOS_ENABLECARD)
+ ret = watchdog_trigger(data);
+ else
+ ret = -EINVAL;
+
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+ return ret;
+}
+
+static const struct file_operations watchdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = watchdog_open,
+ .release = watchdog_release,
+ .write = watchdog_write,
+ .unlocked_ioctl = watchdog_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+};
+
+
+/*
+ * Detect, register, unregister and update device functions
+ */
+
+/*
+ * DMI decode routine to read voltage scaling factors from special DMI tables,
+ * which are available on FSC machines with an fscher or later chip.
+ */
+static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy)
+{
+ int i, mult[3] = { 0 }, offset[3] = { 0 }, vref = 0, found = 0;
+
+ /*
+ * dmi code ugliness, we get passed the address of the contents of
+ * a complete DMI record, but in the form of a dmi_header pointer, in
+ * reality this address holds header->length bytes of which the header
+ * are the first 4 bytes
+ */
+ u8 *dmi_data = (u8 *)header;
+
+ /* We are looking for OEM-specific type 185 */
+ if (header->type != 185)
+ return;
+
+ /*
+ * we are looking for what Siemens calls "subtype" 19, the subtype
+ * is stored in byte 5 of the dmi block
+ */
+ if (header->length < 5 || dmi_data[4] != 19)
+ return;
+
+ /*
+ * After the subtype comes 1 unknown byte and then blocks of 5 bytes,
+ * consisting of what Siemens calls an "Entity" number, followed by
+ * 2 16-bit words in LSB first order
+ */
+ for (i = 6; (i + 4) < header->length; i += 5) {
+ /* entity 1 - 3: voltage multiplier and offset */
+ if (dmi_data[i] >= 1 && dmi_data[i] <= 3) {
+ /* Our in sensors order and the DMI order differ */
+ const int shuffle[3] = { 1, 0, 2 };
+ int in = shuffle[dmi_data[i] - 1];
+
+ /* Check for twice the same entity */
+ if (found & (1 << in))
+ return;
+
+ mult[in] = dmi_data[i + 1] | (dmi_data[i + 2] << 8);
+ offset[in] = dmi_data[i + 3] | (dmi_data[i + 4] << 8);
+
+ found |= 1 << in;
+ }
+
+ /* entity 7: reference voltage */
+ if (dmi_data[i] == 7) {
+ /* Check for twice the same entity */
+ if (found & 0x08)
+ return;
+
+ vref = dmi_data[i + 1] | (dmi_data[i + 2] << 8);
+
+ found |= 0x08;
+ }
+ }
+
+ if (found == 0x0F) {
+ for (i = 0; i < 3; i++) {
+ dmi_mult[i] = mult[i] * 10;
+ dmi_offset[i] = offset[i] * 10;
+ }
+ /*
+ * According to the docs there should be separate dmi entries
+ * for the mult's and offsets of in3-5 of the syl, but on
+ * my test machine these are not present
+ */
+ dmi_mult[3] = dmi_mult[2];
+ dmi_mult[4] = dmi_mult[1];
+ dmi_mult[5] = dmi_mult[2];
+ dmi_offset[3] = dmi_offset[2];
+ dmi_offset[4] = dmi_offset[1];
+ dmi_offset[5] = dmi_offset[2];
+ dmi_vref = vref;
+ }
+}
+
+static int fschmd_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ enum chips kind;
+ struct i2c_adapter *adapter = client->adapter;
+ char id[4];
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Detect & Identify the chip */
+ id[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_0);
+ id[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_1);
+ id[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_2);
+ id[3] = '\0';
+
+ if (!strcmp(id, "PEG"))
+ kind = fscpos;
+ else if (!strcmp(id, "HER"))
+ kind = fscher;
+ else if (!strcmp(id, "SCY"))
+ kind = fscscy;
+ else if (!strcmp(id, "HRC"))
+ kind = fschrc;
+ else if (!strcmp(id, "HMD"))
+ kind = fschmd;
+ else if (!strcmp(id, "HDS"))
+ kind = fschds;
+ else if (!strcmp(id, "SYL"))
+ kind = fscsyl;
+ else
+ return -ENODEV;
+
+ strscpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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 = i2c_match_id(fschmd_id, client)->driver_data;
+
+ data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ mutex_init(&data->watchdog_lock);
+ INIT_LIST_HEAD(&data->list);
+ kref_init(&data->kref);
+ /*
+ * Store client pointer in our data struct for watchdog usage
+ * (where the client is found through a data ptr instead of the
+ * otherway around)
+ */
+ data->client = client;
+ data->kind = kind;
+
+ if (kind == fscpos) {
+ /*
+ * The Poseidon has hardwired temp limits, fill these
+ * in for the alarm resetting code
+ */
+ data->temp_max[0] = 70 + 128;
+ data->temp_max[1] = 50 + 128;
+ data->temp_max[2] = 50 + 128;
+ }
+
+ /* Read the special DMI table for fscher and newer chips */
+ if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) {
+ dmi_walk(fschmd_dmi_decode, NULL);
+ if (dmi_vref == -1) {
+ dev_warn(&client->dev,
+ "Couldn't get voltage scaling factors from "
+ "BIOS DMI table, using builtin defaults\n");
+ dmi_vref = 33;
+ }
+ }
+
+ /* Read in some never changing registers */
+ data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
+ data->global_control = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_CONTROL);
+ data->watchdog_control = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_WDOG_CONTROL[data->kind]);
+ data->watchdog_state = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_WDOG_STATE[data->kind]);
+ data->watchdog_preset = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_WDOG_PRESET[data->kind]);
+
+ err = device_create_file(&client->dev, &dev_attr_alert_led);
+ if (err)
+ goto exit_detach;
+
+ for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++) {
+ err = device_create_file(&client->dev,
+ &fschmd_attr[i].dev_attr);
+ if (err)
+ goto exit_detach;
+ }
+
+ for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) {
+ /* Poseidon doesn't have TEMP_LIMIT registers */
+ if (kind == fscpos && fschmd_temp_attr[i].dev_attr.show ==
+ temp_max_show)
+ continue;
+
+ if (kind == fscsyl) {
+ if (i % 4 == 0)
+ data->temp_status[i / 4] =
+ i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_TEMP_STATE
+ [data->kind][i / 4]);
+ if (data->temp_status[i / 4] & FSCHMD_TEMP_DISABLED)
+ continue;
+ }
+
+ err = device_create_file(&client->dev,
+ &fschmd_temp_attr[i].dev_attr);
+ if (err)
+ goto exit_detach;
+ }
+
+ for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++) {
+ /* Poseidon doesn't have a FAN_MIN register for its 3rd fan */
+ if (kind == fscpos &&
+ !strcmp(fschmd_fan_attr[i].dev_attr.attr.name,
+ "pwm3_auto_point1_pwm"))
+ continue;
+
+ if (kind == fscsyl) {
+ if (i % 5 == 0)
+ data->fan_status[i / 5] =
+ i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_FAN_STATE
+ [data->kind][i / 5]);
+ if (data->fan_status[i / 5] & FSCHMD_FAN_DISABLED)
+ continue;
+ }
+
+ err = device_create_file(&client->dev,
+ &fschmd_fan_attr[i].dev_attr);
+ if (err)
+ goto exit_detach;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ data->hwmon_dev = NULL;
+ goto exit_detach;
+ }
+
+ /*
+ * We take the data_mutex lock early so that watchdog_open() cannot
+ * run when misc_register() has completed, but we've not yet added
+ * our data to the watchdog_data_list (and set the default timeout)
+ */
+ mutex_lock(&watchdog_data_mutex);
+ for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) {
+ /* Register our watchdog part */
+ snprintf(data->watchdog_name, sizeof(data->watchdog_name),
+ "watchdog%c", (i == 0) ? '\0' : ('0' + i));
+ data->watchdog_miscdev.name = data->watchdog_name;
+ data->watchdog_miscdev.fops = &watchdog_fops;
+ data->watchdog_miscdev.minor = watchdog_minors[i];
+ err = misc_register(&data->watchdog_miscdev);
+ if (err == -EBUSY)
+ continue;
+ if (err) {
+ data->watchdog_miscdev.minor = 0;
+ dev_err(&client->dev,
+ "Registering watchdog chardev: %d\n", err);
+ break;
+ }
+
+ list_add(&data->list, &watchdog_data_list);
+ watchdog_set_timeout(data, 60);
+ dev_info(&client->dev,
+ "Registered watchdog chardev major 10, minor: %d\n",
+ watchdog_minors[i]);
+ break;
+ }
+ if (i == ARRAY_SIZE(watchdog_minors)) {
+ data->watchdog_miscdev.minor = 0;
+ dev_warn(&client->dev,
+ "Couldn't register watchdog chardev (due to no free minor)\n");
+ }
+ mutex_unlock(&watchdog_data_mutex);
+
+ dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n",
+ names[data->kind], (int) data->revision);
+
+ return 0;
+
+exit_detach:
+ fschmd_remove(client); /* will also free data for us */
+ return err;
+}
+
+static void fschmd_remove(struct i2c_client *client)
+{
+ struct fschmd_data *data = i2c_get_clientdata(client);
+ int i;
+
+ /* Unregister the watchdog (if registered) */
+ if (data->watchdog_miscdev.minor) {
+ misc_deregister(&data->watchdog_miscdev);
+ if (data->watchdog_is_open) {
+ dev_warn(&client->dev,
+ "i2c client detached with watchdog open! "
+ "Stopping watchdog.\n");
+ watchdog_stop(data);
+ }
+ mutex_lock(&watchdog_data_mutex);
+ list_del(&data->list);
+ mutex_unlock(&watchdog_data_mutex);
+ /* Tell the watchdog code the client is gone */
+ mutex_lock(&data->watchdog_lock);
+ data->client = NULL;
+ mutex_unlock(&data->watchdog_lock);
+ }
+
+ /*
+ * Check if registered in case we're called from fschmd_detect
+ * to cleanup after an error
+ */
+ if (data->hwmon_dev)
+ hwmon_device_unregister(data->hwmon_dev);
+
+ device_remove_file(&client->dev, &dev_attr_alert_led);
+ for (i = 0; i < (FSCHMD_NO_VOLT_SENSORS[data->kind]); i++)
+ device_remove_file(&client->dev, &fschmd_attr[i].dev_attr);
+ for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++)
+ device_remove_file(&client->dev,
+ &fschmd_temp_attr[i].dev_attr);
+ for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++)
+ device_remove_file(&client->dev,
+ &fschmd_fan_attr[i].dev_attr);
+
+ mutex_lock(&watchdog_data_mutex);
+ kref_put(&data->kref, fschmd_release_resources);
+ mutex_unlock(&watchdog_data_mutex);
+}
+
+static struct fschmd_data *fschmd_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct fschmd_data *data = i2c_get_clientdata(client);
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
+
+ for (i = 0; i < FSCHMD_NO_TEMP_SENSORS[data->kind]; i++) {
+ data->temp_act[i] = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_TEMP_ACT[data->kind][i]);
+ data->temp_status[i] = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_TEMP_STATE[data->kind][i]);
+
+ /* The fscpos doesn't have TEMP_LIMIT registers */
+ if (FSCHMD_REG_TEMP_LIMIT[data->kind][i])
+ data->temp_max[i] = i2c_smbus_read_byte_data(
+ client,
+ FSCHMD_REG_TEMP_LIMIT[data->kind][i]);
+
+ /*
+ * reset alarm if the alarm condition is gone,
+ * the chip doesn't do this itself
+ */
+ if ((data->temp_status[i] & FSCHMD_TEMP_ALARM_MASK) ==
+ FSCHMD_TEMP_ALARM_MASK &&
+ data->temp_act[i] < data->temp_max[i])
+ i2c_smbus_write_byte_data(client,
+ FSCHMD_REG_TEMP_STATE[data->kind][i],
+ data->temp_status[i]);
+ }
+
+ for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) {
+ data->fan_act[i] = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_FAN_ACT[data->kind][i]);
+ data->fan_status[i] = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_FAN_STATE[data->kind][i]);
+ data->fan_ripple[i] = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_FAN_RIPPLE[data->kind][i]);
+
+ /* The fscpos third fan doesn't have a fan_min */
+ if (FSCHMD_REG_FAN_MIN[data->kind][i])
+ data->fan_min[i] = i2c_smbus_read_byte_data(
+ client,
+ FSCHMD_REG_FAN_MIN[data->kind][i]);
+
+ /* reset fan status if speed is back to > 0 */
+ if ((data->fan_status[i] & FSCHMD_FAN_ALARM) &&
+ data->fan_act[i])
+ i2c_smbus_write_byte_data(client,
+ FSCHMD_REG_FAN_STATE[data->kind][i],
+ data->fan_status[i]);
+ }
+
+ for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++)
+ data->volt[i] = i2c_smbus_read_byte_data(client,
+ FSCHMD_REG_VOLT[data->kind][i]);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+module_i2c_driver(fschmd_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles, Heimdall, Hades "
+ "and Syleus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c
new file mode 100644
index 000000000..ffa0bb364
--- /dev/null
+++ b/drivers/hwmon/ftsteutates.c
@@ -0,0 +1,838 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for the FTS Systemmonitoring Chip "Teutates"
+ *
+ * Copyright (C) 2016 Fujitsu Technology Solutions GmbH,
+ * Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>
+ */
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define FTS_DEVICE_ID_REG 0x0000
+#define FTS_DEVICE_REVISION_REG 0x0001
+#define FTS_DEVICE_STATUS_REG 0x0004
+#define FTS_SATELLITE_STATUS_REG 0x0005
+#define FTS_EVENT_STATUS_REG 0x0006
+#define FTS_GLOBAL_CONTROL_REG 0x0007
+
+#define FTS_DEVICE_DETECT_REG_1 0x0C
+#define FTS_DEVICE_DETECT_REG_2 0x0D
+#define FTS_DEVICE_DETECT_REG_3 0x0E
+
+#define FTS_SENSOR_EVENT_REG 0x0010
+
+#define FTS_FAN_EVENT_REG 0x0014
+#define FTS_FAN_PRESENT_REG 0x0015
+
+#define FTS_POWER_ON_TIME_COUNTER_A 0x007A
+#define FTS_POWER_ON_TIME_COUNTER_B 0x007B
+#define FTS_POWER_ON_TIME_COUNTER_C 0x007C
+
+#define FTS_PAGE_SELECT_REG 0x007F
+
+#define FTS_WATCHDOG_TIME_PRESET 0x000B
+#define FTS_WATCHDOG_CONTROL 0x5081
+
+#define FTS_NO_FAN_SENSORS 0x08
+#define FTS_NO_TEMP_SENSORS 0x10
+#define FTS_NO_VOLT_SENSORS 0x04
+
+static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
+
+static const struct i2c_device_id fts_id[] = {
+ { "ftsteutates", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, fts_id);
+
+enum WATCHDOG_RESOLUTION {
+ seconds = 1,
+ minutes = 60
+};
+
+struct fts_data {
+ struct i2c_client *client;
+ /* update sensor data lock */
+ struct mutex update_lock;
+ /* read/write register lock */
+ struct mutex access_lock;
+ unsigned long last_updated; /* in jiffies */
+ struct watchdog_device wdd;
+ enum WATCHDOG_RESOLUTION resolution;
+ bool valid; /* false until following fields are valid */
+
+ u8 volt[FTS_NO_VOLT_SENSORS];
+
+ u8 temp_input[FTS_NO_TEMP_SENSORS];
+ u8 temp_alarm;
+
+ u8 fan_present;
+ u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */
+ u8 fan_source[FTS_NO_FAN_SENSORS];
+ u8 fan_alarm;
+};
+
+#define FTS_REG_FAN_INPUT(idx) ((idx) + 0x20)
+#define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30)
+#define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881)
+
+#define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40)
+#define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681)
+
+#define FTS_REG_VOLT(idx) ((idx) + 0x18)
+
+/*****************************************************************************/
+/* I2C Helper functions */
+/*****************************************************************************/
+static int fts_read_byte(struct i2c_client *client, unsigned short reg)
+{
+ int ret;
+ unsigned char page = reg >> 8;
+ struct fts_data *data = dev_get_drvdata(&client->dev);
+
+ mutex_lock(&data->access_lock);
+
+ dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
+ ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
+ if (ret < 0)
+ goto error;
+
+ reg &= 0xFF;
+ ret = i2c_smbus_read_byte_data(client, reg);
+ dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret);
+
+error:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int fts_write_byte(struct i2c_client *client, unsigned short reg,
+ unsigned char value)
+{
+ int ret;
+ unsigned char page = reg >> 8;
+ struct fts_data *data = dev_get_drvdata(&client->dev);
+
+ mutex_lock(&data->access_lock);
+
+ dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
+ ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
+ if (ret < 0)
+ goto error;
+
+ reg &= 0xFF;
+ dev_dbg(&client->dev,
+ "write - reg: 0x%.02x: val: 0x%.02x\n", reg, value);
+ ret = i2c_smbus_write_byte_data(client, reg, value);
+
+error:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+/*****************************************************************************/
+/* Data Updater Helper function */
+/*****************************************************************************/
+static int fts_update_device(struct fts_data *data)
+{
+ int i;
+ int err = 0;
+
+ mutex_lock(&data->update_lock);
+ if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid)
+ goto exit;
+
+ err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG);
+ if (err < 0)
+ goto exit;
+
+ data->valid = !!(err & 0x02); /* Data not ready yet */
+ if (unlikely(!data->valid)) {
+ err = -EAGAIN;
+ goto exit;
+ }
+
+ err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG);
+ if (err < 0)
+ goto exit;
+ data->fan_present = err;
+
+ err = fts_read_byte(data->client, FTS_FAN_EVENT_REG);
+ if (err < 0)
+ goto exit;
+ data->fan_alarm = err;
+
+ for (i = 0; i < FTS_NO_FAN_SENSORS; i++) {
+ if (data->fan_present & BIT(i)) {
+ err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i));
+ if (err < 0)
+ goto exit;
+ data->fan_input[i] = err;
+
+ err = fts_read_byte(data->client,
+ FTS_REG_FAN_SOURCE(i));
+ if (err < 0)
+ goto exit;
+ data->fan_source[i] = err;
+ } else {
+ data->fan_input[i] = 0;
+ data->fan_source[i] = 0;
+ }
+ }
+
+ err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG);
+ if (err < 0)
+ goto exit;
+ data->temp_alarm = err;
+
+ for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) {
+ err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i));
+ if (err < 0)
+ goto exit;
+ data->temp_input[i] = err;
+ }
+
+ for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) {
+ err = fts_read_byte(data->client, FTS_REG_VOLT(i));
+ if (err < 0)
+ goto exit;
+ data->volt[i] = err;
+ }
+ data->last_updated = jiffies;
+ err = 0;
+exit:
+ mutex_unlock(&data->update_lock);
+ return err;
+}
+
+/*****************************************************************************/
+/* Watchdog functions */
+/*****************************************************************************/
+static int fts_wd_set_resolution(struct fts_data *data,
+ enum WATCHDOG_RESOLUTION resolution)
+{
+ int ret;
+
+ if (data->resolution == resolution)
+ return 0;
+
+ ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
+ if (ret < 0)
+ return ret;
+
+ if ((resolution == seconds && ret & BIT(1)) ||
+ (resolution == minutes && (ret & BIT(1)) == 0)) {
+ data->resolution = resolution;
+ return 0;
+ }
+
+ if (resolution == seconds)
+ ret |= BIT(1);
+ else
+ ret &= ~BIT(1);
+
+ ret = fts_write_byte(data->client, FTS_WATCHDOG_CONTROL, ret);
+ if (ret < 0)
+ return ret;
+
+ data->resolution = resolution;
+ return ret;
+}
+
+static int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
+{
+ struct fts_data *data;
+ enum WATCHDOG_RESOLUTION resolution = seconds;
+ int ret;
+
+ data = watchdog_get_drvdata(wdd);
+ /* switch watchdog resolution to minutes if timeout does not fit
+ * into a byte
+ */
+ if (timeout > 0xFF) {
+ timeout = DIV_ROUND_UP(timeout, 60) * 60;
+ resolution = minutes;
+ }
+
+ ret = fts_wd_set_resolution(data, resolution);
+ if (ret < 0)
+ return ret;
+
+ wdd->timeout = timeout;
+ return 0;
+}
+
+static int fts_wd_start(struct watchdog_device *wdd)
+{
+ struct fts_data *data = watchdog_get_drvdata(wdd);
+
+ return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET,
+ wdd->timeout / (u8)data->resolution);
+}
+
+static int fts_wd_stop(struct watchdog_device *wdd)
+{
+ struct fts_data *data;
+
+ data = watchdog_get_drvdata(wdd);
+ return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, 0);
+}
+
+static const struct watchdog_info fts_wd_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "FTS Teutates Hardware Watchdog",
+};
+
+static const struct watchdog_ops fts_wd_ops = {
+ .owner = THIS_MODULE,
+ .start = fts_wd_start,
+ .stop = fts_wd_stop,
+ .set_timeout = fts_wd_set_timeout,
+};
+
+static int fts_watchdog_init(struct fts_data *data)
+{
+ int timeout, ret;
+
+ watchdog_set_drvdata(&data->wdd, data);
+
+ timeout = fts_read_byte(data->client, FTS_WATCHDOG_TIME_PRESET);
+ if (timeout < 0)
+ return timeout;
+
+ /* watchdog not running, set timeout to a default of 60 sec. */
+ if (timeout == 0) {
+ ret = fts_wd_set_resolution(data, seconds);
+ if (ret < 0)
+ return ret;
+ data->wdd.timeout = 60;
+ } else {
+ ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
+ if (ret < 0)
+ return ret;
+
+ data->resolution = ret & BIT(1) ? seconds : minutes;
+ data->wdd.timeout = timeout * (u8)data->resolution;
+ set_bit(WDOG_HW_RUNNING, &data->wdd.status);
+ }
+
+ /* Register our watchdog part */
+ data->wdd.info = &fts_wd_info;
+ data->wdd.ops = &fts_wd_ops;
+ data->wdd.parent = &data->client->dev;
+ data->wdd.min_timeout = 1;
+
+ /* max timeout 255 minutes. */
+ data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC;
+
+ return watchdog_register_device(&data->wdd);
+}
+
+/*****************************************************************************/
+/* SysFS handler functions */
+/*****************************************************************************/
+static ssize_t in_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ int value, err;
+
+ err = fts_update_device(data);
+ if (err < 0)
+ return err;
+
+ value = DIV_ROUND_CLOSEST(data->volt[index] * 3300, 255);
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t temp_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ int value, err;
+
+ err = fts_update_device(data);
+ if (err < 0)
+ return err;
+
+ value = (data->temp_input[index] - 64) * 1000;
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t temp_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ int err;
+
+ err = fts_update_device(data);
+ if (err < 0)
+ return err;
+
+ /* 00h Temperature = Sensor Error */
+ return sprintf(buf, "%d\n", data->temp_input[index] == 0);
+}
+
+static ssize_t temp_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ int err;
+
+ err = fts_update_device(data);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%u\n", !!(data->temp_alarm & BIT(index)));
+}
+
+static ssize_t
+temp_alarm_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ long ret;
+
+ ret = fts_update_device(data);
+ if (ret < 0)
+ return ret;
+
+ if (kstrtoul(buf, 10, &ret) || ret != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(index));
+ if (ret < 0)
+ goto error;
+
+ ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(index),
+ ret | 0x1);
+ if (ret < 0)
+ goto error;
+
+ data->valid = false;
+ ret = count;
+error:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t fan_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ int value, err;
+
+ err = fts_update_device(data);
+ if (err < 0)
+ return err;
+
+ value = data->fan_input[index] * 60;
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t fan_source_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ int err;
+
+ err = fts_update_device(data);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%u\n", data->fan_source[index]);
+}
+
+static ssize_t fan_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ int err;
+
+ err = fts_update_device(data);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%d\n", !!(data->fan_alarm & BIT(index)));
+}
+
+static ssize_t
+fan_alarm_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct fts_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ long ret;
+
+ ret = fts_update_device(data);
+ if (ret < 0)
+ return ret;
+
+ if (kstrtoul(buf, 10, &ret) || ret != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(index));
+ if (ret < 0)
+ goto error;
+
+ ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(index),
+ ret | 0x1);
+ if (ret < 0)
+ goto error;
+
+ data->valid = false;
+ ret = count;
+error:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+/*****************************************************************************/
+/* SysFS structs */
+/*****************************************************************************/
+
+/* 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);
+static SENSOR_DEVICE_ATTR_RO(temp4_input, temp_value, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_input, temp_value, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_input, temp_value, 5);
+static SENSOR_DEVICE_ATTR_RO(temp7_input, temp_value, 6);
+static SENSOR_DEVICE_ATTR_RO(temp8_input, temp_value, 7);
+static SENSOR_DEVICE_ATTR_RO(temp9_input, temp_value, 8);
+static SENSOR_DEVICE_ATTR_RO(temp10_input, temp_value, 9);
+static SENSOR_DEVICE_ATTR_RO(temp11_input, temp_value, 10);
+static SENSOR_DEVICE_ATTR_RO(temp12_input, temp_value, 11);
+static SENSOR_DEVICE_ATTR_RO(temp13_input, temp_value, 12);
+static SENSOR_DEVICE_ATTR_RO(temp14_input, temp_value, 13);
+static SENSOR_DEVICE_ATTR_RO(temp15_input, temp_value, 14);
+static SENSOR_DEVICE_ATTR_RO(temp16_input, temp_value, 15);
+
+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(temp9_fault, temp_fault, 8);
+static SENSOR_DEVICE_ATTR_RO(temp10_fault, temp_fault, 9);
+static SENSOR_DEVICE_ATTR_RO(temp11_fault, temp_fault, 10);
+static SENSOR_DEVICE_ATTR_RO(temp12_fault, temp_fault, 11);
+static SENSOR_DEVICE_ATTR_RO(temp13_fault, temp_fault, 12);
+static SENSOR_DEVICE_ATTR_RO(temp14_fault, temp_fault, 13);
+static SENSOR_DEVICE_ATTR_RO(temp15_fault, temp_fault, 14);
+static SENSOR_DEVICE_ATTR_RO(temp16_fault, temp_fault, 15);
+
+static SENSOR_DEVICE_ATTR_RW(temp1_alarm, temp_alarm, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_alarm, temp_alarm, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_alarm, temp_alarm, 2);
+static SENSOR_DEVICE_ATTR_RW(temp4_alarm, temp_alarm, 3);
+static SENSOR_DEVICE_ATTR_RW(temp5_alarm, temp_alarm, 4);
+static SENSOR_DEVICE_ATTR_RW(temp6_alarm, temp_alarm, 5);
+static SENSOR_DEVICE_ATTR_RW(temp7_alarm, temp_alarm, 6);
+static SENSOR_DEVICE_ATTR_RW(temp8_alarm, temp_alarm, 7);
+static SENSOR_DEVICE_ATTR_RW(temp9_alarm, temp_alarm, 8);
+static SENSOR_DEVICE_ATTR_RW(temp10_alarm, temp_alarm, 9);
+static SENSOR_DEVICE_ATTR_RW(temp11_alarm, temp_alarm, 10);
+static SENSOR_DEVICE_ATTR_RW(temp12_alarm, temp_alarm, 11);
+static SENSOR_DEVICE_ATTR_RW(temp13_alarm, temp_alarm, 12);
+static SENSOR_DEVICE_ATTR_RW(temp14_alarm, temp_alarm, 13);
+static SENSOR_DEVICE_ATTR_RW(temp15_alarm, temp_alarm, 14);
+static SENSOR_DEVICE_ATTR_RW(temp16_alarm, temp_alarm, 15);
+
+static struct attribute *fts_temp_attrs[] = {
+ &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_temp11_input.dev_attr.attr,
+ &sensor_dev_attr_temp12_input.dev_attr.attr,
+ &sensor_dev_attr_temp13_input.dev_attr.attr,
+ &sensor_dev_attr_temp14_input.dev_attr.attr,
+ &sensor_dev_attr_temp15_input.dev_attr.attr,
+ &sensor_dev_attr_temp16_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_temp9_fault.dev_attr.attr,
+ &sensor_dev_attr_temp10_fault.dev_attr.attr,
+ &sensor_dev_attr_temp11_fault.dev_attr.attr,
+ &sensor_dev_attr_temp12_fault.dev_attr.attr,
+ &sensor_dev_attr_temp13_fault.dev_attr.attr,
+ &sensor_dev_attr_temp14_fault.dev_attr.attr,
+ &sensor_dev_attr_temp15_fault.dev_attr.attr,
+ &sensor_dev_attr_temp16_fault.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_temp11_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp12_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp13_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp14_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp15_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp16_alarm.dev_attr.attr,
+ NULL
+};
+
+/* Fans */
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_value, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_value, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_value, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_input, fan_value, 3);
+static SENSOR_DEVICE_ATTR_RO(fan5_input, fan_value, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_input, fan_value, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_input, fan_value, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_input, fan_value, 7);
+
+static SENSOR_DEVICE_ATTR_RO(fan1_source, fan_source, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_source, fan_source, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_source, fan_source, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_source, fan_source, 3);
+static SENSOR_DEVICE_ATTR_RO(fan5_source, fan_source, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_source, fan_source, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_source, fan_source, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_source, fan_source, 7);
+
+static SENSOR_DEVICE_ATTR_RW(fan1_alarm, fan_alarm, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_alarm, fan_alarm, 1);
+static SENSOR_DEVICE_ATTR_RW(fan3_alarm, fan_alarm, 2);
+static SENSOR_DEVICE_ATTR_RW(fan4_alarm, fan_alarm, 3);
+static SENSOR_DEVICE_ATTR_RW(fan5_alarm, fan_alarm, 4);
+static SENSOR_DEVICE_ATTR_RW(fan6_alarm, fan_alarm, 5);
+static SENSOR_DEVICE_ATTR_RW(fan7_alarm, fan_alarm, 6);
+static SENSOR_DEVICE_ATTR_RW(fan8_alarm, fan_alarm, 7);
+
+static struct attribute *fts_fan_attrs[] = {
+ &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_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_source.dev_attr.attr,
+ &sensor_dev_attr_fan2_source.dev_attr.attr,
+ &sensor_dev_attr_fan3_source.dev_attr.attr,
+ &sensor_dev_attr_fan4_source.dev_attr.attr,
+ &sensor_dev_attr_fan5_source.dev_attr.attr,
+ &sensor_dev_attr_fan6_source.dev_attr.attr,
+ &sensor_dev_attr_fan7_source.dev_attr.attr,
+ &sensor_dev_attr_fan8_source.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_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan7_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan8_alarm.dev_attr.attr,
+ NULL
+};
+
+/* Voltages */
+static SENSOR_DEVICE_ATTR_RO(in1_input, in_value, 0);
+static SENSOR_DEVICE_ATTR_RO(in2_input, in_value, 1);
+static SENSOR_DEVICE_ATTR_RO(in3_input, in_value, 2);
+static SENSOR_DEVICE_ATTR_RO(in4_input, in_value, 3);
+static struct attribute *fts_voltage_attrs[] = {
+ &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,
+ NULL
+};
+
+static const struct attribute_group fts_voltage_attr_group = {
+ .attrs = fts_voltage_attrs
+};
+
+static const struct attribute_group fts_temp_attr_group = {
+ .attrs = fts_temp_attrs
+};
+
+static const struct attribute_group fts_fan_attr_group = {
+ .attrs = fts_fan_attrs
+};
+
+static const struct attribute_group *fts_attr_groups[] = {
+ &fts_voltage_attr_group,
+ &fts_temp_attr_group,
+ &fts_fan_attr_group,
+ NULL
+};
+
+/*****************************************************************************/
+/* Module initialization / remove functions */
+/*****************************************************************************/
+static int fts_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int val;
+
+ /* 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;
+
+ /* Device Detect Regs must have 0x17 0x34 and 0x54 */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1);
+ if (val != 0x17)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2);
+ if (val != 0x34)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3);
+ if (val != 0x54)
+ return -ENODEV;
+
+ /*
+ * 0x10 == Baseboard Management Controller, 0x01 == Teutates
+ * Device ID Reg needs to be 0x11
+ */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
+ if (val != 0x11)
+ return -ENODEV;
+
+ strscpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
+ info->flags = 0;
+ return 0;
+}
+
+static void fts_remove(struct i2c_client *client)
+{
+ struct fts_data *data = dev_get_drvdata(&client->dev);
+
+ watchdog_unregister_device(&data->wdd);
+}
+
+static int fts_probe(struct i2c_client *client)
+{
+ u8 revision;
+ struct fts_data *data;
+ int err;
+ s8 deviceid;
+ struct device *hwmon_dev;
+
+ if (client->addr != 0x73)
+ return -ENODEV;
+
+ /* Baseboard Management Controller check */
+ deviceid = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
+ if (deviceid > 0 && (deviceid & 0xF0) == 0x10) {
+ switch (deviceid & 0x0F) {
+ case 0x01:
+ break;
+ default:
+ dev_dbg(&client->dev,
+ "No Baseboard Management Controller\n");
+ return -ENODEV;
+ }
+ } else {
+ dev_dbg(&client->dev, "No fujitsu board\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(&client->dev, sizeof(struct fts_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->update_lock);
+ mutex_init(&data->access_lock);
+ data->client = client;
+ dev_set_drvdata(&client->dev, data);
+
+ err = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
+ if (err < 0)
+ return err;
+ revision = err;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ "ftsteutates",
+ data,
+ fts_attr_groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ err = fts_watchdog_init(data);
+ if (err)
+ return err;
+
+ dev_info(&client->dev, "Detected FTS Teutates chip, revision: %d.%d\n",
+ (revision & 0xF0) >> 4, revision & 0x0F);
+ return 0;
+}
+
+/*****************************************************************************/
+/* Module Details */
+/*****************************************************************************/
+static struct i2c_driver fts_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "ftsteutates",
+ },
+ .id_table = fts_id,
+ .probe_new = fts_probe,
+ .remove = fts_remove,
+ .detect = fts_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(fts_driver);
+
+MODULE_AUTHOR("Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>");
+MODULE_DESCRIPTION("FTS Teutates driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c
new file mode 100644
index 000000000..36717b524
--- /dev/null
+++ b/drivers/hwmon/g760a.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * g760a - Driver for the Global Mixed-mode Technology Inc. G760A
+ * fan speed PWM controller chip
+ *
+ * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org>
+ *
+ * Complete datasheet is available at GMT's website:
+ * http://www.gmt.com.tw/product/datasheet/EDS-760A.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+enum g760a_regs {
+ G760A_REG_SET_CNT = 0x00,
+ G760A_REG_ACT_CNT = 0x01,
+ G760A_REG_FAN_STA = 0x02
+};
+
+#define G760A_REG_FAN_STA_RPM_OFF 0x1 /* +/-20% off */
+#define G760A_REG_FAN_STA_RPM_LOW 0x2 /* below 1920rpm */
+
+/* register data is read (and cached) at most once per second */
+#define G760A_UPDATE_INTERVAL (HZ)
+
+struct g760a_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+
+ /* board specific parameters */
+ u32 clk; /* default 32kHz */
+ u16 fan_div; /* default P=2 */
+
+ /* g760a register cache */
+ unsigned int valid:1;
+ unsigned long last_updated; /* In jiffies */
+
+ u8 set_cnt; /* PWM (period) count number; 0xff stops fan */
+ u8 act_cnt; /* formula: cnt = (CLK * 30)/(rpm * P) */
+ u8 fan_sta; /* bit 0: set when actual fan speed more than 20%
+ * outside requested fan speed
+ * bit 1: set when fan speed below 1920 rpm
+ */
+};
+
+#define G760A_DEFAULT_CLK 32768
+#define G760A_DEFAULT_FAN_DIV 2
+
+#define PWM_FROM_CNT(cnt) (0xff-(cnt))
+#define PWM_TO_CNT(pwm) (0xff-(pwm))
+
+static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div)
+{
+ return ((val == 0x00) ? 0 : ((clk*30)/(val*div)));
+}
+
+/* read/write wrappers */
+static int g760a_read_value(struct i2c_client *client, enum g760a_regs reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int g760a_write_value(struct i2c_client *client, enum g760a_regs reg,
+ u16 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/*
+ * sysfs attributes
+ */
+
+static struct g760a_data *g760a_update_client(struct device *dev)
+{
+ struct g760a_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + G760A_UPDATE_INTERVAL)
+ || !data->valid) {
+ dev_dbg(&client->dev, "Starting g760a update\n");
+
+ data->set_cnt = g760a_read_value(client, G760A_REG_SET_CNT);
+ data->act_cnt = g760a_read_value(client, G760A_REG_ACT_CNT);
+ data->fan_sta = g760a_read_value(client, G760A_REG_FAN_STA);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct g760a_data *data = g760a_update_client(dev);
+ unsigned int rpm = 0;
+
+ mutex_lock(&data->update_lock);
+ if (!(data->fan_sta & G760A_REG_FAN_STA_RPM_LOW))
+ rpm = rpm_from_cnt(data->act_cnt, data->clk, data->fan_div);
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t fan1_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct g760a_data *data = g760a_update_client(dev);
+
+ int fan_alarm = (data->fan_sta & G760A_REG_FAN_STA_RPM_OFF) ? 1 : 0;
+
+ return sprintf(buf, "%d\n", fan_alarm);
+}
+
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct g760a_data *data = g760a_update_client(dev);
+
+ return sprintf(buf, "%d\n", PWM_FROM_CNT(data->set_cnt));
+}
+
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct g760a_data *data = g760a_update_client(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->set_cnt = PWM_TO_CNT(clamp_val(val, 0, 255));
+ g760a_write_value(client, G760A_REG_SET_CNT, data->set_cnt);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR_RO(fan1_alarm);
+
+static struct attribute *g760a_attrs[] = {
+ &dev_attr_pwm1.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_alarm.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(g760a);
+
+/*
+ * new-style driver model code
+ */
+
+static int g760a_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct g760a_data *data;
+ struct device *hwmon_dev;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ data = devm_kzalloc(dev, sizeof(struct g760a_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* setup default configuration for now */
+ data->fan_div = G760A_DEFAULT_FAN_DIV;
+ data->clk = G760A_DEFAULT_CLK;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ g760a_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id g760a_id[] = {
+ { "g760a", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, g760a_id);
+
+static struct i2c_driver g760a_driver = {
+ .driver = {
+ .name = "g760a",
+ },
+ .probe_new = g760a_probe,
+ .id_table = g760a_id,
+};
+
+module_i2c_driver(g760a_driver);
+
+MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
+MODULE_DESCRIPTION("GMT G760A driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
new file mode 100644
index 000000000..64a0599b2
--- /dev/null
+++ b/drivers/hwmon/g762.c
@@ -0,0 +1,1090 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * g762 - Driver for the Global Mixed-mode Technology Inc. fan speed
+ * PWM controller chips from G762 family, i.e. G762 and G763
+ *
+ * Copyright (C) 2013, Arnaud EBALARD <arno@natisbad.org>
+ *
+ * This work is based on a basic version for 2.6.31 kernel developed
+ * by Olivier Mouchet for LaCie. Updates and correction have been
+ * performed to run on recent kernels. Additional features, like the
+ * ability to configure various characteristics via .dts file or
+ * board init file have been added. Detailed datasheet on which this
+ * development is based is available here:
+ *
+ * http://natisbad.org/NAS/refs/GMT_EDS-762_763-080710-0.2.pdf
+ *
+ * Headers from previous developments have been kept below:
+ *
+ * Copyright (c) 2009 LaCie
+ *
+ * Author: Olivier Mouchet <olivier.mouchet@gmail.com>
+ *
+ * based on g760a code written by Herbert Valerio Riedel <hvr@gnu.org>
+ * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org>
+ *
+ * g762: minimal datasheet available at:
+ * http://www.gmt.com.tw/product/datasheet/EDS-762_3.pdf
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/g762.h>
+
+#define DRVNAME "g762"
+
+static const struct i2c_device_id g762_id[] = {
+ { "g762", 0 },
+ { "g763", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, g762_id);
+
+enum g762_regs {
+ G762_REG_SET_CNT = 0x00,
+ G762_REG_ACT_CNT = 0x01,
+ G762_REG_FAN_STA = 0x02,
+ G762_REG_SET_OUT = 0x03,
+ G762_REG_FAN_CMD1 = 0x04,
+ G762_REG_FAN_CMD2 = 0x05,
+};
+
+/* Config register bits */
+#define G762_REG_FAN_CMD1_DET_FAN_FAIL 0x80 /* enable fan_fail signal */
+#define G762_REG_FAN_CMD1_DET_FAN_OOC 0x40 /* enable fan_out_of_control */
+#define G762_REG_FAN_CMD1_OUT_MODE 0x20 /* out mode: PWM or DC */
+#define G762_REG_FAN_CMD1_FAN_MODE 0x10 /* fan mode: closed/open-loop */
+#define G762_REG_FAN_CMD1_CLK_DIV_ID1 0x08 /* clock divisor value */
+#define G762_REG_FAN_CMD1_CLK_DIV_ID0 0x04
+#define G762_REG_FAN_CMD1_PWM_POLARITY 0x02 /* PWM polarity */
+#define G762_REG_FAN_CMD1_PULSE_PER_REV 0x01 /* pulse per fan revolution */
+
+#define G762_REG_FAN_CMD2_GEAR_MODE_1 0x08 /* fan gear mode */
+#define G762_REG_FAN_CMD2_GEAR_MODE_0 0x04
+#define G762_REG_FAN_CMD2_FAN_STARTV_1 0x02 /* fan startup voltage */
+#define G762_REG_FAN_CMD2_FAN_STARTV_0 0x01
+
+#define G762_REG_FAN_STA_FAIL 0x02 /* fan fail */
+#define G762_REG_FAN_STA_OOC 0x01 /* fan out of control */
+
+/* Config register values */
+#define G762_OUT_MODE_PWM 1
+#define G762_OUT_MODE_DC 0
+
+#define G762_FAN_MODE_CLOSED_LOOP 2
+#define G762_FAN_MODE_OPEN_LOOP 1
+
+#define G762_PWM_POLARITY_NEGATIVE 1
+#define G762_PWM_POLARITY_POSITIVE 0
+
+/* Register data is read (and cached) at most once per second. */
+#define G762_UPDATE_INTERVAL HZ
+
+/*
+ * Extract pulse count per fan revolution value (2 or 4) from given
+ * FAN_CMD1 register value.
+ */
+#define G762_PULSE_FROM_REG(reg) \
+ ((((reg) & G762_REG_FAN_CMD1_PULSE_PER_REV) + 1) << 1)
+
+/*
+ * Extract fan clock divisor (1, 2, 4 or 8) from given FAN_CMD1
+ * register value.
+ */
+#define G762_CLKDIV_FROM_REG(reg) \
+ (1 << (((reg) & (G762_REG_FAN_CMD1_CLK_DIV_ID0 | \
+ G762_REG_FAN_CMD1_CLK_DIV_ID1)) >> 2))
+
+/*
+ * Extract fan gear mode multiplier value (0, 2 or 4) from given
+ * FAN_CMD2 register value.
+ */
+#define G762_GEARMULT_FROM_REG(reg) \
+ (1 << (((reg) & (G762_REG_FAN_CMD2_GEAR_MODE_0 | \
+ G762_REG_FAN_CMD2_GEAR_MODE_1)) >> 2))
+
+struct g762_data {
+ struct i2c_client *client;
+ struct clk *clk;
+
+ /* update mutex */
+ struct mutex update_lock;
+
+ /* board specific parameters. */
+ u32 clk_freq;
+
+ /* g762 register cache */
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ u8 set_cnt; /* controls fan rotation speed in closed-loop mode */
+ u8 act_cnt; /* provides access to current fan RPM value */
+ u8 fan_sta; /* bit 0: set when actual fan speed is more than
+ * 25% outside requested fan speed
+ * bit 1: set when no transition occurs on fan
+ * pin for 0.7s
+ */
+ u8 set_out; /* controls fan rotation speed in open-loop mode */
+ u8 fan_cmd1; /* 0: FG_PLS_ID0 FG pulses count per revolution
+ * 0: 2 counts per revolution
+ * 1: 4 counts per revolution
+ * 1: PWM_POLARITY 1: negative_duty
+ * 0: positive_duty
+ * 2,3: [FG_CLOCK_ID0, FG_CLK_ID1]
+ * 00: Divide fan clock by 1
+ * 01: Divide fan clock by 2
+ * 10: Divide fan clock by 4
+ * 11: Divide fan clock by 8
+ * 4: FAN_MODE 1:closed-loop, 0:open-loop
+ * 5: OUT_MODE 1:PWM, 0:DC
+ * 6: DET_FAN_OOC enable "fan ooc" status
+ * 7: DET_FAN_FAIL enable "fan fail" status
+ */
+ u8 fan_cmd2; /* 0,1: FAN_STARTV 0,1,2,3 -> 0,32,64,96 dac_code
+ * 2,3: FG_GEAR_MODE
+ * 00: multiplier = 1
+ * 01: multiplier = 2
+ * 10: multiplier = 4
+ * 4: Mask ALERT# (g763 only)
+ */
+};
+
+/*
+ * Convert count value from fan controller register (FAN_SET_CNT) into fan
+ * speed RPM value. Note that the datasheet documents a basic formula;
+ * influence of additional parameters (fan clock divisor, fan gear mode)
+ * have been infered from examples in the datasheet and tests.
+ */
+static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p,
+ u8 clk_div, u8 gear_mult)
+{
+ if (cnt == 0xff) /* setting cnt to 255 stops the fan */
+ return 0;
+
+ return (clk_freq * 30 * gear_mult) / ((cnt ? cnt : 1) * p * clk_div);
+}
+
+/*
+ * Convert fan RPM value from sysfs into count value for fan controller
+ * register (FAN_SET_CNT).
+ */
+static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p,
+ u8 clk_div, u8 gear_mult)
+{
+ unsigned long f1 = clk_freq * 30 * gear_mult;
+ unsigned long f2 = p * clk_div;
+
+ if (!rpm) /* to stop the fan, set cnt to 255 */
+ return 0xff;
+
+ rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2);
+ return DIV_ROUND_CLOSEST(f1, rpm * f2);
+}
+
+/* helper to grab and cache data, at most one time per second */
+static struct g762_data *g762_update_client(struct device *dev)
+{
+ struct g762_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret = 0;
+
+ mutex_lock(&data->update_lock);
+ if (time_before(jiffies, data->last_updated + G762_UPDATE_INTERVAL) &&
+ likely(data->valid))
+ goto out;
+
+ ret = i2c_smbus_read_byte_data(client, G762_REG_SET_CNT);
+ if (ret < 0)
+ goto out;
+ data->set_cnt = ret;
+
+ ret = i2c_smbus_read_byte_data(client, G762_REG_ACT_CNT);
+ if (ret < 0)
+ goto out;
+ data->act_cnt = ret;
+
+ ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_STA);
+ if (ret < 0)
+ goto out;
+ data->fan_sta = ret;
+
+ ret = i2c_smbus_read_byte_data(client, G762_REG_SET_OUT);
+ if (ret < 0)
+ goto out;
+ data->set_out = ret;
+
+ ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_CMD1);
+ if (ret < 0)
+ goto out;
+ data->fan_cmd1 = ret;
+
+ ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_CMD2);
+ if (ret < 0)
+ goto out;
+ data->fan_cmd2 = ret;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ if (ret < 0) /* upon error, encode it in return value */
+ data = ERR_PTR(ret);
+
+ return data;
+}
+
+/* helpers for writing hardware parameters */
+
+/*
+ * Set input clock frequency received on CLK pin of the chip. Accepted values
+ * are between 0 and 0xffffff. If zero is given, then default frequency
+ * (32,768Hz) is used. Note that clock frequency is a characteristic of the
+ * system but an internal parameter, i.e. value is not passed to the device.
+ */
+static int do_set_clk_freq(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = dev_get_drvdata(dev);
+
+ if (val > 0xffffff)
+ return -EINVAL;
+ if (!val)
+ val = 32768;
+
+ data->clk_freq = val;
+
+ return 0;
+}
+
+/* Set pwm mode. Accepts either 0 (PWM mode) or 1 (DC mode) */
+static int do_set_pwm_mode(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case G762_OUT_MODE_PWM:
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_OUT_MODE;
+ break;
+ case G762_OUT_MODE_DC:
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_OUT_MODE;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
+ data->fan_cmd1);
+ data->valid = false;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/* Set fan clock divisor. Accepts either 1, 2, 4 or 8. */
+static int do_set_fan_div(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case 1:
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID0;
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID1;
+ break;
+ case 2:
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID0;
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID1;
+ break;
+ case 4:
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID0;
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID1;
+ break;
+ case 8:
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID0;
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
+ data->fan_cmd1);
+ data->valid = false;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/* Set fan gear mode. Accepts either 0, 1 or 2. */
+static int do_set_fan_gear_mode(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case 0:
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_0;
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_1;
+ break;
+ case 1:
+ data->fan_cmd2 |= G762_REG_FAN_CMD2_GEAR_MODE_0;
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_1;
+ break;
+ case 2:
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_0;
+ data->fan_cmd2 |= G762_REG_FAN_CMD2_GEAR_MODE_1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD2,
+ data->fan_cmd2);
+ data->valid = false;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/* Set number of fan pulses per revolution. Accepts either 2 or 4. */
+static int do_set_fan_pulses(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case 2:
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_PULSE_PER_REV;
+ break;
+ case 4:
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_PULSE_PER_REV;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
+ data->fan_cmd1);
+ data->valid = false;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/* Set fan mode. Accepts either 1 (open-loop) or 2 (closed-loop). */
+static int do_set_pwm_enable(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case G762_FAN_MODE_CLOSED_LOOP:
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_FAN_MODE;
+ break;
+ case G762_FAN_MODE_OPEN_LOOP:
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_FAN_MODE;
+ /*
+ * BUG FIX: if SET_CNT register value is 255 then, for some
+ * unknown reason, fan will not rotate as expected, no matter
+ * the value of SET_OUT (to be specific, this seems to happen
+ * only in PWM mode). To workaround this bug, we give SET_CNT
+ * value of 254 if it is 255 when switching to open-loop.
+ */
+ if (data->set_cnt == 0xff)
+ i2c_smbus_write_byte_data(data->client,
+ G762_REG_SET_CNT, 254);
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
+ data->fan_cmd1);
+ data->valid = false;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/* Set PWM polarity. Accepts either 0 (positive duty) or 1 (negative duty) */
+static int do_set_pwm_polarity(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case G762_PWM_POLARITY_POSITIVE:
+ data->fan_cmd1 &= ~G762_REG_FAN_CMD1_PWM_POLARITY;
+ break;
+ case G762_PWM_POLARITY_NEGATIVE:
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_PWM_POLARITY;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
+ data->fan_cmd1);
+ data->valid = false;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/*
+ * Set pwm value. Accepts values between 0 (stops the fan) and
+ * 255 (full speed). This only makes sense in open-loop mode.
+ */
+static int do_set_pwm(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ if (val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = i2c_smbus_write_byte_data(client, G762_REG_SET_OUT, val);
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/*
+ * Set fan RPM value. Can be called both in closed and open-loop mode
+ * but effect will only be seen after closed-loop mode is configured.
+ */
+static int do_set_fan_target(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ data->set_cnt = cnt_from_rpm(val, data->clk_freq,
+ G762_PULSE_FROM_REG(data->fan_cmd1),
+ G762_CLKDIV_FROM_REG(data->fan_cmd1),
+ G762_GEARMULT_FROM_REG(data->fan_cmd2));
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_SET_CNT,
+ data->set_cnt);
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/* Set fan startup voltage. Accepted values are either 0, 1, 2 or 3. */
+static int do_set_fan_startv(struct device *dev, unsigned long val)
+{
+ struct g762_data *data = g762_update_client(dev);
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case 0:
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_0;
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_1;
+ break;
+ case 1:
+ data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_0;
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_1;
+ break;
+ case 2:
+ data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_0;
+ data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_1;
+ break;
+ case 3:
+ data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_0;
+ data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD2,
+ data->fan_cmd2);
+ data->valid = false;
+ out:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/*
+ * Helper to import hardware characteristics from .dts file and push
+ * those to the chip.
+ */
+
+#ifdef CONFIG_OF
+static const struct of_device_id g762_dt_match[] = {
+ { .compatible = "gmt,g762" },
+ { .compatible = "gmt,g763" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, g762_dt_match);
+
+/*
+ * Grab clock (a required property), enable it, get (fixed) clock frequency
+ * and store it. Note: upon success, clock has been prepared and enabled; it
+ * must later be unprepared and disabled (e.g. during module unloading) by a
+ * call to g762_of_clock_disable(). Note that a reference to clock is kept
+ * in our private data structure to be used in this function.
+ */
+static void g762_of_clock_disable(void *data)
+{
+ struct g762_data *g762 = data;
+
+ clk_disable_unprepare(g762->clk);
+ clk_put(g762->clk);
+}
+
+static int g762_of_clock_enable(struct i2c_client *client)
+{
+ struct g762_data *data;
+ unsigned long clk_freq;
+ struct clk *clk;
+ int ret;
+
+ if (!client->dev.of_node)
+ return 0;
+
+ clk = of_clk_get(client->dev.of_node, 0);
+ if (IS_ERR(clk)) {
+ dev_err(&client->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable clock\n");
+ goto clk_put;
+ }
+
+ clk_freq = clk_get_rate(clk);
+ ret = do_set_clk_freq(&client->dev, clk_freq);
+ if (ret) {
+ dev_err(&client->dev, "invalid clock freq %lu\n", clk_freq);
+ goto clk_unprep;
+ }
+
+ data = i2c_get_clientdata(client);
+ data->clk = clk;
+
+ devm_add_action(&client->dev, g762_of_clock_disable, data);
+ return 0;
+
+ clk_unprep:
+ clk_disable_unprepare(clk);
+
+ clk_put:
+ clk_put(clk);
+
+ return ret;
+}
+
+static int g762_of_prop_import_one(struct i2c_client *client,
+ const char *pname,
+ int (*psetter)(struct device *dev,
+ unsigned long val))
+{
+ int ret;
+ u32 pval;
+
+ if (of_property_read_u32(client->dev.of_node, pname, &pval))
+ return 0;
+
+ dev_dbg(&client->dev, "found %s (%d)\n", pname, pval);
+ ret = (*psetter)(&client->dev, pval);
+ if (ret)
+ dev_err(&client->dev, "unable to set %s (%d)\n", pname, pval);
+
+ return ret;
+}
+
+static int g762_of_prop_import(struct i2c_client *client)
+{
+ int ret;
+
+ if (!client->dev.of_node)
+ return 0;
+
+ ret = g762_of_prop_import_one(client, "fan_gear_mode",
+ do_set_fan_gear_mode);
+ if (ret)
+ return ret;
+
+ ret = g762_of_prop_import_one(client, "pwm_polarity",
+ do_set_pwm_polarity);
+ if (ret)
+ return ret;
+
+ return g762_of_prop_import_one(client, "fan_startv",
+ do_set_fan_startv);
+}
+
+#else
+static int g762_of_prop_import(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int g762_of_clock_enable(struct i2c_client *client)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Helper to import hardware characteristics from .dts file and push
+ * those to the chip.
+ */
+
+static int g762_pdata_prop_import(struct i2c_client *client)
+{
+ struct g762_platform_data *pdata = dev_get_platdata(&client->dev);
+ int ret;
+
+ if (!pdata)
+ return 0;
+
+ ret = do_set_fan_gear_mode(&client->dev, pdata->fan_gear_mode);
+ if (ret)
+ return ret;
+
+ ret = do_set_pwm_polarity(&client->dev, pdata->pwm_polarity);
+ if (ret)
+ return ret;
+
+ ret = do_set_fan_startv(&client->dev, pdata->fan_startv);
+ if (ret)
+ return ret;
+
+ return do_set_clk_freq(&client->dev, pdata->clk_freq);
+}
+
+/*
+ * sysfs attributes
+ */
+
+/*
+ * Read function for fan1_input sysfs file. Return current fan RPM value, or
+ * 0 if fan is out of control.
+ */
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+ unsigned int rpm = 0;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ /* reverse logic: fan out of control reporting is enabled low */
+ if (data->fan_sta & G762_REG_FAN_STA_OOC) {
+ rpm = rpm_from_cnt(data->act_cnt, data->clk_freq,
+ G762_PULSE_FROM_REG(data->fan_cmd1),
+ G762_CLKDIV_FROM_REG(data->fan_cmd1),
+ G762_GEARMULT_FROM_REG(data->fan_cmd2));
+ }
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%u\n", rpm);
+}
+
+/*
+ * Read and write functions for pwm1_mode sysfs file. Get and set fan speed
+ * control mode i.e. PWM (1) or DC (0).
+ */
+static ssize_t pwm1_mode_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n",
+ !!(data->fan_cmd1 & G762_REG_FAN_CMD1_OUT_MODE));
+}
+
+static ssize_t pwm1_mode_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ ret = do_set_pwm_mode(dev, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+/*
+ * Read and write functions for fan1_div sysfs file. Get and set fan
+ * controller prescaler value
+ */
+static ssize_t fan1_div_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", G762_CLKDIV_FROM_REG(data->fan_cmd1));
+}
+
+static ssize_t fan1_div_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ ret = do_set_fan_div(dev, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+/*
+ * Read and write functions for fan1_pulses sysfs file. Get and set number
+ * of tachometer pulses per fan revolution.
+ */
+static ssize_t fan1_pulses_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", G762_PULSE_FROM_REG(data->fan_cmd1));
+}
+
+static ssize_t fan1_pulses_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ ret = do_set_fan_pulses(dev, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+/*
+ * Read and write functions for pwm1_enable. Get and set fan speed control mode
+ * (i.e. closed or open-loop).
+ *
+ * Following documentation about hwmon's sysfs interface, a pwm1_enable node
+ * should accept the following:
+ *
+ * 0 : no fan speed control (i.e. fan at full speed)
+ * 1 : manual fan speed control enabled (use pwm[1-*]) (open-loop)
+ * 2+: automatic fan speed control enabled (use fan[1-*]_target) (closed-loop)
+ *
+ * but we do not accept 0 as this mode is not natively supported by the chip
+ * and it is not emulated by g762 driver. -EINVAL is returned in this case.
+ */
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n",
+ (!!(data->fan_cmd1 & G762_REG_FAN_CMD1_FAN_MODE)) + 1);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ ret = do_set_pwm_enable(dev, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+/*
+ * Read and write functions for pwm1 sysfs file. Get and set pwm value
+ * (which affects fan speed) in open-loop mode. 0 stops the fan and 255
+ * makes it run at full speed.
+ */
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->set_out);
+}
+
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ ret = do_set_pwm(dev, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+/*
+ * Read and write function for fan1_target sysfs file. Get/set the fan speed in
+ * closed-loop mode. Speed is given as a RPM value; then the chip will regulate
+ * the fan speed using pulses from fan tachometer.
+ *
+ * Refer to rpm_from_cnt() implementation above to get info about count number
+ * calculation.
+ *
+ * Also note that due to rounding errors it is possible that you don't read
+ * back exactly the value you have set.
+ */
+static ssize_t fan1_target_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+ unsigned int rpm;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ rpm = rpm_from_cnt(data->set_cnt, data->clk_freq,
+ G762_PULSE_FROM_REG(data->fan_cmd1),
+ G762_CLKDIV_FROM_REG(data->fan_cmd1),
+ G762_GEARMULT_FROM_REG(data->fan_cmd2));
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%u\n", rpm);
+}
+
+static ssize_t fan1_target_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ ret = do_set_fan_target(dev, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+/* read function for fan1_fault sysfs file. */
+static ssize_t fan1_fault_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n", !!(data->fan_sta & G762_REG_FAN_STA_FAIL));
+}
+
+/*
+ * read function for fan1_alarm sysfs file. Note that OOC condition is
+ * enabled low
+ */
+static ssize_t fan1_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n", !(data->fan_sta & G762_REG_FAN_STA_OOC));
+}
+
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RW(pwm1_mode);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR_RO(fan1_alarm);
+static DEVICE_ATTR_RO(fan1_fault);
+static DEVICE_ATTR_RW(fan1_target);
+static DEVICE_ATTR_RW(fan1_div);
+static DEVICE_ATTR_RW(fan1_pulses);
+
+/* Driver data */
+static struct attribute *g762_attrs[] = {
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_alarm.attr,
+ &dev_attr_fan1_fault.attr,
+ &dev_attr_fan1_target.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_pulses.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_mode.attr,
+ &dev_attr_pwm1_enable.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(g762);
+
+/*
+ * Enable both fan failure detection and fan out of control protection. The
+ * function does not protect change/access to data structure; it must thus
+ * only be called during initialization.
+ */
+static inline int g762_fan_init(struct device *dev)
+{
+ struct g762_data *data = g762_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_FAIL;
+ data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_OOC;
+ data->valid = false;
+
+ return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
+ data->fan_cmd1);
+}
+
+static int g762_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct g762_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct g762_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Enable fan failure detection and fan out of control protection */
+ ret = g762_fan_init(dev);
+ if (ret)
+ return ret;
+
+ /* Get configuration via DT ... */
+ ret = g762_of_clock_enable(client);
+ if (ret)
+ return ret;
+ ret = g762_of_prop_import(client);
+ if (ret)
+ return ret;
+ /* ... or platform_data */
+ ret = g762_pdata_prop_import(client);
+ if (ret)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, g762_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct i2c_driver g762_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .of_match_table = of_match_ptr(g762_dt_match),
+ },
+ .probe_new = g762_probe,
+ .id_table = g762_id,
+};
+
+module_i2c_driver(g762_driver);
+
+MODULE_AUTHOR("Arnaud EBALARD <arno@natisbad.org>");
+MODULE_DESCRIPTION("GMT G762/G763 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
new file mode 100644
index 000000000..95286c40f
--- /dev/null
+++ b/drivers/hwmon/gl518sm.c
@@ -0,0 +1,667 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
+ * Kyosti Malkki <kmalkki@cc.hut.fi>
+ * Copyright (C) 2004 Hong-Gunn Chew <hglinux@gunnet.org> and
+ * Jean Delvare <jdelvare@suse.de>
+ *
+ * Ported to Linux 2.6 by Hong-Gunn Chew with the help of Jean Delvare
+ * and advice of Greg Kroah-Hartman.
+ *
+ * Notes about the port:
+ * Release 0x00 of the GL518SM chipset doesn't support reading of in0,
+ * in1 nor in2. The original driver had an ugly workaround to get them
+ * anyway (changing limits and watching alarms trigger and wear off).
+ * We did not keep that part of the original driver in the Linux 2.6
+ * version, since it was making the driver significantly more complex
+ * with no real benefit.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
+
+enum chips { gl518sm_r00, gl518sm_r80 };
+
+/* Many GL518 constants specified below */
+
+/* The GL518 registers */
+#define GL518_REG_CHIP_ID 0x00
+#define GL518_REG_REVISION 0x01
+#define GL518_REG_VENDOR_ID 0x02
+#define GL518_REG_CONF 0x03
+#define GL518_REG_TEMP_IN 0x04
+#define GL518_REG_TEMP_MAX 0x05
+#define GL518_REG_TEMP_HYST 0x06
+#define GL518_REG_FAN_COUNT 0x07
+#define GL518_REG_FAN_LIMIT 0x08
+#define GL518_REG_VIN1_LIMIT 0x09
+#define GL518_REG_VIN2_LIMIT 0x0a
+#define GL518_REG_VIN3_LIMIT 0x0b
+#define GL518_REG_VDD_LIMIT 0x0c
+#define GL518_REG_VIN3 0x0d
+#define GL518_REG_MISC 0x0f
+#define GL518_REG_ALARM 0x10
+#define GL518_REG_MASK 0x11
+#define GL518_REG_INT 0x12
+#define GL518_REG_VIN2 0x13
+#define GL518_REG_VIN1 0x14
+#define GL518_REG_VDD 0x15
+
+
+/*
+ * Conversions. Rounding and limit checking is only done on the TO_REG
+ * variants. Note that you should be a bit careful with which arguments
+ * these macros are called: arguments may be evaluated more than once.
+ * Fixing this is just not worth it.
+ */
+
+#define RAW_FROM_REG(val) val
+
+#define BOOL_FROM_REG(val) ((val) ? 0 : 1)
+#define BOOL_TO_REG(val) ((val) ? 0 : 1)
+
+#define TEMP_CLAMP(val) clamp_val(val, -119000, 136000)
+#define TEMP_TO_REG(val) (DIV_ROUND_CLOSEST(TEMP_CLAMP(val), 1000) + 119)
+#define TEMP_FROM_REG(val) (((val) - 119) * 1000)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ long rpmdiv;
+ if (rpm == 0)
+ return 0;
+ rpmdiv = clamp_val(rpm, 1, 960000) * div;
+ return clamp_val((480000 + rpmdiv / 2) / rpmdiv, 1, 255);
+}
+#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) * (div))))
+
+#define IN_CLAMP(val) clamp_val(val, 0, 255 * 19)
+#define IN_TO_REG(val) DIV_ROUND_CLOSEST(IN_CLAMP(val), 19)
+#define IN_FROM_REG(val) ((val) * 19)
+
+#define VDD_CLAMP(val) clamp_val(val, 0, 255 * 95 / 4)
+#define VDD_TO_REG(val) DIV_ROUND_CLOSEST(VDD_CLAMP(val) * 4, 95)
+#define VDD_FROM_REG(val) DIV_ROUND_CLOSEST((val) * 95, 4)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+#define BEEP_MASK_TO_REG(val) ((val) & 0x7f & data->alarm_mask)
+#define BEEP_MASK_FROM_REG(val) ((val) & 0x7f)
+
+/* Each client has this additional data */
+struct gl518_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+ enum chips type;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 voltage_in[4]; /* Register values; [0] = VDD */
+ u8 voltage_min[4]; /* Register values; [0] = VDD */
+ u8 voltage_max[4]; /* Register values; [0] = VDD */
+ u8 fan_in[2];
+ u8 fan_min[2];
+ u8 fan_div[2]; /* Register encoding, shifted right */
+ u8 fan_auto1; /* Boolean */
+ u8 temp_in; /* Register values */
+ u8 temp_max; /* Register values */
+ u8 temp_hyst; /* Register values */
+ u8 alarms; /* Register value */
+ u8 alarm_mask;
+ u8 beep_mask; /* Register value */
+ u8 beep_enable; /* Boolean */
+};
+
+/*
+ * Registers 0x07 to 0x0c are word-sized, others are byte-sized
+ * GL518 uses a high-byte first convention, which is exactly opposite to
+ * the SMBus standard.
+ */
+static int gl518_read_value(struct i2c_client *client, u8 reg)
+{
+ if ((reg >= 0x07) && (reg <= 0x0c))
+ return i2c_smbus_read_word_swapped(client, reg);
+ else
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+ if ((reg >= 0x07) && (reg <= 0x0c))
+ return i2c_smbus_write_word_swapped(client, reg, value);
+ else
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static struct gl518_data *gl518_update_device(struct device *dev)
+{
+ struct gl518_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int val;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ dev_dbg(&client->dev, "Starting gl518 update\n");
+
+ data->alarms = gl518_read_value(client, GL518_REG_INT);
+ data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
+
+ val = gl518_read_value(client, GL518_REG_VDD_LIMIT);
+ data->voltage_min[0] = val & 0xff;
+ data->voltage_max[0] = (val >> 8) & 0xff;
+ val = gl518_read_value(client, GL518_REG_VIN1_LIMIT);
+ data->voltage_min[1] = val & 0xff;
+ data->voltage_max[1] = (val >> 8) & 0xff;
+ val = gl518_read_value(client, GL518_REG_VIN2_LIMIT);
+ data->voltage_min[2] = val & 0xff;
+ data->voltage_max[2] = (val >> 8) & 0xff;
+ val = gl518_read_value(client, GL518_REG_VIN3_LIMIT);
+ data->voltage_min[3] = val & 0xff;
+ data->voltage_max[3] = (val >> 8) & 0xff;
+
+ val = gl518_read_value(client, GL518_REG_FAN_COUNT);
+ data->fan_in[0] = (val >> 8) & 0xff;
+ data->fan_in[1] = val & 0xff;
+
+ val = gl518_read_value(client, GL518_REG_FAN_LIMIT);
+ data->fan_min[0] = (val >> 8) & 0xff;
+ data->fan_min[1] = val & 0xff;
+
+ data->temp_in = gl518_read_value(client, GL518_REG_TEMP_IN);
+ data->temp_max =
+ gl518_read_value(client, GL518_REG_TEMP_MAX);
+ data->temp_hyst =
+ gl518_read_value(client, GL518_REG_TEMP_HYST);
+
+ val = gl518_read_value(client, GL518_REG_MISC);
+ data->fan_div[0] = (val >> 6) & 0x03;
+ data->fan_div[1] = (val >> 4) & 0x03;
+ data->fan_auto1 = (val >> 3) & 0x01;
+
+ data->alarms &= data->alarm_mask;
+
+ val = gl518_read_value(client, GL518_REG_CONF);
+ data->beep_enable = (val >> 2) & 1;
+
+ if (data->type != gl518sm_r00) {
+ data->voltage_in[0] =
+ gl518_read_value(client, GL518_REG_VDD);
+ data->voltage_in[1] =
+ gl518_read_value(client, GL518_REG_VIN1);
+ data->voltage_in[2] =
+ gl518_read_value(client, GL518_REG_VIN2);
+ }
+ data->voltage_in[3] =
+ gl518_read_value(client, GL518_REG_VIN3);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+#define show(type, suffix, value) \
+static ssize_t show_##suffix(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct gl518_data *data = gl518_update_device(dev); \
+ return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \
+}
+
+show(TEMP, temp_input1, temp_in);
+show(TEMP, temp_max1, temp_max);
+show(TEMP, temp_hyst1, temp_hyst);
+show(BOOL, fan_auto1, fan_auto1);
+show(VDD, in_input0, voltage_in[0]);
+show(IN, in_input1, voltage_in[1]);
+show(IN, in_input2, voltage_in[2]);
+show(IN, in_input3, voltage_in[3]);
+show(VDD, in_min0, voltage_min[0]);
+show(IN, in_min1, voltage_min[1]);
+show(IN, in_min2, voltage_min[2]);
+show(IN, in_min3, voltage_min[3]);
+show(VDD, in_max0, voltage_max[0]);
+show(IN, in_max1, voltage_max[1]);
+show(IN, in_max2, voltage_max[2]);
+show(IN, in_max3, voltage_max[3]);
+show(RAW, alarms, alarms);
+show(BOOL, beep_enable, beep_enable);
+show(BEEP_MASK, beep_mask, beep_mask);
+
+static ssize_t fan_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct gl518_data *data = gl518_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_in[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct gl518_data *data = gl518_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct gl518_data *data = gl518_update_device(dev);
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
+}
+
+#define set(type, suffix, value, reg) \
+static ssize_t set_##suffix(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct gl518_data *data = dev_get_drvdata(dev); \
+ struct i2c_client *client = data->client; \
+ long val; \
+ int err = kstrtol(buf, 10, &val); \
+ if (err) \
+ return err; \
+ \
+ mutex_lock(&data->update_lock); \
+ data->value = type##_TO_REG(val); \
+ gl518_write_value(client, reg, data->value); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+#define set_bits(type, suffix, value, reg, mask, shift) \
+static ssize_t set_##suffix(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct gl518_data *data = dev_get_drvdata(dev); \
+ struct i2c_client *client = data->client; \
+ int regvalue; \
+ unsigned long val; \
+ int err = kstrtoul(buf, 10, &val); \
+ if (err) \
+ return err; \
+ \
+ mutex_lock(&data->update_lock); \
+ regvalue = gl518_read_value(client, reg); \
+ data->value = type##_TO_REG(val); \
+ regvalue = (regvalue & ~mask) | (data->value << shift); \
+ gl518_write_value(client, reg, regvalue); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+#define set_low(type, suffix, value, reg) \
+ set_bits(type, suffix, value, reg, 0x00ff, 0)
+#define set_high(type, suffix, value, reg) \
+ set_bits(type, suffix, value, reg, 0xff00, 8)
+
+set(TEMP, temp_max1, temp_max, GL518_REG_TEMP_MAX);
+set(TEMP, temp_hyst1, temp_hyst, GL518_REG_TEMP_HYST);
+set_bits(BOOL, fan_auto1, fan_auto1, GL518_REG_MISC, 0x08, 3);
+set_low(VDD, in_min0, voltage_min[0], GL518_REG_VDD_LIMIT);
+set_low(IN, in_min1, voltage_min[1], GL518_REG_VIN1_LIMIT);
+set_low(IN, in_min2, voltage_min[2], GL518_REG_VIN2_LIMIT);
+set_low(IN, in_min3, voltage_min[3], GL518_REG_VIN3_LIMIT);
+set_high(VDD, in_max0, voltage_max[0], GL518_REG_VDD_LIMIT);
+set_high(IN, in_max1, voltage_max[1], GL518_REG_VIN1_LIMIT);
+set_high(IN, in_max2, voltage_max[2], GL518_REG_VIN2_LIMIT);
+set_high(IN, in_max3, voltage_max[3], GL518_REG_VIN3_LIMIT);
+set_bits(BOOL, beep_enable, beep_enable, GL518_REG_CONF, 0x04, 2);
+set(BEEP_MASK, beep_mask, beep_mask, GL518_REG_ALARM);
+
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct gl518_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ int regvalue;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT);
+ data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
+ regvalue = (regvalue & (0xff << (8 * nr)))
+ | (data->fan_min[nr] << (8 * (1 - nr)));
+ gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue);
+
+ data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
+ if (data->fan_min[nr] == 0)
+ data->alarm_mask &= ~(0x20 << nr);
+ else
+ data->alarm_mask |= (0x20 << nr);
+ data->beep_mask &= data->alarm_mask;
+ gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct gl518_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = to_sensor_dev_attr(attr)->index;
+ int regvalue;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ switch (val) {
+ case 1:
+ val = 0;
+ break;
+ case 2:
+ val = 1;
+ break;
+ case 4:
+ val = 2;
+ break;
+ case 8:
+ val = 3;
+ break;
+ default:
+ dev_err(dev,
+ "Invalid fan clock divider %lu, choose one of 1, 2, 4 or 8\n",
+ val);
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ regvalue = gl518_read_value(client, GL518_REG_MISC);
+ data->fan_div[nr] = val;
+ regvalue = (regvalue & ~(0xc0 >> (2 * nr)))
+ | (data->fan_div[nr] << (6 - 2 * nr));
+ gl518_write_value(client, GL518_REG_MISC, regvalue);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR(temp1_input, 0444, show_temp_input1, NULL);
+static DEVICE_ATTR(temp1_max, 0644, show_temp_max1, set_temp_max1);
+static DEVICE_ATTR(temp1_max_hyst, 0644,
+ show_temp_hyst1, set_temp_hyst1);
+static DEVICE_ATTR(fan1_auto, 0644, show_fan_auto1, set_fan_auto1);
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1);
+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(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+static DEVICE_ATTR(in0_input, 0444, show_in_input0, NULL);
+static DEVICE_ATTR(in1_input, 0444, show_in_input1, NULL);
+static DEVICE_ATTR(in2_input, 0444, show_in_input2, NULL);
+static DEVICE_ATTR(in3_input, 0444, show_in_input3, NULL);
+static DEVICE_ATTR(in0_min, 0644, show_in_min0, set_in_min0);
+static DEVICE_ATTR(in1_min, 0644, show_in_min1, set_in_min1);
+static DEVICE_ATTR(in2_min, 0644, show_in_min2, set_in_min2);
+static DEVICE_ATTR(in3_min, 0644, show_in_min3, set_in_min3);
+static DEVICE_ATTR(in0_max, 0644, show_in_max0, set_in_max0);
+static DEVICE_ATTR(in1_max, 0644, show_in_max1, set_in_max1);
+static DEVICE_ATTR(in2_max, 0644, show_in_max2, set_in_max2);
+static DEVICE_ATTR(in3_max, 0644, show_in_max3, set_in_max3);
+static DEVICE_ATTR(alarms, 0444, show_alarms, NULL);
+static DEVICE_ATTR(beep_enable, 0644,
+ show_beep_enable, set_beep_enable);
+static DEVICE_ATTR(beep_mask, 0644,
+ show_beep_mask, set_beep_mask);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct gl518_data *data = gl518_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(temp1_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 6);
+
+static ssize_t beep_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct gl518_data *data = gl518_update_device(dev);
+ return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1);
+}
+
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gl518_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ unsigned long bit;
+ int err;
+
+ err = kstrtoul(buf, 10, &bit);
+ if (err)
+ return err;
+
+ if (bit & ~1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
+ if (bit)
+ data->beep_mask |= (1 << bitnr);
+ else
+ data->beep_mask &= ~(1 << bitnr);
+ gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(in0_beep, beep, 0);
+static SENSOR_DEVICE_ATTR_RW(in1_beep, beep, 1);
+static SENSOR_DEVICE_ATTR_RW(in2_beep, beep, 2);
+static SENSOR_DEVICE_ATTR_RW(in3_beep, beep, 3);
+static SENSOR_DEVICE_ATTR_RW(temp1_beep, beep, 4);
+static SENSOR_DEVICE_ATTR_RW(fan1_beep, beep, 5);
+static SENSOR_DEVICE_ATTR_RW(fan2_beep, beep, 6);
+
+static struct attribute *gl518_attributes[] = {
+ &dev_attr_in3_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_beep.dev_attr.attr,
+ &sensor_dev_attr_in1_beep.dev_attr.attr,
+ &sensor_dev_attr_in2_beep.dev_attr.attr,
+ &sensor_dev_attr_in3_beep.dev_attr.attr,
+
+ &dev_attr_fan1_auto.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_beep.dev_attr.attr,
+ &sensor_dev_attr_fan2_beep.dev_attr.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_beep.dev_attr.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+ NULL
+};
+
+static const struct attribute_group gl518_group = {
+ .attrs = gl518_attributes,
+};
+
+static struct attribute *gl518_attributes_r80[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ NULL
+};
+
+static const struct attribute_group gl518_group_r80 = {
+ .attrs = gl518_attributes_r80,
+};
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int rev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ /* Now, we do the remaining detection. */
+ if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80)
+ || (gl518_read_value(client, GL518_REG_CONF) & 0x80))
+ return -ENODEV;
+
+ /* Determine the chip type. */
+ rev = gl518_read_value(client, GL518_REG_REVISION);
+ if (rev != 0x00 && rev != 0x80)
+ return -ENODEV;
+
+ strscpy(info->type, "gl518sm", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+/*
+ * Called when we have found a new GL518SM.
+ * Note that we preserve D4:NoFan2 and D2:beep_enable.
+ */
+static void gl518_init_client(struct i2c_client *client)
+{
+ /* Make sure we leave D7:Reset untouched */
+ u8 regvalue = gl518_read_value(client, GL518_REG_CONF) & 0x7f;
+
+ /* Comparator mode (D3=0), standby mode (D6=0) */
+ gl518_write_value(client, GL518_REG_CONF, (regvalue &= 0x37));
+
+ /* Never interrupts */
+ gl518_write_value(client, GL518_REG_MASK, 0x00);
+
+ /* Clear status register (D5=1), start (D6=1) */
+ gl518_write_value(client, GL518_REG_CONF, 0x20 | regvalue);
+ gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue);
+}
+
+static int gl518_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct gl518_data *data;
+ int revision;
+
+ data = devm_kzalloc(dev, sizeof(struct gl518_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ revision = gl518_read_value(client, GL518_REG_REVISION);
+ data->type = revision == 0x80 ? gl518sm_r80 : gl518sm_r00;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the GL518SM chip */
+ data->alarm_mask = 0xff;
+ gl518_init_client(client);
+
+ /* sysfs hooks */
+ data->groups[0] = &gl518_group;
+ if (data->type == gl518sm_r80)
+ data->groups[1] = &gl518_group_r80;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id gl518_id[] = {
+ { "gl518sm", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, gl518_id);
+
+static struct i2c_driver gl518_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "gl518sm",
+ },
+ .probe_new = gl518_probe,
+ .id_table = gl518_id,
+ .detect = gl518_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(gl518_driver);
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+ "Kyosti Malkki <kmalkki@cc.hut.fi> and "
+ "Hong-Gunn Chew <hglinux@gunnet.org>");
+MODULE_DESCRIPTION("GL518SM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
new file mode 100644
index 000000000..394da4ac9
--- /dev/null
+++ b/drivers/hwmon/gl520sm.c
@@ -0,0 +1,910 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
+ * Kyösti Mälkki <kmalkki@cc.hut.fi>
+ * Copyright (c) 2005 Maarten Deprez <maartendeprez@users.sourceforge.net>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.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/sysfs.h>
+
+/* Type of the extra sensor */
+static unsigned short extra_sensor_type;
+module_param(extra_sensor_type, ushort, 0);
+MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=temperature, 2=voltage)");
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
+
+/*
+ * Many GL520 constants specified below
+ * One of the inputs can be configured as either temp or voltage.
+ * That's why _TEMP2 and _IN4 access the same register
+ */
+
+/* The GL520 registers */
+#define GL520_REG_CHIP_ID 0x00
+#define GL520_REG_REVISION 0x01
+#define GL520_REG_CONF 0x03
+#define GL520_REG_MASK 0x11
+
+#define GL520_REG_VID_INPUT 0x02
+
+static const u8 GL520_REG_IN_INPUT[] = { 0x15, 0x14, 0x13, 0x0d, 0x0e };
+static const u8 GL520_REG_IN_LIMIT[] = { 0x0c, 0x09, 0x0a, 0x0b };
+static const u8 GL520_REG_IN_MIN[] = { 0x0c, 0x09, 0x0a, 0x0b, 0x18 };
+static const u8 GL520_REG_IN_MAX[] = { 0x0c, 0x09, 0x0a, 0x0b, 0x17 };
+
+static const u8 GL520_REG_TEMP_INPUT[] = { 0x04, 0x0e };
+static const u8 GL520_REG_TEMP_MAX[] = { 0x05, 0x17 };
+static const u8 GL520_REG_TEMP_MAX_HYST[] = { 0x06, 0x18 };
+
+#define GL520_REG_FAN_INPUT 0x07
+#define GL520_REG_FAN_MIN 0x08
+#define GL520_REG_FAN_DIV 0x0f
+#define GL520_REG_FAN_OFF GL520_REG_FAN_DIV
+
+#define GL520_REG_ALARMS 0x12
+#define GL520_REG_BEEP_MASK 0x10
+#define GL520_REG_BEEP_ENABLE GL520_REG_CONF
+
+/* Client data */
+struct gl520_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+ struct mutex update_lock;
+ bool valid; /* false until the following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ u8 vid;
+ u8 vrm;
+ u8 in_input[5]; /* [0] = VVD */
+ u8 in_min[5]; /* [0] = VDD */
+ u8 in_max[5]; /* [0] = VDD */
+ u8 fan_input[2];
+ u8 fan_min[2];
+ u8 fan_div[2];
+ u8 fan_off;
+ u8 temp_input[2];
+ u8 temp_max[2];
+ u8 temp_max_hyst[2];
+ u8 alarms;
+ u8 beep_enable;
+ u8 beep_mask;
+ u8 alarm_mask;
+ u8 two_temps;
+};
+
+/*
+ * Registers 0x07 to 0x0c are word-sized, others are byte-sized
+ * GL520 uses a high-byte first convention
+ */
+static int gl520_read_value(struct i2c_client *client, u8 reg)
+{
+ if ((reg >= 0x07) && (reg <= 0x0c))
+ return i2c_smbus_read_word_swapped(client, reg);
+ else
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+ if ((reg >= 0x07) && (reg <= 0x0c))
+ return i2c_smbus_write_word_swapped(client, reg, value);
+ else
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static struct gl520_data *gl520_update_device(struct device *dev)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int val, i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
+
+ dev_dbg(&client->dev, "Starting gl520sm update\n");
+
+ data->alarms = gl520_read_value(client, GL520_REG_ALARMS);
+ data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
+ data->vid = gl520_read_value(client,
+ GL520_REG_VID_INPUT) & 0x1f;
+
+ for (i = 0; i < 4; i++) {
+ data->in_input[i] = gl520_read_value(client,
+ GL520_REG_IN_INPUT[i]);
+ val = gl520_read_value(client, GL520_REG_IN_LIMIT[i]);
+ data->in_min[i] = val & 0xff;
+ data->in_max[i] = (val >> 8) & 0xff;
+ }
+
+ val = gl520_read_value(client, GL520_REG_FAN_INPUT);
+ data->fan_input[0] = (val >> 8) & 0xff;
+ data->fan_input[1] = val & 0xff;
+
+ val = gl520_read_value(client, GL520_REG_FAN_MIN);
+ data->fan_min[0] = (val >> 8) & 0xff;
+ data->fan_min[1] = val & 0xff;
+
+ data->temp_input[0] = gl520_read_value(client,
+ GL520_REG_TEMP_INPUT[0]);
+ data->temp_max[0] = gl520_read_value(client,
+ GL520_REG_TEMP_MAX[0]);
+ data->temp_max_hyst[0] = gl520_read_value(client,
+ GL520_REG_TEMP_MAX_HYST[0]);
+
+ val = gl520_read_value(client, GL520_REG_FAN_DIV);
+ data->fan_div[0] = (val >> 6) & 0x03;
+ data->fan_div[1] = (val >> 4) & 0x03;
+ data->fan_off = (val >> 2) & 0x01;
+
+ data->alarms &= data->alarm_mask;
+
+ val = gl520_read_value(client, GL520_REG_CONF);
+ data->beep_enable = !((val >> 2) & 1);
+
+ /* Temp1 and Vin4 are the same input */
+ if (data->two_temps) {
+ data->temp_input[1] = gl520_read_value(client,
+ GL520_REG_TEMP_INPUT[1]);
+ data->temp_max[1] = gl520_read_value(client,
+ GL520_REG_TEMP_MAX[1]);
+ data->temp_max_hyst[1] = gl520_read_value(client,
+ GL520_REG_TEMP_MAX_HYST[1]);
+ } else {
+ data->in_input[4] = gl520_read_value(client,
+ GL520_REG_IN_INPUT[4]);
+ data->in_min[4] = gl520_read_value(client,
+ GL520_REG_IN_MIN[4]);
+ data->in_max[4] = gl520_read_value(client,
+ GL520_REG_IN_MAX[4]);
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gl520_data *data = gl520_update_device(dev);
+ return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR_RO(cpu0_vid);
+
+#define VDD_FROM_REG(val) DIV_ROUND_CLOSEST((val) * 95, 4)
+#define VDD_CLAMP(val) clamp_val(val, 0, 255 * 95 / 4)
+#define VDD_TO_REG(val) DIV_ROUND_CLOSEST(VDD_CLAMP(val) * 4, 95)
+
+#define IN_FROM_REG(val) ((val) * 19)
+#define IN_CLAMP(val) clamp_val(val, 0, 255 * 19)
+#define IN_TO_REG(val) DIV_ROUND_CLOSEST(IN_CLAMP(val), 19)
+
+static ssize_t in_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+ u8 r = data->in_input[n];
+
+ if (n == 0)
+ return sprintf(buf, "%d\n", VDD_FROM_REG(r));
+ else
+ return sprintf(buf, "%d\n", IN_FROM_REG(r));
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+ u8 r = data->in_min[n];
+
+ if (n == 0)
+ return sprintf(buf, "%d\n", VDD_FROM_REG(r));
+ else
+ return sprintf(buf, "%d\n", IN_FROM_REG(r));
+}
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+ u8 r = data->in_max[n];
+
+ if (n == 0)
+ return sprintf(buf, "%d\n", VDD_FROM_REG(r));
+ else
+ return sprintf(buf, "%d\n", IN_FROM_REG(r));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int n = to_sensor_dev_attr(attr)->index;
+ u8 r;
+ long v;
+ int err;
+
+ err = kstrtol(buf, 10, &v);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ if (n == 0)
+ r = VDD_TO_REG(v);
+ else
+ r = IN_TO_REG(v);
+
+ data->in_min[n] = r;
+
+ if (n < 4)
+ gl520_write_value(client, GL520_REG_IN_MIN[n],
+ (gl520_read_value(client, GL520_REG_IN_MIN[n])
+ & ~0xff) | r);
+ else
+ gl520_write_value(client, GL520_REG_IN_MIN[n], r);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int n = to_sensor_dev_attr(attr)->index;
+ u8 r;
+ long v;
+ int err;
+
+ err = kstrtol(buf, 10, &v);
+ if (err)
+ return err;
+
+ if (n == 0)
+ r = VDD_TO_REG(v);
+ else
+ r = IN_TO_REG(v);
+
+ mutex_lock(&data->update_lock);
+
+ data->in_max[n] = r;
+
+ if (n < 4)
+ gl520_write_value(client, GL520_REG_IN_MAX[n],
+ (gl520_read_value(client, GL520_REG_IN_MAX[n])
+ & ~0xff00) | (r << 8));
+ else
+ gl520_write_value(client, GL520_REG_IN_MAX[n], r);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(in0_input, in_input, 0);
+static SENSOR_DEVICE_ATTR_RO(in1_input, in_input, 1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, in_input, 2);
+static SENSOR_DEVICE_ATTR_RO(in3_input, in_input, 3);
+static SENSOR_DEVICE_ATTR_RO(in4_input, in_input, 4);
+static SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0);
+static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1);
+static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2);
+static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3);
+static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4);
+static SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0);
+static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1);
+static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2);
+static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3);
+static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4);
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) << (div))))
+
+#define FAN_BASE(div) (480000 >> (div))
+#define FAN_CLAMP(val, div) clamp_val(val, FAN_BASE(div) / 255, \
+ FAN_BASE(div))
+#define FAN_TO_REG(val, div) ((val) == 0 ? 0 : \
+ DIV_ROUND_CLOSEST(480000, \
+ FAN_CLAMP(val, div) << (div)))
+
+static ssize_t fan_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_input[n],
+ data->fan_div[n]));
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[n],
+ data->fan_div[n]));
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[n]));
+}
+
+static ssize_t fan1_off_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gl520_data *data = gl520_update_device(dev);
+ return sprintf(buf, "%d\n", data->fan_off);
+}
+
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int n = to_sensor_dev_attr(attr)->index;
+ u8 r;
+ unsigned long v;
+ int err;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ r = FAN_TO_REG(v, data->fan_div[n]);
+ data->fan_min[n] = r;
+
+ if (n == 0)
+ gl520_write_value(client, GL520_REG_FAN_MIN,
+ (gl520_read_value(client, GL520_REG_FAN_MIN)
+ & ~0xff00) | (r << 8));
+ else
+ gl520_write_value(client, GL520_REG_FAN_MIN,
+ (gl520_read_value(client, GL520_REG_FAN_MIN)
+ & ~0xff) | r);
+
+ data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
+ if (data->fan_min[n] == 0)
+ data->alarm_mask &= (n == 0) ? ~0x20 : ~0x40;
+ else
+ data->alarm_mask |= (n == 0) ? 0x20 : 0x40;
+ data->beep_mask &= data->alarm_mask;
+ gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int n = to_sensor_dev_attr(attr)->index;
+ u8 r;
+ unsigned long v;
+ int err;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err)
+ return err;
+
+ switch (v) {
+ case 1:
+ r = 0;
+ break;
+ case 2:
+ r = 1;
+ break;
+ case 4:
+ r = 2;
+ break;
+ case 8:
+ r = 3;
+ break;
+ default:
+ dev_err(&client->dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v);
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->fan_div[n] = r;
+
+ if (n == 0)
+ gl520_write_value(client, GL520_REG_FAN_DIV,
+ (gl520_read_value(client, GL520_REG_FAN_DIV)
+ & ~0xc0) | (r << 6));
+ else
+ gl520_write_value(client, GL520_REG_FAN_DIV,
+ (gl520_read_value(client, GL520_REG_FAN_DIV)
+ & ~0x30) | (r << 4));
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan1_off_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 r;
+ unsigned long v;
+ int err;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err)
+ return err;
+
+ r = (v ? 1 : 0);
+
+ mutex_lock(&data->update_lock);
+ data->fan_off = r;
+ gl520_write_value(client, GL520_REG_FAN_OFF,
+ (gl520_read_value(client, GL520_REG_FAN_OFF)
+ & ~0x0c) | (r << 2));
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1);
+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(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+static DEVICE_ATTR_RW(fan1_off);
+
+#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
+#define TEMP_CLAMP(val) clamp_val(val, -130000, 125000)
+#define TEMP_TO_REG(val) (DIV_ROUND_CLOSEST(TEMP_CLAMP(val), 1000) + 130)
+
+static ssize_t temp_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_input[n]));
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[n]));
+}
+
+static ssize_t temp_max_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int n = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[n]));
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int n = to_sensor_dev_attr(attr)->index;
+ long v;
+ int err;
+
+ err = kstrtol(buf, 10, &v);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[n] = TEMP_TO_REG(v);
+ gl520_write_value(client, GL520_REG_TEMP_MAX[n], data->temp_max[n]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_max_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int n = to_sensor_dev_attr(attr)->index;
+ long v;
+ int err;
+
+ err = kstrtol(buf, 10, &v);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max_hyst[n] = TEMP_TO_REG(v);
+ gl520_write_value(client, GL520_REG_TEMP_MAX_HYST[n],
+ data->temp_max_hyst[n]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1);
+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(temp1_max_hyst, temp_max_hyst, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, temp_max_hyst, 1);
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct gl520_data *data = gl520_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+static ssize_t beep_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gl520_data *data = gl520_update_device(dev);
+ return sprintf(buf, "%d\n", data->beep_enable);
+}
+
+static ssize_t beep_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gl520_data *data = gl520_update_device(dev);
+ return sprintf(buf, "%d\n", data->beep_mask);
+}
+
+static ssize_t beep_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 r;
+ unsigned long v;
+ int err;
+
+ err = kstrtoul(buf, 10, &v);
+ if (err)
+ return err;
+
+ r = (v ? 0 : 1);
+
+ mutex_lock(&data->update_lock);
+ data->beep_enable = !r;
+ gl520_write_value(client, GL520_REG_BEEP_ENABLE,
+ (gl520_read_value(client, GL520_REG_BEEP_ENABLE)
+ & ~0x04) | (r << 2));
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t beep_mask_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long r;
+ int err;
+
+ err = kstrtoul(buf, 10, &r);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ r &= data->alarm_mask;
+ data->beep_mask = r;
+ gl520_write_value(client, GL520_REG_BEEP_MASK, r);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RO(alarms);
+static DEVICE_ATTR_RW(beep_enable);
+static DEVICE_ATTR_RW(beep_mask);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bit_nr = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", (data->alarms >> bit_nr) & 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(temp1_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 7);
+
+static ssize_t beep_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct gl520_data *data = gl520_update_device(dev);
+
+ return sprintf(buf, "%d\n", (data->beep_mask >> bitnr) & 1);
+}
+
+static ssize_t beep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gl520_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ unsigned long bit;
+
+ int err;
+
+ err = kstrtoul(buf, 10, &bit);
+ if (err)
+ return err;
+ if (bit & ~1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
+ if (bit)
+ data->beep_mask |= (1 << bitnr);
+ else
+ data->beep_mask &= ~(1 << bitnr);
+ gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(in0_beep, beep, 0);
+static SENSOR_DEVICE_ATTR_RW(in1_beep, beep, 1);
+static SENSOR_DEVICE_ATTR_RW(in2_beep, beep, 2);
+static SENSOR_DEVICE_ATTR_RW(in3_beep, beep, 3);
+static SENSOR_DEVICE_ATTR_RW(temp1_beep, beep, 4);
+static SENSOR_DEVICE_ATTR_RW(fan1_beep, beep, 5);
+static SENSOR_DEVICE_ATTR_RW(fan2_beep, beep, 6);
+static SENSOR_DEVICE_ATTR_RW(temp2_beep, beep, 7);
+static SENSOR_DEVICE_ATTR_RW(in4_beep, beep, 7);
+
+static struct attribute *gl520_attributes[] = {
+ &dev_attr_cpu0_vid.attr,
+
+ &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_in0_beep.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_in1_beep.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_in2_beep.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_in3_beep.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_beep.dev_attr.attr,
+ &dev_attr_fan1_off.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.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_temp1_beep.dev_attr.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+ NULL
+};
+
+static const struct attribute_group gl520_group = {
+ .attrs = gl520_attributes,
+};
+
+static struct attribute *gl520_attributes_in4[] = {
+ &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_in4_beep.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *gl520_attributes_temp2[] = {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_beep.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group gl520_group_in4 = {
+ .attrs = gl520_attributes_in4,
+};
+
+static const struct attribute_group gl520_group_temp2 = {
+ .attrs = gl520_attributes_temp2,
+};
+
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ /* Determine the chip type. */
+ if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) ||
+ ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
+ ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) {
+ dev_dbg(&client->dev, "Unknown chip type, skipping\n");
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "gl520sm", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+/* Called when we have found a new GL520SM. */
+static void gl520_init_client(struct i2c_client *client)
+{
+ struct gl520_data *data = i2c_get_clientdata(client);
+ u8 oldconf, conf;
+
+ conf = oldconf = gl520_read_value(client, GL520_REG_CONF);
+
+ data->alarm_mask = 0xff;
+ data->vrm = vid_which_vrm();
+
+ if (extra_sensor_type == 1)
+ conf &= ~0x10;
+ else if (extra_sensor_type == 2)
+ conf |= 0x10;
+ data->two_temps = !(conf & 0x10);
+
+ /* If IRQ# is disabled, we can safely force comparator mode */
+ if (!(conf & 0x20))
+ conf &= 0xf7;
+
+ /* Enable monitoring if needed */
+ conf |= 0x40;
+
+ if (conf != oldconf)
+ gl520_write_value(client, GL520_REG_CONF, conf);
+
+ gl520_update_device(&(client->dev));
+
+ if (data->fan_min[0] == 0)
+ data->alarm_mask &= ~0x20;
+ if (data->fan_min[1] == 0)
+ data->alarm_mask &= ~0x40;
+
+ data->beep_mask &= data->alarm_mask;
+ gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
+}
+
+static int gl520_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct gl520_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct gl520_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ data->client = client;
+
+ /* Initialize the GL520SM chip */
+ gl520_init_client(client);
+
+ /* sysfs hooks */
+ data->groups[0] = &gl520_group;
+
+ if (data->two_temps)
+ data->groups[1] = &gl520_group_temp2;
+ else
+ data->groups[1] = &gl520_group_in4;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id gl520_id[] = {
+ { "gl520sm", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, gl520_id);
+
+static struct i2c_driver gl520_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "gl520sm",
+ },
+ .probe_new = gl520_probe,
+ .id_table = gl520_id,
+ .detect = gl520_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(gl520_driver);
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+ "Kyösti Mälkki <kmalkki@cc.hut.fi>, "
+ "Maarten Deprez <maartendeprez@users.sourceforge.net>");
+MODULE_DESCRIPTION("GL520SM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
new file mode 100644
index 000000000..ba408942d
--- /dev/null
+++ b/drivers/hwmon/gpio-fan.c
@@ -0,0 +1,597 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * gpio-fan.c - Hwmon driver for fans connected to GPIO lines.
+ *
+ * Copyright (C) 2010 LaCie
+ *
+ * Author: Simon Guinot <sguinot@lacie.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/thermal.h>
+
+struct gpio_fan_speed {
+ int rpm;
+ int ctrl_val;
+};
+
+struct gpio_fan_data {
+ struct device *dev;
+ struct device *hwmon_dev;
+ /* Cooling device if any */
+ struct thermal_cooling_device *cdev;
+ struct mutex lock; /* lock GPIOs operations. */
+ int num_gpios;
+ struct gpio_desc **gpios;
+ int num_speed;
+ struct gpio_fan_speed *speed;
+ int speed_index;
+ int resume_speed;
+ bool pwm_enable;
+ struct gpio_desc *alarm_gpio;
+ struct work_struct alarm_work;
+};
+
+/*
+ * Alarm GPIO.
+ */
+
+static void fan_alarm_notify(struct work_struct *ws)
+{
+ struct gpio_fan_data *fan_data =
+ container_of(ws, struct gpio_fan_data, alarm_work);
+
+ sysfs_notify(&fan_data->hwmon_dev->kobj, NULL, "fan1_alarm");
+ kobject_uevent(&fan_data->hwmon_dev->kobj, KOBJ_CHANGE);
+}
+
+static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_fan_data *fan_data = dev_id;
+
+ schedule_work(&fan_data->alarm_work);
+
+ return IRQ_NONE;
+}
+
+static ssize_t fan1_alarm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n",
+ gpiod_get_value_cansleep(fan_data->alarm_gpio));
+}
+
+static DEVICE_ATTR_RO(fan1_alarm);
+
+static int fan_alarm_init(struct gpio_fan_data *fan_data)
+{
+ int alarm_irq;
+ struct device *dev = fan_data->dev;
+
+ /*
+ * If the alarm GPIO don't support interrupts, just leave
+ * without initializing the fail notification support.
+ */
+ alarm_irq = gpiod_to_irq(fan_data->alarm_gpio);
+ if (alarm_irq <= 0)
+ return 0;
+
+ INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
+ irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
+ return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
+ IRQF_SHARED, "GPIO fan alarm", fan_data);
+}
+
+/*
+ * Control GPIOs.
+ */
+
+/* Must be called with fan_data->lock held, except during initialization. */
+static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
+{
+ int i;
+
+ for (i = 0; i < fan_data->num_gpios; i++)
+ gpiod_set_value_cansleep(fan_data->gpios[i],
+ (ctrl_val >> i) & 1);
+}
+
+static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
+{
+ int i;
+ int ctrl_val = 0;
+
+ for (i = 0; i < fan_data->num_gpios; i++) {
+ int value;
+
+ value = gpiod_get_value_cansleep(fan_data->gpios[i]);
+ ctrl_val |= (value << i);
+ }
+ return ctrl_val;
+}
+
+/* Must be called with fan_data->lock held, except during initialization. */
+static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
+{
+ if (fan_data->speed_index == speed_index)
+ return;
+
+ __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
+ fan_data->speed_index = speed_index;
+}
+
+static int get_fan_speed_index(struct gpio_fan_data *fan_data)
+{
+ int ctrl_val = __get_fan_ctrl(fan_data);
+ int i;
+
+ for (i = 0; i < fan_data->num_speed; i++)
+ if (fan_data->speed[i].ctrl_val == ctrl_val)
+ return i;
+
+ dev_warn(fan_data->dev,
+ "missing speed array entry for GPIO value 0x%x\n", ctrl_val);
+
+ return -ENODEV;
+}
+
+static int rpm_to_speed_index(struct gpio_fan_data *fan_data, unsigned long rpm)
+{
+ struct gpio_fan_speed *speed = fan_data->speed;
+ int i;
+
+ for (i = 0; i < fan_data->num_speed; i++)
+ if (speed[i].rpm >= rpm)
+ return i;
+
+ return fan_data->num_speed - 1;
+}
+
+static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1);
+
+ return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ unsigned long pwm;
+ int speed_index;
+ int ret = count;
+
+ if (kstrtoul(buf, 10, &pwm) || pwm > 255)
+ return -EINVAL;
+
+ mutex_lock(&fan_data->lock);
+
+ if (!fan_data->pwm_enable) {
+ ret = -EPERM;
+ goto exit_unlock;
+ }
+
+ speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
+ set_fan_speed(fan_data, speed_index);
+
+exit_unlock:
+ mutex_unlock(&fan_data->lock);
+
+ return ret;
+}
+
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", fan_data->pwm_enable);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) || val > 1)
+ return -EINVAL;
+
+ if (fan_data->pwm_enable == val)
+ return count;
+
+ mutex_lock(&fan_data->lock);
+
+ fan_data->pwm_enable = val;
+
+ /* Disable manual control mode: set fan at full speed. */
+ if (val == 0)
+ set_fan_speed(fan_data, fan_data->num_speed - 1);
+
+ mutex_unlock(&fan_data->lock);
+
+ return count;
+}
+
+static ssize_t pwm1_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t fan1_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", fan_data->speed[0].rpm);
+}
+
+static ssize_t fan1_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n",
+ fan_data->speed[fan_data->num_speed - 1].rpm);
+}
+
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm);
+}
+
+static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ unsigned long rpm;
+ int ret = count;
+
+ if (kstrtoul(buf, 10, &rpm))
+ return -EINVAL;
+
+ mutex_lock(&fan_data->lock);
+
+ if (!fan_data->pwm_enable) {
+ ret = -EPERM;
+ goto exit_unlock;
+ }
+
+ set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
+
+exit_unlock:
+ mutex_unlock(&fan_data->lock);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR_RO(pwm1_mode);
+static DEVICE_ATTR_RO(fan1_min);
+static DEVICE_ATTR_RO(fan1_max);
+static DEVICE_ATTR_RO(fan1_input);
+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 = kobj_to_dev(kobj);
+ struct gpio_fan_data *data = dev_get_drvdata(dev);
+
+ if (index == 0 && !data->alarm_gpio)
+ return 0;
+ if (index > 0 && !data->gpios)
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute *gpio_fan_attributes[] = {
+ &dev_attr_fan1_alarm.attr, /* 0 */
+ &dev_attr_pwm1.attr, /* 1 */
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm1_mode.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_target.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_max.attr,
+ NULL
+};
+
+static const struct attribute_group gpio_fan_group = {
+ .attrs = gpio_fan_attributes,
+ .is_visible = gpio_fan_is_visible,
+};
+
+static const struct attribute_group *gpio_fan_groups[] = {
+ &gpio_fan_group,
+ NULL
+};
+
+static int fan_ctrl_init(struct gpio_fan_data *fan_data)
+{
+ int num_gpios = fan_data->num_gpios;
+ struct gpio_desc **gpios = fan_data->gpios;
+ int i, err;
+
+ for (i = 0; i < num_gpios; i++) {
+ /*
+ * The GPIO descriptors were retrieved with GPIOD_ASIS so here
+ * we set the GPIO into output mode, carefully preserving the
+ * current value by setting it to whatever it is already set
+ * (no surprise changes in default fan speed).
+ */
+ err = gpiod_direction_output(gpios[i],
+ gpiod_get_value_cansleep(gpios[i]));
+ if (err)
+ return err;
+ }
+
+ fan_data->pwm_enable = true; /* Enable manual fan speed control. */
+ fan_data->speed_index = get_fan_speed_index(fan_data);
+ if (fan_data->speed_index < 0)
+ return fan_data->speed_index;
+
+ return 0;
+}
+
+static int gpio_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct gpio_fan_data *fan_data = cdev->devdata;
+
+ if (!fan_data)
+ return -EINVAL;
+
+ *state = fan_data->num_speed - 1;
+ return 0;
+}
+
+static int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct gpio_fan_data *fan_data = cdev->devdata;
+
+ if (!fan_data)
+ return -EINVAL;
+
+ *state = fan_data->speed_index;
+ return 0;
+}
+
+static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct gpio_fan_data *fan_data = cdev->devdata;
+
+ if (!fan_data)
+ return -EINVAL;
+
+ if (state >= fan_data->num_speed)
+ return -EINVAL;
+
+ set_fan_speed(fan_data, state);
+ return 0;
+}
+
+static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
+ .get_max_state = gpio_fan_get_max_state,
+ .get_cur_state = gpio_fan_get_cur_state,
+ .set_cur_state = gpio_fan_set_cur_state,
+};
+
+/*
+ * Translate OpenFirmware node properties into platform_data
+ */
+static int gpio_fan_get_of_data(struct gpio_fan_data *fan_data)
+{
+ struct gpio_fan_speed *speed;
+ struct device *dev = fan_data->dev;
+ struct device_node *np = dev->of_node;
+ struct gpio_desc **gpios;
+ unsigned i;
+ u32 u;
+ struct property *prop;
+ const __be32 *p;
+
+ /* Alarm GPIO if one exists */
+ fan_data->alarm_gpio = devm_gpiod_get_optional(dev, "alarm", GPIOD_IN);
+ if (IS_ERR(fan_data->alarm_gpio))
+ return PTR_ERR(fan_data->alarm_gpio);
+
+ /* Fill GPIO pin array */
+ fan_data->num_gpios = gpiod_count(dev, NULL);
+ if (fan_data->num_gpios <= 0) {
+ if (fan_data->alarm_gpio)
+ return 0;
+ dev_err(dev, "DT properties empty / missing");
+ return -ENODEV;
+ }
+ gpios = devm_kcalloc(dev,
+ fan_data->num_gpios, sizeof(struct gpio_desc *),
+ GFP_KERNEL);
+ if (!gpios)
+ return -ENOMEM;
+ for (i = 0; i < fan_data->num_gpios; i++) {
+ gpios[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
+ if (IS_ERR(gpios[i]))
+ return PTR_ERR(gpios[i]);
+ }
+ fan_data->gpios = gpios;
+
+ /* Get number of RPM/ctrl_val pairs in speed map */
+ prop = of_find_property(np, "gpio-fan,speed-map", &i);
+ if (!prop) {
+ dev_err(dev, "gpio-fan,speed-map DT property missing");
+ return -ENODEV;
+ }
+ i = i / sizeof(u32);
+ if (i == 0 || i & 1) {
+ dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries");
+ return -ENODEV;
+ }
+ fan_data->num_speed = i / 2;
+
+ /*
+ * Populate speed map
+ * Speed map is in the form <RPM ctrl_val RPM ctrl_val ...>
+ * this needs splitting into pairs to create gpio_fan_speed structs
+ */
+ speed = devm_kcalloc(dev,
+ fan_data->num_speed, sizeof(struct gpio_fan_speed),
+ GFP_KERNEL);
+ if (!speed)
+ return -ENOMEM;
+ p = NULL;
+ for (i = 0; i < fan_data->num_speed; i++) {
+ p = of_prop_next_u32(prop, p, &u);
+ if (!p)
+ return -ENODEV;
+ speed[i].rpm = u;
+ p = of_prop_next_u32(prop, p, &u);
+ if (!p)
+ return -ENODEV;
+ speed[i].ctrl_val = u;
+ }
+ fan_data->speed = speed;
+
+ return 0;
+}
+
+static const struct of_device_id of_gpio_fan_match[] = {
+ { .compatible = "gpio-fan", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_gpio_fan_match);
+
+static void gpio_fan_stop(void *data)
+{
+ set_fan_speed(data, 0);
+}
+
+static int gpio_fan_probe(struct platform_device *pdev)
+{
+ int err;
+ struct gpio_fan_data *fan_data;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ fan_data = devm_kzalloc(dev, sizeof(struct gpio_fan_data),
+ GFP_KERNEL);
+ if (!fan_data)
+ return -ENOMEM;
+
+ fan_data->dev = dev;
+ err = gpio_fan_get_of_data(fan_data);
+ if (err)
+ return err;
+
+ platform_set_drvdata(pdev, fan_data);
+ mutex_init(&fan_data->lock);
+
+ /* Configure control GPIOs if available. */
+ if (fan_data->gpios && fan_data->num_gpios > 0) {
+ if (!fan_data->speed || fan_data->num_speed <= 1)
+ return -EINVAL;
+ err = fan_ctrl_init(fan_data);
+ if (err)
+ return err;
+ err = devm_add_action_or_reset(dev, gpio_fan_stop, fan_data);
+ if (err)
+ return err;
+ }
+
+ /* Make this driver part of hwmon class. */
+ fan_data->hwmon_dev =
+ devm_hwmon_device_register_with_groups(dev,
+ "gpio_fan", fan_data,
+ gpio_fan_groups);
+ if (IS_ERR(fan_data->hwmon_dev))
+ return PTR_ERR(fan_data->hwmon_dev);
+
+ /* Configure alarm GPIO if available. */
+ if (fan_data->alarm_gpio) {
+ err = fan_alarm_init(fan_data);
+ if (err)
+ return err;
+ }
+
+ /* Optional cooling device register for Device tree platforms */
+ fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
+ "gpio-fan", fan_data, &gpio_fan_cool_ops);
+
+ dev_info(dev, "GPIO fan initialized\n");
+
+ return 0;
+}
+
+static void gpio_fan_shutdown(struct platform_device *pdev)
+{
+ struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
+
+ if (fan_data->gpios)
+ set_fan_speed(fan_data, 0);
+}
+
+static int gpio_fan_suspend(struct device *dev)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ if (fan_data->gpios) {
+ fan_data->resume_speed = fan_data->speed_index;
+ set_fan_speed(fan_data, 0);
+ }
+
+ return 0;
+}
+
+static int gpio_fan_resume(struct device *dev)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ if (fan_data->gpios)
+ set_fan_speed(fan_data, fan_data->resume_speed);
+
+ return 0;
+}
+
+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 = pm_sleep_ptr(&gpio_fan_pm),
+ .of_match_table = of_match_ptr(of_gpio_fan_match),
+ },
+};
+
+module_platform_driver(gpio_fan_driver);
+
+MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
+MODULE_DESCRIPTION("GPIO FAN driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-fan");
diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c
new file mode 100644
index 000000000..74bfc21c2
--- /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, 100000);
+ temp = DIV_ROUND_CLOSEST(temp, 100);
+
+ 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)));
+}
+
+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
new file mode 100644
index 000000000..d9394e19f
--- /dev/null
+++ b/drivers/hwmon/hih6130.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Honeywell HIH-6130/HIH-6131 humidity and temperature sensor driver
+ *
+ * Copyright (C) 2012 Iain Paton <ipaton0@gmail.com>
+ *
+ * heavily based on the sht21 driver
+ * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.com>
+ *
+ * Data sheets available (2012-06-22) at
+ * http://sensing.honeywell.com/index.php?ci_id=3106&la_id=1&defId=44872
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+
+/**
+ * struct hih6130 - HIH-6130 device specific data
+ * @client: pointer to I2C client device
+ * @lock: mutex to protect measurement values
+ * @valid: only false before first measurement is taken
+ * @last_update: time of last update (jiffies)
+ * @temperature: cached temperature measurement value
+ * @humidity: cached humidity measurement value
+ * @write_length: length for I2C measurement request
+ */
+struct hih6130 {
+ struct i2c_client *client;
+ struct mutex lock;
+ bool valid;
+ unsigned long last_update;
+ int temperature;
+ int humidity;
+ size_t write_length;
+};
+
+/**
+ * hih6130_temp_ticks_to_millicelsius() - convert raw temperature ticks to
+ * milli celsius
+ * @ticks: temperature ticks value received from sensor
+ */
+static inline int hih6130_temp_ticks_to_millicelsius(int ticks)
+{
+ ticks = ticks >> 2;
+ /*
+ * from data sheet section 5.0
+ * Formula T = ( ticks / ( 2^14 - 2 ) ) * 165 -40
+ */
+ return (DIV_ROUND_CLOSEST(ticks * 1650, 16382) - 400) * 100;
+}
+
+/**
+ * hih6130_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to
+ * one-thousandths of a percent relative humidity
+ * @ticks: humidity ticks value received from sensor
+ */
+static inline int hih6130_rh_ticks_to_per_cent_mille(int ticks)
+{
+ ticks &= ~0xC000; /* clear status bits */
+ /*
+ * from data sheet section 4.0
+ * Formula RH = ( ticks / ( 2^14 -2 ) ) * 100
+ */
+ return DIV_ROUND_CLOSEST(ticks * 1000, 16382) * 100;
+}
+
+/**
+ * hih6130_update_measurements() - get updated measurements from device
+ * @dev: device
+ *
+ * Returns 0 on success, else negative errno.
+ */
+static int hih6130_update_measurements(struct device *dev)
+{
+ struct hih6130 *hih6130 = dev_get_drvdata(dev);
+ struct i2c_client *client = hih6130->client;
+ int ret = 0;
+ int t;
+ unsigned char tmp[4];
+ struct i2c_msg msgs[1] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 4,
+ .buf = tmp,
+ }
+ };
+
+ mutex_lock(&hih6130->lock);
+
+ /*
+ * While the measurement can be completed in ~40ms the sensor takes
+ * much longer to react to a change in external conditions. How quickly
+ * it reacts depends on airflow and other factors outwith our control.
+ * The datasheet specifies maximum 'Response time' for humidity at 8s
+ * and temperature at 30s under specified conditions.
+ * We therefore choose to only read the sensor at most once per second.
+ * This trades off pointless activity polling the sensor much faster
+ * than it can react against better response times in conditions more
+ * favourable than specified in the datasheet.
+ */
+ if (time_after(jiffies, hih6130->last_update + HZ) || !hih6130->valid) {
+
+ /*
+ * Write to slave address to request a measurement.
+ * According with the datasheet it should be with no data, but
+ * for systems with I2C bus drivers that do not allow zero
+ * length packets we write one dummy byte to allow sensor
+ * measurements on them.
+ */
+ tmp[0] = 0;
+ ret = i2c_master_send(client, tmp, hih6130->write_length);
+ if (ret < 0)
+ goto out;
+
+ /* measurement cycle time is ~36.65msec */
+ msleep(40);
+
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0)
+ goto out;
+
+ if ((tmp[0] & 0xC0) != 0) {
+ dev_err(&client->dev, "Error while reading measurement result\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ t = (tmp[0] << 8) + tmp[1];
+ hih6130->humidity = hih6130_rh_ticks_to_per_cent_mille(t);
+
+ t = (tmp[2] << 8) + tmp[3];
+ hih6130->temperature = hih6130_temp_ticks_to_millicelsius(t);
+
+ hih6130->last_update = jiffies;
+ hih6130->valid = true;
+ }
+out:
+ mutex_unlock(&hih6130->lock);
+
+ return ret >= 0 ? 0 : ret;
+}
+
+/**
+ * hih6130_show_temperature() - show temperature measurement value in sysfs
+ * @dev: device
+ * @attr: device attribute
+ * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
+ *
+ * Will be called on read access to temp1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t hih6130_temperature_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hih6130 *hih6130 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = hih6130_update_measurements(dev);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", hih6130->temperature);
+}
+
+/**
+ * hih6130_show_humidity() - show humidity measurement value in sysfs
+ * @dev: device
+ * @attr: device attribute
+ * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
+ *
+ * Will be called on read access to humidity1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t hih6130_humidity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hih6130 *hih6130 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = hih6130_update_measurements(dev);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", hih6130->humidity);
+}
+
+/* sysfs attributes */
+static SENSOR_DEVICE_ATTR_RO(temp1_input, hih6130_temperature, 0);
+static SENSOR_DEVICE_ATTR_RO(humidity1_input, hih6130_humidity, 0);
+
+static struct attribute *hih6130_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_humidity1_input.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(hih6130);
+
+static int hih6130_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct hih6130 *hih6130;
+ struct device *hwmon_dev;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "adapter does not support true I2C\n");
+ return -ENODEV;
+ }
+
+ hih6130 = devm_kzalloc(dev, sizeof(*hih6130), GFP_KERNEL);
+ if (!hih6130)
+ return -ENOMEM;
+
+ hih6130->client = client;
+ mutex_init(&hih6130->lock);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK))
+ hih6130->write_length = 1;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ hih6130,
+ hih6130_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* Device ID table */
+static const struct i2c_device_id hih6130_id[] = {
+ { "hih6130", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, hih6130_id);
+
+static const struct of_device_id __maybe_unused hih6130_of_match[] = {
+ { .compatible = "honeywell,hih6130", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hih6130_of_match);
+
+static struct i2c_driver hih6130_driver = {
+ .driver = {
+ .name = "hih6130",
+ .of_match_table = of_match_ptr(hih6130_of_match),
+ },
+ .probe_new = hih6130_probe,
+ .id_table = hih6130_id,
+};
+
+module_i2c_driver(hih6130_driver);
+
+MODULE_AUTHOR("Iain Paton <ipaton0@gmail.com>");
+MODULE_DESCRIPTION("Honeywell HIH-6130 humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c
new file mode 100644
index 000000000..6d1175a51
--- /dev/null
+++ b/drivers/hwmon/hwmon-vid.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * hwmon-vid.c - VID/VRM/VRD voltage conversions
+ *
+ * Copyright (c) 2004 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * Partly imported from i2c-vid.h of the lm_sensors project
+ * Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ * With assistance from Trent Piepho <xyzzy@speakeasy.org>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/hwmon-vid.h>
+
+/*
+ * Common code for decoding VID pins.
+ *
+ * References:
+ *
+ * For VRM 8.4 to 9.1, "VRM x.y DC-DC Converter Design Guidelines",
+ * available at http://developer.intel.com/.
+ *
+ * For VRD 10.0 and up, "VRD x.y Design Guide",
+ * available at http://developer.intel.com/.
+ *
+ * AMD Athlon 64 and AMD Opteron Processors, AMD Publication 26094,
+ * http://support.amd.com/us/Processor_TechDocs/26094.PDF
+ * Table 74. VID Code Voltages
+ * This corresponds to an arbitrary VRM code of 24 in the functions below.
+ * These CPU models (K8 revision <= E) have 5 VID pins. See also:
+ * Revision Guide for AMD Athlon 64 and AMD Opteron Processors, AMD Publication 25759,
+ * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25759.pdf
+ *
+ * AMD NPT Family 0Fh Processors, AMD Publication 32559,
+ * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf
+ * Table 71. VID Code Voltages
+ * This corresponds to an arbitrary VRM code of 25 in the functions below.
+ * These CPU models (K8 revision >= F) have 6 VID pins. See also:
+ * Revision Guide for AMD NPT Family 0Fh Processors, AMD Publication 33610,
+ * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/33610.pdf
+ *
+ * The 17 specification is in fact Intel Mobile Voltage Positioning -
+ * (IMVP-II). You can find more information in the datasheet of Max1718
+ * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2452
+ *
+ * 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:
+ * 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:
+ * https://www.intel.com/design/mobile/datashts/309221.htm
+ *
+ * The 110 (VRM 11) specification corresponds to Intel Conroe based series.
+ * https://www.intel.com/design/processor/applnots/313214.htm
+ */
+
+/*
+ * vrm is the VRM/VRD document version multiplied by 10.
+ * val is the 4-bit or more VID code.
+ * Returned value is in mV to avoid floating point in the kernel.
+ * Some VID have some bits in uV scale, this is rounded to mV.
+ */
+int vid_from_reg(int val, u8 vrm)
+{
+ int vid;
+
+ switch (vrm) {
+
+ case 100: /* VRD 10.0 */
+ /* compute in uV, round to mV */
+ val &= 0x3f;
+ if ((val & 0x1f) == 0x1f)
+ return 0;
+ if ((val & 0x1f) <= 0x09 || val == 0x0a)
+ vid = 1087500 - (val & 0x1f) * 25000;
+ else
+ vid = 1862500 - (val & 0x1f) * 25000;
+ if (val & 0x20)
+ vid -= 12500;
+ return (vid + 500) / 1000;
+
+ case 110: /* Intel Conroe */
+ /* compute in uV, round to mV */
+ val &= 0xff;
+ if (val < 0x02 || val > 0xb2)
+ return 0;
+ return (1600000 - (val - 2) * 6250 + 500) / 1000;
+
+ case 24: /* Athlon64 & Opteron */
+ val &= 0x1f;
+ if (val == 0x1f)
+ return 0;
+ fallthrough;
+ case 25: /* AMD NPT 0Fh */
+ val &= 0x3f;
+ return (val < 32) ? 1550 - 25 * val
+ : 775 - (25 * (val - 31)) / 2;
+
+ case 26: /* AMD family 10h to 15h, serial VID */
+ val &= 0x7f;
+ if (val >= 0x7c)
+ return 0;
+ return DIV_ROUND_CLOSEST(15500 - 125 * val, 10);
+
+ case 91: /* VRM 9.1 */
+ case 90: /* VRM 9.0 */
+ val &= 0x1f;
+ return val == 0x1f ? 0 :
+ 1850 - val * 25;
+
+ case 85: /* VRM 8.5 */
+ val &= 0x1f;
+ return (val & 0x10 ? 25 : 0) +
+ ((val & 0x0f) > 0x04 ? 2050 : 1250) -
+ ((val & 0x0f) * 50);
+
+ case 84: /* VRM 8.4 */
+ val &= 0x0f;
+ fallthrough;
+ case 82: /* VRM 8.2 */
+ val &= 0x1f;
+ return val == 0x1f ? 0 :
+ val & 0x10 ? 5100 - (val) * 100 :
+ 2050 - (val) * 50;
+ case 17: /* Intel IMVP-II */
+ val &= 0x1f;
+ return val & 0x10 ? 975 - (val & 0xF) * 25 :
+ 1750 - val * 50;
+ case 13:
+ case 131:
+ val &= 0x3f;
+ /* Exception for Eden ULV 500 MHz */
+ if (vrm == 131 && val == 0x3f)
+ val++;
+ return 1708 - val * 16;
+ case 14: /* Intel Core */
+ /* compute in uV, round to mV */
+ val &= 0x7f;
+ return val > 0x77 ? 0 : (1500000 - (val * 12500) + 500) / 1000;
+ default: /* report 0 for unknown */
+ if (vrm)
+ pr_warn("Requested unsupported VRM version (%u)\n",
+ (unsigned int)vrm);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(vid_from_reg);
+
+/*
+ * After this point is the code to automatically determine which
+ * VRM/VRD specification should be used depending on the CPU.
+ */
+
+struct vrm_model {
+ u8 vendor;
+ u8 family;
+ u8 model_from;
+ u8 model_to;
+ u8 stepping_to;
+ u8 vrm_type;
+};
+
+#define ANY 0xFF
+
+#ifdef CONFIG_X86
+
+/*
+ * The stepping_to parameter is highest acceptable stepping for current line.
+ * The model match must be exact for 4-bit values. For model values 0x10
+ * and above (extended model), all models below the parameter will match.
+ */
+
+static struct vrm_model vrm_models[] = {
+ {X86_VENDOR_AMD, 0x6, 0x0, ANY, ANY, 90}, /* Athlon Duron etc */
+ {X86_VENDOR_AMD, 0xF, 0x0, 0x3F, ANY, 24}, /* Athlon 64, Opteron */
+ /*
+ * In theory, all NPT family 0Fh processors have 6 VID pins and should
+ * thus use vrm 25, however in practice not all mainboards route the
+ * 6th VID pin because it is never needed. So we use the 5 VID pin
+ * variant (vrm 24) for the models which exist today.
+ */
+ {X86_VENDOR_AMD, 0xF, 0x40, 0x7F, ANY, 24}, /* NPT family 0Fh */
+ {X86_VENDOR_AMD, 0xF, 0x80, ANY, ANY, 25}, /* future fam. 0Fh */
+ {X86_VENDOR_AMD, 0x10, 0x0, ANY, ANY, 25}, /* NPT family 10h */
+ {X86_VENDOR_AMD, 0x11, 0x0, ANY, ANY, 26}, /* family 11h */
+ {X86_VENDOR_AMD, 0x12, 0x0, ANY, ANY, 26}, /* family 12h */
+ {X86_VENDOR_AMD, 0x14, 0x0, ANY, ANY, 26}, /* family 14h */
+ {X86_VENDOR_AMD, 0x15, 0x0, ANY, ANY, 26}, /* family 15h */
+
+ {X86_VENDOR_INTEL, 0x6, 0x0, 0x6, ANY, 82}, /* Pentium Pro,
+ * Pentium II, Xeon,
+ * Mobile Pentium,
+ * Celeron */
+ {X86_VENDOR_INTEL, 0x6, 0x7, 0x7, ANY, 84}, /* Pentium III, Xeon */
+ {X86_VENDOR_INTEL, 0x6, 0x8, 0x8, ANY, 82}, /* Pentium III, Xeon */
+ {X86_VENDOR_INTEL, 0x6, 0x9, 0x9, ANY, 13}, /* Pentium M (130 nm) */
+ {X86_VENDOR_INTEL, 0x6, 0xA, 0xA, ANY, 82}, /* Pentium III Xeon */
+ {X86_VENDOR_INTEL, 0x6, 0xB, 0xB, ANY, 85}, /* Tualatin */
+ {X86_VENDOR_INTEL, 0x6, 0xD, 0xD, ANY, 13}, /* Pentium M (90 nm) */
+ {X86_VENDOR_INTEL, 0x6, 0xE, 0xE, ANY, 14}, /* Intel Core (65 nm) */
+ {X86_VENDOR_INTEL, 0x6, 0xF, ANY, ANY, 110}, /* Intel Conroe and
+ * later */
+ {X86_VENDOR_INTEL, 0xF, 0x0, 0x0, ANY, 90}, /* P4 */
+ {X86_VENDOR_INTEL, 0xF, 0x1, 0x1, ANY, 90}, /* P4 Willamette */
+ {X86_VENDOR_INTEL, 0xF, 0x2, 0x2, ANY, 90}, /* P4 Northwood */
+ {X86_VENDOR_INTEL, 0xF, 0x3, ANY, ANY, 100}, /* Prescott and above
+ * assume VRD 10 */
+
+ {X86_VENDOR_CENTAUR, 0x6, 0x7, 0x7, ANY, 85}, /* Eden ESP/Ezra */
+ {X86_VENDOR_CENTAUR, 0x6, 0x8, 0x8, 0x7, 85}, /* Ezra T */
+ {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x9, 0x7, 85}, /* Nehemiah */
+ {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x9, ANY, 17}, /* C3-M, Eden-N */
+ {X86_VENDOR_CENTAUR, 0x6, 0xA, 0xA, 0x7, 0}, /* No information */
+ {X86_VENDOR_CENTAUR, 0x6, 0xA, 0xA, ANY, 13}, /* C7-M, C7,
+ * Eden (Esther) */
+ {X86_VENDOR_CENTAUR, 0x6, 0xD, 0xD, ANY, 134}, /* C7-D, C7-M, C7,
+ * Eden (Esther) */
+};
+
+/*
+ * Special case for VIA model D: there are two different possible
+ * VID tables, so we have to figure out first, which one must be
+ * used. This resolves temporary drm value 134 to 14 (Intel Core
+ * 7-bit VID), 13 (Pentium M 6-bit VID) or 131 (Pentium M 6-bit VID
+ * + quirk for Eden ULV 500 MHz).
+ * Note: something similar might be needed for model A, I'm not sure.
+ */
+static u8 get_via_model_d_vrm(void)
+{
+ unsigned int vid, brand, __maybe_unused dummy;
+ static const char *brands[4] = {
+ "C7-M", "C7", "Eden", "C7-D"
+ };
+
+ rdmsr(0x198, dummy, vid);
+ vid &= 0xff;
+
+ rdmsr(0x1154, brand, dummy);
+ brand = ((brand >> 4) ^ (brand >> 2)) & 0x03;
+
+ if (vid > 0x3f) {
+ pr_info("Using %d-bit VID table for VIA %s CPU\n",
+ 7, brands[brand]);
+ return 14;
+ } else {
+ pr_info("Using %d-bit VID table for VIA %s CPU\n",
+ 6, brands[brand]);
+ /* Enable quirk for Eden */
+ return brand == 2 ? 131 : 13;
+ }
+}
+
+static u8 find_vrm(u8 family, u8 model, u8 stepping, u8 vendor)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vrm_models); i++) {
+ if (vendor == vrm_models[i].vendor &&
+ family == vrm_models[i].family &&
+ model >= vrm_models[i].model_from &&
+ model <= vrm_models[i].model_to &&
+ stepping <= vrm_models[i].stepping_to)
+ return vrm_models[i].vrm_type;
+ }
+
+ return 0;
+}
+
+u8 vid_which_vrm(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ u8 vrm_ret;
+
+ if (c->x86 < 6) /* Any CPU with family lower than 6 */
+ return 0; /* doesn't have VID */
+
+ vrm_ret = find_vrm(c->x86, c->x86_model, c->x86_stepping, c->x86_vendor);
+ if (vrm_ret == 134)
+ vrm_ret = get_via_model_d_vrm();
+ if (vrm_ret == 0)
+ pr_info("Unknown VRM version of your x86 CPU\n");
+ return vrm_ret;
+}
+
+/* and now for something completely different for the non-x86 world */
+#else
+u8 vid_which_vrm(void)
+{
+ pr_info("Unknown VRM version of your CPU\n");
+ return 0;
+}
+#endif
+EXPORT_SYMBOL(vid_which_vrm);
+
+MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
+
+MODULE_DESCRIPTION("hwmon-vid driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
new file mode 100644
index 000000000..9ed34b2e1
--- /dev/null
+++ b/drivers/hwmon/hwmon.c
@@ -0,0 +1,1192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring
+ *
+ * This file defines the sysfs class "hwmon", for use by sensors drivers.
+ *
+ * Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#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>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/hwmon.h>
+
+#define HWMON_ID_PREFIX "hwmon"
+#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
+
+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;
+};
+
+#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev)
+
+#define MAX_SYSFS_ATTR_NAME_LENGTH 32
+
+struct hwmon_device_attribute {
+ struct device_attribute dev_attr;
+ const struct hwmon_ops *ops;
+ enum hwmon_sensor_types type;
+ u32 attr;
+ int index;
+ char name[MAX_SYSFS_ATTR_NAME_LENGTH];
+};
+
+#define to_hwmon_attr(d) \
+ container_of(d, struct hwmon_device_attribute, dev_attr)
+#define to_dev_attr(a) container_of(a, struct device_attribute, attr)
+
+/*
+ * Thermal zone information
+ */
+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
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_hwmon_device(dev)->name);
+}
+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_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct hwmon_device *hdev = to_hwmon_device(dev);
+
+ 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;
+}
+
+static const struct attribute_group hwmon_dev_attr_group = {
+ .attrs = hwmon_dev_attrs,
+ .is_visible = hwmon_dev_attr_is_visible,
+};
+
+static const struct attribute_group *hwmon_dev_attr_groups[] = {
+ &hwmon_dev_attr_group,
+ NULL
+};
+
+static void hwmon_free_attrs(struct attribute **attrs)
+{
+ int i;
+
+ for (i = 0; attrs[i]; i++) {
+ struct device_attribute *dattr = to_dev_attr(attrs[i]);
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(dattr);
+
+ kfree(hattr);
+ }
+ kfree(attrs);
+}
+
+static void hwmon_dev_release(struct device *dev)
+{
+ struct hwmon_device *hwdev = to_hwmon_device(dev);
+
+ if (hwdev->group.attrs)
+ hwmon_free_attrs(hwdev->group.attrs);
+ kfree(hwdev->groups);
+ kfree(hwdev->label);
+ kfree(hwdev);
+}
+
+static struct class hwmon_class = {
+ .name = "hwmon",
+ .owner = THIS_MODULE,
+ .dev_groups = hwmon_dev_attr_groups,
+ .dev_release = hwmon_dev_release,
+};
+
+static DEFINE_IDA(hwmon_ida);
+
+/* Thermal zone handling */
+
+/*
+ * The complex conditional is necessary to avoid a cyclic dependency
+ * between hwmon and thermal_sys modules.
+ */
+#ifdef CONFIG_THERMAL_OF
+static int hwmon_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct hwmon_thermal_data *tdata = tz->devdata;
+ struct hwmon_device *hwdev = to_hwmon_device(tdata->dev);
+ int ret;
+ long t;
+
+ ret = hwdev->chip->ops->read(tdata->dev, hwmon_temp, hwmon_temp_input,
+ tdata->index, &t);
+ if (ret < 0)
+ return ret;
+
+ *temp = t;
+
+ return 0;
+}
+
+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)
+ return -ENOMEM;
+
+ tdata->dev = dev;
+ tdata->index = index;
+
+ 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_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)
+{
+ if (type == hwmon_in || type == hwmon_intrusion)
+ return 0;
+ return 1;
+}
+
+/* sysfs attribute management */
+
+static ssize_t hwmon_attr_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ long val;
+ int ret;
+
+ ret = hattr->ops->read(dev, hattr->type, hattr->attr, hattr->index,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ trace_hwmon_attr_show(hattr->index + hwmon_attr_base(hattr->type),
+ hattr->name, val);
+
+ return sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t hwmon_attr_show_string(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ enum hwmon_sensor_types type = hattr->type;
+ const char *s;
+ int ret;
+
+ ret = hattr->ops->read_string(dev, hattr->type, hattr->attr,
+ hattr->index, &s);
+ if (ret < 0)
+ return ret;
+
+ trace_hwmon_attr_show_string(hattr->index + hwmon_attr_base(type),
+ hattr->name, s);
+
+ return sprintf(buf, "%s\n", s);
+}
+
+static ssize_t hwmon_attr_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = hattr->ops->write(dev, hattr->type, hattr->attr, hattr->index,
+ val);
+ if (ret < 0)
+ return ret;
+
+ trace_hwmon_attr_store(hattr->index + hwmon_attr_base(hattr->type),
+ hattr->name, val);
+
+ return count;
+}
+
+static bool is_string_attr(enum hwmon_sensor_types type, u32 attr)
+{
+ return (type == hwmon_temp && attr == hwmon_temp_label) ||
+ (type == hwmon_in && attr == hwmon_in_label) ||
+ (type == hwmon_curr && attr == hwmon_curr_label) ||
+ (type == hwmon_power && attr == hwmon_power_label) ||
+ (type == hwmon_energy && attr == hwmon_energy_label) ||
+ (type == hwmon_humidity && attr == hwmon_humidity_label) ||
+ (type == hwmon_fan && attr == hwmon_fan_label);
+}
+
+static struct attribute *hwmon_genattr(const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr,
+ int index,
+ const char *template,
+ const struct hwmon_ops *ops)
+{
+ struct hwmon_device_attribute *hattr;
+ struct device_attribute *dattr;
+ struct attribute *a;
+ umode_t mode;
+ const char *name;
+ bool is_string = is_string_attr(type, attr);
+
+ /* The attribute is invisible if there is no template string */
+ if (!template)
+ return ERR_PTR(-ENOENT);
+
+ mode = ops->is_visible(drvdata, type, attr, index);
+ if (!mode)
+ return ERR_PTR(-ENOENT);
+
+ if ((mode & 0444) && ((is_string && !ops->read_string) ||
+ (!is_string && !ops->read)))
+ return ERR_PTR(-EINVAL);
+ if ((mode & 0222) && !ops->write)
+ return ERR_PTR(-EINVAL);
+
+ hattr = kzalloc(sizeof(*hattr), GFP_KERNEL);
+ if (!hattr)
+ return ERR_PTR(-ENOMEM);
+
+ if (type == hwmon_chip) {
+ name = template;
+ } else {
+ scnprintf(hattr->name, sizeof(hattr->name), template,
+ index + hwmon_attr_base(type));
+ name = hattr->name;
+ }
+
+ hattr->type = type;
+ hattr->attr = attr;
+ hattr->index = index;
+ hattr->ops = ops;
+
+ dattr = &hattr->dev_attr;
+ dattr->show = is_string ? hwmon_attr_show_string : hwmon_attr_show;
+ dattr->store = hwmon_attr_store;
+
+ a = &dattr->attr;
+ sysfs_attr_init(a);
+ a->name = name;
+ a->mode = mode;
+
+ return a;
+}
+
+/*
+ * Chip attributes are not attribute templates but actual sysfs attributes.
+ * See hwmon_genattr() for special handling.
+ */
+static const char * const hwmon_chip_attrs[] = {
+ [hwmon_chip_temp_reset_history] = "temp_reset_history",
+ [hwmon_chip_in_reset_history] = "in_reset_history",
+ [hwmon_chip_curr_reset_history] = "curr_reset_history",
+ [hwmon_chip_power_reset_history] = "power_reset_history",
+ [hwmon_chip_update_interval] = "update_interval",
+ [hwmon_chip_alarms] = "alarms",
+ [hwmon_chip_samples] = "samples",
+ [hwmon_chip_curr_samples] = "curr_samples",
+ [hwmon_chip_in_samples] = "in_samples",
+ [hwmon_chip_power_samples] = "power_samples",
+ [hwmon_chip_temp_samples] = "temp_samples",
+};
+
+static const char * const hwmon_temp_attr_templates[] = {
+ [hwmon_temp_enable] = "temp%d_enable",
+ [hwmon_temp_input] = "temp%d_input",
+ [hwmon_temp_type] = "temp%d_type",
+ [hwmon_temp_lcrit] = "temp%d_lcrit",
+ [hwmon_temp_lcrit_hyst] = "temp%d_lcrit_hyst",
+ [hwmon_temp_min] = "temp%d_min",
+ [hwmon_temp_min_hyst] = "temp%d_min_hyst",
+ [hwmon_temp_max] = "temp%d_max",
+ [hwmon_temp_max_hyst] = "temp%d_max_hyst",
+ [hwmon_temp_crit] = "temp%d_crit",
+ [hwmon_temp_crit_hyst] = "temp%d_crit_hyst",
+ [hwmon_temp_emergency] = "temp%d_emergency",
+ [hwmon_temp_emergency_hyst] = "temp%d_emergency_hyst",
+ [hwmon_temp_alarm] = "temp%d_alarm",
+ [hwmon_temp_lcrit_alarm] = "temp%d_lcrit_alarm",
+ [hwmon_temp_min_alarm] = "temp%d_min_alarm",
+ [hwmon_temp_max_alarm] = "temp%d_max_alarm",
+ [hwmon_temp_crit_alarm] = "temp%d_crit_alarm",
+ [hwmon_temp_emergency_alarm] = "temp%d_emergency_alarm",
+ [hwmon_temp_fault] = "temp%d_fault",
+ [hwmon_temp_offset] = "temp%d_offset",
+ [hwmon_temp_label] = "temp%d_label",
+ [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[] = {
+ [hwmon_in_enable] = "in%d_enable",
+ [hwmon_in_input] = "in%d_input",
+ [hwmon_in_min] = "in%d_min",
+ [hwmon_in_max] = "in%d_max",
+ [hwmon_in_lcrit] = "in%d_lcrit",
+ [hwmon_in_crit] = "in%d_crit",
+ [hwmon_in_average] = "in%d_average",
+ [hwmon_in_lowest] = "in%d_lowest",
+ [hwmon_in_highest] = "in%d_highest",
+ [hwmon_in_reset_history] = "in%d_reset_history",
+ [hwmon_in_label] = "in%d_label",
+ [hwmon_in_alarm] = "in%d_alarm",
+ [hwmon_in_min_alarm] = "in%d_min_alarm",
+ [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[] = {
+ [hwmon_curr_enable] = "curr%d_enable",
+ [hwmon_curr_input] = "curr%d_input",
+ [hwmon_curr_min] = "curr%d_min",
+ [hwmon_curr_max] = "curr%d_max",
+ [hwmon_curr_lcrit] = "curr%d_lcrit",
+ [hwmon_curr_crit] = "curr%d_crit",
+ [hwmon_curr_average] = "curr%d_average",
+ [hwmon_curr_lowest] = "curr%d_lowest",
+ [hwmon_curr_highest] = "curr%d_highest",
+ [hwmon_curr_reset_history] = "curr%d_reset_history",
+ [hwmon_curr_label] = "curr%d_label",
+ [hwmon_curr_alarm] = "curr%d_alarm",
+ [hwmon_curr_min_alarm] = "curr%d_min_alarm",
+ [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[] = {
+ [hwmon_power_enable] = "power%d_enable",
+ [hwmon_power_average] = "power%d_average",
+ [hwmon_power_average_interval] = "power%d_average_interval",
+ [hwmon_power_average_interval_max] = "power%d_interval_max",
+ [hwmon_power_average_interval_min] = "power%d_interval_min",
+ [hwmon_power_average_highest] = "power%d_average_highest",
+ [hwmon_power_average_lowest] = "power%d_average_lowest",
+ [hwmon_power_average_max] = "power%d_average_max",
+ [hwmon_power_average_min] = "power%d_average_min",
+ [hwmon_power_input] = "power%d_input",
+ [hwmon_power_input_highest] = "power%d_input_highest",
+ [hwmon_power_input_lowest] = "power%d_input_lowest",
+ [hwmon_power_reset_history] = "power%d_reset_history",
+ [hwmon_power_accuracy] = "power%d_accuracy",
+ [hwmon_power_cap] = "power%d_cap",
+ [hwmon_power_cap_hyst] = "power%d_cap_hyst",
+ [hwmon_power_cap_max] = "power%d_cap_max",
+ [hwmon_power_cap_min] = "power%d_cap_min",
+ [hwmon_power_min] = "power%d_min",
+ [hwmon_power_max] = "power%d_max",
+ [hwmon_power_lcrit] = "power%d_lcrit",
+ [hwmon_power_crit] = "power%d_crit",
+ [hwmon_power_label] = "power%d_label",
+ [hwmon_power_alarm] = "power%d_alarm",
+ [hwmon_power_cap_alarm] = "power%d_cap_alarm",
+ [hwmon_power_min_alarm] = "power%d_min_alarm",
+ [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[] = {
+ [hwmon_energy_enable] = "energy%d_enable",
+ [hwmon_energy_input] = "energy%d_input",
+ [hwmon_energy_label] = "energy%d_label",
+};
+
+static const char * const hwmon_humidity_attr_templates[] = {
+ [hwmon_humidity_enable] = "humidity%d_enable",
+ [hwmon_humidity_input] = "humidity%d_input",
+ [hwmon_humidity_label] = "humidity%d_label",
+ [hwmon_humidity_min] = "humidity%d_min",
+ [hwmon_humidity_min_hyst] = "humidity%d_min_hyst",
+ [hwmon_humidity_max] = "humidity%d_max",
+ [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[] = {
+ [hwmon_fan_enable] = "fan%d_enable",
+ [hwmon_fan_input] = "fan%d_input",
+ [hwmon_fan_label] = "fan%d_label",
+ [hwmon_fan_min] = "fan%d_min",
+ [hwmon_fan_max] = "fan%d_max",
+ [hwmon_fan_div] = "fan%d_div",
+ [hwmon_fan_pulses] = "fan%d_pulses",
+ [hwmon_fan_target] = "fan%d_target",
+ [hwmon_fan_alarm] = "fan%d_alarm",
+ [hwmon_fan_min_alarm] = "fan%d_min_alarm",
+ [hwmon_fan_max_alarm] = "fan%d_max_alarm",
+ [hwmon_fan_fault] = "fan%d_fault",
+};
+
+static const char * const hwmon_pwm_attr_templates[] = {
+ [hwmon_pwm_input] = "pwm%d",
+ [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[] = {
+ [hwmon_intrusion_alarm] = "intrusion%d_alarm",
+ [hwmon_intrusion_beep] = "intrusion%d_beep",
+};
+
+static const char * const *__templates[] = {
+ [hwmon_chip] = hwmon_chip_attrs,
+ [hwmon_temp] = hwmon_temp_attr_templates,
+ [hwmon_in] = hwmon_in_attr_templates,
+ [hwmon_curr] = hwmon_curr_attr_templates,
+ [hwmon_power] = hwmon_power_attr_templates,
+ [hwmon_energy] = hwmon_energy_attr_templates,
+ [hwmon_humidity] = hwmon_humidity_attr_templates,
+ [hwmon_fan] = hwmon_fan_attr_templates,
+ [hwmon_pwm] = hwmon_pwm_attr_templates,
+ [hwmon_intrusion] = hwmon_intrusion_attr_templates,
+};
+
+static const int __templates_size[] = {
+ [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attrs),
+ [hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates),
+ [hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates),
+ [hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates),
+ [hwmon_power] = ARRAY_SIZE(hwmon_power_attr_templates),
+ [hwmon_energy] = ARRAY_SIZE(hwmon_energy_attr_templates),
+ [hwmon_humidity] = ARRAY_SIZE(hwmon_humidity_attr_templates),
+ [hwmon_fan] = ARRAY_SIZE(hwmon_fan_attr_templates),
+ [hwmon_pwm] = ARRAY_SIZE(hwmon_pwm_attr_templates),
+ [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;
+
+ for (i = n = 0; info->config[i]; i++)
+ n += hweight32(info->config[i]);
+
+ return n;
+}
+
+static int hwmon_genattrs(const void *drvdata,
+ struct attribute **attrs,
+ const struct hwmon_ops *ops,
+ const struct hwmon_channel_info *info)
+{
+ const char * const *templates;
+ int template_size;
+ int i, aindex = 0;
+
+ if (info->type >= ARRAY_SIZE(__templates))
+ return -EINVAL;
+
+ templates = __templates[info->type];
+ template_size = __templates_size[info->type];
+
+ for (i = 0; info->config[i]; i++) {
+ u32 attr_mask = info->config[i];
+ u32 attr;
+
+ while (attr_mask) {
+ struct attribute *a;
+
+ attr = __ffs(attr_mask);
+ attr_mask &= ~BIT(attr);
+ if (attr >= template_size)
+ return -EINVAL;
+ a = hwmon_genattr(drvdata, info->type, attr, i,
+ templates[attr], ops);
+ if (IS_ERR(a)) {
+ if (PTR_ERR(a) != -ENOENT)
+ return PTR_ERR(a);
+ continue;
+ }
+ attrs[aindex++] = a;
+ }
+ }
+ return aindex;
+}
+
+static struct attribute **
+__hwmon_create_attrs(const void *drvdata, const struct hwmon_chip_info *chip)
+{
+ int ret, i, aindex = 0, nattrs = 0;
+ struct attribute **attrs;
+
+ for (i = 0; chip->info[i]; i++)
+ nattrs += hwmon_num_channel_attrs(chip->info[i]);
+
+ if (nattrs == 0)
+ return ERR_PTR(-EINVAL);
+
+ attrs = kcalloc(nattrs + 1, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; chip->info[i]; i++) {
+ ret = hwmon_genattrs(drvdata, &attrs[aindex], chip->ops,
+ chip->info[i]);
+ if (ret < 0) {
+ hwmon_free_attrs(attrs);
+ return ERR_PTR(ret);
+ }
+ aindex += ret;
+ }
+
+ return attrs;
+}
+
+static struct device *
+__hwmon_device_register(struct device *dev, const char *name, void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
+{
+ struct hwmon_device *hwdev;
+ const char *label;
+ struct device *hdev;
+ struct device *tdev = dev;
+ int i, err, id;
+
+ /* Complain about invalid characters in hwmon name attribute */
+ if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
+ dev_warn(dev,
+ "hwmon: '%s' is not a valid name attribute, please fix\n",
+ name);
+
+ id = ida_alloc(&hwmon_ida, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
+
+ hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL);
+ if (hwdev == NULL) {
+ err = -ENOMEM;
+ goto ida_remove;
+ }
+
+ hdev = &hwdev->dev;
+
+ if (chip) {
+ struct attribute **attrs;
+ int ngroups = 2; /* terminating NULL plus &hwdev->groups */
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ ngroups++;
+
+ hwdev->groups = kcalloc(ngroups, sizeof(*groups), GFP_KERNEL);
+ if (!hwdev->groups) {
+ err = -ENOMEM;
+ goto free_hwmon;
+ }
+
+ attrs = __hwmon_create_attrs(drvdata, chip);
+ if (IS_ERR(attrs)) {
+ err = PTR_ERR(attrs);
+ goto free_hwmon;
+ }
+
+ hwdev->group.attrs = attrs;
+ ngroups = 0;
+ hwdev->groups[ngroups++] = &hwdev->group;
+
+ if (groups) {
+ for (i = 0; groups[i]; i++)
+ hwdev->groups[ngroups++] = groups[i];
+ }
+
+ hdev->groups = hwdev->groups;
+ } else {
+ 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;
+ while (tdev && !tdev->of_node)
+ tdev = tdev->parent;
+ hdev->of_node = tdev ? tdev->of_node : NULL;
+ hwdev->chip = chip;
+ dev_set_drvdata(hdev, drvdata);
+ dev_set_name(hdev, HWMON_ID_FORMAT, id);
+ err = device_register(hdev);
+ if (err) {
+ put_device(hdev);
+ goto ida_remove;
+ }
+
+ INIT_LIST_HEAD(&hwdev->tzdata);
+
+ if (hdev->of_node && chip && chip->ops->read &&
+ chip->info[0]->type == hwmon_chip &&
+ (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
+ 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;
+ }
+ }
+
+ return hdev;
+
+free_hwmon:
+ hwmon_dev_release(hdev);
+ida_remove:
+ ida_free(&hwmon_ida, id);
+ return ERR_PTR(err);
+}
+
+/**
+ * hwmon_device_register_with_groups - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @groups: List of attribute groups to create
+ *
+ * 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_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups)
+{
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_device_register(dev, name, drvdata, NULL, groups);
+}
+EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
+
+/**
+ * hwmon_device_register_with_info - register w/ hwmon
+ * @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.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *
+hwmon_device_register_with_info(struct device *dev, const char *name,
+ void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **extra_groups)
+{
+ if (!dev || !name || !chip)
+ return ERR_PTR(-EINVAL);
+
+ if (!chip->ops || !chip->ops->is_visible || !chip->info)
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_device_register(dev, name, drvdata, chip, extra_groups);
+}
+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
+ *
+ * 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(struct device *dev)
+{
+ dev_warn(dev,
+ "hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info().\n");
+
+ return __hwmon_device_register(dev, NULL, NULL, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(hwmon_device_register);
+
+/**
+ * hwmon_device_unregister - removes the previously registered class device
+ *
+ * @dev: the class device to destroy
+ */
+void hwmon_device_unregister(struct device *dev)
+{
+ int id;
+
+ if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
+ device_unregister(dev);
+ ida_free(&hwmon_ida, id);
+ } else
+ dev_dbg(dev->parent,
+ "hwmon_device_unregister() failed: bad class ID!\n");
+}
+EXPORT_SYMBOL_GPL(hwmon_device_unregister);
+
+static void devm_hwmon_release(struct device *dev, void *res)
+{
+ struct device *hwdev = *(struct device **)res;
+
+ hwmon_device_unregister(hwdev);
+}
+
+/**
+ * devm_hwmon_device_register_with_groups - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @groups: List of attribute groups to create
+ *
+ * Returns the pointer to the new device. The new device is automatically
+ * unregistered with the parent device.
+ */
+struct device *
+devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups)
+{
+ struct device **ptr, *hwdev;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups);
+ if (IS_ERR(hwdev))
+ goto error;
+
+ *ptr = hwdev;
+ devres_add(dev, ptr);
+ return hwdev;
+
+error:
+ devres_free(ptr);
+ return hwdev;
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
+
+/**
+ * devm_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
+ * @groups: pointer to list of driver specific attribute groups
+ *
+ * Returns the pointer to the new device. The new device is automatically
+ * unregistered with the parent device.
+ */
+struct device *
+devm_hwmon_device_register_with_info(struct device *dev, const char *name,
+ void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
+{
+ struct device **ptr, *hwdev;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ hwdev = hwmon_device_register_with_info(dev, name, drvdata, chip,
+ groups);
+ if (IS_ERR(hwdev))
+ goto error;
+
+ *ptr = hwdev;
+ devres_add(dev, ptr);
+
+ return hwdev;
+
+error:
+ devres_free(ptr);
+ return hwdev;
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info);
+
+static int devm_hwmon_match(struct device *dev, void *res, void *data)
+{
+ struct device **hwdev = res;
+
+ return *hwdev == data;
+}
+
+/**
+ * devm_hwmon_device_unregister - removes a previously registered hwmon device
+ *
+ * @dev: the parent device of the device to unregister
+ */
+void devm_hwmon_device_unregister(struct device *dev)
+{
+ WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, 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
+ struct pci_dev *sb;
+ u16 base;
+ u8 enable;
+
+ /* Open access to 0x295-0x296 on MSI MS-7031 */
+ sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL);
+ if (sb) {
+ if (sb->subsystem_vendor == 0x1462 && /* MSI */
+ sb->subsystem_device == 0x0031) { /* MS-7031 */
+ pci_read_config_byte(sb, 0x48, &enable);
+ pci_read_config_word(sb, 0x64, &base);
+
+ if (base == 0 && !(enable & BIT(2))) {
+ dev_info(&sb->dev,
+ "Opening wide generic port at 0x295\n");
+ pci_write_config_word(sb, 0x64, 0x295);
+ pci_write_config_byte(sb, 0x48,
+ enable | BIT(2));
+ }
+ }
+ pci_dev_put(sb);
+ }
+#endif
+}
+
+static int __init hwmon_init(void)
+{
+ int err;
+
+ hwmon_pci_quirks();
+
+ err = class_register(&hwmon_class);
+ if (err) {
+ pr_err("couldn't register hwmon sysfs class\n");
+ return err;
+ }
+ return 0;
+}
+
+static void __exit hwmon_exit(void)
+{
+ class_unregister(&hwmon_class);
+}
+
+subsys_initcall(hwmon_init);
+module_exit(hwmon_exit);
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
+MODULE_DESCRIPTION("hardware monitoring sysfs/class support");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/hwmon/i5500_temp.c b/drivers/hwmon/i5500_temp.c
new file mode 100644
index 000000000..23b9f94fe
--- /dev/null
+++ b/drivers/hwmon/i5500_temp.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * i5500_temp - Driver for Intel 5500/5520/X58 chipset thermal sensor
+ *
+ * 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>
+#include <linux/jiffies.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Register definitions from datasheet */
+#define REG_TSTHRCATA 0xE2
+#define REG_TSCTRL 0xE8
+#define REG_TSTHRRPEX 0xEB
+#define REG_TSTHRLO 0xEC
+#define REG_TSTHRHI 0xEE
+#define REG_CTHINT 0xF0
+#define REG_TSFSC 0xF3
+#define REG_CTSTS 0xF4
+#define REG_TSTHRRQPI 0xF5
+#define REG_CTCTRL 0xF7
+#define REG_TSTIMER 0xF8
+
+static umode_t i5500_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ return 0444;
+}
+
+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);
+ u16 tsthr;
+ s8 tsfsc;
+ u8 ctsts;
+
+ 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 -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops i5500_ops = {
+ .is_visible = i5500_is_visible,
+ .read = i5500_read,
+};
+
+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
+};
+
+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) },
+ { 0 },
+};
+
+MODULE_DEVICE_TABLE(pci, i5500_temp_ids);
+
+static int i5500_temp_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int err;
+ struct device *hwmon_dev;
+ u32 tstimer;
+ s8 tsfsc;
+
+ err = pcim_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to enable device\n");
+ return err;
+ }
+
+ pci_read_config_byte(pdev, REG_TSFSC, &tsfsc);
+ pci_read_config_dword(pdev, REG_TSTIMER, &tstimer);
+ if (tsfsc == 0x7F && tstimer == 0x07D30D40) {
+ dev_notice(&pdev->dev, "Sensor seems to be disabled\n");
+ return -ENODEV;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "intel5500", NULL,
+ &i5500_chip_info, NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct pci_driver i5500_temp_driver = {
+ .name = "i5500_temp",
+ .id_table = i5500_temp_ids,
+ .probe = i5500_temp_probe,
+};
+
+module_pci_driver(i5500_temp_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("Intel 5500/5520/X58 chipset thermal sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c
new file mode 100644
index 000000000..783fa936e
--- /dev/null
+++ b/drivers/hwmon/i5k_amb.c
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A hwmon driver for the Intel 5000 series chipset FB-DIMM AMB
+ * temperature sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/log2.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define DRVNAME "i5k_amb"
+
+#define I5K_REG_AMB_BASE_ADDR 0x48
+#define I5K_REG_AMB_LEN_ADDR 0x50
+#define I5K_REG_CHAN0_PRESENCE_ADDR 0x64
+#define I5K_REG_CHAN1_PRESENCE_ADDR 0x66
+
+#define AMB_REG_TEMP_MIN_ADDR 0x80
+#define AMB_REG_TEMP_MID_ADDR 0x81
+#define AMB_REG_TEMP_MAX_ADDR 0x82
+#define AMB_REG_TEMP_STATUS_ADDR 0x84
+#define AMB_REG_TEMP_ADDR 0x85
+
+#define AMB_CONFIG_SIZE 2048
+#define AMB_FUNC_3_OFFSET 768
+
+static unsigned long amb_reg_temp_status(unsigned int amb)
+{
+ return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_STATUS_ADDR +
+ AMB_CONFIG_SIZE * amb;
+}
+
+static unsigned long amb_reg_temp_min(unsigned int amb)
+{
+ return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MIN_ADDR +
+ AMB_CONFIG_SIZE * amb;
+}
+
+static unsigned long amb_reg_temp_mid(unsigned int amb)
+{
+ return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MID_ADDR +
+ AMB_CONFIG_SIZE * amb;
+}
+
+static unsigned long amb_reg_temp_max(unsigned int amb)
+{
+ return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MAX_ADDR +
+ AMB_CONFIG_SIZE * amb;
+}
+
+static unsigned long amb_reg_temp(unsigned int amb)
+{
+ return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_ADDR +
+ AMB_CONFIG_SIZE * amb;
+}
+
+#define MAX_MEM_CHANNELS 4
+#define MAX_AMBS_PER_CHANNEL 16
+#define MAX_AMBS (MAX_MEM_CHANNELS * \
+ MAX_AMBS_PER_CHANNEL)
+#define CHANNEL_SHIFT 4
+#define DIMM_MASK 0xF
+/*
+ * Ugly hack: For some reason the highest bit is set if there
+ * are _any_ DIMMs in the channel. Attempting to read from
+ * this "high-order" AMB results in a memory bus error, so
+ * for now we'll just ignore that top bit, even though that
+ * might prevent us from seeing the 16th DIMM in the channel.
+ */
+#define REAL_MAX_AMBS_PER_CHANNEL 15
+#define KNOBS_PER_AMB 6
+
+static unsigned long amb_num_from_reg(unsigned int byte_num, unsigned int bit)
+{
+ return byte_num * MAX_AMBS_PER_CHANNEL + bit;
+}
+
+#define AMB_SYSFS_NAME_LEN 16
+struct i5k_device_attribute {
+ struct sensor_device_attribute s_attr;
+ char name[AMB_SYSFS_NAME_LEN];
+};
+
+struct i5k_amb_data {
+ struct device *hwmon_dev;
+
+ unsigned long amb_base;
+ unsigned long amb_len;
+ u16 amb_present[MAX_MEM_CHANNELS];
+ void __iomem *amb_mmio;
+ struct i5k_device_attribute *attrs;
+ unsigned int num_attrs;
+};
+
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", DRVNAME);
+}
+
+
+static DEVICE_ATTR_RO(name);
+
+static struct platform_device *amb_pdev;
+
+static u8 amb_read_byte(struct i5k_amb_data *data, unsigned long offset)
+{
+ return ioread8(data->amb_mmio + offset);
+}
+
+static void amb_write_byte(struct i5k_amb_data *data, unsigned long offset,
+ u8 val)
+{
+ iowrite8(val, data->amb_mmio + offset);
+}
+
+static ssize_t show_amb_alarm(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+
+ if (!(amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x20) &&
+ (amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x8))
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t store_amb_min(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+ unsigned long temp;
+ int ret = kstrtoul(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ temp = temp / 500;
+ if (temp > 255)
+ temp = 255;
+
+ amb_write_byte(data, amb_reg_temp_min(attr->index), temp);
+ return count;
+}
+
+static ssize_t store_amb_mid(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+ unsigned long temp;
+ int ret = kstrtoul(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ temp = temp / 500;
+ if (temp > 255)
+ temp = 255;
+
+ amb_write_byte(data, amb_reg_temp_mid(attr->index), temp);
+ return count;
+}
+
+static ssize_t store_amb_max(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+ unsigned long temp;
+ int ret = kstrtoul(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ temp = temp / 500;
+ if (temp > 255)
+ temp = 255;
+
+ amb_write_byte(data, amb_reg_temp_max(attr->index), temp);
+ return count;
+}
+
+static ssize_t show_amb_min(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ 500 * amb_read_byte(data, amb_reg_temp_min(attr->index)));
+}
+
+static ssize_t show_amb_mid(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ 500 * amb_read_byte(data, amb_reg_temp_mid(attr->index)));
+}
+
+static ssize_t show_amb_max(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ 500 * amb_read_byte(data, amb_reg_temp_max(attr->index)));
+}
+
+static ssize_t show_amb_temp(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i5k_amb_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ 500 * amb_read_byte(data, amb_reg_temp(attr->index)));
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "Ch. %d DIMM %d\n", attr->index >> CHANNEL_SHIFT,
+ attr->index & DIMM_MASK);
+}
+
+static int i5k_amb_hwmon_init(struct platform_device *pdev)
+{
+ int i, j, k, d = 0;
+ u16 c;
+ int res = 0;
+ int num_ambs = 0;
+ struct i5k_amb_data *data = platform_get_drvdata(pdev);
+
+ /* Count the number of AMBs found */
+ /* ignore the high-order bit, see "Ugly hack" comment above */
+ for (i = 0; i < MAX_MEM_CHANNELS; i++)
+ num_ambs += hweight16(data->amb_present[i] & 0x7fff);
+
+ /* Set up sysfs stuff */
+ data->attrs = kzalloc(array3_size(num_ambs, KNOBS_PER_AMB,
+ sizeof(*data->attrs)),
+ GFP_KERNEL);
+ if (!data->attrs)
+ return -ENOMEM;
+ data->num_attrs = 0;
+
+ for (i = 0; i < MAX_MEM_CHANNELS; i++) {
+ c = data->amb_present[i];
+ for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) {
+ struct i5k_device_attribute *iattr;
+
+ k = amb_num_from_reg(i, j);
+ if (!(c & 0x1))
+ continue;
+ d++;
+
+ /* sysfs label */
+ iattr = data->attrs + data->num_attrs;
+ snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
+ "temp%d_label", d);
+ iattr->s_attr.dev_attr.attr.name = iattr->name;
+ iattr->s_attr.dev_attr.attr.mode = 0444;
+ iattr->s_attr.dev_attr.show = show_label;
+ iattr->s_attr.index = k;
+ sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
+ res = device_create_file(&pdev->dev,
+ &iattr->s_attr.dev_attr);
+ if (res)
+ goto exit_remove;
+ data->num_attrs++;
+
+ /* Temperature sysfs knob */
+ iattr = data->attrs + data->num_attrs;
+ snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
+ "temp%d_input", d);
+ iattr->s_attr.dev_attr.attr.name = iattr->name;
+ iattr->s_attr.dev_attr.attr.mode = 0444;
+ iattr->s_attr.dev_attr.show = show_amb_temp;
+ iattr->s_attr.index = k;
+ sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
+ res = device_create_file(&pdev->dev,
+ &iattr->s_attr.dev_attr);
+ if (res)
+ goto exit_remove;
+ data->num_attrs++;
+
+ /* Temperature min sysfs knob */
+ iattr = data->attrs + data->num_attrs;
+ snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
+ "temp%d_min", d);
+ iattr->s_attr.dev_attr.attr.name = iattr->name;
+ iattr->s_attr.dev_attr.attr.mode = 0644;
+ iattr->s_attr.dev_attr.show = show_amb_min;
+ iattr->s_attr.dev_attr.store = store_amb_min;
+ iattr->s_attr.index = k;
+ sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
+ res = device_create_file(&pdev->dev,
+ &iattr->s_attr.dev_attr);
+ if (res)
+ goto exit_remove;
+ data->num_attrs++;
+
+ /* Temperature mid sysfs knob */
+ iattr = data->attrs + data->num_attrs;
+ snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
+ "temp%d_mid", d);
+ iattr->s_attr.dev_attr.attr.name = iattr->name;
+ iattr->s_attr.dev_attr.attr.mode = 0644;
+ iattr->s_attr.dev_attr.show = show_amb_mid;
+ iattr->s_attr.dev_attr.store = store_amb_mid;
+ iattr->s_attr.index = k;
+ sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
+ res = device_create_file(&pdev->dev,
+ &iattr->s_attr.dev_attr);
+ if (res)
+ goto exit_remove;
+ data->num_attrs++;
+
+ /* Temperature max sysfs knob */
+ iattr = data->attrs + data->num_attrs;
+ snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
+ "temp%d_max", d);
+ iattr->s_attr.dev_attr.attr.name = iattr->name;
+ iattr->s_attr.dev_attr.attr.mode = 0644;
+ iattr->s_attr.dev_attr.show = show_amb_max;
+ iattr->s_attr.dev_attr.store = store_amb_max;
+ iattr->s_attr.index = k;
+ sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
+ res = device_create_file(&pdev->dev,
+ &iattr->s_attr.dev_attr);
+ if (res)
+ goto exit_remove;
+ data->num_attrs++;
+
+ /* Temperature alarm sysfs knob */
+ iattr = data->attrs + data->num_attrs;
+ snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
+ "temp%d_alarm", d);
+ iattr->s_attr.dev_attr.attr.name = iattr->name;
+ iattr->s_attr.dev_attr.attr.mode = 0444;
+ iattr->s_attr.dev_attr.show = show_amb_alarm;
+ iattr->s_attr.index = k;
+ sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
+ res = device_create_file(&pdev->dev,
+ &iattr->s_attr.dev_attr);
+ if (res)
+ goto exit_remove;
+ data->num_attrs++;
+ }
+ }
+
+ res = device_create_file(&pdev->dev, &dev_attr_name);
+ if (res)
+ goto exit_remove;
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ res = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ return res;
+
+exit_remove:
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ for (i = 0; i < data->num_attrs; i++)
+ device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
+ kfree(data->attrs);
+
+ return res;
+}
+
+static int i5k_amb_add(void)
+{
+ int res;
+
+ /* only ever going to be one of these */
+ amb_pdev = platform_device_alloc(DRVNAME, 0);
+ if (!amb_pdev)
+ return -ENOMEM;
+
+ res = platform_device_add(amb_pdev);
+ if (res)
+ goto err;
+ return 0;
+
+err:
+ platform_device_put(amb_pdev);
+ return res;
+}
+
+static int i5k_find_amb_registers(struct i5k_amb_data *data,
+ unsigned long devid)
+{
+ struct pci_dev *pcidev;
+ u32 val32;
+ int res = -ENODEV;
+
+ /* Find AMB register memory space */
+ pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ devid,
+ NULL);
+ if (!pcidev)
+ return -ENODEV;
+
+ pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32);
+ if (val32 == (u32)~0)
+ goto out;
+ data->amb_base = val32;
+
+ pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32);
+ if (val32 == (u32)~0)
+ goto out;
+ data->amb_len = val32;
+
+ /* Is it big enough? */
+ if (data->amb_len < AMB_CONFIG_SIZE * MAX_AMBS) {
+ dev_err(&pcidev->dev, "AMB region too small!\n");
+ goto out;
+ }
+
+ res = 0;
+out:
+ pci_dev_put(pcidev);
+ return res;
+}
+
+static int i5k_channel_probe(u16 *amb_present, unsigned long dev_id)
+{
+ struct pci_dev *pcidev;
+ u16 val16;
+ int res = -ENODEV;
+
+ /* Copy the DIMM presence map for these two channels */
+ pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
+ if (!pcidev)
+ return -ENODEV;
+
+ pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16);
+ if (val16 == (u16)~0)
+ goto out;
+ amb_present[0] = val16;
+
+ pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16);
+ if (val16 == (u16)~0)
+ goto out;
+ amb_present[1] = val16;
+
+ res = 0;
+
+out:
+ pci_dev_put(pcidev);
+ return res;
+}
+
+static struct {
+ unsigned long err;
+ unsigned long fbd0;
+} chipset_ids[] = {
+ { PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 },
+ { PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 },
+ { 0, 0 }
+};
+
+#ifdef MODULE
+static const struct pci_device_id i5k_amb_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, i5k_amb_ids);
+#endif
+
+static int i5k_amb_probe(struct platform_device *pdev)
+{
+ struct i5k_amb_data *data;
+ struct resource *reso;
+ int i, res;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* Figure out where the AMB registers live */
+ i = 0;
+ do {
+ res = i5k_find_amb_registers(data, chipset_ids[i].err);
+ if (res == 0)
+ break;
+ i++;
+ } while (chipset_ids[i].err);
+
+ if (res)
+ goto err;
+
+ /* Copy the DIMM presence map for the first two channels */
+ res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0);
+ if (res)
+ goto err;
+
+ /* Copy the DIMM presence map for the optional second two channels */
+ i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1);
+
+ /* Set up resource regions */
+ reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
+ if (!reso) {
+ res = -EBUSY;
+ goto err;
+ }
+
+ data->amb_mmio = ioremap(data->amb_base, data->amb_len);
+ if (!data->amb_mmio) {
+ res = -EBUSY;
+ goto err_map_failed;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ res = i5k_amb_hwmon_init(pdev);
+ if (res)
+ goto err_init_failed;
+
+ return res;
+
+err_init_failed:
+ iounmap(data->amb_mmio);
+err_map_failed:
+ release_mem_region(data->amb_base, data->amb_len);
+err:
+ kfree(data);
+ return res;
+}
+
+static int i5k_amb_remove(struct platform_device *pdev)
+{
+ int i;
+ struct i5k_amb_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ for (i = 0; i < data->num_attrs; i++)
+ device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
+ kfree(data->attrs);
+ iounmap(data->amb_mmio);
+ release_mem_region(data->amb_base, data->amb_len);
+ kfree(data);
+ return 0;
+}
+
+static struct platform_driver i5k_amb_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = i5k_amb_probe,
+ .remove = i5k_amb_remove,
+};
+
+static int __init i5k_amb_init(void)
+{
+ int res;
+
+ res = platform_driver_register(&i5k_amb_driver);
+ if (res)
+ return res;
+
+ res = i5k_amb_add();
+ if (res)
+ platform_driver_unregister(&i5k_amb_driver);
+
+ return res;
+}
+
+static void __exit i5k_amb_exit(void)
+{
+ platform_device_unregister(amb_pdev);
+ platform_driver_unregister(&i5k_amb_driver);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
+MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor");
+MODULE_LICENSE("GPL");
+
+module_init(i5k_amb_init);
+module_exit(i5k_amb_exit);
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
new file mode 100644
index 000000000..157e232aa
--- /dev/null
+++ b/drivers/hwmon/ibmaem.c
@@ -0,0 +1,1107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A hwmon driver for the IBM System Director Active Energy Manager (AEM)
+ * temperature/power/energy sensors and capping functionality.
+ * Copyright (C) 2008 IBM
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/kdev_t.h>
+#include <linux/spinlock.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/math64.h>
+#include <linux/time.h>
+#include <linux/err.h>
+
+#define REFRESH_INTERVAL (HZ)
+#define IPMI_TIMEOUT (30 * HZ)
+#define DRVNAME "aem"
+
+#define AEM_NETFN 0x2E
+
+#define AEM_FIND_FW_CMD 0x80
+#define AEM_ELEMENT_CMD 0x81
+#define AEM_FW_INSTANCE_CMD 0x82
+
+#define AEM_READ_ELEMENT_CFG 0x80
+#define AEM_READ_BUFFER 0x81
+#define AEM_READ_REGISTER 0x82
+#define AEM_WRITE_REGISTER 0x83
+#define AEM_SET_REG_MASK 0x84
+#define AEM_CLEAR_REG_MASK 0x85
+#define AEM_READ_ELEMENT_CFG2 0x86
+
+#define AEM_CONTROL_ELEMENT 0
+#define AEM_ENERGY_ELEMENT 1
+#define AEM_CLOCK_ELEMENT 4
+#define AEM_POWER_CAP_ELEMENT 7
+#define AEM_EXHAUST_ELEMENT 9
+#define AEM_POWER_ELEMENT 10
+
+#define AEM_MODULE_TYPE_ID 0x0001
+
+#define AEM2_NUM_ENERGY_REGS 2
+#define AEM2_NUM_PCAP_REGS 6
+#define AEM2_NUM_TEMP_REGS 2
+#define AEM2_NUM_SENSORS 14
+
+#define AEM1_NUM_ENERGY_REGS 1
+#define AEM1_NUM_SENSORS 3
+
+/* AEM 2.x has more energy registers */
+#define AEM_NUM_ENERGY_REGS AEM2_NUM_ENERGY_REGS
+/* AEM 2.x needs more sensor files */
+#define AEM_NUM_SENSORS AEM2_NUM_SENSORS
+
+#define POWER_CAP 0
+#define POWER_CAP_MAX_HOTPLUG 1
+#define POWER_CAP_MAX 2
+#define POWER_CAP_MIN_WARNING 3
+#define POWER_CAP_MIN 4
+#define POWER_AUX 5
+
+#define AEM_DEFAULT_POWER_INTERVAL 1000
+#define AEM_MIN_POWER_INTERVAL 200
+#define UJ_PER_MJ 1000L
+
+static DEFINE_IDA(aem_ida);
+
+static struct platform_driver aem_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .bus = &platform_bus_type,
+ }
+};
+
+struct aem_ipmi_data {
+ struct completion read_complete;
+ struct ipmi_addr address;
+ struct ipmi_user *user;
+ int interface;
+
+ struct kernel_ipmi_msg tx_message;
+ long tx_msgid;
+
+ void *rx_msg_data;
+ unsigned short rx_msg_len;
+ unsigned char rx_result;
+ int rx_recv_type;
+
+ struct device *bmc_device;
+};
+
+struct aem_ro_sensor_template {
+ char *label;
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+ int index;
+};
+
+struct aem_rw_sensor_template {
+ char *label;
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+ ssize_t (*set)(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+ int index;
+};
+
+struct aem_data {
+ struct list_head list;
+
+ struct device *hwmon_dev;
+ struct platform_device *pdev;
+ struct mutex lock;
+ bool valid;
+ unsigned long last_updated; /* In jiffies */
+ u8 ver_major;
+ u8 ver_minor;
+ u8 module_handle;
+ int id;
+ struct aem_ipmi_data ipmi;
+
+ /* Function and buffer to update sensors */
+ void (*update)(struct aem_data *data);
+ struct aem_read_sensor_resp *rs_resp;
+
+ /*
+ * AEM 1.x sensors:
+ * Available sensors:
+ * Energy meter
+ * Power meter
+ *
+ * AEM 2.x sensors:
+ * Two energy meters
+ * Two power meters
+ * Two temperature sensors
+ * Six power cap registers
+ */
+
+ /* sysfs attrs */
+ struct sensor_device_attribute sensors[AEM_NUM_SENSORS];
+
+ /* energy use in mJ */
+ u64 energy[AEM_NUM_ENERGY_REGS];
+
+ /* power sampling interval in ms */
+ unsigned long power_period[AEM_NUM_ENERGY_REGS];
+
+ /* Everything past here is for AEM2 only */
+
+ /* power caps in dW */
+ u16 pcap[AEM2_NUM_PCAP_REGS];
+
+ /* exhaust temperature in C */
+ u8 temp[AEM2_NUM_TEMP_REGS];
+};
+
+/* Data structures returned by the AEM firmware */
+struct aem_iana_id {
+ u8 bytes[3];
+};
+static struct aem_iana_id system_x_id = {
+ .bytes = {0x4D, 0x4F, 0x00}
+};
+
+/* These are used to find AEM1 instances */
+struct aem_find_firmware_req {
+ struct aem_iana_id id;
+ u8 rsvd;
+ __be16 index;
+ __be16 module_type_id;
+} __packed;
+
+struct aem_find_firmware_resp {
+ struct aem_iana_id id;
+ u8 num_instances;
+} __packed;
+
+/* These are used to find AEM2 instances */
+struct aem_find_instance_req {
+ struct aem_iana_id id;
+ u8 instance_number;
+ __be16 module_type_id;
+} __packed;
+
+struct aem_find_instance_resp {
+ struct aem_iana_id id;
+ u8 num_instances;
+ u8 major;
+ u8 minor;
+ u8 module_handle;
+ u16 record_id;
+} __packed;
+
+/* These are used to query sensors */
+struct aem_read_sensor_req {
+ struct aem_iana_id id;
+ u8 module_handle;
+ u8 element;
+ u8 subcommand;
+ u8 reg;
+ u8 rx_buf_size;
+} __packed;
+
+struct aem_read_sensor_resp {
+ struct aem_iana_id id;
+ u8 bytes[];
+} __packed;
+
+/* Data structures to talk to the IPMI layer */
+struct aem_driver_data {
+ struct list_head aem_devices;
+ struct ipmi_smi_watcher bmc_events;
+ struct ipmi_user_hndl ipmi_hndlrs;
+};
+
+static void aem_register_bmc(int iface, struct device *dev);
+static void aem_bmc_gone(int iface);
+static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+
+static void aem_remove_sensors(struct aem_data *data);
+static int aem1_find_sensors(struct aem_data *data);
+static int aem2_find_sensors(struct aem_data *data);
+static void update_aem1_sensors(struct aem_data *data);
+static void update_aem2_sensors(struct aem_data *data);
+
+static struct aem_driver_data driver_data = {
+ .aem_devices = LIST_HEAD_INIT(driver_data.aem_devices),
+ .bmc_events = {
+ .owner = THIS_MODULE,
+ .new_smi = aem_register_bmc,
+ .smi_gone = aem_bmc_gone,
+ },
+ .ipmi_hndlrs = {
+ .ipmi_recv_hndl = aem_msg_handler,
+ },
+};
+
+/* Functions to talk to the IPMI layer */
+
+/* Initialize IPMI address, message buffers and user data */
+static int aem_init_ipmi_data(struct aem_ipmi_data *data, int iface,
+ struct device *bmc)
+{
+ int err;
+
+ init_completion(&data->read_complete);
+ data->bmc_device = bmc;
+
+ /* Initialize IPMI address */
+ data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ data->address.channel = IPMI_BMC_CHANNEL;
+ data->address.data[0] = 0;
+ data->interface = iface;
+
+ /* Initialize message buffers */
+ data->tx_msgid = 0;
+ data->tx_message.netfn = AEM_NETFN;
+
+ /* Create IPMI messaging interface user */
+ err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+ data, &data->user);
+ if (err < 0) {
+ dev_err(bmc,
+ "Unable to register user with IPMI interface %d\n",
+ data->interface);
+ return err;
+ }
+
+ return 0;
+}
+
+/* Send an IPMI command */
+static int aem_send_message(struct aem_ipmi_data *data)
+{
+ int err;
+
+ err = ipmi_validate_addr(&data->address, sizeof(data->address));
+ if (err)
+ goto out;
+
+ data->tx_msgid++;
+ err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+ &data->tx_message, data, 0, 0, 0);
+ if (err)
+ goto out1;
+
+ return 0;
+out1:
+ dev_err(data->bmc_device, "request_settime=%x\n", err);
+ return err;
+out:
+ dev_err(data->bmc_device, "validate_addr=%x\n", err);
+ return err;
+}
+
+/* Dispatch IPMI messages to callers */
+static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+ unsigned short rx_len;
+ struct aem_ipmi_data *data = user_msg_data;
+
+ if (msg->msgid != data->tx_msgid) {
+ dev_err(data->bmc_device,
+ "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
+ (int)msg->msgid,
+ (int)data->tx_msgid);
+ ipmi_free_recv_msg(msg);
+ return;
+ }
+
+ data->rx_recv_type = msg->recv_type;
+ if (msg->msg.data_len > 0)
+ data->rx_result = msg->msg.data[0];
+ else
+ data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+ if (msg->msg.data_len > 1) {
+ rx_len = msg->msg.data_len - 1;
+ if (data->rx_msg_len < rx_len)
+ rx_len = data->rx_msg_len;
+ data->rx_msg_len = rx_len;
+ memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+ } else
+ data->rx_msg_len = 0;
+
+ ipmi_free_recv_msg(msg);
+ complete(&data->read_complete);
+}
+
+/* Sensor support functions */
+
+/* Read a sensor value; must be called with data->lock held */
+static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
+ void *buf, size_t size)
+{
+ int rs_size, res;
+ struct aem_read_sensor_req rs_req;
+ /* Use preallocated rx buffer */
+ struct aem_read_sensor_resp *rs_resp = data->rs_resp;
+ struct aem_ipmi_data *ipmi = &data->ipmi;
+
+ /* AEM registers are 1, 2, 4 or 8 bytes */
+ switch (size) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rs_req.id = system_x_id;
+ rs_req.module_handle = data->module_handle;
+ rs_req.element = elt;
+ rs_req.subcommand = AEM_READ_REGISTER;
+ rs_req.reg = reg;
+ rs_req.rx_buf_size = size;
+
+ ipmi->tx_message.cmd = AEM_ELEMENT_CMD;
+ ipmi->tx_message.data = (char *)&rs_req;
+ ipmi->tx_message.data_len = sizeof(rs_req);
+
+ rs_size = sizeof(*rs_resp) + size;
+ ipmi->rx_msg_data = rs_resp;
+ ipmi->rx_msg_len = rs_size;
+
+ aem_send_message(ipmi);
+
+ res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT);
+ if (!res) {
+ res = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (ipmi->rx_result || ipmi->rx_msg_len != rs_size ||
+ memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) {
+ res = -ENOENT;
+ goto out;
+ }
+
+ switch (size) {
+ case 1: {
+ u8 *x = buf;
+ *x = rs_resp->bytes[0];
+ break;
+ }
+ case 2: {
+ u16 *x = buf;
+ *x = be16_to_cpup((__be16 *)rs_resp->bytes);
+ break;
+ }
+ case 4: {
+ u32 *x = buf;
+ *x = be32_to_cpup((__be32 *)rs_resp->bytes);
+ break;
+ }
+ case 8: {
+ u64 *x = buf;
+ *x = be64_to_cpup((__be64 *)rs_resp->bytes);
+ break;
+ }
+ }
+ res = 0;
+
+out:
+ return res;
+}
+
+/* Update AEM energy registers */
+static void update_aem_energy_one(struct aem_data *data, int which)
+{
+ aem_read_sensor(data, AEM_ENERGY_ELEMENT, which,
+ &data->energy[which], 8);
+}
+
+static void update_aem_energy(struct aem_data *data)
+{
+ update_aem_energy_one(data, 0);
+ if (data->ver_major < 2)
+ return;
+ update_aem_energy_one(data, 1);
+}
+
+/* Update all AEM1 sensors */
+static void update_aem1_sensors(struct aem_data *data)
+{
+ mutex_lock(&data->lock);
+ if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+ data->valid)
+ goto out;
+
+ update_aem_energy(data);
+out:
+ mutex_unlock(&data->lock);
+}
+
+/* Update all AEM2 sensors */
+static void update_aem2_sensors(struct aem_data *data)
+{
+ int i;
+
+ mutex_lock(&data->lock);
+ if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+ data->valid)
+ goto out;
+
+ update_aem_energy(data);
+ aem_read_sensor(data, AEM_EXHAUST_ELEMENT, 0, &data->temp[0], 1);
+ aem_read_sensor(data, AEM_EXHAUST_ELEMENT, 1, &data->temp[1], 1);
+
+ for (i = POWER_CAP; i <= POWER_AUX; i++)
+ aem_read_sensor(data, AEM_POWER_CAP_ELEMENT, i,
+ &data->pcap[i], 2);
+out:
+ mutex_unlock(&data->lock);
+}
+
+/* Delete an AEM instance */
+static void aem_delete(struct aem_data *data)
+{
+ list_del(&data->list);
+ aem_remove_sensors(data);
+ kfree(data->rs_resp);
+ hwmon_device_unregister(data->hwmon_dev);
+ ipmi_destroy_user(data->ipmi.user);
+ platform_set_drvdata(data->pdev, NULL);
+ platform_device_unregister(data->pdev);
+ ida_free(&aem_ida, data->id);
+ kfree(data);
+}
+
+/* Probe functions for AEM1 devices */
+
+/* Retrieve version and module handle for an AEM1 instance */
+static int aem_find_aem1_count(struct aem_ipmi_data *data)
+{
+ int res;
+ struct aem_find_firmware_req ff_req;
+ struct aem_find_firmware_resp ff_resp;
+
+ ff_req.id = system_x_id;
+ ff_req.index = 0;
+ ff_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
+
+ data->tx_message.cmd = AEM_FIND_FW_CMD;
+ data->tx_message.data = (char *)&ff_req;
+ data->tx_message.data_len = sizeof(ff_req);
+
+ data->rx_msg_data = &ff_resp;
+ data->rx_msg_len = sizeof(ff_resp);
+
+ aem_send_message(data);
+
+ res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT);
+ if (!res)
+ return -ETIMEDOUT;
+
+ if (data->rx_result || data->rx_msg_len != sizeof(ff_resp) ||
+ memcmp(&ff_resp.id, &system_x_id, sizeof(system_x_id)))
+ return -ENOENT;
+
+ return ff_resp.num_instances;
+}
+
+/* Find and initialize one AEM1 instance */
+static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
+{
+ struct aem_data *data;
+ int i;
+ int res = -ENOMEM;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return res;
+ mutex_init(&data->lock);
+
+ /* Copy instance data */
+ data->ver_major = 1;
+ data->ver_minor = 0;
+ data->module_handle = module_handle;
+ for (i = 0; i < AEM1_NUM_ENERGY_REGS; i++)
+ data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
+
+ /* Create sub-device for this fw instance */
+ data->id = ida_alloc(&aem_ida, GFP_KERNEL);
+ if (data->id < 0)
+ goto id_err;
+
+ data->pdev = platform_device_alloc(DRVNAME, data->id);
+ if (!data->pdev)
+ goto dev_err;
+ data->pdev->dev.driver = &aem_driver.driver;
+
+ res = platform_device_add(data->pdev);
+ if (res)
+ goto dev_add_err;
+
+ platform_set_drvdata(data->pdev, data);
+
+ /* Set up IPMI interface */
+ res = aem_init_ipmi_data(&data->ipmi, probe->interface,
+ probe->bmc_device);
+ if (res)
+ goto ipmi_err;
+
+ /* Register with hwmon */
+ data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_err(&data->pdev->dev,
+ "Unable to register hwmon device for IPMI interface %d\n",
+ probe->interface);
+ res = PTR_ERR(data->hwmon_dev);
+ goto hwmon_reg_err;
+ }
+
+ data->update = update_aem1_sensors;
+ data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
+ if (!data->rs_resp) {
+ res = -ENOMEM;
+ goto alloc_resp_err;
+ }
+
+ /* Find sensors */
+ res = aem1_find_sensors(data);
+ if (res)
+ goto sensor_err;
+
+ /* Add to our list of AEM devices */
+ list_add_tail(&data->list, &driver_data.aem_devices);
+
+ dev_info(data->ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
+ data->ver_major, data->ver_minor,
+ data->module_handle);
+ return 0;
+
+sensor_err:
+ kfree(data->rs_resp);
+alloc_resp_err:
+ hwmon_device_unregister(data->hwmon_dev);
+hwmon_reg_err:
+ ipmi_destroy_user(data->ipmi.user);
+ipmi_err:
+ platform_set_drvdata(data->pdev, NULL);
+ platform_device_del(data->pdev);
+dev_add_err:
+ platform_device_put(data->pdev);
+dev_err:
+ ida_free(&aem_ida, data->id);
+id_err:
+ kfree(data);
+
+ return res;
+}
+
+/* Find and initialize all AEM1 instances */
+static void aem_init_aem1(struct aem_ipmi_data *probe)
+{
+ int num, i, err;
+
+ num = aem_find_aem1_count(probe);
+ for (i = 0; i < num; i++) {
+ err = aem_init_aem1_inst(probe, i);
+ if (err) {
+ dev_err(probe->bmc_device,
+ "Error %d initializing AEM1 0x%X\n",
+ err, i);
+ }
+ }
+}
+
+/* Probe functions for AEM2 devices */
+
+/* Retrieve version and module handle for an AEM2 instance */
+static int aem_find_aem2(struct aem_ipmi_data *data,
+ struct aem_find_instance_resp *fi_resp,
+ int instance_num)
+{
+ int res;
+ struct aem_find_instance_req fi_req;
+
+ fi_req.id = system_x_id;
+ fi_req.instance_number = instance_num;
+ fi_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
+
+ data->tx_message.cmd = AEM_FW_INSTANCE_CMD;
+ data->tx_message.data = (char *)&fi_req;
+ data->tx_message.data_len = sizeof(fi_req);
+
+ data->rx_msg_data = fi_resp;
+ data->rx_msg_len = sizeof(*fi_resp);
+
+ aem_send_message(data);
+
+ res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT);
+ if (!res)
+ return -ETIMEDOUT;
+
+ if (data->rx_result || data->rx_msg_len != sizeof(*fi_resp) ||
+ memcmp(&fi_resp->id, &system_x_id, sizeof(system_x_id)) ||
+ fi_resp->num_instances <= instance_num)
+ return -ENOENT;
+
+ return 0;
+}
+
+/* Find and initialize one AEM2 instance */
+static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
+ struct aem_find_instance_resp *fi_resp)
+{
+ struct aem_data *data;
+ int i;
+ int res = -ENOMEM;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return res;
+ mutex_init(&data->lock);
+
+ /* Copy instance data */
+ data->ver_major = fi_resp->major;
+ data->ver_minor = fi_resp->minor;
+ data->module_handle = fi_resp->module_handle;
+ for (i = 0; i < AEM2_NUM_ENERGY_REGS; i++)
+ data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
+
+ /* Create sub-device for this fw instance */
+ data->id = ida_alloc(&aem_ida, GFP_KERNEL);
+ if (data->id < 0)
+ goto id_err;
+
+ data->pdev = platform_device_alloc(DRVNAME, data->id);
+ if (!data->pdev)
+ goto dev_err;
+ data->pdev->dev.driver = &aem_driver.driver;
+
+ res = platform_device_add(data->pdev);
+ if (res)
+ goto dev_add_err;
+
+ platform_set_drvdata(data->pdev, data);
+
+ /* Set up IPMI interface */
+ res = aem_init_ipmi_data(&data->ipmi, probe->interface,
+ probe->bmc_device);
+ if (res)
+ goto ipmi_err;
+
+ /* Register with hwmon */
+ data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_err(&data->pdev->dev,
+ "Unable to register hwmon device for IPMI interface %d\n",
+ probe->interface);
+ res = PTR_ERR(data->hwmon_dev);
+ goto hwmon_reg_err;
+ }
+
+ data->update = update_aem2_sensors;
+ data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
+ if (!data->rs_resp) {
+ res = -ENOMEM;
+ goto alloc_resp_err;
+ }
+
+ /* Find sensors */
+ res = aem2_find_sensors(data);
+ if (res)
+ goto sensor_err;
+
+ /* Add to our list of AEM devices */
+ list_add_tail(&data->list, &driver_data.aem_devices);
+
+ dev_info(data->ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
+ data->ver_major, data->ver_minor,
+ data->module_handle);
+ return 0;
+
+sensor_err:
+ kfree(data->rs_resp);
+alloc_resp_err:
+ hwmon_device_unregister(data->hwmon_dev);
+hwmon_reg_err:
+ ipmi_destroy_user(data->ipmi.user);
+ipmi_err:
+ platform_set_drvdata(data->pdev, NULL);
+ platform_device_del(data->pdev);
+dev_add_err:
+ platform_device_put(data->pdev);
+dev_err:
+ ida_free(&aem_ida, data->id);
+id_err:
+ kfree(data);
+
+ return res;
+}
+
+/* Find and initialize all AEM2 instances */
+static void aem_init_aem2(struct aem_ipmi_data *probe)
+{
+ struct aem_find_instance_resp fi_resp;
+ int err;
+ int i = 0;
+
+ while (!aem_find_aem2(probe, &fi_resp, i)) {
+ if (fi_resp.major != 2) {
+ dev_err(probe->bmc_device,
+ "Unknown AEM v%d; please report this to the maintainer.\n",
+ fi_resp.major);
+ i++;
+ continue;
+ }
+ err = aem_init_aem2_inst(probe, &fi_resp);
+ if (err) {
+ dev_err(probe->bmc_device,
+ "Error %d initializing AEM2 0x%X\n",
+ err, fi_resp.module_handle);
+ }
+ i++;
+ }
+}
+
+/* Probe a BMC for AEM firmware instances */
+static void aem_register_bmc(int iface, struct device *dev)
+{
+ struct aem_ipmi_data probe;
+
+ if (aem_init_ipmi_data(&probe, iface, dev))
+ return;
+
+ /* Ignore probe errors; they won't cause problems */
+ aem_init_aem1(&probe);
+ aem_init_aem2(&probe);
+
+ ipmi_destroy_user(probe.user);
+}
+
+/* Handle BMC deletion */
+static void aem_bmc_gone(int iface)
+{
+ struct aem_data *p1, *next1;
+
+ list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list)
+ if (p1->ipmi.interface == iface)
+ aem_delete(p1);
+}
+
+/* sysfs support functions */
+
+/* AEM device name */
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct aem_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s%d\n", DRVNAME, data->ver_major);
+}
+static SENSOR_DEVICE_ATTR_RO(name, name, 0);
+
+/* AEM device version */
+static ssize_t version_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct aem_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d.%d\n", data->ver_major, data->ver_minor);
+}
+static SENSOR_DEVICE_ATTR_RO(version, version, 0);
+
+/* Display power use */
+static ssize_t aem_show_power(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct aem_data *data = dev_get_drvdata(dev);
+ u64 before, after, delta, time;
+ signed long leftover;
+
+ mutex_lock(&data->lock);
+ update_aem_energy_one(data, attr->index);
+ time = ktime_get_ns();
+ before = data->energy[attr->index];
+
+ leftover = schedule_timeout_interruptible(
+ msecs_to_jiffies(data->power_period[attr->index])
+ );
+ if (leftover) {
+ mutex_unlock(&data->lock);
+ return 0;
+ }
+
+ update_aem_energy_one(data, attr->index);
+ time = ktime_get_ns() - time;
+ after = data->energy[attr->index];
+ mutex_unlock(&data->lock);
+
+ delta = (after - before) * UJ_PER_MJ;
+
+ return sprintf(buf, "%llu\n",
+ (unsigned long long)div64_u64(delta * NSEC_PER_SEC, time));
+}
+
+/* Display energy use */
+static ssize_t aem_show_energy(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct aem_data *a = dev_get_drvdata(dev);
+ mutex_lock(&a->lock);
+ update_aem_energy_one(a, attr->index);
+ mutex_unlock(&a->lock);
+
+ return sprintf(buf, "%llu\n",
+ (unsigned long long)a->energy[attr->index] * 1000);
+}
+
+/* Display power interval registers */
+static ssize_t aem_show_power_period(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct aem_data *a = dev_get_drvdata(dev);
+ a->update(a);
+
+ return sprintf(buf, "%lu\n", a->power_period[attr->index]);
+}
+
+/* Set power interval registers */
+static ssize_t aem_set_power_period(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct aem_data *a = dev_get_drvdata(dev);
+ unsigned long temp;
+ int res;
+
+ res = kstrtoul(buf, 10, &temp);
+ if (res)
+ return res;
+
+ if (temp < AEM_MIN_POWER_INTERVAL)
+ return -EINVAL;
+
+ mutex_lock(&a->lock);
+ a->power_period[attr->index] = temp;
+ mutex_unlock(&a->lock);
+
+ return count;
+}
+
+/* Discover sensors on an AEM device */
+static int aem_register_sensors(struct aem_data *data,
+ const struct aem_ro_sensor_template *ro,
+ const struct aem_rw_sensor_template *rw)
+{
+ struct device *dev = &data->pdev->dev;
+ struct sensor_device_attribute *sensors = data->sensors;
+ int err;
+
+ /* Set up read-only sensors */
+ while (ro->label) {
+ sysfs_attr_init(&sensors->dev_attr.attr);
+ sensors->dev_attr.attr.name = ro->label;
+ sensors->dev_attr.attr.mode = 0444;
+ sensors->dev_attr.show = ro->show;
+ sensors->index = ro->index;
+
+ err = device_create_file(dev, &sensors->dev_attr);
+ if (err) {
+ sensors->dev_attr.attr.name = NULL;
+ goto error;
+ }
+ sensors++;
+ ro++;
+ }
+
+ /* Set up read-write sensors */
+ while (rw->label) {
+ sysfs_attr_init(&sensors->dev_attr.attr);
+ sensors->dev_attr.attr.name = rw->label;
+ sensors->dev_attr.attr.mode = 0644;
+ sensors->dev_attr.show = rw->show;
+ sensors->dev_attr.store = rw->set;
+ sensors->index = rw->index;
+
+ err = device_create_file(dev, &sensors->dev_attr);
+ if (err) {
+ sensors->dev_attr.attr.name = NULL;
+ goto error;
+ }
+ sensors++;
+ rw++;
+ }
+
+ err = device_create_file(dev, &sensor_dev_attr_name.dev_attr);
+ if (err)
+ goto error;
+ err = device_create_file(dev, &sensor_dev_attr_version.dev_attr);
+ return err;
+
+error:
+ aem_remove_sensors(data);
+ return err;
+}
+
+/* sysfs support functions for AEM2 sensors */
+
+/* Display temperature use */
+static ssize_t aem2_show_temp(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct aem_data *a = dev_get_drvdata(dev);
+ a->update(a);
+
+ return sprintf(buf, "%u\n", a->temp[attr->index] * 1000);
+}
+
+/* Display power-capping registers */
+static ssize_t aem2_show_pcap_value(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct aem_data *a = dev_get_drvdata(dev);
+ a->update(a);
+
+ return sprintf(buf, "%u\n", a->pcap[attr->index] * 100000);
+}
+
+/* Remove sensors attached to an AEM device */
+static void aem_remove_sensors(struct aem_data *data)
+{
+ int i;
+
+ for (i = 0; i < AEM_NUM_SENSORS; i++) {
+ if (!data->sensors[i].dev_attr.attr.name)
+ continue;
+ device_remove_file(&data->pdev->dev,
+ &data->sensors[i].dev_attr);
+ }
+
+ device_remove_file(&data->pdev->dev,
+ &sensor_dev_attr_name.dev_attr);
+ device_remove_file(&data->pdev->dev,
+ &sensor_dev_attr_version.dev_attr);
+}
+
+/* Sensor probe functions */
+
+/* Description of AEM1 sensors */
+static const struct aem_ro_sensor_template aem1_ro_sensors[] = {
+{"energy1_input", aem_show_energy, 0},
+{"power1_average", aem_show_power, 0},
+{NULL, NULL, 0},
+};
+
+static const struct aem_rw_sensor_template aem1_rw_sensors[] = {
+{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
+{NULL, NULL, NULL, 0},
+};
+
+/* Description of AEM2 sensors */
+static const struct aem_ro_sensor_template aem2_ro_sensors[] = {
+{"energy1_input", aem_show_energy, 0},
+{"energy2_input", aem_show_energy, 1},
+{"power1_average", aem_show_power, 0},
+{"power2_average", aem_show_power, 1},
+{"temp1_input", aem2_show_temp, 0},
+{"temp2_input", aem2_show_temp, 1},
+
+{"power4_average", aem2_show_pcap_value, POWER_CAP_MAX_HOTPLUG},
+{"power5_average", aem2_show_pcap_value, POWER_CAP_MAX},
+{"power6_average", aem2_show_pcap_value, POWER_CAP_MIN_WARNING},
+{"power7_average", aem2_show_pcap_value, POWER_CAP_MIN},
+
+{"power3_average", aem2_show_pcap_value, POWER_AUX},
+{"power_cap", aem2_show_pcap_value, POWER_CAP},
+{NULL, NULL, 0},
+};
+
+static const struct aem_rw_sensor_template aem2_rw_sensors[] = {
+{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
+{"power2_average_interval", aem_show_power_period, aem_set_power_period, 1},
+{NULL, NULL, NULL, 0},
+};
+
+/* Set up AEM1 sensor attrs */
+static int aem1_find_sensors(struct aem_data *data)
+{
+ return aem_register_sensors(data, aem1_ro_sensors, aem1_rw_sensors);
+}
+
+/* Set up AEM2 sensor attrs */
+static int aem2_find_sensors(struct aem_data *data)
+{
+ return aem_register_sensors(data, aem2_ro_sensors, aem2_rw_sensors);
+}
+
+/* Module init/exit routines */
+
+static int __init aem_init(void)
+{
+ int res;
+
+ res = driver_register(&aem_driver.driver);
+ if (res) {
+ pr_err("Can't register aem driver\n");
+ return res;
+ }
+
+ res = ipmi_smi_watcher_register(&driver_data.bmc_events);
+ if (res)
+ goto ipmi_reg_err;
+ return 0;
+
+ipmi_reg_err:
+ driver_unregister(&aem_driver.driver);
+ return res;
+
+}
+
+static void __exit aem_exit(void)
+{
+ struct aem_data *p1, *next1;
+
+ ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+ driver_unregister(&aem_driver.driver);
+ list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list)
+ aem_delete(p1);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
+MODULE_DESCRIPTION("IBM AEM power/temp/energy sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(aem_init);
+module_exit(aem_exit);
+
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMBladeHC10-*");
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644
index 000000000..1837cccd9
--- /dev/null
+++ b/drivers/hwmon/ibmpex.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#define REFRESH_INTERVAL (2 * HZ)
+#define DRVNAME "ibmpex"
+
+#define PEX_GET_VERSION 1
+#define PEX_GET_SENSOR_COUNT 2
+#define PEX_GET_SENSOR_NAME 3
+#define PEX_RESET_HIGH_LOW 4
+#define PEX_GET_SENSOR_DATA 6
+
+#define PEX_NET_FUNCTION 0x3A
+#define PEX_COMMAND 0x3C
+
+static inline u16 extract_value(const char *data, int offset)
+{
+ return be16_to_cpup((__be16 *)&data[offset]);
+}
+
+#define TEMP_SENSOR 1
+#define POWER_SENSOR 2
+
+#define PEX_SENSOR_TYPE_LEN 3
+static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
+static u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D};
+
+#define PEX_MULT_LEN 2
+static u8 const watt_sensor_sig[] = {0x41, 0x43};
+
+#define PEX_NUM_SENSOR_FUNCS 3
+static const char * const sensor_name_suffixes[] = {
+ "",
+ "_lowest",
+ "_highest"
+};
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+ int in_use;
+ s16 values[PEX_NUM_SENSOR_FUNCS];
+ int multiplier;
+
+ struct sensor_device_attribute_2 attr[PEX_NUM_SENSOR_FUNCS];
+};
+
+struct ibmpex_bmc_data {
+ struct list_head list;
+ struct device *hwmon_dev;
+ struct device *bmc_device;
+ struct mutex lock;
+ bool valid;
+ unsigned long last_updated; /* In jiffies */
+
+ struct ipmi_addr address;
+ struct completion read_complete;
+ struct ipmi_user *user;
+ int interface;
+
+ struct kernel_ipmi_msg tx_message;
+ unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH];
+ long tx_msgid;
+
+ unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH];
+ unsigned long rx_msg_len;
+ unsigned char rx_result;
+ int rx_recv_type;
+
+ unsigned char sensor_major;
+ unsigned char sensor_minor;
+
+ unsigned char num_sensors;
+ struct ibmpex_sensor_data *sensors;
+};
+
+struct ibmpex_driver_data {
+ struct list_head bmc_data;
+ struct ipmi_smi_watcher bmc_events;
+ struct ipmi_user_hndl ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+ .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+ .bmc_events = {
+ .owner = THIS_MODULE,
+ .new_smi = ibmpex_register_bmc,
+ .smi_gone = ibmpex_bmc_gone,
+ },
+ .ipmi_hndlrs = {
+ .ipmi_recv_hndl = ibmpex_msg_handler,
+ },
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+ int err;
+
+ err = ipmi_validate_addr(&data->address, sizeof(data->address));
+ if (err)
+ goto out;
+
+ data->tx_msgid++;
+ err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+ &data->tx_message, data, 0, 0, 0);
+ if (err)
+ goto out1;
+
+ return 0;
+out1:
+ dev_err(data->bmc_device, "request_settime=%x\n", err);
+ return err;
+out:
+ dev_err(data->bmc_device, "validate_addr=%x\n", err);
+ return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+ data->tx_msg_data[0] = PEX_GET_VERSION;
+ data->tx_message.data_len = 1;
+ ibmpex_send_message(data);
+
+ wait_for_completion(&data->read_complete);
+
+ if (data->rx_result || data->rx_msg_len != 6)
+ return -ENOENT;
+
+ data->sensor_major = data->rx_msg_data[0];
+ data->sensor_minor = data->rx_msg_data[1];
+
+ dev_info(data->bmc_device,
+ "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n",
+ data->sensor_major,
+ data->sensor_minor,
+ extract_value(data->rx_msg_data, 2),
+ data->rx_msg_data[4],
+ data->rx_msg_data[5],
+ data->interface);
+
+ return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+ data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
+ data->tx_message.data_len = 1;
+ ibmpex_send_message(data);
+
+ wait_for_completion(&data->read_complete);
+
+ if (data->rx_result || data->rx_msg_len != 1)
+ return -ENOENT;
+
+ return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+ data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
+ data->tx_msg_data[1] = sensor;
+ data->tx_message.data_len = 2;
+ ibmpex_send_message(data);
+
+ wait_for_completion(&data->read_complete);
+
+ if (data->rx_result || data->rx_msg_len < 1)
+ return -ENOENT;
+
+ return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+ data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
+ data->tx_msg_data[1] = sensor;
+ data->tx_message.data_len = 2;
+ ibmpex_send_message(data);
+
+ wait_for_completion(&data->read_complete);
+
+ if (data->rx_result || data->rx_msg_len < 26) {
+ dev_err(data->bmc_device, "Error reading sensor %d.\n",
+ sensor);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
+{
+ data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
+ data->tx_message.data_len = 1;
+ ibmpex_send_message(data);
+
+ wait_for_completion(&data->read_complete);
+
+ return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+ int i, err;
+
+ mutex_lock(&data->lock);
+ if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+ data->valid)
+ goto out;
+
+ for (i = 0; i < data->num_sensors; i++) {
+ if (!data->sensors[i].in_use)
+ continue;
+ err = ibmpex_query_sensor_data(data, i);
+ if (err)
+ continue;
+ data->sensors[i].values[0] =
+ extract_value(data->rx_msg_data, 16);
+ data->sensors[i].values[1] =
+ extract_value(data->rx_msg_data, 18);
+ data->sensors[i].values[2] =
+ extract_value(data->rx_msg_data, 20);
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+
+out:
+ mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+ struct ibmpex_bmc_data *p, *next;
+
+ list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+ if (p->interface == iface)
+ return p;
+
+ return NULL;
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR_RO(name, name, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+ int mult = data->sensors[attr->index].multiplier;
+ ibmpex_update_device(data);
+
+ return sprintf(buf, "%d\n",
+ data->sensors[attr->index].values[attr->nr] * mult);
+}
+
+static ssize_t ibmpex_high_low_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+
+ ibmpex_reset_high_low_data(data);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_WO(reset_high_low, ibmpex_high_low, 0);
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+ if (len < PEX_SENSOR_TYPE_LEN)
+ return 0;
+
+ if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
+ return 1;
+ return 0;
+}
+
+static int is_temp_sensor(const char *sensor_id, int len)
+{
+ if (len < PEX_SENSOR_TYPE_LEN)
+ return 0;
+
+ if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
+ return 1;
+ return 0;
+}
+
+static int power_sensor_multiplier(struct ibmpex_bmc_data *data,
+ const char *sensor_id, int len)
+{
+ int i;
+
+ if (data->sensor_major == 2)
+ return 1000000;
+
+ for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
+ if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
+ return 1000000;
+
+ return 100000;
+}
+
+static int create_sensor(struct ibmpex_bmc_data *data, int type,
+ int counter, int sensor, int func)
+{
+ int err;
+ char *n;
+
+ n = kmalloc(32, GFP_KERNEL);
+ if (!n)
+ return -ENOMEM;
+
+ if (type == TEMP_SENSOR)
+ sprintf(n, "temp%d_input%s",
+ counter, sensor_name_suffixes[func]);
+ else if (type == POWER_SENSOR)
+ sprintf(n, "power%d_average%s",
+ counter, sensor_name_suffixes[func]);
+
+ sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr);
+ data->sensors[sensor].attr[func].dev_attr.attr.name = n;
+ data->sensors[sensor].attr[func].dev_attr.attr.mode = 0444;
+ data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
+ data->sensors[sensor].attr[func].index = sensor;
+ data->sensors[sensor].attr[func].nr = func;
+
+ err = device_create_file(data->bmc_device,
+ &data->sensors[sensor].attr[func].dev_attr);
+ if (err) {
+ data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
+ kfree(n);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
+{
+ int i, j, err;
+ int sensor_type;
+ int sensor_counter;
+ int num_power = 0;
+ int num_temp = 0;
+
+ err = ibmpex_query_sensor_count(data);
+ if (err <= 0)
+ return -ENOENT;
+ data->num_sensors = err;
+
+ data->sensors = kcalloc(data->num_sensors, sizeof(*data->sensors),
+ GFP_KERNEL);
+ if (!data->sensors)
+ return -ENOMEM;
+
+ for (i = 0; i < data->num_sensors; i++) {
+ err = ibmpex_query_sensor_name(data, i);
+ if (err)
+ continue;
+
+ if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
+ sensor_type = POWER_SENSOR;
+ num_power++;
+ sensor_counter = num_power;
+ data->sensors[i].multiplier =
+ power_sensor_multiplier(data,
+ data->rx_msg_data,
+ data->rx_msg_len);
+ } else if (is_temp_sensor(data->rx_msg_data,
+ data->rx_msg_len)) {
+ sensor_type = TEMP_SENSOR;
+ num_temp++;
+ sensor_counter = num_temp;
+ data->sensors[i].multiplier = 1000;
+ } else
+ continue;
+
+ data->sensors[i].in_use = 1;
+
+ /* Create attributes */
+ for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+ err = create_sensor(data, sensor_type, sensor_counter,
+ i, j);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
+ err = device_create_file(data->bmc_device,
+ &sensor_dev_attr_reset_high_low.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ err = device_create_file(data->bmc_device,
+ &sensor_dev_attr_name.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ return 0;
+
+exit_remove:
+ device_remove_file(data->bmc_device,
+ &sensor_dev_attr_reset_high_low.dev_attr);
+ device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+ for (i = 0; i < data->num_sensors; i++)
+ for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+ if (!data->sensors[i].attr[j].dev_attr.attr.name)
+ continue;
+ device_remove_file(data->bmc_device,
+ &data->sensors[i].attr[j].dev_attr);
+ kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+ }
+
+ kfree(data->sensors);
+ return err;
+}
+
+static void ibmpex_register_bmc(int iface, struct device *dev)
+{
+ struct ibmpex_bmc_data *data;
+ int err;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ data->address.channel = IPMI_BMC_CHANNEL;
+ data->address.data[0] = 0;
+ data->interface = iface;
+ data->bmc_device = dev;
+
+ /* Create IPMI messaging interface user */
+ err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
+ data, &data->user);
+ if (err < 0) {
+ dev_err(dev,
+ "Unable to register user with IPMI interface %d\n",
+ data->interface);
+ goto out;
+ }
+
+ mutex_init(&data->lock);
+
+ /* Initialize message */
+ data->tx_msgid = 0;
+ init_completion(&data->read_complete);
+ data->tx_message.netfn = PEX_NET_FUNCTION;
+ data->tx_message.cmd = PEX_COMMAND;
+ data->tx_message.data = data->tx_msg_data;
+
+ /* Does this BMC support PowerExecutive? */
+ err = ibmpex_ver_check(data);
+ if (err)
+ goto out_user;
+
+ /* Register the BMC as a HWMON class device */
+ data->hwmon_dev = hwmon_device_register(data->bmc_device);
+
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_err(data->bmc_device,
+ "Unable to register hwmon device for IPMI interface %d\n",
+ data->interface);
+ goto out_user;
+ }
+
+ /* finally add the new bmc data to the bmc data list */
+ dev_set_drvdata(dev, data);
+ list_add_tail(&data->list, &driver_data.bmc_data);
+
+ /* Now go find all the sensors */
+ err = ibmpex_find_sensors(data);
+ if (err) {
+ dev_err(data->bmc_device, "Error %d finding sensors\n", err);
+ goto out_register;
+ }
+
+ return;
+
+out_register:
+ list_del(&data->list);
+ hwmon_device_unregister(data->hwmon_dev);
+out_user:
+ ipmi_destroy_user(data->user);
+out:
+ kfree(data);
+}
+
+static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
+{
+ int i, j;
+
+ device_remove_file(data->bmc_device,
+ &sensor_dev_attr_reset_high_low.dev_attr);
+ device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
+ for (i = 0; i < data->num_sensors; i++)
+ for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
+ if (!data->sensors[i].attr[j].dev_attr.attr.name)
+ continue;
+ device_remove_file(data->bmc_device,
+ &data->sensors[i].attr[j].dev_attr);
+ kfree(data->sensors[i].attr[j].dev_attr.attr.name);
+ }
+
+ list_del(&data->list);
+ dev_set_drvdata(data->bmc_device, NULL);
+ hwmon_device_unregister(data->hwmon_dev);
+ ipmi_destroy_user(data->user);
+ kfree(data->sensors);
+ kfree(data);
+}
+
+static void ibmpex_bmc_gone(int iface)
+{
+ struct ibmpex_bmc_data *data = get_bmc_data(iface);
+
+ if (!data)
+ return;
+
+ ibmpex_bmc_delete(data);
+}
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+ struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
+
+ if (msg->msgid != data->tx_msgid) {
+ dev_err(data->bmc_device,
+ "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
+ (int)msg->msgid,
+ (int)data->tx_msgid);
+ ipmi_free_recv_msg(msg);
+ return;
+ }
+
+ data->rx_recv_type = msg->recv_type;
+ if (msg->msg.data_len > 0)
+ data->rx_result = msg->msg.data[0];
+ else
+ data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
+
+ if (msg->msg.data_len > 1) {
+ data->rx_msg_len = msg->msg.data_len - 1;
+ memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
+ } else
+ data->rx_msg_len = 0;
+
+ ipmi_free_recv_msg(msg);
+ complete(&data->read_complete);
+}
+
+static int __init ibmpex_init(void)
+{
+ return ipmi_smi_watcher_register(&driver_data.bmc_events);
+}
+
+static void __exit ibmpex_exit(void)
+{
+ struct ibmpex_bmc_data *p, *next;
+
+ ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+ list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+ ibmpex_bmc_delete(p);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
+MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(ibmpex_init);
+module_exit(ibmpex_exit);
+
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
+MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
new file mode 100644
index 000000000..8e3724728
--- /dev/null
+++ b/drivers/hwmon/ibmpowernv.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * IBM PowerNV platform sensors for temperature/fan/voltage/power
+ * Copyright (C) 2014 IBM
+ */
+
+#define DRVNAME "ibmpowernv"
+#define pr_fmt(fmt) DRVNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include <linux/platform_device.h>
+#include <asm/opal.h>
+#include <linux/err.h>
+#include <asm/cputhreads.h>
+#include <asm/smp.h>
+
+#define MAX_ATTR_LEN 32
+#define MAX_LABEL_LEN 64
+
+/* Sensor suffix name from DT */
+#define DT_FAULT_ATTR_SUFFIX "faulted"
+#define DT_DATA_ATTR_SUFFIX "data"
+#define DT_THRESHOLD_ATTR_SUFFIX "thrs"
+
+/*
+ * Enumerates all the types of sensors in the POWERNV platform and does index
+ * into 'struct sensor_group'
+ */
+enum sensors {
+ FAN,
+ TEMP,
+ POWER_SUPPLY,
+ POWER_INPUT,
+ CURRENT,
+ ENERGY,
+ MAX_SENSOR_TYPE,
+};
+
+#define INVALID_INDEX (-1U)
+
+/*
+ * 'compatible' string properties for sensor types as defined in old
+ * PowerNV firmware (skiboot). These are ordered as 'enum sensors'.
+ */
+static const char * const legacy_compatibles[] = {
+ "ibm,opal-sensor-cooling-fan",
+ "ibm,opal-sensor-amb-temp",
+ "ibm,opal-sensor-power-supply",
+ "ibm,opal-sensor-power"
+};
+
+static struct sensor_group {
+ const char *name; /* matches property 'sensor-type' */
+ struct attribute_group group;
+ u32 attr_count;
+ u32 hwmon_index;
+} sensor_groups[] = {
+ { "fan" },
+ { "temp" },
+ { "in" },
+ { "power" },
+ { "curr" },
+ { "energy" },
+};
+
+struct sensor_data {
+ u32 id; /* An opaque id of the firmware for each sensor */
+ u32 hwmon_index;
+ u32 opal_index;
+ enum sensors type;
+ char label[MAX_LABEL_LEN];
+ char name[MAX_ATTR_LEN];
+ struct device_attribute dev_attr;
+ struct sensor_group_data *sgrp_data;
+};
+
+struct sensor_group_data {
+ struct mutex mutex;
+ u32 gid;
+ bool enable;
+};
+
+struct platform_data {
+ const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1];
+ struct sensor_group_data *sgrp_data;
+ u32 sensors_count; /* Total count of sensors from each group */
+ u32 nr_sensor_groups; /* Total number of sensor groups */
+};
+
+static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_data *sdata = container_of(devattr, struct sensor_data,
+ dev_attr);
+ ssize_t ret;
+ u64 x;
+
+ if (sdata->sgrp_data && !sdata->sgrp_data->enable)
+ return -ENODATA;
+
+ ret = opal_get_sensor_data_u64(sdata->id, &x);
+
+ if (ret)
+ return ret;
+
+ /* Convert temperature to milli-degrees */
+ if (sdata->type == TEMP)
+ x *= 1000;
+ /* Convert power to micro-watts */
+ else if (sdata->type == POWER_INPUT)
+ x *= 1000000;
+
+ return sprintf(buf, "%llu\n", x);
+}
+
+static ssize_t show_enable(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_data *sdata = container_of(devattr, struct sensor_data,
+ dev_attr);
+
+ return sprintf(buf, "%u\n", sdata->sgrp_data->enable);
+}
+
+static ssize_t store_enable(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct sensor_data *sdata = container_of(devattr, struct sensor_data,
+ dev_attr);
+ struct sensor_group_data *sgrp_data = sdata->sgrp_data;
+ int ret;
+ bool data;
+
+ ret = kstrtobool(buf, &data);
+ if (ret)
+ return ret;
+
+ ret = mutex_lock_interruptible(&sgrp_data->mutex);
+ if (ret)
+ return ret;
+
+ if (data != sgrp_data->enable) {
+ ret = sensor_group_enable(sgrp_data->gid, data);
+ if (!ret)
+ sgrp_data->enable = data;
+ }
+
+ if (!ret)
+ ret = count;
+
+ mutex_unlock(&sgrp_data->mutex);
+ return ret;
+}
+
+static ssize_t show_label(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_data *sdata = container_of(devattr, struct sensor_data,
+ dev_attr);
+
+ return sprintf(buf, "%s\n", sdata->label);
+}
+
+static int get_logical_cpu(int hwcpu)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ if (get_hard_smp_processor_id(cpu) == hwcpu)
+ return cpu;
+
+ return -ENOENT;
+}
+
+static void make_sensor_label(struct device_node *np,
+ struct sensor_data *sdata, const char *label)
+{
+ u32 id;
+ size_t n;
+
+ n = scnprintf(sdata->label, sizeof(sdata->label), "%s", label);
+
+ /*
+ * Core temp pretty print
+ */
+ if (!of_property_read_u32(np, "ibm,pir", &id)) {
+ int cpuid = get_logical_cpu(id);
+
+ if (cpuid >= 0)
+ /*
+ * The digital thermal sensors are associated
+ * with a core.
+ */
+ n += scnprintf(sdata->label + n,
+ sizeof(sdata->label) - n, " %d",
+ cpuid);
+ else
+ n += scnprintf(sdata->label + n,
+ sizeof(sdata->label) - n, " phy%d", id);
+ }
+
+ /*
+ * Membuffer pretty print
+ */
+ if (!of_property_read_u32(np, "ibm,chip-id", &id))
+ n += scnprintf(sdata->label + n, sizeof(sdata->label) - n,
+ " %d", id & 0xffff);
+}
+
+static int get_sensor_index_attr(const char *name, u32 *index, char *attr)
+{
+ char *hash_pos = strchr(name, '#');
+ char buf[8] = { 0 };
+ char *dash_pos;
+ u32 copy_len;
+ int err;
+
+ if (!hash_pos)
+ return -EINVAL;
+
+ dash_pos = strchr(hash_pos, '-');
+ if (!dash_pos)
+ return -EINVAL;
+
+ copy_len = dash_pos - hash_pos - 1;
+ if (copy_len >= sizeof(buf))
+ return -EINVAL;
+
+ strncpy(buf, hash_pos + 1, copy_len);
+
+ err = kstrtou32(buf, 10, index);
+ if (err)
+ return err;
+
+ strscpy(attr, dash_pos + 1, MAX_ATTR_LEN);
+
+ return 0;
+}
+
+static const char *convert_opal_attr_name(enum sensors type,
+ const char *opal_attr)
+{
+ const char *attr_name = NULL;
+
+ if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) {
+ attr_name = "fault";
+ } else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) {
+ attr_name = "input";
+ } else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) {
+ if (type == TEMP)
+ attr_name = "max";
+ else if (type == FAN)
+ attr_name = "min";
+ }
+
+ return attr_name;
+}
+
+/*
+ * This function translates the DT node name into the 'hwmon' attribute name.
+ * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc.
+ * which need to be mapped as fan2_input, temp1_max respectively before
+ * populating them inside hwmon device class.
+ */
+static const char *parse_opal_node_name(const char *node_name,
+ enum sensors type, u32 *index)
+{
+ char attr_suffix[MAX_ATTR_LEN];
+ const char *attr_name;
+ int err;
+
+ err = get_sensor_index_attr(node_name, index, attr_suffix);
+ if (err)
+ return ERR_PTR(err);
+
+ attr_name = convert_opal_attr_name(type, attr_suffix);
+ if (!attr_name)
+ return ERR_PTR(-ENOENT);
+
+ return attr_name;
+}
+
+static int get_sensor_type(struct device_node *np)
+{
+ enum sensors type;
+ const char *str;
+
+ for (type = 0; type < ARRAY_SIZE(legacy_compatibles); type++) {
+ if (of_device_is_compatible(np, legacy_compatibles[type]))
+ return type;
+ }
+
+ /*
+ * Let's check if we have a newer device tree
+ */
+ if (!of_device_is_compatible(np, "ibm,opal-sensor"))
+ return MAX_SENSOR_TYPE;
+
+ if (of_property_read_string(np, "sensor-type", &str))
+ return MAX_SENSOR_TYPE;
+
+ for (type = 0; type < MAX_SENSOR_TYPE; type++)
+ if (!strcmp(str, sensor_groups[type].name))
+ return type;
+
+ return MAX_SENSOR_TYPE;
+}
+
+static u32 get_sensor_hwmon_index(struct sensor_data *sdata,
+ struct sensor_data *sdata_table, int count)
+{
+ int i;
+
+ /*
+ * We don't use the OPAL index on newer device trees
+ */
+ if (sdata->opal_index != INVALID_INDEX) {
+ for (i = 0; i < count; i++)
+ if (sdata_table[i].opal_index == sdata->opal_index &&
+ sdata_table[i].type == sdata->type)
+ return sdata_table[i].hwmon_index;
+ }
+ return ++sensor_groups[sdata->type].hwmon_index;
+}
+
+static int init_sensor_group_data(struct platform_device *pdev,
+ struct platform_data *pdata)
+{
+ struct sensor_group_data *sgrp_data;
+ struct device_node *groups, *sgrp;
+ int count = 0, ret = 0;
+ enum sensors type;
+
+ groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");
+ if (!groups)
+ return ret;
+
+ for_each_child_of_node(groups, sgrp) {
+ type = get_sensor_type(sgrp);
+ if (type != MAX_SENSOR_TYPE)
+ pdata->nr_sensor_groups++;
+ }
+
+ if (!pdata->nr_sensor_groups)
+ goto out;
+
+ sgrp_data = devm_kcalloc(&pdev->dev, pdata->nr_sensor_groups,
+ sizeof(*sgrp_data), GFP_KERNEL);
+ if (!sgrp_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for_each_child_of_node(groups, sgrp) {
+ u32 gid;
+
+ type = get_sensor_type(sgrp);
+ if (type == MAX_SENSOR_TYPE)
+ continue;
+
+ if (of_property_read_u32(sgrp, "sensor-group-id", &gid))
+ continue;
+
+ if (of_count_phandle_with_args(sgrp, "sensors", NULL) <= 0)
+ continue;
+
+ sensor_groups[type].attr_count++;
+ sgrp_data[count].gid = gid;
+ mutex_init(&sgrp_data[count].mutex);
+ sgrp_data[count++].enable = false;
+ }
+
+ pdata->sgrp_data = sgrp_data;
+out:
+ of_node_put(groups);
+ return ret;
+}
+
+static struct sensor_group_data *get_sensor_group(struct platform_data *pdata,
+ struct device_node *node,
+ enum sensors gtype)
+{
+ struct sensor_group_data *sgrp_data = pdata->sgrp_data;
+ struct device_node *groups, *sgrp;
+
+ groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");
+ if (!groups)
+ return NULL;
+
+ for_each_child_of_node(groups, sgrp) {
+ struct of_phandle_iterator it;
+ u32 gid;
+ int rc, i;
+ enum sensors type;
+
+ type = get_sensor_type(sgrp);
+ if (type != gtype)
+ continue;
+
+ if (of_property_read_u32(sgrp, "sensor-group-id", &gid))
+ continue;
+
+ of_for_each_phandle(&it, rc, sgrp, "sensors", NULL, 0)
+ if (it.phandle == node->phandle) {
+ of_node_put(it.node);
+ break;
+ }
+
+ if (rc)
+ continue;
+
+ for (i = 0; i < pdata->nr_sensor_groups; i++)
+ if (gid == sgrp_data[i].gid) {
+ of_node_put(sgrp);
+ of_node_put(groups);
+ return &sgrp_data[i];
+ }
+ }
+
+ of_node_put(groups);
+ return NULL;
+}
+
+static int populate_attr_groups(struct platform_device *pdev)
+{
+ struct platform_data *pdata = platform_get_drvdata(pdev);
+ const struct attribute_group **pgroups = pdata->attr_groups;
+ struct device_node *opal, *np;
+ enum sensors type;
+ int ret;
+
+ ret = init_sensor_group_data(pdev, pdata);
+ if (ret)
+ return ret;
+
+ opal = of_find_node_by_path("/ibm,opal/sensors");
+ for_each_child_of_node(opal, np) {
+ const char *label;
+
+ type = get_sensor_type(np);
+ if (type == MAX_SENSOR_TYPE)
+ continue;
+
+ sensor_groups[type].attr_count++;
+
+ /*
+ * add attributes for labels, min and max
+ */
+ if (!of_property_read_string(np, "label", &label))
+ sensor_groups[type].attr_count++;
+ if (of_find_property(np, "sensor-data-min", NULL))
+ sensor_groups[type].attr_count++;
+ if (of_find_property(np, "sensor-data-max", NULL))
+ sensor_groups[type].attr_count++;
+ }
+
+ of_node_put(opal);
+
+ for (type = 0; type < MAX_SENSOR_TYPE; type++) {
+ sensor_groups[type].group.attrs = devm_kcalloc(&pdev->dev,
+ sensor_groups[type].attr_count + 1,
+ sizeof(struct attribute *),
+ GFP_KERNEL);
+ if (!sensor_groups[type].group.attrs)
+ return -ENOMEM;
+
+ pgroups[type] = &sensor_groups[type].group;
+ pdata->sensors_count += sensor_groups[type].attr_count;
+ sensor_groups[type].attr_count = 0;
+ }
+
+ return 0;
+}
+
+static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name,
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*store)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count))
+{
+ snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s",
+ sensor_groups[sdata->type].name, sdata->hwmon_index,
+ attr_name);
+
+ sysfs_attr_init(&sdata->dev_attr.attr);
+ sdata->dev_attr.attr.name = sdata->name;
+ sdata->dev_attr.show = show;
+ if (store) {
+ sdata->dev_attr.store = store;
+ sdata->dev_attr.attr.mode = 0664;
+ } else {
+ sdata->dev_attr.attr.mode = 0444;
+ }
+}
+
+static void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid,
+ const char *attr_name, enum sensors type,
+ const struct attribute_group *pgroup,
+ struct sensor_group_data *sgrp_data,
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*store)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count))
+{
+ sdata->id = sid;
+ sdata->type = type;
+ sdata->opal_index = od;
+ sdata->hwmon_index = hd;
+ create_hwmon_attr(sdata, attr_name, show, store);
+ pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr;
+ sdata->sgrp_data = sgrp_data;
+}
+
+static char *get_max_attr(enum sensors type)
+{
+ switch (type) {
+ case POWER_INPUT:
+ return "input_highest";
+ default:
+ return "highest";
+ }
+}
+
+static char *get_min_attr(enum sensors type)
+{
+ switch (type) {
+ case POWER_INPUT:
+ return "input_lowest";
+ default:
+ return "lowest";
+ }
+}
+
+/*
+ * Iterate through the device tree for each child of 'sensors' node, create
+ * a sysfs attribute file, the file is named by translating the DT node name
+ * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max
+ * etc..
+ */
+static int create_device_attrs(struct platform_device *pdev)
+{
+ struct platform_data *pdata = platform_get_drvdata(pdev);
+ const struct attribute_group **pgroups = pdata->attr_groups;
+ struct device_node *opal, *np;
+ struct sensor_data *sdata;
+ u32 count = 0;
+ u32 group_attr_id[MAX_SENSOR_TYPE] = {0};
+
+ sdata = devm_kcalloc(&pdev->dev,
+ pdata->sensors_count, sizeof(*sdata),
+ GFP_KERNEL);
+ if (!sdata)
+ return -ENOMEM;
+
+ opal = of_find_node_by_path("/ibm,opal/sensors");
+ for_each_child_of_node(opal, np) {
+ struct sensor_group_data *sgrp_data;
+ const char *attr_name;
+ u32 opal_index, hw_id;
+ u32 sensor_id;
+ const char *label;
+ enum sensors type;
+
+ type = get_sensor_type(np);
+ if (type == MAX_SENSOR_TYPE)
+ continue;
+
+ /*
+ * Newer device trees use a "sensor-data" property
+ * name for input.
+ */
+ if (of_property_read_u32(np, "sensor-id", &sensor_id) &&
+ of_property_read_u32(np, "sensor-data", &sensor_id)) {
+ dev_info(&pdev->dev,
+ "'sensor-id' missing in the node '%pOFn'\n",
+ np);
+ continue;
+ }
+
+ sdata[count].id = sensor_id;
+ sdata[count].type = type;
+
+ /*
+ * If we can not parse the node name, it means we are
+ * running on a newer device tree. We can just forget
+ * about the OPAL index and use a defaut value for the
+ * hwmon attribute name
+ */
+ attr_name = parse_opal_node_name(np->name, type, &opal_index);
+ if (IS_ERR(attr_name)) {
+ attr_name = "input";
+ opal_index = INVALID_INDEX;
+ }
+
+ hw_id = get_sensor_hwmon_index(&sdata[count], sdata, count);
+ sgrp_data = get_sensor_group(pdata, np, type);
+ populate_sensor(&sdata[count], opal_index, hw_id, sensor_id,
+ attr_name, type, pgroups[type], sgrp_data,
+ show_sensor, NULL);
+ count++;
+
+ if (!of_property_read_string(np, "label", &label)) {
+ /*
+ * For the label attribute, we can reuse the
+ * "properties" of the previous "input"
+ * attribute. They are related to the same
+ * sensor.
+ */
+
+ make_sensor_label(np, &sdata[count], label);
+ populate_sensor(&sdata[count], opal_index, hw_id,
+ sensor_id, "label", type, pgroups[type],
+ NULL, show_label, NULL);
+ count++;
+ }
+
+ if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) {
+ attr_name = get_max_attr(type);
+ populate_sensor(&sdata[count], opal_index, hw_id,
+ sensor_id, attr_name, type,
+ pgroups[type], sgrp_data, show_sensor,
+ NULL);
+ count++;
+ }
+
+ if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) {
+ attr_name = get_min_attr(type);
+ populate_sensor(&sdata[count], opal_index, hw_id,
+ sensor_id, attr_name, type,
+ pgroups[type], sgrp_data, show_sensor,
+ NULL);
+ count++;
+ }
+
+ if (sgrp_data && !sgrp_data->enable) {
+ sgrp_data->enable = true;
+ hw_id = ++group_attr_id[type];
+ populate_sensor(&sdata[count], opal_index, hw_id,
+ sgrp_data->gid, "enable", type,
+ pgroups[type], sgrp_data, show_enable,
+ store_enable);
+ count++;
+ }
+ }
+
+ of_node_put(opal);
+ return 0;
+}
+
+static int ibmpowernv_probe(struct platform_device *pdev)
+{
+ struct platform_data *pdata;
+ struct device *hwmon_dev;
+ int err;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, pdata);
+ pdata->sensors_count = 0;
+ pdata->nr_sensor_groups = 0;
+ err = populate_attr_groups(pdev);
+ if (err)
+ return err;
+
+ /* Create sysfs attribute data for each sensor found in the DT */
+ err = create_device_attrs(pdev);
+ if (err)
+ return err;
+
+ /* Finally, register with hwmon */
+ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME,
+ pdata,
+ pdata->attr_groups);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct platform_device_id opal_sensor_driver_ids[] = {
+ {
+ .name = "opal-sensor",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
+
+static const struct of_device_id opal_sensor_match[] = {
+ { .compatible = "ibm,opal-sensor" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, opal_sensor_match);
+
+static struct platform_driver ibmpowernv_driver = {
+ .probe = ibmpowernv_probe,
+ .id_table = opal_sensor_driver_ids,
+ .driver = {
+ .name = DRVNAME,
+ .of_match_table = opal_sensor_match,
+ },
+};
+
+module_platform_driver(ibmpowernv_driver);
+
+MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("IBM POWERNV platform sensors");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
new file mode 100644
index 000000000..3aa40893f
--- /dev/null
+++ b/drivers/hwmon/iio_hwmon.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Hwmon client for industrial I/O devices
+ *
+ * Copyright (c) 2011 Jonathan Cameron
+ */
+
+#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/hwmon-sysfs.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+
+/**
+ * struct iio_hwmon_state - device instance state
+ * @channels: filled with array of channels from iio
+ * @num_channels: number of channels in channels (saves counting twice)
+ * @attr_group: the group of attributes
+ * @groups: null terminated array of attribute groups
+ * @attrs: null terminated array of attribute pointers.
+ */
+struct iio_hwmon_state {
+ struct iio_channel *channels;
+ int num_channels;
+ struct attribute_group attr_group;
+ const struct attribute_group *groups[2];
+ struct attribute **attrs;
+};
+
+/*
+ * Assumes that IIO and hwmon operate in the same base units.
+ * This is supposed to be true, but needs verification for
+ * new channel types.
+ */
+static ssize_t iio_hwmon_read_val(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int result;
+ int ret;
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct iio_hwmon_state *state = dev_get_drvdata(dev);
+ struct iio_channel *chan = &state->channels[sattr->index];
+ enum iio_chan_type type;
+
+ ret = iio_read_channel_processed(chan, &result);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_get_channel_type(chan, &type);
+ if (ret < 0)
+ return ret;
+
+ if (type == IIO_POWER)
+ result *= 1000; /* mili-Watts to micro-Watts conversion */
+
+ return sprintf(buf, "%d\n", result);
+}
+
+static int iio_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_hwmon_state *st;
+ struct sensor_device_attribute *a;
+ int ret, i;
+ int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1, power_i = 1;
+ enum iio_chan_type type;
+ struct iio_channel *channels;
+ struct device *hwmon_dev;
+ char *sname;
+
+ channels = devm_iio_channel_get_all(dev);
+ if (IS_ERR(channels)) {
+ if (PTR_ERR(channels) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(channels);
+ }
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (st == NULL)
+ return -ENOMEM;
+
+ st->channels = channels;
+
+ /* count how many attributes we have */
+ while (st->channels[st->num_channels].indio_dev)
+ st->num_channels++;
+
+ st->attrs = devm_kcalloc(dev,
+ st->num_channels + 1, sizeof(*st->attrs),
+ GFP_KERNEL);
+ if (st->attrs == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < st->num_channels; i++) {
+ const char *prefix;
+ int n;
+
+ a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL);
+ if (a == NULL)
+ return -ENOMEM;
+
+ sysfs_attr_init(&a->dev_attr.attr);
+ ret = iio_get_channel_type(&st->channels[i], &type);
+ if (ret < 0)
+ return ret;
+
+ switch (type) {
+ case IIO_VOLTAGE:
+ n = in_i++;
+ prefix = "in";
+ break;
+ case IIO_TEMP:
+ n = temp_i++;
+ prefix = "temp";
+ break;
+ case IIO_CURRENT:
+ n = curr_i++;
+ prefix = "curr";
+ break;
+ case IIO_POWER:
+ n = power_i++;
+ prefix = "power";
+ break;
+ case IIO_HUMIDITYRELATIVE:
+ n = humidity_i++;
+ prefix = "humidity";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ a->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s%d_input",
+ prefix, n);
+ if (a->dev_attr.attr.name == NULL)
+ return -ENOMEM;
+
+ a->dev_attr.show = iio_hwmon_read_val;
+ a->dev_attr.attr.mode = 0444;
+ a->index = i;
+ st->attrs[i] = &a->dev_attr.attr;
+ }
+
+ st->attr_group.attrs = st->attrs;
+ st->groups[0] = &st->attr_group;
+
+ if (dev_fwnode(dev)) {
+ sname = devm_kasprintf(dev, GFP_KERNEL, "%pfwP", dev_fwnode(dev));
+ if (!sname)
+ return -ENOMEM;
+ strreplace(sname, '-', '_');
+ } else {
+ sname = "iio_hwmon";
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, sname, st,
+ st->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct of_device_id iio_hwmon_of_match[] = {
+ { .compatible = "iio-hwmon", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, iio_hwmon_of_match);
+
+static struct platform_driver iio_hwmon_driver = {
+ .driver = {
+ .name = "iio_hwmon",
+ .of_match_table = iio_hwmon_of_match,
+ },
+ .probe = iio_hwmon_probe,
+};
+
+module_platform_driver(iio_hwmon_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("IIO to hwmon driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c
new file mode 100644
index 000000000..9b58655d2
--- /dev/null
+++ b/drivers/hwmon/ina209.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the Texas Instruments / Burr Brown INA209
+ * Bidirectional Current/Power Monitor
+ *
+ * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from Ira W. Snyder's original driver submission
+ * Copyright (C) 2008 Paul Hays <Paul.Hays@cattail.ca>
+ * Copyright (C) 2008-2009 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * Aligned with ina2xx driver
+ * Copyright (C) 2012 Lothar Felten <l-felten@ti.com>
+ * Thanks to Jan Volkering
+ *
+ * Datasheet:
+ * https://www.ti.com/lit/gpn/ina209
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/bug.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/ina2xx.h>
+
+/* register definitions */
+#define INA209_CONFIGURATION 0x00
+#define INA209_STATUS 0x01
+#define INA209_STATUS_MASK 0x02
+#define INA209_SHUNT_VOLTAGE 0x03
+#define INA209_BUS_VOLTAGE 0x04
+#define INA209_POWER 0x05
+#define INA209_CURRENT 0x06
+#define INA209_SHUNT_VOLTAGE_POS_PEAK 0x07
+#define INA209_SHUNT_VOLTAGE_NEG_PEAK 0x08
+#define INA209_BUS_VOLTAGE_MAX_PEAK 0x09
+#define INA209_BUS_VOLTAGE_MIN_PEAK 0x0a
+#define INA209_POWER_PEAK 0x0b
+#define INA209_SHUNT_VOLTAGE_POS_WARN 0x0c
+#define INA209_SHUNT_VOLTAGE_NEG_WARN 0x0d
+#define INA209_POWER_WARN 0x0e
+#define INA209_BUS_VOLTAGE_OVER_WARN 0x0f
+#define INA209_BUS_VOLTAGE_UNDER_WARN 0x10
+#define INA209_POWER_OVER_LIMIT 0x11
+#define INA209_BUS_VOLTAGE_OVER_LIMIT 0x12
+#define INA209_BUS_VOLTAGE_UNDER_LIMIT 0x13
+#define INA209_CRITICAL_DAC_POS 0x14
+#define INA209_CRITICAL_DAC_NEG 0x15
+#define INA209_CALIBRATION 0x16
+
+#define INA209_REGISTERS 0x17
+
+#define INA209_CONFIG_DEFAULT 0x3c47 /* PGA=8, full range */
+#define INA209_SHUNT_DEFAULT 10000 /* uOhm */
+
+struct ina209_data {
+ struct i2c_client *client;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ u16 regs[INA209_REGISTERS]; /* All chip registers */
+
+ u16 config_orig; /* Original configuration */
+ u16 calibration_orig; /* Original calibration */
+ u16 update_interval;
+};
+
+static struct ina209_data *ina209_update_device(struct device *dev)
+{
+ struct ina209_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct ina209_data *ret = data;
+ s32 val;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (!data->valid ||
+ time_after(jiffies, data->last_updated + data->update_interval)) {
+ for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
+ val = i2c_smbus_read_word_swapped(client, i);
+ if (val < 0) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->regs[i] = val;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+/*
+ * Read a value from a device register and convert it to the
+ * appropriate sysfs units
+ */
+static long ina209_from_reg(const u8 reg, const u16 val)
+{
+ switch (reg) {
+ case INA209_SHUNT_VOLTAGE:
+ case INA209_SHUNT_VOLTAGE_POS_PEAK:
+ case INA209_SHUNT_VOLTAGE_NEG_PEAK:
+ case INA209_SHUNT_VOLTAGE_POS_WARN:
+ case INA209_SHUNT_VOLTAGE_NEG_WARN:
+ /* LSB=10 uV. Convert to mV. */
+ return DIV_ROUND_CLOSEST((s16)val, 100);
+
+ case INA209_BUS_VOLTAGE:
+ case INA209_BUS_VOLTAGE_MAX_PEAK:
+ case INA209_BUS_VOLTAGE_MIN_PEAK:
+ case INA209_BUS_VOLTAGE_OVER_WARN:
+ case INA209_BUS_VOLTAGE_UNDER_WARN:
+ case INA209_BUS_VOLTAGE_OVER_LIMIT:
+ case INA209_BUS_VOLTAGE_UNDER_LIMIT:
+ /* LSB=4 mV, last 3 bits unused */
+ return (val >> 3) * 4;
+
+ case INA209_CRITICAL_DAC_POS:
+ /* LSB=1 mV, in the upper 8 bits */
+ return val >> 8;
+
+ case INA209_CRITICAL_DAC_NEG:
+ /* LSB=1 mV, in the upper 8 bits */
+ return -1 * (val >> 8);
+
+ case INA209_POWER:
+ case INA209_POWER_PEAK:
+ case INA209_POWER_WARN:
+ case INA209_POWER_OVER_LIMIT:
+ /* LSB=20 mW. Convert to uW */
+ return val * 20 * 1000L;
+
+ case INA209_CURRENT:
+ /* LSB=1 mA (selected). Is in mA */
+ return (s16)val;
+ }
+
+ /* programmer goofed */
+ WARN_ON_ONCE(1);
+ return 0;
+}
+
+/*
+ * Take a value and convert it to register format, clamping the value
+ * to the appropriate range.
+ */
+static int ina209_to_reg(u8 reg, u16 old, long val)
+{
+ switch (reg) {
+ case INA209_SHUNT_VOLTAGE_POS_WARN:
+ case INA209_SHUNT_VOLTAGE_NEG_WARN:
+ /* Limit to +- 320 mV, 10 uV LSB */
+ return clamp_val(val, -320, 320) * 100;
+
+ case INA209_BUS_VOLTAGE_OVER_WARN:
+ case INA209_BUS_VOLTAGE_UNDER_WARN:
+ case INA209_BUS_VOLTAGE_OVER_LIMIT:
+ case INA209_BUS_VOLTAGE_UNDER_LIMIT:
+ /*
+ * Limit to 0-32000 mV, 4 mV LSB
+ *
+ * The last three bits aren't part of the value, but we'll
+ * preserve them in their original state.
+ */
+ return (DIV_ROUND_CLOSEST(clamp_val(val, 0, 32000), 4) << 3)
+ | (old & 0x7);
+
+ case INA209_CRITICAL_DAC_NEG:
+ /*
+ * Limit to -255-0 mV, 1 mV LSB
+ * Convert the value to a positive value for the register
+ *
+ * The value lives in the top 8 bits only, be careful
+ * and keep original value of other bits.
+ */
+ return (clamp_val(-val, 0, 255) << 8) | (old & 0xff);
+
+ case INA209_CRITICAL_DAC_POS:
+ /*
+ * Limit to 0-255 mV, 1 mV LSB
+ *
+ * The value lives in the top 8 bits only, be careful
+ * and keep original value of other bits.
+ */
+ return (clamp_val(val, 0, 255) << 8) | (old & 0xff);
+
+ case INA209_POWER_WARN:
+ case INA209_POWER_OVER_LIMIT:
+ /* 20 mW LSB */
+ return DIV_ROUND_CLOSEST(val, 20 * 1000);
+ }
+
+ /* Other registers are read-only, return access error */
+ return -EACCES;
+}
+
+static int ina209_interval_from_reg(u16 reg)
+{
+ return 68 >> (15 - ((reg >> 3) & 0x0f));
+}
+
+static u16 ina209_reg_from_interval(u16 config, long interval)
+{
+ int i, adc;
+
+ if (interval <= 0) {
+ adc = 8;
+ } else {
+ adc = 15;
+ for (i = 34 + 34 / 2; i; i >>= 1) {
+ if (i < interval)
+ break;
+ adc--;
+ }
+ }
+ return (config & 0xf807) | (adc << 3) | (adc << 7);
+}
+
+static ssize_t ina209_interval_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct ina209_data *data = ina209_update_device(dev);
+ long val;
+ u16 regval;
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ regval = ina209_reg_from_interval(data->regs[INA209_CONFIGURATION],
+ val);
+ i2c_smbus_write_word_swapped(data->client, INA209_CONFIGURATION,
+ regval);
+ data->regs[INA209_CONFIGURATION] = regval;
+ data->update_interval = ina209_interval_from_reg(regval);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t ina209_interval_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct ina209_data *data = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", data->update_interval);
+}
+
+/*
+ * History is reset by writing 1 into bit 0 of the respective peak register.
+ * Since more than one peak register may be affected by the scope of a
+ * reset_history attribute write, use a bit mask in attr->index to identify
+ * which registers are affected.
+ */
+static u16 ina209_reset_history_regs[] = {
+ INA209_SHUNT_VOLTAGE_POS_PEAK,
+ INA209_SHUNT_VOLTAGE_NEG_PEAK,
+ INA209_BUS_VOLTAGE_MAX_PEAK,
+ INA209_BUS_VOLTAGE_MIN_PEAK,
+ INA209_POWER_PEAK
+};
+
+static ssize_t ina209_history_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 ina209_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u32 mask = attr->index;
+ long val;
+ int i, ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ for (i = 0; i < ARRAY_SIZE(ina209_reset_history_regs); i++) {
+ if (mask & (1 << i))
+ i2c_smbus_write_word_swapped(client,
+ ina209_reset_history_regs[i], 1);
+ }
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t ina209_value_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct ina209_data *data = ina209_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int reg = attr->index;
+ long val;
+ int ret;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ ret = ina209_to_reg(reg, data->regs[reg], val);
+ if (ret < 0) {
+ count = ret;
+ goto abort;
+ }
+ i2c_smbus_write_word_swapped(data->client, reg, ret);
+ data->regs[reg] = ret;
+abort:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t ina209_value_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ina209_data *data = ina209_update_device(dev);
+ long val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = ina209_from_reg(attr->index, data->regs[attr->index]);
+ return sysfs_emit(buf, "%ld\n", val);
+}
+
+static ssize_t ina209_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ina209_data *data = ina209_update_device(dev);
+ const unsigned int mask = attr->index;
+ u16 status;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ status = data->regs[INA209_STATUS];
+
+ /*
+ * All alarms are in the INA209_STATUS register. To avoid a long
+ * switch statement, the mask is passed in attr->index
+ */
+ return sysfs_emit(buf, "%u\n", !!(status & mask));
+}
+
+/* Shunt voltage, history, limits, alarms */
+static SENSOR_DEVICE_ATTR_RO(in0_input, ina209_value, INA209_SHUNT_VOLTAGE);
+static SENSOR_DEVICE_ATTR_RO(in0_input_highest, ina209_value,
+ INA209_SHUNT_VOLTAGE_POS_PEAK);
+static SENSOR_DEVICE_ATTR_RO(in0_input_lowest, ina209_value,
+ INA209_SHUNT_VOLTAGE_NEG_PEAK);
+static SENSOR_DEVICE_ATTR_WO(in0_reset_history, ina209_history,
+ (1 << 0) | (1 << 1));
+static SENSOR_DEVICE_ATTR_RW(in0_max, ina209_value,
+ INA209_SHUNT_VOLTAGE_POS_WARN);
+static SENSOR_DEVICE_ATTR_RW(in0_min, ina209_value,
+ INA209_SHUNT_VOLTAGE_NEG_WARN);
+static SENSOR_DEVICE_ATTR_RW(in0_crit_max, ina209_value,
+ INA209_CRITICAL_DAC_POS);
+static SENSOR_DEVICE_ATTR_RW(in0_crit_min, ina209_value,
+ INA209_CRITICAL_DAC_NEG);
+
+static SENSOR_DEVICE_ATTR_RO(in0_min_alarm, ina209_alarm, 1 << 11);
+static SENSOR_DEVICE_ATTR_RO(in0_max_alarm, ina209_alarm, 1 << 12);
+static SENSOR_DEVICE_ATTR_RO(in0_crit_min_alarm, ina209_alarm, 1 << 6);
+static SENSOR_DEVICE_ATTR_RO(in0_crit_max_alarm, ina209_alarm, 1 << 7);
+
+/* Bus voltage, history, limits, alarms */
+static SENSOR_DEVICE_ATTR_RO(in1_input, ina209_value, INA209_BUS_VOLTAGE);
+static SENSOR_DEVICE_ATTR_RO(in1_input_highest, ina209_value,
+ INA209_BUS_VOLTAGE_MAX_PEAK);
+static SENSOR_DEVICE_ATTR_RO(in1_input_lowest, ina209_value,
+ INA209_BUS_VOLTAGE_MIN_PEAK);
+static SENSOR_DEVICE_ATTR_WO(in1_reset_history, ina209_history,
+ (1 << 2) | (1 << 3));
+static SENSOR_DEVICE_ATTR_RW(in1_max, ina209_value,
+ INA209_BUS_VOLTAGE_OVER_WARN);
+static SENSOR_DEVICE_ATTR_RW(in1_min, ina209_value,
+ INA209_BUS_VOLTAGE_UNDER_WARN);
+static SENSOR_DEVICE_ATTR_RW(in1_crit_max, ina209_value,
+ INA209_BUS_VOLTAGE_OVER_LIMIT);
+static SENSOR_DEVICE_ATTR_RW(in1_crit_min, ina209_value,
+ INA209_BUS_VOLTAGE_UNDER_LIMIT);
+
+static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ina209_alarm, 1 << 14);
+static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ina209_alarm, 1 << 15);
+static SENSOR_DEVICE_ATTR_RO(in1_crit_min_alarm, ina209_alarm, 1 << 9);
+static SENSOR_DEVICE_ATTR_RO(in1_crit_max_alarm, ina209_alarm, 1 << 10);
+
+/* Power */
+static SENSOR_DEVICE_ATTR_RO(power1_input, ina209_value, INA209_POWER);
+static SENSOR_DEVICE_ATTR_RO(power1_input_highest, ina209_value,
+ INA209_POWER_PEAK);
+static SENSOR_DEVICE_ATTR_WO(power1_reset_history, ina209_history, 1 << 4);
+static SENSOR_DEVICE_ATTR_RW(power1_max, ina209_value, INA209_POWER_WARN);
+static SENSOR_DEVICE_ATTR_RW(power1_crit, ina209_value,
+ INA209_POWER_OVER_LIMIT);
+
+static SENSOR_DEVICE_ATTR_RO(power1_max_alarm, ina209_alarm, 1 << 13);
+static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina209_alarm, 1 << 8);
+
+/* Current */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ina209_value, INA209_CURRENT);
+
+static SENSOR_DEVICE_ATTR_RW(update_interval, ina209_interval, 0);
+
+/*
+ * Finally, construct an array of pointers to members of the above objects,
+ * as required for sysfs_create_group()
+ */
+static struct attribute *ina209_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_input_highest.dev_attr.attr,
+ &sensor_dev_attr_in0_input_lowest.dev_attr.attr,
+ &sensor_dev_attr_in0_reset_history.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_crit_max.dev_attr.attr,
+ &sensor_dev_attr_in0_crit_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_crit_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_crit_min_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input_highest.dev_attr.attr,
+ &sensor_dev_attr_in1_input_lowest.dev_attr.attr,
+ &sensor_dev_attr_in1_reset_history.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_crit_max.dev_attr.attr,
+ &sensor_dev_attr_in1_crit_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_crit_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_crit_min_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_power1_input.dev_attr.attr,
+ &sensor_dev_attr_power1_input_highest.dev_attr.attr,
+ &sensor_dev_attr_power1_reset_history.dev_attr.attr,
+ &sensor_dev_attr_power1_max.dev_attr.attr,
+ &sensor_dev_attr_power1_crit.dev_attr.attr,
+ &sensor_dev_attr_power1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_power1_crit_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+
+ &sensor_dev_attr_update_interval.dev_attr.attr,
+
+ NULL,
+};
+ATTRIBUTE_GROUPS(ina209);
+
+static void ina209_restore_conf(struct i2c_client *client,
+ struct ina209_data *data)
+{
+ /* Restore initial configuration */
+ i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION,
+ data->config_orig);
+ i2c_smbus_write_word_swapped(client, INA209_CALIBRATION,
+ data->calibration_orig);
+}
+
+static int ina209_init_client(struct i2c_client *client,
+ struct ina209_data *data)
+{
+ struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev);
+ u32 shunt;
+ int reg;
+
+ reg = i2c_smbus_read_word_swapped(client, INA209_CALIBRATION);
+ if (reg < 0)
+ return reg;
+ data->calibration_orig = reg;
+
+ reg = i2c_smbus_read_word_swapped(client, INA209_CONFIGURATION);
+ if (reg < 0)
+ return reg;
+ data->config_orig = reg;
+
+ if (pdata) {
+ if (pdata->shunt_uohms <= 0)
+ return -EINVAL;
+ shunt = pdata->shunt_uohms;
+ } else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor",
+ &shunt)) {
+ if (shunt == 0)
+ return -EINVAL;
+ } else {
+ shunt = data->calibration_orig ?
+ 40960000 / data->calibration_orig : INA209_SHUNT_DEFAULT;
+ }
+
+ i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION,
+ INA209_CONFIG_DEFAULT);
+ data->update_interval = ina209_interval_from_reg(INA209_CONFIG_DEFAULT);
+
+ /*
+ * Calibrate current LSB to 1mA. Shunt is in uOhms.
+ * See equation 13 in datasheet.
+ */
+ i2c_smbus_write_word_swapped(client, INA209_CALIBRATION,
+ clamp_val(40960000 / shunt, 1, 65535));
+
+ /* Clear status register */
+ i2c_smbus_read_word_swapped(client, INA209_STATUS);
+
+ return 0;
+}
+
+static int ina209_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct ina209_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ ret = ina209_init_client(client, data);
+ if (ret)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name,
+ data, ina209_groups);
+ if (IS_ERR(hwmon_dev)) {
+ ret = PTR_ERR(hwmon_dev);
+ goto out_restore_conf;
+ }
+
+ return 0;
+
+out_restore_conf:
+ ina209_restore_conf(client, data);
+ return ret;
+}
+
+static void ina209_remove(struct i2c_client *client)
+{
+ struct ina209_data *data = i2c_get_clientdata(client);
+
+ ina209_restore_conf(client, data);
+}
+
+static const struct i2c_device_id ina209_id[] = {
+ { "ina209", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ina209_id);
+
+static const struct of_device_id __maybe_unused ina209_of_match[] = {
+ { .compatible = "ti,ina209" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ina209_of_match);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ina209_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "ina209",
+ .of_match_table = of_match_ptr(ina209_of_match),
+ },
+ .probe_new = ina209_probe,
+ .remove = ina209_remove,
+ .id_table = ina209_id,
+};
+
+module_i2c_driver(ina209_driver);
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>, Paul Hays <Paul.Hays@cattail.ca>, Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("INA209 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c
new file mode 100644
index 000000000..50eb9c5e1
--- /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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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
new file mode 100644
index 000000000..00fc70305
--- /dev/null
+++ b/drivers/hwmon/ina2xx.c
@@ -0,0 +1,728 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Texas Instruments INA219, INA226 power monitor chips
+ *
+ * INA219:
+ * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: https://www.ti.com/product/ina219
+ *
+ * INA220:
+ * Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: https://www.ti.com/product/ina220
+ *
+ * INA226:
+ * Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: https://www.ti.com/product/ina226
+ *
+ * INA230:
+ * Bi-directional Current/Power Monitor with I2C Interface
+ * Datasheet: https://www.ti.com/product/ina230
+ *
+ * Copyright (C) 2012 Lothar Felten <lothar.felten@gmail.com>
+ * Thanks to Jan Volkering
+ */
+
+#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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/util_macros.h>
+#include <linux/regmap.h>
+
+#include <linux/platform_data/ina2xx.h>
+
+/* common register definitions */
+#define INA2XX_CONFIG 0x00
+#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
+#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
+#define INA2XX_POWER 0x03 /* readonly */
+#define INA2XX_CURRENT 0x04 /* readonly */
+#define INA2XX_CALIBRATION 0x05
+
+/* INA226 register definitions */
+#define INA226_MASK_ENABLE 0x06
+#define INA226_ALERT_LIMIT 0x07
+#define INA226_DIE_ID 0xFF
+
+/* register count */
+#define INA219_REGISTERS 6
+#define INA226_REGISTERS 8
+
+#define INA2XX_MAX_REGISTERS 8
+
+/* settings - depend on use case */
+#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
+#define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */
+
+/* worst case is 68.10 ms (~14.6Hz, ina219) */
+#define INA2XX_CONVERSION_RATE 15
+#define INA2XX_MAX_DELAY 69 /* worst case delay in ms */
+
+#define INA2XX_RSHUNT_DEFAULT 10000
+
+/* bit mask for reading the averaging setting in the configuration register */
+#define INA226_AVG_RD_MASK 0x0E00
+
+#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
+
+/*
+ * Both bus voltage and shunt voltage conversion times for ina226 are set
+ * to 0b0100 on POR, which translates to 2200 microseconds in total.
+ */
+#define INA226_TOTAL_CONV_TIME_DEFAULT 2200
+
+static struct regmap_config ina2xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+};
+
+enum ina2xx_ids { ina219, ina226 };
+
+struct ina2xx_config {
+ u16 config_default;
+ int calibration_value;
+ int registers;
+ int shunt_div;
+ int bus_voltage_shift;
+ int bus_voltage_lsb; /* uV */
+ int power_lsb_factor;
+};
+
+struct ina2xx_data {
+ const struct ina2xx_config *config;
+
+ long rshunt;
+ long current_lsb_uA;
+ long power_lsb_uW;
+ struct mutex config_lock;
+ struct regmap *regmap;
+
+ const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS];
+};
+
+static const struct ina2xx_config ina2xx_config[] = {
+ [ina219] = {
+ .config_default = INA219_CONFIG_DEFAULT,
+ .calibration_value = 4096,
+ .registers = INA219_REGISTERS,
+ .shunt_div = 100,
+ .bus_voltage_shift = 3,
+ .bus_voltage_lsb = 4000,
+ .power_lsb_factor = 20,
+ },
+ [ina226] = {
+ .config_default = INA226_CONFIG_DEFAULT,
+ .calibration_value = 2048,
+ .registers = INA226_REGISTERS,
+ .shunt_div = 400,
+ .bus_voltage_shift = 0,
+ .bus_voltage_lsb = 1250,
+ .power_lsb_factor = 25,
+ },
+};
+
+/*
+ * 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
+ * https://www.ti.com/lit/ds/symlink/ina226.pdf.
+ */
+static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
+
+static int ina226_reg_to_interval(u16 config)
+{
+ int avg = ina226_avg_tab[INA226_READ_AVG(config)];
+
+ /*
+ * Multiply the total conversion time by the number of averages.
+ * Return the result in milliseconds.
+ */
+ return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000);
+}
+
+/*
+ * Return the new, shifted AVG field value of CONFIG register,
+ * to use with regmap_update_bits
+ */
+static u16 ina226_interval_to_reg(int interval)
+{
+ int avg, avg_bits;
+
+ avg = DIV_ROUND_CLOSEST(interval * 1000,
+ INA226_TOTAL_CONV_TIME_DEFAULT);
+ avg_bits = find_closest(avg, ina226_avg_tab,
+ ARRAY_SIZE(ina226_avg_tab));
+
+ return INA226_SHIFT_AVG(avg_bits);
+}
+
+/*
+ * Calibration register is set to the best value, which eliminates
+ * truncation errors on calculating current register in hardware.
+ * According to datasheet (eq. 3) the best values are 2048 for
+ * ina226 and 4096 for ina219. They are hardcoded as calibration_value.
+ */
+static int ina2xx_calibrate(struct ina2xx_data *data)
+{
+ return regmap_write(data->regmap, INA2XX_CALIBRATION,
+ data->config->calibration_value);
+}
+
+/*
+ * Initialize the configuration and calibration registers.
+ */
+static int ina2xx_init(struct ina2xx_data *data)
+{
+ int ret = regmap_write(data->regmap, INA2XX_CONFIG,
+ data->config->config_default);
+ if (ret < 0)
+ return ret;
+
+ return ina2xx_calibrate(data);
+}
+
+static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
+{
+ struct ina2xx_data *data = dev_get_drvdata(dev);
+ int ret, retry;
+
+ dev_dbg(dev, "Starting register %d read\n", reg);
+
+ for (retry = 5; retry; retry--) {
+
+ ret = regmap_read(data->regmap, reg, regval);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval);
+
+ /*
+ * If the current value in the calibration register is 0, the
+ * power and current registers will also remain at 0. In case
+ * the chip has been reset let's check the calibration
+ * register and reinitialize if needed.
+ * We do that extra read of the calibration register if there
+ * is some hint of a chip reset.
+ */
+ if (*regval == 0) {
+ unsigned int cal;
+
+ ret = regmap_read(data->regmap, INA2XX_CALIBRATION,
+ &cal);
+ if (ret < 0)
+ return ret;
+
+ if (cal == 0) {
+ dev_warn(dev, "chip not calibrated, reinitializing\n");
+
+ ret = ina2xx_init(data);
+ if (ret < 0)
+ return ret;
+ /*
+ * Let's make sure the power and current
+ * registers have been updated before trying
+ * again.
+ */
+ msleep(INA2XX_MAX_DELAY);
+ continue;
+ }
+ }
+ return 0;
+ }
+
+ /*
+ * If we're here then although all write operations succeeded, the
+ * chip still returns 0 in the calibration register. Nothing more we
+ * can do here.
+ */
+ dev_err(dev, "unable to reinitialize the chip\n");
+ return -ENODEV;
+}
+
+static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+ unsigned int regval)
+{
+ int val;
+
+ switch (reg) {
+ case INA2XX_SHUNT_VOLTAGE:
+ /* signed register */
+ val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div);
+ break;
+ case INA2XX_BUS_VOLTAGE:
+ val = (regval >> data->config->bus_voltage_shift)
+ * data->config->bus_voltage_lsb;
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ break;
+ case INA2XX_POWER:
+ val = regval * data->power_lsb_uW;
+ break;
+ case INA2XX_CURRENT:
+ /* signed register, result in mA */
+ val = (s16)regval * data->current_lsb_uA;
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ break;
+ case INA2XX_CALIBRATION:
+ val = regval;
+ break;
+ default:
+ /* programmer goofed */
+ WARN_ON_ONCE(1);
+ val = 0;
+ break;
+ }
+
+ return val;
+}
+
+static ssize_t ina2xx_value_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);
+ unsigned int regval;
+
+ int err = ina2xx_read_reg(dev, attr->index, &regval);
+
+ if (err < 0)
+ return err;
+
+ 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, &regval);
+ if (ret)
+ goto abort;
+
+ if (regval & BIT(attr->index)) {
+ ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, &regval);
+ 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, &regval);
+ if (ret)
+ return ret;
+
+ alarm = (regval & BIT(attr->index)) &&
+ (regval & INA226_ALERT_FUNCTION_FLAG);
+ return sysfs_emit(buf, "%d\n", alarm);
+}
+
+/*
+ * In order to keep calibration register value fixed, the product
+ * of current_lsb and shunt_resistor should also be fixed and equal
+ * to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order
+ * to keep the scale.
+ */
+static int ina2xx_set_shunt(struct ina2xx_data *data, long val)
+{
+ unsigned int dividend = DIV_ROUND_CLOSEST(1000000000,
+ data->config->shunt_div);
+ if (val <= 0 || val > dividend)
+ return -EINVAL;
+
+ mutex_lock(&data->config_lock);
+ data->rshunt = val;
+ data->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val);
+ data->power_lsb_uW = data->config->power_lsb_factor *
+ data->current_lsb_uA;
+ mutex_unlock(&data->config_lock);
+
+ return 0;
+}
+
+static ssize_t ina2xx_shunt_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct ina2xx_data *data = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%li\n", data->rshunt);
+}
+
+static ssize_t ina2xx_shunt_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ int status;
+ struct ina2xx_data *data = dev_get_drvdata(dev);
+
+ status = kstrtoul(buf, 10, &val);
+ if (status < 0)
+ return status;
+
+ status = ina2xx_set_shunt(data, val);
+ if (status < 0)
+ return status;
+ return count;
+}
+
+static ssize_t ina226_interval_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct ina2xx_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int status;
+
+ status = kstrtoul(buf, 10, &val);
+ if (status < 0)
+ return status;
+
+ if (val > INT_MAX || val == 0)
+ return -EINVAL;
+
+ status = regmap_update_bits(data->regmap, INA2XX_CONFIG,
+ INA226_AVG_RD_MASK,
+ ina226_interval_to_reg(val));
+ if (status < 0)
+ return status;
+
+ return count;
+}
+
+static ssize_t ina226_interval_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct ina2xx_data *data = dev_get_drvdata(dev);
+ int status;
+ unsigned int regval;
+
+ status = regmap_read(data->regmap, INA2XX_CONFIG, &regval);
+ if (status)
+ return status;
+
+ 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);
+
+/* update interval (ina226 only) */
+static SENSOR_DEVICE_ATTR_RW(update_interval, ina226_interval, 0);
+
+/* pointers to created device attributes */
+static struct attribute *ina2xx_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_power1_input.dev_attr.attr,
+ &sensor_dev_attr_shunt_resistor.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ina2xx_group = {
+ .attrs = ina2xx_attrs,
+};
+
+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,
+};
+
+static const struct attribute_group ina226_group = {
+ .attrs = ina226_attrs,
+};
+
+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;
+ struct device *hwmon_dev;
+ u32 val;
+ int ret, group = 0;
+ enum ina2xx_ids chip;
+
+ if (client->dev.of_node)
+ chip = (enum ina2xx_ids)of_device_get_match_data(&client->dev);
+ else
+ chip = i2c_match_id(ina2xx_id, client)->driver_data;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* set the device type */
+ data->config = &ina2xx_config[chip];
+ mutex_init(&data->config_lock);
+
+ if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
+ struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
+
+ if (pdata)
+ val = pdata->shunt_uohms;
+ else
+ val = INA2XX_RSHUNT_DEFAULT;
+ }
+
+ ina2xx_set_shunt(data, val);
+
+ ina2xx_regmap_config.max_register = data->config->registers;
+
+ data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = ina2xx_init(data);
+ if (ret < 0) {
+ dev_err(dev, "error configuring the device: %d\n", ret);
+ return -ENODEV;
+ }
+
+ data->groups[group++] = &ina2xx_group;
+ if (chip == ina226)
+ data->groups[group++] = &ina226_group;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n",
+ client->name, data->rshunt);
+
+ return 0;
+}
+
+static const struct i2c_device_id ina2xx_id[] = {
+ { "ina219", ina219 },
+ { "ina220", ina219 },
+ { "ina226", ina226 },
+ { "ina230", ina226 },
+ { "ina231", ina226 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+
+static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
+ {
+ .compatible = "ti,ina219",
+ .data = (void *)ina219
+ },
+ {
+ .compatible = "ti,ina220",
+ .data = (void *)ina219
+ },
+ {
+ .compatible = "ti,ina226",
+ .data = (void *)ina226
+ },
+ {
+ .compatible = "ti,ina230",
+ .data = (void *)ina226
+ },
+ {
+ .compatible = "ti,ina231",
+ .data = (void *)ina226
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ina2xx_of_match);
+
+static struct i2c_driver ina2xx_driver = {
+ .driver = {
+ .name = "ina2xx",
+ .of_match_table = of_match_ptr(ina2xx_of_match),
+ },
+ .probe_new = ina2xx_probe,
+ .id_table = ina2xx_id,
+};
+
+module_i2c_driver(ina2xx_driver);
+
+MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>");
+MODULE_DESCRIPTION("ina2xx driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
new file mode 100644
index 000000000..f3a4c5633
--- /dev/null
+++ b/drivers/hwmon/ina3221.c
@@ -0,0 +1,1026 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * INA3221 Triple Current/Voltage Monitor
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
+
+#define INA3221_DRIVER_NAME "ina3221"
+
+#define INA3221_CONFIG 0x00
+#define INA3221_SHUNT1 0x01
+#define INA3221_BUS1 0x02
+#define INA3221_SHUNT2 0x03
+#define INA3221_BUS2 0x04
+#define INA3221_SHUNT3 0x05
+#define INA3221_BUS3 0x06
+#define INA3221_CRIT1 0x07
+#define INA3221_WARN1 0x08
+#define INA3221_CRIT2 0x09
+#define INA3221_WARN2 0x0a
+#define INA3221_CRIT3 0x0b
+#define INA3221_WARN3 0x0c
+#define INA3221_SHUNT_SUM 0x0d
+#define INA3221_CRIT_SUM 0x0e
+#define INA3221_MASK_ENABLE 0x0f
+
+#define INA3221_CONFIG_MODE_MASK GENMASK(2, 0)
+#define INA3221_CONFIG_MODE_POWERDOWN 0
+#define INA3221_CONFIG_MODE_SHUNT BIT(0)
+#define INA3221_CONFIG_MODE_BUS BIT(1)
+#define INA3221_CONFIG_MODE_CONTINUOUS BIT(2)
+#define INA3221_CONFIG_VSH_CT_SHIFT 3
+#define INA3221_CONFIG_VSH_CT_MASK GENMASK(5, 3)
+#define INA3221_CONFIG_VSH_CT(x) (((x) & GENMASK(5, 3)) >> 3)
+#define INA3221_CONFIG_VBUS_CT_SHIFT 6
+#define INA3221_CONFIG_VBUS_CT_MASK GENMASK(8, 6)
+#define INA3221_CONFIG_VBUS_CT(x) (((x) & GENMASK(8, 6)) >> 6)
+#define INA3221_CONFIG_AVG_SHIFT 9
+#define INA3221_CONFIG_AVG_MASK GENMASK(11, 9)
+#define INA3221_CONFIG_AVG(x) (((x) & GENMASK(11, 9)) >> 9)
+#define INA3221_CONFIG_CHs_EN_MASK GENMASK(14, 12)
+#define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x))
+
+#define INA3221_MASK_ENABLE_SCC_MASK GENMASK(14, 12)
+
+#define INA3221_CONFIG_DEFAULT 0x7127
+#define INA3221_RSHUNT_DEFAULT 10000
+
+enum ina3221_fields {
+ /* Configuration */
+ F_RST,
+
+ /* Status Flags */
+ F_CVRF,
+
+ /* Warning Flags */
+ F_WF3, F_WF2, F_WF1,
+
+ /* Alert Flags: SF is the summation-alert flag */
+ F_SF, F_CF3, F_CF2, F_CF1,
+
+ /* sentinel */
+ F_MAX_FIELDS
+};
+
+static const struct reg_field ina3221_reg_fields[] = {
+ [F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15),
+
+ [F_CVRF] = REG_FIELD(INA3221_MASK_ENABLE, 0, 0),
+ [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3),
+ [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4),
+ [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5),
+ [F_SF] = REG_FIELD(INA3221_MASK_ENABLE, 6, 6),
+ [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7),
+ [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8),
+ [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9),
+};
+
+enum ina3221_channels {
+ INA3221_CHANNEL1,
+ INA3221_CHANNEL2,
+ INA3221_CHANNEL3,
+ INA3221_NUM_CHANNELS
+};
+
+/**
+ * struct ina3221_input - channel input source specific information
+ * @label: label of channel input source
+ * @shunt_resistor: shunt resistor value of channel input source
+ * @disconnected: connection status of channel input source
+ */
+struct ina3221_input {
+ const char *label;
+ int shunt_resistor;
+ bool disconnected;
+};
+
+/**
+ * struct ina3221_data - device specific information
+ * @pm_dev: Device pointer for pm runtime
+ * @regmap: Register map of the device
+ * @fields: Register fields of the device
+ * @inputs: Array of channel input source specific structures
+ * @lock: mutex lock to serialize sysfs attribute accesses
+ * @reg_config: Register value of INA3221_CONFIG
+ * @summation_shunt_resistor: equivalent shunt resistor value for summation
+ * @single_shot: running in single-shot operating mode
+ */
+struct ina3221_data {
+ struct device *pm_dev;
+ struct regmap *regmap;
+ struct regmap_field *fields[F_MAX_FIELDS];
+ struct ina3221_input inputs[INA3221_NUM_CHANNELS];
+ struct mutex lock;
+ u32 reg_config;
+ int summation_shunt_resistor;
+
+ bool single_shot;
+};
+
+static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
+{
+ /* Summation channel checks shunt resistor values */
+ if (channel > INA3221_CHANNEL3)
+ return ina->summation_shunt_resistor != 0;
+
+ return pm_runtime_active(ina->pm_dev) &&
+ (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
+ * resistor values should be the same, so as to simply fit the formula:
+ * current summation = shunt voltage summation / shunt resistor
+ *
+ * Returns the equivalent shunt resistor value on success or 0 on failure
+ */
+static inline int ina3221_summation_shunt_resistor(struct ina3221_data *ina)
+{
+ struct ina3221_input *input = ina->inputs;
+ int i, shunt_resistor = 0;
+
+ for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
+ if (input[i].disconnected || !input[i].shunt_resistor)
+ continue;
+ if (!shunt_resistor) {
+ /* Found the reference shunt resistor value */
+ shunt_resistor = input[i].shunt_resistor;
+ } else {
+ /* No summation if resistor values are different */
+ if (shunt_resistor != input[i].shunt_resistor)
+ return 0;
+ }
+ }
+
+ return shunt_resistor;
+}
+
+/* Lookup table for Bus and Shunt conversion times in usec */
+static const u16 ina3221_conv_time[] = {
+ 140, 204, 332, 588, 1100, 2116, 4156, 8244,
+};
+
+/* Lookup table for number of samples using in averaging mode */
+static const int ina3221_avg_samples[] = {
+ 1, 4, 16, 64, 128, 256, 512, 1024,
+};
+
+/* Converting update_interval in msec to conversion time in usec */
+static inline u32 ina3221_interval_ms_to_conv_time(u16 config, int interval)
+{
+ u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK);
+ u32 samples_idx = INA3221_CONFIG_AVG(config);
+ u32 samples = ina3221_avg_samples[samples_idx];
+
+ /* Bisect the result to Bus and Shunt conversion times */
+ return DIV_ROUND_CLOSEST(interval * 1000 / 2, channels * samples);
+}
+
+/* Converting CONFIG register value to update_interval in usec */
+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 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);
+}
+
+static inline int ina3221_wait_for_data(struct ina3221_data *ina)
+{
+ u32 wait, cvrf;
+
+ wait = ina3221_reg_to_interval_us(ina->reg_config);
+
+ /* Polling the CVRF bit to make sure read data is ready */
+ return regmap_field_read_poll_timeout(ina->fields[F_CVRF],
+ cvrf, cvrf, wait, wait * 2);
+}
+
+static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
+ int *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(ina->regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ /*
+ * Shunt Voltage Sum register has 14-bit value with 1-bit shift
+ * Other Shunt Voltage registers have 12 bits with 3-bit shift
+ */
+ if (reg == INA3221_SHUNT_SUM || reg == INA3221_CRIT_SUM)
+ *val = sign_extend32(regval >> 1, 14);
+ else
+ *val = sign_extend32(regval >> 3, 12);
+
+ return 0;
+}
+
+static const u8 ina3221_in_reg[] = {
+ INA3221_BUS1,
+ INA3221_BUS2,
+ INA3221_BUS3,
+ INA3221_SHUNT1,
+ INA3221_SHUNT2,
+ INA3221_SHUNT3,
+ INA3221_SHUNT_SUM,
+};
+
+static int ina3221_read_chip(struct device *dev, u32 attr, long *val)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ int regval;
+
+ switch (attr) {
+ case hwmon_chip_samples:
+ regval = INA3221_CONFIG_AVG(ina->reg_config);
+ *val = ina3221_avg_samples[regval];
+ return 0;
+ case hwmon_chip_update_interval:
+ /* Return in msec */
+ *val = ina3221_reg_to_interval_us(ina->reg_config);
+ *val = DIV_ROUND_CLOSEST(*val, 1000);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
+{
+ const bool is_shunt = channel > INA3221_CHANNEL3;
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ u8 reg = ina3221_in_reg[channel];
+ int regval, ret;
+
+ /*
+ * Translate shunt channel index to sensor channel index except
+ * the 7th channel (6 since being 0-aligned) is for summation.
+ */
+ if (channel != 6)
+ channel %= INA3221_NUM_CHANNELS;
+
+ switch (attr) {
+ case hwmon_in_input:
+ if (!ina3221_is_enabled(ina, channel))
+ return -ENODATA;
+
+ /* Write CONFIG register to trigger a single-shot measurement */
+ 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_read_value(ina, reg, &regval);
+ if (ret)
+ return ret;
+
+ /*
+ * Scale of shunt voltage (uV): LSB is 40uV
+ * Scale of bus voltage (mV): LSB is 8mV
+ */
+ *val = regval * (is_shunt ? 40 : 8);
+ return 0;
+ case hwmon_in_enable:
+ *val = ina3221_is_enabled(ina, channel);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS + 1] = {
+ [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2,
+ INA3221_SHUNT3, INA3221_SHUNT_SUM },
+ [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3, 0 },
+ [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2,
+ INA3221_CRIT3, INA3221_CRIT_SUM },
+ [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3, 0 },
+ [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3, F_SF },
+};
+
+static int ina3221_read_curr(struct device *dev, u32 attr,
+ int channel, long *val)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ struct ina3221_input *input = ina->inputs;
+ u8 reg = ina3221_curr_reg[attr][channel];
+ int resistance_uo, voltage_nv;
+ int regval, ret;
+
+ if (channel > INA3221_CHANNEL3)
+ resistance_uo = ina->summation_shunt_resistor;
+ else
+ resistance_uo = input[channel].shunt_resistor;
+
+ switch (attr) {
+ case hwmon_curr_input:
+ if (!ina3221_is_enabled(ina, channel))
+ return -ENODATA;
+
+ /* Write CONFIG register to trigger a single-shot measurement */
+ if (ina->single_shot) {
+ regmap_write(ina->regmap, INA3221_CONFIG,
+ ina->reg_config);
+
+ ret = ina3221_wait_for_data(ina);
+ if (ret)
+ return ret;
+ }
+
+ fallthrough;
+ case hwmon_curr_crit:
+ case hwmon_curr_max:
+ if (!resistance_uo)
+ return -ENODATA;
+
+ ret = ina3221_read_value(ina, reg, &regval);
+ if (ret)
+ return ret;
+
+ /* Scale of shunt voltage: LSB is 40uV (40000nV) */
+ voltage_nv = regval * 40000;
+ /* Return current in mA */
+ *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
+ return 0;
+ case hwmon_curr_crit_alarm:
+ case hwmon_curr_max_alarm:
+ /* No actual register read if channel is disabled */
+ if (!ina3221_is_enabled(ina, channel)) {
+ /* Return 0 for alert flags */
+ *val = 0;
+ return 0;
+ }
+ ret = regmap_field_read(ina->fields[reg], &regval);
+ if (ret)
+ return ret;
+ *val = regval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina3221_write_chip(struct device *dev, u32 attr, long val)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ int ret, idx;
+ u32 tmp;
+
+ switch (attr) {
+ case hwmon_chip_samples:
+ idx = find_closest(val, ina3221_avg_samples,
+ ARRAY_SIZE(ina3221_avg_samples));
+
+ tmp = (ina->reg_config & ~INA3221_CONFIG_AVG_MASK) |
+ (idx << INA3221_CONFIG_AVG_SHIFT);
+ ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp);
+ if (ret)
+ return ret;
+
+ /* Update reg_config accordingly */
+ ina->reg_config = tmp;
+ return 0;
+ case hwmon_chip_update_interval:
+ tmp = ina3221_interval_ms_to_conv_time(ina->reg_config, val);
+ idx = find_closest(tmp, ina3221_conv_time,
+ ARRAY_SIZE(ina3221_conv_time));
+
+ /* Update Bus and Shunt voltage conversion times */
+ tmp = INA3221_CONFIG_VBUS_CT_MASK | INA3221_CONFIG_VSH_CT_MASK;
+ tmp = (ina->reg_config & ~tmp) |
+ (idx << INA3221_CONFIG_VBUS_CT_SHIFT) |
+ (idx << INA3221_CONFIG_VSH_CT_SHIFT);
+ ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp);
+ if (ret)
+ return ret;
+
+ /* Update reg_config accordingly */
+ ina->reg_config = tmp;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ina3221_write_curr(struct device *dev, u32 attr,
+ int channel, long val)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ struct ina3221_input *input = ina->inputs;
+ u8 reg = ina3221_curr_reg[attr][channel];
+ int resistance_uo, current_ma, voltage_uv;
+ int regval;
+
+ if (channel > INA3221_CHANNEL3)
+ resistance_uo = ina->summation_shunt_resistor;
+ else
+ resistance_uo = input[channel].shunt_resistor;
+
+ if (!resistance_uo)
+ return -EOPNOTSUPP;
+
+ /* clamp current */
+ current_ma = clamp_val(val,
+ INT_MIN / resistance_uo,
+ INT_MAX / resistance_uo);
+
+ voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000);
+
+ /* clamp voltage */
+ voltage_uv = clamp_val(voltage_uv, -163800, 163800);
+
+ /*
+ * Formula to convert voltage_uv to register value:
+ * regval = (voltage_uv / scale) << shift
+ * Note:
+ * The scale is 40uV for all shunt voltage registers
+ * Shunt Voltage Sum register left-shifts 1 bit
+ * All other Shunt Voltage registers shift 3 bits
+ * Results:
+ * SHUNT_SUM: (1 / 40uV) << 1 = 1 / 20uV
+ * SHUNT[1-3]: (1 / 40uV) << 3 = 1 / 5uV
+ */
+ if (reg == INA3221_SHUNT_SUM || reg == INA3221_CRIT_SUM)
+ regval = DIV_ROUND_CLOSEST(voltage_uv, 20) & 0xfffe;
+ else
+ regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
+
+ return regmap_write(ina->regmap, reg, regval);
+}
+
+static int ina3221_write_enable(struct device *dev, int channel, bool enable)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ u16 config, mask = INA3221_CONFIG_CHx_EN(channel);
+ u16 config_old = ina->reg_config & mask;
+ u32 tmp;
+ int ret;
+
+ config = enable ? mask : 0;
+
+ /* Bypass if enable status is not being changed */
+ if (config_old == config)
+ return 0;
+
+ /* For enabling routine, increase refcount and resume() at first */
+ if (enable) {
+ ret = pm_runtime_resume_and_get(ina->pm_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get PM runtime\n");
+ return ret;
+ }
+ }
+
+ /* Enable or disable the channel */
+ tmp = (ina->reg_config & ~mask) | (config & mask);
+ ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp);
+ if (ret)
+ goto fail;
+
+ /* Cache the latest config register value */
+ ina->reg_config = tmp;
+
+ /* For disabling routine, decrease refcount or suspend() at last */
+ if (!enable)
+ pm_runtime_put_sync(ina->pm_dev);
+
+ return 0;
+
+fail:
+ if (enable) {
+ dev_err(dev, "Failed to enable channel %d: error %d\n",
+ channel, ret);
+ pm_runtime_put_sync(ina->pm_dev);
+ }
+
+ return ret;
+}
+
+static int ina3221_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&ina->lock);
+
+ switch (type) {
+ case hwmon_chip:
+ ret = ina3221_read_chip(dev, attr, val);
+ break;
+ case hwmon_in:
+ /* 0-align channel ID */
+ ret = ina3221_read_in(dev, attr, channel - 1, val);
+ break;
+ case hwmon_curr:
+ ret = ina3221_read_curr(dev, attr, channel, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&ina->lock);
+
+ return ret;
+}
+
+static int ina3221_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&ina->lock);
+
+ switch (type) {
+ case hwmon_chip:
+ ret = ina3221_write_chip(dev, attr, val);
+ break;
+ case hwmon_in:
+ /* 0-align channel ID */
+ ret = ina3221_write_enable(dev, channel - 1, val);
+ break;
+ case hwmon_curr:
+ ret = ina3221_write_curr(dev, attr, channel, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&ina->lock);
+
+ return ret;
+}
+
+static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ int index = channel - 1;
+
+ if (channel == 7)
+ *str = "sum of shunt voltages";
+ else
+ *str = ina->inputs[index].label;
+
+ return 0;
+}
+
+static umode_t ina3221_is_visible(const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct ina3221_data *ina = drvdata;
+ const struct ina3221_input *input = NULL;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_samples:
+ case hwmon_chip_update_interval:
+ return 0644;
+ default:
+ return 0;
+ }
+ case hwmon_in:
+ /* Ignore in0_ */
+ if (channel == 0)
+ return 0;
+
+ switch (attr) {
+ case hwmon_in_label:
+ if (channel - 1 <= INA3221_CHANNEL3)
+ input = &ina->inputs[channel - 1];
+ else if (channel == 7)
+ return 0444;
+ /* Hide label node if label is not provided */
+ return (input && input->label) ? 0444 : 0;
+ case hwmon_in_input:
+ return 0444;
+ case hwmon_in_enable:
+ return 0644;
+ default:
+ return 0;
+ }
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_crit_alarm:
+ case hwmon_curr_max_alarm:
+ return 0444;
+ case hwmon_curr_crit:
+ case hwmon_curr_max:
+ return 0644;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+#define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \
+ HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \
+ HWMON_C_MAX | HWMON_C_MAX_ALARM)
+
+static const struct hwmon_channel_info *ina3221_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_SAMPLES,
+ HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(in,
+ /* 0: dummy, skipped in is_visible */
+ HWMON_I_INPUT,
+ /* 1-3: input voltage Channels */
+ 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,
+ /* 4-6: shunt voltage Channels */
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ /* 7: summation of shunt voltage channels */
+ HWMON_I_INPUT | HWMON_I_LABEL),
+ HWMON_CHANNEL_INFO(curr,
+ /* 1-3: current channels*/
+ INA3221_HWMON_CURR_CONFIG,
+ INA3221_HWMON_CURR_CONFIG,
+ INA3221_HWMON_CURR_CONFIG,
+ /* 4: summation of current channels */
+ HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM),
+ NULL
+};
+
+static const struct hwmon_ops ina3221_hwmon_ops = {
+ .is_visible = ina3221_is_visible,
+ .read_string = ina3221_read_string,
+ .read = ina3221_read,
+ .write = ina3221_write,
+};
+
+static const struct hwmon_chip_info ina3221_chip_info = {
+ .ops = &ina3221_hwmon_ops,
+ .info = ina3221_info,
+};
+
+/* Extra attribute groups */
+static ssize_t ina3221_shunt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ unsigned int channel = sd_attr->index;
+ struct ina3221_input *input = &ina->inputs[channel];
+
+ return sysfs_emit(buf, "%d\n", input->shunt_resistor);
+}
+
+static ssize_t ina3221_shunt_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ unsigned int channel = sd_attr->index;
+ struct ina3221_input *input = &ina->inputs[channel];
+ int val;
+ int ret;
+
+ ret = kstrtoint(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ val = clamp_val(val, 1, INT_MAX);
+
+ input->shunt_resistor = val;
+
+ /* Update summation_shunt_resistor for summation channel */
+ ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina);
+
+ return count;
+}
+
+/* shunt resistance */
+static SENSOR_DEVICE_ATTR_RW(shunt1_resistor, ina3221_shunt, INA3221_CHANNEL1);
+static SENSOR_DEVICE_ATTR_RW(shunt2_resistor, ina3221_shunt, INA3221_CHANNEL2);
+static SENSOR_DEVICE_ATTR_RW(shunt3_resistor, ina3221_shunt, INA3221_CHANNEL3);
+
+static struct attribute *ina3221_attrs[] = {
+ &sensor_dev_attr_shunt1_resistor.dev_attr.attr,
+ &sensor_dev_attr_shunt2_resistor.dev_attr.attr,
+ &sensor_dev_attr_shunt3_resistor.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ina3221);
+
+static const struct regmap_range ina3221_yes_ranges[] = {
+ regmap_reg_range(INA3221_CONFIG, INA3221_BUS3),
+ regmap_reg_range(INA3221_SHUNT_SUM, INA3221_SHUNT_SUM),
+ regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
+};
+
+static const struct regmap_access_table ina3221_volatile_table = {
+ .yes_ranges = ina3221_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges),
+};
+
+static const struct regmap_config ina3221_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_table = &ina3221_volatile_table,
+};
+
+static int ina3221_probe_child_from_dt(struct device *dev,
+ struct device_node *child,
+ struct ina3221_data *ina)
+{
+ struct ina3221_input *input;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32(child, "reg", &val);
+ if (ret) {
+ dev_err(dev, "missing reg property of %pOFn\n", child);
+ return ret;
+ } else if (val > INA3221_CHANNEL3) {
+ dev_err(dev, "invalid reg %d of %pOFn\n", val, child);
+ return -EINVAL;
+ }
+
+ input = &ina->inputs[val];
+
+ /* Log the disconnected channel input */
+ if (!of_device_is_available(child)) {
+ input->disconnected = true;
+ return 0;
+ }
+
+ /* Save the connected input label if available */
+ of_property_read_string(child, "label", &input->label);
+
+ /* Overwrite default shunt resistor value optionally */
+ if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) {
+ if (val < 1 || val > INT_MAX) {
+ dev_err(dev, "invalid shunt resistor value %u of %pOFn\n",
+ val, child);
+ return -EINVAL;
+ }
+ input->shunt_resistor = val;
+ }
+
+ return 0;
+}
+
+static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina)
+{
+ const struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int ret;
+
+ /* Compatible with non-DT platforms */
+ if (!np)
+ return 0;
+
+ ina->single_shot = of_property_read_bool(np, "ti,single-shot");
+
+ for_each_child_of_node(np, child) {
+ ret = ina3221_probe_child_from_dt(dev, child, ina);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ina3221_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct ina3221_data *ina;
+ struct device *hwmon_dev;
+ int i, ret;
+
+ ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
+ if (!ina)
+ return -ENOMEM;
+
+ ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config);
+ if (IS_ERR(ina->regmap)) {
+ dev_err(dev, "Unable to allocate register map\n");
+ return PTR_ERR(ina->regmap);
+ }
+
+ for (i = 0; i < F_MAX_FIELDS; i++) {
+ ina->fields[i] = devm_regmap_field_alloc(dev,
+ ina->regmap,
+ ina3221_reg_fields[i]);
+ if (IS_ERR(ina->fields[i])) {
+ dev_err(dev, "Unable to allocate regmap fields\n");
+ return PTR_ERR(ina->fields[i]);
+ }
+ }
+
+ for (i = 0; i < INA3221_NUM_CHANNELS; i++)
+ ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT;
+
+ ret = ina3221_probe_from_dt(dev, ina);
+ if (ret) {
+ dev_err(dev, "Unable to probe from device tree\n");
+ return ret;
+ }
+
+ /* The driver will be reset, so use reset value */
+ ina->reg_config = INA3221_CONFIG_DEFAULT;
+
+ /* Clear continuous bit to use single-shot mode */
+ if (ina->single_shot)
+ ina->reg_config &= ~INA3221_CONFIG_MODE_CONTINUOUS;
+
+ /* Disable channels if their inputs are disconnected */
+ for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
+ if (ina->inputs[i].disconnected)
+ ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i);
+ }
+
+ /* Initialize summation_shunt_resistor for summation channel control */
+ ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina);
+
+ ina->pm_dev = dev;
+ mutex_init(&ina->lock);
+ dev_set_drvdata(dev, ina);
+
+ /* Enable PM runtime -- status is suspended by default */
+ pm_runtime_enable(ina->pm_dev);
+
+ /* Initialize (resume) the device */
+ for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
+ if (ina->inputs[i].disconnected)
+ continue;
+ /* Match the refcount with number of enabled channels */
+ ret = pm_runtime_get_sync(ina->pm_dev);
+ if (ret < 0)
+ goto fail;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina,
+ &ina3221_chip_info,
+ ina3221_groups);
+ if (IS_ERR(hwmon_dev)) {
+ dev_err(dev, "Unable to register hwmon device\n");
+ ret = PTR_ERR(hwmon_dev);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ pm_runtime_disable(ina->pm_dev);
+ pm_runtime_set_suspended(ina->pm_dev);
+ /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
+ for (i = 0; i < INA3221_NUM_CHANNELS; i++)
+ pm_runtime_put_noidle(ina->pm_dev);
+ mutex_destroy(&ina->lock);
+
+ return ret;
+}
+
+static void ina3221_remove(struct i2c_client *client)
+{
+ struct ina3221_data *ina = dev_get_drvdata(&client->dev);
+ int i;
+
+ pm_runtime_disable(ina->pm_dev);
+ pm_runtime_set_suspended(ina->pm_dev);
+
+ /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
+ for (i = 0; i < INA3221_NUM_CHANNELS; i++)
+ pm_runtime_put_noidle(ina->pm_dev);
+
+ mutex_destroy(&ina->lock);
+}
+
+static int ina3221_suspend(struct device *dev)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ int ret;
+
+ /* Save config register value and enable cache-only */
+ ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
+ if (ret)
+ return ret;
+
+ /* Set to power-down mode for power saving */
+ ret = regmap_update_bits(ina->regmap, INA3221_CONFIG,
+ INA3221_CONFIG_MODE_MASK,
+ INA3221_CONFIG_MODE_POWERDOWN);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(ina->regmap, true);
+ regcache_mark_dirty(ina->regmap);
+
+ return 0;
+}
+
+static int ina3221_resume(struct device *dev)
+{
+ struct ina3221_data *ina = dev_get_drvdata(dev);
+ int ret;
+
+ regcache_cache_only(ina->regmap, false);
+
+ /* Software reset the chip */
+ ret = regmap_field_write(ina->fields[F_RST], true);
+ if (ret) {
+ dev_err(dev, "Unable to reset device\n");
+ return ret;
+ }
+
+ /* Restore cached register values to hardware */
+ ret = regcache_sync(ina->regmap);
+ if (ret)
+ return ret;
+
+ /* Restore config register value to hardware */
+ ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config);
+ if (ret)
+ return ret;
+
+ /* Initialize summation channel control */
+ if (ina->summation_shunt_resistor) {
+ /*
+ * Take all three channels into summation by default
+ * Shunt measurements of disconnected channels should
+ * be 0, so it does not matter for summation.
+ */
+ ret = regmap_update_bits(ina->regmap, INA3221_MASK_ENABLE,
+ INA3221_MASK_ENABLE_SCC_MASK,
+ INA3221_MASK_ENABLE_SCC_MASK);
+ if (ret) {
+ dev_err(dev, "Unable to control summation channel\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+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", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ina3221_of_match_table);
+
+static const struct i2c_device_id ina3221_ids[] = {
+ { "ina3221", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, ina3221_ids);
+
+static struct i2c_driver ina3221_i2c_driver = {
+ .probe_new = ina3221_probe,
+ .remove = ina3221_remove,
+ .driver = {
+ .name = INA3221_DRIVER_NAME,
+ .of_match_table = ina3221_of_match_table,
+ .pm = pm_ptr(&ina3221_pm),
+ },
+ .id_table = ina3221_ids,
+};
+module_i2c_driver(ina3221_i2c_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c
new file mode 100644
index 000000000..6e82f7200
--- /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, &regval);
+ 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
new file mode 100644
index 000000000..b45bd3aa5
--- /dev/null
+++ b/drivers/hwmon/it87.c
@@ -0,0 +1,3371 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * it87.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring.
+ *
+ * The IT8705F is an LPC-based Super I/O part that contains UARTs, a
+ * parallel port, an IR port, a MIDI port, a floppy controller, etc., in
+ * addition to an Environment Controller (Enhanced Hardware Monitor and
+ * Fan Controller)
+ *
+ * This driver supports only the Environment Controller in the IT8705F and
+ * similar parts. The other devices are supported by different drivers.
+ *
+ * Supports: IT8603E Super I/O chip w/LPC interface
+ * IT8620E Super I/O chip w/LPC interface
+ * IT8622E Super I/O chip w/LPC interface
+ * IT8623E Super I/O chip w/LPC interface
+ * IT8628E Super I/O chip w/LPC interface
+ * IT8705F Super I/O chip w/LPC interface
+ * IT8712F Super I/O chip w/LPC interface
+ * IT8716F Super I/O chip w/LPC interface
+ * IT8718F Super I/O chip w/LPC interface
+ * IT8720F Super I/O chip w/LPC interface
+ * IT8721F Super I/O chip w/LPC interface
+ * IT8726F Super I/O chip w/LPC interface
+ * IT8728F Super I/O chip w/LPC interface
+ * IT8732F Super I/O chip w/LPC interface
+ * IT8758E Super I/O chip w/LPC interface
+ * IT8771E Super I/O chip w/LPC interface
+ * IT8772E Super I/O chip w/LPC interface
+ * IT8781F Super I/O chip w/LPC interface
+ * IT8782F Super I/O chip w/LPC interface
+ * IT8783E/F Super I/O chip w/LPC interface
+ * IT8786E Super I/O chip w/LPC interface
+ * IT8790E Super I/O chip w/LPC interface
+ * IT8792E Super I/O chip w/LPC interface
+ * Sis950 A clone of the IT8705F
+ *
+ * Copyright (C) 2001 Chris Gauthron
+ * Copyright (C) 2005-2010 Jean Delvare <jdelvare@suse.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/module.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/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+#define DRVNAME "it87"
+
+enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732,
+ it8771, it8772, it8781, it8782, it8783, it8786, it8790,
+ it8792, it8603, it8620, it8622, it8628 };
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static struct platform_device *it87_pdev[2];
+
+#define REG_2E 0x2e /* The register to read/write */
+#define REG_4E 0x4e /* Secondary register to read/write */
+
+#define DEV 0x07 /* Register: Logical device select */
+#define PME 0x04 /* The device with the fan registers in it */
+
+/* The device with the IT8718F/IT8720F VID value in it */
+#define GPIO 0x07
+
+#define DEVID 0x20 /* Register: Device ID */
+#define DEVREV 0x22 /* Register: Device Revision */
+
+static inline int superio_inb(int ioreg, int reg)
+{
+ outb(reg, ioreg);
+ return inb(ioreg + 1);
+}
+
+static inline void superio_outb(int ioreg, int reg, int val)
+{
+ outb(reg, ioreg);
+ outb(val, ioreg + 1);
+}
+
+static int superio_inw(int ioreg, int reg)
+{
+ int val;
+
+ outb(reg++, ioreg);
+ val = inb(ioreg + 1) << 8;
+ outb(reg, ioreg);
+ val |= inb(ioreg + 1);
+ return val;
+}
+
+static inline void superio_select(int ioreg, int ldn)
+{
+ outb(DEV, ioreg);
+ outb(ldn, 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(0x01, ioreg);
+ outb(0x55, ioreg);
+ outb(ioreg == REG_4E ? 0xaa : 0x55, ioreg);
+ return 0;
+}
+
+static inline void superio_exit(int ioreg)
+{
+ outb(0x02, ioreg);
+ outb(0x02, ioreg + 1);
+ release_region(ioreg, 2);
+}
+
+/* Logical device 4 registers */
+#define IT8712F_DEVID 0x8712
+#define IT8705F_DEVID 0x8705
+#define IT8716F_DEVID 0x8716
+#define IT8718F_DEVID 0x8718
+#define IT8720F_DEVID 0x8720
+#define IT8721F_DEVID 0x8721
+#define IT8726F_DEVID 0x8726
+#define IT8728F_DEVID 0x8728
+#define IT8732F_DEVID 0x8732
+#define IT8792E_DEVID 0x8733
+#define IT8771E_DEVID 0x8771
+#define IT8772E_DEVID 0x8772
+#define IT8781F_DEVID 0x8781
+#define IT8782F_DEVID 0x8782
+#define IT8783E_DEVID 0x8783
+#define IT8786E_DEVID 0x8786
+#define IT8790E_DEVID 0x8790
+#define IT8603E_DEVID 0x8603
+#define IT8620E_DEVID 0x8620
+#define IT8622E_DEVID 0x8622
+#define IT8623E_DEVID 0x8623
+#define IT8628E_DEVID 0x8628
+#define IT87_ACT_REG 0x30
+#define IT87_BASE_REG 0x60
+
+/* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_GPIO1_REG 0x25
+#define IT87_SIO_GPIO2_REG 0x26
+#define IT87_SIO_GPIO3_REG 0x27
+#define IT87_SIO_GPIO4_REG 0x28
+#define IT87_SIO_GPIO5_REG 0x29
+#define IT87_SIO_PINX1_REG 0x2a /* Pin selection */
+#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */
+#define IT87_SIO_SPI_REG 0xef /* SPI function pin select */
+#define IT87_SIO_VID_REG 0xfc /* VID value */
+#define IT87_SIO_BEEP_PIN_REG 0xf6 /* Beep pin mapping */
+
+/* Update battery voltage after every reading if true */
+static bool update_vbat;
+
+/* Not all BIOSes properly configure the PWM registers */
+static bool fix_pwm_polarity;
+
+/* Many IT87 constants specified below */
+
+/* Length of ISA address segment */
+#define IT87_EXTENT 8
+
+/* Length of ISA address segment for Environmental Controller */
+#define IT87_EC_EXTENT 2
+
+/* Offset of EC registers from ISA base address */
+#define IT87_EC_OFFSET 5
+
+/* Where are the ISA address/data registers relative to the EC base address */
+#define IT87_ADDR_REG_OFFSET 0
+#define IT87_DATA_REG_OFFSET 1
+
+/*----- The IT87 registers -----*/
+
+#define IT87_REG_CONFIG 0x00
+
+#define IT87_REG_ALARM1 0x01
+#define IT87_REG_ALARM2 0x02
+#define IT87_REG_ALARM3 0x03
+
+/*
+ * The IT8718F and IT8720F have the VID value in a different register, in
+ * Super-I/O configuration space.
+ */
+#define IT87_REG_VID 0x0a
+/*
+ * The IT8705F and IT8712F earlier than revision 0x08 use register 0x0b
+ * for fan divisors. Later IT8712F revisions must use 16-bit tachometer
+ * mode.
+ */
+#define IT87_REG_FAN_DIV 0x0b
+#define IT87_REG_FAN_16BIT 0x0c
+
+/*
+ * Monitors:
+ * - up to 13 voltage (0 to 7, battery, avcc, 10 to 12)
+ * - up to 6 temp (1 to 6)
+ * - up to 6 fan (1 to 6)
+ */
+
+static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82, 0x4c };
+static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86, 0x4e };
+static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83, 0x4d };
+static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87, 0x4f };
+static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 };
+
+#define IT87_REG_FAN_MAIN_CTRL 0x13
+#define IT87_REG_FAN_CTL 0x14
+static const u8 IT87_REG_PWM[] = { 0x15, 0x16, 0x17, 0x7f, 0xa7, 0xaf };
+static const u8 IT87_REG_PWM_DUTY[] = { 0x63, 0x6b, 0x73, 0x7b, 0xa3, 0xab };
+
+static const u8 IT87_REG_VIN[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+ 0x27, 0x28, 0x2f, 0x2c, 0x2d, 0x2e };
+
+#define IT87_REG_TEMP(nr) (0x29 + (nr))
+
+#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2)
+#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2)
+#define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2)
+#define IT87_REG_TEMP_LOW(nr) (0x41 + (nr) * 2)
+
+#define IT87_REG_VIN_ENABLE 0x50
+#define IT87_REG_TEMP_ENABLE 0x51
+#define IT87_REG_TEMP_EXTRA 0x55
+#define IT87_REG_BEEP_ENABLE 0x5c
+
+#define IT87_REG_CHIPID 0x58
+
+static const u8 IT87_REG_AUTO_BASE[] = { 0x60, 0x68, 0x70, 0x78, 0xa0, 0xa8 };
+
+#define IT87_REG_AUTO_TEMP(nr, i) (IT87_REG_AUTO_BASE[nr] + (i))
+#define IT87_REG_AUTO_PWM(nr, i) (IT87_REG_AUTO_BASE[nr] + 5 + (i))
+
+#define IT87_REG_TEMP456_ENABLE 0x77
+
+#define NUM_VIN ARRAY_SIZE(IT87_REG_VIN)
+#define NUM_VIN_LIMIT 8
+#define NUM_TEMP 6
+#define NUM_TEMP_OFFSET ARRAY_SIZE(IT87_REG_TEMP_OFFSET)
+#define NUM_TEMP_LIMIT 3
+#define NUM_FAN ARRAY_SIZE(IT87_REG_FAN)
+#define NUM_FAN_DIV 3
+#define NUM_PWM ARRAY_SIZE(IT87_REG_PWM)
+#define NUM_AUTO_PWM ARRAY_SIZE(IT87_REG_PWM)
+
+struct it87_devices {
+ const char *name;
+ const char * const suffix;
+ u32 features;
+ u8 peci_mask;
+ u8 old_peci_mask;
+};
+
+#define FEAT_12MV_ADC BIT(0)
+#define FEAT_NEWER_AUTOPWM BIT(1)
+#define FEAT_OLD_AUTOPWM BIT(2)
+#define FEAT_16BIT_FANS BIT(3)
+#define FEAT_TEMP_OFFSET BIT(4)
+#define FEAT_TEMP_PECI BIT(5)
+#define FEAT_TEMP_OLD_PECI BIT(6)
+#define FEAT_FAN16_CONFIG BIT(7) /* Need to enable 16-bit fans */
+#define FEAT_FIVE_FANS BIT(8) /* Supports five fans */
+#define FEAT_VID BIT(9) /* Set if chip supports VID */
+#define FEAT_IN7_INTERNAL BIT(10) /* Set if in7 is internal */
+#define FEAT_SIX_FANS BIT(11) /* Supports six fans */
+#define FEAT_10_9MV_ADC BIT(12)
+#define FEAT_AVCC3 BIT(13) /* Chip supports in9/AVCC3 */
+#define FEAT_FIVE_PWM BIT(14) /* Chip supports 5 pwm chn */
+#define FEAT_SIX_PWM BIT(15) /* Chip supports 6 pwm chn */
+#define FEAT_PWM_FREQ2 BIT(16) /* Separate pwm freq 2 */
+#define FEAT_SIX_TEMP BIT(17) /* Up to 6 temp sensors */
+#define FEAT_VIN3_5V BIT(18) /* VIN3 connected to +5V */
+
+static const struct it87_devices it87_devices[] = {
+ [it87] = {
+ .name = "it87",
+ .suffix = "F",
+ .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */
+ },
+ [it8712] = {
+ .name = "it8712",
+ .suffix = "F",
+ .features = FEAT_OLD_AUTOPWM | FEAT_VID,
+ /* may need to overwrite */
+ },
+ [it8716] = {
+ .name = "it8716",
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
+ | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_PWM_FREQ2,
+ },
+ [it8718] = {
+ .name = "it8718",
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS
+ | FEAT_PWM_FREQ2,
+ .old_peci_mask = 0x4,
+ },
+ [it8720] = {
+ .name = "it8720",
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS
+ | FEAT_PWM_FREQ2,
+ .old_peci_mask = 0x4,
+ },
+ [it8721] = {
+ .name = "it8721",
+ .suffix = "F",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+ | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL
+ | FEAT_PWM_FREQ2,
+ .peci_mask = 0x05,
+ .old_peci_mask = 0x02, /* Actually reports PCH */
+ },
+ [it8728] = {
+ .name = "it8728",
+ .suffix = "F",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
+ | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2,
+ .peci_mask = 0x07,
+ },
+ [it8732] = {
+ .name = "it8732",
+ .suffix = "F",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+ | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL,
+ .peci_mask = 0x07,
+ .old_peci_mask = 0x02, /* Actually reports PCH */
+ },
+ [it8771] = {
+ .name = "it8771",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
+ | FEAT_PWM_FREQ2,
+ /* PECI: guesswork */
+ /* 12mV ADC (OHM) */
+ /* 16 bit fans (OHM) */
+ /* three fans, always 16 bit (guesswork) */
+ .peci_mask = 0x07,
+ },
+ [it8772] = {
+ .name = "it8772",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
+ | FEAT_PWM_FREQ2,
+ /* PECI (coreboot) */
+ /* 12mV ADC (HWSensors4, OHM) */
+ /* 16 bit fans (HWSensors4, OHM) */
+ /* three fans, always 16 bit (datasheet) */
+ .peci_mask = 0x07,
+ },
+ [it8781] = {
+ .name = "it8781",
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2,
+ .old_peci_mask = 0x4,
+ },
+ [it8782] = {
+ .name = "it8782",
+ .suffix = "F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2,
+ .old_peci_mask = 0x4,
+ },
+ [it8783] = {
+ .name = "it8783",
+ .suffix = "E/F",
+ .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+ | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2,
+ .old_peci_mask = 0x4,
+ },
+ [it8786] = {
+ .name = "it8786",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
+ | FEAT_PWM_FREQ2,
+ .peci_mask = 0x07,
+ },
+ [it8790] = {
+ .name = "it8790",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
+ | FEAT_PWM_FREQ2,
+ .peci_mask = 0x07,
+ },
+ [it8792] = {
+ .name = "it8792",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+ | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL,
+ .peci_mask = 0x07,
+ .old_peci_mask = 0x02, /* Actually reports PCH */
+ },
+ [it8603] = {
+ .name = "it8603",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
+ | FEAT_AVCC3 | FEAT_PWM_FREQ2,
+ .peci_mask = 0x07,
+ },
+ [it8620] = {
+ .name = "it8620",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
+ | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
+ | FEAT_SIX_TEMP | FEAT_VIN3_5V,
+ .peci_mask = 0x07,
+ },
+ [it8622] = {
+ .name = "it8622",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
+ | FEAT_FIVE_PWM | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2
+ | FEAT_AVCC3 | FEAT_VIN3_5V,
+ .peci_mask = 0x07,
+ },
+ [it8628] = {
+ .name = "it8628",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
+ | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
+ | FEAT_SIX_TEMP | FEAT_VIN3_5V,
+ .peci_mask = 0x07,
+ },
+};
+
+#define has_16bit_fans(data) ((data)->features & FEAT_16BIT_FANS)
+#define has_12mv_adc(data) ((data)->features & FEAT_12MV_ADC)
+#define has_10_9mv_adc(data) ((data)->features & FEAT_10_9MV_ADC)
+#define has_newer_autopwm(data) ((data)->features & FEAT_NEWER_AUTOPWM)
+#define has_old_autopwm(data) ((data)->features & FEAT_OLD_AUTOPWM)
+#define has_temp_offset(data) ((data)->features & FEAT_TEMP_OFFSET)
+#define has_temp_peci(data, nr) (((data)->features & FEAT_TEMP_PECI) && \
+ ((data)->peci_mask & BIT(nr)))
+#define has_temp_old_peci(data, nr) \
+ (((data)->features & FEAT_TEMP_OLD_PECI) && \
+ ((data)->old_peci_mask & BIT(nr)))
+#define has_fan16_config(data) ((data)->features & FEAT_FAN16_CONFIG)
+#define has_five_fans(data) ((data)->features & (FEAT_FIVE_FANS | \
+ FEAT_SIX_FANS))
+#define has_vid(data) ((data)->features & FEAT_VID)
+#define has_in7_internal(data) ((data)->features & FEAT_IN7_INTERNAL)
+#define has_six_fans(data) ((data)->features & FEAT_SIX_FANS)
+#define has_avcc3(data) ((data)->features & FEAT_AVCC3)
+#define has_five_pwm(data) ((data)->features & (FEAT_FIVE_PWM \
+ | FEAT_SIX_PWM))
+#define has_six_pwm(data) ((data)->features & FEAT_SIX_PWM)
+#define has_pwm_freq2(data) ((data)->features & FEAT_PWM_FREQ2)
+#define has_six_temp(data) ((data)->features & FEAT_SIX_TEMP)
+#define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V)
+#define has_scaling(data) ((data)->features & (FEAT_12MV_ADC | \
+ FEAT_10_9MV_ADC))
+
+struct it87_sio_data {
+ int sioaddr;
+ enum chips type;
+ /* Values read from Super-I/O config space */
+ u8 revision;
+ u8 vid_value;
+ u8 beep_pin;
+ u8 internal; /* Internal sensors can be labeled */
+ bool need_in7_reroute;
+ /* Features skipped based on config or DMI */
+ u16 skip_in;
+ u8 skip_vid;
+ u8 skip_fan;
+ u8 skip_pwm;
+ u8 skip_temp;
+};
+
+/*
+ * For each registered chip, we need to keep some data in memory.
+ * The structure is dynamically allocated.
+ */
+struct it87_data {
+ const struct attribute_group *groups[7];
+ int sioaddr;
+ enum chips type;
+ u32 features;
+ u8 peci_mask;
+ u8 old_peci_mask;
+
+ unsigned short addr;
+ const char *name;
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u16 in_scaled; /* Internal voltage sensors are scaled */
+ u16 in_internal; /* Bitfield, internal sensors (for labels) */
+ u16 has_in; /* Bitfield, voltage sensors enabled */
+ u8 in[NUM_VIN][3]; /* [nr][0]=in, [1]=min, [2]=max */
+ bool need_in7_reroute;
+ u8 has_fan; /* Bitfield, fans enabled */
+ u16 fan[NUM_FAN][2]; /* Register values, [nr][0]=fan, [1]=min */
+ u8 has_temp; /* Bitfield, temp sensors enabled */
+ s8 temp[NUM_TEMP][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */
+ u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */
+ u8 extra; /* Register value (IT87_REG_TEMP_EXTRA) */
+ u8 fan_div[NUM_FAN_DIV];/* Register encoding, shifted right */
+ bool has_vid; /* True if VID supported */
+ u8 vid; /* Register encoding, combined */
+ u8 vrm;
+ u32 alarms; /* Register encoding, combined */
+ bool has_beep; /* true if beep supported */
+ u8 beeps; /* Register encoding */
+ u8 fan_main_ctrl; /* Register value */
+ u8 fan_ctl; /* Register value */
+
+ /*
+ * The following 3 arrays correspond to the same registers up to
+ * the IT8720F. The meaning of bits 6-0 depends on the value of bit
+ * 7, and we want to preserve settings on mode changes, so we have
+ * to track all values separately.
+ * Starting with the IT8721F, the manual PWM duty cycles are stored
+ * in separate registers (8-bit values), so the separate tracking
+ * is no longer needed, but it is still done to keep the driver
+ * simple.
+ */
+ u8 has_pwm; /* Bitfield, pwm control enabled */
+ u8 pwm_ctrl[NUM_PWM]; /* Register value */
+ u8 pwm_duty[NUM_PWM]; /* Manual PWM value set by user */
+ u8 pwm_temp_map[NUM_PWM];/* PWM to temp. chan. mapping (bits 1-0) */
+
+ /* Automatic fan speed control registers */
+ u8 auto_pwm[NUM_AUTO_PWM][4]; /* [nr][3] is hard-coded */
+ s8 auto_temp[NUM_AUTO_PWM][5]; /* [nr][0] is point1_temp_hyst */
+};
+
+static int adc_lsb(const struct it87_data *data, int nr)
+{
+ int lsb;
+
+ if (has_12mv_adc(data))
+ lsb = 120;
+ else if (has_10_9mv_adc(data))
+ lsb = 109;
+ else
+ lsb = 160;
+ if (data->in_scaled & BIT(nr))
+ lsb <<= 1;
+ return lsb;
+}
+
+static u8 in_to_reg(const struct it87_data *data, int nr, long val)
+{
+ val = DIV_ROUND_CLOSEST(val * 10, adc_lsb(data, nr));
+ return clamp_val(val, 0, 255);
+}
+
+static int in_from_reg(const struct it87_data *data, int nr, int val)
+{
+ return DIV_ROUND_CLOSEST(val * adc_lsb(data, nr), 10);
+}
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+static inline u16 FAN16_TO_REG(long rpm)
+{
+ if (rpm == 0)
+ return 0xffff;
+ return clamp_val((1350000 + rpm) / (rpm * 2), 1, 0xfffe);
+}
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 255 ? 0 : \
+ 1350000 / ((val) * (div)))
+/* The divider is fixed to 2 in 16-bit mode */
+#define FAN16_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \
+ 1350000 / ((val) * 2))
+
+#define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (((val) - 500) / 1000) : \
+ ((val) + 500) / 1000), -128, 127))
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+static u8 pwm_to_reg(const struct it87_data *data, long val)
+{
+ if (has_newer_autopwm(data))
+ return val;
+ else
+ return val >> 1;
+}
+
+static int pwm_from_reg(const struct it87_data *data, u8 reg)
+{
+ if (has_newer_autopwm(data))
+ return reg;
+ else
+ return (reg & 0x7f) << 1;
+}
+
+static int DIV_TO_REG(int val)
+{
+ int answer = 0;
+
+ while (answer < 7 && (val >>= 1))
+ answer++;
+ return answer;
+}
+
+#define DIV_FROM_REG(val) BIT(val)
+
+/*
+ * PWM base frequencies. The frequency has to be divided by either 128 or 256,
+ * depending on the chip type, to calculate the actual PWM frequency.
+ *
+ * Some of the chip datasheets suggest a base frequency of 51 kHz instead
+ * of 750 kHz for the slowest base frequency, resulting in a PWM frequency
+ * of 200 Hz. Sometimes both PWM frequency select registers are affected,
+ * sometimes just one. It is unknown if this is a datasheet error or real,
+ * so this is ignored for now.
+ */
+static const unsigned int pwm_freq[8] = {
+ 48000000,
+ 24000000,
+ 12000000,
+ 8000000,
+ 6000000,
+ 3000000,
+ 1500000,
+ 750000,
+};
+
+/*
+ * Must be called with data->update_lock held, except during initialization.
+ * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
+ * would slow down the IT87 access and should not be necessary.
+ */
+static int it87_read_value(struct it87_data *data, u8 reg)
+{
+ outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET);
+ return inb_p(data->addr + IT87_DATA_REG_OFFSET);
+}
+
+/*
+ * Must be called with data->update_lock held, except during initialization.
+ * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
+ * would slow down the IT87 access and should not be necessary.
+ */
+static void it87_write_value(struct it87_data *data, u8 reg, u8 value)
+{
+ outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET);
+ outb_p(value, data->addr + IT87_DATA_REG_OFFSET);
+}
+
+static void it87_update_pwm_ctrl(struct it87_data *data, int nr)
+{
+ data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM[nr]);
+ if (has_newer_autopwm(data)) {
+ data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03;
+ data->pwm_duty[nr] = it87_read_value(data,
+ IT87_REG_PWM_DUTY[nr]);
+ } else {
+ if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */
+ data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03;
+ else /* Manual mode */
+ data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f;
+ }
+
+ if (has_old_autopwm(data)) {
+ int i;
+
+ for (i = 0; i < 5 ; i++)
+ data->auto_temp[nr][i] = it87_read_value(data,
+ IT87_REG_AUTO_TEMP(nr, i));
+ for (i = 0; i < 3 ; i++)
+ data->auto_pwm[nr][i] = it87_read_value(data,
+ IT87_REG_AUTO_PWM(nr, i));
+ } else if (has_newer_autopwm(data)) {
+ int i;
+
+ /*
+ * 0: temperature hysteresis (base + 5)
+ * 1: fan off temperature (base + 0)
+ * 2: fan start temperature (base + 1)
+ * 3: fan max temperature (base + 2)
+ */
+ data->auto_temp[nr][0] =
+ it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 5));
+
+ for (i = 0; i < 3 ; i++)
+ data->auto_temp[nr][i + 1] =
+ it87_read_value(data,
+ IT87_REG_AUTO_TEMP(nr, i));
+ /*
+ * 0: start pwm value (base + 3)
+ * 1: pwm slope (base + 4, 1/8th pwm)
+ */
+ data->auto_pwm[nr][0] =
+ it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 3));
+ data->auto_pwm[nr][1] =
+ it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 4));
+ }
+}
+
+static struct it87_data *it87_update_device(struct device *dev)
+{
+ struct it87_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) {
+ if (update_vbat) {
+ /*
+ * Cleared after each update, so reenable. Value
+ * returned by this read will be previous value
+ */
+ it87_write_value(data, IT87_REG_CONFIG,
+ it87_read_value(data, IT87_REG_CONFIG) | 0x40);
+ }
+ for (i = 0; i < NUM_VIN; i++) {
+ if (!(data->has_in & BIT(i)))
+ continue;
+
+ data->in[i][0] =
+ it87_read_value(data, IT87_REG_VIN[i]);
+
+ /* VBAT and AVCC don't have limit registers */
+ if (i >= NUM_VIN_LIMIT)
+ continue;
+
+ data->in[i][1] =
+ it87_read_value(data, IT87_REG_VIN_MIN(i));
+ data->in[i][2] =
+ it87_read_value(data, IT87_REG_VIN_MAX(i));
+ }
+
+ for (i = 0; i < NUM_FAN; i++) {
+ /* Skip disabled fans */
+ if (!(data->has_fan & BIT(i)))
+ continue;
+
+ data->fan[i][1] =
+ it87_read_value(data, IT87_REG_FAN_MIN[i]);
+ data->fan[i][0] = it87_read_value(data,
+ IT87_REG_FAN[i]);
+ /* Add high byte if in 16-bit mode */
+ if (has_16bit_fans(data)) {
+ data->fan[i][0] |= it87_read_value(data,
+ IT87_REG_FANX[i]) << 8;
+ data->fan[i][1] |= it87_read_value(data,
+ IT87_REG_FANX_MIN[i]) << 8;
+ }
+ }
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->has_temp & BIT(i)))
+ continue;
+ data->temp[i][0] =
+ it87_read_value(data, IT87_REG_TEMP(i));
+
+ if (has_temp_offset(data) && i < NUM_TEMP_OFFSET)
+ data->temp[i][3] =
+ it87_read_value(data,
+ IT87_REG_TEMP_OFFSET[i]);
+
+ if (i >= NUM_TEMP_LIMIT)
+ continue;
+
+ data->temp[i][1] =
+ it87_read_value(data, IT87_REG_TEMP_LOW(i));
+ data->temp[i][2] =
+ it87_read_value(data, IT87_REG_TEMP_HIGH(i));
+ }
+
+ /* Newer chips don't have clock dividers */
+ if ((data->has_fan & 0x07) && !has_16bit_fans(data)) {
+ i = it87_read_value(data, IT87_REG_FAN_DIV);
+ data->fan_div[0] = i & 0x07;
+ data->fan_div[1] = (i >> 3) & 0x07;
+ data->fan_div[2] = (i & 0x40) ? 3 : 1;
+ }
+
+ data->alarms =
+ it87_read_value(data, IT87_REG_ALARM1) |
+ (it87_read_value(data, IT87_REG_ALARM2) << 8) |
+ (it87_read_value(data, IT87_REG_ALARM3) << 16);
+ data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE);
+
+ data->fan_main_ctrl = it87_read_value(data,
+ IT87_REG_FAN_MAIN_CTRL);
+ data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL);
+ for (i = 0; i < NUM_PWM; i++) {
+ if (!(data->has_pwm & BIT(i)))
+ continue;
+ it87_update_pwm_ctrl(data, i);
+ }
+
+ data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE);
+ data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA);
+ /*
+ * The IT8705F does not have VID capability.
+ * The IT8718F and later don't use IT87_REG_VID for the
+ * same purpose.
+ */
+ if (data->type == it8712 || data->type == it8716) {
+ data->vid = it87_read_value(data, IT87_REG_VID);
+ /*
+ * The older IT8712F revisions had only 5 VID pins,
+ * but we assume it is always safe to read 6 bits.
+ */
+ data->vid &= 0x3f;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct it87_data *data = it87_update_device(dev);
+ int index = sattr->index;
+ int nr = sattr->nr;
+
+ return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr][index]));
+}
+
+static ssize_t set_in(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 it87_data *data = dev_get_drvdata(dev);
+ int index = sattr->index;
+ int nr = sattr->nr;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->in[nr][index] = in_to_reg(data, nr, val);
+ it87_write_value(data,
+ index == 1 ? IT87_REG_VIN_MIN(nr)
+ : IT87_REG_VIN_MAX(nr),
+ data->in[nr][index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 0, 1);
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 0, 2);
+
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 1, 1);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 1, 2);
+
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 2, 1);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 2, 2);
+
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 3, 1);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 3, 2);
+
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 4, 1);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 4, 2);
+
+static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 5, 0);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 5, 1);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 5, 2);
+
+static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 6, 0);
+static SENSOR_DEVICE_ATTR_2(in6_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 6, 1);
+static SENSOR_DEVICE_ATTR_2(in6_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 6, 2);
+
+static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 7, 0);
+static SENSOR_DEVICE_ATTR_2(in7_min, S_IRUGO | S_IWUSR, show_in, set_in,
+ 7, 1);
+static SENSOR_DEVICE_ATTR_2(in7_max, S_IRUGO | S_IWUSR, show_in, set_in,
+ 7, 2);
+
+static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 8, 0);
+static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 9, 0);
+static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in, NULL, 10, 0);
+static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in, NULL, 11, 0);
+static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in, NULL, 12, 0);
+
+/* Up to 6 temperatures */
+static ssize_t show_temp(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;
+ struct it87_data *data = it87_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index]));
+}
+
+static ssize_t set_temp(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);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ struct it87_data *data = dev_get_drvdata(dev);
+ long val;
+ u8 reg, regval;
+
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ switch (index) {
+ default:
+ case 1:
+ reg = IT87_REG_TEMP_LOW(nr);
+ break;
+ case 2:
+ reg = IT87_REG_TEMP_HIGH(nr);
+ break;
+ case 3:
+ regval = it87_read_value(data, IT87_REG_BEEP_ENABLE);
+ if (!(regval & 0x80)) {
+ regval |= 0x80;
+ it87_write_value(data, IT87_REG_BEEP_ENABLE, regval);
+ }
+ data->valid = false;
+ reg = IT87_REG_TEMP_OFFSET[nr];
+ break;
+ }
+
+ data->temp[nr][index] = TEMP_TO_REG(val);
+ it87_write_value(data, reg, data->temp[nr][index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp1_offset, S_IRUGO | S_IWUSR, show_temp,
+ set_temp, 0, 3);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 1, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 1, 2);
+static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IRUGO | S_IWUSR, show_temp,
+ set_temp, 1, 3);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 2, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+ 2, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_offset, S_IRUGO | S_IWUSR, show_temp,
+ set_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0);
+static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0);
+
+static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct it87_data *data = it87_update_device(dev);
+ u8 reg = data->sensor; /* In case value is updated while used */
+ u8 extra = data->extra;
+
+ if ((has_temp_peci(data, nr) && (reg >> 6 == nr + 1)) ||
+ (has_temp_old_peci(data, nr) && (extra & 0x80)))
+ return sprintf(buf, "6\n"); /* Intel PECI */
+ if (reg & (1 << nr))
+ return sprintf(buf, "3\n"); /* thermal diode */
+ if (reg & (8 << nr))
+ return sprintf(buf, "4\n"); /* thermistor */
+ return sprintf(buf, "0\n"); /* disabled */
+}
+
+static ssize_t set_temp_type(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+
+ struct it87_data *data = dev_get_drvdata(dev);
+ long val;
+ u8 reg, extra;
+
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ reg = it87_read_value(data, IT87_REG_TEMP_ENABLE);
+ reg &= ~(1 << nr);
+ reg &= ~(8 << nr);
+ if (has_temp_peci(data, nr) && (reg >> 6 == nr + 1 || val == 6))
+ reg &= 0x3f;
+ extra = it87_read_value(data, IT87_REG_TEMP_EXTRA);
+ if (has_temp_old_peci(data, nr) && ((extra & 0x80) || val == 6))
+ extra &= 0x7f;
+ if (val == 2) { /* backwards compatibility */
+ dev_warn(dev,
+ "Sensor type 2 is deprecated, please use 4 instead\n");
+ val = 4;
+ }
+ /* 3 = thermal diode; 4 = thermistor; 6 = Intel PECI; 0 = disabled */
+ if (val == 3)
+ reg |= 1 << nr;
+ else if (val == 4)
+ reg |= 8 << nr;
+ else if (has_temp_peci(data, nr) && val == 6)
+ reg |= (nr + 1) << 6;
+ else if (has_temp_old_peci(data, nr) && val == 6)
+ extra |= 0x80;
+ else if (val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->sensor = reg;
+ data->extra = extra;
+ 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 = false; /* Force cache refresh */
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type,
+ set_temp_type, 0);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type,
+ set_temp_type, 1);
+static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type,
+ set_temp_type, 2);
+
+/* 6 Fans */
+
+static int pwm_mode(const struct it87_data *data, int nr)
+{
+ if (data->type != it8603 && nr < 3 && !(data->fan_main_ctrl & BIT(nr)))
+ return 0; /* Full speed */
+ if (data->pwm_ctrl[nr] & 0x80)
+ return 2; /* Automatic mode */
+ if ((data->type == it8603 || nr >= 3) &&
+ data->pwm_duty[nr] == pwm_to_reg(data, 0xff))
+ return 0; /* Full speed */
+
+ return 1; /* Manual mode */
+}
+
+static ssize_t show_fan(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 speed;
+ struct it87_data *data = it87_update_device(dev);
+
+ speed = has_16bit_fans(data) ?
+ FAN16_FROM_REG(data->fan[nr][index]) :
+ FAN_FROM_REG(data->fan[nr][index],
+ DIV_FROM_REG(data->fan_div[nr]));
+ return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = it87_update_device(dev);
+ int nr = sensor_attr->index;
+
+ return sprintf(buf, "%lu\n", DIV_FROM_REG(data->fan_div[nr]));
+}
+
+static ssize_t show_pwm_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = it87_update_device(dev);
+ int nr = sensor_attr->index;
+
+ return sprintf(buf, "%d\n", pwm_mode(data, nr));
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = it87_update_device(dev);
+ int nr = sensor_attr->index;
+
+ return sprintf(buf, "%d\n",
+ pwm_from_reg(data, data->pwm_duty[nr]));
+}
+
+static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = it87_update_device(dev);
+ int nr = sensor_attr->index;
+ unsigned int freq;
+ int index;
+
+ if (has_pwm_freq2(data) && nr == 1)
+ index = (data->extra >> 4) & 0x07;
+ else
+ index = (data->fan_ctl >> 4) & 0x07;
+
+ freq = pwm_freq[index] / (has_newer_autopwm(data) ? 256 : 128);
+
+ return sprintf(buf, "%u\n", freq);
+}
+
+static ssize_t set_fan(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);
+ int nr = sattr->nr;
+ int index = sattr->index;
+
+ struct it87_data *data = dev_get_drvdata(dev);
+ long val;
+ u8 reg;
+
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ if (has_16bit_fans(data)) {
+ data->fan[nr][index] = FAN16_TO_REG(val);
+ it87_write_value(data, IT87_REG_FAN_MIN[nr],
+ data->fan[nr][index] & 0xff);
+ it87_write_value(data, IT87_REG_FANX_MIN[nr],
+ data->fan[nr][index] >> 8);
+ } else {
+ reg = it87_read_value(data, IT87_REG_FAN_DIV);
+ switch (nr) {
+ case 0:
+ data->fan_div[nr] = reg & 0x07;
+ break;
+ case 1:
+ data->fan_div[nr] = (reg >> 3) & 0x07;
+ break;
+ case 2:
+ data->fan_div[nr] = (reg & 0x40) ? 3 : 1;
+ break;
+ }
+ data->fan[nr][index] =
+ FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
+ it87_write_value(data, IT87_REG_FAN_MIN[nr],
+ data->fan[nr][index]);
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = dev_get_drvdata(dev);
+ int nr = sensor_attr->index;
+ unsigned long val;
+ int min;
+ u8 old;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ old = it87_read_value(data, IT87_REG_FAN_DIV);
+
+ /* Save fan min limit */
+ min = FAN_FROM_REG(data->fan[nr][1], DIV_FROM_REG(data->fan_div[nr]));
+
+ switch (nr) {
+ case 0:
+ case 1:
+ data->fan_div[nr] = DIV_TO_REG(val);
+ break;
+ case 2:
+ if (val < 8)
+ data->fan_div[nr] = 1;
+ else
+ data->fan_div[nr] = 3;
+ }
+ val = old & 0x80;
+ val |= (data->fan_div[0] & 0x07);
+ val |= (data->fan_div[1] & 0x07) << 3;
+ if (data->fan_div[2] == 3)
+ val |= 0x1 << 6;
+ it87_write_value(data, IT87_REG_FAN_DIV, val);
+
+ /* Restore fan min limit */
+ data->fan[nr][1] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ it87_write_value(data, IT87_REG_FAN_MIN[nr], data->fan[nr][1]);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* Returns 0 if OK, -EINVAL otherwise */
+static int check_trip_points(struct device *dev, int nr)
+{
+ const struct it87_data *data = dev_get_drvdata(dev);
+ int i, err = 0;
+
+ if (has_old_autopwm(data)) {
+ for (i = 0; i < 3; i++) {
+ if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1])
+ err = -EINVAL;
+ }
+ for (i = 0; i < 2; i++) {
+ if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1])
+ err = -EINVAL;
+ }
+ } else if (has_newer_autopwm(data)) {
+ for (i = 1; i < 3; i++) {
+ if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1])
+ err = -EINVAL;
+ }
+ }
+
+ if (err) {
+ dev_err(dev,
+ "Inconsistent trip points, not switching to automatic mode\n");
+ dev_err(dev, "Adjust the trip points and try again\n");
+ }
+ return err;
+}
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = dev_get_drvdata(dev);
+ int nr = sensor_attr->index;
+ long val;
+
+ if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 2)
+ return -EINVAL;
+
+ /* Check trip points before switching to automatic mode */
+ if (val == 2) {
+ if (check_trip_points(dev, nr) < 0)
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+
+ if (val == 0) {
+ if (nr < 3 && data->type != it8603) {
+ int tmp;
+ /* make sure the fan is on when in on/off mode */
+ tmp = it87_read_value(data, IT87_REG_FAN_CTL);
+ it87_write_value(data, IT87_REG_FAN_CTL, tmp | BIT(nr));
+ /* set on/off mode */
+ data->fan_main_ctrl &= ~BIT(nr);
+ it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
+ data->fan_main_ctrl);
+ } else {
+ u8 ctrl;
+
+ /* No on/off mode, set maximum pwm value */
+ data->pwm_duty[nr] = pwm_to_reg(data, 0xff);
+ it87_write_value(data, IT87_REG_PWM_DUTY[nr],
+ data->pwm_duty[nr]);
+ /* and set manual mode */
+ if (has_newer_autopwm(data)) {
+ ctrl = (data->pwm_ctrl[nr] & 0x7c) |
+ data->pwm_temp_map[nr];
+ } else {
+ ctrl = data->pwm_duty[nr];
+ }
+ data->pwm_ctrl[nr] = ctrl;
+ it87_write_value(data, IT87_REG_PWM[nr], ctrl);
+ }
+ } else {
+ u8 ctrl;
+
+ if (has_newer_autopwm(data)) {
+ ctrl = (data->pwm_ctrl[nr] & 0x7c) |
+ data->pwm_temp_map[nr];
+ if (val != 1)
+ ctrl |= 0x80;
+ } else {
+ ctrl = (val == 1 ? data->pwm_duty[nr] : 0x80);
+ }
+ data->pwm_ctrl[nr] = ctrl;
+ it87_write_value(data, IT87_REG_PWM[nr], ctrl);
+
+ if (data->type != it8603 && nr < 3) {
+ /* set SmartGuardian mode */
+ data->fan_main_ctrl |= BIT(nr);
+ it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
+ data->fan_main_ctrl);
+ }
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = dev_get_drvdata(dev);
+ int nr = sensor_attr->index;
+ long val;
+
+ if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ it87_update_pwm_ctrl(data, nr);
+ if (has_newer_autopwm(data)) {
+ /*
+ * If we are in automatic mode, the PWM duty cycle register
+ * is read-only so we can't write the value.
+ */
+ if (data->pwm_ctrl[nr] & 0x80) {
+ mutex_unlock(&data->update_lock);
+ return -EBUSY;
+ }
+ data->pwm_duty[nr] = pwm_to_reg(data, val);
+ it87_write_value(data, IT87_REG_PWM_DUTY[nr],
+ data->pwm_duty[nr]);
+ } else {
+ data->pwm_duty[nr] = pwm_to_reg(data, val);
+ /*
+ * If we are in manual mode, write the duty cycle immediately;
+ * otherwise, just store it for later use.
+ */
+ if (!(data->pwm_ctrl[nr] & 0x80)) {
+ data->pwm_ctrl[nr] = data->pwm_duty[nr];
+ it87_write_value(data, IT87_REG_PWM[nr],
+ data->pwm_ctrl[nr]);
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = dev_get_drvdata(dev);
+ int nr = sensor_attr->index;
+ unsigned long val;
+ int i;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ val = clamp_val(val, 0, 1000000);
+ val *= has_newer_autopwm(data) ? 256 : 128;
+
+ /* Search for the nearest available frequency */
+ for (i = 0; i < 7; i++) {
+ if (val > (pwm_freq[i] + pwm_freq[i + 1]) / 2)
+ break;
+ }
+
+ mutex_lock(&data->update_lock);
+ if (nr == 0) {
+ data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL) & 0x8f;
+ data->fan_ctl |= i << 4;
+ it87_write_value(data, IT87_REG_FAN_CTL, data->fan_ctl);
+ } else {
+ data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x8f;
+ data->extra |= i << 4;
+ it87_write_value(data, IT87_REG_TEMP_EXTRA, data->extra);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_pwm_temp_map(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = it87_update_device(dev);
+ int nr = sensor_attr->index;
+ int map;
+
+ map = data->pwm_temp_map[nr];
+ if (map >= 3)
+ map = 0; /* Should never happen */
+ if (nr >= 3) /* pwm channels 3..6 map to temp4..6 */
+ map += 3;
+
+ return sprintf(buf, "%d\n", (int)BIT(map));
+}
+
+static ssize_t set_pwm_temp_map(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct it87_data *data = dev_get_drvdata(dev);
+ int nr = sensor_attr->index;
+ long val;
+ u8 reg;
+
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ if (nr >= 3)
+ val -= 3;
+
+ switch (val) {
+ case BIT(0):
+ reg = 0x00;
+ break;
+ case BIT(1):
+ reg = 0x01;
+ break;
+ case BIT(2):
+ reg = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ it87_update_pwm_ctrl(data, nr);
+ data->pwm_temp_map[nr] = reg;
+ /*
+ * If we are in automatic mode, write the temp mapping immediately;
+ * otherwise, just store it for later use.
+ */
+ if (data->pwm_ctrl[nr] & 0x80) {
+ data->pwm_ctrl[nr] = (data->pwm_ctrl[nr] & 0xfc) |
+ data->pwm_temp_map[nr];
+ it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_auto_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct it87_data *data = it87_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int point = sensor_attr->index;
+
+ return sprintf(buf, "%d\n",
+ pwm_from_reg(data, data->auto_pwm[nr][point]));
+}
+
+static ssize_t set_auto_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct it87_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int point = sensor_attr->index;
+ int regaddr;
+ long val;
+
+ if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->auto_pwm[nr][point] = pwm_to_reg(data, val);
+ if (has_newer_autopwm(data))
+ regaddr = IT87_REG_AUTO_TEMP(nr, 3);
+ else
+ regaddr = IT87_REG_AUTO_PWM(nr, point);
+ it87_write_value(data, regaddr, data->auto_pwm[nr][point]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_auto_pwm_slope(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct it87_data *data = it87_update_device(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+
+ return sprintf(buf, "%d\n", data->auto_pwm[nr][1] & 0x7f);
+}
+
+static ssize_t set_auto_pwm_slope(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct it87_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0 || val > 127)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->auto_pwm[nr][1] = (data->auto_pwm[nr][1] & 0x80) | val;
+ it87_write_value(data, IT87_REG_AUTO_TEMP(nr, 4),
+ data->auto_pwm[nr][1]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_auto_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct it87_data *data = it87_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int point = sensor_attr->index;
+ int reg;
+
+ if (has_old_autopwm(data) || point)
+ reg = data->auto_temp[nr][point];
+ else
+ reg = data->auto_temp[nr][1] - (data->auto_temp[nr][0] & 0x1f);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(reg));
+}
+
+static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct it87_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int point = sensor_attr->index;
+ long val;
+ int reg;
+
+ if (kstrtol(buf, 10, &val) < 0 || val < -128000 || val > 127000)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (has_newer_autopwm(data) && !point) {
+ reg = data->auto_temp[nr][1] - TEMP_TO_REG(val);
+ reg = clamp_val(reg, 0, 0x1f) | (data->auto_temp[nr][0] & 0xe0);
+ data->auto_temp[nr][0] = reg;
+ it87_write_value(data, IT87_REG_AUTO_TEMP(nr, 5), reg);
+ } else {
+ reg = TEMP_TO_REG(val);
+ data->auto_temp[nr][point] = reg;
+ if (has_newer_autopwm(data))
+ point--;
+ it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), reg);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+ 0, 1);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div,
+ set_fan_div, 0);
+
+static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+ 1, 1);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, show_fan_div,
+ set_fan_div, 1);
+
+static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 2, 0);
+static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+ 2, 1);
+static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO | S_IWUSR, show_fan_div,
+ set_fan_div, 2);
+
+static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 3, 0);
+static SENSOR_DEVICE_ATTR_2(fan4_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+ 3, 1);
+
+static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+ 4, 1);
+
+static SENSOR_DEVICE_ATTR_2(fan6_input, S_IRUGO, show_fan, NULL, 5, 0);
+static SENSOR_DEVICE_ATTR_2(fan6_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+ 5, 1);
+
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR, show_pwm_freq,
+ set_pwm_freq, 0);
+static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO,
+ show_pwm_temp_map, set_pwm_temp_map, 0);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO,
+ show_auto_pwm, NULL, 0, 3);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 0, 3);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 0, 4);
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_start, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 0, 0);
+static SENSOR_DEVICE_ATTR(pwm1_auto_slope, S_IRUGO | S_IWUSR,
+ show_auto_pwm_slope, set_auto_pwm_slope, 0);
+
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm2_freq, S_IRUGO, show_pwm_freq, set_pwm_freq, 1);
+static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IRUGO,
+ show_pwm_temp_map, set_pwm_temp_map, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO,
+ show_auto_pwm, NULL, 1, 3);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 1, 3);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 1, 4);
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_start, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 1, 0);
+static SENSOR_DEVICE_ATTR(pwm2_auto_slope, S_IRUGO | S_IWUSR,
+ show_auto_pwm_slope, set_auto_pwm_slope, 1);
+
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm3_freq, S_IRUGO, show_pwm_freq, NULL, 2);
+static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IRUGO,
+ show_pwm_temp_map, set_pwm_temp_map, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO,
+ show_auto_pwm, NULL, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 4);
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_start, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 2, 0);
+static SENSOR_DEVICE_ATTR(pwm3_auto_slope, S_IRUGO | S_IWUSR,
+ show_auto_pwm_slope, set_auto_pwm_slope, 2);
+
+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 3);
+static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm4_freq, S_IRUGO, show_pwm_freq, NULL, 3);
+static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IRUGO,
+ show_pwm_temp_map, set_pwm_temp_map, 3);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm4_auto_start, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 3, 0);
+static SENSOR_DEVICE_ATTR(pwm4_auto_slope, S_IRUGO | S_IWUSR,
+ show_auto_pwm_slope, set_auto_pwm_slope, 3);
+
+static SENSOR_DEVICE_ATTR(pwm5_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 4);
+static SENSOR_DEVICE_ATTR(pwm5, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 4);
+static SENSOR_DEVICE_ATTR(pwm5_freq, S_IRUGO, show_pwm_freq, NULL, 4);
+static SENSOR_DEVICE_ATTR(pwm5_auto_channels_temp, S_IRUGO,
+ show_pwm_temp_map, set_pwm_temp_map, 4);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm5_auto_start, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 4, 0);
+static SENSOR_DEVICE_ATTR(pwm5_auto_slope, S_IRUGO | S_IWUSR,
+ show_auto_pwm_slope, set_auto_pwm_slope, 4);
+
+static SENSOR_DEVICE_ATTR(pwm6_enable, S_IRUGO | S_IWUSR,
+ show_pwm_enable, set_pwm_enable, 5);
+static SENSOR_DEVICE_ATTR(pwm6, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 5);
+static SENSOR_DEVICE_ATTR(pwm6_freq, S_IRUGO, show_pwm_freq, NULL, 5);
+static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO,
+ show_pwm_temp_map, set_pwm_temp_map, 5);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point1_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point2_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_point3_temp, S_IRUGO | S_IWUSR,
+ show_auto_temp, set_auto_temp, 2, 3);
+static SENSOR_DEVICE_ATTR_2(pwm6_auto_start, S_IRUGO | S_IWUSR,
+ show_auto_pwm, set_auto_pwm, 5, 0);
+static SENSOR_DEVICE_ATTR(pwm6_auto_slope, S_IRUGO | S_IWUSR,
+ show_auto_pwm_slope, set_auto_pwm_slope, 5);
+
+/* Alarms */
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct it87_data *data = it87_update_device(dev);
+
+ return sprintf(buf, "%u\n", data->alarms);
+}
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct it87_data *data = it87_update_device(dev);
+ int bitnr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
+static ssize_t clear_intrusion(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct it87_data *data = dev_get_drvdata(dev);
+ int config;
+ long val;
+
+ if (kstrtol(buf, 10, &val) < 0 || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ config = it87_read_value(data, IT87_REG_CONFIG);
+ if (config < 0) {
+ count = config;
+ } else {
+ config |= BIT(5);
+ it87_write_value(data, IT87_REG_CONFIG, config);
+ /* Invalidate cache to force re-read */
+ data->valid = false;
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 14);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 15);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18);
+static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR,
+ show_alarm, clear_intrusion, 4);
+
+static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct it87_data *data = it87_update_device(dev);
+ int bitnr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u\n", (data->beeps >> bitnr) & 1);
+}
+
+static ssize_t set_beep(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct it87_data *data = dev_get_drvdata(dev);
+ long val;
+
+ if (kstrtol(buf, 10, &val) < 0 || (val != 0 && val != 1))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE);
+ if (val)
+ data->beeps |= BIT(bitnr);
+ else
+ data->beeps &= ~BIT(bitnr);
+ it87_write_value(data, IT87_REG_BEEP_ENABLE, data->beeps);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(in0_beep, S_IRUGO | S_IWUSR,
+ show_beep, set_beep, 1);
+static SENSOR_DEVICE_ATTR(in1_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in3_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in4_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in5_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in6_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in7_beep, S_IRUGO, show_beep, NULL, 1);
+/* fanX_beep writability is set later */
+static SENSOR_DEVICE_ATTR(fan1_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan4_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan5_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan6_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR,
+ show_beep, set_beep, 2);
+static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_beep, NULL, 2);
+
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct it87_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)
+{
+ struct it87_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ data->vrm = val;
+
+ return count;
+}
+static DEVICE_ATTR_RW(vrm);
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct it87_data *data = it87_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 show_label(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ static const char * const labels[] = {
+ "+5V",
+ "5VSB",
+ "Vbat",
+ "AVCC",
+ };
+ static const char * const labels_it8721[] = {
+ "+3.3V",
+ "3VSB",
+ "Vbat",
+ "+3.3V",
+ };
+ struct it87_data *data = dev_get_drvdata(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+ const char *label;
+
+ if (has_vin3_5v(data) && nr == 0)
+ label = labels[0];
+ else if (has_12mv_adc(data) || has_10_9mv_adc(data))
+ label = labels_it8721[nr];
+ else
+ label = labels[nr];
+
+ return sprintf(buf, "%s\n", label);
+}
+static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
+/* AVCC3 */
+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 = kobj_to_dev(kobj);
+ struct it87_data *data = dev_get_drvdata(dev);
+ int i = index / 5; /* voltage index */
+ int a = index % 5; /* attribute index */
+
+ if (index >= 40) { /* in8 and higher only have input attributes */
+ i = index - 40 + 8;
+ a = 0;
+ }
+
+ if (!(data->has_in & BIT(i)))
+ return 0;
+
+ if (a == 4 && !data->has_beep)
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute *it87_attributes_in[] = {
+ &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_in0_beep.dev_attr.attr, /* 4 */
+
+ &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_in1_beep.dev_attr.attr, /* 9 */
+
+ &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_in2_beep.dev_attr.attr, /* 14 */
+
+ &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_in3_beep.dev_attr.attr, /* 19 */
+
+ &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_in4_beep.dev_attr.attr, /* 24 */
+
+ &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,
+ &sensor_dev_attr_in5_beep.dev_attr.attr, /* 29 */
+
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_beep.dev_attr.attr, /* 34 */
+
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */
+
+ &sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */
+ &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,
+ NULL
+};
+
+static const struct attribute_group it87_group_in = {
+ .attrs = it87_attributes_in,
+ .is_visible = it87_in_is_visible,
+};
+
+static umode_t it87_temp_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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 */
+
+ if (index >= 21) {
+ i = index - 21 + 3;
+ a = 0;
+ }
+
+ if (!(data->has_temp & BIT(i)))
+ return 0;
+
+ if (a == 5 && !has_temp_offset(data))
+ return 0;
+
+ if (a == 6 && !data->has_beep)
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute *it87_attributes_temp[] = {
+ &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_type.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr, /* 5 */
+ &sensor_dev_attr_temp1_beep.dev_attr.attr, /* 6 */
+
+ &sensor_dev_attr_temp2_input.dev_attr.attr, /* 7 */
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_input.dev_attr.attr, /* 14 */
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_input.dev_attr.attr, /* 21 */
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group_temp = {
+ .attrs = it87_attributes_temp,
+ .is_visible = it87_temp_is_visible,
+};
+
+static umode_t it87_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct it87_data *data = dev_get_drvdata(dev);
+
+ if ((index == 2 || index == 3) && !data->has_vid)
+ return 0;
+
+ if (index > 3 && !(data->in_internal & BIT(index - 4)))
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute *it87_attributes[] = {
+ &dev_attr_alarms.attr,
+ &sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
+ &dev_attr_vrm.attr, /* 2 */
+ &dev_attr_cpu0_vid.attr, /* 3 */
+ &sensor_dev_attr_in3_label.dev_attr.attr, /* 4 .. 7 */
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+ &sensor_dev_attr_in9_label.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group = {
+ .attrs = it87_attributes,
+ .is_visible = it87_is_visible,
+};
+
+static umode_t it87_fan_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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 */
+
+ if (index >= 15) { /* fan 4..6 don't have divisor attributes */
+ i = (index - 15) / 4 + 3;
+ a = (index - 15) % 4;
+ }
+
+ if (!(data->has_fan & BIT(i)))
+ return 0;
+
+ if (a == 3) { /* beep */
+ if (!data->has_beep)
+ return 0;
+ /* first fan beep attribute is writable */
+ if (i == __ffs(data->has_fan))
+ return attr->mode | S_IWUSR;
+ }
+
+ if (a == 4 && has_16bit_fans(data)) /* divisor */
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute *it87_attributes_fan[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_beep.dev_attr.attr, /* 3 */
+ &sensor_dev_attr_fan1_div.dev_attr.attr, /* 4 */
+
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_beep.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr, /* 9 */
+
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_beep.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr, /* 14 */
+
+ &sensor_dev_attr_fan4_input.dev_attr.attr, /* 15 */
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan4_beep.dev_attr.attr,
+
+ &sensor_dev_attr_fan5_input.dev_attr.attr, /* 19 */
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan5_beep.dev_attr.attr,
+
+ &sensor_dev_attr_fan6_input.dev_attr.attr, /* 23 */
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan6_beep.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group_fan = {
+ .attrs = it87_attributes_fan,
+ .is_visible = it87_fan_is_visible,
+};
+
+static umode_t it87_pwm_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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 */
+
+ if (!(data->has_pwm & BIT(i)))
+ return 0;
+
+ /* pwmX_auto_channels_temp is only writable if auto pwm is supported */
+ if (a == 3 && (has_old_autopwm(data) || has_newer_autopwm(data)))
+ return attr->mode | S_IWUSR;
+
+ /* pwm2_freq is writable if there are two pwm frequency selects */
+ if (has_pwm_freq2(data) && i == 1 && a == 2)
+ return attr->mode | S_IWUSR;
+
+ return attr->mode;
+}
+
+static struct attribute *it87_attributes_pwm[] = {
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ &sensor_dev_attr_pwm4_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm5_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm5_auto_channels_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm6_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm6_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm6_auto_channels_temp.dev_attr.attr,
+
+ NULL
+};
+
+static const struct attribute_group it87_group_pwm = {
+ .attrs = it87_attributes_pwm,
+ .is_visible = it87_pwm_is_visible,
+};
+
+static umode_t it87_auto_pwm_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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 */
+
+ if (index >= 33) { /* pwm 4..6 */
+ i = (index - 33) / 6 + 3;
+ a = (index - 33) % 6 + 4;
+ }
+
+ if (!(data->has_pwm & BIT(i)))
+ return 0;
+
+ if (has_newer_autopwm(data)) {
+ if (a < 4) /* no auto point pwm */
+ return 0;
+ if (a == 8) /* no auto_point4 */
+ return 0;
+ }
+ if (has_old_autopwm(data)) {
+ if (a >= 9) /* no pwm_auto_start, pwm_auto_slope */
+ return 0;
+ }
+
+ return attr->mode;
+}
+
+static struct attribute *it87_attributes_auto_pwm[] = {
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_start.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_slope.dev_attr.attr,
+
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, /* 11 */
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_start.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_slope.dev_attr.attr,
+
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, /* 22 */
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_start.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_slope.dev_attr.attr,
+
+ &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr, /* 33 */
+ &sensor_dev_attr_pwm4_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_start.dev_attr.attr,
+ &sensor_dev_attr_pwm4_auto_slope.dev_attr.attr,
+
+ &sensor_dev_attr_pwm5_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm5_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm5_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm5_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm5_auto_start.dev_attr.attr,
+ &sensor_dev_attr_pwm5_auto_slope.dev_attr.attr,
+
+ &sensor_dev_attr_pwm6_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm6_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm6_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm6_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm6_auto_start.dev_attr.attr,
+ &sensor_dev_attr_pwm6_auto_slope.dev_attr.attr,
+
+ NULL,
+};
+
+static const struct attribute_group it87_group_auto_pwm = {
+ .attrs = it87_attributes_auto_pwm,
+ .is_visible = it87_auto_pwm_is_visible,
+};
+
+/* SuperIO detection - will change isa_address if a chip is found */
+static int __init it87_find(int sioaddr, unsigned short *address,
+ struct it87_sio_data *sio_data)
+{
+ int err;
+ u16 chip_type;
+ const char *board_vendor, *board_name;
+ const struct it87_devices *config;
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ err = -ENODEV;
+ chip_type = force_id ? force_id : superio_inw(sioaddr, DEVID);
+
+ switch (chip_type) {
+ case IT8705F_DEVID:
+ sio_data->type = it87;
+ break;
+ case IT8712F_DEVID:
+ sio_data->type = it8712;
+ break;
+ case IT8716F_DEVID:
+ case IT8726F_DEVID:
+ sio_data->type = it8716;
+ break;
+ case IT8718F_DEVID:
+ sio_data->type = it8718;
+ break;
+ case IT8720F_DEVID:
+ sio_data->type = it8720;
+ break;
+ case IT8721F_DEVID:
+ sio_data->type = it8721;
+ break;
+ case IT8728F_DEVID:
+ sio_data->type = it8728;
+ break;
+ case IT8732F_DEVID:
+ sio_data->type = it8732;
+ break;
+ case IT8792E_DEVID:
+ sio_data->type = it8792;
+ break;
+ case IT8771E_DEVID:
+ sio_data->type = it8771;
+ break;
+ case IT8772E_DEVID:
+ sio_data->type = it8772;
+ break;
+ case IT8781F_DEVID:
+ sio_data->type = it8781;
+ break;
+ case IT8782F_DEVID:
+ sio_data->type = it8782;
+ break;
+ case IT8783E_DEVID:
+ sio_data->type = it8783;
+ break;
+ case IT8786E_DEVID:
+ sio_data->type = it8786;
+ break;
+ case IT8790E_DEVID:
+ sio_data->type = it8790;
+ break;
+ case IT8603E_DEVID:
+ case IT8623E_DEVID:
+ sio_data->type = it8603;
+ break;
+ case IT8620E_DEVID:
+ sio_data->type = it8620;
+ break;
+ case IT8622E_DEVID:
+ sio_data->type = it8622;
+ break;
+ case IT8628E_DEVID:
+ sio_data->type = it8628;
+ break;
+ case 0xffff: /* No device at all */
+ goto exit;
+ default:
+ pr_debug("Unsupported chip (DEVID=0x%x)\n", chip_type);
+ goto exit;
+ }
+
+ superio_select(sioaddr, PME);
+ if (!(superio_inb(sioaddr, IT87_ACT_REG) & 0x01)) {
+ pr_info("Device not activated, skipping\n");
+ goto exit;
+ }
+
+ *address = superio_inw(sioaddr, IT87_BASE_REG) & ~(IT87_EXTENT - 1);
+ if (*address == 0) {
+ pr_info("Base address not set, skipping\n");
+ goto exit;
+ }
+
+ err = 0;
+ sio_data->sioaddr = sioaddr;
+ sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f;
+ pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type,
+ it87_devices[sio_data->type].suffix,
+ *address, sio_data->revision);
+
+ config = &it87_devices[sio_data->type];
+
+ /* in7 (VSB or VCCH5V) is always internal on some chips */
+ if (has_in7_internal(config))
+ sio_data->internal |= BIT(1);
+
+ /* in8 (Vbat) is always internal */
+ sio_data->internal |= BIT(2);
+
+ /* in9 (AVCC3), always internal if supported */
+ if (has_avcc3(config))
+ sio_data->internal |= BIT(3); /* in9 is AVCC */
+ else
+ sio_data->skip_in |= BIT(9);
+
+ if (!has_five_pwm(config))
+ sio_data->skip_pwm |= BIT(3) | BIT(4) | BIT(5);
+ else if (!has_six_pwm(config))
+ sio_data->skip_pwm |= BIT(5);
+
+ if (!has_vid(config))
+ sio_data->skip_vid = 1;
+
+ /* Read GPIO config and VID value from LDN 7 (GPIO) */
+ if (sio_data->type == it87) {
+ /* The IT8705F has a different LD number for GPIO */
+ superio_select(sioaddr, 5);
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8783) {
+ int reg25, reg27, reg2a, reg2c, regef;
+
+ superio_select(sioaddr, GPIO);
+
+ reg25 = superio_inb(sioaddr, IT87_SIO_GPIO1_REG);
+ reg27 = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ reg2a = superio_inb(sioaddr, IT87_SIO_PINX1_REG);
+ reg2c = superio_inb(sioaddr, IT87_SIO_PINX2_REG);
+ regef = superio_inb(sioaddr, IT87_SIO_SPI_REG);
+
+ /* Check if fan3 is there or not */
+ if ((reg27 & BIT(0)) || !(reg2c & BIT(2)))
+ sio_data->skip_fan |= BIT(2);
+ if ((reg25 & BIT(4)) ||
+ (!(reg2a & BIT(1)) && (regef & BIT(0))))
+ sio_data->skip_pwm |= BIT(2);
+
+ /* Check if fan2 is there or not */
+ if (reg27 & BIT(7))
+ sio_data->skip_fan |= BIT(1);
+ if (reg27 & BIT(3))
+ sio_data->skip_pwm |= BIT(1);
+
+ /* VIN5 */
+ if ((reg27 & BIT(0)) || (reg2c & BIT(2)))
+ sio_data->skip_in |= BIT(5); /* No VIN5 */
+
+ /* VIN6 */
+ if (reg27 & BIT(1))
+ sio_data->skip_in |= BIT(6); /* No VIN6 */
+
+ /*
+ * VIN7
+ * Does not depend on bit 2 of Reg2C, contrary to datasheet.
+ */
+ if (reg27 & BIT(2)) {
+ /*
+ * The data sheet is a bit unclear regarding the
+ * internal voltage divider for VCCH5V. It says
+ * "This bit enables and switches VIN7 (pin 91) to the
+ * internal voltage divider for VCCH5V".
+ * This is different to other chips, where the internal
+ * voltage divider would connect VIN7 to an internal
+ * voltage source. Maybe that is the case here as well.
+ *
+ * Since we don't know for sure, re-route it if that is
+ * not the case, and ask the user to report if the
+ * resulting voltage is sane.
+ */
+ if (!(reg2c & BIT(1))) {
+ reg2c |= BIT(1);
+ superio_outb(sioaddr, IT87_SIO_PINX2_REG,
+ reg2c);
+ sio_data->need_in7_reroute = true;
+ pr_notice("Routing internal VCCH5V to in7.\n");
+ }
+ pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
+ pr_notice("Please report if it displays a reasonable voltage.\n");
+ }
+
+ if (reg2c & BIT(0))
+ sio_data->internal |= BIT(0);
+ if (reg2c & BIT(1))
+ sio_data->internal |= BIT(1);
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8603) {
+ int reg27, reg29;
+
+ superio_select(sioaddr, GPIO);
+
+ reg27 = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+
+ /* Check if fan3 is there or not */
+ if (reg27 & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg27 & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+
+ /* Check if fan2 is there or not */
+ reg29 = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg29 & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+ if (reg29 & BIT(2))
+ sio_data->skip_fan |= BIT(1);
+
+ sio_data->skip_in |= BIT(5); /* No VIN5 */
+ sio_data->skip_in |= BIT(6); /* No VIN6 */
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8620 || sio_data->type == it8628) {
+ int reg;
+
+ superio_select(sioaddr, GPIO);
+
+ /* Check for pwm5 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO1_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(4);
+
+ /* Check for fan4, fan5 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO2_REG);
+ if (!(reg & BIT(5)))
+ sio_data->skip_fan |= BIT(3);
+ if (!(reg & BIT(4)))
+ sio_data->skip_fan |= BIT(4);
+
+ /* Check for pwm3, fan3 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+
+ /* Check for pwm4 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO4_REG);
+ if (reg & BIT(2))
+ sio_data->skip_pwm |= BIT(3);
+
+ /* Check for pwm2, fan2 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+ if (reg & BIT(2))
+ sio_data->skip_fan |= BIT(1);
+ /* Check for pwm6, fan6 */
+ if (!(reg & BIT(7))) {
+ sio_data->skip_pwm |= BIT(5);
+ sio_data->skip_fan |= BIT(5);
+ }
+
+ /* Check if AVCC is on VIN3 */
+ reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG);
+ if (reg & BIT(0))
+ sio_data->internal |= BIT(0);
+ else
+ sio_data->skip_in |= BIT(9);
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8622) {
+ int reg;
+
+ superio_select(sioaddr, GPIO);
+
+ /* Check for pwm4, fan4 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO1_REG);
+ if (reg & BIT(6))
+ sio_data->skip_fan |= BIT(3);
+ if (reg & BIT(5))
+ sio_data->skip_pwm |= BIT(3);
+
+ /* Check for pwm3, fan3, pwm5, fan5 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+ if (reg & BIT(3))
+ sio_data->skip_pwm |= BIT(4);
+ if (reg & BIT(1))
+ sio_data->skip_fan |= BIT(4);
+
+ /* Check for pwm2, fan2 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+ if (reg & BIT(2))
+ sio_data->skip_fan |= BIT(1);
+
+ /* Check for AVCC */
+ reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG);
+ if (!(reg & BIT(0)))
+ sio_data->skip_in |= BIT(9);
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else {
+ int reg;
+ bool uart6;
+
+ superio_select(sioaddr, GPIO);
+
+ /* Check for fan4, fan5 */
+ if (has_five_fans(config)) {
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO2_REG);
+ switch (sio_data->type) {
+ case it8718:
+ if (reg & BIT(5))
+ sio_data->skip_fan |= BIT(3);
+ if (reg & BIT(4))
+ sio_data->skip_fan |= BIT(4);
+ break;
+ case it8720:
+ case it8721:
+ case it8728:
+ if (!(reg & BIT(5)))
+ sio_data->skip_fan |= BIT(3);
+ if (!(reg & BIT(4)))
+ sio_data->skip_fan |= BIT(4);
+ break;
+ default:
+ break;
+ }
+ }
+
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ if (!sio_data->skip_vid) {
+ /* We need at least 4 VID pins */
+ if (reg & 0x0f) {
+ pr_info("VID is disabled (pins used for GPIO)\n");
+ sio_data->skip_vid = 1;
+ }
+ }
+
+ /* Check if fan3 is there or not */
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+
+ /* Check if fan2 is there or not */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+ if (reg & BIT(2))
+ sio_data->skip_fan |= BIT(1);
+
+ if ((sio_data->type == it8718 || sio_data->type == it8720) &&
+ !(sio_data->skip_vid))
+ sio_data->vid_value = superio_inb(sioaddr,
+ IT87_SIO_VID_REG);
+
+ reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG);
+
+ uart6 = sio_data->type == it8782 && (reg & BIT(2));
+
+ /*
+ * The IT8720F has no VIN7 pin, so VCCH5V should always be
+ * routed internally to VIN7 with an internal divider.
+ * Curiously, there still is a configuration bit to control
+ * this, which means it can be set incorrectly. And even
+ * more curiously, many boards out there are improperly
+ * configured, even though the IT8720F datasheet claims
+ * that the internal routing of VCCH5V to VIN7 is the default
+ * setting. So we force the internal routing in this case.
+ *
+ * On IT8782F, VIN7 is multiplexed with one of the UART6 pins.
+ * If UART6 is enabled, re-route VIN7 to the internal divider
+ * if that is not already the case.
+ */
+ if ((sio_data->type == it8720 || uart6) && !(reg & BIT(1))) {
+ reg |= BIT(1);
+ superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg);
+ sio_data->need_in7_reroute = true;
+ pr_notice("Routing internal VCCH5V to in7\n");
+ }
+ if (reg & BIT(0))
+ sio_data->internal |= BIT(0);
+ if (reg & BIT(1))
+ sio_data->internal |= BIT(1);
+
+ /*
+ * On IT8782F, UART6 pins overlap with VIN5, VIN6, and VIN7.
+ * While VIN7 can be routed to the internal voltage divider,
+ * VIN5 and VIN6 are not available if UART6 is enabled.
+ *
+ * Also, temp3 is not available if UART6 is enabled and TEMPIN3
+ * is the temperature source. Since we can not read the
+ * temperature source here, skip_temp is preliminary.
+ */
+ if (uart6) {
+ sio_data->skip_in |= BIT(5) | BIT(6);
+ sio_data->skip_temp |= BIT(2);
+ }
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ }
+ if (sio_data->beep_pin)
+ pr_info("Beeping is supported\n");
+
+ /* Disable specific features based on DMI strings */
+ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+ if (board_vendor && board_name) {
+ if (strcmp(board_vendor, "nVIDIA") == 0 &&
+ strcmp(board_name, "FN68PT") == 0) {
+ /*
+ * On the Shuttle SN68PT, FAN_CTL2 is apparently not
+ * connected to a fan, but to something else. One user
+ * has reported instant system power-off when changing
+ * the PWM2 duty cycle, so we disable it.
+ * I use the board name string as the trigger in case
+ * the same board is ever used in other systems.
+ */
+ pr_info("Disabling pwm2 due to hardware constraints\n");
+ sio_data->skip_pwm = BIT(1);
+ }
+ }
+
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+/*
+ * Some chips seem to have default value 0xff for all limit
+ * registers. For low voltage limits it makes no sense and triggers
+ * alarms, so change to 0 instead. For high temperature limits, it
+ * means -1 degree C, which surprisingly doesn't trigger an alarm,
+ * but is still confusing, so change to 127 degrees C.
+ */
+static void it87_check_limit_regs(struct it87_data *data)
+{
+ int i, reg;
+
+ for (i = 0; i < NUM_VIN_LIMIT; i++) {
+ reg = it87_read_value(data, IT87_REG_VIN_MIN(i));
+ if (reg == 0xff)
+ it87_write_value(data, IT87_REG_VIN_MIN(i), 0);
+ }
+ for (i = 0; i < NUM_TEMP_LIMIT; i++) {
+ reg = it87_read_value(data, IT87_REG_TEMP_HIGH(i));
+ if (reg == 0xff)
+ it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127);
+ }
+}
+
+/* Check if voltage monitors are reset manually or by some reason */
+static void it87_check_voltage_monitors_reset(struct it87_data *data)
+{
+ int reg;
+
+ reg = it87_read_value(data, IT87_REG_VIN_ENABLE);
+ if ((reg & 0xff) == 0) {
+ /* Enable all voltage monitors */
+ it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff);
+ }
+}
+
+/* Check if tachometers are reset manually or by some reason */
+static void it87_check_tachometers_reset(struct platform_device *pdev)
+{
+ struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
+ struct it87_data *data = platform_get_drvdata(pdev);
+ u8 mask, fan_main_ctrl;
+
+ mask = 0x70 & ~(sio_data->skip_fan << 4);
+ fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL);
+ if ((fan_main_ctrl & mask) == 0) {
+ /* Enable all fan tachometers */
+ fan_main_ctrl |= mask;
+ it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
+ fan_main_ctrl);
+ }
+}
+
+/* Set tachometers to 16-bit mode if needed */
+static void it87_check_tachometers_16bit_mode(struct platform_device *pdev)
+{
+ struct it87_data *data = platform_get_drvdata(pdev);
+ int reg;
+
+ if (!has_fan16_config(data))
+ return;
+
+ reg = it87_read_value(data, IT87_REG_FAN_16BIT);
+ if (~reg & 0x07 & data->has_fan) {
+ dev_dbg(&pdev->dev,
+ "Setting fan1-3 to 16-bit mode\n");
+ it87_write_value(data, IT87_REG_FAN_16BIT,
+ reg | 0x07);
+ }
+}
+
+static void it87_start_monitoring(struct it87_data *data)
+{
+ it87_write_value(data, IT87_REG_CONFIG,
+ (it87_read_value(data, IT87_REG_CONFIG) & 0x3e)
+ | (update_vbat ? 0x41 : 0x01));
+}
+
+/* Called when we have found a new IT87. */
+static void it87_init_device(struct platform_device *pdev)
+{
+ struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
+ struct it87_data *data = platform_get_drvdata(pdev);
+ int tmp, i;
+
+ /*
+ * For each PWM channel:
+ * - If it is in automatic mode, setting to manual mode should set
+ * the fan to full speed by default.
+ * - If it is in manual mode, we need a mapping to temperature
+ * channels to use when later setting to automatic mode later.
+ * Use a 1:1 mapping by default (we are clueless.)
+ * In both cases, the value can (and should) be changed by the user
+ * prior to switching to a different mode.
+ * Note that this is no longer needed for the IT8721F and later, as
+ * these have separate registers for the temperature mapping and the
+ * manual duty cycle.
+ */
+ for (i = 0; i < NUM_AUTO_PWM; i++) {
+ data->pwm_temp_map[i] = i;
+ data->pwm_duty[i] = 0x7f; /* Full speed */
+ data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */
+ }
+
+ it87_check_limit_regs(data);
+
+ /*
+ * Temperature channels are not forcibly enabled, as they can be
+ * set to two different sensor types and we can't guess which one
+ * is correct for a given system. These channels can be enabled at
+ * run-time through the temp{1-3}_type sysfs accessors if needed.
+ */
+
+ it87_check_voltage_monitors_reset(data);
+
+ it87_check_tachometers_reset(pdev);
+
+ data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL);
+ data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
+
+ it87_check_tachometers_16bit_mode(pdev);
+
+ /* Check for additional fans */
+ if (has_five_fans(data)) {
+ tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
+
+ if (tmp & BIT(4))
+ data->has_fan |= BIT(3); /* fan4 enabled */
+ if (tmp & BIT(5))
+ data->has_fan |= BIT(4); /* fan5 enabled */
+ if (has_six_fans(data) && (tmp & BIT(2)))
+ data->has_fan |= BIT(5); /* fan6 enabled */
+ }
+
+ /* Fan input pins may be used for alternative functions */
+ data->has_fan &= ~sio_data->skip_fan;
+
+ /* Check if pwm5, pwm6 are enabled */
+ if (has_six_pwm(data)) {
+ /* The following code may be IT8620E specific */
+ tmp = it87_read_value(data, IT87_REG_FAN_DIV);
+ if ((tmp & 0xc0) == 0xc0)
+ sio_data->skip_pwm |= BIT(4);
+ if (!(tmp & BIT(3)))
+ sio_data->skip_pwm |= BIT(5);
+ }
+
+ it87_start_monitoring(data);
+}
+
+/* Return 1 if and only if the PWM interface is safe to use */
+static int it87_check_pwm(struct device *dev)
+{
+ struct it87_data *data = dev_get_drvdata(dev);
+ /*
+ * Some BIOSes fail to correctly configure the IT87 fans. All fans off
+ * and polarity set to active low is sign that this is the case so we
+ * disable pwm control to protect the user.
+ */
+ int tmp = it87_read_value(data, IT87_REG_FAN_CTL);
+
+ if ((tmp & 0x87) == 0) {
+ if (fix_pwm_polarity) {
+ /*
+ * The user asks us to attempt a chip reconfiguration.
+ * This means switching to active high polarity and
+ * inverting all fan speed values.
+ */
+ int i;
+ u8 pwm[3];
+
+ for (i = 0; i < ARRAY_SIZE(pwm); i++)
+ pwm[i] = it87_read_value(data,
+ IT87_REG_PWM[i]);
+
+ /*
+ * If any fan is in automatic pwm mode, the polarity
+ * might be correct, as suspicious as it seems, so we
+ * better don't change anything (but still disable the
+ * PWM interface).
+ */
+ if (!((pwm[0] | pwm[1] | pwm[2]) & 0x80)) {
+ dev_info(dev,
+ "Reconfiguring PWM to active high polarity\n");
+ it87_write_value(data, IT87_REG_FAN_CTL,
+ tmp | 0x87);
+ for (i = 0; i < 3; i++)
+ it87_write_value(data,
+ IT87_REG_PWM[i],
+ 0x7f & ~pwm[i]);
+ return 1;
+ }
+
+ dev_info(dev,
+ "PWM configuration is too broken to be fixed\n");
+ }
+
+ return 0;
+ } else if (fix_pwm_polarity) {
+ dev_info(dev,
+ "PWM configuration looks sane, won't touch\n");
+ }
+
+ return 1;
+}
+
+static int it87_probe(struct platform_device *pdev)
+{
+ struct it87_data *data;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct it87_sio_data *sio_data = dev_get_platdata(dev);
+ int enable_pwm_interface;
+ struct device *hwmon_dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT,
+ DRVNAME)) {
+ dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
+ (unsigned long)res->start,
+ (unsigned long)(res->start + IT87_EC_EXTENT - 1));
+ return -EBUSY;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct it87_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = res->start;
+ data->sioaddr = sio_data->sioaddr;
+ data->type = sio_data->type;
+ data->features = it87_devices[sio_data->type].features;
+ data->peci_mask = it87_devices[sio_data->type].peci_mask;
+ data->old_peci_mask = it87_devices[sio_data->type].old_peci_mask;
+ /*
+ * IT8705F Datasheet 0.4.1, 3h == Version G.
+ * IT8712F Datasheet 0.9.1, section 8.3.5 indicates 8h == Version J.
+ * These are the first revisions with 16-bit tachometer support.
+ */
+ switch (data->type) {
+ case it87:
+ if (sio_data->revision >= 0x03) {
+ data->features &= ~FEAT_OLD_AUTOPWM;
+ data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS;
+ }
+ break;
+ case it8712:
+ if (sio_data->revision >= 0x08) {
+ data->features &= ~FEAT_OLD_AUTOPWM;
+ data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS |
+ FEAT_FIVE_FANS;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Now, we do the remaining detection. */
+ if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80) ||
+ it87_read_value(data, IT87_REG_CHIPID) != 0x90)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, data);
+
+ mutex_init(&data->update_lock);
+
+ /* Check PWM configuration */
+ enable_pwm_interface = it87_check_pwm(dev);
+ if (!enable_pwm_interface)
+ dev_info(dev,
+ "Detected broken BIOS defaults, disabling PWM interface\n");
+
+ /* Starting with IT8721F, we handle scaling of internal voltages */
+ if (has_scaling(data)) {
+ if (sio_data->internal & BIT(0))
+ data->in_scaled |= BIT(3); /* in3 is AVCC */
+ if (sio_data->internal & BIT(1))
+ data->in_scaled |= BIT(7); /* in7 is VSB */
+ if (sio_data->internal & BIT(2))
+ data->in_scaled |= BIT(8); /* in8 is Vbat */
+ if (sio_data->internal & BIT(3))
+ data->in_scaled |= BIT(9); /* in9 is AVCC */
+ } else if (sio_data->type == it8781 || sio_data->type == it8782 ||
+ sio_data->type == it8783) {
+ if (sio_data->internal & BIT(0))
+ data->in_scaled |= BIT(3); /* in3 is VCC5V */
+ if (sio_data->internal & BIT(1))
+ data->in_scaled |= BIT(7); /* in7 is VCCH5V */
+ }
+
+ data->has_temp = 0x07;
+ if (sio_data->skip_temp & BIT(2)) {
+ if (sio_data->type == it8782 &&
+ !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80))
+ data->has_temp &= ~BIT(2);
+ }
+
+ data->in_internal = sio_data->internal;
+ data->need_in7_reroute = sio_data->need_in7_reroute;
+ data->has_in = 0x3ff & ~sio_data->skip_in;
+
+ if (has_six_temp(data)) {
+ u8 reg = it87_read_value(data, IT87_REG_TEMP456_ENABLE);
+
+ /* Check for additional temperature sensors */
+ if ((reg & 0x03) >= 0x02)
+ data->has_temp |= BIT(3);
+ if (((reg >> 2) & 0x03) >= 0x02)
+ data->has_temp |= BIT(4);
+ if (((reg >> 4) & 0x03) >= 0x02)
+ data->has_temp |= BIT(5);
+
+ /* Check for additional voltage sensors */
+ if ((reg & 0x03) == 0x01)
+ data->has_in |= BIT(10);
+ if (((reg >> 2) & 0x03) == 0x01)
+ data->has_in |= BIT(11);
+ if (((reg >> 4) & 0x03) == 0x01)
+ data->has_in |= BIT(12);
+ }
+
+ data->has_beep = !!sio_data->beep_pin;
+
+ /* Initialize the IT87 chip */
+ it87_init_device(pdev);
+
+ if (!sio_data->skip_vid) {
+ data->has_vid = true;
+ data->vrm = vid_which_vrm();
+ /* VID reading from Super-I/O config space if available */
+ data->vid = sio_data->vid_value;
+ }
+
+ /* Prepare for sysfs hooks */
+ data->groups[0] = &it87_group;
+ data->groups[1] = &it87_group_in;
+ data->groups[2] = &it87_group_temp;
+ data->groups[3] = &it87_group_fan;
+
+ if (enable_pwm_interface) {
+ data->has_pwm = BIT(ARRAY_SIZE(IT87_REG_PWM)) - 1;
+ data->has_pwm &= ~sio_data->skip_pwm;
+
+ data->groups[4] = &it87_group_pwm;
+ if (has_old_autopwm(data) || has_newer_autopwm(data))
+ data->groups[5] = &it87_group_auto_pwm;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ it87_devices[sio_data->type].name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static void it87_resume_sio(struct platform_device *pdev)
+{
+ struct it87_data *data = dev_get_drvdata(&pdev->dev);
+ int err;
+ int reg2c;
+
+ if (!data->need_in7_reroute)
+ return;
+
+ err = superio_enter(data->sioaddr);
+ if (err) {
+ dev_warn(&pdev->dev,
+ "Unable to enter Super I/O to reroute in7 (%d)",
+ err);
+ return;
+ }
+
+ superio_select(data->sioaddr, GPIO);
+
+ reg2c = superio_inb(data->sioaddr, IT87_SIO_PINX2_REG);
+ if (!(reg2c & BIT(1))) {
+ dev_dbg(&pdev->dev,
+ "Routing internal VCCH5V to in7 again");
+
+ reg2c |= BIT(1);
+ superio_outb(data->sioaddr, IT87_SIO_PINX2_REG,
+ reg2c);
+ }
+
+ superio_exit(data->sioaddr);
+}
+
+static int it87_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct it87_data *data = dev_get_drvdata(dev);
+
+ it87_resume_sio(pdev);
+
+ mutex_lock(&data->update_lock);
+
+ it87_check_pwm(dev);
+ it87_check_limit_regs(data);
+ it87_check_voltage_monitors_reset(data);
+ it87_check_tachometers_reset(pdev);
+ it87_check_tachometers_16bit_mode(pdev);
+
+ it87_start_monitoring(data);
+
+ /* force update */
+ data->valid = false;
+
+ mutex_unlock(&data->update_lock);
+
+ it87_update_device(dev);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume);
+
+static struct platform_driver it87_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .pm = pm_sleep_ptr(&it87_dev_pm_ops),
+ },
+ .probe = it87_probe,
+};
+
+static int __init it87_device_add(int index, unsigned short address,
+ const struct it87_sio_data *sio_data)
+{
+ struct platform_device *pdev;
+ struct resource res = {
+ .start = address + IT87_EC_OFFSET,
+ .end = address + IT87_EC_OFFSET + IT87_EC_EXTENT - 1,
+ .name = DRVNAME,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ return err;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev)
+ return -ENOMEM;
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add_data(pdev, sio_data,
+ sizeof(struct it87_sio_data));
+ if (err) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ it87_pdev[index] = pdev;
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+ return err;
+}
+
+static int __init sm_it87_init(void)
+{
+ int sioaddr[2] = { REG_2E, REG_4E };
+ struct it87_sio_data sio_data;
+ unsigned short isa_address[2];
+ bool found = false;
+ int i, err;
+
+ err = platform_driver_register(&it87_driver);
+ if (err)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(sioaddr); i++) {
+ memset(&sio_data, 0, sizeof(struct it87_sio_data));
+ isa_address[i] = 0;
+ err = it87_find(sioaddr[i], &isa_address[i], &sio_data);
+ if (err || isa_address[i] == 0)
+ continue;
+ /*
+ * Don't register second chip if its ISA address matches
+ * the first chip's ISA address.
+ */
+ if (i && isa_address[i] == isa_address[0])
+ break;
+
+ err = it87_device_add(i, isa_address[i], &sio_data);
+ if (err)
+ goto exit_dev_unregister;
+
+ found = true;
+
+ /*
+ * IT8705F may respond on both SIO addresses.
+ * Stop probing after finding one.
+ */
+ if (sio_data.type == it87)
+ break;
+ }
+
+ if (!found) {
+ err = -ENODEV;
+ goto exit_unregister;
+ }
+ return 0;
+
+exit_dev_unregister:
+ /* NULL check handled by platform_device_unregister */
+ platform_device_unregister(it87_pdev[0]);
+exit_unregister:
+ platform_driver_unregister(&it87_driver);
+ return err;
+}
+
+static void __exit sm_it87_exit(void)
+{
+ /* NULL check handled by platform_device_unregister */
+ platform_device_unregister(it87_pdev[1]);
+ platform_device_unregister(it87_pdev[0]);
+ platform_driver_unregister(&it87_driver);
+}
+
+MODULE_AUTHOR("Chris Gauthron, Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver");
+module_param(update_vbat, bool, 0);
+MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
+module_param(fix_pwm_polarity, bool, 0);
+MODULE_PARM_DESC(fix_pwm_polarity,
+ "Force PWM polarity to active high (DANGEROUS)");
+MODULE_LICENSE("GPL");
+
+module_init(sm_it87_init);
+module_exit(sm_it87_exit);
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
new file mode 100644
index 000000000..6593d81cb
--- /dev/null
+++ b/drivers/hwmon/jc42.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * jc42.c - driver for Jedec JC42.4 compliant temperature sensors
+ *
+ * Copyright (c) 2010 Ericsson AB.
+ *
+ * Derived from lm77.c by Andras BALI <drewie@freemail.hu>.
+ *
+ * JC42.4 compliant temperature sensors are typically used on memory modules.
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, I2C_CLIENT_END };
+
+/* JC42 registers. All registers are 16 bit. */
+#define JC42_REG_CAP 0x00
+#define JC42_REG_CONFIG 0x01
+#define JC42_REG_TEMP_UPPER 0x02
+#define JC42_REG_TEMP_LOWER 0x03
+#define JC42_REG_TEMP_CRITICAL 0x04
+#define JC42_REG_TEMP 0x05
+#define JC42_REG_MANID 0x06
+#define JC42_REG_DEVICEID 0x07
+#define JC42_REG_SMBUS 0x22 /* NXP and Atmel, possibly others? */
+
+/* Status bits in temperature register */
+#define JC42_ALARM_CRIT_BIT 15
+#define JC42_ALARM_MAX_BIT 14
+#define JC42_ALARM_MIN_BIT 13
+
+/* Configuration register defines */
+#define JC42_CFG_CRIT_ONLY (1 << 2)
+#define JC42_CFG_TCRIT_LOCK (1 << 6)
+#define JC42_CFG_EVENT_LOCK (1 << 7)
+#define JC42_CFG_SHUTDOWN (1 << 8)
+#define JC42_CFG_HYST_SHIFT 9
+#define JC42_CFG_HYST_MASK (0x03 << 9)
+
+/* Capabilities */
+#define JC42_CAP_RANGE (1 << 2)
+
+/* Manufacturer IDs */
+#define ADT_MANID 0x11d4 /* Analog Devices */
+#define ATMEL_MANID 0x001f /* Atmel */
+#define ATMEL_MANID2 0x1114 /* Atmel */
+#define MAX_MANID 0x004d /* Maxim */
+#define IDT_MANID 0x00b3 /* IDT */
+#define MCP_MANID 0x0054 /* Microchip */
+#define NXP_MANID 0x1131 /* NXP Semiconductors */
+#define ONS_MANID 0x1b09 /* ON Semiconductor */
+#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 */
+
+/* Supported chips */
+
+/* Analog Devices */
+#define ADT7408_DEVID 0x0801
+#define ADT7408_DEVID_MASK 0xffff
+
+/* Atmel */
+#define AT30TS00_DEVID 0x8201
+#define AT30TS00_DEVID_MASK 0xffff
+
+#define AT30TSE004_DEVID 0x2200
+#define AT30TSE004_DEVID_MASK 0xffff
+
+/* Giantec */
+#define GT30TS00_DEVID 0x2200
+#define GT30TS00_DEVID_MASK 0xff00
+
+#define GT34TS02_DEVID 0x3300
+#define GT34TS02_DEVID_MASK 0xff00
+
+/* IDT */
+#define TSE2004_DEVID 0x2200
+#define TSE2004_DEVID_MASK 0xff00
+
+#define TS3000_DEVID 0x2900 /* Also matches TSE2002 */
+#define TS3000_DEVID_MASK 0xff00
+
+#define TS3001_DEVID 0x3000
+#define TS3001_DEVID_MASK 0xff00
+
+/* Maxim */
+#define MAX6604_DEVID 0x3e00
+#define MAX6604_DEVID_MASK 0xffff
+
+/* Microchip */
+#define MCP9804_DEVID 0x0200
+#define MCP9804_DEVID_MASK 0xfffc
+
+#define MCP9808_DEVID 0x0400
+#define MCP9808_DEVID_MASK 0xfffc
+
+#define MCP98242_DEVID 0x2000
+#define MCP98242_DEVID_MASK 0xfffc
+
+#define MCP98243_DEVID 0x2100
+#define MCP98243_DEVID_MASK 0xfffc
+
+#define MCP98244_DEVID 0x2200
+#define MCP98244_DEVID_MASK 0xfffc
+
+#define MCP9843_DEVID 0x0000 /* Also matches mcp9805 */
+#define MCP9843_DEVID_MASK 0xfffe
+
+/* NXP */
+#define SE97_DEVID 0xa200
+#define SE97_DEVID_MASK 0xfffc
+
+#define SE98_DEVID 0xa100
+#define SE98_DEVID_MASK 0xfffc
+
+/* ON Semiconductor */
+#define CAT6095_DEVID 0x0800 /* Also matches CAT34TS02 */
+#define CAT6095_DEVID_MASK 0xffe0
+
+#define CAT34TS02C_DEVID 0x0a00
+#define CAT34TS02C_DEVID_MASK 0xfff0
+
+#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
+
+#define STTS424E_DEVID 0x0000
+#define STTS424E_DEVID_MASK 0xfffe
+
+#define STTS2002_DEVID 0x0300
+#define STTS2002_DEVID_MASK 0xffff
+
+#define STTS2004_DEVID 0x2201
+#define STTS2004_DEVID_MASK 0xffff
+
+#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 {
+ u16 manid;
+ u16 devid;
+ u16 devid_mask;
+};
+
+static struct jc42_chips jc42_chips[] = {
+ { ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
+ { ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK },
+ { ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK },
+ { GT_MANID, GT30TS00_DEVID, GT30TS00_DEVID_MASK },
+ { GT_MANID2, GT34TS02_DEVID, GT34TS02_DEVID_MASK },
+ { IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
+ { IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK },
+ { IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK },
+ { MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK },
+ { MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK },
+ { MCP_MANID, MCP9808_DEVID, MCP9808_DEVID_MASK },
+ { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK },
+ { MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK },
+ { MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK },
+ { MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK },
+ { NXP_MANID, SE97_DEVID, SE97_DEVID_MASK },
+ { 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 },
+ { STM_MANID, STTS2004_DEVID, STTS2004_DEVID_MASK },
+ { STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK },
+};
+
+/* Each client has this additional data */
+struct jc42_data {
+ struct mutex update_lock; /* protect register access */
+ struct regmap *regmap;
+ bool extended; /* true if extended range supported */
+ bool valid;
+ u16 orig_config; /* original configuration */
+ u16 config; /* current configuration */
+};
+
+#define JC42_TEMP_MIN_EXTENDED (-40000)
+#define JC42_TEMP_MIN 0
+#define JC42_TEMP_MAX 125000
+
+static u16 jc42_temp_to_reg(long temp, bool extended)
+{
+ int ntemp = clamp_val(temp,
+ extended ? JC42_TEMP_MIN_EXTENDED :
+ JC42_TEMP_MIN, JC42_TEMP_MAX);
+
+ /* convert from 0.001 to 0.0625 resolution */
+ return (ntemp * 2 / 125) & 0x1fff;
+}
+
+static int jc42_temp_from_reg(s16 reg)
+{
+ reg = sign_extend32(reg, 12);
+
+ /* convert from 0.0625 to 0.001 resolution */
+ return reg * 125 / 2;
+}
+
+static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct jc42_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int ret, temp, hyst;
+
+ mutex_lock(&data->update_lock);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+ if (ret)
+ break;
+
+ *val = jc42_temp_from_reg(regval);
+ break;
+ case hwmon_temp_min:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP_LOWER, &regval);
+ if (ret)
+ break;
+
+ *val = jc42_temp_from_reg(regval);
+ break;
+ case hwmon_temp_max:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
+ if (ret)
+ break;
+
+ *val = jc42_temp_from_reg(regval);
+ break;
+ case hwmon_temp_crit:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
+ &regval);
+ if (ret)
+ break;
+
+ *val = jc42_temp_from_reg(regval);
+ break;
+ case hwmon_temp_max_hyst:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
+ if (ret)
+ break;
+
+ temp = jc42_temp_from_reg(regval);
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
+ *val = temp - hyst;
+ break;
+ case hwmon_temp_crit_hyst:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
+ &regval);
+ if (ret)
+ break;
+
+ temp = jc42_temp_from_reg(regval);
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
+ *val = temp - hyst;
+ break;
+ case hwmon_temp_min_alarm:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+ if (ret)
+ break;
+
+ *val = (regval >> JC42_ALARM_MIN_BIT) & 1;
+ break;
+ case hwmon_temp_max_alarm:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+ if (ret)
+ break;
+
+ *val = (regval >> JC42_ALARM_MAX_BIT) & 1;
+ break;
+ case hwmon_temp_crit_alarm:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+ if (ret)
+ break;
+
+ *val = (regval >> JC42_ALARM_CRIT_BIT) & 1;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct jc42_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int diff, hyst;
+ int ret;
+
+ mutex_lock(&data->update_lock);
+
+ switch (attr) {
+ case hwmon_temp_min:
+ ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER,
+ jc42_temp_to_reg(val, data->extended));
+ break;
+ case hwmon_temp_max:
+ ret = regmap_write(data->regmap, JC42_REG_TEMP_UPPER,
+ jc42_temp_to_reg(val, data->extended));
+ break;
+ case hwmon_temp_crit:
+ ret = regmap_write(data->regmap, JC42_REG_TEMP_CRITICAL,
+ jc42_temp_to_reg(val, data->extended));
+ break;
+ case hwmon_temp_crit_hyst:
+ ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
+ &regval);
+ if (ret)
+ break;
+
+ /*
+ * JC42.4 compliant chips only support four hysteresis values.
+ * Pick best choice and go from there.
+ */
+ val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED
+ : JC42_TEMP_MIN) - 6000,
+ JC42_TEMP_MAX);
+ diff = jc42_temp_from_reg(regval) - val;
+ hyst = 0;
+ if (diff > 0) {
+ if (diff < 2250)
+ hyst = 1; /* 1.5 degrees C */
+ else if (diff < 4500)
+ hyst = 2; /* 3.0 degrees C */
+ else
+ hyst = 3; /* 6.0 degrees C */
+ }
+ data->config = (data->config & ~JC42_CFG_HYST_MASK) |
+ (hyst << JC42_CFG_HYST_SHIFT);
+ ret = regmap_write(data->regmap, JC42_REG_CONFIG,
+ data->config);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static umode_t jc42_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct jc42_data *data = _data;
+ unsigned int config = data->config;
+ umode_t mode = 0444;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ if (!(config & JC42_CFG_EVENT_LOCK))
+ mode |= 0200;
+ break;
+ case hwmon_temp_crit:
+ if (!(config & JC42_CFG_TCRIT_LOCK))
+ mode |= 0200;
+ break;
+ case hwmon_temp_crit_hyst:
+ if (!(config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK)))
+ mode |= 0200;
+ break;
+ case hwmon_temp_input:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ break;
+ default:
+ mode = 0;
+ break;
+ }
+ return mode;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int i, config, cap, manid, devid;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP);
+ config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG);
+ manid = i2c_smbus_read_word_swapped(client, JC42_REG_MANID);
+ devid = i2c_smbus_read_word_swapped(client, JC42_REG_DEVICEID);
+
+ if (cap < 0 || config < 0 || manid < 0 || devid < 0)
+ return -ENODEV;
+
+ if ((cap & 0xff00) || (config & 0xf800))
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(jc42_chips); i++) {
+ struct jc42_chips *chip = &jc42_chips[i];
+ if (manid == chip->manid &&
+ (devid & chip->devid_mask) == chip->devid) {
+ strscpy(info->type, "jc42", I2C_NAME_SIZE);
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+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 |
+ HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM),
+ NULL
+};
+
+static const struct hwmon_ops jc42_hwmon_ops = {
+ .is_visible = jc42_is_visible,
+ .read = jc42_read,
+ .write = jc42_write,
+};
+
+static const struct hwmon_chip_info jc42_chip_info = {
+ .ops = &jc42_hwmon_ops,
+ .info = jc42_info,
+};
+
+static bool jc42_readable_reg(struct device *dev, unsigned int reg)
+{
+ return (reg >= JC42_REG_CAP && reg <= JC42_REG_DEVICEID) ||
+ reg == JC42_REG_SMBUS;
+}
+
+static bool jc42_writable_reg(struct device *dev, unsigned int reg)
+{
+ return (reg >= JC42_REG_CONFIG && reg <= JC42_REG_TEMP_CRITICAL) ||
+ reg == JC42_REG_SMBUS;
+}
+
+static bool jc42_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg == JC42_REG_CONFIG || reg == JC42_REG_TEMP;
+}
+
+static const struct regmap_config jc42_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = JC42_REG_SMBUS,
+ .writeable_reg = jc42_writable_reg,
+ .readable_reg = jc42_readable_reg,
+ .volatile_reg = jc42_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int jc42_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ unsigned int config, cap;
+ struct jc42_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &jc42_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ ret = regmap_read(data->regmap, JC42_REG_CAP, &cap);
+ if (ret)
+ return ret;
+
+ data->extended = !!(cap & JC42_CAP_RANGE);
+
+ if (device_property_read_bool(dev, "smbus-timeout-disable")) {
+ /*
+ * Not all chips support this register, but from a
+ * quick read of various datasheets no chip appears
+ * incompatible with the below attempt to disable
+ * the timeout. And the whole thing is opt-in...
+ */
+ ret = regmap_set_bits(data->regmap, JC42_REG_SMBUS,
+ SMBUS_STMOUT);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_read(data->regmap, JC42_REG_CONFIG, &config);
+ if (ret)
+ return ret;
+
+ data->orig_config = config;
+ if (config & JC42_CFG_SHUTDOWN) {
+ config &= ~JC42_CFG_SHUTDOWN;
+ regmap_write(data->regmap, JC42_REG_CONFIG, config);
+ }
+ data->config = config;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "jc42",
+ data, &jc42_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static void jc42_remove(struct i2c_client *client)
+{
+ struct jc42_data *data = i2c_get_clientdata(client);
+
+ /* Restore original configuration except hysteresis */
+ if ((data->config & ~JC42_CFG_HYST_MASK) !=
+ (data->orig_config & ~JC42_CFG_HYST_MASK)) {
+ int config;
+
+ config = (data->orig_config & ~JC42_CFG_HYST_MASK)
+ | (data->config & JC42_CFG_HYST_MASK);
+ regmap_write(data->regmap, JC42_REG_CONFIG, config);
+ }
+}
+
+#ifdef CONFIG_PM
+
+static int jc42_suspend(struct device *dev)
+{
+ struct jc42_data *data = dev_get_drvdata(dev);
+
+ data->config |= JC42_CFG_SHUTDOWN;
+ regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
+
+ regcache_cache_only(data->regmap, true);
+ regcache_mark_dirty(data->regmap);
+
+ return 0;
+}
+
+static int jc42_resume(struct device *dev)
+{
+ struct jc42_data *data = dev_get_drvdata(dev);
+
+ regcache_cache_only(data->regmap, false);
+
+ data->config &= ~JC42_CFG_SHUTDOWN;
+ regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
+
+ /* Restore cached register values to hardware */
+ return regcache_sync(data->regmap);
+}
+
+static const struct dev_pm_ops jc42_dev_pm_ops = {
+ .suspend = jc42_suspend,
+ .resume = jc42_resume,
+};
+
+#define JC42_DEV_PM_OPS (&jc42_dev_pm_ops)
+#else
+#define JC42_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id jc42_id[] = {
+ { "jc42", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, jc42_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id jc42_of_ids[] = {
+ { .compatible = "jedec,jc-42.4-temp", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, jc42_of_ids);
+#endif
+
+static struct i2c_driver jc42_driver = {
+ .class = I2C_CLASS_SPD | I2C_CLASS_HWMON,
+ .driver = {
+ .name = "jc42",
+ .pm = JC42_DEV_PM_OPS,
+ .of_match_table = of_match_ptr(jc42_of_ids),
+ },
+ .probe_new = jc42_probe,
+ .remove = jc42_remove,
+ .id_table = jc42_id,
+ .detect = jc42_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(jc42_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("JC42 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
new file mode 100644
index 000000000..43aa955ec
--- /dev/null
+++ b/drivers/hwmon/k10temp.c
@@ -0,0 +1,535 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h/17h
+ * processor hardware monitoring
+ *
+ * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2020 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Implementation notes:
+ * - CCD register address information as well as the calculation to
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <asm/amd_nb.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL");
+
+static bool force;
+module_param(force, bool, 0444);
+MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
+
+/* Provide lock for writing to NB_SMU_IND_ADDR */
+static DEFINE_MUTEX(nb_smu_ind_mutex);
+
+#ifndef PCI_DEVICE_ID_AMD_15H_M70H_NB_F3
+#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F3 0x15b3
+#endif
+
+/* CPUID function 0x80000001, ebx */
+#define CPUID_PKGTYPE_MASK GENMASK(31, 28)
+#define CPUID_PKGTYPE_F 0x00000000
+#define CPUID_PKGTYPE_AM2R2_AM3 0x10000000
+
+/* DRAM controller (PCI function 2) */
+#define REG_DCT0_CONFIG_HIGH 0x094
+#define DDR3_MODE BIT(8)
+
+/* miscellaneous (PCI function 3) */
+#define REG_HARDWARE_THERMAL_CONTROL 0x64
+#define HTC_ENABLE BIT(0)
+
+#define REG_REPORTED_TEMPERATURE 0xa4
+
+#define REG_NORTHBRIDGE_CAPABILITIES 0xe8
+#define NB_CAP_HTC BIT(10)
+
+/*
+ * For F15h M60h and M70h, REG_HARDWARE_THERMAL_CONTROL
+ * and REG_REPORTED_TEMPERATURE have been moved to
+ * D0F0xBC_xD820_0C64 [Hardware Temperature Control]
+ * D0F0xBC_xD820_0CA4 [Reported Temperature Control]
+ */
+#define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET 0xd8200c64
+#define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4
+
+/* Common for Zen CPU families (Family 17h and 18h and 19h) */
+#define ZEN_REPORTED_TEMP_CTRL_BASE 0x00059800
+
+#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 ZEN_CUR_TEMP_SHIFT 21
+#define ZEN_CUR_TEMP_RANGE_SEL_MASK BIT(19)
+#define ZEN_CUR_TEMP_TJ_SEL_MASK GENMASK(17, 16)
+
+/*
+ * AMD's Industrial processor 3255 supports temperature from -40 deg to 105 deg Celsius.
+ * Use the model name to identify 3255 CPUs and set a flag to display negative temperature.
+ * Do not round off to zero for negative Tctl or Tdie values if the flag is set
+ */
+#define AMD_I3255_STR "3255"
+
+struct k10temp_data {
+ struct pci_dev *pdev;
+ void (*read_htcreg)(struct pci_dev *pdev, u32 *regval);
+ void (*read_tempreg)(struct pci_dev *pdev, u32 *regval);
+ int temp_offset;
+ u32 temp_adjust_mask;
+ u32 show_temp;
+ bool is_zen;
+ u32 ccd_offset;
+ bool disp_negative;
+};
+
+#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;
+ int offset;
+};
+
+static const struct tctl_offset tctl_offset_table[] = {
+ { 0x17, "AMD Ryzen 5 1600X", 20000 },
+ { 0x17, "AMD Ryzen 7 1700X", 20000 },
+ { 0x17, "AMD Ryzen 7 1800X", 20000 },
+ { 0x17, "AMD Ryzen 7 2700X", 10000 },
+ { 0x17, "AMD Ryzen Threadripper 19", 27000 }, /* 19{00,20,50}X */
+ { 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
+};
+
+static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
+{
+ pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval);
+}
+
+static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval)
+{
+ pci_read_config_dword(pdev, REG_REPORTED_TEMPERATURE, regval);
+}
+
+static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn,
+ unsigned int base, int offset, u32 *val)
+{
+ mutex_lock(&nb_smu_ind_mutex);
+ pci_bus_write_config_dword(pdev->bus, devfn,
+ base, offset);
+ pci_bus_read_config_dword(pdev->bus, devfn,
+ base + 4, val);
+ mutex_unlock(&nb_smu_ind_mutex);
+}
+
+static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval)
+{
+ amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
+ F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET, regval);
+}
+
+static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval)
+{
+ amd_nb_index_read(pdev, PCI_DEVFN(0, 0), 0xb8,
+ F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval);
+}
+
+static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval)
+{
+ amd_smn_read(amd_pci_dev_to_node_id(pdev),
+ ZEN_REPORTED_TEMP_CTRL_BASE, regval);
+}
+
+static long get_raw_temp(struct k10temp_data *data)
+{
+ u32 regval;
+ long temp;
+
+ data->read_tempreg(data->pdev, &regval);
+ temp = (regval >> ZEN_CUR_TEMP_SHIFT) * 125;
+ if ((regval & data->temp_adjust_mask) ||
+ (regval & ZEN_CUR_TEMP_TJ_SEL_MASK) == ZEN_CUR_TEMP_TJ_SEL_MASK)
+ temp -= 49000;
+ return temp;
+}
+
+static const char *k10temp_temp_label[] = {
+ "Tctl",
+ "Tdie",
+ "Tccd1",
+ "Tccd2",
+ "Tccd3",
+ "Tccd4",
+ "Tccd5",
+ "Tccd6",
+ "Tccd7",
+ "Tccd8",
+ "Tccd9",
+ "Tccd10",
+ "Tccd11",
+ "Tccd12",
+};
+
+static int k10temp_read_labels(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ switch (type) {
+ case hwmon_temp:
+ *str = k10temp_temp_label[channel];
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int k10temp_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct k10temp_data *data = dev_get_drvdata(dev);
+ u32 regval;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ switch (channel) {
+ case 0: /* Tctl */
+ *val = get_raw_temp(data);
+ if (*val < 0 && !data->disp_negative)
+ *val = 0;
+ break;
+ case 1: /* Tdie */
+ *val = get_raw_temp(data) - data->temp_offset;
+ if (*val < 0 && !data->disp_negative)
+ *val = 0;
+ break;
+ case 2 ... 13: /* Tccd{1-12} */
+ amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
+ ZEN_CCD_TEMP(data->ccd_offset, channel - 2),
+ &regval);
+ *val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+ case hwmon_temp_max:
+ *val = 70 * 1000;
+ break;
+ case hwmon_temp_crit:
+ data->read_htcreg(data->pdev, &regval);
+ *val = ((regval >> 16) & 0x7f) * 500 + 52000;
+ break;
+ case hwmon_temp_crit_hyst:
+ data->read_htcreg(data->pdev, &regval);
+ *val = (((regval >> 16) & 0x7f)
+ - ((regval >> 24) & 0xf)) * 500 + 52000;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_temp:
+ return k10temp_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t k10temp_is_visible(const void *_data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct k10temp_data *data = _data;
+ struct pci_dev *pdev = data->pdev;
+ u32 reg;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ if (!HAVE_TEMP(data, channel))
+ return 0;
+ break;
+ case hwmon_temp_max:
+ if (channel || data->is_zen)
+ return 0;
+ break;
+ case hwmon_temp_crit:
+ case hwmon_temp_crit_hyst:
+ if (channel || !data->read_htcreg)
+ return 0;
+
+ pci_read_config_dword(pdev,
+ REG_NORTHBRIDGE_CAPABILITIES,
+ &reg);
+ if (!(reg & NB_CAP_HTC))
+ return 0;
+
+ data->read_htcreg(data->pdev, &reg);
+ if (!(reg & HTC_ENABLE))
+ return 0;
+ break;
+ case hwmon_temp_label:
+ /* Show temperature labels only on Zen CPUs */
+ if (!data->is_zen || !HAVE_TEMP(data, channel))
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+ return 0444;
+}
+
+static bool has_erratum_319(struct pci_dev *pdev)
+{
+ u32 pkg_type, reg_dram_cfg;
+
+ if (boot_cpu_data.x86 != 0x10)
+ return false;
+
+ /*
+ * Erratum 319: The thermal sensor of Socket F/AM2+ processors
+ * may be unreliable.
+ */
+ pkg_type = cpuid_ebx(0x80000001) & CPUID_PKGTYPE_MASK;
+ if (pkg_type == CPUID_PKGTYPE_F)
+ return true;
+ if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3)
+ return false;
+
+ /* DDR3 memory implies socket AM3, which is good */
+ pci_bus_read_config_dword(pdev->bus,
+ PCI_DEVFN(PCI_SLOT(pdev->devfn), 2),
+ REG_DCT0_CONFIG_HIGH, &reg_dram_cfg);
+ if (reg_dram_cfg & DDR3_MODE)
+ return false;
+
+ /*
+ * Unfortunately it is possible to run a socket AM3 CPU with DDR2
+ * memory. We blacklist all the cores which do exist in socket AM2+
+ * format. It still isn't perfect, as RB-C2 cores exist in both AM2+
+ * and AM3 formats, but that's the best we can do.
+ */
+ return boot_cpu_data.x86_model < 4 ||
+ (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2);
+}
+
+static const struct hwmon_channel_info *k10temp_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST |
+ 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),
+ NULL
+};
+
+static const struct hwmon_ops k10temp_hwmon_ops = {
+ .is_visible = k10temp_is_visible,
+ .read = k10temp_read,
+ .read_string = k10temp_read_labels,
+};
+
+static const struct hwmon_chip_info k10temp_chip_info = {
+ .ops = &k10temp_hwmon_ops,
+ .info = k10temp_info,
+};
+
+static void k10temp_get_ccd_support(struct pci_dev *pdev,
+ struct k10temp_data *data, int limit)
+{
+ u32 regval;
+ int i;
+
+ for (i = 0; i < limit; i++) {
+ amd_smn_read(amd_pci_dev_to_node_id(pdev),
+ ZEN_CCD_TEMP(data->ccd_offset, i), &regval);
+ if (regval & ZEN_CCD_TEMP_VALID)
+ data->show_temp |= BIT(TCCD_BIT(i));
+ }
+}
+
+static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int unreliable = has_erratum_319(pdev);
+ struct device *dev = &pdev->dev;
+ struct k10temp_data *data;
+ struct device *hwmon_dev;
+ int i;
+
+ if (unreliable) {
+ if (!force) {
+ dev_err(dev,
+ "unreliable CPU thermal sensor; monitoring disabled\n");
+ return -ENODEV;
+ }
+ dev_warn(dev,
+ "unreliable CPU thermal sensor; check erratum 319\n");
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->pdev = pdev;
+ data->show_temp |= BIT(TCTL_BIT); /* Always show Tctl */
+
+ if (boot_cpu_data.x86 == 0x17 &&
+ strstr(boot_cpu_data.x86_model_id, AMD_I3255_STR)) {
+ data->disp_negative = true;
+ }
+
+ if (boot_cpu_data.x86 == 0x15 &&
+ ((boot_cpu_data.x86_model & 0xf0) == 0x60 ||
+ (boot_cpu_data.x86_model & 0xf0) == 0x70)) {
+ 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 = 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->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->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;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
+ const struct tctl_offset *entry = &tctl_offset_table[i];
+
+ 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;
+ }
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data,
+ &k10temp_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct pci_device_id k10temp_id_table[] = {
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
+ { 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(AMD, PCI_DEVICE_ID_AMD_19H_M78H_DF_F3) },
+ { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, k10temp_id_table);
+
+static struct pci_driver k10temp_driver = {
+ .name = "k10temp",
+ .id_table = k10temp_id_table,
+ .probe = k10temp_probe,
+};
+
+module_pci_driver(k10temp_driver);
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c
new file mode 100644
index 000000000..f73bd4ece
--- /dev/null
+++ b/drivers/hwmon/k8temp.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * k8temp.c - Linux kernel module for hardware monitoring
+ *
+ * Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * Inspired from the w83785 and amd756 drivers.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <asm/processor.h>
+
+#define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000)
+#define REG_TEMP 0xe4
+#define SEL_PLACE 0x40
+#define SEL_CORE 0x04
+
+struct k8temp_data {
+ struct mutex update_lock;
+
+ /* registers values */
+ u8 sensorsp; /* sensor presence bits - SEL_CORE, SEL_PLACE */
+ u8 swap_core_select; /* meaning of SEL_CORE is inverted */
+ u32 temp_offset;
+};
+
+static const struct pci_device_id k8temp_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, k8temp_ids);
+
+static int is_rev_g_desktop(u8 model)
+{
+ u32 brandidx;
+
+ if (model < 0x69)
+ return 0;
+
+ if (model == 0xc1 || model == 0x6c || model == 0x7c)
+ return 0;
+
+ /*
+ * Differentiate between AM2 and ASB1.
+ * See "Constructing the processor Name String" in "Revision
+ * Guide for AMD NPT Family 0Fh Processors" (33610).
+ */
+ brandidx = cpuid_ebx(0x80000001);
+ brandidx = (brandidx >> 9) & 0x1f;
+
+ /* Single core */
+ if ((model == 0x6f || model == 0x7f) &&
+ (brandidx == 0x7 || brandidx == 0x9 || brandidx == 0xc))
+ return 0;
+
+ /* Dual core */
+ if (model == 0x6b &&
+ (brandidx == 0xb || brandidx == 0xc))
+ return 0;
+
+ return 1;
+}
+
+static umode_t
+k8temp_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct k8temp_data *data = drvdata;
+
+ if ((channel & 1) && !(data->sensorsp & SEL_PLACE))
+ return 0;
+
+ if ((channel & 2) && !(data->sensorsp & SEL_CORE))
+ return 0;
+
+ return 0444;
+}
+
+static int
+k8temp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct k8temp_data *data = dev_get_drvdata(dev);
+ struct pci_dev *pdev = to_pci_dev(dev->parent);
+ int core, place;
+ u32 temp;
+ u8 tmp;
+
+ core = (channel >> 1) & 1;
+ place = channel & 1;
+
+ core ^= data->swap_core_select;
+
+ mutex_lock(&data->update_lock);
+ pci_read_config_byte(pdev, REG_TEMP, &tmp);
+ tmp &= ~(SEL_PLACE | SEL_CORE);
+ if (core)
+ tmp |= SEL_CORE;
+ if (place)
+ tmp |= SEL_PLACE;
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP, &temp);
+ mutex_unlock(&data->update_lock);
+
+ *val = TEMP_FROM_REG(temp) + data->temp_offset;
+
+ return 0;
+}
+
+static const struct hwmon_ops k8temp_ops = {
+ .is_visible = k8temp_is_visible,
+ .read = k8temp_read,
+};
+
+static const struct hwmon_channel_info *k8temp_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_chip_info k8temp_chip_info = {
+ .ops = &k8temp_ops,
+ .info = k8temp_info,
+};
+
+static int k8temp_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ u8 scfg;
+ u32 temp;
+ u8 model, stepping;
+ struct k8temp_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct k8temp_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ model = boot_cpu_data.x86_model;
+ stepping = boot_cpu_data.x86_stepping;
+
+ /* feature available since SH-C0, exclude older revisions */
+ if ((model == 4 && stepping == 0) ||
+ (model == 5 && stepping <= 1))
+ return -ENODEV;
+
+ /*
+ * AMD NPT family 0fh, i.e. RevF and RevG:
+ * meaning of SEL_CORE bit is inverted
+ */
+ if (model >= 0x40) {
+ data->swap_core_select = 1;
+ dev_warn(&pdev->dev,
+ "Temperature readouts might be wrong - check erratum #141\n");
+ }
+
+ /*
+ * RevG desktop CPUs (i.e. no socket S1G1 or ASB1 parts) need
+ * additional offset, otherwise reported temperature is below
+ * ambient temperature
+ */
+ if (is_rev_g_desktop(model))
+ data->temp_offset = 21000;
+
+ pci_read_config_byte(pdev, REG_TEMP, &scfg);
+ scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_byte(pdev, REG_TEMP, &scfg);
+
+ if (scfg & (SEL_PLACE | SEL_CORE)) {
+ dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n");
+ return -ENODEV;
+ }
+
+ scfg |= (SEL_PLACE | SEL_CORE);
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+
+ /* now we know if we can change core and/or sensor */
+ pci_read_config_byte(pdev, REG_TEMP, &data->sensorsp);
+
+ if (data->sensorsp & SEL_PLACE) {
+ scfg &= ~SEL_CORE; /* Select sensor 1, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_dword(pdev, REG_TEMP, &temp);
+ scfg |= SEL_CORE; /* prepare for next selection */
+ if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */
+ data->sensorsp &= ~SEL_PLACE;
+ }
+
+ if (data->sensorsp & SEL_CORE) {
+ scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_dword(pdev, REG_TEMP, &temp);
+ if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */
+ data->sensorsp &= ~SEL_CORE;
+ }
+
+ mutex_init(&data->update_lock);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+ "k8temp",
+ data,
+ &k8temp_chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct pci_driver k8temp_driver = {
+ .name = "k8temp",
+ .id_table = k8temp_ids,
+ .probe = k8temp_probe,
+};
+
+module_pci_driver(k8temp_driver);
+
+MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
+MODULE_DESCRIPTION("AMD K8 core temperature monitor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lan966x-hwmon.c b/drivers/hwmon/lan966x-hwmon.c
new file mode 100644
index 000000000..f41df053a
--- /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, &regmap_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
new file mode 100644
index 000000000..ef5a49cd9
--- /dev/null
+++ b/drivers/hwmon/lineage-pem.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Lineage Compact Power Line series of power entry modules.
+ *
+ * Copyright (C) 2010, 2011 Ericsson AB.
+ *
+ * Documentation:
+ * http://www.lineagepower.com/oem/pdf/CPLI2C.pdf
+ */
+
+#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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+
+/*
+ * This driver supports various Lineage Compact Power Line DC/DC and AC/DC
+ * converters such as CP1800, CP2000AC, CP2000DC, CP2100DC, and others.
+ *
+ * The devices are nominally PMBus compliant. However, most standard PMBus
+ * commands are not supported. Specifically, all hardware monitoring and
+ * status reporting commands are non-standard. For this reason, a standard
+ * PMBus driver can not be used.
+ *
+ * All Lineage CPL devices have a built-in I2C bus master selector (PCA9541).
+ * To ensure device access, this driver should only be used as client driver
+ * to the pca9541 I2C master selector driver.
+ */
+
+/* Command codes */
+#define PEM_OPERATION 0x01
+#define PEM_CLEAR_INFO_FLAGS 0x03
+#define PEM_VOUT_COMMAND 0x21
+#define PEM_VOUT_OV_FAULT_LIMIT 0x40
+#define PEM_READ_DATA_STRING 0xd0
+#define PEM_READ_INPUT_STRING 0xdc
+#define PEM_READ_FIRMWARE_REV 0xdd
+#define PEM_READ_RUN_TIMER 0xde
+#define PEM_FAN_HI_SPEED 0xdf
+#define PEM_FAN_NORMAL_SPEED 0xe0
+#define PEM_READ_FAN_SPEED 0xe1
+
+/* offsets in data string */
+#define PEM_DATA_STATUS_2 0
+#define PEM_DATA_STATUS_1 1
+#define PEM_DATA_ALARM_2 2
+#define PEM_DATA_ALARM_1 3
+#define PEM_DATA_VOUT_LSB 4
+#define PEM_DATA_VOUT_MSB 5
+#define PEM_DATA_CURRENT 6
+#define PEM_DATA_TEMP 7
+
+/* Virtual entries, to report constants */
+#define PEM_DATA_TEMP_MAX 10
+#define PEM_DATA_TEMP_CRIT 11
+
+/* offsets in input string */
+#define PEM_INPUT_VOLTAGE 0
+#define PEM_INPUT_POWER_LSB 1
+#define PEM_INPUT_POWER_MSB 2
+
+/* offsets in fan data */
+#define PEM_FAN_ADJUSTMENT 0
+#define PEM_FAN_FAN1 1
+#define PEM_FAN_FAN2 2
+#define PEM_FAN_FAN3 3
+
+/* Status register bits */
+#define STS1_OUTPUT_ON (1 << 0)
+#define STS1_LEDS_FLASHING (1 << 1)
+#define STS1_EXT_FAULT (1 << 2)
+#define STS1_SERVICE_LED_ON (1 << 3)
+#define STS1_SHUTDOWN_OCCURRED (1 << 4)
+#define STS1_INT_FAULT (1 << 5)
+#define STS1_ISOLATION_TEST_OK (1 << 6)
+
+#define STS2_ENABLE_PIN_HI (1 << 0)
+#define STS2_DATA_OUT_RANGE (1 << 1)
+#define STS2_RESTARTED_OK (1 << 1)
+#define STS2_ISOLATION_TEST_FAIL (1 << 3)
+#define STS2_HIGH_POWER_CAP (1 << 4)
+#define STS2_INVALID_INSTR (1 << 5)
+#define STS2_WILL_RESTART (1 << 6)
+#define STS2_PEC_ERR (1 << 7)
+
+/* Alarm register bits */
+#define ALRM1_VIN_OUT_LIMIT (1 << 0)
+#define ALRM1_VOUT_OUT_LIMIT (1 << 1)
+#define ALRM1_OV_VOLT_SHUTDOWN (1 << 2)
+#define ALRM1_VIN_OVERCURRENT (1 << 3)
+#define ALRM1_TEMP_WARNING (1 << 4)
+#define ALRM1_TEMP_SHUTDOWN (1 << 5)
+#define ALRM1_PRIMARY_FAULT (1 << 6)
+#define ALRM1_POWER_LIMIT (1 << 7)
+
+#define ALRM2_5V_OUT_LIMIT (1 << 1)
+#define ALRM2_TEMP_FAULT (1 << 2)
+#define ALRM2_OV_LOW (1 << 3)
+#define ALRM2_DCDC_TEMP_HIGH (1 << 4)
+#define ALRM2_PRI_TEMP_HIGH (1 << 5)
+#define ALRM2_NO_PRIMARY (1 << 6)
+#define ALRM2_FAN_FAULT (1 << 7)
+
+#define FIRMWARE_REV_LEN 4
+#define DATA_STRING_LEN 9
+#define INPUT_STRING_LEN 5 /* 4 for most devices */
+#define FAN_SPEED_LEN 5
+
+struct pem_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[4];
+
+ struct mutex update_lock;
+ bool valid;
+ bool fans_supported;
+ int input_length;
+ unsigned long last_updated; /* in jiffies */
+
+ u8 firmware_rev[FIRMWARE_REV_LEN];
+ u8 data_string[DATA_STRING_LEN];
+ u8 input_string[INPUT_STRING_LEN];
+ u8 fan_speed[FAN_SPEED_LEN];
+};
+
+static int pem_read_block(struct i2c_client *client, u8 command, u8 *data,
+ int data_len)
+{
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX];
+ int result;
+
+ result = i2c_smbus_read_block_data(client, command, block_buffer);
+ if (unlikely(result < 0))
+ goto abort;
+ if (unlikely(result == 0xff || result != data_len)) {
+ result = -EIO;
+ goto abort;
+ }
+ memcpy(data, block_buffer, data_len);
+ result = 0;
+abort:
+ return result;
+}
+
+static struct pem_data *pem_update_device(struct device *dev)
+{
+ struct pem_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct pem_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ int result;
+
+ /* Read data string */
+ result = pem_read_block(client, PEM_READ_DATA_STRING,
+ data->data_string,
+ sizeof(data->data_string));
+ if (unlikely(result < 0)) {
+ ret = ERR_PTR(result);
+ goto abort;
+ }
+
+ /* Read input string */
+ if (data->input_length) {
+ result = pem_read_block(client, PEM_READ_INPUT_STRING,
+ data->input_string,
+ data->input_length);
+ if (unlikely(result < 0)) {
+ ret = ERR_PTR(result);
+ goto abort;
+ }
+ }
+
+ /* Read fan speeds */
+ if (data->fans_supported) {
+ result = pem_read_block(client, PEM_READ_FAN_SPEED,
+ data->fan_speed,
+ sizeof(data->fan_speed));
+ if (unlikely(result < 0)) {
+ ret = ERR_PTR(result);
+ goto abort;
+ }
+ }
+
+ i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static long pem_get_data(u8 *data, int len, int index)
+{
+ long val;
+
+ switch (index) {
+ case PEM_DATA_VOUT_LSB:
+ val = (data[index] + (data[index+1] << 8)) * 5 / 2;
+ break;
+ case PEM_DATA_CURRENT:
+ val = data[index] * 200;
+ break;
+ case PEM_DATA_TEMP:
+ val = data[index] * 1000;
+ break;
+ case PEM_DATA_TEMP_MAX:
+ val = 97 * 1000; /* 97 degrees C per datasheet */
+ break;
+ case PEM_DATA_TEMP_CRIT:
+ val = 107 * 1000; /* 107 degrees C per datasheet */
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ val = 0;
+ }
+ return val;
+}
+
+static long pem_get_input(u8 *data, int len, int index)
+{
+ long val;
+
+ switch (index) {
+ case PEM_INPUT_VOLTAGE:
+ if (len == INPUT_STRING_LEN)
+ val = (data[index] + (data[index+1] << 8) - 75) * 1000;
+ else
+ val = (data[index] - 75) * 1000;
+ break;
+ case PEM_INPUT_POWER_LSB:
+ if (len == INPUT_STRING_LEN)
+ index++;
+ val = (data[index] + (data[index+1] << 8)) * 1000000L;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ val = 0;
+ }
+ return val;
+}
+
+static long pem_get_fan(u8 *data, int len, int index)
+{
+ long val;
+
+ switch (index) {
+ case PEM_FAN_FAN1:
+ case PEM_FAN_FAN2:
+ case PEM_FAN_FAN3:
+ val = data[index] * 100;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ val = 0;
+ }
+ return val;
+}
+
+/*
+ * Show boolean, either a fault or an alarm.
+ * .nr points to the register, .index is the bit mask to check
+ */
+static ssize_t pem_bool_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
+ struct pem_data *data = pem_update_device(dev);
+ u8 status;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ status = data->data_string[attr->nr] & attr->index;
+ return sysfs_emit(buf, "%d\n", !!status);
+}
+
+static ssize_t pem_data_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct pem_data *data = pem_update_device(dev);
+ long value;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ value = pem_get_data(data->data_string, sizeof(data->data_string),
+ attr->index);
+
+ return sysfs_emit(buf, "%ld\n", value);
+}
+
+static ssize_t pem_input_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct pem_data *data = pem_update_device(dev);
+ long value;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ value = pem_get_input(data->input_string, sizeof(data->input_string),
+ attr->index);
+
+ return sysfs_emit(buf, "%ld\n", value);
+}
+
+static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct pem_data *data = pem_update_device(dev);
+ long value;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed),
+ attr->index);
+
+ return sysfs_emit(buf, "%ld\n", value);
+}
+
+/* Voltages */
+static SENSOR_DEVICE_ATTR_RO(in1_input, pem_data, PEM_DATA_VOUT_LSB);
+static SENSOR_DEVICE_ATTR_2_RO(in1_alarm, pem_bool, PEM_DATA_ALARM_1,
+ ALRM1_VOUT_OUT_LIMIT);
+static SENSOR_DEVICE_ATTR_2_RO(in1_crit_alarm, pem_bool, PEM_DATA_ALARM_1,
+ ALRM1_OV_VOLT_SHUTDOWN);
+static SENSOR_DEVICE_ATTR_RO(in2_input, pem_input, PEM_INPUT_VOLTAGE);
+static SENSOR_DEVICE_ATTR_2_RO(in2_alarm, pem_bool, PEM_DATA_ALARM_1,
+ ALRM1_VIN_OUT_LIMIT | ALRM1_PRIMARY_FAULT);
+
+/* Currents */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, pem_data, PEM_DATA_CURRENT);
+static SENSOR_DEVICE_ATTR_2_RO(curr1_alarm, pem_bool, PEM_DATA_ALARM_1,
+ ALRM1_VIN_OVERCURRENT);
+
+/* Power */
+static SENSOR_DEVICE_ATTR_RO(power1_input, pem_input, PEM_INPUT_POWER_LSB);
+static SENSOR_DEVICE_ATTR_2_RO(power1_alarm, pem_bool, PEM_DATA_ALARM_1,
+ ALRM1_POWER_LIMIT);
+
+/* Fans */
+static SENSOR_DEVICE_ATTR_RO(fan1_input, pem_fan, PEM_FAN_FAN1);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, pem_fan, PEM_FAN_FAN2);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, pem_fan, PEM_FAN_FAN3);
+static SENSOR_DEVICE_ATTR_2_RO(fan1_alarm, pem_bool, PEM_DATA_ALARM_2,
+ ALRM2_FAN_FAULT);
+
+/* Temperatures */
+static SENSOR_DEVICE_ATTR_RO(temp1_input, pem_data, PEM_DATA_TEMP);
+static SENSOR_DEVICE_ATTR_RO(temp1_max, pem_data, PEM_DATA_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit, pem_data, PEM_DATA_TEMP_CRIT);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_alarm, pem_bool, PEM_DATA_ALARM_1,
+ ALRM1_TEMP_WARNING);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_crit_alarm, pem_bool, PEM_DATA_ALARM_1,
+ ALRM1_TEMP_SHUTDOWN);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_fault, pem_bool, PEM_DATA_ALARM_2,
+ ALRM2_TEMP_FAULT);
+
+static struct attribute *pem_attributes[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_power1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+
+ NULL,
+};
+
+static const struct attribute_group pem_group = {
+ .attrs = pem_attributes,
+};
+
+static struct attribute *pem_input_attributes[] = {
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_power1_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group pem_input_group = {
+ .attrs = pem_input_attributes,
+};
+
+static struct attribute *pem_fan_attributes[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group pem_fan_group = {
+ .attrs = pem_fan_attributes,
+};
+
+static int pem_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct pem_data *data;
+ int ret, idx = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_DATA
+ | I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /*
+ * We use the next two commands to determine if the device is really
+ * there.
+ */
+ ret = pem_read_block(client, PEM_READ_FIRMWARE_REV,
+ data->firmware_rev, sizeof(data->firmware_rev));
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS);
+ if (ret < 0)
+ return ret;
+
+ dev_info(dev, "Firmware revision %d.%d.%d\n",
+ data->firmware_rev[0], data->firmware_rev[1],
+ data->firmware_rev[2]);
+
+ /* sysfs hooks */
+ data->groups[idx++] = &pem_group;
+
+ /*
+ * Check if input readings are supported.
+ * This is the case if we can read input data,
+ * and if the returned data is not all zeros.
+ * Note that input alarms are always supported.
+ */
+ ret = pem_read_block(client, PEM_READ_INPUT_STRING,
+ data->input_string,
+ sizeof(data->input_string) - 1);
+ if (!ret && (data->input_string[0] || data->input_string[1] ||
+ data->input_string[2]))
+ data->input_length = sizeof(data->input_string) - 1;
+ else if (ret < 0) {
+ /* Input string is one byte longer for some devices */
+ ret = pem_read_block(client, PEM_READ_INPUT_STRING,
+ data->input_string,
+ sizeof(data->input_string));
+ if (!ret && (data->input_string[0] || data->input_string[1] ||
+ data->input_string[2] || data->input_string[3]))
+ data->input_length = sizeof(data->input_string);
+ }
+
+ if (data->input_length)
+ data->groups[idx++] = &pem_input_group;
+
+ /*
+ * Check if fan speed readings are supported.
+ * This is the case if we can read fan speed data,
+ * and if the returned data is not all zeros.
+ * Note that the fan alarm is always supported.
+ */
+ ret = pem_read_block(client, PEM_READ_FAN_SPEED,
+ data->fan_speed,
+ sizeof(data->fan_speed));
+ if (!ret && (data->fan_speed[0] || data->fan_speed[1] ||
+ data->fan_speed[2] || data->fan_speed[3])) {
+ data->fans_supported = true;
+ data->groups[idx++] = &pem_fan_group;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id pem_id[] = {
+ {"lineage_pem", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pem_id);
+
+static struct i2c_driver pem_driver = {
+ .driver = {
+ .name = "lineage_pem",
+ },
+ .probe_new = pem_probe,
+ .id_table = pem_id,
+};
+
+module_i2c_driver(pem_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("Lineage CPL PEM hardware monitoring driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
new file mode 100644
index 000000000..9ab2cab4c
--- /dev/null
+++ b/drivers/hwmon/lm63.c
@@ -0,0 +1,1177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm63.c - driver for the National Semiconductor LM63 temperature sensor
+ * with integrated fan control
+ * Copyright (C) 2004-2008 Jean Delvare <jdelvare@suse.de>
+ * Based on the lm90 driver.
+ *
+ * The LM63 is a sensor chip made by National Semiconductor. It measures
+ * two temperatures (its own and one external one) and the speed of one
+ * fan, those speed it can additionally control. Complete datasheet can be
+ * obtained from National's website at:
+ * http://www.national.com/pf/LM/LM63.html
+ *
+ * The LM63 is basically an LM86 with fan speed monitoring and control
+ * capabilities added. It misses some of the LM86 features though:
+ * - No low limit for local temperature.
+ * - No critical limit for local temperature.
+ * - Critical limit for remote temperature can be changed only once. We
+ * will consider that the critical limit is read-only.
+ *
+ * The datasheet isn't very clear about what the tachometer reading is.
+ * I had a explanation from National Semiconductor though. The two lower
+ * bits of the read value have to be masked out. The value is still 16 bit
+ * in width.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+/*
+ * Addresses to scan
+ * Address is fully defined internally and cannot be changed except for
+ * LM64 which has one pin dedicated to address selection.
+ * LM63 and LM96163 have address 0x4c.
+ * LM64 can have address 0x18 or 0x4e.
+ */
+
+static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
+
+/*
+ * The LM63 registers
+ */
+
+#define LM63_REG_CONFIG1 0x03
+#define LM63_REG_CONVRATE 0x04
+#define LM63_REG_CONFIG2 0xBF
+#define LM63_REG_CONFIG_FAN 0x4A
+
+#define LM63_REG_TACH_COUNT_MSB 0x47
+#define LM63_REG_TACH_COUNT_LSB 0x46
+#define LM63_REG_TACH_LIMIT_MSB 0x49
+#define LM63_REG_TACH_LIMIT_LSB 0x48
+
+#define LM63_REG_PWM_VALUE 0x4C
+#define LM63_REG_PWM_FREQ 0x4D
+#define LM63_REG_LUT_TEMP_HYST 0x4F
+#define LM63_REG_LUT_TEMP(nr) (0x50 + 2 * (nr))
+#define LM63_REG_LUT_PWM(nr) (0x51 + 2 * (nr))
+
+#define LM63_REG_LOCAL_TEMP 0x00
+#define LM63_REG_LOCAL_HIGH 0x05
+
+#define LM63_REG_REMOTE_TEMP_MSB 0x01
+#define LM63_REG_REMOTE_TEMP_LSB 0x10
+#define LM63_REG_REMOTE_OFFSET_MSB 0x11
+#define LM63_REG_REMOTE_OFFSET_LSB 0x12
+#define LM63_REG_REMOTE_HIGH_MSB 0x07
+#define LM63_REG_REMOTE_HIGH_LSB 0x13
+#define LM63_REG_REMOTE_LOW_MSB 0x08
+#define LM63_REG_REMOTE_LOW_LSB 0x14
+#define LM63_REG_REMOTE_TCRIT 0x19
+#define LM63_REG_REMOTE_TCRIT_HYST 0x21
+
+#define LM63_REG_ALERT_STATUS 0x02
+#define LM63_REG_ALERT_MASK 0x16
+
+#define LM63_REG_MAN_ID 0xFE
+#define LM63_REG_CHIP_ID 0xFF
+
+#define LM96163_REG_TRUTHERM 0x30
+#define LM96163_REG_REMOTE_TEMP_U_MSB 0x31
+#define LM96163_REG_REMOTE_TEMP_U_LSB 0x32
+#define LM96163_REG_CONFIG_ENHANCED 0x45
+
+#define LM63_MAX_CONVRATE 9
+
+#define LM63_MAX_CONVRATE_HZ 32
+#define LM96163_MAX_CONVRATE_HZ 26
+
+/*
+ * Conversions and various macros
+ * For tachometer counts, the LM63 uses 16-bit values.
+ * For local temperature and high limit, remote critical limit and hysteresis
+ * value, it uses signed 8-bit values with LSB = 1 degree Celsius.
+ * For remote temperature, low and high limits, it uses signed 11-bit values
+ * with LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
+ * For LM64 the actual remote diode temperature is 16 degree Celsius higher
+ * than the register reading. Remote temperature setpoints have to be
+ * adapted accordingly.
+ */
+
+#define FAN_FROM_REG(reg) ((reg) == 0xFFFC || (reg) == 0 ? 0 : \
+ 5400000 / (reg))
+#define FAN_TO_REG(val) ((val) <= 82 ? 0xFFFC : \
+ (5400000 / (val)) & 0xFFFC)
+#define TEMP8_FROM_REG(reg) ((reg) * 1000)
+#define TEMP8_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
+ 127000), 1000)
+#define TEMP8U_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), 0, \
+ 255000), 1000)
+#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
+#define TEMP11_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
+ 127875), 125) * 32)
+#define TEMP11U_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), 0, \
+ 255875), 125) * 32)
+#define HYST_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), 0, 127000), \
+ 1000)
+
+#define UPDATE_INTERVAL(max, rate) \
+ ((1000 << (LM63_MAX_CONVRATE - (rate))) / (max))
+
+enum chips { lm63, lm64, lm96163 };
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm63_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ const struct attribute_group *groups[5];
+ 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 */
+ enum chips kind;
+ int temp2_offset;
+
+ int update_interval; /* in milliseconds */
+ int max_convrate_hz;
+ int lut_size; /* 8 or 12 */
+
+ /* registers values */
+ u8 config, config_fan;
+ u16 fan[2]; /* 0: input
+ 1: low limit */
+ u8 pwm1_freq;
+ u8 pwm1[13]; /* 0: current output
+ 1-12: lookup table */
+ s8 temp8[15]; /* 0: local input
+ 1: local high limit
+ 2: remote critical limit
+ 3-14: lookup table */
+ s16 temp11[4]; /* 0: remote input
+ 1: remote low limit
+ 2: remote high limit
+ 3: remote offset */
+ u16 temp11u; /* remote input (unsigned) */
+ u8 temp2_crit_hyst;
+ u8 lut_temp_hyst;
+ u8 alarms;
+ bool pwm_highres;
+ bool lut_temp_highres;
+ bool remote_unsigned; /* true if unsigned remote upper limits */
+ bool trutherm;
+};
+
+static inline int temp8_from_reg(struct lm63_data *data, int nr)
+{
+ if (data->remote_unsigned)
+ return TEMP8_FROM_REG((u8)data->temp8[nr]);
+ return TEMP8_FROM_REG(data->temp8[nr]);
+}
+
+static inline int lut_temp_from_reg(struct lm63_data *data, int nr)
+{
+ return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000);
+}
+
+static inline int lut_temp_to_reg(struct lm63_data *data, long val)
+{
+ val -= data->temp2_offset;
+ if (data->lut_temp_highres)
+ return DIV_ROUND_CLOSEST(clamp_val(val, 0, 127500), 500);
+ else
+ return DIV_ROUND_CLOSEST(clamp_val(val, 0, 127000), 1000);
+}
+
+/*
+ * Update the lookup table register cache.
+ * client->update_lock must be held when calling this function.
+ */
+static void lm63_update_lut(struct lm63_data *data)
+{
+ struct i2c_client *client = data->client;
+ int i;
+
+ if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
+ !data->lut_valid) {
+ for (i = 0; i < data->lut_size; i++) {
+ data->pwm1[1 + i] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LUT_PWM(i));
+ data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LUT_TEMP(i));
+ }
+ data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
+ LM63_REG_LUT_TEMP_HYST);
+
+ data->lut_last_updated = jiffies;
+ data->lut_valid = 1;
+ }
+}
+
+static struct lm63_data *lm63_update_device(struct device *dev)
+{
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long next_update;
+
+ 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->config & 0x04) { /* tachometer enabled */
+ /* order matters for fan1_input */
+ data->fan[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_COUNT_LSB) & 0xFC;
+ data->fan[0] |= i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_COUNT_MSB) << 8;
+ data->fan[1] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_LIMIT_LSB) & 0xFC)
+ | (i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_LIMIT_MSB) << 8);
+ }
+
+ data->pwm1_freq = i2c_smbus_read_byte_data(client,
+ LM63_REG_PWM_FREQ);
+ if (data->pwm1_freq == 0)
+ data->pwm1_freq = 1;
+ data->pwm1[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_PWM_VALUE);
+
+ data->temp8[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LOCAL_TEMP);
+ data->temp8[1] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LOCAL_HIGH);
+
+ /* order matters for temp2_input */
+ data->temp11[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TEMP_MSB) << 8;
+ data->temp11[0] |= i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TEMP_LSB);
+ data->temp11[1] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_LOW_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_LOW_LSB);
+ data->temp11[2] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_HIGH_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_HIGH_LSB);
+ data->temp11[3] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_OFFSET_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_OFFSET_LSB);
+
+ if (data->kind == lm96163)
+ data->temp11u = (i2c_smbus_read_byte_data(client,
+ LM96163_REG_REMOTE_TEMP_U_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM96163_REG_REMOTE_TEMP_U_LSB);
+
+ data->temp8[2] = i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TCRIT);
+ data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TCRIT_HYST);
+
+ data->alarms = i2c_smbus_read_byte_data(client,
+ LM63_REG_ALERT_STATUS) & 0x7F;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ lm63_update_lut(data);
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Trip points in the lookup table should be in ascending order for both
+ * temperatures and PWM output values.
+ */
+static int lm63_lut_looks_bad(struct device *dev, struct lm63_data *data)
+{
+ int i;
+
+ mutex_lock(&data->update_lock);
+ lm63_update_lut(data);
+
+ for (i = 1; i < data->lut_size; i++) {
+ if (data->pwm1[1 + i - 1] > data->pwm1[1 + i]
+ || data->temp8[3 + i - 1] > data->temp8[3 + i]) {
+ dev_warn(dev,
+ "Lookup table doesn't look sane (check entries %d and %d)\n",
+ i, i + 1);
+ break;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+
+ return i == data->lut_size ? 0 : 1;
+}
+
+/*
+ * Sysfs callback functions and files
+ */
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index]));
+}
+
+static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
+ const char *buf, size_t count)
+{
+ struct lm63_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->fan[1] = FAN_TO_REG(val);
+ i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB,
+ data->fan[1] & 0xFF);
+ i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB,
+ data->fan[1] >> 8);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ int nr = attr->index;
+ int pwm;
+
+ if (data->pwm_highres)
+ pwm = data->pwm1[nr];
+ else
+ pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ?
+ 255 : (data->pwm1[nr] * 255 + data->pwm1_freq) /
+ (2 * data->pwm1_freq);
+
+ return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+ u8 reg;
+
+ if (!(data->config_fan & 0x20)) /* register is read-only */
+ return -EPERM;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE;
+ val = clamp_val(val, 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->pwm1[nr] = data->pwm_highres ? val :
+ (val * data->pwm1_freq * 2 + 127) / 255;
+ i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *dummy, char *buf)
+{
+ struct lm63_data *data = lm63_update_device(dev);
+ return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *dummy,
+ const char *buf, size_t count)
+{
+ struct lm63_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;
+ if (val < 1 || val > 2)
+ return -EINVAL;
+
+ /*
+ * Only let the user switch to automatic mode if the lookup table
+ * looks sane.
+ */
+ if (val == 2 && lm63_lut_looks_bad(dev, data))
+ return -EPERM;
+
+ mutex_lock(&data->update_lock);
+ data->config_fan = i2c_smbus_read_byte_data(client,
+ LM63_REG_CONFIG_FAN);
+ if (val == 1)
+ data->config_fan |= 0x20;
+ else
+ data->config_fan &= ~0x20;
+ i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
+ data->config_fan);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * There are 8bit registers for both local(temp1) and remote(temp2) sensor.
+ * For remote sensor registers temp2_offset has to be considered,
+ * for local sensor it must not.
+ * So we need separate 8bit accessors for local and remote sensor.
+ */
+static ssize_t show_local_temp8(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index]));
+}
+
+static ssize_t show_remote_temp8(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ return sprintf(buf, "%d\n", temp8_from_reg(data, attr->index)
+ + data->temp2_offset);
+}
+
+static ssize_t show_lut_temp(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index)
+ + data->temp2_offset);
+}
+
+static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = attr->index;
+ long val;
+ int err;
+ int temp;
+ u8 reg;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (nr) {
+ case 2:
+ reg = LM63_REG_REMOTE_TCRIT;
+ if (data->remote_unsigned)
+ temp = TEMP8U_TO_REG(val - data->temp2_offset);
+ else
+ temp = TEMP8_TO_REG(val - data->temp2_offset);
+ break;
+ case 1:
+ reg = LM63_REG_LOCAL_HIGH;
+ temp = TEMP8_TO_REG(val);
+ break;
+ default: /* lookup table */
+ reg = LM63_REG_LUT_TEMP(nr - 3);
+ temp = lut_temp_to_reg(data, val);
+ }
+ data->temp8[nr] = temp;
+ i2c_smbus_write_byte_data(client, reg, temp);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ int nr = attr->index;
+ int temp;
+
+ if (!nr) {
+ /*
+ * Use unsigned temperature unless its value is zero.
+ * If it is zero, use signed temperature.
+ */
+ if (data->temp11u)
+ temp = TEMP11_FROM_REG(data->temp11u);
+ else
+ temp = TEMP11_FROM_REG(data->temp11[nr]);
+ } else {
+ if (data->remote_unsigned && nr == 2)
+ temp = TEMP11_FROM_REG((u16)data->temp11[nr]);
+ else
+ temp = TEMP11_FROM_REG(data->temp11[nr]);
+ }
+ return sprintf(buf, "%d\n", temp + data->temp2_offset);
+}
+
+static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ static const u8 reg[6] = {
+ LM63_REG_REMOTE_LOW_MSB,
+ LM63_REG_REMOTE_LOW_LSB,
+ LM63_REG_REMOTE_HIGH_MSB,
+ LM63_REG_REMOTE_HIGH_LSB,
+ LM63_REG_REMOTE_OFFSET_MSB,
+ LM63_REG_REMOTE_OFFSET_LSB,
+ };
+
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+ int nr = attr->index;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if (data->remote_unsigned && nr == 2)
+ data->temp11[nr] = TEMP11U_TO_REG(val - data->temp2_offset);
+ else
+ data->temp11[nr] = TEMP11_TO_REG(val - data->temp2_offset);
+
+ i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
+ data->temp11[nr] >> 8);
+ i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
+ data->temp11[nr] & 0xff);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Hysteresis register holds a relative value, while we want to present
+ * an absolute to user-space
+ */
+static ssize_t temp2_crit_hyst_show(struct device *dev,
+ struct device_attribute *dummy, char *buf)
+{
+ struct lm63_data *data = lm63_update_device(dev);
+ return sprintf(buf, "%d\n", temp8_from_reg(data, 2)
+ + data->temp2_offset
+ - TEMP8_FROM_REG(data->temp2_crit_hyst));
+}
+
+static ssize_t show_lut_temp_hyst(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+
+ return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index)
+ + data->temp2_offset
+ - TEMP8_FROM_REG(data->lut_temp_hyst));
+}
+
+/*
+ * And now the other way around, user-space provides an absolute
+ * hysteresis value and we have to store a relative one
+ */
+static ssize_t temp2_crit_hyst_store(struct device *dev,
+ struct device_attribute *dummy,
+ const char *buf, size_t count)
+{
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+ long hyst;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ hyst = temp8_from_reg(data, 2) + data->temp2_offset - val;
+ i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
+ HYST_TO_REG(hyst));
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Set conversion rate.
+ * client->update_lock must be held when calling this function.
+ */
+static void lm63_set_convrate(struct lm63_data *data, unsigned int interval)
+{
+ struct i2c_client *client = data->client;
+ unsigned int update_interval;
+ int i;
+
+ /* Shift calculations to avoid rounding errors */
+ interval <<= 6;
+
+ /* find the nearest update rate */
+ update_interval = (1 << (LM63_MAX_CONVRATE + 6)) * 1000
+ / data->max_convrate_hz;
+ for (i = 0; i < LM63_MAX_CONVRATE; i++, update_interval >>= 1)
+ if (interval >= update_interval * 3 / 4)
+ break;
+
+ i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i);
+ data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i);
+}
+
+static ssize_t update_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm63_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->update_interval);
+}
+
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm63_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);
+ lm63_set_convrate(data, clamp_val(val, 0, 100000));
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t temp2_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm63_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, data->trutherm ? "1\n" : "2\n");
+}
+
+static ssize_t temp2_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm63_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+ int ret;
+ u8 reg;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+ if (val != 1 && val != 2)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->trutherm = val == 1;
+ 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 = false;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *dummy,
+ char *buf)
+{
+ struct lm63_data *data = lm63_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
+ set_fan, 1);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
+static DEVICE_ATTR_RW(pwm1_enable);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 1);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 3);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 3);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 2);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 3);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 13);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 13);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 14);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO,
+ show_lut_temp_hyst, NULL, 14);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8,
+ set_temp8, 1);
+
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
+ set_temp11, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
+ set_temp11, 2);
+static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
+ set_temp11, 3);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_remote_temp8,
+ set_temp8, 2);
+static DEVICE_ATTR_RW(temp2_crit_hyst);
+
+static DEVICE_ATTR_RW(temp2_type);
+
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+/* Raw alarm file for compatibility */
+static DEVICE_ATTR_RO(alarms);
+
+static DEVICE_ATTR_RW(update_interval);
+
+static struct attribute *lm63_attributes[] = {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &dev_attr_pwm1_enable.attr,
+ &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_point1_temp_hyst.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_point2_temp_hyst.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_point3_temp_hyst.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_point4_temp_hyst.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_point5_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point6_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point7_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point8_temp_hyst.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &dev_attr_temp2_crit_hyst.attr,
+
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_update_interval.attr,
+ NULL
+};
+
+static struct attribute *lm63_attributes_temp2_type[] = {
+ &dev_attr_temp2_type.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group_temp2_type = {
+ .attrs = lm63_attributes_temp2_type,
+};
+
+static struct attribute *lm63_attributes_extra_lut[] = {
+ &sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point9_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point10_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point10_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point10_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point11_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point11_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point11_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point12_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point12_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point12_temp_hyst.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group_extra_lut = {
+ .attrs = lm63_attributes_extra_lut,
+};
+
+/*
+ * On LM63, temp2_crit can be set only once, which should be job
+ * of the bootloader.
+ * On LM64, temp2_crit can always be set.
+ * On LM96163, temp2_crit can be set if bit 1 of the configuration
+ * register is true.
+ */
+static umode_t lm63_attribute_mode(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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
+ && (data->kind == lm64 ||
+ (data->kind == lm96163 && (data->config & 0x02))))
+ return attr->mode | S_IWUSR;
+
+ return attr->mode;
+}
+
+static const struct attribute_group lm63_group = {
+ .is_visible = lm63_attribute_mode,
+ .attrs = lm63_attributes,
+};
+
+static struct attribute *lm63_attributes_fan1[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group_fan1 = {
+ .attrs = lm63_attributes_fan1,
+};
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm63_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u8 man_id, chip_id, reg_config1, reg_config2;
+ u8 reg_alert_status, reg_alert_mask;
+ int address = client->addr;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ man_id = i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID);
+
+ reg_config1 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
+ reg_config2 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2);
+ reg_alert_status = i2c_smbus_read_byte_data(client,
+ LM63_REG_ALERT_STATUS);
+ reg_alert_mask = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK);
+
+ if (man_id != 0x01 /* National Semiconductor */
+ || (reg_config1 & 0x18) != 0x00
+ || (reg_config2 & 0xF8) != 0x00
+ || (reg_alert_status & 0x20) != 0x00
+ || (reg_alert_mask & 0xA4) != 0xA4) {
+ dev_dbg(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
+ }
+
+ if (chip_id == 0x41 && address == 0x4c)
+ strscpy(info->type, "lm63", I2C_NAME_SIZE);
+ else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e))
+ strscpy(info->type, "lm64", I2C_NAME_SIZE);
+ else if (chip_id == 0x49 && address == 0x4c)
+ strscpy(info->type, "lm96163", I2C_NAME_SIZE);
+ else
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ * Ideally we shouldn't have to initialize anything, since the BIOS
+ * should have taken care of everything
+ */
+static void lm63_init_client(struct lm63_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct device *dev = &client->dev;
+ u8 convrate;
+
+ data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
+ data->config_fan = i2c_smbus_read_byte_data(client,
+ LM63_REG_CONFIG_FAN);
+
+ /* Start converting if needed */
+ if (data->config & 0x40) { /* standby */
+ dev_dbg(dev, "Switching to operational mode\n");
+ data->config &= 0xA7;
+ i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
+ data->config);
+ }
+ /* Tachometer is always enabled on LM64 */
+ if (data->kind == lm64)
+ data->config |= 0x04;
+
+ /* We may need pwm1_freq before ever updating the client data */
+ data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
+ if (data->pwm1_freq == 0)
+ data->pwm1_freq = 1;
+
+ switch (data->kind) {
+ case lm63:
+ case lm64:
+ data->max_convrate_hz = LM63_MAX_CONVRATE_HZ;
+ data->lut_size = 8;
+ break;
+ case lm96163:
+ data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ;
+ data->lut_size = 12;
+ data->trutherm
+ = i2c_smbus_read_byte_data(client,
+ LM96163_REG_TRUTHERM) & 0x02;
+ break;
+ }
+ convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE);
+ if (unlikely(convrate > LM63_MAX_CONVRATE))
+ convrate = LM63_MAX_CONVRATE;
+ data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz,
+ convrate);
+
+ /*
+ * For LM96163, check if high resolution PWM
+ * and unsigned temperature format is enabled.
+ */
+ if (data->kind == lm96163) {
+ u8 config_enhanced
+ = i2c_smbus_read_byte_data(client,
+ LM96163_REG_CONFIG_ENHANCED);
+ if (config_enhanced & 0x20)
+ data->lut_temp_highres = true;
+ if ((config_enhanced & 0x10)
+ && !(data->config_fan & 0x08) && data->pwm1_freq == 8)
+ data->pwm_highres = true;
+ if (config_enhanced & 0x08)
+ data->remote_unsigned = true;
+ }
+
+ /* Show some debug info about the LM63 configuration */
+ if (data->kind == lm63)
+ dev_dbg(dev, "Alert/tach pin configured for %s\n",
+ (data->config & 0x04) ? "tachometer input" :
+ "alert output");
+ dev_dbg(dev, "PWM clock %s kHz, output frequency %u Hz\n",
+ (data->config_fan & 0x08) ? "1.4" : "360",
+ ((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq);
+ dev_dbg(dev, "PWM output active %s, %s mode\n",
+ (data->config_fan & 0x10) ? "low" : "high",
+ (data->config_fan & 0x20) ? "manual" : "auto");
+}
+
+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;
+ struct lm63_data *data;
+ int groups = 0;
+
+ data = devm_kzalloc(dev, sizeof(struct lm63_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Set the device type */
+ if (client->dev.of_node)
+ data->kind = (enum chips)of_device_get_match_data(&client->dev);
+ else
+ data->kind = i2c_match_id(lm63_id, client)->driver_data;
+ if (data->kind == lm64)
+ data->temp2_offset = 16000;
+
+ /* Initialize chip */
+ lm63_init_client(data);
+
+ /* Register sysfs hooks */
+ data->groups[groups++] = &lm63_group;
+ if (data->config & 0x04) /* tachometer enabled */
+ data->groups[groups++] = &lm63_group_fan1;
+
+ if (data->kind == lm96163) {
+ data->groups[groups++] = &lm63_group_temp2_type;
+ data->groups[groups++] = &lm63_group_extra_lut;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id lm63_id[] = {
+ { "lm63", lm63 },
+ { "lm64", lm64 },
+ { "lm96163", lm96163 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm63_id);
+
+static const struct of_device_id __maybe_unused lm63_of_match[] = {
+ {
+ .compatible = "national,lm63",
+ .data = (void *)lm63
+ },
+ {
+ .compatible = "national,lm64",
+ .data = (void *)lm64
+ },
+ {
+ .compatible = "national,lm96163",
+ .data = (void *)lm96163
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lm63_of_match);
+
+static struct i2c_driver lm63_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm63",
+ .of_match_table = of_match_ptr(lm63_of_match),
+ },
+ .probe_new = lm63_probe,
+ .id_table = lm63_id,
+ .detect = lm63_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm63_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("LM63 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c
new file mode 100644
index 000000000..c20a749fc
--- /dev/null
+++ b/drivers/hwmon/lm70.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm70.c
+ *
+ * The LM70 is a temperature sensor chip from National Semiconductor (NS).
+ * Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com>
+ *
+ * The LM70 communicates with a host processor via an SPI/Microwire Bus
+ * interface. The complete datasheet is available at National's website
+ * here:
+ * http://www.national.com/pf/LM/LM70.html
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#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>
+
+#define DRVNAME "lm70"
+
+#define LM70_CHIP_LM70 0 /* original NS LM70 */
+#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */
+#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;
+ struct mutex lock;
+ unsigned int chip;
+};
+
+/* sysfs hook function */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm70 *p_lm70 = dev_get_drvdata(dev);
+ struct spi_device *spi = p_lm70->spi;
+ int status, val = 0;
+ u8 rxbuf[2];
+ s16 raw = 0;
+
+ if (mutex_lock_interruptible(&p_lm70->lock))
+ return -ERESTARTSYS;
+
+ /*
+ * spi_read() requires a DMA-safe buffer; so we use
+ * spi_write_then_read(), transmitting 0 bytes.
+ */
+ status = spi_write_then_read(spi, NULL, 0, &rxbuf[0], 2);
+ if (status < 0) {
+ dev_warn(dev, "spi_write_then_read failed with status %d\n",
+ status);
+ goto out;
+ }
+ raw = (rxbuf[0] << 8) + rxbuf[1];
+ dev_dbg(dev, "rxbuf[0] : 0x%02x rxbuf[1] : 0x%02x raw=0x%04x\n",
+ rxbuf[0], rxbuf[1], raw);
+
+ /*
+ * LM70:
+ * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's
+ * complement value. Only the MSB 11 bits (1 sign + 10 temperature
+ * bits) are meaningful; the LSB 5 bits are to be discarded.
+ * See the datasheet.
+ *
+ * Further, each bit represents 0.25 degrees Celsius; so, multiply
+ * by 0.25. Also multiply by 1000 to represent in millidegrees
+ * Celsius.
+ * So it's equivalent to multiplying by 0.25 * 1000 = 250.
+ *
+ * LM74 and TMP121/TMP122/TMP123/TMP124:
+ * 13 bits of 2's complement data, discard LSB 3 bits,
+ * resolution 0.0625 degrees celsius.
+ *
+ * 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:
+ val = ((int)raw / 32) * 250;
+ break;
+
+ case LM70_CHIP_TMP121:
+ case LM70_CHIP_TMP122:
+ case LM70_CHIP_LM74:
+ val = ((int)raw / 8) * 625 / 10;
+ break;
+
+ 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 */
+out:
+ mutex_unlock(&p_lm70->lock);
+ return status;
+}
+
+static DEVICE_ATTR_RO(temp1_input);
+
+static struct attribute *lm70_attrs[] = {
+ &dev_attr_temp1_input.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(lm70);
+
+/*----------------------------------------------------------------------*/
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm70_of_ids[] = {
+ {
+ .compatible = "ti,lm70",
+ .data = (void *) LM70_CHIP_LM70,
+ },
+ {
+ .compatible = "ti,tmp121",
+ .data = (void *) LM70_CHIP_TMP121,
+ },
+ {
+ .compatible = "ti,tmp122",
+ .data = (void *) LM70_CHIP_TMP122,
+ },
+ {
+ .compatible = "ti,tmp125",
+ .data = (void *) LM70_CHIP_TMP125,
+ },
+ {
+ .compatible = "ti,lm71",
+ .data = (void *) LM70_CHIP_LM71,
+ },
+ {
+ .compatible = "ti,lm74",
+ .data = (void *) LM70_CHIP_LM74,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lm70_of_ids);
+#endif
+
+static int lm70_probe(struct spi_device *spi)
+{
+ struct device *hwmon_dev;
+ struct lm70 *p_lm70;
+ int chip;
+
+ 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_MODE_X_MASK) != SPI_MODE_0)
+ return -EINVAL;
+
+ /* NOTE: we assume 8-bit words, and convert to 16 bits manually */
+
+ p_lm70 = devm_kzalloc(&spi->dev, sizeof(*p_lm70), GFP_KERNEL);
+ if (!p_lm70)
+ return -ENOMEM;
+
+ mutex_init(&p_lm70->lock);
+ p_lm70->chip = chip;
+ p_lm70->spi = spi;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&spi->dev,
+ spi->modalias,
+ p_lm70, lm70_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+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 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, lm70_ids);
+
+static struct spi_driver lm70_driver = {
+ .driver = {
+ .name = "lm70",
+ .of_match_table = of_match_ptr(lm70_of_ids),
+ },
+ .id_table = lm70_ids,
+ .probe = lm70_probe,
+};
+
+module_spi_driver(lm70_driver);
+
+MODULE_AUTHOR("Kaiwan N Billimoria");
+MODULE_DESCRIPTION("NS LM70 and compatibles Linux driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
new file mode 100644
index 000000000..1346b3b3f
--- /dev/null
+++ b/drivers/hwmon/lm73.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * LM73 Sensor driver
+ * Based on LM75
+ *
+ * Copyright (C) 2007, CenoSYS (www.cenosys.com).
+ * Copyright (C) 2009, Bollore telecom (www.bolloretelecom.eu).
+ *
+ * Guillaume Ligneul <guillaume.ligneul@gmail.com>
+ * Adrien Demarez <adrien.demarez@bolloretelecom.eu>
+ * Jeremy Laine <jeremy.laine@bolloretelecom.eu>
+ * Chris Verges <kg4ysn@gmail.com>
+ */
+
+#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>
+
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
+ 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* LM73 registers */
+#define LM73_REG_INPUT 0x00
+#define LM73_REG_CONF 0x01
+#define LM73_REG_MAX 0x02
+#define LM73_REG_MIN 0x03
+#define LM73_REG_CTRL 0x04
+#define LM73_REG_ID 0x07
+
+#define LM73_ID 0x9001 /* 0x0190, byte-swapped */
+#define DRVNAME "lm73"
+#define LM73_TEMP_MIN (-256000 / 250)
+#define LM73_TEMP_MAX (255750 / 250)
+
+#define LM73_CTRL_RES_SHIFT 5
+#define LM73_CTRL_RES_MASK (BIT(5) | BIT(6))
+#define LM73_CTRL_TO_MASK BIT(7)
+
+#define LM73_CTRL_HI_SHIFT 2
+#define LM73_CTRL_LO_SHIFT 1
+
+static const unsigned short lm73_convrates[] = {
+ 14, /* 11-bits (0.25000 C/LSB): RES1 Bit = 0, RES0 Bit = 0 */
+ 28, /* 12-bits (0.12500 C/LSB): RES1 Bit = 0, RES0 Bit = 1 */
+ 56, /* 13-bits (0.06250 C/LSB): RES1 Bit = 1, RES0 Bit = 0 */
+ 112, /* 14-bits (0.03125 C/LSB): RES1 Bit = 1, RES0 Bit = 1 */
+};
+
+struct lm73_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ u8 ctrl; /* control register value */
+};
+
+/*-----------------------------------------------------------------------*/
+
+static ssize_t temp_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 lm73_data *data = dev_get_drvdata(dev);
+ long temp;
+ short value;
+ s32 err;
+
+ int status = kstrtol(buf, 10, &temp);
+ if (status < 0)
+ return status;
+
+ /* Write value */
+ value = clamp_val(temp / 250, LM73_TEMP_MIN, LM73_TEMP_MAX) << 5;
+ err = i2c_smbus_write_word_swapped(data->client, attr->index, value);
+ return (err < 0) ? err : count;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm73_data *data = dev_get_drvdata(dev);
+ int temp;
+
+ s32 err = i2c_smbus_read_word_swapped(data->client, attr->index);
+ if (err < 0)
+ return err;
+
+ /* use integer division instead of equivalent right shift to
+ guarantee arithmetic shift and preserve the sign */
+ temp = (((s16) err) * 250) / 32;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", temp);
+}
+
+static ssize_t convrate_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct lm73_data *data = dev_get_drvdata(dev);
+ unsigned long convrate;
+ s32 err;
+ int res = 0;
+
+ err = kstrtoul(buf, 10, &convrate);
+ if (err < 0)
+ return err;
+
+ /*
+ * Convert the desired conversion rate into register bits.
+ * res is already initialized, and everything past the second-to-last
+ * value in the array is treated as belonging to the last value
+ * in the array.
+ */
+ while (res < (ARRAY_SIZE(lm73_convrates) - 1) &&
+ convrate > lm73_convrates[res])
+ res++;
+
+ mutex_lock(&data->lock);
+ data->ctrl &= LM73_CTRL_TO_MASK;
+ data->ctrl |= res << LM73_CTRL_RES_SHIFT;
+ err = i2c_smbus_write_byte_data(data->client, LM73_REG_CTRL,
+ data->ctrl);
+ mutex_unlock(&data->lock);
+
+ if (err < 0)
+ return err;
+
+ return count;
+}
+
+static ssize_t convrate_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct lm73_data *data = dev_get_drvdata(dev);
+ int res;
+
+ res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT;
+ return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]);
+}
+
+static ssize_t maxmin_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm73_data *data = dev_get_drvdata(dev);
+ s32 ctrl;
+
+ mutex_lock(&data->lock);
+ ctrl = i2c_smbus_read_byte_data(data->client, LM73_REG_CTRL);
+ if (ctrl < 0)
+ goto abort;
+ data->ctrl = ctrl;
+ mutex_unlock(&data->lock);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", (ctrl >> attr->index) & 1);
+
+abort:
+ mutex_unlock(&data->lock);
+ return ctrl;
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* sysfs attributes for hwmon */
+
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, LM73_REG_MAX);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, LM73_REG_MIN);
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, LM73_REG_INPUT);
+static SENSOR_DEVICE_ATTR_RW(update_interval, convrate, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, maxmin_alarm,
+ LM73_CTRL_HI_SHIFT);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, maxmin_alarm,
+ LM73_CTRL_LO_SHIFT);
+
+static struct attribute *lm73_attrs[] = {
+ &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_update_interval.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(lm73);
+
+/*-----------------------------------------------------------------------*/
+
+/* device probe and removal */
+
+static int
+lm73_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct lm73_data *data;
+ int ctrl;
+
+ data = devm_kzalloc(dev, sizeof(struct lm73_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+
+ ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL);
+ if (ctrl < 0)
+ return ctrl;
+ data->ctrl = ctrl;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, lm73_groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(dev, "sensor '%s'\n", client->name);
+
+ return 0;
+}
+
+static const struct i2c_device_id lm73_ids[] = {
+ { "lm73", 0 },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, lm73_ids);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm73_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ int id, ctrl, conf;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ /*
+ * Do as much detection as possible with byte reads first, as word
+ * reads can confuse other devices.
+ */
+ ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL);
+ if (ctrl < 0 || (ctrl & 0x10))
+ return -ENODEV;
+
+ conf = i2c_smbus_read_byte_data(new_client, LM73_REG_CONF);
+ if (conf < 0 || (conf & 0x0c))
+ return -ENODEV;
+
+ id = i2c_smbus_read_byte_data(new_client, LM73_REG_ID);
+ if (id < 0 || id != (LM73_ID & 0xff))
+ return -ENODEV;
+
+ /* Check device ID */
+ id = i2c_smbus_read_word_data(new_client, LM73_REG_ID);
+ if (id < 0 || id != LM73_ID)
+ return -ENODEV;
+
+ 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_new = lm73_probe,
+ .id_table = lm73_ids,
+ .detect = lm73_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm73_driver);
+
+MODULE_AUTHOR("Guillaume Ligneul <guillaume.ligneul@gmail.com>");
+MODULE_DESCRIPTION("LM73 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
new file mode 100644
index 000000000..bcc3adcb3
--- /dev/null
+++ b/drivers/hwmon/lm75.c
@@ -0,0 +1,958 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm75.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.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/of.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
+#include <linux/regulator/consumer.h>
+#include "lm75.h"
+
+/*
+ * This driver handles the LM75 and compatible digital temperature sensors.
+ */
+
+enum lm75_type { /* keep sorted in alphabetical order */
+ adt75,
+ at30ts74,
+ ds1775,
+ ds75,
+ ds7505,
+ g751,
+ lm75,
+ lm75a,
+ lm75b,
+ max6625,
+ max6626,
+ max31725,
+ mcp980x,
+ pct2075,
+ stds75,
+ stlm75,
+ tcn75,
+ tmp100,
+ tmp101,
+ tmp105,
+ tmp112,
+ tmp175,
+ tmp275,
+ tmp75,
+ tmp75b,
+ tmp75c,
+ tmp1075,
+};
+
+/**
+ * struct lm75_params - lm75 configuration parameters.
+ * @set_mask: Bits to set in configuration register when configuring
+ * the chip.
+ * @clr_mask: Bits to clear in configuration register when configuring
+ * the chip.
+ * @default_resolution: Default number of bits to represent the temperature
+ * value.
+ * @resolution_limits: Limit register resolution. Optional. Should be set if
+ * the resolution of limit registers does not match the
+ * resolution of the temperature register.
+ * @resolutions: List of resolutions associated with sample times.
+ * Optional. Should be set if num_sample_times is larger
+ * than 1, and if the resolution changes with sample times.
+ * If set, number of entries must match num_sample_times.
+ * @default_sample_time:Sample time to be set by default.
+ * @num_sample_times: Number of possible sample times to be set. Optional.
+ * Should be set if the number of sample times is larger
+ * than one.
+ * @sample_times: All the possible sample times to be set. Mandatory if
+ * num_sample_times is larger than 1. If set, number of
+ * entries must match num_sample_times.
+ */
+
+struct lm75_params {
+ u8 set_mask;
+ u8 clr_mask;
+ u8 default_resolution;
+ u8 resolution_limits;
+ const u8 *resolutions;
+ unsigned int default_sample_time;
+ u8 num_sample_times;
+ const unsigned int *sample_times;
+};
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+ 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
+
+/* The LM75 registers */
+#define LM75_REG_TEMP 0x00
+#define LM75_REG_CONF 0x01
+#define LM75_REG_HYST 0x02
+#define LM75_REG_MAX 0x03
+#define PCT2075_REG_IDLE 0x04
+
+/* Each client has this additional data */
+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 */
+ unsigned int sample_time; /* In ms */
+ enum lm75_type kind;
+ const struct lm75_params *params;
+};
+
+/*-----------------------------------------------------------------------*/
+
+static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 };
+
+#define LM75_SAMPLE_CLEAR_MASK (3 << 5)
+
+/* The structure below stores the configuration values of the supported devices.
+ * In case of being supported multiple configurations, the default one must
+ * always be the first element of the array
+ */
+static const struct lm75_params device_params[] = {
+ [adt75] = {
+ .clr_mask = 1 << 5, /* not one-shot mode */
+ .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 */
+ .default_resolution = 11,
+ .default_sample_time = 500,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 125, 250, 500, 1000 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [ds75] = {
+ .clr_mask = 3 << 5,
+ .set_mask = 2 << 5, /* 11-bit mode */
+ .default_resolution = 11,
+ .default_sample_time = 600,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 150, 300, 600, 1200 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [stds75] = {
+ .clr_mask = 3 << 5,
+ .set_mask = 2 << 5, /* 11-bit mode */
+ .default_resolution = 11,
+ .default_sample_time = 600,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 150, 300, 600, 1200 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [stlm75] = {
+ .default_resolution = 9,
+ .default_sample_time = MSEC_PER_SEC / 6,
+ },
+ [ds7505] = {
+ .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 },
+ },
+ [g751] = {
+ .default_resolution = 9,
+ .default_sample_time = MSEC_PER_SEC / 10,
+ },
+ [lm75] = {
+ .default_resolution = 9,
+ .default_sample_time = MSEC_PER_SEC / 10,
+ },
+ [lm75a] = {
+ .default_resolution = 9,
+ .default_sample_time = MSEC_PER_SEC / 10,
+ },
+ [lm75b] = {
+ .default_resolution = 11,
+ .default_sample_time = MSEC_PER_SEC / 10,
+ },
+ [max6625] = {
+ .default_resolution = 9,
+ .default_sample_time = MSEC_PER_SEC / 7,
+ },
+ [max6626] = {
+ .default_resolution = 12,
+ .default_sample_time = MSEC_PER_SEC / 7,
+ .resolution_limits = 9,
+ },
+ [max31725] = {
+ .default_resolution = 16,
+ .default_sample_time = MSEC_PER_SEC / 20,
+ },
+ [tcn75] = {
+ .default_resolution = 9,
+ .default_sample_time = MSEC_PER_SEC / 18,
+ },
+ [pct2075] = {
+ .default_resolution = 11,
+ .default_sample_time = MSEC_PER_SEC / 10,
+ .num_sample_times = 31,
+ .sample_times = (unsigned int []){ 100, 200, 300, 400, 500, 600,
+ 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700,
+ 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700,
+ 2800, 2900, 3000, 3100 },
+ },
+ [mcp980x] = {
+ .set_mask = 3 << 5, /* 12-bit mode */
+ .clr_mask = 1 << 7, /* not one-shot mode */
+ .default_resolution = 12,
+ .resolution_limits = 9,
+ .default_sample_time = 240,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 30, 60, 120, 240 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [tmp100] = {
+ .set_mask = 3 << 5, /* 12-bit mode */
+ .clr_mask = 1 << 7, /* not one-shot mode */
+ .default_resolution = 12,
+ .default_sample_time = 320,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 40, 80, 160, 320 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [tmp101] = {
+ .set_mask = 3 << 5, /* 12-bit mode */
+ .clr_mask = 1 << 7, /* not one-shot mode */
+ .default_resolution = 12,
+ .default_sample_time = 320,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 40, 80, 160, 320 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [tmp105] = {
+ .set_mask = 3 << 5, /* 12-bit mode */
+ .clr_mask = 1 << 7, /* not one-shot mode*/
+ .default_resolution = 12,
+ .default_sample_time = 220,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 28, 55, 110, 220 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [tmp112] = {
+ .set_mask = 3 << 5, /* 8 samples / second */
+ .clr_mask = 1 << 7, /* no one-shot mode*/
+ .default_resolution = 12,
+ .default_sample_time = 125,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 125, 250, 1000, 4000 },
+ },
+ [tmp175] = {
+ .set_mask = 3 << 5, /* 12-bit mode */
+ .clr_mask = 1 << 7, /* not one-shot mode*/
+ .default_resolution = 12,
+ .default_sample_time = 220,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 28, 55, 110, 220 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [tmp275] = {
+ .set_mask = 3 << 5, /* 12-bit mode */
+ .clr_mask = 1 << 7, /* not one-shot mode*/
+ .default_resolution = 12,
+ .default_sample_time = 220,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 28, 55, 110, 220 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [tmp75] = {
+ .set_mask = 3 << 5, /* 12-bit mode */
+ .clr_mask = 1 << 7, /* not one-shot mode*/
+ .default_resolution = 12,
+ .default_sample_time = 220,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 28, 55, 110, 220 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
+ [tmp75b] = { /* not one-shot mode, Conversion rate 37Hz */
+ .clr_mask = 1 << 7 | 3 << 5,
+ .default_resolution = 12,
+ .default_sample_time = MSEC_PER_SEC / 37,
+ .sample_times = (unsigned int []){ MSEC_PER_SEC / 37,
+ MSEC_PER_SEC / 18,
+ MSEC_PER_SEC / 9, MSEC_PER_SEC / 4 },
+ .num_sample_times = 4,
+ },
+ [tmp75c] = {
+ .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 },
+ }
+};
+
+static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
+{
+ return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
+}
+
+static int lm75_write_config(struct lm75_data *data, u8 set_mask,
+ u8 clr_mask)
+{
+ u8 value;
+
+ clr_mask |= LM75_SHUTDOWN;
+ value = data->current_conf & ~clr_mask;
+ value |= set_mask;
+
+ if (data->current_conf != value) {
+ s32 err;
+
+ err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF,
+ value);
+ if (err)
+ return err;
+ data->current_conf = value;
+ }
+ return 0;
+}
+
+static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct lm75_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err, reg;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->sample_time;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ reg = LM75_REG_TEMP;
+ break;
+ case hwmon_temp_max:
+ reg = LM75_REG_MAX;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = LM75_REG_HYST;
+ break;
+ default:
+ return -EINVAL;
+ }
+ err = regmap_read(data->regmap, reg, &regval);
+ if (err < 0)
+ return err;
+
+ *val = lm75_reg_to_mc(regval, data->resolution);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int lm75_write_temp(struct device *dev, u32 attr, long temp)
+{
+ struct lm75_data *data = dev_get_drvdata(dev);
+ u8 resolution;
+ int reg;
+
+ switch (attr) {
+ case hwmon_temp_max:
+ reg = LM75_REG_MAX;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = LM75_REG_HYST;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Resolution of limit registers is assumed to be the same as the
+ * temperature input register resolution unless given explicitly.
+ */
+ if (data->params->resolution_limits)
+ resolution = data->params->resolution_limits;
+ else
+ resolution = data->resolution;
+
+ temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
+ temp = DIV_ROUND_CLOSEST(temp << (resolution - 8),
+ 1000) << (16 - resolution);
+
+ return regmap_write(data->regmap, reg, (u16)temp);
+}
+
+static int lm75_update_interval(struct device *dev, long val)
+{
+ struct lm75_data *data = dev_get_drvdata(dev);
+ unsigned int reg;
+ u8 index;
+ s32 err;
+
+ index = find_closest(val, data->params->sample_times,
+ (int)data->params->num_sample_times);
+
+ switch (data->kind) {
+ default:
+ err = lm75_write_config(data, lm75_sample_set_masks[index],
+ LM75_SAMPLE_CLEAR_MASK);
+ if (err)
+ return err;
+
+ data->sample_time = data->params->sample_times[index];
+ if (data->params->resolutions)
+ data->resolution = data->params->resolutions[index];
+ break;
+ case tmp112:
+ err = regmap_read(data->regmap, LM75_REG_CONF, &reg);
+ if (err < 0)
+ return err;
+ reg &= ~0x00c0;
+ reg |= (3 - index) << 6;
+ err = regmap_write(data->regmap, LM75_REG_CONF, reg);
+ if (err < 0)
+ return err;
+ data->sample_time = data->params->sample_times[index];
+ break;
+ case pct2075:
+ err = i2c_smbus_write_byte_data(data->client, PCT2075_REG_IDLE,
+ index + 1);
+ if (err)
+ return err;
+ data->sample_time = data->params->sample_times[index];
+ break;
+ }
+ return 0;
+}
+
+static int lm75_write_chip(struct device *dev, u32 attr, long val)
+{
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return lm75_update_interval(dev, val);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm75_write_chip(dev, attr, val);
+ case hwmon_temp:
+ return lm75_write_temp(dev, attr, val);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct lm75_data *config_data = data;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ if (config_data->params->num_sample_times > 1)
+ return 0644;
+ return 0444;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ case hwmon_temp_max:
+ case hwmon_temp_max_hyst:
+ return 0644;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *lm75_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST),
+ NULL
+};
+
+static const struct hwmon_ops lm75_hwmon_ops = {
+ .is_visible = lm75_is_visible,
+ .read = lm75_read,
+ .write = lm75_write,
+};
+
+static const struct hwmon_chip_info lm75_chip_info = {
+ .ops = &lm75_hwmon_ops,
+ .info = lm75_info,
+};
+
+static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg != LM75_REG_TEMP;
+}
+
+static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg == LM75_REG_TEMP || reg == LM75_REG_CONF;
+}
+
+static const struct regmap_config lm75_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = PCT2075_REG_IDLE,
+ .writeable_reg = lm75_is_writeable_reg,
+ .volatile_reg = lm75_is_volatile_reg,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .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;
+ struct i2c_client *client = lm75->client;
+
+ i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf);
+}
+
+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;
+ struct lm75_data *data;
+ int status, err;
+ enum lm75_type kind;
+
+ if (client->dev.of_node)
+ kind = (enum lm75_type)of_device_get_match_data(&client->dev);
+ else
+ 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))
+ return -EIO;
+
+ data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ 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);
+
+ /* Set to LM75 resolution (9 bits, 1/2 degree C) and range.
+ * Then tweak to be more precise when appropriate.
+ */
+
+ data->params = &device_params[data->kind];
+
+ /* Save default sample time and resolution*/
+ 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) {
+ dev_dbg(dev, "Can't read config? %d\n", status);
+ return status;
+ }
+ data->orig_conf = status;
+ data->current_conf = status;
+
+ err = lm75_write_config(data, data->params->set_mask,
+ data->params->clr_mask);
+ if (err)
+ return err;
+
+ err = devm_add_action_or_reset(dev, lm75_remove, data);
+ if (err)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &lm75_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);
+
+ return 0;
+}
+
+static const struct i2c_device_id lm75_ids[] = {
+ { "adt75", adt75, },
+ { "at30ts74", at30ts74, },
+ { "ds1775", ds1775, },
+ { "ds75", ds75, },
+ { "ds7505", ds7505, },
+ { "g751", g751, },
+ { "lm75", lm75, },
+ { "lm75a", lm75a, },
+ { "lm75b", lm75b, },
+ { "max6625", max6625, },
+ { "max6626", max6626, },
+ { "max31725", max31725, },
+ { "max31726", max31725, },
+ { "mcp980x", mcp980x, },
+ { "pct2075", pct2075, },
+ { "stds75", stds75, },
+ { "stlm75", stlm75, },
+ { "tcn75", tcn75, },
+ { "tmp100", tmp100, },
+ { "tmp101", tmp101, },
+ { "tmp105", tmp105, },
+ { "tmp112", tmp112, },
+ { "tmp175", tmp175, },
+ { "tmp275", tmp275, },
+ { "tmp75", tmp75, },
+ { "tmp75b", tmp75b, },
+ { "tmp75c", tmp75c, },
+ { "tmp1075", tmp1075, },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, lm75_ids);
+
+static const struct of_device_id __maybe_unused lm75_of_match[] = {
+ {
+ .compatible = "adi,adt75",
+ .data = (void *)adt75
+ },
+ {
+ .compatible = "atmel,at30ts74",
+ .data = (void *)at30ts74
+ },
+ {
+ .compatible = "dallas,ds1775",
+ .data = (void *)ds1775
+ },
+ {
+ .compatible = "dallas,ds75",
+ .data = (void *)ds75
+ },
+ {
+ .compatible = "dallas,ds7505",
+ .data = (void *)ds7505
+ },
+ {
+ .compatible = "gmt,g751",
+ .data = (void *)g751
+ },
+ {
+ .compatible = "national,lm75",
+ .data = (void *)lm75
+ },
+ {
+ .compatible = "national,lm75a",
+ .data = (void *)lm75a
+ },
+ {
+ .compatible = "national,lm75b",
+ .data = (void *)lm75b
+ },
+ {
+ .compatible = "maxim,max6625",
+ .data = (void *)max6625
+ },
+ {
+ .compatible = "maxim,max6626",
+ .data = (void *)max6626
+ },
+ {
+ .compatible = "maxim,max31725",
+ .data = (void *)max31725
+ },
+ {
+ .compatible = "maxim,max31726",
+ .data = (void *)max31725
+ },
+ {
+ .compatible = "maxim,mcp980x",
+ .data = (void *)mcp980x
+ },
+ {
+ .compatible = "nxp,pct2075",
+ .data = (void *)pct2075
+ },
+ {
+ .compatible = "st,stds75",
+ .data = (void *)stds75
+ },
+ {
+ .compatible = "st,stlm75",
+ .data = (void *)stlm75
+ },
+ {
+ .compatible = "microchip,tcn75",
+ .data = (void *)tcn75
+ },
+ {
+ .compatible = "ti,tmp100",
+ .data = (void *)tmp100
+ },
+ {
+ .compatible = "ti,tmp101",
+ .data = (void *)tmp101
+ },
+ {
+ .compatible = "ti,tmp105",
+ .data = (void *)tmp105
+ },
+ {
+ .compatible = "ti,tmp112",
+ .data = (void *)tmp112
+ },
+ {
+ .compatible = "ti,tmp175",
+ .data = (void *)tmp175
+ },
+ {
+ .compatible = "ti,tmp275",
+ .data = (void *)tmp275
+ },
+ {
+ .compatible = "ti,tmp75",
+ .data = (void *)tmp75
+ },
+ {
+ .compatible = "ti,tmp75b",
+ .data = (void *)tmp75b
+ },
+ {
+ .compatible = "ti,tmp75c",
+ .data = (void *)tmp75c
+ },
+ {
+ .compatible = "ti,tmp1075",
+ .data = (void *)tmp1075
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lm75_of_match);
+
+#define LM75A_ID 0xA1
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm75_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ int i;
+ int conf, hyst, os;
+ bool is_lm75a = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ /*
+ * Now, we do the remaining detection. There is no identification-
+ * dedicated register so we have to rely on several tricks:
+ * unused bits, registers cycling over 8-address boundaries,
+ * addresses 0x04-0x07 returning the last read value.
+ * The cycling+unused addresses combination is not tested,
+ * since it would significantly slow the detection down and would
+ * hardly add any value.
+ *
+ * The National Semiconductor LM75A is different than earlier
+ * LM75s. It has an ID byte of 0xaX (where X is the chip
+ * revision, with 1 being the only revision in existence) in
+ * register 7, and unused registers return 0xff rather than the
+ * last read value.
+ *
+ * Note that this function only detects the original National
+ * Semiconductor LM75 and the LM75A. Clones from other vendors
+ * aren't detected, on purpose, because they are typically never
+ * found on PC hardware. They are found on embedded designs where
+ * they can be instantiated explicitly so detection is not needed.
+ * The absence of identification registers on all these clones
+ * would make their exhaustive detection very difficult and weak,
+ * and odds are that the driver would bind to unsupported devices.
+ */
+
+ /* Unused bits */
+ conf = i2c_smbus_read_byte_data(new_client, 1);
+ if (conf & 0xe0)
+ return -ENODEV;
+
+ /* 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.
+ */
+ 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)
+ return -ENODEV;
+ is_lm75a = 1;
+ hyst = i2c_smbus_read_byte_data(new_client, 2);
+ os = i2c_smbus_read_byte_data(new_client, 3);
+ } else { /* Traditional style LM75 detection */
+ /* Unused addresses */
+ hyst = i2c_smbus_read_byte_data(new_client, 2);
+ if (i2c_smbus_read_byte_data(new_client, 4) != hyst
+ || i2c_smbus_read_byte_data(new_client, 5) != hyst
+ || i2c_smbus_read_byte_data(new_client, 6) != hyst
+ || i2c_smbus_read_byte_data(new_client, 7) != hyst)
+ return -ENODEV;
+ os = i2c_smbus_read_byte_data(new_client, 3);
+ if (i2c_smbus_read_byte_data(new_client, 4) != os
+ || i2c_smbus_read_byte_data(new_client, 5) != os
+ || i2c_smbus_read_byte_data(new_client, 6) != os
+ || i2c_smbus_read_byte_data(new_client, 7) != os)
+ return -ENODEV;
+ }
+ /*
+ * It is very unlikely that this is a LM75 if both
+ * hysteresis and temperature limit registers are 0.
+ */
+ if (hyst == 0 && os == 0)
+ return -ENODEV;
+
+ /* Addresses cycling */
+ for (i = 8; i <= 248; i += 40) {
+ if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+ || i2c_smbus_read_byte_data(new_client, i + 2) != hyst
+ || i2c_smbus_read_byte_data(new_client, i + 3) != os)
+ return -ENODEV;
+ if (is_lm75a && i2c_smbus_read_byte_data(new_client, i + 7)
+ != LM75A_ID)
+ return -ENODEV;
+ }
+
+ strscpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+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);
+ return status;
+ }
+ status = status | LM75_SHUTDOWN;
+ i2c_smbus_write_byte_data(client, LM75_REG_CONF, status);
+ return 0;
+}
+
+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);
+ return status;
+ }
+ status = status & ~LM75_SHUTDOWN;
+ i2c_smbus_write_byte_data(client, LM75_REG_CONF, status);
+ return 0;
+}
+
+static const struct dev_pm_ops lm75_dev_pm_ops = {
+ .suspend = lm75_suspend,
+ .resume = lm75_resume,
+};
+#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops)
+#else
+#define LM75_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_driver lm75_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm75",
+ .of_match_table = of_match_ptr(lm75_of_match),
+ .pm = LM75_DEV_PM_OPS,
+ },
+ .probe_new = lm75_probe,
+ .id_table = lm75_ids,
+ .detect = lm75_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm75_driver);
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("LM75 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h
new file mode 100644
index 000000000..b803ada5e
--- /dev/null
+++ b/drivers/hwmon/lm75.h
@@ -0,0 +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>
+ */
+
+/*
+ * 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/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
+ */
+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
+ */
+ return ((s16)reg / 128) * 500;
+}
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
new file mode 100644
index 000000000..645cb2191
--- /dev/null
+++ b/drivers/hwmon/lm77.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm77.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ *
+ * Copyright (c) 2004 Andras BALI <drewie@freemail.hu>
+ *
+ * Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>. The LM77
+ * is a temperature sensor and thermal window comparator with 0.5 deg
+ * resolution made by National Semiconductor. Complete datasheet can be
+ * obtained at their site:
+ * http://www.national.com/pf/LM/LM77.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
+ I2C_CLIENT_END };
+
+/* The LM77 registers */
+#define LM77_REG_TEMP 0x00
+#define LM77_REG_CONF 0x01
+#define LM77_REG_TEMP_HYST 0x02
+#define LM77_REG_TEMP_CRIT 0x03
+#define LM77_REG_TEMP_MIN 0x04
+#define LM77_REG_TEMP_MAX 0x05
+
+enum temp_index {
+ t_input = 0,
+ t_crit,
+ t_min,
+ t_max,
+ t_hyst,
+ t_num_temp
+};
+
+static const u8 temp_regs[t_num_temp] = {
+ [t_input] = LM77_REG_TEMP,
+ [t_min] = LM77_REG_TEMP_MIN,
+ [t_max] = LM77_REG_TEMP_MAX,
+ [t_crit] = LM77_REG_TEMP_CRIT,
+ [t_hyst] = LM77_REG_TEMP_HYST,
+};
+
+/* Each client has this additional data */
+struct lm77_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* In jiffies */
+ int temp[t_num_temp]; /* index using temp_index */
+ u8 alarms;
+};
+
+/* straight from the datasheet */
+#define LM77_TEMP_MIN (-55000)
+#define LM77_TEMP_MAX 125000
+
+/*
+ * In the temperature registers, the low 3 bits are not part of the
+ * temperature values; they are the status bits.
+ */
+static inline s16 LM77_TEMP_TO_REG(int temp)
+{
+ return (temp / 500) * 8;
+}
+
+static inline int LM77_TEMP_FROM_REG(s16 reg)
+{
+ return (reg / 8) * 500;
+}
+
+/*
+ * All registers are word-sized, except for the configuration register.
+ * The LM77 uses the high-byte first convention.
+ */
+static u16 lm77_read_value(struct i2c_client *client, u8 reg)
+{
+ if (reg == LM77_REG_CONF)
+ return i2c_smbus_read_byte_data(client, reg);
+ else
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+ if (reg == LM77_REG_CONF)
+ return i2c_smbus_write_byte_data(client, reg, value);
+ else
+ return i2c_smbus_write_word_swapped(client, reg, value);
+}
+
+static struct lm77_data *lm77_update_device(struct device *dev)
+{
+ struct lm77_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ dev_dbg(&client->dev, "Starting lm77 update\n");
+ for (i = 0; i < t_num_temp; i++) {
+ data->temp[i] =
+ LM77_TEMP_FROM_REG(lm77_read_value(client,
+ temp_regs[i]));
+ }
+ data->alarms =
+ lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* sysfs stuff */
+
+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 lm77_data *data = lm77_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->temp[attr->index]);
+}
+
+static ssize_t temp_hyst_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm77_data *data = lm77_update_device(dev);
+ int nr = attr->index;
+ int temp;
+
+ temp = nr == t_min ? data->temp[nr] + data->temp[t_hyst] :
+ data->temp[nr] - data->temp[t_hyst];
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t temp_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 lm77_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ val = clamp_val(val, LM77_TEMP_MIN, LM77_TEMP_MAX);
+ mutex_lock(&data->update_lock);
+ data->temp[nr] = val;
+ lm77_write_value(client, temp_regs[nr], LM77_TEMP_TO_REG(val));
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * hysteresis is stored as a relative value on the chip, so it has to be
+ * converted first.
+ */
+static ssize_t temp_hyst_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct lm77_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ val = clamp_val(data->temp[t_crit] - val, LM77_TEMP_MIN, LM77_TEMP_MAX);
+ data->temp[t_hyst] = val;
+ lm77_write_value(client, LM77_REG_TEMP_HYST,
+ LM77_TEMP_TO_REG(data->temp[t_hyst]));
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct lm77_data *data = lm77_update_device(dev);
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_min);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_max);
+
+static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_hyst, t_crit);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_hyst, temp_hyst, t_min);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, temp_hyst, t_max);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1);
+
+static struct attribute *lm77_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(lm77);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int i, cur, conf, hyst, crit, min, max;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ /*
+ * Here comes the remaining detection. Since the LM77 has no
+ * register dedicated to identification, we have to rely on the
+ * following tricks:
+ *
+ * 1. the high 4 bits represent the sign and thus they should
+ * always be the same
+ * 2. the high 3 bits are unused in the configuration register
+ * 3. addresses 0x06 and 0x07 return the last read value
+ * 4. registers cycling over 8-address boundaries
+ *
+ * Word-sized registers are high-byte first.
+ */
+
+ /* addresses cycling */
+ cur = i2c_smbus_read_word_data(client, 0);
+ conf = i2c_smbus_read_byte_data(client, 1);
+ hyst = i2c_smbus_read_word_data(client, 2);
+ crit = i2c_smbus_read_word_data(client, 3);
+ min = i2c_smbus_read_word_data(client, 4);
+ max = i2c_smbus_read_word_data(client, 5);
+ for (i = 8; i <= 0xff; i += 8) {
+ if (i2c_smbus_read_byte_data(client, i + 1) != conf
+ || i2c_smbus_read_word_data(client, i + 2) != hyst
+ || i2c_smbus_read_word_data(client, i + 3) != crit
+ || i2c_smbus_read_word_data(client, i + 4) != min
+ || i2c_smbus_read_word_data(client, i + 5) != max)
+ return -ENODEV;
+ }
+
+ /* sign bits */
+ if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
+ || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
+ || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
+ || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
+ || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
+ return -ENODEV;
+
+ /* unused bits */
+ if (conf & 0xe0)
+ return -ENODEV;
+
+ /* 0x06 and 0x07 return the last read value */
+ cur = i2c_smbus_read_word_data(client, 0);
+ if (i2c_smbus_read_word_data(client, 6) != cur
+ || i2c_smbus_read_word_data(client, 7) != cur)
+ return -ENODEV;
+ hyst = i2c_smbus_read_word_data(client, 2);
+ if (i2c_smbus_read_word_data(client, 6) != hyst
+ || i2c_smbus_read_word_data(client, 7) != hyst)
+ return -ENODEV;
+ min = i2c_smbus_read_word_data(client, 4);
+ if (i2c_smbus_read_word_data(client, 6) != min
+ || i2c_smbus_read_word_data(client, 7) != min)
+ return -ENODEV;
+
+ strscpy(info->type, "lm77", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void lm77_init_client(struct i2c_client *client)
+{
+ /* Initialize the LM77 chip - turn off shutdown mode */
+ int conf = lm77_read_value(client, LM77_REG_CONF);
+ if (conf & 1)
+ lm77_write_value(client, LM77_REG_CONF, conf & 0xfe);
+}
+
+static int lm77_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct lm77_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct lm77_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM77 chip */
+ lm77_init_client(client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, lm77_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id lm77_id[] = {
+ { "lm77", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm77_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm77_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm77",
+ },
+ .probe_new = lm77_probe,
+ .id_table = lm77_id,
+ .detect = lm77_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm77_driver);
+
+MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>");
+MODULE_DESCRIPTION("LM77 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
new file mode 100644
index 000000000..694e171ca
--- /dev/null
+++ b/drivers/hwmon/lm78.c
@@ -0,0 +1,1033 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm78.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ * Copyright (c) 2007, 2011 Jean Delvare <jdelvare@suse.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+#ifdef CONFIG_ISA
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#endif
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, I2C_CLIENT_END };
+enum chips { lm78, lm79 };
+
+/* Many LM78 constants specified below */
+
+/* Length of ISA address segment */
+#define LM78_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define LM78_ADDR_REG_OFFSET 5
+#define LM78_DATA_REG_OFFSET 6
+
+/* The LM78 registers */
+#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define LM78_REG_IN(nr) (0x20 + (nr))
+
+#define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
+#define LM78_REG_FAN(nr) (0x28 + (nr))
+
+#define LM78_REG_TEMP 0x27
+#define LM78_REG_TEMP_OVER 0x39
+#define LM78_REG_TEMP_HYST 0x3a
+
+#define LM78_REG_ALARM1 0x41
+#define LM78_REG_ALARM2 0x42
+
+#define LM78_REG_VID_FANDIV 0x47
+
+#define LM78_REG_CONFIG 0x40
+#define LM78_REG_CHIPID 0x49
+#define LM78_REG_I2C_ADDR 0x48
+
+/*
+ * Conversions. Rounding and limit checking is only done on the TO_REG
+ * variants.
+ */
+
+/*
+ * IN: mV (0V to 4.08V)
+ * REG: 16mV/bit
+ */
+static inline u8 IN_TO_REG(unsigned long val)
+{
+ unsigned long nval = clamp_val(val, 0, 4080);
+ return (nval + 8) / 16;
+}
+#define IN_FROM_REG(val) ((val) * 16)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm <= 0)
+ return 255;
+ if (rpm > 1350000)
+ return 1;
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+static inline int FAN_FROM_REG(u8 val, int div)
+{
+ return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div);
+}
+
+/*
+ * TEMP: mC (-128C to +127C)
+ * REG: 1C/bit, two's complement
+ */
+static inline s8 TEMP_TO_REG(long val)
+{
+ int nval = clamp_val(val, -128000, 127000) ;
+ return nval < 0 ? (nval - 500) / 1000 : (nval + 500) / 1000;
+}
+
+static inline int TEMP_FROM_REG(s8 val)
+{
+ return val * 1000;
+}
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+struct lm78_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ enum chips type;
+
+ /* For ISA device only */
+ const char *name;
+ int isa_addr;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 in[7]; /* Register value */
+ u8 in_max[7]; /* Register value */
+ u8 in_min[7]; /* Register value */
+ u8 fan[3]; /* Register value */
+ u8 fan_min[3]; /* Register value */
+ s8 temp; /* Register value */
+ s8 temp_over; /* Register value */
+ s8 temp_hyst; /* Register value */
+ u8 fan_div[3]; /* Register encoding, shifted right */
+ u8 vid; /* Register encoding, combined */
+ u16 alarms; /* Register encoding, combined */
+};
+
+static int lm78_read_value(struct lm78_data *data, u8 reg);
+static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);
+static struct lm78_data *lm78_update_device(struct device *dev);
+static void lm78_init_device(struct lm78_data *data);
+
+/* 7 Voltages */
+static ssize_t in_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index]));
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index]));
+}
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index]));
+}
+
+static ssize_t in_min_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 lm78_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[nr] = IN_TO_REG(val);
+ lm78_write_value(data, LM78_REG_IN_MIN(nr), data->in_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in_max_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 lm78_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_max[nr] = IN_TO_REG(val);
+ lm78_write_value(data, LM78_REG_IN_MAX(nr), data->in_max[nr]);
+ 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);
+static SENSOR_DEVICE_ATTR_RO(in6_input, in, 6);
+static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 6);
+static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 6);
+
+/* Temperature */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
+}
+
+static ssize_t temp1_max_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
+}
+
+static ssize_t temp1_max_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ struct lm78_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->temp_over = TEMP_TO_REG(val);
+ lm78_write_value(data, LM78_REG_TEMP_OVER, data->temp_over);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp1_max_hyst_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
+}
+
+static ssize_t temp1_max_hyst_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct lm78_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->temp_hyst = TEMP_TO_REG(val);
+ lm78_write_value(data, LM78_REG_TEMP_HYST, data->temp_hyst);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RW(temp1_max);
+static DEVICE_ATTR_RW(temp1_max_hyst);
+
+/* 3 Fans */
+static ssize_t fan_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm78_data *data = lm78_update_device(dev);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm78_data *data = lm78_update_device(dev);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_min_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 lm78_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ 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]));
+ lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
+}
+
+/*
+ * 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t fan_div_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 lm78_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ unsigned long min;
+ u8 reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ min = FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+
+ switch (val) {
+ case 1:
+ data->fan_div[nr] = 0;
+ break;
+ case 2:
+ data->fan_div[nr] = 1;
+ break;
+ case 4:
+ data->fan_div[nr] = 2;
+ break;
+ case 8:
+ data->fan_div[nr] = 3;
+ break;
+ default:
+ dev_err(dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ reg = lm78_read_value(data, LM78_REG_VID_FANDIV);
+ switch (nr) {
+ case 0:
+ reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
+ break;
+ case 1:
+ reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
+ break;
+ }
+ lm78_write_value(data, LM78_REG_VID_FANDIV, reg);
+
+ data->fan_min[nr] =
+ FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ lm78_write_value(data, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
+ 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(fan2_input, fan, 1);
+static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
+static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2);
+
+/* Fan 3 divisor is locked in H/W */
+static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_div, fan_div, 2);
+
+/* VID */
+static ssize_t cpu0_vid_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, 82));
+}
+static DEVICE_ATTR_RO(cpu0_vid);
+
+/* Alarms */
+static ssize_t alarms_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct lm78_data *data = lm78_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct lm78_data *data = lm78_update_device(dev);
+ int nr = to_sensor_dev_attr(da)->index;
+ return sprintf(buf, "%u\n", (data->alarms >> nr) & 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(in6_alarm, alarm, 10);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
+
+static struct attribute *lm78_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,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_cpu0_vid.attr,
+
+ NULL
+};
+
+ATTRIBUTE_GROUPS(lm78);
+
+/*
+ * ISA related code
+ */
+#ifdef CONFIG_ISA
+
+/* ISA device, if found */
+static struct platform_device *pdev;
+
+static unsigned short isa_address = 0x290;
+
+static struct lm78_data *lm78_data_if_isa(void)
+{
+ return pdev ? platform_get_drvdata(pdev) : NULL;
+}
+
+/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
+static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
+{
+ struct lm78_data *isa;
+ int i;
+
+ if (!pdev) /* No ISA chip */
+ return 0;
+ isa = platform_get_drvdata(pdev);
+
+ if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr)
+ return 0; /* Address doesn't match */
+ if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe))
+ return 0; /* Chip type doesn't match */
+
+ /*
+ * We compare all the limit registers, the config register and the
+ * interrupt mask registers
+ */
+ for (i = 0x2b; i <= 0x3d; i++) {
+ if (lm78_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
+ }
+ if (lm78_read_value(isa, LM78_REG_CONFIG) !=
+ i2c_smbus_read_byte_data(client, LM78_REG_CONFIG))
+ return 0;
+ for (i = 0x43; i <= 0x46; i++) {
+ if (lm78_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
+ }
+
+ return 1;
+}
+#else /* !CONFIG_ISA */
+
+static int lm78_alias_detect(struct i2c_client *client, u8 chipid)
+{
+ return 0;
+}
+
+static struct lm78_data *lm78_data_if_isa(void)
+{
+ return NULL;
+}
+#endif /* CONFIG_ISA */
+
+static int lm78_i2c_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int i;
+ struct lm78_data *isa = lm78_data_if_isa();
+ const char *client_name;
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /*
+ * We block updates of the ISA device to minimize the risk of
+ * concurrent access to the same LM78 chip through different
+ * interfaces.
+ */
+ if (isa)
+ mutex_lock(&isa->update_lock);
+
+ if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
+ || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) != address)
+ goto err_nodev;
+
+ /* Explicitly prevent the misdetection of Winbond chips */
+ i = i2c_smbus_read_byte_data(client, 0x4f);
+ if (i == 0xa3 || i == 0x5c)
+ goto err_nodev;
+
+ /* Determine the chip type. */
+ i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
+ if (i == 0x00 || i == 0x20 /* LM78 */
+ || i == 0x40) /* LM78-J */
+ client_name = "lm78";
+ else if ((i & 0xfe) == 0xc0)
+ client_name = "lm79";
+ else
+ goto err_nodev;
+
+ if (lm78_alias_detect(client, i)) {
+ dev_dbg(&adapter->dev,
+ "Device at 0x%02x appears to be the same as ISA device\n",
+ address);
+ goto err_nodev;
+ }
+
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+
+ strscpy(info->type, client_name, I2C_NAME_SIZE);
+
+ return 0;
+
+ err_nodev:
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+ return -ENODEV;
+}
+
+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;
+ struct lm78_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct lm78_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->type = i2c_match_id(lm78_i2c_id, client)->driver_data;
+
+ /* Initialize the LM78 chip */
+ lm78_init_device(data);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, lm78_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id lm78_i2c_id[] = {
+ { "lm78", lm78 },
+ { "lm79", lm79 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
+
+static struct i2c_driver lm78_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm78",
+ },
+ .probe_new = lm78_i2c_probe,
+ .id_table = lm78_i2c_id,
+ .detect = lm78_i2c_detect,
+ .address_list = normal_i2c,
+};
+
+/*
+ * The SMBus locks itself, but ISA access must be locked explicitly!
+ * We don't want to lock the whole ISA bus, so we lock each client
+ * separately.
+ * We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
+ * would slow down the LM78 access and should not be necessary.
+ */
+static int lm78_read_value(struct lm78_data *data, u8 reg)
+{
+ struct i2c_client *client = data->client;
+
+#ifdef CONFIG_ISA
+ if (!client) { /* ISA device */
+ int res;
+ mutex_lock(&data->lock);
+ outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
+ res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET);
+ mutex_unlock(&data->lock);
+ return res;
+ } else
+#endif
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)
+{
+ struct i2c_client *client = data->client;
+
+#ifdef CONFIG_ISA
+ if (!client) { /* ISA device */
+ mutex_lock(&data->lock);
+ outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET);
+ outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET);
+ mutex_unlock(&data->lock);
+ return 0;
+ } else
+#endif
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static void lm78_init_device(struct lm78_data *data)
+{
+ u8 config;
+ int i;
+
+ /* Start monitoring */
+ config = lm78_read_value(data, LM78_REG_CONFIG);
+ if ((config & 0x09) != 0x01)
+ lm78_write_value(data, LM78_REG_CONFIG,
+ (config & 0xf7) | 0x01);
+
+ /* A few vars need to be filled upon startup */
+ for (i = 0; i < 3; i++) {
+ data->fan_min[i] = lm78_read_value(data,
+ LM78_REG_FAN_MIN(i));
+ }
+
+ mutex_init(&data->update_lock);
+}
+
+static struct lm78_data *lm78_update_device(struct device *dev)
+{
+ struct lm78_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) {
+
+ dev_dbg(dev, "Starting lm78 update\n");
+
+ for (i = 0; i <= 6; i++) {
+ data->in[i] =
+ lm78_read_value(data, LM78_REG_IN(i));
+ data->in_min[i] =
+ lm78_read_value(data, LM78_REG_IN_MIN(i));
+ data->in_max[i] =
+ lm78_read_value(data, LM78_REG_IN_MAX(i));
+ }
+ for (i = 0; i < 3; i++) {
+ data->fan[i] =
+ lm78_read_value(data, LM78_REG_FAN(i));
+ data->fan_min[i] =
+ lm78_read_value(data, LM78_REG_FAN_MIN(i));
+ }
+ data->temp = lm78_read_value(data, LM78_REG_TEMP);
+ data->temp_over =
+ lm78_read_value(data, LM78_REG_TEMP_OVER);
+ data->temp_hyst =
+ lm78_read_value(data, LM78_REG_TEMP_HYST);
+ i = lm78_read_value(data, LM78_REG_VID_FANDIV);
+ data->vid = i & 0x0f;
+ if (data->type == lm79)
+ data->vid |=
+ (lm78_read_value(data, LM78_REG_CHIPID) &
+ 0x01) << 4;
+ else
+ data->vid |= 0x10;
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = i >> 6;
+ data->alarms = lm78_read_value(data, LM78_REG_ALARM1) +
+ (lm78_read_value(data, LM78_REG_ALARM2) << 8);
+ data->last_updated = jiffies;
+ data->valid = true;
+
+ data->fan_div[2] = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+#ifdef CONFIG_ISA
+static int lm78_isa_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hwmon_dev;
+ struct lm78_data *data;
+ struct resource *res;
+
+ /* Reserve the ISA region */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(dev, res->start + LM78_ADDR_REG_OFFSET,
+ 2, "lm78"))
+ return -EBUSY;
+
+ data = devm_kzalloc(dev, sizeof(struct lm78_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->lock);
+ data->isa_addr = res->start;
+ platform_set_drvdata(pdev, data);
+
+ if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) {
+ data->type = lm79;
+ data->name = "lm79";
+ } else {
+ data->type = lm78;
+ data->name = "lm78";
+ }
+
+ /* Initialize the LM78 chip */
+ lm78_init_device(data);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name,
+ data, lm78_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver lm78_isa_driver = {
+ .driver = {
+ .name = "lm78",
+ },
+ .probe = lm78_isa_probe,
+};
+
+/* return 1 if a supported chip is found, 0 otherwise */
+static int __init lm78_isa_found(unsigned short address)
+{
+ int val, save, found = 0;
+ int port;
+
+ /*
+ * Some boards declare base+0 to base+7 as a PNP device, some base+4
+ * to base+7 and some base+5 to base+6. So we better request each port
+ * individually for the probing phase.
+ */
+ for (port = address; port < address + LM78_EXTENT; port++) {
+ if (!request_region(port, 1, "lm78")) {
+ pr_debug("Failed to request port 0x%x\n", port);
+ goto release;
+ }
+ }
+
+#define REALLY_SLOW_IO
+ /*
+ * We need the timeouts for at least some LM78-like
+ * chips. But only if we read 'undefined' registers.
+ */
+ val = inb_p(address + 1);
+ if (inb_p(address + 2) != val
+ || inb_p(address + 3) != val
+ || inb_p(address + 7) != val)
+ goto release;
+#undef REALLY_SLOW_IO
+
+ /*
+ * We should be able to change the 7 LSB of the address port. The
+ * MSB (busy flag) should be clear initially, set after the write.
+ */
+ save = inb_p(address + LM78_ADDR_REG_OFFSET);
+ if (save & 0x80)
+ goto release;
+ val = ~save & 0x7f;
+ outb_p(val, address + LM78_ADDR_REG_OFFSET);
+ if (inb_p(address + LM78_ADDR_REG_OFFSET) != (val | 0x80)) {
+ outb_p(save, address + LM78_ADDR_REG_OFFSET);
+ goto release;
+ }
+
+ /* We found a device, now see if it could be an LM78 */
+ outb_p(LM78_REG_CONFIG, address + LM78_ADDR_REG_OFFSET);
+ val = inb_p(address + LM78_DATA_REG_OFFSET);
+ if (val & 0x80)
+ goto release;
+ outb_p(LM78_REG_I2C_ADDR, address + LM78_ADDR_REG_OFFSET);
+ val = inb_p(address + LM78_DATA_REG_OFFSET);
+ if (val < 0x03 || val > 0x77) /* Not a valid I2C address */
+ goto release;
+
+ /* The busy flag should be clear again */
+ if (inb_p(address + LM78_ADDR_REG_OFFSET) & 0x80)
+ goto release;
+
+ /* Explicitly prevent the misdetection of Winbond chips */
+ outb_p(0x4f, address + LM78_ADDR_REG_OFFSET);
+ val = inb_p(address + LM78_DATA_REG_OFFSET);
+ if (val == 0xa3 || val == 0x5c)
+ goto release;
+
+ /* Explicitly prevent the misdetection of ITE chips */
+ outb_p(0x58, address + LM78_ADDR_REG_OFFSET);
+ val = inb_p(address + LM78_DATA_REG_OFFSET);
+ if (val == 0x90)
+ goto release;
+
+ /* Determine the chip type */
+ outb_p(LM78_REG_CHIPID, address + LM78_ADDR_REG_OFFSET);
+ val = inb_p(address + LM78_DATA_REG_OFFSET);
+ if (val == 0x00 || val == 0x20 /* LM78 */
+ || val == 0x40 /* LM78-J */
+ || (val & 0xfe) == 0xc0) /* LM79 */
+ found = 1;
+
+ if (found)
+ pr_info("Found an %s chip at %#x\n",
+ val & 0x80 ? "LM79" : "LM78", (int)address);
+
+ release:
+ for (port--; port >= address; port--)
+ release_region(port, 1);
+ return found;
+}
+
+static int __init lm78_isa_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + LM78_EXTENT - 1,
+ .name = "lm78",
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ pdev = platform_device_alloc("lm78", address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+ exit_device_put:
+ platform_device_put(pdev);
+ exit:
+ pdev = NULL;
+ return err;
+}
+
+static int __init lm78_isa_register(void)
+{
+ int res;
+
+ if (lm78_isa_found(isa_address)) {
+ res = platform_driver_register(&lm78_isa_driver);
+ if (res)
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ res = lm78_isa_device_add(isa_address);
+ if (res)
+ goto exit_unreg_isa_driver;
+ }
+
+ return 0;
+
+ exit_unreg_isa_driver:
+ platform_driver_unregister(&lm78_isa_driver);
+ exit:
+ return res;
+}
+
+static void lm78_isa_unregister(void)
+{
+ if (pdev) {
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&lm78_isa_driver);
+ }
+}
+#else /* !CONFIG_ISA */
+
+static int __init lm78_isa_register(void)
+{
+ return 0;
+}
+
+static void lm78_isa_unregister(void)
+{
+}
+#endif /* CONFIG_ISA */
+
+static int __init sm_lm78_init(void)
+{
+ int res;
+
+ /*
+ * We register the ISA device first, so that we can skip the
+ * registration of an I2C interface to the same device.
+ */
+ res = lm78_isa_register();
+ if (res)
+ goto exit;
+
+ res = i2c_add_driver(&lm78_driver);
+ if (res)
+ goto exit_unreg_isa_device;
+
+ return 0;
+
+ exit_unreg_isa_device:
+ lm78_isa_unregister();
+ exit:
+ return res;
+}
+
+static void __exit sm_lm78_exit(void)
+{
+ lm78_isa_unregister();
+ i2c_del_driver(&lm78_driver);
+}
+
+MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("LM78/LM79 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_lm78_init);
+module_exit(sm_lm78_exit);
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
new file mode 100644
index 000000000..35db0b97f
--- /dev/null
+++ b/drivers/hwmon/lm80.c
@@ -0,0 +1,647 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm80.c - From lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ * and Philip Edelbrock <phil@netroedge.com>
+ *
+ * Ported to Linux 2.6 by Tiago Sousa <mirage@kaotik.org>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, I2C_CLIENT_END };
+
+/* Many LM80 constants specified below */
+
+/* The LM80 registers */
+#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2)
+#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2)
+#define LM80_REG_IN(nr) (0x20 + (nr))
+
+#define LM80_REG_FAN1 0x28
+#define LM80_REG_FAN2 0x29
+#define LM80_REG_FAN_MIN(nr) (0x3b + (nr))
+
+#define LM80_REG_TEMP 0x27
+#define LM80_REG_TEMP_HOT_MAX 0x38
+#define LM80_REG_TEMP_HOT_HYST 0x39
+#define LM80_REG_TEMP_OS_MAX 0x3a
+#define LM80_REG_TEMP_OS_HYST 0x3b
+
+#define LM80_REG_CONFIG 0x00
+#define LM80_REG_ALARM1 0x01
+#define LM80_REG_ALARM2 0x02
+#define LM80_REG_MASK1 0x03
+#define LM80_REG_MASK2 0x04
+#define LM80_REG_FANDIV 0x05
+#define LM80_REG_RES 0x06
+
+#define LM96080_REG_CONV_RATE 0x07
+#define LM96080_REG_MAN_ID 0x3e
+#define LM96080_REG_DEV_ID 0x3f
+
+
+/*
+ * Conversions. Rounding and limit checking is only done on the TO_REG
+ * variants. Note that you should be a bit careful with which arguments
+ * these macros are called: arguments may be evaluated more than once.
+ * Fixing this is just not worth it.
+ */
+
+#define IN_TO_REG(val) (clamp_val(((val) + 5) / 10, 0, 255))
+#define IN_FROM_REG(val) ((val) * 10)
+
+static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
+ (val) == 255 ? 0 : 1350000/((div) * (val)))
+
+#define TEMP_FROM_REG(reg) ((reg) * 125 / 32)
+#define TEMP_TO_REG(temp) (DIV_ROUND_CLOSEST(clamp_val((temp), \
+ -128000, 127000), 1000) << 8)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+enum temp_index {
+ t_input = 0,
+ t_hot_max,
+ t_hot_hyst,
+ t_os_max,
+ t_os_hyst,
+ t_num_temp
+};
+
+static const u8 temp_regs[t_num_temp] = {
+ [t_input] = LM80_REG_TEMP,
+ [t_hot_max] = LM80_REG_TEMP_HOT_MAX,
+ [t_hot_hyst] = LM80_REG_TEMP_HOT_HYST,
+ [t_os_max] = LM80_REG_TEMP_OS_MAX,
+ [t_os_hyst] = LM80_REG_TEMP_OS_HYST,
+};
+
+enum in_index {
+ i_input = 0,
+ i_max,
+ i_min,
+ i_num_in
+};
+
+enum fan_index {
+ f_input,
+ f_min,
+ f_num_fan
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm80_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ char error; /* !=0 if error occurred during last update */
+ 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 */
+ u8 fan[f_num_fan][2]; /* Register value, 1st index enum fan_index */
+ u8 fan_div[2]; /* Register encoding, shifted right */
+ s16 temp[t_num_temp]; /* Register values, normalized to 16 bit */
+ u16 alarms; /* Register encoding, combined */
+};
+
+static int lm80_read_value(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new LM80 and after read errors */
+static void lm80_init_client(struct i2c_client *client)
+{
+ /*
+ * Reset all except Watchdog values and last conversion values
+ * This sets fan-divs to 2, among others. This makes most other
+ * initializations unnecessary
+ */
+ lm80_write_value(client, LM80_REG_CONFIG, 0x80);
+ /* Set 11-bit temperature resolution */
+ lm80_write_value(client, LM80_REG_RES, 0x08);
+
+ /* Start monitoring */
+ lm80_write_value(client, LM80_REG_CONFIG, 0x01);
+}
+
+static struct lm80_data *lm80_update_device(struct device *dev)
+{
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i;
+ int rv;
+ int prev_rv;
+ struct lm80_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ if (data->error)
+ lm80_init_client(client);
+
+ if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
+ dev_dbg(dev, "Starting lm80 update\n");
+ for (i = 0; i <= 6; i++) {
+ rv = lm80_read_value(client, LM80_REG_IN(i));
+ if (rv < 0)
+ goto abort;
+ data->in[i_input][i] = rv;
+
+ rv = lm80_read_value(client, LM80_REG_IN_MIN(i));
+ if (rv < 0)
+ goto abort;
+ data->in[i_min][i] = rv;
+
+ rv = lm80_read_value(client, LM80_REG_IN_MAX(i));
+ if (rv < 0)
+ goto abort;
+ data->in[i_max][i] = rv;
+ }
+
+ rv = lm80_read_value(client, LM80_REG_FAN1);
+ if (rv < 0)
+ goto abort;
+ data->fan[f_input][0] = rv;
+
+ rv = lm80_read_value(client, LM80_REG_FAN_MIN(1));
+ if (rv < 0)
+ goto abort;
+ data->fan[f_min][0] = rv;
+
+ rv = lm80_read_value(client, LM80_REG_FAN2);
+ if (rv < 0)
+ goto abort;
+ data->fan[f_input][1] = rv;
+
+ rv = lm80_read_value(client, LM80_REG_FAN_MIN(2));
+ if (rv < 0)
+ goto abort;
+ data->fan[f_min][1] = rv;
+
+ prev_rv = rv = lm80_read_value(client, LM80_REG_TEMP);
+ if (rv < 0)
+ goto abort;
+ rv = lm80_read_value(client, LM80_REG_RES);
+ if (rv < 0)
+ goto abort;
+ data->temp[t_input] = (prev_rv << 8) | (rv & 0xf0);
+
+ for (i = t_input + 1; i < t_num_temp; i++) {
+ rv = lm80_read_value(client, temp_regs[i]);
+ if (rv < 0)
+ goto abort;
+ data->temp[i] = rv << 8;
+ }
+
+ rv = lm80_read_value(client, LM80_REG_FANDIV);
+ if (rv < 0)
+ goto abort;
+ data->fan_div[0] = (rv >> 2) & 0x03;
+ data->fan_div[1] = (rv >> 4) & 0x03;
+
+ prev_rv = rv = lm80_read_value(client, LM80_REG_ALARM1);
+ if (rv < 0)
+ goto abort;
+ rv = lm80_read_value(client, LM80_REG_ALARM2);
+ if (rv < 0)
+ goto abort;
+ data->alarms = prev_rv + (rv << 8);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ data->error = 0;
+ }
+ goto done;
+
+abort:
+ ret = ERR_PTR(rv);
+ data->valid = false;
+ data->error = 1;
+
+done:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t in_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm80_data *data = lm80_update_device(dev);
+ int index = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr][index]));
+}
+
+static ssize_t in_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int index = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ long val;
+ u8 reg;
+ int err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ reg = nr == i_min ? LM80_REG_IN_MIN(index) : LM80_REG_IN_MAX(index);
+
+ mutex_lock(&data->update_lock);
+ data->in[nr][index] = IN_TO_REG(val);
+ lm80_write_value(client, reg, data->in[nr][index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ struct lm80_data *data = lm80_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr][index],
+ DIV_FROM_REG(data->fan_div[index])));
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm80_data *data = lm80_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
+}
+
+static ssize_t fan_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+ int err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->fan[nr][index] = FAN_TO_REG(val,
+ DIV_FROM_REG(data->fan_div[index]));
+ lm80_write_value(client, LM80_REG_FAN_MIN(index + 1),
+ data->fan[nr][index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long min, val;
+ u8 reg;
+ int rv;
+
+ rv = kstrtoul(buf, 10, &val);
+ if (rv < 0)
+ return rv;
+
+ /* Save fan_min */
+ mutex_lock(&data->update_lock);
+ min = FAN_FROM_REG(data->fan[f_min][nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+
+ switch (val) {
+ case 1:
+ data->fan_div[nr] = 0;
+ break;
+ case 2:
+ data->fan_div[nr] = 1;
+ break;
+ case 4:
+ data->fan_div[nr] = 2;
+ break;
+ case 8:
+ data->fan_div[nr] = 3;
+ break;
+ default:
+ dev_err(dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ rv = lm80_read_value(client, LM80_REG_FANDIV);
+ if (rv < 0) {
+ mutex_unlock(&data->update_lock);
+ return rv;
+ }
+ reg = (rv & ~(3 << (2 * (nr + 1))))
+ | (data->fan_div[nr] << (2 * (nr + 1)));
+ lm80_write_value(client, LM80_REG_FANDIV, reg);
+
+ /* Restore fan_min */
+ data->fan[f_min][nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1),
+ data->fan[f_min][nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+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 lm80_data *data = lm80_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static ssize_t temp_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 lm80_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = attr->index;
+ long val;
+ int err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp[nr] = TEMP_TO_REG(val);
+ lm80_write_value(client, temp_regs[nr], data->temp[nr] >> 8);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm80_data *data = lm80_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct lm80_data *data = lm80_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_2_RW(in0_min, in, i_min, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in1_min, in, i_min, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_min, in, i_min, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in3_min, in, i_min, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in4_min, in, i_min, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in5_min, in, i_min, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in6_min, in, i_min, 6);
+static SENSOR_DEVICE_ATTR_2_RW(in0_max, in, i_max, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in1_max, in, i_max, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_max, in, i_max, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in3_max, in, i_max, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in4_max, in, i_max, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in5_max, in, i_max, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in6_max, in, i_max, 6);
+static SENSOR_DEVICE_ATTR_2_RO(in0_input, in, i_input, 0);
+static SENSOR_DEVICE_ATTR_2_RO(in1_input, in, i_input, 1);
+static SENSOR_DEVICE_ATTR_2_RO(in2_input, in, i_input, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in3_input, in, i_input, 3);
+static SENSOR_DEVICE_ATTR_2_RO(in4_input, in, i_input, 4);
+static SENSOR_DEVICE_ATTR_2_RO(in5_input, in, i_input, 5);
+static SENSOR_DEVICE_ATTR_2_RO(in6_input, in, i_input, 6);
+static SENSOR_DEVICE_ATTR_2_RW(fan1_min, fan, f_min, 0);
+static SENSOR_DEVICE_ATTR_2_RW(fan2_min, fan, f_min, 1);
+static SENSOR_DEVICE_ATTR_2_RO(fan1_input, fan, f_input, 0);
+static SENSOR_DEVICE_ATTR_2_RO(fan2_input, fan, f_input, 1);
+static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_hot_max);
+static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp, t_hot_hyst);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_os_max);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp, t_os_hyst);
+static DEVICE_ATTR_RO(alarms);
+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, 4);
+static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 10);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 8);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 13);
+
+/*
+ * Real code
+ */
+
+static struct attribute *lm80_attrs[] = {
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.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_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(lm80);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int i, cur, man_id, dev_id;
+ const char *name = NULL;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* First check for unused bits, common to both chip types */
+ if ((lm80_read_value(client, LM80_REG_ALARM2) & 0xc0)
+ || (lm80_read_value(client, LM80_REG_CONFIG) & 0x80))
+ return -ENODEV;
+
+ /*
+ * The LM96080 has manufacturer and stepping/die rev registers so we
+ * can just check that. The LM80 does not have such registers so we
+ * have to use a more expensive trick.
+ */
+ man_id = lm80_read_value(client, LM96080_REG_MAN_ID);
+ dev_id = lm80_read_value(client, LM96080_REG_DEV_ID);
+ if (man_id == 0x01 && dev_id == 0x08) {
+ /* Check more unused bits for confirmation */
+ if (lm80_read_value(client, LM96080_REG_CONV_RATE) & 0xfe)
+ return -ENODEV;
+
+ name = "lm96080";
+ } else {
+ /* Check 6-bit addressing */
+ for (i = 0x2a; i <= 0x3d; i++) {
+ cur = i2c_smbus_read_byte_data(client, i);
+ if ((i2c_smbus_read_byte_data(client, i + 0x40) != cur)
+ || (i2c_smbus_read_byte_data(client, i + 0x80) != cur)
+ || (i2c_smbus_read_byte_data(client, i + 0xc0) != cur))
+ return -ENODEV;
+ }
+
+ name = "lm80";
+ }
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int lm80_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct lm80_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct lm80_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM80 chip */
+ lm80_init_client(client);
+
+ /* A few vars need to be filled upon startup */
+ 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);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id lm80_id[] = {
+ { "lm80", 0 },
+ { "lm96080", 1 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm80_id);
+
+static struct i2c_driver lm80_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm80",
+ },
+ .probe_new = lm80_probe,
+ .id_table = lm80_id,
+ .detect = lm80_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm80_driver);
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
+ "Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("LM80 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
new file mode 100644
index 000000000..616449f2c
--- /dev/null
+++ b/drivers/hwmon/lm83.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm83.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2003-2009 Jean Delvare <jdelvare@suse.de>
+ *
+ * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
+ * a sensor chip made by National Semiconductor. It reports up to four
+ * temperatures (its own plus up to three external ones) with a 1 deg
+ * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained
+ * from National's website at:
+ * http://www.national.com/pf/LM/LM83.html
+ * Since the datasheet omits to give the chip stepping code, I give it
+ * here: 0x03 (at register 0xff).
+ *
+ * Also supports the LM82 temp sensor, which is basically a stripped down
+ * model of the LM83. Datasheet is here:
+ * http://www.national.com/pf/LM/LM82.html
+ */
+
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/*
+ * Addresses to scan
+ * Address is selected using 2 three-level pins, resulting in 9 possible
+ * addresses.
+ */
+
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+
+enum chips { lm83, lm82 };
+
+/*
+ * The LM83 registers
+ * Manufacturer ID is 0x01 for National Semiconductor.
+ */
+
+#define LM83_REG_R_MAN_ID 0xFE
+#define LM83_REG_R_CHIP_ID 0xFF
+#define LM83_REG_R_CONFIG 0x03
+#define LM83_REG_W_CONFIG 0x09
+#define LM83_REG_R_STATUS1 0x02
+#define LM83_REG_R_STATUS2 0x35
+#define LM83_REG_R_LOCAL_TEMP 0x00
+#define LM83_REG_R_LOCAL_HIGH 0x05
+#define LM83_REG_W_LOCAL_HIGH 0x0B
+#define LM83_REG_R_REMOTE1_TEMP 0x30
+#define LM83_REG_R_REMOTE1_HIGH 0x38
+#define LM83_REG_W_REMOTE1_HIGH 0x50
+#define LM83_REG_R_REMOTE2_TEMP 0x01
+#define LM83_REG_R_REMOTE2_HIGH 0x07
+#define LM83_REG_W_REMOTE2_HIGH 0x0D
+#define LM83_REG_R_REMOTE3_TEMP 0x31
+#define LM83_REG_R_REMOTE3_HIGH 0x3A
+#define LM83_REG_W_REMOTE3_HIGH 0x52
+#define LM83_REG_R_TCRIT 0x42
+#define LM83_REG_W_TCRIT 0x5A
+
+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,
+};
+
+/* 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)
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm83_data {
+ struct regmap *regmap;
+ enum chips type;
+};
+
+/* regmap code */
+
+static int lm83_regmap_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+}
+
+/*
+ * 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;
+
+ 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;
+ }
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+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;
+ }
+}
+
+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,
+};
+
+/* hwmon API */
+
+static int lm83_temp_read(struct device *dev, u32 attr, int channel, long *val)
+{
+ 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], &regval);
+ if (err < 0)
+ return err;
+ *val = (s8)regval * 1000;
+ break;
+ case hwmon_temp_max:
+ err = regmap_read(data->regmap, LM83_REG_MAX[channel], &regval);
+ if (err < 0)
+ return err;
+ *val = (s8)regval * 1000;
+ break;
+ case hwmon_temp_crit:
+ err = regmap_read(data->regmap, LM83_REG_R_TCRIT, &regval);
+ if (err < 0)
+ return err;
+ *val = (s8)regval * 1000;
+ break;
+ case hwmon_temp_max_alarm:
+ err = regmap_read(data->regmap, LM83_ALARM_REG[channel], &regval);
+ 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], &regval);
+ 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], &regval);
+ if (err < 0)
+ return err;
+ *val = !!(regval & LM83_FAULT_BIT[channel]);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int lm83_temp_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct lm83_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err;
+
+ regval = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
+
+ 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 int lm83_chip_read(struct device *dev, u32 attr, int channel, long *val)
+{
+ 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, &regval);
+ if (err < 0)
+ return err;
+ *val = regval;
+ err = regmap_read(data->regmap, LM83_REG_R_STATUS2, &regval);
+ if (err < 0)
+ return err;
+ *val |= regval << 8;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int lm83_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ 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;
+ }
+}
+
+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 umode_t lm83_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct lm83_data *data = _data;
+
+ /*
+ * 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 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 hwmon_ops lm83_hwmon_ops = {
+ .is_visible = lm83_is_visible,
+ .read = lm83_read,
+ .write = lm83_write,
+};
+
+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 *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *name;
+ u8 man_id, chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Detection */
+ 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",
+ client->addr);
+ return -ENODEV;
+ }
+
+ /* Identification */
+ 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(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:
+ name = "lm82";
+ break;
+ default:
+ /* identification failed */
+ dev_dbg(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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(dev, sizeof(struct lm83_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init(dev, NULL, client, &lm83_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ data->type = i2c_match_id(lm83_id, client)->driver_data;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &lm83_chip_info, NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver lm83_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm83",
+ },
+ .probe_new = lm83_probe,
+ .id_table = lm83_id,
+ .detect = lm83_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm83_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("LM83 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
new file mode 100644
index 000000000..8d33c2484
--- /dev/null
+++ b/drivers/hwmon/lm85.c
@@ -0,0 +1,1713 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm85.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ * Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
+ * Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
+ * Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
+ * Copyright (C) 2007--2014 Jean Delvare <jdelvare@suse.de>
+ *
+ * Chip details at <http://www.national.com/ds/LM/LM85.pdf>
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/util_macros.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+enum chips {
+ lm85, lm96000,
+ adm1027, adt7463, adt7468,
+ emc6d100, emc6d102, emc6d103, emc6d103s
+};
+
+/* The LM85 registers */
+
+#define LM85_REG_IN(nr) (0x20 + (nr))
+#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2)
+#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2)
+
+#define LM85_REG_TEMP(nr) (0x25 + (nr))
+#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2)
+#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2)
+
+/* Fan speeds are LSB, MSB (2 bytes) */
+#define LM85_REG_FAN(nr) (0x28 + (nr) * 2)
+#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) * 2)
+
+#define LM85_REG_PWM(nr) (0x30 + (nr))
+
+#define LM85_REG_COMPANY 0x3e
+#define LM85_REG_VERSTEP 0x3f
+
+#define ADT7468_REG_CFG5 0x7c
+#define ADT7468_OFF64 (1 << 0)
+#define ADT7468_HFPWM (1 << 1)
+#define IS_ADT7468_OFF64(data) \
+ ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_OFF64))
+#define IS_ADT7468_HFPWM(data) \
+ ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_HFPWM))
+
+/* These are the recognized values for the above regs */
+#define LM85_COMPANY_NATIONAL 0x01
+#define LM85_COMPANY_ANALOG_DEV 0x41
+#define LM85_COMPANY_SMSC 0x5c
+#define LM85_VERSTEP_LM85C 0x60
+#define LM85_VERSTEP_LM85B 0x62
+#define LM85_VERSTEP_LM96000_1 0x68
+#define LM85_VERSTEP_LM96000_2 0x69
+#define LM85_VERSTEP_ADM1027 0x60
+#define LM85_VERSTEP_ADT7463 0x62
+#define LM85_VERSTEP_ADT7463C 0x6A
+#define LM85_VERSTEP_ADT7468_1 0x71
+#define LM85_VERSTEP_ADT7468_2 0x72
+#define LM85_VERSTEP_EMC6D100_A0 0x60
+#define LM85_VERSTEP_EMC6D100_A1 0x61
+#define LM85_VERSTEP_EMC6D102 0x65
+#define LM85_VERSTEP_EMC6D103_A0 0x68
+#define LM85_VERSTEP_EMC6D103_A1 0x69
+#define LM85_VERSTEP_EMC6D103S 0x6A /* Also known as EMC6D103:A2 */
+
+#define LM85_REG_CONFIG 0x40
+
+#define LM85_REG_ALARM1 0x41
+#define LM85_REG_ALARM2 0x42
+
+#define LM85_REG_VID 0x43
+
+/* Automated FAN control */
+#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr))
+#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr))
+#define LM85_REG_AFAN_SPIKE1 0x62
+#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr))
+#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr))
+#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr))
+#define LM85_REG_AFAN_HYST1 0x6d
+#define LM85_REG_AFAN_HYST2 0x6e
+
+#define ADM1027_REG_EXTEND_ADC1 0x76
+#define ADM1027_REG_EXTEND_ADC2 0x77
+
+#define EMC6D100_REG_ALARM3 0x7d
+/* IN5, IN6 and IN7 */
+#define EMC6D100_REG_IN(nr) (0x70 + ((nr) - 5))
+#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr) - 5) * 2)
+#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr) - 5) * 2)
+#define EMC6D102_REG_EXTEND_ADC1 0x85
+#define EMC6D102_REG_EXTEND_ADC2 0x86
+#define EMC6D102_REG_EXTEND_ADC3 0x87
+#define EMC6D102_REG_EXTEND_ADC4 0x88
+
+/*
+ * Conversions. Rounding and limit checking is only done on the TO_REG
+ * variants. Note that you should be a bit careful with which arguments
+ * these macros are called: arguments may be evaluated more than once.
+ */
+
+/* IN are scaled according to built-in resistors */
+static const int lm85_scaling[] = { /* .001 Volts */
+ 2500, 2250, 3300, 5000, 12000,
+ 3300, 1500, 1800 /*EMC6D100*/
+};
+#define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from))
+
+#define INS_TO_REG(n, val) \
+ SCALE(clamp_val(val, 0, 255 * lm85_scaling[n] / 192), \
+ lm85_scaling[n], 192)
+
+#define INSEXT_FROM_REG(n, val, ext) \
+ SCALE(((val) << 4) + (ext), 192 << 4, lm85_scaling[n])
+
+#define INS_FROM_REG(n, val) SCALE((val), 192, lm85_scaling[n])
+
+/* FAN speed is measured using 90kHz clock */
+static inline u16 FAN_TO_REG(unsigned long val)
+{
+ if (!val)
+ return 0xffff;
+ return clamp_val(5400000 / val, 1, 0xfffe);
+}
+#define FAN_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \
+ 5400000 / (val))
+
+/* Temperature is reported in .001 degC increments */
+#define TEMP_TO_REG(val) \
+ DIV_ROUND_CLOSEST(clamp_val((val), -127000, 127000), 1000)
+#define TEMPEXT_FROM_REG(val, ext) \
+ SCALE(((val) << 4) + (ext), 16, 1000)
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+#define PWM_TO_REG(val) clamp_val(val, 0, 255)
+#define PWM_FROM_REG(val) (val)
+
+/*
+ * ZONEs have the following parameters:
+ * Limit (low) temp, 1. degC
+ * Hysteresis (below limit), 1. degC (0-15)
+ * Range of speed control, .1 degC (2-80)
+ * Critical (high) temp, 1. degC
+ *
+ * FAN PWMs have the following parameters:
+ * Reference Zone, 1, 2, 3, etc.
+ * Spinup time, .05 sec
+ * PWM value at limit/low temp, 1 count
+ * PWM Frequency, 1. Hz
+ * PWM is Min or OFF below limit, flag
+ * Invert PWM output, flag
+ *
+ * Some chips filter the temp, others the fan.
+ * Filter constant (or disabled) .1 seconds
+ */
+
+/* These are the zone temperature range encodings in .001 degree C */
+static const int lm85_range_map[] = {
+ 2000, 2500, 3300, 4000, 5000, 6600, 8000, 10000,
+ 13300, 16000, 20000, 26600, 32000, 40000, 53300, 80000
+};
+
+static int RANGE_TO_REG(long range)
+{
+ return find_closest(range, lm85_range_map, ARRAY_SIZE(lm85_range_map));
+}
+#define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]
+
+/* These are the PWM frequency encodings */
+static const int lm85_freq_map[] = { /* 1 Hz */
+ 10, 15, 23, 30, 38, 47, 61, 94
+};
+
+static const int lm96000_freq_map[] = { /* 1 Hz */
+ 10, 15, 23, 30, 38, 47, 61, 94,
+ 22500, 24000, 25700, 25700, 27700, 27700, 30000, 30000
+};
+
+static const int adm1027_freq_map[] = { /* 1 Hz */
+ 11, 15, 22, 29, 35, 44, 59, 88
+};
+
+static int FREQ_TO_REG(const int *map,
+ unsigned int map_size, unsigned long freq)
+{
+ return find_closest(freq, map, map_size);
+}
+
+static int FREQ_FROM_REG(const int *map, unsigned int map_size, u8 reg)
+{
+ return map[reg % map_size];
+}
+
+/*
+ * Since we can't use strings, I'm abusing these numbers
+ * to stand in for the following meanings:
+ * 1 -- PWM responds to Zone 1
+ * 2 -- PWM responds to Zone 2
+ * 3 -- PWM responds to Zone 3
+ * 23 -- PWM responds to the higher temp of Zone 2 or 3
+ * 123 -- PWM responds to highest of Zone 1, 2, or 3
+ * 0 -- PWM is always at 0% (ie, off)
+ * -1 -- PWM is always at 100%
+ * -2 -- PWM responds to manual control
+ */
+
+static const int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 };
+#define ZONE_FROM_REG(val) lm85_zone_map[(val) >> 5]
+
+static int ZONE_TO_REG(int zone)
+{
+ int i;
+
+ for (i = 0; i <= 7; ++i)
+ if (zone == lm85_zone_map[i])
+ break;
+ if (i > 7) /* Not found. */
+ i = 3; /* Always 100% */
+ return i << 5;
+}
+
+#define HYST_TO_REG(val) clamp_val(((val) + 500) / 1000, 0, 15)
+#define HYST_FROM_REG(val) ((val) * 1000)
+
+/*
+ * Chip sampling rates
+ *
+ * Some sensors are not updated more frequently than once per second
+ * so it doesn't make sense to read them more often than that.
+ * We cache the results and return the saved data if the driver
+ * is called again before a second has elapsed.
+ *
+ * Also, there is significant configuration data for this chip
+ * given the automatic PWM fan control that is possible. There
+ * are about 47 bytes of config data to only 22 bytes of actual
+ * readings. So, we keep the config data up to date in the cache
+ * when it is written and only sample it once every 1 *minute*
+ */
+#define LM85_DATA_INTERVAL (HZ + HZ / 2)
+#define LM85_CONFIG_INTERVAL (1 * 60 * HZ)
+
+/*
+ * LM85 can automatically adjust fan speeds based on temperature
+ * This structure encapsulates an entire Zone config. There are
+ * three zones (one for each temperature input) on the lm85
+ */
+struct lm85_zone {
+ s8 limit; /* Low temp limit */
+ u8 hyst; /* Low limit hysteresis. (0-15) */
+ u8 range; /* Temp range, encoded */
+ s8 critical; /* "All fans ON" temp limit */
+ u8 max_desired; /*
+ * Actual "max" temperature specified. Preserved
+ * to prevent "drift" as other autofan control
+ * values change.
+ */
+};
+
+struct lm85_autofan {
+ u8 config; /* Register value */
+ u8 min_pwm; /* Minimum PWM value, encoded */
+ u8 min_off; /* Min PWM or OFF below "limit", flag */
+};
+
+/*
+ * For each registered chip, we need to keep some data in memory.
+ * The structure is dynamically allocated.
+ */
+struct lm85_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[6];
+ const int *freq_map;
+ unsigned int freq_map_size;
+
+ enum chips type;
+
+ bool has_vid5; /* true if VID5 is configured for ADT7463 or ADT7468 */
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_reading; /* In jiffies */
+ unsigned long last_config; /* In jiffies */
+
+ u8 in[8]; /* Register value */
+ u8 in_max[8]; /* Register value */
+ u8 in_min[8]; /* Register value */
+ s8 temp[3]; /* Register value */
+ s8 temp_min[3]; /* Register value */
+ s8 temp_max[3]; /* Register value */
+ u16 fan[4]; /* Register value */
+ u16 fan_min[4]; /* Register value */
+ u8 pwm[3]; /* Register value */
+ u8 pwm_freq[3]; /* Register encoding */
+ u8 temp_ext[3]; /* Decoded values */
+ u8 in_ext[8]; /* Decoded values */
+ u8 vid; /* Register value */
+ u8 vrm; /* VRM version */
+ u32 alarms; /* Register encoding, combined */
+ u8 cfg5; /* Config Register 5 on ADT7468 */
+ struct lm85_autofan autofan[3];
+ struct lm85_zone zone[3];
+};
+
+static int lm85_read_value(struct i2c_client *client, u8 reg)
+{
+ int res;
+
+ /* What size location is it? */
+ switch (reg) {
+ case LM85_REG_FAN(0): /* Read WORD data */
+ case LM85_REG_FAN(1):
+ case LM85_REG_FAN(2):
+ case LM85_REG_FAN(3):
+ case LM85_REG_FAN_MIN(0):
+ case LM85_REG_FAN_MIN(1):
+ case LM85_REG_FAN_MIN(2):
+ case LM85_REG_FAN_MIN(3):
+ case LM85_REG_ALARM1: /* Read both bytes at once */
+ res = i2c_smbus_read_byte_data(client, reg) & 0xff;
+ res |= i2c_smbus_read_byte_data(client, reg + 1) << 8;
+ break;
+ default: /* Read BYTE data */
+ res = i2c_smbus_read_byte_data(client, reg);
+ break;
+ }
+
+ return res;
+}
+
+static void lm85_write_value(struct i2c_client *client, u8 reg, int value)
+{
+ switch (reg) {
+ case LM85_REG_FAN(0): /* Write WORD data */
+ case LM85_REG_FAN(1):
+ case LM85_REG_FAN(2):
+ case LM85_REG_FAN(3):
+ case LM85_REG_FAN_MIN(0):
+ case LM85_REG_FAN_MIN(1):
+ case LM85_REG_FAN_MIN(2):
+ case LM85_REG_FAN_MIN(3):
+ /* NOTE: ALARM is read only, so not included here */
+ i2c_smbus_write_byte_data(client, reg, value & 0xff);
+ i2c_smbus_write_byte_data(client, reg + 1, value >> 8);
+ break;
+ default: /* Write BYTE data */
+ i2c_smbus_write_byte_data(client, reg, value);
+ break;
+ }
+}
+
+static struct lm85_data *lm85_update_device(struct device *dev)
+{
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (!data->valid ||
+ time_after(jiffies, data->last_reading + LM85_DATA_INTERVAL)) {
+ /* Things that change quickly */
+ dev_dbg(&client->dev, "Reading sensor values\n");
+
+ /*
+ * Have to read extended bits first to "freeze" the
+ * more significant bits that are read later.
+ * There are 2 additional resolution bits per channel and we
+ * have room for 4, so we shift them to the left.
+ */
+ if (data->type == adm1027 || data->type == adt7463 ||
+ data->type == adt7468) {
+ int ext1 = lm85_read_value(client,
+ ADM1027_REG_EXTEND_ADC1);
+ int ext2 = lm85_read_value(client,
+ ADM1027_REG_EXTEND_ADC2);
+ int val = (ext1 << 8) + ext2;
+
+ for (i = 0; i <= 4; i++)
+ data->in_ext[i] =
+ ((val >> (i * 2)) & 0x03) << 2;
+
+ for (i = 0; i <= 2; i++)
+ data->temp_ext[i] =
+ (val >> ((i + 4) * 2)) & 0x0c;
+ }
+
+ data->vid = lm85_read_value(client, LM85_REG_VID);
+
+ for (i = 0; i <= 3; ++i) {
+ data->in[i] =
+ lm85_read_value(client, LM85_REG_IN(i));
+ data->fan[i] =
+ lm85_read_value(client, LM85_REG_FAN(i));
+ }
+
+ if (!data->has_vid5)
+ data->in[4] = lm85_read_value(client, LM85_REG_IN(4));
+
+ if (data->type == adt7468)
+ data->cfg5 = lm85_read_value(client, ADT7468_REG_CFG5);
+
+ for (i = 0; i <= 2; ++i) {
+ data->temp[i] =
+ lm85_read_value(client, LM85_REG_TEMP(i));
+ data->pwm[i] =
+ lm85_read_value(client, LM85_REG_PWM(i));
+
+ if (IS_ADT7468_OFF64(data))
+ data->temp[i] -= 64;
+ }
+
+ data->alarms = lm85_read_value(client, LM85_REG_ALARM1);
+
+ if (data->type == emc6d100) {
+ /* Three more voltage sensors */
+ for (i = 5; i <= 7; ++i) {
+ data->in[i] = lm85_read_value(client,
+ EMC6D100_REG_IN(i));
+ }
+ /* More alarm bits */
+ data->alarms |= lm85_read_value(client,
+ EMC6D100_REG_ALARM3) << 16;
+ } else if (data->type == emc6d102 || data->type == emc6d103 ||
+ data->type == emc6d103s) {
+ /*
+ * Have to read LSB bits after the MSB ones because
+ * the reading of the MSB bits has frozen the
+ * LSBs (backward from the ADM1027).
+ */
+ int ext1 = lm85_read_value(client,
+ EMC6D102_REG_EXTEND_ADC1);
+ int ext2 = lm85_read_value(client,
+ EMC6D102_REG_EXTEND_ADC2);
+ int ext3 = lm85_read_value(client,
+ EMC6D102_REG_EXTEND_ADC3);
+ int ext4 = lm85_read_value(client,
+ EMC6D102_REG_EXTEND_ADC4);
+ data->in_ext[0] = ext3 & 0x0f;
+ data->in_ext[1] = ext4 & 0x0f;
+ data->in_ext[2] = ext4 >> 4;
+ data->in_ext[3] = ext3 >> 4;
+ data->in_ext[4] = ext2 >> 4;
+
+ data->temp_ext[0] = ext1 & 0x0f;
+ data->temp_ext[1] = ext2 & 0x0f;
+ data->temp_ext[2] = ext1 >> 4;
+ }
+
+ data->last_reading = jiffies;
+ } /* last_reading */
+
+ if (!data->valid ||
+ time_after(jiffies, data->last_config + LM85_CONFIG_INTERVAL)) {
+ /* Things that don't change often */
+ dev_dbg(&client->dev, "Reading config values\n");
+
+ for (i = 0; i <= 3; ++i) {
+ data->in_min[i] =
+ lm85_read_value(client, LM85_REG_IN_MIN(i));
+ data->in_max[i] =
+ lm85_read_value(client, LM85_REG_IN_MAX(i));
+ data->fan_min[i] =
+ lm85_read_value(client, LM85_REG_FAN_MIN(i));
+ }
+
+ if (!data->has_vid5) {
+ data->in_min[4] = lm85_read_value(client,
+ LM85_REG_IN_MIN(4));
+ data->in_max[4] = lm85_read_value(client,
+ LM85_REG_IN_MAX(4));
+ }
+
+ if (data->type == emc6d100) {
+ for (i = 5; i <= 7; ++i) {
+ data->in_min[i] = lm85_read_value(client,
+ EMC6D100_REG_IN_MIN(i));
+ data->in_max[i] = lm85_read_value(client,
+ EMC6D100_REG_IN_MAX(i));
+ }
+ }
+
+ for (i = 0; i <= 2; ++i) {
+ int val;
+
+ data->temp_min[i] =
+ lm85_read_value(client, LM85_REG_TEMP_MIN(i));
+ data->temp_max[i] =
+ lm85_read_value(client, LM85_REG_TEMP_MAX(i));
+
+ data->autofan[i].config =
+ lm85_read_value(client, LM85_REG_AFAN_CONFIG(i));
+ val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i));
+ data->pwm_freq[i] = val % data->freq_map_size;
+ data->zone[i].range = val >> 4;
+ data->autofan[i].min_pwm =
+ lm85_read_value(client, LM85_REG_AFAN_MINPWM(i));
+ data->zone[i].limit =
+ lm85_read_value(client, LM85_REG_AFAN_LIMIT(i));
+ data->zone[i].critical =
+ lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i));
+
+ if (IS_ADT7468_OFF64(data)) {
+ data->temp_min[i] -= 64;
+ data->temp_max[i] -= 64;
+ data->zone[i].limit -= 64;
+ data->zone[i].critical -= 64;
+ }
+ }
+
+ if (data->type != emc6d103s) {
+ i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);
+ data->autofan[0].min_off = (i & 0x20) != 0;
+ data->autofan[1].min_off = (i & 0x40) != 0;
+ data->autofan[2].min_off = (i & 0x80) != 0;
+
+ i = lm85_read_value(client, LM85_REG_AFAN_HYST1);
+ data->zone[0].hyst = i >> 4;
+ data->zone[1].hyst = i & 0x0f;
+
+ i = lm85_read_value(client, LM85_REG_AFAN_HYST2);
+ data->zone[2].hyst = i >> 4;
+ }
+
+ data->last_config = jiffies;
+ } /* last_config */
+
+ data->valid = true;
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* 4 Fans */
+static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr]));
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr]));
+}
+
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_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->fan_min[nr] = FAN_TO_REG(val);
+ lm85_write_value(client, LM85_REG_FAN_MIN(nr), data->fan_min[nr]);
+ 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(fan2_input, fan, 1);
+static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
+static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3);
+static SENSOR_DEVICE_ATTR_RW(fan4_min, fan_min, 3);
+
+/* vid, vrm, alarms */
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm85_data *data = lm85_update_device(dev);
+ int vid;
+
+ if (data->has_vid5) {
+ /* 6-pin VID (VRM 10) */
+ vid = vid_from_reg(data->vid & 0x3f, data->vrm);
+ } else {
+ /* 5-pin VID (VRM 9) */
+ vid = vid_from_reg(data->vid & 0x1f, data->vrm);
+ }
+
+ return sprintf(buf, "%d\n", vid);
+}
+
+static DEVICE_ATTR_RO(cpu0_vid);
+
+static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm85_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 lm85_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 lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%u\n", (data->alarms >> nr) & 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, 18);
+static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 16);
+static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 17);
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp1_fault, alarm, 14);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 15);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 10);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, 12);
+static SENSOR_DEVICE_ATTR_RO(fan4_alarm, alarm, 13);
+
+/* pwm */
+
+static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
+}
+
+static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_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->pwm[nr] = PWM_TO_REG(val);
+ lm85_write_value(client, LM85_REG_PWM(nr), data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ int pwm_zone, enable;
+
+ pwm_zone = ZONE_FROM_REG(data->autofan[nr].config);
+ switch (pwm_zone) {
+ case -1: /* PWM is always at 100% */
+ enable = 0;
+ break;
+ case 0: /* PWM is always at 0% */
+ case -2: /* PWM responds to manual control */
+ enable = 1;
+ break;
+ default: /* PWM in automatic mode */
+ enable = 2;
+ }
+ return sprintf(buf, "%d\n", enable);
+}
+
+static ssize_t pwm_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 config;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ switch (val) {
+ case 0:
+ config = 3;
+ break;
+ case 1:
+ config = 7;
+ break;
+ case 2:
+ /*
+ * Here we have to choose arbitrarily one of the 5 possible
+ * configurations; I go for the safest
+ */
+ config = 6;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->autofan[nr].config = lm85_read_value(client,
+ LM85_REG_AFAN_CONFIG(nr));
+ data->autofan[nr].config = (data->autofan[nr].config & ~0xe0)
+ | (config << 5);
+ lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr),
+ data->autofan[nr].config);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm_freq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ int freq;
+
+ if (IS_ADT7468_HFPWM(data))
+ freq = 22500;
+ else
+ freq = FREQ_FROM_REG(data->freq_map, data->freq_map_size,
+ data->pwm_freq[nr]);
+
+ return sprintf(buf, "%d\n", freq);
+}
+
+static ssize_t pwm_freq_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_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);
+ /*
+ * The ADT7468 has a special high-frequency PWM output mode,
+ * where all PWM outputs are driven by a 22.5 kHz clock.
+ * This might confuse the user, but there's not much we can do.
+ */
+ if (data->type == adt7468 && val >= 11300) { /* High freq. mode */
+ data->cfg5 &= ~ADT7468_HFPWM;
+ lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5);
+ } else { /* Low freq. mode */
+ data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map,
+ data->freq_map_size, val);
+ lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+ (data->zone[nr].range << 4)
+ | data->pwm_freq[nr]);
+ if (data->type == adt7468) {
+ data->cfg5 |= ADT7468_HFPWM;
+ lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5);
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_enable, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_freq, pwm_freq, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_enable, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm2_freq, pwm_freq, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_enable, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm3_freq, pwm_freq, 2);
+
+/* Voltages */
+
+static ssize_t in_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", INSEXT_FROM_REG(nr, data->in[nr],
+ data->in_ext[nr]));
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(nr, data->in_min[nr]));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[nr] = INS_TO_REG(nr, val);
+ lm85_write_value(client, LM85_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 *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", INS_FROM_REG(nr, data->in_max[nr]));
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_max[nr] = INS_TO_REG(nr, val);
+ lm85_write_value(client, LM85_REG_IN_MAX(nr), data->in_max[nr]);
+ 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);
+static SENSOR_DEVICE_ATTR_RO(in6_input, in, 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, 7);
+static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 7);
+static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 7);
+
+/* Temps */
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", TEMPEXT_FROM_REG(data->temp[nr],
+ data->temp_ext[nr]));
+}
+
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
+}
+
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (IS_ADT7468_OFF64(data))
+ val += 64;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = TEMP_TO_REG(val);
+ lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (IS_ADT7468_OFF64(data))
+ val += 64;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = TEMP_TO_REG(val);
+ lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
+
+/* Automatic PWM control */
+
+static ssize_t pwm_auto_channels_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", ZONE_FROM_REG(data->autofan[nr].config));
+}
+
+static ssize_t pwm_auto_channels_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->autofan[nr].config = (data->autofan[nr].config & (~0xe0))
+ | ZONE_TO_REG(val);
+ lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr),
+ data->autofan[nr].config);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm_auto_pwm_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", PWM_FROM_REG(data->autofan[nr].min_pwm));
+}
+
+static ssize_t pwm_auto_pwm_min_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_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->autofan[nr].min_pwm = PWM_TO_REG(val);
+ lm85_write_value(client, LM85_REG_AFAN_MINPWM(nr),
+ data->autofan[nr].min_pwm);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm_auto_pwm_minctl_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", data->autofan[nr].min_off);
+}
+
+static ssize_t pwm_auto_pwm_minctl_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 tmp;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->autofan[nr].min_off = val;
+ tmp = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);
+ tmp &= ~(0x20 << nr);
+ if (data->autofan[nr].min_off)
+ tmp |= 0x20 << nr;
+ lm85_write_value(client, LM85_REG_AFAN_SPIKE1, tmp);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels, pwm_auto_channels, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_pwm_min, pwm_auto_pwm_min, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_pwm_minctl, pwm_auto_pwm_minctl, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels, pwm_auto_channels, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_pwm_min, pwm_auto_pwm_min, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_pwm_minctl, pwm_auto_pwm_minctl, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_channels, pwm_auto_channels, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_pwm_min, pwm_auto_pwm_min, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_pwm_minctl, pwm_auto_pwm_minctl, 2);
+
+/* Temperature settings for automatic PWM control */
+
+static ssize_t temp_auto_temp_off_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->zone[nr].limit) -
+ HYST_FROM_REG(data->zone[nr].hyst));
+}
+
+static ssize_t temp_auto_temp_off_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int min;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ min = TEMP_FROM_REG(data->zone[nr].limit);
+ data->zone[nr].hyst = HYST_TO_REG(min - val);
+ if (nr == 0 || nr == 1) {
+ lm85_write_value(client, LM85_REG_AFAN_HYST1,
+ (data->zone[0].hyst << 4)
+ | data->zone[1].hyst);
+ } else {
+ lm85_write_value(client, LM85_REG_AFAN_HYST2,
+ (data->zone[2].hyst << 4));
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_auto_temp_min_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->zone[nr].limit));
+}
+
+static ssize_t temp_auto_temp_min_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->zone[nr].limit = TEMP_TO_REG(val);
+ lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr),
+ data->zone[nr].limit);
+
+/* Update temp_auto_max and temp_auto_range */
+ data->zone[nr].range = RANGE_TO_REG(
+ TEMP_FROM_REG(data->zone[nr].max_desired) -
+ TEMP_FROM_REG(data->zone[nr].limit));
+ lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+ ((data->zone[nr].range & 0x0f) << 4)
+ | data->pwm_freq[nr]);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_auto_temp_max_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->zone[nr].limit) +
+ RANGE_FROM_REG(data->zone[nr].range));
+}
+
+static ssize_t temp_auto_temp_max_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int min;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ min = TEMP_FROM_REG(data->zone[nr].limit);
+ data->zone[nr].max_desired = TEMP_TO_REG(val);
+ data->zone[nr].range = RANGE_TO_REG(
+ val - min);
+ lm85_write_value(client, LM85_REG_AFAN_RANGE(nr),
+ ((data->zone[nr].range & 0x0f) << 4)
+ | data->pwm_freq[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_auto_temp_crit_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = lm85_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->zone[nr].critical));
+}
+
+static ssize_t temp_auto_temp_crit_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct lm85_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->zone[nr].critical = TEMP_TO_REG(val);
+ lm85_write_value(client, LM85_REG_AFAN_CRITICAL(nr),
+ data->zone[nr].critical);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_temp_off, temp_auto_temp_off, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_temp_min, temp_auto_temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_temp_max, temp_auto_temp_max, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_temp_crit, temp_auto_temp_crit, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_temp_off, temp_auto_temp_off, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_temp_min, temp_auto_temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_temp_max, temp_auto_temp_max, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_temp_crit, temp_auto_temp_crit, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_temp_off, temp_auto_temp_off, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_temp_min, temp_auto_temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_temp_max, temp_auto_temp_max, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_temp_crit, temp_auto_temp_crit, 2);
+
+static struct attribute *lm85_attributes[] = {
+ &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_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_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_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm3.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_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.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_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.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_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.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_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1_auto_channels.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_channels.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_auto_temp_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_temp_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_temp_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_temp_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_temp_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_temp_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_temp_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_temp_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_temp_crit.dev_attr.attr,
+
+ &dev_attr_vrm.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group lm85_group = {
+ .attrs = lm85_attributes,
+};
+
+static struct attribute *lm85_attributes_minctl[] = {
+ &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm85_group_minctl = {
+ .attrs = lm85_attributes_minctl,
+};
+
+static struct attribute *lm85_attributes_temp_off[] = {
+ &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_temp_off.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm85_group_temp_off = {
+ .attrs = lm85_attributes_temp_off,
+};
+
+static struct attribute *lm85_attributes_in4[] = {
+ &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,
+ NULL
+};
+
+static const struct attribute_group lm85_group_in4 = {
+ .attrs = lm85_attributes_in4,
+};
+
+static struct attribute *lm85_attributes_in567[] = {
+ &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_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm85_group_in567 = {
+ .attrs = lm85_attributes_in567,
+};
+
+static void lm85_init_client(struct i2c_client *client)
+{
+ int value;
+
+ /* Start monitoring if needed */
+ value = lm85_read_value(client, LM85_REG_CONFIG);
+ if (!(value & 0x01)) {
+ dev_info(&client->dev, "Starting monitoring\n");
+ lm85_write_value(client, LM85_REG_CONFIG, value | 0x01);
+ }
+
+ /* Warn about unusual configuration bits */
+ if (value & 0x02)
+ dev_warn(&client->dev, "Device configuration is locked\n");
+ if (!(value & 0x04))
+ dev_warn(&client->dev, "Device is not ready\n");
+}
+
+static int lm85_is_fake(struct i2c_client *client)
+{
+ /*
+ * Differenciate between real LM96000 and Winbond WPCD377I. The latter
+ * emulate the former except that it has no hardware monitoring function
+ * so the readings are always 0.
+ */
+ int i;
+ u8 in_temp, fan;
+
+ for (i = 0; i < 8; i++) {
+ in_temp = i2c_smbus_read_byte_data(client, 0x20 + i);
+ fan = i2c_smbus_read_byte_data(client, 0x28 + i);
+ if (in_temp != 0x00 || fan != 0xff)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ const char *type_name = NULL;
+ int company, verstep;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ /* We need to be able to do byte I/O */
+ return -ENODEV;
+ }
+
+ /* Determine the chip type */
+ company = lm85_read_value(client, LM85_REG_COMPANY);
+ verstep = lm85_read_value(client, LM85_REG_VERSTEP);
+
+ dev_dbg(&adapter->dev,
+ "Detecting device at 0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+ address, company, verstep);
+
+ if (company == LM85_COMPANY_NATIONAL) {
+ switch (verstep) {
+ case LM85_VERSTEP_LM85C:
+ type_name = "lm85c";
+ break;
+ case LM85_VERSTEP_LM85B:
+ type_name = "lm85b";
+ break;
+ case LM85_VERSTEP_LM96000_1:
+ case LM85_VERSTEP_LM96000_2:
+ /* Check for Winbond WPCD377I */
+ if (lm85_is_fake(client)) {
+ dev_dbg(&adapter->dev,
+ "Found Winbond WPCD377I, ignoring\n");
+ return -ENODEV;
+ }
+ type_name = "lm96000";
+ break;
+ }
+ } else if (company == LM85_COMPANY_ANALOG_DEV) {
+ switch (verstep) {
+ case LM85_VERSTEP_ADM1027:
+ type_name = "adm1027";
+ break;
+ case LM85_VERSTEP_ADT7463:
+ case LM85_VERSTEP_ADT7463C:
+ type_name = "adt7463";
+ break;
+ case LM85_VERSTEP_ADT7468_1:
+ case LM85_VERSTEP_ADT7468_2:
+ type_name = "adt7468";
+ break;
+ }
+ } else if (company == LM85_COMPANY_SMSC) {
+ switch (verstep) {
+ case LM85_VERSTEP_EMC6D100_A0:
+ case LM85_VERSTEP_EMC6D100_A1:
+ /* Note: we can't tell a '100 from a '101 */
+ type_name = "emc6d100";
+ break;
+ case LM85_VERSTEP_EMC6D102:
+ type_name = "emc6d102";
+ break;
+ case LM85_VERSTEP_EMC6D103_A0:
+ case LM85_VERSTEP_EMC6D103_A1:
+ type_name = "emc6d103";
+ break;
+ case LM85_VERSTEP_EMC6D103S:
+ type_name = "emc6d103s";
+ break;
+ }
+ }
+
+ if (!type_name)
+ return -ENODEV;
+
+ strscpy(info->type, type_name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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;
+ struct lm85_data *data;
+ int idx = 0;
+
+ data = devm_kzalloc(dev, sizeof(struct lm85_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ if (client->dev.of_node)
+ data->type = (enum chips)of_device_get_match_data(&client->dev);
+ else
+ data->type = i2c_match_id(lm85_id, client)->driver_data;
+ mutex_init(&data->update_lock);
+
+ /* Fill in the chip specific driver values */
+ switch (data->type) {
+ case adm1027:
+ case adt7463:
+ case adt7468:
+ case emc6d100:
+ case emc6d102:
+ case emc6d103:
+ case emc6d103s:
+ data->freq_map = adm1027_freq_map;
+ data->freq_map_size = ARRAY_SIZE(adm1027_freq_map);
+ break;
+ case lm96000:
+ data->freq_map = lm96000_freq_map;
+ data->freq_map_size = ARRAY_SIZE(lm96000_freq_map);
+ break;
+ default:
+ data->freq_map = lm85_freq_map;
+ data->freq_map_size = ARRAY_SIZE(lm85_freq_map);
+ }
+
+ /* Set the VRM version */
+ data->vrm = vid_which_vrm();
+
+ /* Initialize the LM85 chip */
+ lm85_init_client(client);
+
+ /* sysfs hooks */
+ data->groups[idx++] = &lm85_group;
+
+ /* minctl and temp_off exist on all chips except emc6d103s */
+ if (data->type != emc6d103s) {
+ data->groups[idx++] = &lm85_group_minctl;
+ data->groups[idx++] = &lm85_group_temp_off;
+ }
+
+ /*
+ * The ADT7463/68 have an optional VRM 10 mode where pin 21 is used
+ * as a sixth digital VID input rather than an analog input.
+ */
+ if (data->type == adt7463 || data->type == adt7468) {
+ u8 vid = lm85_read_value(client, LM85_REG_VID);
+ if (vid & 0x80)
+ data->has_vid5 = true;
+ }
+
+ if (!data->has_vid5)
+ data->groups[idx++] = &lm85_group_in4;
+
+ /* The EMC6D100 has 3 additional voltage inputs */
+ if (data->type == emc6d100)
+ data->groups[idx++] = &lm85_group_in567;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id lm85_id[] = {
+ { "adm1027", adm1027 },
+ { "adt7463", adt7463 },
+ { "adt7468", adt7468 },
+ { "lm85", lm85 },
+ { "lm85b", lm85 },
+ { "lm85c", lm85 },
+ { "lm96000", lm96000 },
+ { "emc6d100", emc6d100 },
+ { "emc6d101", emc6d100 },
+ { "emc6d102", emc6d102 },
+ { "emc6d103", emc6d103 },
+ { "emc6d103s", emc6d103s },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm85_id);
+
+static const struct of_device_id __maybe_unused lm85_of_match[] = {
+ {
+ .compatible = "adi,adm1027",
+ .data = (void *)adm1027
+ },
+ {
+ .compatible = "adi,adt7463",
+ .data = (void *)adt7463
+ },
+ {
+ .compatible = "adi,adt7468",
+ .data = (void *)adt7468
+ },
+ {
+ .compatible = "national,lm85",
+ .data = (void *)lm85
+ },
+ {
+ .compatible = "national,lm85b",
+ .data = (void *)lm85
+ },
+ {
+ .compatible = "national,lm85c",
+ .data = (void *)lm85
+ },
+ {
+ .compatible = "ti,lm96000",
+ .data = (void *)lm96000
+ },
+ {
+ .compatible = "smsc,emc6d100",
+ .data = (void *)emc6d100
+ },
+ {
+ .compatible = "smsc,emc6d101",
+ .data = (void *)emc6d100
+ },
+ {
+ .compatible = "smsc,emc6d102",
+ .data = (void *)emc6d102
+ },
+ {
+ .compatible = "smsc,emc6d103",
+ .data = (void *)emc6d103
+ },
+ {
+ .compatible = "smsc,emc6d103s",
+ .data = (void *)emc6d103s
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lm85_of_match);
+
+static struct i2c_driver lm85_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm85",
+ .of_match_table = of_match_ptr(lm85_of_match),
+ },
+ .probe_new = lm85_probe,
+ .id_table = lm85_id,
+ .detect = lm85_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm85_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, "
+ "Margit Schubert-While <margitsw@t-online.de>, "
+ "Justin Thiessen <jthiessen@penguincomputing.com>");
+MODULE_DESCRIPTION("LM85-B, LM85-C driver");
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
new file mode 100644
index 000000000..818fb6195
--- /dev/null
+++ b/drivers/hwmon/lm87.c
@@ -0,0 +1,1007 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm87.c
+ *
+ * Copyright (C) 2000 Frodo Looijaard <frodol@dds.nl>
+ * Philip Edelbrock <phil@netroedge.com>
+ * Stephen Rousset <stephen.rousset@rocketlogix.com>
+ * Dan Eaton <dan.eaton@rocketlogix.com>
+ * Copyright (C) 2004-2008 Jean Delvare <jdelvare@suse.de>
+ *
+ * Original port to Linux 2.6 by Jeff Oliver.
+ *
+ * The LM87 is a sensor chip made by National Semiconductor. It monitors up
+ * to 8 voltages (including its own power source), up to three temperatures
+ * (its own plus up to two external ones) and up to two fans. The default
+ * configuration is 6 voltages, two temperatures and two fans (see below).
+ * Voltages are scaled internally with ratios such that the nominal value of
+ * each voltage correspond to a register value of 192 (which means a
+ * resolution of about 0.5% of the nominal value). Temperature values are
+ * reported with a 1 deg resolution and a 3-4 deg accuracy. Complete
+ * datasheet can be obtained from National's website at:
+ * http://www.national.com/pf/LM/LM87.html
+ *
+ * Some functions share pins, so not all functions are available at the same
+ * time. Which are depends on the hardware setup. This driver normally
+ * assumes that firmware configured the chip correctly. Where this is not
+ * the case, platform code must set the I2C client's platform_data to point
+ * to a u8 value to be written to the channel register.
+ * For reference, here is the list of exclusive functions:
+ * - in0+in5 (default) or temp3
+ * - fan1 (default) or in6
+ * - fan2 (default) or in7
+ * - VID lines (default) or IRQ lines (not handled by this driver)
+ *
+ * The LM87 additionally features an analog output, supposedly usable to
+ * control the speed of a fan. All new chips use pulse width modulation
+ * instead. The LM87 is the only hardware monitoring chipset I know of
+ * which uses amplitude modulation. Be careful when using this feature.
+ *
+ * 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:
+ * https://www.analog.com/en/prod/0,2877,ADM1024,00.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.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/regulator/consumer.h>
+
+/*
+ * Addresses to scan
+ * LM87 has three possible addresses: 0x2c, 0x2d and 0x2e.
+ */
+
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+/*
+ * The LM87 registers
+ */
+
+/* nr in 0..5 */
+#define LM87_REG_IN(nr) (0x20 + (nr))
+#define LM87_REG_IN_MAX(nr) (0x2B + (nr) * 2)
+#define LM87_REG_IN_MIN(nr) (0x2C + (nr) * 2)
+/* nr in 0..1 */
+#define LM87_REG_AIN(nr) (0x28 + (nr))
+#define LM87_REG_AIN_MIN(nr) (0x1A + (nr))
+#define LM87_REG_AIN_MAX(nr) (0x3B + (nr))
+
+static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 };
+static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B };
+static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
+
+#define LM87_REG_TEMP_HW_INT_LOCK 0x13
+#define LM87_REG_TEMP_HW_EXT_LOCK 0x14
+#define LM87_REG_TEMP_HW_INT 0x17
+#define LM87_REG_TEMP_HW_EXT 0x18
+
+/* nr in 0..1 */
+#define LM87_REG_FAN(nr) (0x28 + (nr))
+#define LM87_REG_FAN_MIN(nr) (0x3B + (nr))
+#define LM87_REG_AOUT 0x19
+
+#define LM87_REG_CONFIG 0x40
+#define LM87_REG_CHANNEL_MODE 0x16
+#define LM87_REG_VID_FAN_DIV 0x47
+#define LM87_REG_VID4 0x49
+
+#define LM87_REG_ALARMS1 0x41
+#define LM87_REG_ALARMS2 0x42
+
+#define LM87_REG_COMPANY_ID 0x3E
+#define LM87_REG_REVISION 0x3F
+
+/*
+ * Conversions and various macros
+ * The LM87 uses signed 8-bit values for temperatures.
+ */
+
+#define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192)
+#define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \
+ (val) >= (scale) * 255 / 192 ? 255 : \
+ ((val) * 192 + (scale) / 2) / (scale))
+
+#define TEMP_FROM_REG(reg) ((reg) * 1000)
+#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \
+ (val) >= 126500 ? 127 : \
+ (((val) < 0 ? (val) - 500 : \
+ (val) + 500) / 1000))
+
+#define FAN_FROM_REG(reg, div) ((reg) == 255 || (reg) == 0 ? 0 : \
+ (1350000 + (reg)*(div) / 2) / ((reg) * (div)))
+#define FAN_TO_REG(val, div) ((val) * (div) * 255 <= 1350000 ? 255 : \
+ (1350000 + (val)*(div) / 2) / ((val) * (div)))
+
+#define FAN_DIV_FROM_REG(reg) (1 << (reg))
+
+/* analog out is 9.80mV/LSB */
+#define AOUT_FROM_REG(reg) (((reg) * 98 + 5) / 10)
+#define AOUT_TO_REG(val) ((val) <= 0 ? 0 : \
+ (val) >= 2500 ? 255 : \
+ ((val) * 10 + 49) / 98)
+
+/* nr in 0..1 */
+#define CHAN_NO_FAN(nr) (1 << (nr))
+#define CHAN_TEMP3 (1 << 2)
+#define CHAN_VCC_5V (1 << 3)
+#define CHAN_NO_VID (1 << 7)
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm87_data {
+ struct mutex update_lock;
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 channel; /* register value */
+ u8 config; /* original register value */
+
+ u8 in[8]; /* register value */
+ u8 in_max[8]; /* register value */
+ u8 in_min[8]; /* register value */
+ u16 in_scale[8];
+
+ s8 temp[3]; /* register value */
+ s8 temp_high[3]; /* register value */
+ s8 temp_low[3]; /* register value */
+ s8 temp_crit_int; /* min of two register values */
+ s8 temp_crit_ext; /* min of two register values */
+
+ u8 fan[2]; /* register value */
+ u8 fan_min[2]; /* register value */
+ u8 fan_div[2]; /* register value, shifted right */
+ u8 aout; /* register value */
+
+ u16 alarms; /* register values, combined */
+ u8 vid; /* register values, combined */
+ u8 vrm;
+
+ const struct attribute_group *attr_groups[6];
+};
+
+static inline int lm87_read_value(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static struct lm87_data *lm87_update_device(struct device *dev)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ int i, j;
+
+ dev_dbg(&client->dev, "Updating data.\n");
+
+ i = (data->channel & CHAN_TEMP3) ? 1 : 0;
+ j = (data->channel & CHAN_TEMP3) ? 5 : 6;
+ for (; i < j; i++) {
+ data->in[i] = lm87_read_value(client,
+ LM87_REG_IN(i));
+ data->in_min[i] = lm87_read_value(client,
+ LM87_REG_IN_MIN(i));
+ data->in_max[i] = lm87_read_value(client,
+ LM87_REG_IN_MAX(i));
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (data->channel & CHAN_NO_FAN(i)) {
+ data->in[6+i] = lm87_read_value(client,
+ LM87_REG_AIN(i));
+ data->in_max[6+i] = lm87_read_value(client,
+ LM87_REG_AIN_MAX(i));
+ data->in_min[6+i] = lm87_read_value(client,
+ LM87_REG_AIN_MIN(i));
+
+ } else {
+ data->fan[i] = lm87_read_value(client,
+ LM87_REG_FAN(i));
+ data->fan_min[i] = lm87_read_value(client,
+ LM87_REG_FAN_MIN(i));
+ }
+ }
+
+ j = (data->channel & CHAN_TEMP3) ? 3 : 2;
+ for (i = 0 ; i < j; i++) {
+ data->temp[i] = lm87_read_value(client,
+ LM87_REG_TEMP[i]);
+ data->temp_high[i] = lm87_read_value(client,
+ LM87_REG_TEMP_HIGH[i]);
+ data->temp_low[i] = lm87_read_value(client,
+ LM87_REG_TEMP_LOW[i]);
+ }
+
+ i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK);
+ j = lm87_read_value(client, LM87_REG_TEMP_HW_INT);
+ data->temp_crit_int = min(i, j);
+
+ i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK);
+ j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT);
+ data->temp_crit_ext = min(i, j);
+
+ i = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = (i >> 6) & 0x03;
+ data->vid = (i & 0x0F)
+ | (lm87_read_value(client, LM87_REG_VID4) & 0x01)
+ << 4;
+
+ data->alarms = lm87_read_value(client, LM87_REG_ALARMS1)
+ | (lm87_read_value(client, LM87_REG_ALARMS2)
+ << 8);
+ data->aout = lm87_read_value(client, LM87_REG_AOUT);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t in_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in[nr],
+ data->in_scale[nr]));
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[nr],
+ data->in_scale[nr]));
+}
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[nr],
+ data->in_scale[nr]));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+ int nr = to_sensor_dev_attr(attr)->index;
+ 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, data->in_scale[nr]);
+ lm87_write_value(client, nr < 6 ? LM87_REG_IN_MIN(nr) :
+ LM87_REG_AIN_MIN(nr - 6), data->in_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+ int nr = to_sensor_dev_attr(attr)->index;
+ 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, data->in_scale[nr]);
+ lm87_write_value(client, nr < 6 ? LM87_REG_IN_MAX(nr) :
+ LM87_REG_AIN_MAX(nr - 6), data->in_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(in0_input, in_input, 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_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 ssize_t temp_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
+}
+
+static ssize_t temp_low_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%d\n",
+ TEMP_FROM_REG(data->temp_low[nr]));
+}
+
+static ssize_t temp_high_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%d\n",
+ TEMP_FROM_REG(data->temp_high[nr]));
+}
+
+static ssize_t temp_low_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_low[nr] = TEMP_TO_REG(val);
+ lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_high_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_high[nr] = TEMP_TO_REG(val);
+ lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_low, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_high, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_low, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_high, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_input, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_low, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_high, 2);
+
+static ssize_t temp1_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int));
+}
+
+static ssize_t temp2_crit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext));
+}
+
+static DEVICE_ATTR_RO(temp1_crit);
+static DEVICE_ATTR_RO(temp2_crit);
+static DEVICE_ATTR(temp3_crit, 0444, temp2_crit_show, NULL);
+
+static ssize_t fan_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%d\n",
+ FAN_DIV_FROM_REG(data->fan_div[nr]));
+}
+
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->fan_min[nr] = FAN_TO_REG(val,
+ FAN_DIV_FROM_REG(data->fan_div[nr]));
+ lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Note: we save and restore the fan minimum here, because its value is
+ * determined in part by the fan clock divider. This follows the principle
+ * of least surprise; the user doesn't expect the fan minimum to change just
+ * because the divider changed.
+ */
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+ int nr = to_sensor_dev_attr(attr)->index;
+ long val;
+ int err;
+ unsigned long min;
+ u8 reg;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ min = FAN_FROM_REG(data->fan_min[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr]));
+
+ switch (val) {
+ case 1:
+ data->fan_div[nr] = 0;
+ break;
+ case 2:
+ data->fan_div[nr] = 1;
+ break;
+ case 4:
+ data->fan_div[nr] = 2;
+ break;
+ case 8:
+ data->fan_div[nr] = 3;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+ switch (nr) {
+ case 0:
+ reg = (reg & 0xCF) | (data->fan_div[0] << 4);
+ break;
+ case 1:
+ reg = (reg & 0x3F) | (data->fan_div[1] << 6);
+ break;
+ }
+ lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg);
+
+ data->fan_min[nr] = FAN_TO_REG(min, val);
+ lm87_write_value(client, LM87_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_RW(fan1_div, fan_div, 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_RW(fan2_div, fan_div, 1);
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ return sprintf(buf, "%d\n", 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 lm87_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", data->vrm);
+}
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm87_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 aout_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm87_data *data = lm87_update_device(dev);
+ return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
+}
+static ssize_t aout_output_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = dev_get_drvdata(dev);
+ struct lm87_data *data = i2c_get_clientdata(client);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->aout = AOUT_TO_REG(val);
+ lm87_write_value(client, LM87_REG_AOUT, data->aout);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static DEVICE_ATTR_RW(aout_output);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm87_data *data = lm87_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);
+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(in6_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 14);
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 15);
+
+/*
+ * Real code
+ */
+
+static struct attribute *lm87_attributes[] = {
+ &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_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &dev_attr_temp1_crit.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &dev_attr_temp2_crit.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_aout_output.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm87_group = {
+ .attrs = lm87_attributes,
+};
+
+static struct attribute *lm87_attributes_in6[] = {
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm87_group_in6 = {
+ .attrs = lm87_attributes_in6,
+};
+
+static struct attribute *lm87_attributes_fan1[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm87_group_fan1 = {
+ .attrs = lm87_attributes_fan1,
+};
+
+static struct attribute *lm87_attributes_in7[] = {
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm87_group_in7 = {
+ .attrs = lm87_attributes_in7,
+};
+
+static struct attribute *lm87_attributes_fan2[] = {
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm87_group_fan2 = {
+ .attrs = lm87_attributes_fan2,
+};
+
+static struct attribute *lm87_attributes_temp3[] = {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &dev_attr_temp3_crit.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm87_group_temp3 = {
+ .attrs = lm87_attributes_temp3,
+};
+
+static struct attribute *lm87_attributes_in0_5[] = {
+ &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_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,
+ NULL
+};
+
+static const struct attribute_group lm87_group_in0_5 = {
+ .attrs = lm87_attributes_in0_5,
+};
+
+static struct attribute *lm87_attributes_vid[] = {
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group lm87_group_vid = {
+ .attrs = lm87_attributes_vid,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *name;
+ u8 cid, rev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ if (lm87_read_value(client, LM87_REG_CONFIG) & 0x80)
+ return -ENODEV;
+
+ /* Now, we do the remaining detection. */
+ cid = lm87_read_value(client, LM87_REG_COMPANY_ID);
+ rev = lm87_read_value(client, LM87_REG_REVISION);
+
+ if (cid == 0x02 /* National Semiconductor */
+ && (rev >= 0x01 && rev <= 0x08))
+ name = "lm87";
+ else if (cid == 0x41 /* Analog Devices */
+ && (rev & 0xf0) == 0x10)
+ name = "adm1024";
+ else {
+ dev_dbg(&adapter->dev, "LM87 detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void lm87_restore_config(void *arg)
+{
+ struct i2c_client *client = arg;
+ struct lm87_data *data = i2c_get_clientdata(client);
+
+ lm87_write_value(client, LM87_REG_CONFIG, data->config);
+}
+
+static int lm87_init_client(struct i2c_client *client)
+{
+ struct lm87_data *data = i2c_get_clientdata(client);
+ int rc;
+ struct device_node *of_node = client->dev.of_node;
+ u8 val = 0;
+ struct regulator *vcc = NULL;
+
+ if (of_node) {
+ if (of_property_read_bool(of_node, "has-temp3"))
+ val |= CHAN_TEMP3;
+ if (of_property_read_bool(of_node, "has-in6"))
+ val |= CHAN_NO_FAN(0);
+ if (of_property_read_bool(of_node, "has-in7"))
+ val |= CHAN_NO_FAN(1);
+ vcc = devm_regulator_get_optional(&client->dev, "vcc");
+ if (!IS_ERR(vcc)) {
+ if (regulator_get_voltage(vcc) == 5000000)
+ val |= CHAN_VCC_5V;
+ }
+ data->channel = val;
+ lm87_write_value(client,
+ LM87_REG_CHANNEL_MODE, data->channel);
+ } else if (dev_get_platdata(&client->dev)) {
+ data->channel = *(u8 *)dev_get_platdata(&client->dev);
+ lm87_write_value(client,
+ LM87_REG_CHANNEL_MODE, data->channel);
+ } else {
+ data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
+ }
+ data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F;
+
+ rc = devm_add_action(&client->dev, lm87_restore_config, client);
+ if (rc)
+ return rc;
+
+ if (!(data->config & 0x01)) {
+ int i;
+
+ /* Limits are left uninitialized after power-up */
+ for (i = 1; i < 6; i++) {
+ lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
+ lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
+ }
+ for (i = 0; i < 2; i++) {
+ lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F);
+ lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00);
+ lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00);
+ lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF);
+ }
+ if (data->channel & CHAN_TEMP3) {
+ lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F);
+ lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00);
+ } else {
+ lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00);
+ lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
+ }
+ }
+
+ /* Make sure Start is set and INT#_Clear is clear */
+ if ((data->config & 0x09) != 0x01)
+ lm87_write_value(client, LM87_REG_CONFIG,
+ (data->config & 0x77) | 0x01);
+ return 0;
+}
+
+static int lm87_probe(struct i2c_client *client)
+{
+ struct lm87_data *data;
+ struct device *hwmon_dev;
+ int err;
+ unsigned int group_tail = 0;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct lm87_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM87 chip */
+ err = lm87_init_client(client);
+ if (err)
+ return err;
+
+ data->in_scale[0] = 2500;
+ data->in_scale[1] = 2700;
+ data->in_scale[2] = (data->channel & CHAN_VCC_5V) ? 5000 : 3300;
+ data->in_scale[3] = 5000;
+ data->in_scale[4] = 12000;
+ data->in_scale[5] = 2700;
+ data->in_scale[6] = 1875;
+ data->in_scale[7] = 1875;
+
+ /*
+ * Construct the list of attributes, the list depends on the
+ * configuration of the chip
+ */
+ data->attr_groups[group_tail++] = &lm87_group;
+ if (data->channel & CHAN_NO_FAN(0))
+ data->attr_groups[group_tail++] = &lm87_group_in6;
+ else
+ data->attr_groups[group_tail++] = &lm87_group_fan1;
+
+ if (data->channel & CHAN_NO_FAN(1))
+ data->attr_groups[group_tail++] = &lm87_group_in7;
+ else
+ data->attr_groups[group_tail++] = &lm87_group_fan2;
+
+ if (data->channel & CHAN_TEMP3)
+ data->attr_groups[group_tail++] = &lm87_group_temp3;
+ else
+ data->attr_groups[group_tail++] = &lm87_group_in0_5;
+
+ if (!(data->channel & CHAN_NO_VID)) {
+ data->vrm = vid_which_vrm();
+ data->attr_groups[group_tail++] = &lm87_group_vid;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(
+ &client->dev, client->name, client, data->attr_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id lm87_id[] = {
+ { "lm87", 0 },
+ { "adm1024", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm87_id);
+
+static const struct of_device_id lm87_of_match[] = {
+ { .compatible = "ti,lm87" },
+ { .compatible = "adi,adm1024" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lm87_of_match);
+
+static struct i2c_driver lm87_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm87",
+ .of_match_table = lm87_of_match,
+ },
+ .probe_new = lm87_probe,
+ .id_table = lm87_id,
+ .detect = lm87_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm87_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de> and others");
+MODULE_DESCRIPTION("LM87 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
new file mode 100644
index 000000000..db595f7d0
--- /dev/null
+++ b/drivers/hwmon/lm90.c
@@ -0,0 +1,3001 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm90.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2003-2010 Jean Delvare <jdelvare@suse.de>
+ *
+ * Based on the lm83 driver. The LM90 is a sensor chip made by National
+ * Semiconductor. It reports up to two temperatures (its own plus up to
+ * one external one) with a 0.125 deg resolution (1 deg for local
+ * temperature) and a 3-4 deg accuracy.
+ *
+ * This driver also supports the LM89 and LM99, two other sensor chips
+ * made by National Semiconductor. Both have an increased remote
+ * temperature measurement accuracy (1 degree), and the LM99
+ * additionally shifts remote temperatures (measured and limits) by 16
+ * degrees, which allows for higher temperatures measurement.
+ * Note that there is no way to differentiate between both chips.
+ * When device is auto-detected, the driver will assume an LM99.
+ *
+ * This driver also supports the LM86, another sensor chip made by
+ * National Semiconductor. It is exactly similar to the LM90 except it
+ * has a higher accuracy.
+ *
+ * This driver also supports the ADM1032, a sensor chip made by Analog
+ * Devices. That chip is similar to the LM90, with a few differences
+ * that are not handled by this driver. Among others, it has a higher
+ * accuracy than the LM90, much like the LM86 does.
+ *
+ * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor
+ * chips made by Maxim. These chips are similar to the LM86.
+ * Note that there is no easy way to differentiate between the three
+ * variants. We use the device address to detect MAX6659, which will result
+ * in a detection as max6657 if it is on address 0x4c. The extra address
+ * and features of the MAX6659 are only supported if the chip is configured
+ * 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
+ * from 0 to 145 degrees.
+ *
+ * This driver also supports the MAX6680 and MAX6681, two other sensor
+ * chips made by Maxim. These are quite similar to the other Maxim
+ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can
+ * be treated identically.
+ *
+ * This driver also supports the MAX6695 and MAX6696, two other sensor
+ * chips made by Maxim. These are also quite similar to other Maxim
+ * chips, but support three temperature sensors instead of two. MAX6695
+ * and MAX6696 only differ in the pinout so they can be treated identically.
+ *
+ * This driver also supports ADT7461 and ADT7461A from Analog Devices as well as
+ * NCT1008 from ON Semiconductor. The chips are supported in both compatibility
+ * 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 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/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.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
+ * Address is fully defined internally and cannot be changed except for
+ * MAX6659, MAX6680 and MAX6681.
+ * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, ADT7461A, MAX6649,
+ * MAX6657, MAX6658, NCT1008 and W83L771 have address 0x4c.
+ * ADM1032-2, ADT7461-2, ADT7461A-2, LM89-1, LM99-1, MAX6646, and NCT1008D
+ * have address 0x4d.
+ * MAX6647 has address 0x4e.
+ * MAX6659 can have address 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.
+ */
+
+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 { 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_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_LOCAL_TEMPL 0x22
+
+#define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */
+
+/* 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 features */
+#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 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", adt7461a },
+ { "adt7481", adt7481 },
+ { "adt7482", adt7481 },
+ { "adt7483a", adt7481 },
+ { "g781", g781 },
+ { "gl523sm", max1617 },
+ { "lm84", lm84 },
+ { "lm86", lm90 },
+ { "lm89", lm90 },
+ { "lm90", lm90 },
+ { "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 },
+ { "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);
+
+static const struct of_device_id __maybe_unused lm90_of_match[] = {
+ {
+ .compatible = "adi,adm1032",
+ .data = (void *)adm1032
+ },
+ {
+ .compatible = "adi,adt7461",
+ .data = (void *)adt7461
+ },
+ {
+ .compatible = "adi,adt7461a",
+ .data = (void *)adt7461a
+ },
+ {
+ .compatible = "adi,adt7481",
+ .data = (void *)adt7481
+ },
+ {
+ .compatible = "gmt,g781",
+ .data = (void *)g781
+ },
+ {
+ .compatible = "national,lm90",
+ .data = (void *)lm90
+ },
+ {
+ .compatible = "national,lm86",
+ .data = (void *)lm90
+ },
+ {
+ .compatible = "national,lm89",
+ .data = (void *)lm90
+ },
+ {
+ .compatible = "national,lm99",
+ .data = (void *)lm99
+ },
+ {
+ .compatible = "dallas,max6646",
+ .data = (void *)max6646
+ },
+ {
+ .compatible = "dallas,max6647",
+ .data = (void *)max6646
+ },
+ {
+ .compatible = "dallas,max6649",
+ .data = (void *)max6646
+ },
+ {
+ .compatible = "dallas,max6654",
+ .data = (void *)max6654
+ },
+ {
+ .compatible = "dallas,max6657",
+ .data = (void *)max6657
+ },
+ {
+ .compatible = "dallas,max6658",
+ .data = (void *)max6657
+ },
+ {
+ .compatible = "dallas,max6659",
+ .data = (void *)max6659
+ },
+ {
+ .compatible = "dallas,max6680",
+ .data = (void *)max6680
+ },
+ {
+ .compatible = "dallas,max6681",
+ .data = (void *)max6680
+ },
+ {
+ .compatible = "dallas,max6695",
+ .data = (void *)max6696
+ },
+ {
+ .compatible = "dallas,max6696",
+ .data = (void *)max6696
+ },
+ {
+ .compatible = "onnn,nct1008",
+ .data = (void *)adt7461a
+ },
+ {
+ .compatible = "onnn,nct214",
+ .data = (void *)nct72
+ },
+ {
+ .compatible = "onnn,nct218",
+ .data = (void *)nct72
+ },
+ {
+ .compatible = "onnn,nct72",
+ .data = (void *)nct72
+ },
+ {
+ .compatible = "winbond,w83l771",
+ .data = (void *)w83l771
+ },
+ {
+ .compatible = "nxp,sa56004",
+ .data = (void *)sa56004
+ },
+ {
+ .compatible = "ti,tmp451",
+ .data = (void *)tmp451
+ },
+ {
+ .compatible = "ti,tmp461",
+ .data = (void *)tmp461
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lm90_of_match);
+
+/*
+ * chip type specific parameters
+ */
+struct lm90_params {
+ u32 flags; /* Capabilities */
+ 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_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_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_CRIT
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
+ .alert_alarms = 0x7c,
+ .max_convrate = 7,
+ },
+ [lm84] = {
+ .flags = LM90_HAVE_ALARMS,
+ .resolution = 8,
+ },
+ [lm90] = {
+ .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
+ | 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_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 | 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_LOCAL_TEMPL,
+ },
+ [max6659] = {
+ .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_LOCAL_TEMPL,
+ },
+ [max6680] = {
+ /*
+ * 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_CRIT
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE,
+ .alert_alarms = 0x1c7c,
+ .max_convrate = 6,
+ .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 | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
+ .alert_alarms = 0x7c,
+ .max_convrate = 8,
+ },
+ [sa56004] = {
+ /*
+ * 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_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_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,
+ .resolution = 12,
+ .reg_local_ext = TMP451_REG_LOCAL_TEMPL,
+ },
+};
+
+/*
+ * temperature register index
+ */
+enum lm90_temp_reg_index {
+ LOCAL_LOW = 0,
+ LOCAL_HIGH,
+ LOCAL_CRIT,
+ REMOTE_CRIT,
+ LOCAL_EMERG, /* max6659 and max6695/96 */
+ REMOTE_EMERG, /* max6659 and max6695/96 */
+ REMOTE2_CRIT, /* max6695/96 only */
+ REMOTE2_EMERG, /* max6695/96 only */
+
+ REMOTE_TEMP,
+ REMOTE_LOW,
+ REMOTE_HIGH,
+ REMOTE_OFFSET, /* except max6646, max6657/58/59, and max6695/96 */
+ LOCAL_TEMP,
+ REMOTE2_TEMP, /* max6695/96 only */
+ REMOTE2_LOW, /* max6695/96 only */
+ REMOTE2_HIGH, /* max6695/96 only */
+ REMOTE2_OFFSET,
+
+ TEMP_REG_NUM
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm90_data {
+ struct i2c_client *client;
+ 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;
+
+ unsigned int update_interval; /* in milliseconds */
+
+ 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 */
+ u16 temp[TEMP_REG_NUM];
+ u8 temp_hyst;
+ 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 */
+};
+
+/*
+ * Support functions
+ */
+
+/*
+ * If the chip supports PEC but not on write byte transactions, we need
+ * to explicitly ask for a transaction without PEC.
+ */
+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,
+ I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
+}
+
+/*
+ * It is assumed that client->update_lock is held (unless we are in
+ * 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 (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 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,
+ bool is_volatile)
+{
+ int oldh, newh, l;
+
+ 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;
+ if (oldh != newh) {
+ l = lm90_read_reg(client, regl);
+ if (l < 0)
+ return l;
+ }
+ return (newh << 8) | l;
+}
+
+static int lm90_update_confreg(struct lm90_data *data, u8 config)
+{
+ if (data->config != config) {
+ int err;
+
+ err = lm90_write_reg(data->client, LM90_REG_CONFIG1, config);
+ if (err)
+ return err;
+ data->config = config;
+ }
+ return 0;
+}
+
+/*
+ * client->update_lock must be held when calling this function (unless we are
+ * in detection or initialization steps), and while a remote channel other
+ * than channel 0 is selected. Also, calling code must make sure to re-select
+ * external channel 0 before releasing the lock. This is necessary because
+ * 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, bool second)
+{
+ u8 config = data->config & ~0x08;
+
+ if (second)
+ config |= 0x08;
+
+ return lm90_update_confreg(data, config);
+}
+
+static int lm90_write_convrate(struct lm90_data *data, int val)
+{
+ u8 config = data->config;
+ int err;
+
+ /* Save config and pause conversion */
+ if (data->flags & LM90_PAUSE_FOR_CONFIG) {
+ err = lm90_update_confreg(data, config | 0x40);
+ if (err < 0)
+ return err;
+ }
+
+ /* Set conv rate */
+ err = lm90_write_reg(data->client, LM90_REG_CONVRATE, val);
+
+ /* Revert change to config */
+ lm90_update_confreg(data, config);
+
+ return err;
+}
+
+/*
+ * Set conversion rate.
+ * client->update_lock must be held when calling this function (unless we are
+ * in detection or initialization steps).
+ */
+static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
+ unsigned int interval)
+{
+ unsigned int update_interval;
+ int i, err;
+
+ /* Shift calculations to avoid rounding errors */
+ interval <<= 6;
+
+ /* find the nearest update rate */
+ for (i = 0, update_interval = LM90_MAX_CONVRATE_MS << 6;
+ i < data->max_convrate; i++, update_interval >>= 1)
+ if (interval >= update_interval * 3 / 4)
+ break;
+
+ err = lm90_write_convrate(data, i);
+ data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64);
+ 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;
+
+ 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_REMOTE_CRIT);
+ if (val < 0)
+ return val;
+ data->temp[REMOTE_CRIT] = val << 8;
+
+ val = lm90_read_reg(client, LM90_REG_TCRIT_HYST);
+ if (val < 0)
+ return 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_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->temp[REMOTE_LOW] = 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_REMOTE_OFFSH,
+ LM90_REG_REMOTE_OFFSL, false);
+ if (val < 0)
+ return val;
+ data->temp[REMOTE_OFFSET] = val;
+ }
+
+ if (data->flags & LM90_HAVE_EMERGENCY) {
+ val = lm90_read_reg(client, MAX6659_REG_LOCAL_EMERG);
+ if (val < 0)
+ return val;
+ data->temp[LOCAL_EMERG] = val << 8;
+
+ val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG);
+ if (val < 0)
+ return val;
+ data->temp[REMOTE_EMERG] = val << 8;
+ }
+
+ 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_REMOTE_CRIT);
+ if (val < 0)
+ return 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, LM90_REG_REMOTE_LOWH);
+ if (val < 0)
+ return val;
+ data->temp[REMOTE2_LOW] = val << 8;
+
+ val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH);
+ if (val < 0)
+ return val;
+ 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;
+
+ data->alarms_valid = false;
+
+ val = lm90_read_reg(client, LM90_REG_STATUS);
+ if (val < 0)
+ return val;
+ alarms = val & ~LM90_STATUS_BUSY;
+
+ 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);
+ struct i2c_client *client = data->client;
+ unsigned long next_update;
+ int val;
+
+ if (!data->valid) {
+ val = lm90_update_limits(dev);
+ if (val < 0)
+ return val;
+ }
+
+ next_update = data->last_updated +
+ msecs_to_jiffies(data->update_interval);
+ if (time_after(jiffies, next_update) || !data->valid) {
+ dev_dbg(&client->dev, "Updating lm90 data.\n");
+
+ data->valid = false;
+
+ val = lm90_read_reg(client, LM90_REG_LOCAL_LOW);
+ if (val < 0)
+ return val;
+ data->temp[LOCAL_LOW] = val << 8;
+
+ val = lm90_read_reg(client, LM90_REG_LOCAL_HIGH);
+ if (val < 0)
+ return val;
+ data->temp[LOCAL_HIGH] = val << 8;
+
+ val = lm90_read16(client, LM90_REG_LOCAL_TEMP,
+ data->reg_local_ext, true);
+ if (val < 0)
+ return val;
+ data->temp[LOCAL_TEMP] = val;
+ val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
+ data->reg_remote_ext, true);
+ if (val < 0)
+ return val;
+ data->temp[REMOTE_TEMP] = val;
+
+ if (data->flags & LM90_HAVE_TEMP3) {
+ val = lm90_select_remote_channel(data, true);
+ if (val < 0)
+ return val;
+
+ val = lm90_read16(client, LM90_REG_REMOTE_TEMPH,
+ data->reg_remote_ext, true);
+ if (val < 0) {
+ lm90_select_remote_channel(data, false);
+ return val;
+ }
+ data->temp[REMOTE2_TEMP] = val;
+
+ lm90_select_remote_channel(data, false);
+ }
+
+ val = lm90_update_alarms_locked(data, false);
+ if (val < 0)
+ return val;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ return 0;
+}
+
+/* pec used for devices with PEC support */
+static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return sprintf(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);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ switch (val) {
+ case 0:
+ client->flags &= ~I2C_CLIENT_PEC;
+ break;
+ case 1:
+ client->flags |= I2C_CLIENT_PEC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(pec);
+
+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)
+{
+ int val;
+
+ if (flags & LM90_HAVE_EXTENDED_TEMP)
+ val = regval - 0x4000;
+ else if (flags & (LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_EXT_UNSIGNED))
+ val = regval;
+ else
+ val = (s16)regval;
+
+ 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 u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution)
+{
+ 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;
+ u8 regh = regs[index];
+ u8 regl = 0;
+ int err;
+
+ 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;
+ }
+
+ data->temp[index] = lm90_temp_to_reg(data->flags, val,
+ lm90_temp_get_resolution(data, index));
+
+ 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);
+
+ return err;
+}
+
+static int lm90_get_temphyst(struct lm90_data *data, int index, int channel)
+{
+ int temp = lm90_get_temp(data, index, channel);
+
+ return temp - data->temp_hyst * 1000;
+}
+
+static int lm90_set_temphyst(struct lm90_data *data, long val)
+{
+ int temp = lm90_get_temp(data, LOCAL_CRIT, 0);
+
+ /* prevent integer overflow/underflow */
+ val = clamp_val(val, -128000l, 255000l);
+ data->temp_hyst = clamp_val(DIV_ROUND_CLOSEST(temp - val, 1000), 0, 31);
+
+ return lm90_write_reg(data->client, LM90_REG_TCRIT_HYST, data->temp_hyst);
+}
+
+static int lm90_get_temp_offset(struct lm90_data *data, int index)
+{
+ int res = lm90_temp_get_resolution(data, index);
+
+ return lm90_temp_from_reg(0, data->temp[index], res);
+}
+
+static int lm90_set_temp_offset(struct lm90_data *data, int index, int channel, long val)
+{
+ int err;
+
+ val = lm90_temp_to_reg(0, val, lm90_temp_get_resolution(data, index));
+
+ /* For ADT7481 we can use the same registers for remote channel 1 and 2 */
+ if (channel > 1)
+ lm90_select_remote_channel(data, true);
+
+ err = lm90_write16(data->client, LM90_REG_REMOTE_OFFSH, LM90_REG_REMOTE_OFFSL, val);
+
+ if (channel > 1)
+ lm90_select_remote_channel(data, false);
+
+ if (err)
+ return err;
+
+ data->temp[index] = val;
+
+ return 0;
+}
+
+static const u8 lm90_temp_index[MAX_CHANNELS] = {
+ LOCAL_TEMP, REMOTE_TEMP, REMOTE2_TEMP
+};
+
+static const u8 lm90_temp_min_index[MAX_CHANNELS] = {
+ LOCAL_LOW, REMOTE_LOW, REMOTE2_LOW
+};
+
+static const u8 lm90_temp_max_index[MAX_CHANNELS] = {
+ LOCAL_HIGH, REMOTE_HIGH, REMOTE2_HIGH
+};
+
+static const u8 lm90_temp_crit_index[MAX_CHANNELS] = {
+ LOCAL_CRIT, REMOTE_CRIT, REMOTE2_CRIT
+};
+
+static const u8 lm90_temp_emerg_index[MAX_CHANNELS] = {
+ LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG
+};
+
+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);
+ mutex_unlock(&data->update_lock);
+ if (err)
+ return err;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ *val = lm90_get_temp(data, lm90_temp_index[channel], channel);
+ break;
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_emergency_alarm:
+ case hwmon_temp_fault:
+ 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:
+ *val = lm90_get_temp(data, lm90_temp_min_index[channel], channel);
+ break;
+ case hwmon_temp_max:
+ *val = lm90_get_temp(data, lm90_temp_max_index[channel], channel);
+ break;
+ case hwmon_temp_crit:
+ *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], channel);
+ break;
+ case hwmon_temp_emergency:
+ *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], channel);
+ break;
+ case hwmon_temp_offset:
+ *val = lm90_get_temp_offset(data, lm90_temp_offset_index[channel]);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ int err;
+
+ mutex_lock(&data->update_lock);
+
+ err = lm90_update_device(dev);
+ if (err)
+ goto error;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ err = lm90_set_temp(data, lm90_temp_min_index[channel],
+ channel, val);
+ break;
+ case hwmon_temp_max:
+ err = lm90_set_temp(data, lm90_temp_max_index[channel],
+ channel, val);
+ break;
+ case hwmon_temp_crit:
+ 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_temp(data, lm90_temp_emerg_index[channel],
+ channel, val);
+ break;
+ case hwmon_temp_offset:
+ err = lm90_set_temp_offset(data, lm90_temp_offset_index[channel],
+ channel, val);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+error:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static umode_t lm90_temp_is_visible(const void *data, u32 attr, int channel)
+{
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ 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:
+ case hwmon_temp_crit:
+ case hwmon_temp_emergency:
+ case hwmon_temp_offset:
+ return 0644;
+ case hwmon_temp_crit_hyst:
+ if (channel == 0)
+ return 0644;
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ int err;
+
+ mutex_lock(&data->update_lock);
+ err = lm90_update_device(dev);
+ mutex_unlock(&data->update_lock);
+ if (err)
+ return err;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->update_interval;
+ break;
+ 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;
+ }
+
+ return 0;
+}
+
+static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int err;
+
+ mutex_lock(&data->update_lock);
+
+ err = lm90_update_device(dev);
+ if (err)
+ goto error;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ 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;
+ }
+error:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+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;
+ default:
+ return 0;
+ }
+}
+
+static int lm90_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_read(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm90_temp_read(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+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)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_write(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm90_temp_write(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_is_visible(data, attr, channel);
+ case hwmon_temp:
+ return lm90_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+static const char *lm90_detect_lm84(struct i2c_client *client)
+{
+ 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;
+
+ if (config2 < 0)
+ return NULL;
+
+ if ((config1 & 0x2a) || (config2 & 0xf8) || convrate > 0x09)
+ return NULL;
+
+ 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";
+ break;
+ case 0x51: /* ADT7461 */
+ if (man_id2 == 0x00 && chip_id2 == 0x00 &&
+ (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) &&
+ convrate <= 0x0a)
+ name = "adt7461";
+ 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";
+ }
+ 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_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_REMOTE_EMERG);
+ man_id = i2c_smbus_read_byte_data(client,
+ LM90_REG_MAN_ID);
+ emerg2 = i2c_smbus_read_byte_data(client,
+ MAX6659_REG_REMOTE_EMERG);
+ status2 = i2c_smbus_read_byte_data(client,
+ MAX6696_REG_STATUS2);
+ if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0)
+ return NULL;
+
+ /*
+ * 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
+ * the status2 register is unused and should return zero when
+ * read.
+ *
+ * MAX6695 and MAX6696 have an additional set of temperature
+ * limit registers. We can detect those chips by checking if
+ * one of those registers exists.
+ */
+ if (!(config1 & 0x10) && !(status2 & 0x01) && emerg == emerg2 &&
+ convrate <= 0x07)
+ name = "max6696";
+ /*
+ * 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). 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.
+ */
+ else if (!(config1 & 0x03) && convrate <= 0x07 &&
+ emerg2 == man_id && emerg2 != status2)
+ name = "max6680";
+ /*
+ * 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 (!(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;
+ }
+ }
+ 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";
+ }
+ }
+ 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_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);
+
+ /* 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 */
+ dev_dbg(&adapter->dev,
+ "Unsupported chip at 0x%02x (man_id=0x%02X, chip_id=0x%02X)\n",
+ client->addr, man_id, chip_id);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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 */
+ 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;
+
+ 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_CONFIG1);
+ if (config < 0)
+ return config;
+ data->config_orig = config;
+ data->config = config;
+
+ /* Check Temperature Range Select */
+ 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;
+
+ /*
+ * 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->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)
+{
+ struct lm90_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ ret = lm90_update_alarms(data, true);
+ if (ret < 0)
+ return false;
+
+ return !!data->current_alarms;
+}
+
+static irqreturn_t lm90_irq_thread(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+
+ if (lm90_is_tripped(client))
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void lm90_remove_pec(void *dev)
+{
+ device_remove_file(dev, &dev_attr_pec);
+}
+
+static void lm90_regulator_disable(void *regulator)
+{
+ regulator_disable(regulator);
+}
+
+static int lm90_probe_channel_from_dt(struct i2c_client *client,
+ struct device_node *child,
+ struct lm90_data *data)
+{
+ 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)
+{
+ struct device *dev = &client->dev;
+ struct i2c_adapter *adapter = client->adapter;
+ struct hwmon_channel_info *info;
+ struct regulator *regulator;
+ struct device *hwmon_dev;
+ struct lm90_data *data;
+ int err;
+
+ regulator = devm_regulator_get(dev, "vcc");
+ if (IS_ERR(regulator))
+ return PTR_ERR(regulator);
+
+ err = regulator_enable(regulator);
+ if (err < 0) {
+ dev_err(dev, "Failed to enable regulator: %d\n", err);
+ return err;
+ }
+
+ err = devm_add_action_or_reset(dev, lm90_regulator_disable, regulator);
+ if (err)
+ return err;
+
+ data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ 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 = 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] = &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_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;
+
+ if (data->flags & LM90_HAVE_EMERGENCY) {
+ data->channel_config[0] |= HWMON_T_EMERGENCY |
+ HWMON_T_EMERGENCY_HYST;
+ data->channel_config[1] |= HWMON_T_EMERGENCY |
+ HWMON_T_EMERGENCY_HYST;
+ }
+
+ if (data->flags & LM90_HAVE_EMERGENCY_ALARM) {
+ data->channel_config[0] |= HWMON_T_EMERGENCY_ALARM;
+ data->channel_config[1] |= HWMON_T_EMERGENCY_ALARM;
+ }
+
+ if (data->flags & LM90_HAVE_TEMP3) {
+ data->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;
+ 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) {
+ dev_err(dev, "Failed to initialize device\n");
+ return err;
+ }
+
+ /*
+ * The 'pec' attribute is attached to the i2c device and thus created
+ * separately.
+ */
+ if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) {
+ err = device_create_file(dev, &dev_attr_pec);
+ if (err)
+ return err;
+ err = devm_add_action_or_reset(dev, lm90_remove_pec, dev);
+ if (err)
+ return err;
+ }
+
+ 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);
+
+ 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_ONESHOT, "lm90", client);
+ if (err < 0) {
+ dev_err(dev, "cannot request IRQ %d\n", client->irq);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
+ unsigned int flag)
+{
+ if (type != I2C_PROTOCOL_SMBUS_ALERT)
+ return;
+
+ if (lm90_is_tripped(client)) {
+ /*
+ * Disable ALERT# output, because these chips don't implement
+ * SMBus alert correctly; they should only hold the alert line
+ * low briefly.
+ */
+ struct lm90_data *data = i2c_get_clientdata(client);
+
+ if ((data->flags & LM90_HAVE_BROKEN_ALERT) &&
+ (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_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_new = lm90_probe,
+ .alert = lm90_alert,
+ .id_table = lm90_id,
+ .detect = lm90_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm90_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("LM90/ADM1032 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
new file mode 100644
index 000000000..2ff3044a6
--- /dev/null
+++ b/drivers/hwmon/lm92.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm92 - Hardware monitoring driver
+ * Copyright (C) 2005-2008 Jean Delvare <jdelvare@suse.de>
+ *
+ * Based on the lm90 driver, with some ideas taken from the lm_sensors
+ * lm92 driver as well.
+ *
+ * The LM92 is a sensor chip made by National Semiconductor. It reports
+ * its own temperature with a 0.0625 deg resolution and a 0.33 deg
+ * accuracy. Complete datasheet can be obtained from National's website
+ * at:
+ * http://www.national.com/pf/LM/LM92.html
+ *
+ * This driver also supports the MAX6635 sensor chip made by Maxim.
+ * This chip is compatible with the LM92, but has a lesser accuracy
+ * (1.0 deg). Complete datasheet can be obtained from Maxim's website
+ * at:
+ * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074
+ *
+ * Since the LM92 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.
+ *
+ * Support could easily be added for the National Semiconductor LM76
+ * and Maxim MAX6633 and MAX6634 chips, which are mostly compatible
+ * with the LM92.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+
+/*
+ * The LM92 and MAX6635 have 2 two-state pins for address selection,
+ * resulting in 4 possible addresses.
+ */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
+ I2C_CLIENT_END };
+enum chips { lm92, max6635 };
+
+/* The LM92 registers */
+#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */
+#define LM92_REG_TEMP 0x00 /* 16-bit, RO */
+#define LM92_REG_TEMP_HYST 0x02 /* 16-bit, RW */
+#define LM92_REG_TEMP_CRIT 0x03 /* 16-bit, RW */
+#define LM92_REG_TEMP_LOW 0x04 /* 16-bit, RW */
+#define LM92_REG_TEMP_HIGH 0x05 /* 16-bit, RW */
+#define LM92_REG_MAN_ID 0x07 /* 16-bit, RO, LM92 only */
+
+/*
+ * The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius,
+ * left-justified in 16-bit registers. No rounding is done, with such
+ * a resolution it's just not worth it. Note that the MAX6635 doesn't
+ * make use of the 4 lower bits for limits (i.e. effective resolution
+ * for limits is 1 degree Celsius).
+ */
+static inline int TEMP_FROM_REG(s16 reg)
+{
+ return reg / 8 * 625 / 10;
+}
+
+static inline s16 TEMP_TO_REG(long val)
+{
+ val = clamp_val(val, -60000, 160000);
+ return val * 10 / 625 * 8;
+}
+
+/* Alarm flags are stored in the 3 LSB of the temperature register */
+static inline u8 ALARMS_FROM_REG(s16 reg)
+{
+ return reg & 0x0007;
+}
+
+enum temp_index {
+ t_input,
+ t_crit,
+ t_min,
+ t_max,
+ t_hyst,
+ t_num_regs
+};
+
+static const u8 regs[t_num_regs] = {
+ [t_input] = LM92_REG_TEMP,
+ [t_crit] = LM92_REG_TEMP_CRIT,
+ [t_min] = LM92_REG_TEMP_LOW,
+ [t_max] = LM92_REG_TEMP_HIGH,
+ [t_hyst] = LM92_REG_TEMP_HYST,
+};
+
+/* Client data (each client gets its own) */
+struct lm92_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ s16 temp[t_num_regs]; /* index with enum temp_index */
+};
+
+/*
+ * Sysfs attributes and callback functions
+ */
+
+static struct lm92_data *lm92_update_device(struct device *dev)
+{
+ struct lm92_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) ||
+ !data->valid) {
+ dev_dbg(&client->dev, "Updating lm92 data\n");
+ for (i = 0; i < t_num_regs; i++) {
+ data->temp[i] =
+ i2c_smbus_read_word_swapped(client, regs[i]);
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+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 lm92_data *data = lm92_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static ssize_t temp_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 lm92_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp[nr] = TEMP_TO_REG(val);
+ i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_hyst_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm92_data *data = lm92_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])
+ - TEMP_FROM_REG(data->temp[t_hyst]));
+}
+
+static ssize_t temp1_min_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm92_data *data = lm92_update_device(dev);
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min])
+ + TEMP_FROM_REG(data->temp[t_hyst]));
+}
+
+static ssize_t temp_hyst_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 lm92_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ val = clamp_val(val, -120000, 220000);
+ mutex_lock(&data->update_lock);
+ data->temp[t_hyst] =
+ TEMP_TO_REG(TEMP_FROM_REG(data->temp[attr->index]) - val);
+ i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST,
+ data->temp[t_hyst]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm92_data *data = lm92_update_device(dev);
+
+ return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input]));
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct lm92_data *data = lm92_update_device(dev);
+ return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_hyst, t_crit);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_min);
+static DEVICE_ATTR_RO(temp1_min_hyst);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_max);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, temp_hyst, t_max);
+static DEVICE_ATTR_RO(alarms);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1);
+
+/*
+ * Detection and registration
+ */
+
+static void lm92_init_client(struct i2c_client *client)
+{
+ u8 config;
+
+ /* Start the conversions if needed */
+ config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
+ if (config & 0x01)
+ i2c_smbus_write_byte_data(client, LM92_REG_CONFIG,
+ config & 0xFE);
+}
+
+static struct attribute *lm92_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &dev_attr_temp1_min_hyst.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(lm92);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm92_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ u8 config;
+ u16 man_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
+ | I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG);
+ man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID);
+
+ if ((config & 0xe0) == 0x00 && man_id == 0x0180)
+ pr_info("lm92: Found National Semiconductor LM92 chip\n");
+ else
+ return -ENODEV;
+
+ strscpy(info->type, "lm92", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int lm92_probe(struct i2c_client *new_client)
+{
+ struct device *hwmon_dev;
+ struct lm92_data *data;
+
+ data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = new_client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the chipset */
+ lm92_init_client(new_client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
+ new_client->name,
+ data, lm92_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/*
+ * Module and driver stuff
+ */
+
+static const struct i2c_device_id lm92_id[] = {
+ { "lm92", lm92 },
+ { "max6635", max6635 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm92_id);
+
+static struct i2c_driver lm92_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm92",
+ },
+ .probe_new = lm92_probe,
+ .id_table = lm92_id,
+ .detect = lm92_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm92_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("LM92/MAX6635 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
new file mode 100644
index 000000000..4cf50d5f4
--- /dev/null
+++ b/drivers/hwmon/lm93.c
@@ -0,0 +1,2649 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lm93.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
+ *
+ * Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com>
+ * Copyright (c) 2004 Utilitek Systems, Inc.
+ *
+ * derived in part from lm78.c:
+ * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+ *
+ * derived in part from lm85.c:
+ * Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
+ * Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
+ *
+ * derived in part from w83l785ts.c:
+ * Copyright (c) 2003-2004 Jean Delvare <jdelvare@suse.de>
+ *
+ * Ported to Linux 2.6 by Eric J. Bowersox <ericb@aspsys.com>
+ * Copyright (c) 2005 Aspen Systems, Inc.
+ *
+ * Adapted to 2.6.20 by Carsten Emde <cbe@osadl.org>
+ * Copyright (c) 2006 Carsten Emde, Open Source Automation Development Lab
+ *
+ * Modified for mainline integration by Hans J. Koch <hjk@hansjkoch.de>
+ * Copyright (c) 2007 Hans J. Koch, Linutronix GmbH
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+
+/* LM93 REGISTER ADDRESSES */
+
+/* miscellaneous */
+#define LM93_REG_MFR_ID 0x3e
+#define LM93_REG_VER 0x3f
+#define LM93_REG_STATUS_CONTROL 0xe2
+#define LM93_REG_CONFIG 0xe3
+#define LM93_REG_SLEEP_CONTROL 0xe4
+
+/* alarm values start here */
+#define LM93_REG_HOST_ERROR_1 0x48
+
+/* voltage inputs: in1-in16 (nr => 0-15) */
+#define LM93_REG_IN(nr) (0x56 + (nr))
+#define LM93_REG_IN_MIN(nr) (0x90 + (nr) * 2)
+#define LM93_REG_IN_MAX(nr) (0x91 + (nr) * 2)
+
+/* temperature inputs: temp1-temp4 (nr => 0-3) */
+#define LM93_REG_TEMP(nr) (0x50 + (nr))
+#define LM93_REG_TEMP_MIN(nr) (0x78 + (nr) * 2)
+#define LM93_REG_TEMP_MAX(nr) (0x79 + (nr) * 2)
+
+/* temp[1-4]_auto_boost (nr => 0-3) */
+#define LM93_REG_BOOST(nr) (0x80 + (nr))
+
+/* #PROCHOT inputs: prochot1-prochot2 (nr => 0-1) */
+#define LM93_REG_PROCHOT_CUR(nr) (0x67 + (nr) * 2)
+#define LM93_REG_PROCHOT_AVG(nr) (0x68 + (nr) * 2)
+#define LM93_REG_PROCHOT_MAX(nr) (0xb0 + (nr))
+
+/* fan tach inputs: fan1-fan4 (nr => 0-3) */
+#define LM93_REG_FAN(nr) (0x6e + (nr) * 2)
+#define LM93_REG_FAN_MIN(nr) (0xb4 + (nr) * 2)
+
+/* pwm outputs: pwm1-pwm2 (nr => 0-1, reg => 0-3) */
+#define LM93_REG_PWM_CTL(nr, reg) (0xc8 + (reg) + (nr) * 4)
+#define LM93_PWM_CTL1 0x0
+#define LM93_PWM_CTL2 0x1
+#define LM93_PWM_CTL3 0x2
+#define LM93_PWM_CTL4 0x3
+
+/* GPIO input state */
+#define LM93_REG_GPI 0x6b
+
+/* vid inputs: vid1-vid2 (nr => 0-1) */
+#define LM93_REG_VID(nr) (0x6c + (nr))
+
+/* vccp1 & vccp2: VID relative inputs (nr => 0-1) */
+#define LM93_REG_VCCP_LIMIT_OFF(nr) (0xb2 + (nr))
+
+/* temp[1-4]_auto_boost_hyst */
+#define LM93_REG_BOOST_HYST_12 0xc0
+#define LM93_REG_BOOST_HYST_34 0xc1
+#define LM93_REG_BOOST_HYST(nr) (0xc0 + (nr)/2)
+
+/* temp[1-4]_auto_pwm_[min|hyst] */
+#define LM93_REG_PWM_MIN_HYST_12 0xc3
+#define LM93_REG_PWM_MIN_HYST_34 0xc4
+#define LM93_REG_PWM_MIN_HYST(nr) (0xc3 + (nr)/2)
+
+/* prochot_override & prochot_interval */
+#define LM93_REG_PROCHOT_OVERRIDE 0xc6
+#define LM93_REG_PROCHOT_INTERVAL 0xc7
+
+/* temp[1-4]_auto_base (nr => 0-3) */
+#define LM93_REG_TEMP_BASE(nr) (0xd0 + (nr))
+
+/* temp[1-4]_auto_offsets (step => 0-11) */
+#define LM93_REG_TEMP_OFFSET(step) (0xd4 + (step))
+
+/* #PROCHOT & #VRDHOT PWM ramp control */
+#define LM93_REG_PWM_RAMP_CTL 0xbf
+
+/* miscellaneous */
+#define LM93_REG_SFC1 0xbc
+#define LM93_REG_SFC2 0xbd
+#define LM93_REG_GPI_VID_CTL 0xbe
+#define LM93_REG_SF_TACH_TO_PWM 0xe0
+
+/* error masks */
+#define LM93_REG_GPI_ERR_MASK 0xec
+#define LM93_REG_MISC_ERR_MASK 0xed
+
+/* LM93 REGISTER VALUES */
+#define LM93_MFR_ID 0x73
+#define LM93_MFR_ID_PROTOTYPE 0x72
+
+/* LM94 REGISTER VALUES */
+#define LM94_MFR_ID_2 0x7a
+#define LM94_MFR_ID 0x79
+#define LM94_MFR_ID_PROTOTYPE 0x78
+
+/* SMBus capabilities */
+#define LM93_SMBUS_FUNC_FULL (I2C_FUNC_SMBUS_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA)
+#define LM93_SMBUS_FUNC_MIN (I2C_FUNC_SMBUS_BYTE_DATA | \
+ I2C_FUNC_SMBUS_WORD_DATA)
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+/* Insmod parameters */
+
+static bool disable_block;
+module_param(disable_block, bool, 0);
+MODULE_PARM_DESC(disable_block,
+ "Set to non-zero to disable SMBus block data transactions.");
+
+static bool init;
+module_param(init, bool, 0);
+MODULE_PARM_DESC(init, "Set to non-zero to force chip initialization.");
+
+static int vccp_limit_type[2] = {0, 0};
+module_param_array(vccp_limit_type, int, NULL, 0);
+MODULE_PARM_DESC(vccp_limit_type, "Configures in7 and in8 limit modes.");
+
+static int vid_agtl;
+module_param(vid_agtl, int, 0);
+MODULE_PARM_DESC(vid_agtl, "Configures VID pin input thresholds.");
+
+/* Driver data */
+static struct i2c_driver lm93_driver;
+
+/* LM93 BLOCK READ COMMANDS */
+static const struct { u8 cmd; u8 len; } lm93_block_read_cmds[12] = {
+ { 0xf2, 8 },
+ { 0xf3, 8 },
+ { 0xf4, 6 },
+ { 0xf5, 16 },
+ { 0xf6, 4 },
+ { 0xf7, 8 },
+ { 0xf8, 12 },
+ { 0xf9, 32 },
+ { 0xfa, 8 },
+ { 0xfb, 8 },
+ { 0xfc, 16 },
+ { 0xfd, 9 },
+};
+
+/*
+ * ALARMS: SYSCTL format described further below
+ * REG: 64 bits in 8 registers, as immediately below
+ */
+struct block1_t {
+ u8 host_status_1;
+ u8 host_status_2;
+ u8 host_status_3;
+ u8 host_status_4;
+ u8 p1_prochot_status;
+ u8 p2_prochot_status;
+ u8 gpi_status;
+ u8 fan_status;
+};
+
+/*
+ * Client-specific data
+ */
+struct lm93_data {
+ struct i2c_client *client;
+
+ struct mutex update_lock;
+ unsigned long last_updated; /* In jiffies */
+
+ /* client update function */
+ void (*update)(struct lm93_data *, struct i2c_client *);
+
+ bool valid; /* true if following fields are valid */
+
+ /* register values, arranged by block read groups */
+ struct block1_t block1;
+
+ /*
+ * temp1 - temp4: unfiltered readings
+ * temp1 - temp2: filtered readings
+ */
+ u8 block2[6];
+
+ /* vin1 - vin16: readings */
+ u8 block3[16];
+
+ /* prochot1 - prochot2: readings */
+ struct {
+ u8 cur;
+ u8 avg;
+ } block4[2];
+
+ /* fan counts 1-4 => 14-bits, LE, *left* justified */
+ u16 block5[4];
+
+ /* block6 has a lot of data we don't need */
+ struct {
+ u8 min;
+ u8 max;
+ } temp_lim[4];
+
+ /* vin1 - vin16: low and high limits */
+ struct {
+ u8 min;
+ u8 max;
+ } block7[16];
+
+ /* fan count limits 1-4 => same format as block5 */
+ u16 block8[4];
+
+ /* pwm control registers (2 pwms, 4 regs) */
+ u8 block9[2][4];
+
+ /* auto/pwm base temp and offset temp registers */
+ struct {
+ u8 base[4];
+ u8 offset[12];
+ } block10;
+
+ /* master config register */
+ u8 config;
+
+ /* VID1 & VID2 => register format, 6-bits, right justified */
+ u8 vid[2];
+
+ /* prochot1 - prochot2: limits */
+ u8 prochot_max[2];
+
+ /* vccp1 & vccp2 (in7 & in8): VID relative limits (register format) */
+ u8 vccp_limits[2];
+
+ /* GPIO input state (register format, i.e. inverted) */
+ u8 gpi;
+
+ /* #PROCHOT override (register format) */
+ u8 prochot_override;
+
+ /* #PROCHOT intervals (register format) */
+ u8 prochot_interval;
+
+ /* Fan Boost Temperatures (register format) */
+ u8 boost[4];
+
+ /* Fan Boost Hysteresis (register format) */
+ u8 boost_hyst[2];
+
+ /* Temperature Zone Min. PWM & Hysteresis (register format) */
+ u8 auto_pwm_min_hyst[2];
+
+ /* #PROCHOT & #VRDHOT PWM Ramp Control */
+ u8 pwm_ramp_ctl;
+
+ /* miscellaneous setup regs */
+ u8 sfc1;
+ u8 sfc2;
+ u8 sf_tach_to_pwm;
+
+ /*
+ * The two PWM CTL2 registers can read something other than what was
+ * last written for the OVR_DC field (duty cycle override). So, we
+ * save the user-commanded value here.
+ */
+ u8 pwm_override[2];
+};
+
+/*
+ * VID: mV
+ * REG: 6-bits, right justified, *always* using Intel VRM/VRD 10
+ */
+static int LM93_VID_FROM_REG(u8 reg)
+{
+ return vid_from_reg((reg & 0x3f), 100);
+}
+
+/* min, max, and nominal register values, per channel (u8) */
+static const u8 lm93_vin_reg_min[16] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae,
+};
+static const u8 lm93_vin_reg_max[16] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd1,
+};
+/*
+ * Values from the datasheet. They're here for documentation only.
+ * static const u8 lm93_vin_reg_nom[16] = {
+ * 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ * 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0xc0,
+ * };
+ */
+
+/* min, max, and nominal voltage readings, per channel (mV)*/
+static const unsigned long lm93_vin_val_min[16] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3000,
+};
+
+static const unsigned long lm93_vin_val_max[16] = {
+ 1236, 1236, 1236, 1600, 2000, 2000, 1600, 1600,
+ 4400, 6500, 3333, 2625, 1312, 1312, 1236, 3600,
+};
+/*
+ * Values from the datasheet. They're here for documentation only.
+ * static const unsigned long lm93_vin_val_nom[16] = {
+ * 927, 927, 927, 1200, 1500, 1500, 1200, 1200,
+ * 3300, 5000, 2500, 1969, 984, 984, 309, 3300,
+ * };
+ */
+
+static unsigned LM93_IN_FROM_REG(int nr, u8 reg)
+{
+ const long uv_max = lm93_vin_val_max[nr] * 1000;
+ const long uv_min = lm93_vin_val_min[nr] * 1000;
+
+ const long slope = (uv_max - uv_min) /
+ (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]);
+ const long intercept = uv_min - slope * lm93_vin_reg_min[nr];
+
+ return (slope * reg + intercept + 500) / 1000;
+}
+
+/*
+ * IN: mV, limits determined by channel nr
+ * REG: scaling determined by channel nr
+ */
+static u8 LM93_IN_TO_REG(int nr, unsigned val)
+{
+ /* range limit */
+ const long mv = clamp_val(val,
+ lm93_vin_val_min[nr], lm93_vin_val_max[nr]);
+
+ /* try not to lose too much precision here */
+ const long uv = mv * 1000;
+ const long uv_max = lm93_vin_val_max[nr] * 1000;
+ const long uv_min = lm93_vin_val_min[nr] * 1000;
+
+ /* convert */
+ const long slope = (uv_max - uv_min) /
+ (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]);
+ const long intercept = uv_min - slope * lm93_vin_reg_min[nr];
+
+ u8 result = ((uv - intercept + (slope/2)) / slope);
+ result = clamp_val(result,
+ lm93_vin_reg_min[nr], lm93_vin_reg_max[nr]);
+ return result;
+}
+
+/* vid in mV, upper == 0 indicates low limit, otherwise upper limit */
+static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid)
+{
+ const long uv_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) :
+ (((reg >> 0 & 0x0f) + 1) * -25000);
+ const long uv_vid = vid * 1000;
+ return (uv_vid + uv_offset + 5000) / 10000;
+}
+
+#define LM93_IN_MIN_FROM_REG(reg, vid) LM93_IN_REL_FROM_REG((reg), 0, (vid))
+#define LM93_IN_MAX_FROM_REG(reg, vid) LM93_IN_REL_FROM_REG((reg), 1, (vid))
+
+/*
+ * vid in mV , upper == 0 indicates low limit, otherwise upper limit
+ * upper also determines which nibble of the register is returned
+ * (the other nibble will be 0x0)
+ */
+static u8 LM93_IN_REL_TO_REG(unsigned val, int upper, int vid)
+{
+ long uv_offset = vid * 1000 - val * 10000;
+ if (upper) {
+ uv_offset = clamp_val(uv_offset, 12500, 200000);
+ return (u8)((uv_offset / 12500 - 1) << 4);
+ } else {
+ uv_offset = clamp_val(uv_offset, -400000, -25000);
+ return (u8)((uv_offset / -25000 - 1) << 0);
+ }
+}
+
+/*
+ * TEMP: 1/1000 degrees C (-128C to +127C)
+ * REG: 1C/bit, two's complement
+ */
+static int LM93_TEMP_FROM_REG(u8 reg)
+{
+ return (s8)reg * 1000;
+}
+
+#define LM93_TEMP_MIN (-128000)
+#define LM93_TEMP_MAX (127000)
+
+/*
+ * TEMP: 1/1000 degrees C (-128C to +127C)
+ * REG: 1C/bit, two's complement
+ */
+static u8 LM93_TEMP_TO_REG(long temp)
+{
+ int ntemp = clamp_val(temp, LM93_TEMP_MIN, LM93_TEMP_MAX);
+ ntemp += (ntemp < 0 ? -500 : 500);
+ return (u8)(ntemp / 1000);
+}
+
+/* Determine 4-bit temperature offset resolution */
+static int LM93_TEMP_OFFSET_MODE_FROM_REG(u8 sfc2, int nr)
+{
+ /* mode: 0 => 1C/bit, nonzero => 0.5C/bit */
+ return sfc2 & (nr < 2 ? 0x10 : 0x20);
+}
+
+/*
+ * This function is common to all 4-bit temperature offsets
+ * reg is 4 bits right justified
+ * mode 0 => 1C/bit, mode !0 => 0.5C/bit
+ */
+static int LM93_TEMP_OFFSET_FROM_REG(u8 reg, int mode)
+{
+ return (reg & 0x0f) * (mode ? 5 : 10);
+}
+
+#define LM93_TEMP_OFFSET_MIN (0)
+#define LM93_TEMP_OFFSET_MAX0 (150)
+#define LM93_TEMP_OFFSET_MAX1 (75)
+
+/*
+ * This function is common to all 4-bit temperature offsets
+ * returns 4 bits right justified
+ * mode 0 => 1C/bit, mode !0 => 0.5C/bit
+ */
+static u8 LM93_TEMP_OFFSET_TO_REG(int off, int mode)
+{
+ int factor = mode ? 5 : 10;
+
+ off = clamp_val(off, LM93_TEMP_OFFSET_MIN,
+ mode ? LM93_TEMP_OFFSET_MAX1 : LM93_TEMP_OFFSET_MAX0);
+ return (u8)((off + factor/2) / factor);
+}
+
+/* 0 <= nr <= 3 */
+static int LM93_TEMP_AUTO_OFFSET_FROM_REG(u8 reg, int nr, int mode)
+{
+ /* temp1-temp2 (nr=0,1) use lower nibble */
+ if (nr < 2)
+ return LM93_TEMP_OFFSET_FROM_REG(reg & 0x0f, mode);
+
+ /* temp3-temp4 (nr=2,3) use upper nibble */
+ else
+ return LM93_TEMP_OFFSET_FROM_REG(reg >> 4 & 0x0f, mode);
+}
+
+/*
+ * TEMP: 1/10 degrees C (0C to +15C (mode 0) or +7.5C (mode non-zero))
+ * REG: 1.0C/bit (mode 0) or 0.5C/bit (mode non-zero)
+ * 0 <= nr <= 3
+ */
+static u8 LM93_TEMP_AUTO_OFFSET_TO_REG(u8 old, int off, int nr, int mode)
+{
+ u8 new = LM93_TEMP_OFFSET_TO_REG(off, mode);
+
+ /* temp1-temp2 (nr=0,1) use lower nibble */
+ if (nr < 2)
+ return (old & 0xf0) | (new & 0x0f);
+
+ /* temp3-temp4 (nr=2,3) use upper nibble */
+ else
+ return (new << 4 & 0xf0) | (old & 0x0f);
+}
+
+static int LM93_AUTO_BOOST_HYST_FROM_REGS(struct lm93_data *data, int nr,
+ int mode)
+{
+ u8 reg;
+
+ switch (nr) {
+ case 0:
+ reg = data->boost_hyst[0] & 0x0f;
+ break;
+ case 1:
+ reg = data->boost_hyst[0] >> 4 & 0x0f;
+ break;
+ case 2:
+ reg = data->boost_hyst[1] & 0x0f;
+ break;
+ case 3:
+ default:
+ reg = data->boost_hyst[1] >> 4 & 0x0f;
+ break;
+ }
+
+ return LM93_TEMP_FROM_REG(data->boost[nr]) -
+ LM93_TEMP_OFFSET_FROM_REG(reg, mode);
+}
+
+static u8 LM93_AUTO_BOOST_HYST_TO_REG(struct lm93_data *data, long hyst,
+ int nr, int mode)
+{
+ u8 reg = LM93_TEMP_OFFSET_TO_REG(
+ (LM93_TEMP_FROM_REG(data->boost[nr]) - hyst), mode);
+
+ switch (nr) {
+ case 0:
+ reg = (data->boost_hyst[0] & 0xf0) | (reg & 0x0f);
+ break;
+ case 1:
+ reg = (reg << 4 & 0xf0) | (data->boost_hyst[0] & 0x0f);
+ break;
+ case 2:
+ reg = (data->boost_hyst[1] & 0xf0) | (reg & 0x0f);
+ break;
+ case 3:
+ default:
+ reg = (reg << 4 & 0xf0) | (data->boost_hyst[1] & 0x0f);
+ break;
+ }
+
+ return reg;
+}
+
+/*
+ * PWM: 0-255 per sensors documentation
+ * REG: 0-13 as mapped below... right justified
+ */
+enum pwm_freq { LM93_PWM_MAP_HI_FREQ, LM93_PWM_MAP_LO_FREQ };
+
+static int lm93_pwm_map[2][16] = {
+ {
+ 0x00, /* 0.00% */ 0x40, /* 25.00% */
+ 0x50, /* 31.25% */ 0x60, /* 37.50% */
+ 0x70, /* 43.75% */ 0x80, /* 50.00% */
+ 0x90, /* 56.25% */ 0xa0, /* 62.50% */
+ 0xb0, /* 68.75% */ 0xc0, /* 75.00% */
+ 0xd0, /* 81.25% */ 0xe0, /* 87.50% */
+ 0xf0, /* 93.75% */ 0xff, /* 100.00% */
+ 0xff, 0xff, /* 14, 15 are reserved and should never occur */
+ },
+ {
+ 0x00, /* 0.00% */ 0x40, /* 25.00% */
+ 0x49, /* 28.57% */ 0x52, /* 32.14% */
+ 0x5b, /* 35.71% */ 0x64, /* 39.29% */
+ 0x6d, /* 42.86% */ 0x76, /* 46.43% */
+ 0x80, /* 50.00% */ 0x89, /* 53.57% */
+ 0x92, /* 57.14% */ 0xb6, /* 71.43% */
+ 0xdb, /* 85.71% */ 0xff, /* 100.00% */
+ 0xff, 0xff, /* 14, 15 are reserved and should never occur */
+ },
+};
+
+static int LM93_PWM_FROM_REG(u8 reg, enum pwm_freq freq)
+{
+ return lm93_pwm_map[freq][reg & 0x0f];
+}
+
+/* round up to nearest match */
+static u8 LM93_PWM_TO_REG(int pwm, enum pwm_freq freq)
+{
+ int i;
+ for (i = 0; i < 13; i++)
+ if (pwm <= lm93_pwm_map[freq][i])
+ break;
+
+ /* can fall through with i==13 */
+ return (u8)i;
+}
+
+static int LM93_FAN_FROM_REG(u16 regs)
+{
+ const u16 count = le16_to_cpu(regs) >> 2;
+ return count == 0 ? -1 : count == 0x3fff ? 0 : 1350000 / count;
+}
+
+/*
+ * RPM: (82.5 to 1350000)
+ * REG: 14-bits, LE, *left* justified
+ */
+static u16 LM93_FAN_TO_REG(long rpm)
+{
+ u16 count, regs;
+
+ if (rpm == 0) {
+ count = 0x3fff;
+ } else {
+ rpm = clamp_val(rpm, 1, 1000000);
+ count = clamp_val((1350000 + rpm) / rpm, 1, 0x3ffe);
+ }
+
+ regs = count << 2;
+ return cpu_to_le16(regs);
+}
+
+/*
+ * PWM FREQ: HZ
+ * REG: 0-7 as mapped below
+ */
+static int lm93_pwm_freq_map[8] = {
+ 22500, 96, 84, 72, 60, 48, 36, 12
+};
+
+static int LM93_PWM_FREQ_FROM_REG(u8 reg)
+{
+ return lm93_pwm_freq_map[reg & 0x07];
+}
+
+/* round up to nearest match */
+static u8 LM93_PWM_FREQ_TO_REG(int freq)
+{
+ int i;
+ for (i = 7; i > 0; i--)
+ if (freq <= lm93_pwm_freq_map[i])
+ break;
+
+ /* can fall through with i==0 */
+ return (u8)i;
+}
+
+/*
+ * TIME: 1/100 seconds
+ * REG: 0-7 as mapped below
+ */
+static int lm93_spinup_time_map[8] = {
+ 0, 10, 25, 40, 70, 100, 200, 400,
+};
+
+static int LM93_SPINUP_TIME_FROM_REG(u8 reg)
+{
+ return lm93_spinup_time_map[reg >> 5 & 0x07];
+}
+
+/* round up to nearest match */
+static u8 LM93_SPINUP_TIME_TO_REG(int time)
+{
+ int i;
+ for (i = 0; i < 7; i++)
+ if (time <= lm93_spinup_time_map[i])
+ break;
+
+ /* can fall through with i==8 */
+ return (u8)i;
+}
+
+#define LM93_RAMP_MIN 0
+#define LM93_RAMP_MAX 75
+
+static int LM93_RAMP_FROM_REG(u8 reg)
+{
+ return (reg & 0x0f) * 5;
+}
+
+/*
+ * RAMP: 1/100 seconds
+ * REG: 50mS/bit 4-bits right justified
+ */
+static u8 LM93_RAMP_TO_REG(int ramp)
+{
+ ramp = clamp_val(ramp, LM93_RAMP_MIN, LM93_RAMP_MAX);
+ return (u8)((ramp + 2) / 5);
+}
+
+/*
+ * PROCHOT: 0-255, 0 => 0%, 255 => > 96.6%
+ * REG: (same)
+ */
+static u8 LM93_PROCHOT_TO_REG(long prochot)
+{
+ prochot = clamp_val(prochot, 0, 255);
+ return (u8)prochot;
+}
+
+/*
+ * PROCHOT-INTERVAL: 73 - 37200 (1/100 seconds)
+ * REG: 0-9 as mapped below
+ */
+static int lm93_interval_map[10] = {
+ 73, 146, 290, 580, 1170, 2330, 4660, 9320, 18600, 37200,
+};
+
+static int LM93_INTERVAL_FROM_REG(u8 reg)
+{
+ return lm93_interval_map[reg & 0x0f];
+}
+
+/* round up to nearest match */
+static u8 LM93_INTERVAL_TO_REG(long interval)
+{
+ int i;
+ for (i = 0; i < 9; i++)
+ if (interval <= lm93_interval_map[i])
+ break;
+
+ /* can fall through with i==9 */
+ return (u8)i;
+}
+
+/*
+ * GPIO: 0-255, GPIO0 is LSB
+ * REG: inverted
+ */
+static unsigned LM93_GPI_FROM_REG(u8 reg)
+{
+ return ~reg & 0xff;
+}
+
+/*
+ * alarm bitmask definitions
+ * The LM93 has nearly 64 bits of error status... I've pared that down to
+ * what I think is a useful subset in order to fit it into 32 bits.
+ *
+ * Especially note that the #VRD_HOT alarms are missing because we provide
+ * that information as values in another sysfs file.
+ *
+ * If libsensors is extended to support 64 bit values, this could be revisited.
+ */
+#define LM93_ALARM_IN1 0x00000001
+#define LM93_ALARM_IN2 0x00000002
+#define LM93_ALARM_IN3 0x00000004
+#define LM93_ALARM_IN4 0x00000008
+#define LM93_ALARM_IN5 0x00000010
+#define LM93_ALARM_IN6 0x00000020
+#define LM93_ALARM_IN7 0x00000040
+#define LM93_ALARM_IN8 0x00000080
+#define LM93_ALARM_IN9 0x00000100
+#define LM93_ALARM_IN10 0x00000200
+#define LM93_ALARM_IN11 0x00000400
+#define LM93_ALARM_IN12 0x00000800
+#define LM93_ALARM_IN13 0x00001000
+#define LM93_ALARM_IN14 0x00002000
+#define LM93_ALARM_IN15 0x00004000
+#define LM93_ALARM_IN16 0x00008000
+#define LM93_ALARM_FAN1 0x00010000
+#define LM93_ALARM_FAN2 0x00020000
+#define LM93_ALARM_FAN3 0x00040000
+#define LM93_ALARM_FAN4 0x00080000
+#define LM93_ALARM_PH1_ERR 0x00100000
+#define LM93_ALARM_PH2_ERR 0x00200000
+#define LM93_ALARM_SCSI1_ERR 0x00400000
+#define LM93_ALARM_SCSI2_ERR 0x00800000
+#define LM93_ALARM_DVDDP1_ERR 0x01000000
+#define LM93_ALARM_DVDDP2_ERR 0x02000000
+#define LM93_ALARM_D1_ERR 0x04000000
+#define LM93_ALARM_D2_ERR 0x08000000
+#define LM93_ALARM_TEMP1 0x10000000
+#define LM93_ALARM_TEMP2 0x20000000
+#define LM93_ALARM_TEMP3 0x40000000
+
+static unsigned LM93_ALARMS_FROM_REG(struct block1_t b1)
+{
+ unsigned result;
+ result = b1.host_status_2 & 0x3f;
+
+ if (vccp_limit_type[0])
+ result |= (b1.host_status_4 & 0x10) << 2;
+ else
+ result |= b1.host_status_2 & 0x40;
+
+ if (vccp_limit_type[1])
+ result |= (b1.host_status_4 & 0x20) << 2;
+ else
+ result |= b1.host_status_2 & 0x80;
+
+ result |= b1.host_status_3 << 8;
+ result |= (b1.fan_status & 0x0f) << 16;
+ result |= (b1.p1_prochot_status & 0x80) << 13;
+ result |= (b1.p2_prochot_status & 0x80) << 14;
+ result |= (b1.host_status_4 & 0xfc) << 20;
+ result |= (b1.host_status_1 & 0x07) << 28;
+ return result;
+}
+
+#define MAX_RETRIES 5
+
+static u8 lm93_read_byte(struct i2c_client *client, u8 reg)
+{
+ int value, i;
+
+ /* retry in case of read errors */
+ for (i = 1; i <= MAX_RETRIES; i++) {
+ value = i2c_smbus_read_byte_data(client, reg);
+ if (value >= 0) {
+ return value;
+ } else {
+ dev_warn(&client->dev,
+ "lm93: read byte data failed, address 0x%02x.\n",
+ reg);
+ mdelay(i + 3);
+ }
+
+ }
+
+ /* <TODO> what to return in case of error? */
+ dev_err(&client->dev, "lm93: All read byte retries failed!!\n");
+ return 0;
+}
+
+static int lm93_write_byte(struct i2c_client *client, u8 reg, u8 value)
+{
+ int result;
+
+ /* <TODO> how to handle write errors? */
+ result = i2c_smbus_write_byte_data(client, reg, value);
+
+ if (result < 0)
+ dev_warn(&client->dev,
+ "lm93: write byte data failed, 0x%02x at address 0x%02x.\n",
+ value, reg);
+
+ return result;
+}
+
+static u16 lm93_read_word(struct i2c_client *client, u8 reg)
+{
+ int value, i;
+
+ /* retry in case of read errors */
+ for (i = 1; i <= MAX_RETRIES; i++) {
+ value = i2c_smbus_read_word_data(client, reg);
+ if (value >= 0) {
+ return value;
+ } else {
+ dev_warn(&client->dev,
+ "lm93: read word data failed, address 0x%02x.\n",
+ reg);
+ mdelay(i + 3);
+ }
+
+ }
+
+ /* <TODO> what to return in case of error? */
+ dev_err(&client->dev, "lm93: All read word retries failed!!\n");
+ return 0;
+}
+
+static int lm93_write_word(struct i2c_client *client, u8 reg, u16 value)
+{
+ int result;
+
+ /* <TODO> how to handle write errors? */
+ result = i2c_smbus_write_word_data(client, reg, value);
+
+ if (result < 0)
+ dev_warn(&client->dev,
+ "lm93: write word data failed, 0x%04x at address 0x%02x.\n",
+ value, reg);
+
+ return result;
+}
+
+static u8 lm93_block_buffer[I2C_SMBUS_BLOCK_MAX];
+
+/*
+ * read block data into values, retry if not expected length
+ * fbn => index to lm93_block_read_cmds table
+ * (Fixed Block Number - section 14.5.2 of LM93 datasheet)
+ */
+static void lm93_read_block(struct i2c_client *client, u8 fbn, u8 *values)
+{
+ int i, result = 0;
+
+ for (i = 1; i <= MAX_RETRIES; i++) {
+ result = i2c_smbus_read_block_data(client,
+ lm93_block_read_cmds[fbn].cmd, lm93_block_buffer);
+
+ if (result == lm93_block_read_cmds[fbn].len) {
+ break;
+ } else {
+ dev_warn(&client->dev,
+ "lm93: block read data failed, command 0x%02x.\n",
+ lm93_block_read_cmds[fbn].cmd);
+ mdelay(i + 3);
+ }
+ }
+
+ if (result == lm93_block_read_cmds[fbn].len) {
+ memcpy(values, lm93_block_buffer,
+ lm93_block_read_cmds[fbn].len);
+ } else {
+ /* <TODO> what to do in case of error? */
+ }
+}
+
+static struct lm93_data *lm93_update_device(struct device *dev)
+{
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ const unsigned long interval = HZ + (HZ / 2);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + interval) ||
+ !data->valid) {
+
+ data->update(data, client);
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+/* update routine for data that has no corresponding SMBus block command */
+static void lm93_update_client_common(struct lm93_data *data,
+ struct i2c_client *client)
+{
+ int i;
+ u8 *ptr;
+
+ /* temp1 - temp4: limits */
+ for (i = 0; i < 4; i++) {
+ data->temp_lim[i].min =
+ lm93_read_byte(client, LM93_REG_TEMP_MIN(i));
+ data->temp_lim[i].max =
+ lm93_read_byte(client, LM93_REG_TEMP_MAX(i));
+ }
+
+ /* config register */
+ data->config = lm93_read_byte(client, LM93_REG_CONFIG);
+
+ /* vid1 - vid2: values */
+ for (i = 0; i < 2; i++)
+ data->vid[i] = lm93_read_byte(client, LM93_REG_VID(i));
+
+ /* prochot1 - prochot2: limits */
+ for (i = 0; i < 2; i++)
+ data->prochot_max[i] = lm93_read_byte(client,
+ LM93_REG_PROCHOT_MAX(i));
+
+ /* vccp1 - vccp2: VID relative limits */
+ for (i = 0; i < 2; i++)
+ data->vccp_limits[i] = lm93_read_byte(client,
+ LM93_REG_VCCP_LIMIT_OFF(i));
+
+ /* GPIO input state */
+ data->gpi = lm93_read_byte(client, LM93_REG_GPI);
+
+ /* #PROCHOT override state */
+ data->prochot_override = lm93_read_byte(client,
+ LM93_REG_PROCHOT_OVERRIDE);
+
+ /* #PROCHOT intervals */
+ data->prochot_interval = lm93_read_byte(client,
+ LM93_REG_PROCHOT_INTERVAL);
+
+ /* Fan Boost Temperature registers */
+ for (i = 0; i < 4; i++)
+ data->boost[i] = lm93_read_byte(client, LM93_REG_BOOST(i));
+
+ /* Fan Boost Temperature Hyst. registers */
+ data->boost_hyst[0] = lm93_read_byte(client, LM93_REG_BOOST_HYST_12);
+ data->boost_hyst[1] = lm93_read_byte(client, LM93_REG_BOOST_HYST_34);
+
+ /* Temperature Zone Min. PWM & Hysteresis registers */
+ data->auto_pwm_min_hyst[0] =
+ lm93_read_byte(client, LM93_REG_PWM_MIN_HYST_12);
+ data->auto_pwm_min_hyst[1] =
+ lm93_read_byte(client, LM93_REG_PWM_MIN_HYST_34);
+
+ /* #PROCHOT & #VRDHOT PWM Ramp Control register */
+ data->pwm_ramp_ctl = lm93_read_byte(client, LM93_REG_PWM_RAMP_CTL);
+
+ /* misc setup registers */
+ data->sfc1 = lm93_read_byte(client, LM93_REG_SFC1);
+ data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
+ data->sf_tach_to_pwm = lm93_read_byte(client,
+ LM93_REG_SF_TACH_TO_PWM);
+
+ /* write back alarm values to clear */
+ for (i = 0, ptr = (u8 *)(&data->block1); i < 8; i++)
+ lm93_write_byte(client, LM93_REG_HOST_ERROR_1 + i, *(ptr + i));
+}
+
+/* update routine which uses SMBus block data commands */
+static void lm93_update_client_full(struct lm93_data *data,
+ struct i2c_client *client)
+{
+ dev_dbg(&client->dev, "starting device update (block data enabled)\n");
+
+ /* in1 - in16: values & limits */
+ lm93_read_block(client, 3, (u8 *)(data->block3));
+ lm93_read_block(client, 7, (u8 *)(data->block7));
+
+ /* temp1 - temp4: values */
+ lm93_read_block(client, 2, (u8 *)(data->block2));
+
+ /* prochot1 - prochot2: values */
+ lm93_read_block(client, 4, (u8 *)(data->block4));
+
+ /* fan1 - fan4: values & limits */
+ lm93_read_block(client, 5, (u8 *)(data->block5));
+ lm93_read_block(client, 8, (u8 *)(data->block8));
+
+ /* pmw control registers */
+ lm93_read_block(client, 9, (u8 *)(data->block9));
+
+ /* alarm values */
+ lm93_read_block(client, 1, (u8 *)(&data->block1));
+
+ /* auto/pwm registers */
+ lm93_read_block(client, 10, (u8 *)(&data->block10));
+
+ lm93_update_client_common(data, client);
+}
+
+/* update routine which uses SMBus byte/word data commands only */
+static void lm93_update_client_min(struct lm93_data *data,
+ struct i2c_client *client)
+{
+ int i, j;
+ u8 *ptr;
+
+ dev_dbg(&client->dev, "starting device update (block data disabled)\n");
+
+ /* in1 - in16: values & limits */
+ for (i = 0; i < 16; i++) {
+ data->block3[i] =
+ lm93_read_byte(client, LM93_REG_IN(i));
+ data->block7[i].min =
+ lm93_read_byte(client, LM93_REG_IN_MIN(i));
+ data->block7[i].max =
+ lm93_read_byte(client, LM93_REG_IN_MAX(i));
+ }
+
+ /* temp1 - temp4: values */
+ for (i = 0; i < 4; i++) {
+ data->block2[i] =
+ lm93_read_byte(client, LM93_REG_TEMP(i));
+ }
+
+ /* prochot1 - prochot2: values */
+ for (i = 0; i < 2; i++) {
+ data->block4[i].cur =
+ lm93_read_byte(client, LM93_REG_PROCHOT_CUR(i));
+ data->block4[i].avg =
+ lm93_read_byte(client, LM93_REG_PROCHOT_AVG(i));
+ }
+
+ /* fan1 - fan4: values & limits */
+ for (i = 0; i < 4; i++) {
+ data->block5[i] =
+ lm93_read_word(client, LM93_REG_FAN(i));
+ data->block8[i] =
+ lm93_read_word(client, LM93_REG_FAN_MIN(i));
+ }
+
+ /* pwm control registers */
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 4; j++) {
+ data->block9[i][j] =
+ lm93_read_byte(client, LM93_REG_PWM_CTL(i, j));
+ }
+ }
+
+ /* alarm values */
+ for (i = 0, ptr = (u8 *)(&data->block1); i < 8; i++) {
+ *(ptr + i) =
+ lm93_read_byte(client, LM93_REG_HOST_ERROR_1 + i);
+ }
+
+ /* auto/pwm (base temp) registers */
+ for (i = 0; i < 4; i++) {
+ data->block10.base[i] =
+ lm93_read_byte(client, LM93_REG_TEMP_BASE(i));
+ }
+
+ /* auto/pwm (offset temp) registers */
+ for (i = 0; i < 12; i++) {
+ data->block10.offset[i] =
+ lm93_read_byte(client, LM93_REG_TEMP_OFFSET(i));
+ }
+
+ lm93_update_client_common(data, client);
+}
+
+/* following are the sysfs callback functions */
+static ssize_t in_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_IN_FROM_REG(nr, data->block3[nr]));
+}
+
+static SENSOR_DEVICE_ATTR_RO(in1_input, in, 0);
+static SENSOR_DEVICE_ATTR_RO(in2_input, in, 1);
+static SENSOR_DEVICE_ATTR_RO(in3_input, in, 2);
+static SENSOR_DEVICE_ATTR_RO(in4_input, in, 3);
+static SENSOR_DEVICE_ATTR_RO(in5_input, in, 4);
+static SENSOR_DEVICE_ATTR_RO(in6_input, in, 5);
+static SENSOR_DEVICE_ATTR_RO(in7_input, in, 6);
+static SENSOR_DEVICE_ATTR_RO(in8_input, in, 7);
+static SENSOR_DEVICE_ATTR_RO(in9_input, in, 8);
+static SENSOR_DEVICE_ATTR_RO(in10_input, in, 9);
+static SENSOR_DEVICE_ATTR_RO(in11_input, in, 10);
+static SENSOR_DEVICE_ATTR_RO(in12_input, in, 11);
+static SENSOR_DEVICE_ATTR_RO(in13_input, in, 12);
+static SENSOR_DEVICE_ATTR_RO(in14_input, in, 13);
+static SENSOR_DEVICE_ATTR_RO(in15_input, in, 14);
+static SENSOR_DEVICE_ATTR_RO(in16_input, in, 15);
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ int vccp = nr - 6;
+ long rc, vid;
+
+ if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) {
+ vid = LM93_VID_FROM_REG(data->vid[vccp]);
+ rc = LM93_IN_MIN_FROM_REG(data->vccp_limits[vccp], vid);
+ } else {
+ rc = LM93_IN_FROM_REG(nr, data->block7[nr].min);
+ }
+ return sprintf(buf, "%ld\n", rc);
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int vccp = nr - 6;
+ long vid;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) {
+ vid = LM93_VID_FROM_REG(data->vid[vccp]);
+ data->vccp_limits[vccp] = (data->vccp_limits[vccp] & 0xf0) |
+ LM93_IN_REL_TO_REG(val, 0, vid);
+ lm93_write_byte(client, LM93_REG_VCCP_LIMIT_OFF(vccp),
+ data->vccp_limits[vccp]);
+ } else {
+ data->block7[nr].min = LM93_IN_TO_REG(nr, val);
+ lm93_write_byte(client, LM93_REG_IN_MIN(nr),
+ data->block7[nr].min);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 0);
+static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 1);
+static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 2);
+static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 3);
+static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 4);
+static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 5);
+static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 6);
+static SENSOR_DEVICE_ATTR_RW(in8_min, in_min, 7);
+static SENSOR_DEVICE_ATTR_RW(in9_min, in_min, 8);
+static SENSOR_DEVICE_ATTR_RW(in10_min, in_min, 9);
+static SENSOR_DEVICE_ATTR_RW(in11_min, in_min, 10);
+static SENSOR_DEVICE_ATTR_RW(in12_min, in_min, 11);
+static SENSOR_DEVICE_ATTR_RW(in13_min, in_min, 12);
+static SENSOR_DEVICE_ATTR_RW(in14_min, in_min, 13);
+static SENSOR_DEVICE_ATTR_RW(in15_min, in_min, 14);
+static SENSOR_DEVICE_ATTR_RW(in16_min, in_min, 15);
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ int vccp = nr - 6;
+ long rc, vid;
+
+ if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) {
+ vid = LM93_VID_FROM_REG(data->vid[vccp]);
+ rc = LM93_IN_MAX_FROM_REG(data->vccp_limits[vccp], vid);
+ } else {
+ rc = LM93_IN_FROM_REG(nr, data->block7[nr].max);
+ }
+ return sprintf(buf, "%ld\n", rc);
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int vccp = nr - 6;
+ long vid;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) {
+ vid = LM93_VID_FROM_REG(data->vid[vccp]);
+ data->vccp_limits[vccp] = (data->vccp_limits[vccp] & 0x0f) |
+ LM93_IN_REL_TO_REG(val, 1, vid);
+ lm93_write_byte(client, LM93_REG_VCCP_LIMIT_OFF(vccp),
+ data->vccp_limits[vccp]);
+ } else {
+ data->block7[nr].max = LM93_IN_TO_REG(nr, val);
+ lm93_write_byte(client, LM93_REG_IN_MAX(nr),
+ data->block7[nr].max);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 0);
+static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 1);
+static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 2);
+static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 3);
+static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 4);
+static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 5);
+static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 6);
+static SENSOR_DEVICE_ATTR_RW(in8_max, in_max, 7);
+static SENSOR_DEVICE_ATTR_RW(in9_max, in_max, 8);
+static SENSOR_DEVICE_ATTR_RW(in10_max, in_max, 9);
+static SENSOR_DEVICE_ATTR_RW(in11_max, in_max, 10);
+static SENSOR_DEVICE_ATTR_RW(in12_max, in_max, 11);
+static SENSOR_DEVICE_ATTR_RW(in13_max, in_max, 12);
+static SENSOR_DEVICE_ATTR_RW(in14_max, in_max, 13);
+static SENSOR_DEVICE_ATTR_RW(in15_max, in_max, 14);
+static SENSOR_DEVICE_ATTR_RW(in16_max, in_max, 15);
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->block2[nr]));
+}
+
+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 ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->temp_lim[nr].min));
+}
+
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_lim[nr].min = LM93_TEMP_TO_REG(val);
+ lm93_write_byte(client, LM93_REG_TEMP_MIN(nr), data->temp_lim[nr].min);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+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 ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->temp_lim[nr].max));
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_lim[nr].max = LM93_TEMP_TO_REG(val);
+ lm93_write_byte(client, LM93_REG_TEMP_MAX(nr), data->temp_lim[nr].max);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+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 ssize_t temp_auto_base_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->block10.base[nr]));
+}
+
+static ssize_t temp_auto_base_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->block10.base[nr] = LM93_TEMP_TO_REG(val);
+ lm93_write_byte(client, LM93_REG_TEMP_BASE(nr), data->block10.base[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_base, temp_auto_base, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_base, temp_auto_base, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_base, temp_auto_base, 2);
+
+static ssize_t temp_auto_boost_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->boost[nr]));
+}
+
+static ssize_t temp_auto_boost_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->boost[nr] = LM93_TEMP_TO_REG(val);
+ lm93_write_byte(client, LM93_REG_BOOST(nr), data->boost[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_boost, temp_auto_boost, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_boost, temp_auto_boost, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_boost, temp_auto_boost, 2);
+
+static ssize_t temp_auto_boost_hyst_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ int mode = LM93_TEMP_OFFSET_MODE_FROM_REG(data->sfc2, nr);
+ return sprintf(buf, "%d\n",
+ LM93_AUTO_BOOST_HYST_FROM_REGS(data, nr, mode));
+}
+
+static ssize_t temp_auto_boost_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_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);
+ /* force 0.5C/bit mode */
+ data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
+ data->sfc2 |= ((nr < 2) ? 0x10 : 0x20);
+ lm93_write_byte(client, LM93_REG_SFC2, data->sfc2);
+ data->boost_hyst[nr/2] = LM93_AUTO_BOOST_HYST_TO_REG(data, val, nr, 1);
+ lm93_write_byte(client, LM93_REG_BOOST_HYST(nr),
+ data->boost_hyst[nr/2]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_boost_hyst, temp_auto_boost_hyst, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_boost_hyst, temp_auto_boost_hyst, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_boost_hyst, temp_auto_boost_hyst, 2);
+
+static ssize_t temp_auto_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *s_attr = to_sensor_dev_attr_2(attr);
+ int nr = s_attr->index;
+ int ofs = s_attr->nr;
+ struct lm93_data *data = lm93_update_device(dev);
+ int mode = LM93_TEMP_OFFSET_MODE_FROM_REG(data->sfc2, nr);
+ return sprintf(buf, "%d\n",
+ LM93_TEMP_AUTO_OFFSET_FROM_REG(data->block10.offset[ofs],
+ nr, mode));
+}
+
+static ssize_t temp_auto_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *s_attr = to_sensor_dev_attr_2(attr);
+ int nr = s_attr->index;
+ int ofs = s_attr->nr;
+ struct lm93_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);
+ /* force 0.5C/bit mode */
+ data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
+ data->sfc2 |= ((nr < 2) ? 0x10 : 0x20);
+ lm93_write_byte(client, LM93_REG_SFC2, data->sfc2);
+ data->block10.offset[ofs] = LM93_TEMP_AUTO_OFFSET_TO_REG(
+ data->block10.offset[ofs], val, nr, 1);
+ lm93_write_byte(client, LM93_REG_TEMP_OFFSET(ofs),
+ data->block10.offset[ofs]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset1, temp_auto_offset, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset2, temp_auto_offset, 1, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset3, temp_auto_offset, 2, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset4, temp_auto_offset, 3, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset5, temp_auto_offset, 4, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset6, temp_auto_offset, 5, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset7, temp_auto_offset, 6, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset8, temp_auto_offset, 7, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset9, temp_auto_offset, 8, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset10, temp_auto_offset, 9, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset11, temp_auto_offset, 10, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_offset12, temp_auto_offset, 11, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset1, temp_auto_offset, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset2, temp_auto_offset, 1, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset3, temp_auto_offset, 2, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset4, temp_auto_offset, 3, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset5, temp_auto_offset, 4, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset6, temp_auto_offset, 5, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset7, temp_auto_offset, 6, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset8, temp_auto_offset, 7, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset9, temp_auto_offset, 8, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset10, temp_auto_offset, 9, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset11, temp_auto_offset, 10, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_offset12, temp_auto_offset, 11, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset1, temp_auto_offset, 0, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset2, temp_auto_offset, 1, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset3, temp_auto_offset, 2, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset4, temp_auto_offset, 3, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset5, temp_auto_offset, 4, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset6, temp_auto_offset, 5, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset7, temp_auto_offset, 6, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset8, temp_auto_offset, 7, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset9, temp_auto_offset, 8, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset10, temp_auto_offset, 9, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset11, temp_auto_offset, 10, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_auto_offset12, temp_auto_offset, 11, 2);
+
+static ssize_t temp_auto_pwm_min_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ u8 reg, ctl4;
+ struct lm93_data *data = lm93_update_device(dev);
+ reg = data->auto_pwm_min_hyst[nr/2] >> 4 & 0x0f;
+ ctl4 = data->block9[nr][LM93_PWM_CTL4];
+ return sprintf(buf, "%d\n", LM93_PWM_FROM_REG(reg, (ctl4 & 0x07) ?
+ LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ));
+}
+
+static ssize_t temp_auto_pwm_min_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 reg, ctl4;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ reg = lm93_read_byte(client, LM93_REG_PWM_MIN_HYST(nr));
+ ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4));
+ reg = (reg & 0x0f) |
+ LM93_PWM_TO_REG(val, (ctl4 & 0x07) ?
+ LM93_PWM_MAP_LO_FREQ :
+ LM93_PWM_MAP_HI_FREQ) << 4;
+ data->auto_pwm_min_hyst[nr/2] = reg;
+ lm93_write_byte(client, LM93_REG_PWM_MIN_HYST(nr), reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_pwm_min, temp_auto_pwm_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_pwm_min, temp_auto_pwm_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_pwm_min, temp_auto_pwm_min, 2);
+
+static ssize_t temp_auto_offset_hyst_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ int mode = LM93_TEMP_OFFSET_MODE_FROM_REG(data->sfc2, nr);
+ return sprintf(buf, "%d\n", LM93_TEMP_OFFSET_FROM_REG(
+ data->auto_pwm_min_hyst[nr / 2], mode));
+}
+
+static ssize_t temp_auto_offset_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ /* force 0.5C/bit mode */
+ data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
+ data->sfc2 |= ((nr < 2) ? 0x10 : 0x20);
+ lm93_write_byte(client, LM93_REG_SFC2, data->sfc2);
+ reg = data->auto_pwm_min_hyst[nr/2];
+ reg = (reg & 0xf0) | (LM93_TEMP_OFFSET_TO_REG(val, 1) & 0x0f);
+ data->auto_pwm_min_hyst[nr/2] = reg;
+ lm93_write_byte(client, LM93_REG_PWM_MIN_HYST(nr), reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_auto_offset_hyst, temp_auto_offset_hyst, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_auto_offset_hyst, temp_auto_offset_hyst, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_auto_offset_hyst, temp_auto_offset_hyst, 2);
+
+static ssize_t fan_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *s_attr = to_sensor_dev_attr(attr);
+ int nr = s_attr->index;
+ struct lm93_data *data = lm93_update_device(dev);
+
+ return sprintf(buf, "%d\n", LM93_FAN_FROM_REG(data->block5[nr]));
+}
+
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_input, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_input, fan_input, 3);
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+
+ return sprintf(buf, "%d\n", LM93_FAN_FROM_REG(data->block8[nr]));
+}
+
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_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->block8[nr] = LM93_FAN_TO_REG(val);
+ lm93_write_word(client, LM93_REG_FAN_MIN(nr), data->block8[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+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);
+
+/*
+ * some tedious bit-twiddling here to deal with the register format:
+ *
+ * data->sf_tach_to_pwm: (tach to pwm mapping bits)
+ *
+ * bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * T4:P2 T4:P1 T3:P2 T3:P1 T2:P2 T2:P1 T1:P2 T1:P1
+ *
+ * data->sfc2: (enable bits)
+ *
+ * bit | 3 | 2 | 1 | 0
+ * T4 T3 T2 T1
+ */
+
+static ssize_t fan_smart_tach_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ long rc = 0;
+ int mapping;
+
+ /* extract the relevant mapping */
+ mapping = (data->sf_tach_to_pwm >> (nr * 2)) & 0x03;
+
+ /* if there's a mapping and it's enabled */
+ if (mapping && ((data->sfc2 >> nr) & 0x01))
+ rc = mapping;
+ return sprintf(buf, "%ld\n", rc);
+}
+
+/*
+ * helper function - must grab data->update_lock before calling
+ * fan is 0-3, indicating fan1-fan4
+ */
+static void lm93_write_fan_smart_tach(struct i2c_client *client,
+ struct lm93_data *data, int fan, long value)
+{
+ /* insert the new mapping and write it out */
+ data->sf_tach_to_pwm = lm93_read_byte(client, LM93_REG_SF_TACH_TO_PWM);
+ data->sf_tach_to_pwm &= ~(0x3 << fan * 2);
+ data->sf_tach_to_pwm |= value << fan * 2;
+ lm93_write_byte(client, LM93_REG_SF_TACH_TO_PWM, data->sf_tach_to_pwm);
+
+ /* insert the enable bit and write it out */
+ data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
+ if (value)
+ data->sfc2 |= 1 << fan;
+ else
+ data->sfc2 &= ~(1 << fan);
+ lm93_write_byte(client, LM93_REG_SFC2, data->sfc2);
+}
+
+static ssize_t fan_smart_tach_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_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);
+ /* sanity test, ignore the write otherwise */
+ if (val <= 2) {
+ /* can't enable if pwm freq is 22.5KHz */
+ if (val) {
+ u8 ctl4 = lm93_read_byte(client,
+ LM93_REG_PWM_CTL(val - 1, LM93_PWM_CTL4));
+ if ((ctl4 & 0x07) == 0)
+ val = 0;
+ }
+ lm93_write_fan_smart_tach(client, data, nr, val);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(fan1_smart_tach, fan_smart_tach, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_smart_tach, fan_smart_tach, 1);
+static SENSOR_DEVICE_ATTR_RW(fan3_smart_tach, fan_smart_tach, 2);
+static SENSOR_DEVICE_ATTR_RW(fan4_smart_tach, fan_smart_tach, 3);
+
+static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ u8 ctl2, ctl4;
+ long rc;
+
+ ctl2 = data->block9[nr][LM93_PWM_CTL2];
+ ctl4 = data->block9[nr][LM93_PWM_CTL4];
+ if (ctl2 & 0x01) /* show user commanded value if enabled */
+ rc = data->pwm_override[nr];
+ else /* show present h/w value if manual pwm disabled */
+ rc = LM93_PWM_FROM_REG(ctl2 >> 4, (ctl4 & 0x07) ?
+ LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ);
+ return sprintf(buf, "%ld\n", rc);
+}
+
+static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 ctl2, ctl4;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ ctl2 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2));
+ ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4));
+ ctl2 = (ctl2 & 0x0f) | LM93_PWM_TO_REG(val, (ctl4 & 0x07) ?
+ LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ) << 4;
+ /* save user commanded value */
+ data->pwm_override[nr] = LM93_PWM_FROM_REG(ctl2 >> 4,
+ (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ :
+ LM93_PWM_MAP_HI_FREQ);
+ lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2), ctl2);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1);
+
+static ssize_t pwm_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ u8 ctl2;
+ long rc;
+
+ ctl2 = data->block9[nr][LM93_PWM_CTL2];
+ if (ctl2 & 0x01) /* manual override enabled ? */
+ rc = ((ctl2 & 0xF0) == 0xF0) ? 0 : 1;
+ else
+ rc = 2;
+ return sprintf(buf, "%ld\n", rc);
+}
+
+static ssize_t pwm_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 ctl2;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ ctl2 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2));
+
+ switch (val) {
+ case 0:
+ ctl2 |= 0xF1; /* enable manual override, set PWM to max */
+ break;
+ case 1:
+ ctl2 |= 0x01; /* enable manual override */
+ break;
+ case 2:
+ ctl2 &= ~0x01; /* disable manual override */
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2), ctl2);
+ 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 ssize_t pwm_freq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ u8 ctl4;
+
+ ctl4 = data->block9[nr][LM93_PWM_CTL4];
+ return sprintf(buf, "%d\n", LM93_PWM_FREQ_FROM_REG(ctl4));
+}
+
+/*
+ * helper function - must grab data->update_lock before calling
+ * pwm is 0-1, indicating pwm1-pwm2
+ * this disables smart tach for all tach channels bound to the given pwm
+ */
+static void lm93_disable_fan_smart_tach(struct i2c_client *client,
+ struct lm93_data *data, int pwm)
+{
+ int mapping = lm93_read_byte(client, LM93_REG_SF_TACH_TO_PWM);
+ int mask;
+
+ /* collapse the mapping into a mask of enable bits */
+ mapping = (mapping >> pwm) & 0x55;
+ mask = mapping & 0x01;
+ mask |= (mapping & 0x04) >> 1;
+ mask |= (mapping & 0x10) >> 2;
+ mask |= (mapping & 0x40) >> 3;
+
+ /* disable smart tach according to the mask */
+ data->sfc2 = lm93_read_byte(client, LM93_REG_SFC2);
+ data->sfc2 &= ~mask;
+ lm93_write_byte(client, LM93_REG_SFC2, data->sfc2);
+}
+
+static ssize_t pwm_freq_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 ctl4;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4));
+ ctl4 = (ctl4 & 0xf8) | LM93_PWM_FREQ_TO_REG(val);
+ data->block9[nr][LM93_PWM_CTL4] = ctl4;
+ /* ctl4 == 0 -> 22.5KHz -> disable smart tach */
+ if (!ctl4)
+ lm93_disable_fan_smart_tach(client, data, nr);
+ lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4), ctl4);
+ 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 ssize_t pwm_auto_channels_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", data->block9[nr][LM93_PWM_CTL1]);
+}
+
+static ssize_t pwm_auto_channels_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_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->block9[nr][LM93_PWM_CTL1] = clamp_val(val, 0, 255);
+ lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL1),
+ data->block9[nr][LM93_PWM_CTL1]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels, pwm_auto_channels, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels, pwm_auto_channels, 1);
+
+static ssize_t pwm_auto_spinup_min_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ u8 ctl3, ctl4;
+
+ ctl3 = data->block9[nr][LM93_PWM_CTL3];
+ ctl4 = data->block9[nr][LM93_PWM_CTL4];
+ return sprintf(buf, "%d\n",
+ LM93_PWM_FROM_REG(ctl3 & 0x0f, (ctl4 & 0x07) ?
+ LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ));
+}
+
+static ssize_t pwm_auto_spinup_min_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 ctl3, ctl4;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ ctl3 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3));
+ ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4));
+ ctl3 = (ctl3 & 0xf0) | LM93_PWM_TO_REG(val, (ctl4 & 0x07) ?
+ LM93_PWM_MAP_LO_FREQ :
+ LM93_PWM_MAP_HI_FREQ);
+ data->block9[nr][LM93_PWM_CTL3] = ctl3;
+ lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3), ctl3);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_spinup_min, pwm_auto_spinup_min, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_spinup_min, pwm_auto_spinup_min, 1);
+
+static ssize_t pwm_auto_spinup_time_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_SPINUP_TIME_FROM_REG(
+ data->block9[nr][LM93_PWM_CTL3]));
+}
+
+static ssize_t pwm_auto_spinup_time_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 ctl3;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ ctl3 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3));
+ ctl3 = (ctl3 & 0x1f) | (LM93_SPINUP_TIME_TO_REG(val) << 5 & 0xe0);
+ data->block9[nr][LM93_PWM_CTL3] = ctl3;
+ lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3), ctl3);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_spinup_time, pwm_auto_spinup_time, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_spinup_time, pwm_auto_spinup_time, 1);
+
+static ssize_t pwm_auto_prochot_ramp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n",
+ LM93_RAMP_FROM_REG(data->pwm_ramp_ctl >> 4 & 0x0f));
+}
+
+static ssize_t pwm_auto_prochot_ramp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 ramp;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ ramp = lm93_read_byte(client, LM93_REG_PWM_RAMP_CTL);
+ ramp = (ramp & 0x0f) | (LM93_RAMP_TO_REG(val) << 4 & 0xf0);
+ lm93_write_byte(client, LM93_REG_PWM_RAMP_CTL, ramp);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(pwm_auto_prochot_ramp);
+
+static ssize_t pwm_auto_vrdhot_ramp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n",
+ LM93_RAMP_FROM_REG(data->pwm_ramp_ctl & 0x0f));
+}
+
+static ssize_t pwm_auto_vrdhot_ramp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 ramp;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ ramp = lm93_read_byte(client, LM93_REG_PWM_RAMP_CTL);
+ ramp = (ramp & 0xf0) | (LM93_RAMP_TO_REG(val) & 0x0f);
+ lm93_write_byte(client, LM93_REG_PWM_RAMP_CTL, ramp);
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+static DEVICE_ATTR_RW(pwm_auto_vrdhot_ramp);
+
+static ssize_t vid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_VID_FROM_REG(data->vid[nr]));
+}
+
+static SENSOR_DEVICE_ATTR_RO(cpu0_vid, vid, 0);
+static SENSOR_DEVICE_ATTR_RO(cpu1_vid, vid, 1);
+
+static ssize_t prochot_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", data->block4[nr].cur);
+}
+
+static SENSOR_DEVICE_ATTR_RO(prochot1, prochot, 0);
+static SENSOR_DEVICE_ATTR_RO(prochot2, prochot, 1);
+
+static ssize_t prochot_avg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", data->block4[nr].avg);
+}
+
+static SENSOR_DEVICE_ATTR_RO(prochot1_avg, prochot_avg, 0);
+static SENSOR_DEVICE_ATTR_RO(prochot2_avg, prochot_avg, 1);
+
+static ssize_t prochot_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", data->prochot_max[nr]);
+}
+
+static ssize_t prochot_max_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_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->prochot_max[nr] = LM93_PROCHOT_TO_REG(val);
+ lm93_write_byte(client, LM93_REG_PROCHOT_MAX(nr),
+ data->prochot_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(prochot1_max, prochot_max, 0);
+static SENSOR_DEVICE_ATTR_RW(prochot2_max, prochot_max, 1);
+
+static const u8 prochot_override_mask[] = { 0x80, 0x40 };
+
+static ssize_t prochot_override_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n",
+ (data->prochot_override & prochot_override_mask[nr]) ? 1 : 0);
+}
+
+static ssize_t prochot_override_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_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);
+ if (val)
+ data->prochot_override |= prochot_override_mask[nr];
+ else
+ data->prochot_override &= (~prochot_override_mask[nr]);
+ lm93_write_byte(client, LM93_REG_PROCHOT_OVERRIDE,
+ data->prochot_override);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(prochot1_override, prochot_override, 0);
+static SENSOR_DEVICE_ATTR_RW(prochot2_override, prochot_override, 1);
+
+static ssize_t prochot_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ u8 tmp;
+ if (nr == 1)
+ tmp = (data->prochot_interval & 0xf0) >> 4;
+ else
+ tmp = data->prochot_interval & 0x0f;
+ return sprintf(buf, "%d\n", LM93_INTERVAL_FROM_REG(tmp));
+}
+
+static ssize_t prochot_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 tmp;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ tmp = lm93_read_byte(client, LM93_REG_PROCHOT_INTERVAL);
+ if (nr == 1)
+ tmp = (tmp & 0x0f) | (LM93_INTERVAL_TO_REG(val) << 4);
+ else
+ tmp = (tmp & 0xf0) | LM93_INTERVAL_TO_REG(val);
+ data->prochot_interval = tmp;
+ lm93_write_byte(client, LM93_REG_PROCHOT_INTERVAL, tmp);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(prochot1_interval, prochot_interval, 0);
+static SENSOR_DEVICE_ATTR_RW(prochot2_interval, prochot_interval, 1);
+
+static ssize_t prochot_override_duty_cycle_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", data->prochot_override & 0x0f);
+}
+
+static ssize_t prochot_override_duty_cycle_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm93_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->prochot_override = (data->prochot_override & 0xf0) |
+ clamp_val(val, 0, 15);
+ lm93_write_byte(client, LM93_REG_PROCHOT_OVERRIDE,
+ data->prochot_override);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(prochot_override_duty_cycle);
+
+static ssize_t prochot_short_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", (data->config & 0x10) ? 1 : 0);
+}
+
+static ssize_t prochot_short_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm93_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);
+ if (val)
+ data->config |= 0x10;
+ else
+ data->config &= ~0x10;
+ lm93_write_byte(client, LM93_REG_CONFIG, data->config);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(prochot_short);
+
+static ssize_t vrdhot_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = (to_sensor_dev_attr(attr))->index;
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n",
+ data->block1.host_status_1 & (1 << (nr + 4)) ? 1 : 0);
+}
+
+static SENSOR_DEVICE_ATTR_RO(vrdhot1, vrdhot, 0);
+static SENSOR_DEVICE_ATTR_RO(vrdhot2, vrdhot, 1);
+
+static ssize_t gpio_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_GPI_FROM_REG(data->gpi));
+}
+
+static DEVICE_ATTR_RO(gpio);
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm93_data *data = lm93_update_device(dev);
+ return sprintf(buf, "%d\n", LM93_ALARMS_FROM_REG(data->block1));
+}
+
+static DEVICE_ATTR_RO(alarms);
+
+static struct attribute *lm93_attrs[] = {
+ &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_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ &sensor_dev_attr_in16_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ &sensor_dev_attr_in12_min.dev_attr.attr,
+ &sensor_dev_attr_in13_min.dev_attr.attr,
+ &sensor_dev_attr_in14_min.dev_attr.attr,
+ &sensor_dev_attr_in15_min.dev_attr.attr,
+ &sensor_dev_attr_in16_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ &sensor_dev_attr_in12_max.dev_attr.attr,
+ &sensor_dev_attr_in13_max.dev_attr.attr,
+ &sensor_dev_attr_in14_max.dev_attr.attr,
+ &sensor_dev_attr_in15_max.dev_attr.attr,
+ &sensor_dev_attr_in16_max.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_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.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_temp1_auto_base.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_base.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_base.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_boost.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_boost.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_boost.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_boost_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_boost_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_boost_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset1.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset2.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset3.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset4.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset5.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset6.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset7.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset8.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset9.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset10.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset11.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset12.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset1.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset2.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset3.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset4.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset5.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset6.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset7.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset8.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset9.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset10.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset11.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset12.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset1.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset2.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset3.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset4.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset5.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset6.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset7.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset8.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset9.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset10.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset11.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset12.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_offset_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_offset_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_offset_hyst.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_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_smart_tach.dev_attr.attr,
+ &sensor_dev_attr_fan2_smart_tach.dev_attr.attr,
+ &sensor_dev_attr_fan3_smart_tach.dev_attr.attr,
+ &sensor_dev_attr_fan4_smart_tach.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_spinup_min.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_spinup_min.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_spinup_time.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_spinup_time.dev_attr.attr,
+ &dev_attr_pwm_auto_prochot_ramp.attr,
+ &dev_attr_pwm_auto_vrdhot_ramp.attr,
+ &sensor_dev_attr_cpu0_vid.dev_attr.attr,
+ &sensor_dev_attr_cpu1_vid.dev_attr.attr,
+ &sensor_dev_attr_prochot1.dev_attr.attr,
+ &sensor_dev_attr_prochot2.dev_attr.attr,
+ &sensor_dev_attr_prochot1_avg.dev_attr.attr,
+ &sensor_dev_attr_prochot2_avg.dev_attr.attr,
+ &sensor_dev_attr_prochot1_max.dev_attr.attr,
+ &sensor_dev_attr_prochot2_max.dev_attr.attr,
+ &sensor_dev_attr_prochot1_override.dev_attr.attr,
+ &sensor_dev_attr_prochot2_override.dev_attr.attr,
+ &sensor_dev_attr_prochot1_interval.dev_attr.attr,
+ &sensor_dev_attr_prochot2_interval.dev_attr.attr,
+ &dev_attr_prochot_override_duty_cycle.attr,
+ &dev_attr_prochot_short.attr,
+ &sensor_dev_attr_vrdhot1.dev_attr.attr,
+ &sensor_dev_attr_vrdhot2.dev_attr.attr,
+ &dev_attr_gpio.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(lm93);
+
+static void lm93_init_client(struct i2c_client *client)
+{
+ int i;
+ u8 reg;
+
+ /* configure VID pin input thresholds */
+ reg = lm93_read_byte(client, LM93_REG_GPI_VID_CTL);
+ lm93_write_byte(client, LM93_REG_GPI_VID_CTL,
+ reg | (vid_agtl ? 0x03 : 0x00));
+
+ if (init) {
+ /* enable #ALERT pin */
+ reg = lm93_read_byte(client, LM93_REG_CONFIG);
+ lm93_write_byte(client, LM93_REG_CONFIG, reg | 0x08);
+
+ /* enable ASF mode for BMC status registers */
+ reg = lm93_read_byte(client, LM93_REG_STATUS_CONTROL);
+ lm93_write_byte(client, LM93_REG_STATUS_CONTROL, reg | 0x02);
+
+ /* set sleep state to S0 */
+ lm93_write_byte(client, LM93_REG_SLEEP_CONTROL, 0);
+
+ /* unmask #VRDHOT and dynamic VCCP (if nec) error events */
+ reg = lm93_read_byte(client, LM93_REG_MISC_ERR_MASK);
+ reg &= ~0x03;
+ reg &= ~(vccp_limit_type[0] ? 0x10 : 0);
+ reg &= ~(vccp_limit_type[1] ? 0x20 : 0);
+ lm93_write_byte(client, LM93_REG_MISC_ERR_MASK, reg);
+ }
+
+ /* start monitoring */
+ reg = lm93_read_byte(client, LM93_REG_CONFIG);
+ lm93_write_byte(client, LM93_REG_CONFIG, reg | 0x01);
+
+ /* spin until ready */
+ for (i = 0; i < 20; i++) {
+ msleep(10);
+ if ((lm93_read_byte(client, LM93_REG_CONFIG) & 0x80) == 0x80)
+ return;
+ }
+
+ dev_warn(&client->dev,
+ "timed out waiting for sensor chip to signal ready!\n");
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int mfr, ver;
+ const char *name;
+
+ if (!i2c_check_functionality(adapter, LM93_SMBUS_FUNC_MIN))
+ return -ENODEV;
+
+ /* detection */
+ mfr = lm93_read_byte(client, LM93_REG_MFR_ID);
+ if (mfr != 0x01) {
+ dev_dbg(&adapter->dev,
+ "detect failed, bad manufacturer id 0x%02x!\n", mfr);
+ return -ENODEV;
+ }
+
+ ver = lm93_read_byte(client, LM93_REG_VER);
+ switch (ver) {
+ case LM93_MFR_ID:
+ case LM93_MFR_ID_PROTOTYPE:
+ name = "lm93";
+ break;
+ case LM94_MFR_ID_2:
+ case LM94_MFR_ID:
+ case LM94_MFR_ID_PROTOTYPE:
+ name = "lm94";
+ break;
+ default:
+ dev_dbg(&adapter->dev,
+ "detect failed, bad version id 0x%02x!\n", ver);
+ return -ENODEV;
+ }
+
+ 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);
+
+ return 0;
+}
+
+static int lm93_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct lm93_data *data;
+ struct device *hwmon_dev;
+ int func;
+ void (*update)(struct lm93_data *, struct i2c_client *);
+
+ /* choose update routine based on bus capabilities */
+ func = i2c_get_functionality(client->adapter);
+ if (((LM93_SMBUS_FUNC_FULL & func) == LM93_SMBUS_FUNC_FULL) &&
+ (!disable_block)) {
+ dev_dbg(dev, "using SMBus block data transactions\n");
+ update = lm93_update_client_full;
+ } else if ((LM93_SMBUS_FUNC_MIN & func) == LM93_SMBUS_FUNC_MIN) {
+ dev_dbg(dev, "disabled SMBus block data transactions\n");
+ update = lm93_update_client_min;
+ } else {
+ dev_dbg(dev, "detect failed, smbus byte and/or word data not supported!\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(dev, sizeof(struct lm93_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* housekeeping */
+ data->client = client;
+ data->update = update;
+ mutex_init(&data->update_lock);
+
+ /* initialize the chip */
+ lm93_init_client(client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ lm93_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id lm93_id[] = {
+ { "lm93", 0 },
+ { "lm94", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm93_id);
+
+static struct i2c_driver lm93_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm93",
+ },
+ .probe_new = lm93_probe,
+ .id_table = lm93_id,
+ .detect = lm93_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm93_driver);
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>, "
+ "Hans J. Koch <hjk@hansjkoch.de>");
+MODULE_DESCRIPTION("LM93 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c
new file mode 100644
index 000000000..b4a9d0c22
--- /dev/null
+++ b/drivers/hwmon/lm95234.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Texas Instruments / National Semiconductor LM95234
+ *
+ * Copyright (c) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from lm95241.c
+ * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#define DRVNAME "lm95234"
+
+enum chips { lm95233, lm95234 };
+
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x2a, 0x2b, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* LM95234 registers */
+#define LM95234_REG_MAN_ID 0xFE
+#define LM95234_REG_CHIP_ID 0xFF
+#define LM95234_REG_STATUS 0x02
+#define LM95234_REG_CONFIG 0x03
+#define LM95234_REG_CONVRATE 0x04
+#define LM95234_REG_STS_FAULT 0x07
+#define LM95234_REG_STS_TCRIT1 0x08
+#define LM95234_REG_STS_TCRIT2 0x09
+#define LM95234_REG_TEMPH(x) ((x) + 0x10)
+#define LM95234_REG_TEMPL(x) ((x) + 0x20)
+#define LM95234_REG_UTEMPH(x) ((x) + 0x19) /* Remote only */
+#define LM95234_REG_UTEMPL(x) ((x) + 0x29)
+#define LM95234_REG_REM_MODEL 0x30
+#define LM95234_REG_REM_MODEL_STS 0x38
+#define LM95234_REG_OFFSET(x) ((x) + 0x31) /* Remote only */
+#define LM95234_REG_TCRIT1(x) ((x) + 0x40)
+#define LM95234_REG_TCRIT2(x) ((x) + 0x49) /* Remote channel 1,2 */
+#define LM95234_REG_TCRIT_HYST 0x5a
+
+#define NATSEMI_MAN_ID 0x01
+#define LM95233_CHIP_ID 0x89
+#define LM95234_CHIP_ID 0x79
+
+/* Client data (each client gets its own) */
+struct lm95234_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+ struct mutex update_lock;
+ unsigned long last_updated, interval; /* in jiffies */
+ bool valid; /* false until following fields are valid */
+ /* registers values */
+ int temp[5]; /* temperature (signed) */
+ u32 status; /* fault/alarm status */
+ u8 tcrit1[5]; /* critical temperature limit */
+ u8 tcrit2[2]; /* high temperature limit */
+ s8 toffset[4]; /* remote temperature offset */
+ u8 thyst; /* common hysteresis */
+
+ u8 sensor_type; /* temperature sensor type */
+};
+
+static int lm95234_read_temp(struct i2c_client *client, int index, int *t)
+{
+ int val;
+ u16 temp = 0;
+
+ if (index) {
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_UTEMPH(index - 1));
+ if (val < 0)
+ return val;
+ temp = val << 8;
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_UTEMPL(index - 1));
+ if (val < 0)
+ return val;
+ temp |= val;
+ *t = temp;
+ }
+ /*
+ * Read signed temperature if unsigned temperature is 0,
+ * or if this is the local sensor.
+ */
+ if (!temp) {
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_TEMPH(index));
+ if (val < 0)
+ return val;
+ temp = val << 8;
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_TEMPL(index));
+ if (val < 0)
+ return val;
+ temp |= val;
+ *t = (s16)temp;
+ }
+ return 0;
+}
+
+static u16 update_intervals[] = { 143, 364, 1000, 2500 };
+
+/* Fill value cache. Must be called with update lock held. */
+
+static int lm95234_fill_cache(struct lm95234_data *data,
+ struct i2c_client *client)
+{
+ int i, ret;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
+ if (ret < 0)
+ return ret;
+
+ data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]);
+
+ for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) {
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i));
+ if (ret < 0)
+ return ret;
+ data->tcrit1[i] = ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) {
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i));
+ if (ret < 0)
+ return ret;
+ data->tcrit2[i] = ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(data->toffset); i++) {
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i));
+ if (ret < 0)
+ return ret;
+ data->toffset[i] = ret;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST);
+ if (ret < 0)
+ return ret;
+ data->thyst = ret;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+ if (ret < 0)
+ return ret;
+ data->sensor_type = ret;
+
+ return 0;
+}
+
+static int lm95234_update_device(struct lm95234_data *data)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + data->interval) ||
+ !data->valid) {
+ int i;
+
+ if (!data->valid) {
+ ret = lm95234_fill_cache(data, client);
+ if (ret < 0)
+ goto abort;
+ }
+
+ data->valid = false;
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ ret = lm95234_read_temp(client, i, &data->temp[i]);
+ if (ret < 0)
+ goto abort;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT);
+ if (ret < 0)
+ goto abort;
+ data->status = ret;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1);
+ if (ret < 0)
+ goto abort;
+ data->status |= ret << 8;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2);
+ if (ret < 0)
+ goto abort;
+ data->status |= ret << 16;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+ ret = 0;
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n",
+ DIV_ROUND_CLOSEST(data->temp[index] * 125, 32));
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ u32 mask = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u", !!(data->status & mask));
+}
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ u8 mask = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n");
+}
+
+static ssize_t type_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ u8 mask = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val != 1 && val != 2)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (val == 1)
+ data->sensor_type |= mask;
+ else
+ data->sensor_type &= ~mask;
+ data->valid = false;
+ i2c_smbus_write_byte_data(data->client, LM95234_REG_REM_MODEL,
+ data->sensor_type);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t tcrit2_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u", data->tcrit2[index] * 1000);
+}
+
+static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, index ? 255 : 127);
+
+ mutex_lock(&data->update_lock);
+ data->tcrit2[index] = val;
+ i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT2(index), val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t tcrit2_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ /* Result can be negative, so be careful with unsigned operands */
+ return sprintf(buf, "%d",
+ ((int)data->tcrit2[index] - (int)data->thyst) * 1000);
+}
+
+static ssize_t tcrit1_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u", data->tcrit1[index] * 1000);
+}
+
+static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+ long val;
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->tcrit1[index] = val;
+ i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT1(index), val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t tcrit1_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ /* Result can be negative, so be careful with unsigned operands */
+ return sprintf(buf, "%d",
+ ((int)data->tcrit1[index] - (int)data->thyst) * 1000);
+}
+
+static ssize_t tcrit1_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+ long val;
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ val = clamp_val((int)data->tcrit1[index] - val, 0, 31);
+
+ mutex_lock(&data->update_lock);
+ data->thyst = val;
+ i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT_HYST, val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t offset_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d", data->toffset[index] * 500);
+}
+
+static ssize_t offset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(data);
+ long val;
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ /* Accuracy is 1/2 degrees C */
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 500), -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->toffset[index] = val;
+ i2c_smbus_write_byte_data(data->client, LM95234_REG_OFFSET(index), val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t update_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int ret = lm95234_update_device(data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%lu\n",
+ DIV_ROUND_CLOSEST(data->interval * 1000, HZ));
+}
+
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lm95234_data *data = dev_get_drvdata(dev);
+ int ret = lm95234_update_device(data);
+ unsigned long val;
+ u8 regval;
+
+ if (ret)
+ return ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ for (regval = 0; regval < 3; regval++) {
+ if (val <= update_intervals[regval])
+ break;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->interval = msecs_to_jiffies(update_intervals[regval]);
+ i2c_smbus_write_byte_data(data->client, LM95234_REG_CONVRATE, regval);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+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(temp2_fault, alarm, BIT(0) | BIT(1));
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, BIT(2) | BIT(3));
+static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, BIT(4) | BIT(5));
+static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, BIT(6) | BIT(7));
+
+static SENSOR_DEVICE_ATTR_RW(temp2_type, type, BIT(1));
+static SENSOR_DEVICE_ATTR_RW(temp3_type, type, BIT(2));
+static SENSOR_DEVICE_ATTR_RW(temp4_type, type, BIT(3));
+static SENSOR_DEVICE_ATTR_RW(temp5_type, type, BIT(4));
+
+static SENSOR_DEVICE_ATTR_RW(temp1_max, tcrit1, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, tcrit2, 0);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, tcrit2, 1);
+static SENSOR_DEVICE_ATTR_RW(temp4_max, tcrit1, 3);
+static SENSOR_DEVICE_ATTR_RW(temp5_max, tcrit1, 4);
+
+static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, tcrit1_hyst, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_hyst, tcrit2_hyst, 0);
+static SENSOR_DEVICE_ATTR_RO(temp3_max_hyst, tcrit2_hyst, 1);
+static SENSOR_DEVICE_ATTR_RO(temp4_max_hyst, tcrit1_hyst, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_max_hyst, tcrit1_hyst, 4);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, BIT(0 + 8));
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, BIT(1 + 16));
+static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, BIT(2 + 16));
+static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, BIT(3 + 8));
+static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, alarm, BIT(4 + 8));
+
+static SENSOR_DEVICE_ATTR_RW(temp2_crit, tcrit1, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_crit, tcrit1, 2);
+
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, tcrit1_hyst, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, tcrit1_hyst, 2);
+
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, BIT(1 + 8));
+static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, BIT(2 + 8));
+
+static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 0);
+static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 1);
+static SENSOR_DEVICE_ATTR_RW(temp4_offset, offset, 2);
+static SENSOR_DEVICE_ATTR_RW(temp5_offset, offset, 3);
+
+static DEVICE_ATTR_RW(update_interval);
+
+static struct attribute *lm95234_common_attrs[] = {
+ &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_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.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_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &dev_attr_update_interval.attr,
+ NULL
+};
+
+static const struct attribute_group lm95234_common_group = {
+ .attrs = lm95234_common_attrs,
+};
+
+static struct attribute *lm95234_attrs[] = {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_type.dev_attr.attr,
+ &sensor_dev_attr_temp5_type.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_offset.dev_attr.attr,
+ &sensor_dev_attr_temp5_offset.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm95234_group = {
+ .attrs = lm95234_attrs,
+};
+
+static int lm95234_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ u8 config_mask, model_mask;
+ int mfg_id, chip_id, val;
+ const char *name;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ mfg_id = i2c_smbus_read_byte_data(client, LM95234_REG_MAN_ID);
+ if (mfg_id != NATSEMI_MAN_ID)
+ return -ENODEV;
+
+ chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID);
+ switch (chip_id) {
+ case LM95233_CHIP_ID:
+ if (address != 0x18 && address != 0x2a && address != 0x2b)
+ return -ENODEV;
+ config_mask = 0xbf;
+ model_mask = 0xf9;
+ name = "lm95233";
+ break;
+ case LM95234_CHIP_ID:
+ if (address != 0x18 && address != 0x4d && address != 0x4e)
+ return -ENODEV;
+ config_mask = 0xbc;
+ model_mask = 0xe1;
+ name = "lm95234";
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS);
+ if (val & 0x30)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
+ if (val & config_mask)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
+ if (val & 0xfc)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+ if (val & model_mask)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
+ if (val & model_mask)
+ return -ENODEV;
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+ return 0;
+}
+
+static int lm95234_init_client(struct i2c_client *client)
+{
+ int val, model;
+
+ /* start conversion if necessary */
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
+ if (val < 0)
+ return val;
+ if (val & 0x40)
+ i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG,
+ val & ~0x40);
+
+ /* If diode type status reports an error, try to fix it */
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
+ if (val < 0)
+ return val;
+ model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+ if (model < 0)
+ return model;
+ if (model & val) {
+ dev_notice(&client->dev,
+ "Fixing remote diode type misconfiguration (0x%x)\n",
+ val);
+ i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL,
+ model & ~val);
+ }
+ return 0;
+}
+
+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;
+ struct device *hwmon_dev;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM95234 chip */
+ err = lm95234_init_client(client);
+ if (err < 0)
+ return err;
+
+ data->groups[0] = &lm95234_common_group;
+ 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,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95234_id[] = {
+ { "lm95233", lm95233 },
+ { "lm95234", lm95234 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm95234_id);
+
+static struct i2c_driver lm95234_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe_new = lm95234_probe,
+ .id_table = lm95234_id,
+ .detect = lm95234_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm95234_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LM95233/LM95234 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
new file mode 100644
index 000000000..f1ed777a8
--- /dev/null
+++ b/drivers/hwmon/lm95241.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com>
+ *
+ * The LM95241 is a sensor chip made by National Semiconductors.
+ * It reports up to three temperatures (its own plus up to two external ones).
+ * Complete datasheet can be obtained from National's website at:
+ * http://www.national.com/ds.cgi/LM/LM95241.pdf
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#define DEVNAME "lm95241"
+
+static const unsigned short normal_i2c[] = {
+ 0x19, 0x2a, 0x2b, I2C_CLIENT_END };
+
+/* LM95241 registers */
+#define LM95241_REG_R_MAN_ID 0xFE
+#define LM95241_REG_R_CHIP_ID 0xFF
+#define LM95241_REG_R_STATUS 0x02
+#define LM95241_REG_RW_CONFIG 0x03
+#define LM95241_REG_RW_REM_FILTER 0x06
+#define LM95241_REG_RW_TRUTHERM 0x07
+#define LM95241_REG_W_ONE_SHOT 0x0F
+#define LM95241_REG_R_LOCAL_TEMPH 0x10
+#define LM95241_REG_R_REMOTE1_TEMPH 0x11
+#define LM95241_REG_R_REMOTE2_TEMPH 0x12
+#define LM95241_REG_R_LOCAL_TEMPL 0x20
+#define LM95241_REG_R_REMOTE1_TEMPL 0x21
+#define LM95241_REG_R_REMOTE2_TEMPL 0x22
+#define LM95241_REG_RW_REMOTE_MODEL 0x30
+
+/* LM95241 specific bitfields */
+#define CFG_STOP BIT(6)
+#define CFG_CR0076 0x00
+#define CFG_CR0182 BIT(4)
+#define CFG_CR1000 BIT(5)
+#define CFG_CR2700 (BIT(4) | BIT(5))
+#define CFG_CRMASK (BIT(4) | BIT(5))
+#define R1MS_MASK BIT(0)
+#define R2MS_MASK BIT(2)
+#define R1DF_MASK BIT(1)
+#define R2DF_MASK BIT(2)
+#define R1FE_MASK BIT(0)
+#define R2FE_MASK BIT(2)
+#define R1DM BIT(0)
+#define R2DM BIT(1)
+#define TT1_SHIFT 0
+#define TT2_SHIFT 4
+#define TT_OFF 0
+#define TT_ON 1
+#define TT_MASK 7
+#define NATSEMI_MAN_ID 0x01
+#define LM95231_CHIP_ID 0xA1
+#define LM95241_CHIP_ID 0xA4
+
+static const u8 lm95241_reg_address[] = {
+ LM95241_REG_R_LOCAL_TEMPH,
+ LM95241_REG_R_LOCAL_TEMPL,
+ LM95241_REG_R_REMOTE1_TEMPH,
+ LM95241_REG_R_REMOTE1_TEMPL,
+ LM95241_REG_R_REMOTE2_TEMPH,
+ LM95241_REG_R_REMOTE2_TEMPL
+};
+
+/* Client data (each client gets its own) */
+struct lm95241_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ unsigned long last_updated; /* in jiffies */
+ unsigned long interval; /* in milli-seconds */
+ bool valid; /* false until following fields are valid */
+ /* registers values */
+ u8 temp[ARRAY_SIZE(lm95241_reg_address)];
+ u8 status, config, model, trutherm;
+};
+
+/* Conversions */
+static int temp_from_reg_signed(u8 val_h, u8 val_l)
+{
+ s16 val_hl = (val_h << 8) | val_l;
+ return val_hl * 1000 / 256;
+}
+
+static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
+{
+ u16 val_hl = (val_h << 8) | val_l;
+ return val_hl * 1000 / 256;
+}
+
+static struct lm95241_data *lm95241_update_device(struct device *dev)
+{
+ struct lm95241_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated
+ + msecs_to_jiffies(data->interval)) ||
+ !data->valid) {
+ int i;
+
+ dev_dbg(dev, "Updating lm95241 data.\n");
+ for (i = 0; i < ARRAY_SIZE(lm95241_reg_address); i++)
+ data->temp[i]
+ = i2c_smbus_read_byte_data(client,
+ lm95241_reg_address[i]);
+
+ data->status = i2c_smbus_read_byte_data(client,
+ LM95241_REG_R_STATUS);
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static int lm95241_read_chip(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct lm95241_data *data = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->interval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95241_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct lm95241_data *data = lm95241_update_device(dev);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ if (!channel || (data->config & BIT(channel - 1)))
+ *val = temp_from_reg_signed(data->temp[channel * 2],
+ data->temp[channel * 2 + 1]);
+ else
+ *val = temp_from_reg_unsigned(data->temp[channel * 2],
+ data->temp[channel * 2 + 1]);
+ return 0;
+ case hwmon_temp_min:
+ if (channel == 1)
+ *val = (data->config & R1DF_MASK) ? -128000 : 0;
+ else
+ *val = (data->config & R2DF_MASK) ? -128000 : 0;
+ return 0;
+ case hwmon_temp_max:
+ if (channel == 1)
+ *val = (data->config & R1DF_MASK) ? 127875 : 255875;
+ else
+ *val = (data->config & R2DF_MASK) ? 127875 : 255875;
+ return 0;
+ case hwmon_temp_type:
+ if (channel == 1)
+ *val = (data->model & R1MS_MASK) ? 1 : 2;
+ else
+ *val = (data->model & R2MS_MASK) ? 1 : 2;
+ return 0;
+ case hwmon_temp_fault:
+ if (channel == 1)
+ *val = !!(data->status & R1DM);
+ else
+ *val = !!(data->status & R2DM);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95241_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm95241_read_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95241_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95241_write_chip(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct lm95241_data *data = dev_get_drvdata(dev);
+ int convrate;
+ u8 config;
+ int ret;
+
+ mutex_lock(&data->update_lock);
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ config = data->config & ~CFG_CRMASK;
+ if (val < 130) {
+ convrate = 76;
+ config |= CFG_CR0076;
+ } else if (val < 590) {
+ convrate = 182;
+ config |= CFG_CR0182;
+ } else if (val < 1850) {
+ convrate = 1000;
+ config |= CFG_CR1000;
+ } else {
+ convrate = 2700;
+ config |= CFG_CR2700;
+ }
+ data->interval = convrate;
+ data->config = config;
+ ret = i2c_smbus_write_byte_data(data->client,
+ LM95241_REG_RW_CONFIG, config);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int lm95241_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct lm95241_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ mutex_lock(&data->update_lock);
+
+ switch (attr) {
+ case hwmon_temp_min:
+ if (channel == 1) {
+ if (val < 0)
+ data->config |= R1DF_MASK;
+ else
+ data->config &= ~R1DF_MASK;
+ } else {
+ if (val < 0)
+ data->config |= R2DF_MASK;
+ else
+ data->config &= ~R2DF_MASK;
+ }
+ data->valid = false;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
+ data->config);
+ break;
+ case hwmon_temp_max:
+ if (channel == 1) {
+ if (val <= 127875)
+ data->config |= R1DF_MASK;
+ else
+ data->config &= ~R1DF_MASK;
+ } else {
+ if (val <= 127875)
+ data->config |= R2DF_MASK;
+ else
+ data->config &= ~R2DF_MASK;
+ }
+ data->valid = false;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
+ data->config);
+ break;
+ case hwmon_temp_type:
+ if (val != 1 && val != 2) {
+ ret = -EINVAL;
+ break;
+ }
+ if (channel == 1) {
+ data->trutherm &= ~(TT_MASK << TT1_SHIFT);
+ if (val == 1) {
+ data->model |= R1MS_MASK;
+ data->trutherm |= (TT_ON << TT1_SHIFT);
+ } else {
+ data->model &= ~R1MS_MASK;
+ data->trutherm |= (TT_OFF << TT1_SHIFT);
+ }
+ } else {
+ data->trutherm &= ~(TT_MASK << TT2_SHIFT);
+ if (val == 1) {
+ data->model |= R2MS_MASK;
+ data->trutherm |= (TT_ON << TT2_SHIFT);
+ } else {
+ data->model &= ~R2MS_MASK;
+ data->trutherm |= (TT_OFF << TT2_SHIFT);
+ }
+ }
+ ret = i2c_smbus_write_byte_data(client,
+ LM95241_REG_RW_REMOTE_MODEL,
+ data->model);
+ if (ret < 0)
+ break;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
+ data->trutherm);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static int lm95241_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm95241_write_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95241_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t lm95241_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:
+ return 0644;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ case hwmon_temp_fault:
+ return 0444;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_type:
+ return 0644;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm95241_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ const char *name;
+ int mfg_id, chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID);
+ if (mfg_id != NATSEMI_MAN_ID)
+ return -ENODEV;
+
+ chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID);
+ switch (chip_id) {
+ case LM95231_CHIP_ID:
+ name = "lm95231";
+ break;
+ case LM95241_CHIP_ID:
+ name = "lm95241";
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ /* Fill the i2c board info */
+ strscpy(info->type, name, I2C_NAME_SIZE);
+ return 0;
+}
+
+static void lm95241_init_client(struct i2c_client *client,
+ struct lm95241_data *data)
+{
+ data->interval = 1000;
+ data->config = CFG_CR1000;
+ data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT);
+
+ i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config);
+ i2c_smbus_write_byte_data(client, LM95241_REG_RW_REM_FILTER,
+ R1FE_MASK | R2FE_MASK);
+ i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
+ data->trutherm);
+ i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL,
+ data->model);
+}
+
+static const struct hwmon_channel_info *lm95241_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
+ HWMON_T_TYPE | HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
+ HWMON_T_TYPE | HWMON_T_FAULT),
+ NULL
+};
+
+static const struct hwmon_ops lm95241_hwmon_ops = {
+ .is_visible = lm95241_is_visible,
+ .read = lm95241_read,
+ .write = lm95241_write,
+};
+
+static const struct hwmon_chip_info lm95241_chip_info = {
+ .ops = &lm95241_hwmon_ops,
+ .info = lm95241_info,
+};
+
+static int lm95241_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct lm95241_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct lm95241_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM95241 chip */
+ lm95241_init_client(client, data);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &lm95241_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95241_id[] = {
+ { "lm95231", 0 },
+ { "lm95241", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm95241_id);
+
+static struct i2c_driver lm95241_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DEVNAME,
+ },
+ .probe_new = lm95241_probe,
+ .id_table = lm95241_id,
+ .detect = lm95241_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm95241_driver);
+
+MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>");
+MODULE_DESCRIPTION("LM95231/LM95241 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
new file mode 100644
index 000000000..c433f0af2
--- /dev/null
+++ b/drivers/hwmon/lm95245.c
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com>
+ *
+ * The LM95245 is a sensor chip made by TI / National Semiconductor.
+ * It reports up to two temperatures (its own plus an external one).
+ *
+ * This driver is based on lm95241.c
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
+
+/* LM95245 registers */
+/* general registers */
+#define LM95245_REG_RW_CONFIG1 0x03
+#define LM95245_REG_RW_CONVERS_RATE 0x04
+#define LM95245_REG_W_ONE_SHOT 0x0F
+
+/* diode configuration */
+#define LM95245_REG_RW_CONFIG2 0xBF
+#define LM95245_REG_RW_REMOTE_OFFH 0x11
+#define LM95245_REG_RW_REMOTE_OFFL 0x12
+
+/* status registers */
+#define LM95245_REG_R_STATUS1 0x02
+#define LM95245_REG_R_STATUS2 0x33
+
+/* limit registers */
+#define LM95245_REG_RW_REMOTE_OS_LIMIT 0x07
+#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT 0x20
+#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT 0x19
+#define LM95245_REG_RW_COMMON_HYSTERESIS 0x21
+
+/* temperature signed */
+#define LM95245_REG_R_LOCAL_TEMPH_S 0x00
+#define LM95245_REG_R_LOCAL_TEMPL_S 0x30
+#define LM95245_REG_R_REMOTE_TEMPH_S 0x01
+#define LM95245_REG_R_REMOTE_TEMPL_S 0x10
+/* temperature unsigned */
+#define LM95245_REG_R_REMOTE_TEMPH_U 0x31
+#define LM95245_REG_R_REMOTE_TEMPL_U 0x32
+
+/* id registers */
+#define LM95245_REG_R_MAN_ID 0xFE
+#define LM95245_REG_R_CHIP_ID 0xFF
+
+/* LM95245 specific bitfields */
+#define CFG_STOP 0x40
+#define CFG_REMOTE_TCRIT_MASK 0x10
+#define CFG_REMOTE_OS_MASK 0x08
+#define CFG_LOCAL_TCRIT_MASK 0x04
+#define CFG_LOCAL_OS_MASK 0x02
+
+#define CFG2_OS_A0 0x40
+#define CFG2_DIODE_FAULT_OS 0x20
+#define CFG2_DIODE_FAULT_TCRIT 0x10
+#define CFG2_REMOTE_TT 0x08
+#define CFG2_REMOTE_FILTER_DIS 0x00
+#define CFG2_REMOTE_FILTER_EN 0x06
+
+/* conversation rate in ms */
+#define RATE_CR0063 0x00
+#define RATE_CR0364 0x01
+#define RATE_CR1000 0x02
+#define RATE_CR2500 0x03
+
+#define STATUS1_ROS 0x10
+#define STATUS1_DIODE_FAULT 0x04
+#define STATUS1_RTCRIT 0x02
+#define STATUS1_LOC 0x01
+
+#define MANUFACTURER_ID 0x01
+#define LM95235_REVISION 0xB1
+#define LM95245_REVISION 0xB3
+
+/* Client data (each client gets its own) */
+struct lm95245_data {
+ struct regmap *regmap;
+ struct mutex update_lock;
+ int interval; /* in msecs */
+};
+
+/* Conversions */
+static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
+{
+ return val_h * 1000 + val_l * 1000 / 256;
+}
+
+static int temp_from_reg_signed(u8 val_h, u8 val_l)
+{
+ if (val_h & 0x80)
+ return (val_h - 0x100) * 1000;
+ return temp_from_reg_unsigned(val_h, val_l);
+}
+
+static int lm95245_read_conversion_rate(struct lm95245_data *data)
+{
+ unsigned int rate;
+ int ret;
+
+ ret = regmap_read(data->regmap, LM95245_REG_RW_CONVERS_RATE, &rate);
+ if (ret < 0)
+ return ret;
+
+ switch (rate) {
+ case RATE_CR0063:
+ data->interval = 63;
+ break;
+ case RATE_CR0364:
+ data->interval = 364;
+ break;
+ case RATE_CR1000:
+ data->interval = 1000;
+ break;
+ case RATE_CR2500:
+ default:
+ data->interval = 2500;
+ break;
+ }
+ return 0;
+}
+
+static int lm95245_set_conversion_rate(struct lm95245_data *data, long interval)
+{
+ int ret, rate;
+
+ if (interval <= 63) {
+ interval = 63;
+ rate = RATE_CR0063;
+ } else if (interval <= 364) {
+ interval = 364;
+ rate = RATE_CR0364;
+ } else if (interval <= 1000) {
+ interval = 1000;
+ rate = RATE_CR1000;
+ } else {
+ interval = 2500;
+ rate = RATE_CR2500;
+ }
+
+ ret = regmap_write(data->regmap, LM95245_REG_RW_CONVERS_RATE, rate);
+ if (ret < 0)
+ return ret;
+
+ data->interval = interval;
+ return 0;
+}
+
+static int lm95245_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct lm95245_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ int ret, regl, regh, regvall, regvalh;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S :
+ LM95245_REG_R_LOCAL_TEMPL_S;
+ regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S :
+ LM95245_REG_R_LOCAL_TEMPH_S;
+ ret = regmap_read(regmap, regl, &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ /*
+ * Local temp is always signed.
+ * Remote temp has both signed and unsigned data.
+ * Use signed calculation for remote if signed bit is set
+ * or if reported temperature is below signed limit.
+ */
+ if (!channel || (regvalh & 0x80) || regvalh < 0x7f) {
+ *val = temp_from_reg_signed(regvalh, regvall);
+ return 0;
+ }
+ ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = temp_from_reg_unsigned(regvalh, regvall);
+ return 0;
+ case hwmon_temp_max:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = regvalh * 1000;
+ return 0;
+ case hwmon_temp_crit:
+ regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = regvalh * 1000;
+ return 0;
+ case hwmon_temp_max_hyst:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh - regvall) * 1000;
+ return 0;
+ case hwmon_temp_crit_hyst:
+ regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh - regvall) * 1000;
+ return 0;
+ case hwmon_temp_type:
+ ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2;
+ return 0;
+ case hwmon_temp_offset:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = temp_from_reg_signed(regvalh, regvall);
+ return 0;
+ case hwmon_temp_max_alarm:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & STATUS1_ROS);
+ return 0;
+ case hwmon_temp_crit_alarm:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC));
+ return 0;
+ case hwmon_temp_fault:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & STATUS1_DIODE_FAULT);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct lm95245_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int ret, reg;
+
+ switch (attr) {
+ case hwmon_temp_max:
+ val = clamp_val(val / 1000, 0, 255);
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, val);
+ return ret;
+ case hwmon_temp_crit:
+ reg = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ val = clamp_val(val / 1000, 0, channel ? 255 : 127);
+ ret = regmap_write(regmap, reg, val);
+ return ret;
+ case hwmon_temp_crit_hyst:
+ mutex_lock(&data->update_lock);
+ ret = regmap_read(regmap, LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
+ &regval);
+ if (ret < 0) {
+ mutex_unlock(&data->update_lock);
+ return ret;
+ }
+ /* Clamp to reasonable range to prevent overflow */
+ val = clamp_val(val, -1000000, 1000000);
+ val = regval - val / 1000;
+ val = clamp_val(val, 0, 31);
+ ret = regmap_write(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ val);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ case hwmon_temp_offset:
+ val = clamp_val(val, -128000, 127875);
+ val = val * 256 / 1000;
+ mutex_lock(&data->update_lock);
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL,
+ val & 0xe0);
+ if (ret < 0) {
+ mutex_unlock(&data->update_lock);
+ return ret;
+ }
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH,
+ (val >> 8) & 0xff);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ case hwmon_temp_type:
+ if (val != 1 && val != 2)
+ return -EINVAL;
+ ret = regmap_update_bits(regmap, LM95245_REG_RW_CONFIG2,
+ CFG2_REMOTE_TT,
+ val == 1 ? CFG2_REMOTE_TT : 0);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95245_read_chip(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct lm95245_data *data = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->interval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95245_write_chip(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct lm95245_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ mutex_lock(&data->update_lock);
+ ret = lm95245_set_conversion_rate(data, val);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95245_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm95245_read_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95245_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm95245_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm95245_write_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95245_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t lm95245_temp_is_visible(const void *data, u32 attr, int channel)
+{
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_fault:
+ return 0444;
+ case hwmon_temp_type:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_offset:
+ return 0644;
+ case hwmon_temp_crit_hyst:
+ return (channel == 0) ? 0644 : 0444;
+ default:
+ return 0;
+ }
+}
+
+static umode_t lm95245_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:
+ return 0644;
+ default:
+ return 0;
+ }
+ case hwmon_temp:
+ return lm95245_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm95245_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ int address = new_client->addr;
+ const char *name;
+ int rev, id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ id = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID);
+ if (id != MANUFACTURER_ID)
+ return -ENODEV;
+
+ rev = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID);
+ switch (rev) {
+ case LM95235_REVISION:
+ if (address != 0x18 && address != 0x29 && address != 0x4c)
+ return -ENODEV;
+ name = "lm95235";
+ break;
+ case LM95245_REVISION:
+ name = "lm95245";
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ strscpy(info->type, name, I2C_NAME_SIZE);
+ return 0;
+}
+
+static int lm95245_init_client(struct lm95245_data *data)
+{
+ int ret;
+
+ ret = lm95245_read_conversion_rate(data);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(data->regmap, LM95245_REG_RW_CONFIG1,
+ CFG_STOP, 0);
+}
+
+static bool lm95245_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LM95245_REG_RW_CONFIG1:
+ case LM95245_REG_RW_CONVERS_RATE:
+ case LM95245_REG_W_ONE_SHOT:
+ case LM95245_REG_RW_CONFIG2:
+ case LM95245_REG_RW_REMOTE_OFFH:
+ case LM95245_REG_RW_REMOTE_OFFL:
+ case LM95245_REG_RW_REMOTE_OS_LIMIT:
+ case LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT:
+ case LM95245_REG_RW_REMOTE_TCRIT_LIMIT:
+ case LM95245_REG_RW_COMMON_HYSTERESIS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool lm95245_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LM95245_REG_R_STATUS1:
+ case LM95245_REG_R_STATUS2:
+ case LM95245_REG_R_LOCAL_TEMPH_S:
+ case LM95245_REG_R_LOCAL_TEMPL_S:
+ case LM95245_REG_R_REMOTE_TEMPH_S:
+ case LM95245_REG_R_REMOTE_TEMPL_S:
+ case LM95245_REG_R_REMOTE_TEMPH_U:
+ case LM95245_REG_R_REMOTE_TEMPL_U:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config lm95245_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = lm95245_is_writeable_reg,
+ .volatile_reg = lm95245_is_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct hwmon_channel_info *lm95245_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_HYST |
+ HWMON_T_CRIT_ALARM,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_FAULT |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM |
+ HWMON_T_TYPE | HWMON_T_OFFSET),
+ NULL
+};
+
+static const struct hwmon_ops lm95245_hwmon_ops = {
+ .is_visible = lm95245_is_visible,
+ .read = lm95245_read,
+ .write = lm95245_write,
+};
+
+static const struct hwmon_chip_info lm95245_chip_info = {
+ .ops = &lm95245_hwmon_ops,
+ .info = lm95245_info,
+};
+
+static int lm95245_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct lm95245_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &lm95245_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM95245 chip */
+ ret = lm95245_init_client(data);
+ if (ret < 0)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &lm95245_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95245_id[] = {
+ { "lm95235", 0 },
+ { "lm95245", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm95245_id);
+
+static const struct of_device_id __maybe_unused lm95245_of_match[] = {
+ { .compatible = "national,lm95235" },
+ { .compatible = "national,lm95245" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lm95245_of_match);
+
+static struct i2c_driver lm95245_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm95245",
+ .of_match_table = of_match_ptr(lm95245_of_match),
+ },
+ .probe_new = lm95245_probe,
+ .id_table = lm95245_id,
+ .detect = lm95245_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm95245_driver);
+
+MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
+MODULE_DESCRIPTION("LM95235/LM95245 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lochnagar-hwmon.c b/drivers/hwmon/lochnagar-hwmon.c
new file mode 100644
index 000000000..8b19adf2e
--- /dev/null
+++ b/drivers/hwmon/lochnagar-hwmon.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lochnagar hardware monitoring features
+ *
+ * Copyright (c) 2016-2019 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/math64.h>
+#include <linux/mfd/lochnagar.h>
+#include <linux/mfd/lochnagar2_regs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define LN2_MAX_NSAMPLE 1023
+#define LN2_SAMPLE_US 1670
+
+#define LN2_CURR_UNITS 1000
+#define LN2_VOLT_UNITS 1000
+#define LN2_TEMP_UNITS 1000
+#define LN2_PWR_UNITS 1000000
+
+static const char * const lochnagar_chan_names[] = {
+ "DBVDD1",
+ "1V8 DSP",
+ "1V8 CDC",
+ "VDDCORE DSP",
+ "AVDD 1V8",
+ "SYSVDD",
+ "VDDCORE CDC",
+ "MICVDD",
+};
+
+struct lochnagar_hwmon {
+ struct regmap *regmap;
+
+ long power_nsamples[ARRAY_SIZE(lochnagar_chan_names)];
+
+ /* Lock to ensure only a single sensor is read at a time */
+ struct mutex sensor_lock;
+};
+
+enum lochnagar_measure_mode {
+ LN2_CURR = 0,
+ LN2_VOLT,
+ LN2_TEMP,
+};
+
+/**
+ * float_to_long - Convert ieee754 reading from hardware to an integer
+ *
+ * @data: Value read from the hardware
+ * @precision: Units to multiply up to eg. 1000 = milli, 1000000 = micro
+ *
+ * Return: Converted integer reading
+ *
+ * Depending on the measurement type the hardware returns an ieee754
+ * floating point value in either volts, amps or celsius. This function
+ * will convert that into an integer in a smaller unit such as micro-amps
+ * or milli-celsius. The hardware does not return NaN, so consideration of
+ * that is not required.
+ */
+static long float_to_long(u32 data, u32 precision)
+{
+ u64 man = data & 0x007FFFFF;
+ int exp = ((data & 0x7F800000) >> 23) - 127 - 23;
+ bool negative = data & 0x80000000;
+ long result;
+
+ man = (man + (1 << 23)) * precision;
+
+ if (fls64(man) + exp > (int)sizeof(long) * 8 - 1)
+ result = LONG_MAX;
+ else if (exp < 0)
+ result = (man + (1ull << (-exp - 1))) >> -exp;
+ else
+ result = man << exp;
+
+ return negative ? -result : result;
+}
+
+static int do_measurement(struct regmap *regmap, int chan,
+ enum lochnagar_measure_mode mode, int nsamples)
+{
+ unsigned int val;
+ int ret;
+
+ chan = 1 << (chan + LOCHNAGAR2_IMON_MEASURED_CHANNELS_SHIFT);
+
+ ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL1,
+ LOCHNAGAR2_IMON_ENA_MASK | chan | mode);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL2, nsamples);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3,
+ LOCHNAGAR2_IMON_CONFIGURE_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val,
+ val & LOCHNAGAR2_IMON_DONE_MASK,
+ 1000, 10000);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3,
+ LOCHNAGAR2_IMON_MEASURE_MASK);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Actual measurement time is ~1.67mS per sample, approximate this
+ * with a 1.5mS per sample msleep and then poll for success up to
+ * ~0.17mS * 1023 (LN2_MAX_NSAMPLES). Normally for smaller values
+ * of nsamples the poll will complete on the first loop due to
+ * other latency in the system.
+ */
+ msleep((nsamples * 3) / 2);
+
+ ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL3, val,
+ val & LOCHNAGAR2_IMON_DONE_MASK,
+ 5000, 200000);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL3, 0);
+}
+
+static int request_data(struct regmap *regmap, int chan, u32 *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4,
+ LOCHNAGAR2_IMON_DATA_REQ_MASK |
+ chan << LOCHNAGAR2_IMON_CH_SEL_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap, LOCHNAGAR2_IMON_CTRL4, val,
+ val & LOCHNAGAR2_IMON_DATA_RDY_MASK,
+ 1000, 10000);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA1, &val);
+ if (ret < 0)
+ return ret;
+
+ *data = val << 16;
+
+ ret = regmap_read(regmap, LOCHNAGAR2_IMON_DATA2, &val);
+ if (ret < 0)
+ return ret;
+
+ *data |= val;
+
+ return regmap_write(regmap, LOCHNAGAR2_IMON_CTRL4, 0);
+}
+
+static int read_sensor(struct device *dev, int chan,
+ enum lochnagar_measure_mode mode, int nsamples,
+ unsigned int precision, long *val)
+{
+ struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
+ struct regmap *regmap = priv->regmap;
+ u32 data;
+ int ret;
+
+ mutex_lock(&priv->sensor_lock);
+
+ ret = do_measurement(regmap, chan, mode, nsamples);
+ if (ret < 0) {
+ dev_err(dev, "Failed to perform measurement: %d\n", ret);
+ goto error;
+ }
+
+ ret = request_data(regmap, chan, &data);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read measurement: %d\n", ret);
+ goto error;
+ }
+
+ *val = float_to_long(data, precision);
+
+error:
+ mutex_unlock(&priv->sensor_lock);
+
+ return ret;
+}
+
+static int read_power(struct device *dev, int chan, long *val)
+{
+ struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
+ int nsamples = priv->power_nsamples[chan];
+ u64 power;
+ int ret;
+
+ if (!strcmp("SYSVDD", lochnagar_chan_names[chan])) {
+ power = 5 * LN2_PWR_UNITS;
+ } else {
+ ret = read_sensor(dev, chan, LN2_VOLT, 1, LN2_PWR_UNITS, val);
+ if (ret < 0)
+ return ret;
+
+ power = abs(*val);
+ }
+
+ ret = read_sensor(dev, chan, LN2_CURR, nsamples, LN2_PWR_UNITS, val);
+ if (ret < 0)
+ return ret;
+
+ power *= abs(*val);
+ power = DIV_ROUND_CLOSEST_ULL(power, LN2_PWR_UNITS);
+
+ if (power > LONG_MAX)
+ *val = LONG_MAX;
+ else
+ *val = power;
+
+ return 0;
+}
+
+static umode_t lochnagar_is_visible(const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr, int chan)
+{
+ switch (type) {
+ case hwmon_in:
+ if (!strcmp("SYSVDD", lochnagar_chan_names[chan]))
+ return 0;
+ break;
+ case hwmon_power:
+ if (attr == hwmon_power_average_interval)
+ return 0644;
+ break;
+ default:
+ break;
+ }
+
+ return 0444;
+}
+
+static int lochnagar_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int chan, long *val)
+{
+ struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
+ int interval;
+
+ switch (type) {
+ case hwmon_in:
+ return read_sensor(dev, chan, LN2_VOLT, 1, LN2_VOLT_UNITS, val);
+ case hwmon_curr:
+ return read_sensor(dev, chan, LN2_CURR, 1, LN2_CURR_UNITS, val);
+ case hwmon_temp:
+ return read_sensor(dev, chan, LN2_TEMP, 1, LN2_TEMP_UNITS, val);
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_average:
+ return read_power(dev, chan, val);
+ case hwmon_power_average_interval:
+ interval = priv->power_nsamples[chan] * LN2_SAMPLE_US;
+ *val = DIV_ROUND_CLOSEST(interval, 1000);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lochnagar_read_string(struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ int chan, const char **str)
+{
+ switch (type) {
+ case hwmon_in:
+ case hwmon_curr:
+ case hwmon_power:
+ *str = lochnagar_chan_names[chan];
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lochnagar_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int chan, long val)
+{
+ struct lochnagar_hwmon *priv = dev_get_drvdata(dev);
+
+ if (type != hwmon_power || attr != hwmon_power_average_interval)
+ return -EOPNOTSUPP;
+
+ val = clamp_t(long, val, 1, (LN2_MAX_NSAMPLE * LN2_SAMPLE_US) / 1000);
+ val = DIV_ROUND_CLOSEST(val * 1000, LN2_SAMPLE_US);
+
+ priv->power_nsamples[chan] = val;
+
+ return 0;
+}
+
+static const struct hwmon_ops lochnagar_ops = {
+ .is_visible = lochnagar_is_visible,
+ .read = lochnagar_read,
+ .read_string = lochnagar_read_string,
+ .write = lochnagar_write,
+};
+
+static const struct hwmon_channel_info *lochnagar_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_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),
+ HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL,
+ HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL,
+ HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL,
+ HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL,
+ HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL,
+ HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL,
+ HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL,
+ HWMON_P_AVERAGE | HWMON_P_AVERAGE_INTERVAL |
+ HWMON_P_LABEL),
+ NULL
+};
+
+static const struct hwmon_chip_info lochnagar_chip_info = {
+ .ops = &lochnagar_ops,
+ .info = lochnagar_info,
+};
+
+static const struct of_device_id lochnagar_of_match[] = {
+ { .compatible = "cirrus,lochnagar2-hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lochnagar_of_match);
+
+static int lochnagar_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hwmon_dev;
+ struct lochnagar_hwmon *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->sensor_lock);
+
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!priv->regmap) {
+ dev_err(dev, "No register map found\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->power_nsamples); i++)
+ priv->power_nsamples[i] = 96;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "Lochnagar", priv,
+ &lochnagar_chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver lochnagar_hwmon_driver = {
+ .driver = {
+ .name = "lochnagar-hwmon",
+ .of_match_table = lochnagar_of_match,
+ },
+ .probe = lochnagar_hwmon_probe,
+};
+module_platform_driver(lochnagar_hwmon_driver);
+
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_DESCRIPTION("Lochnagar hardware monitoring features");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c
new file mode 100644
index 000000000..c06ab7317
--- /dev/null
+++ b/drivers/hwmon/ltc2945.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Linear Technology LTC2945 I2C Power Monitor
+ *
+ * Copyright (c) 2014 Guenter Roeck
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/regmap.h>
+
+/* chip registers */
+#define LTC2945_CONTROL 0x00
+#define LTC2945_ALERT 0x01
+#define LTC2945_STATUS 0x02
+#define LTC2945_FAULT 0x03
+#define LTC2945_POWER_H 0x05
+#define LTC2945_MAX_POWER_H 0x08
+#define LTC2945_MIN_POWER_H 0x0b
+#define LTC2945_MAX_POWER_THRES_H 0x0e
+#define LTC2945_MIN_POWER_THRES_H 0x11
+#define LTC2945_SENSE_H 0x14
+#define LTC2945_MAX_SENSE_H 0x16
+#define LTC2945_MIN_SENSE_H 0x18
+#define LTC2945_MAX_SENSE_THRES_H 0x1a
+#define LTC2945_MIN_SENSE_THRES_H 0x1c
+#define LTC2945_VIN_H 0x1e
+#define LTC2945_MAX_VIN_H 0x20
+#define LTC2945_MIN_VIN_H 0x22
+#define LTC2945_MAX_VIN_THRES_H 0x24
+#define LTC2945_MIN_VIN_THRES_H 0x26
+#define LTC2945_ADIN_H 0x28
+#define LTC2945_MAX_ADIN_H 0x2a
+#define LTC2945_MIN_ADIN_H 0x2c
+#define LTC2945_MAX_ADIN_THRES_H 0x2e
+#define LTC2945_MIN_ADIN_THRES_H 0x30
+#define LTC2945_MIN_ADIN_THRES_L 0x31
+
+/* Fault register bits */
+
+#define FAULT_ADIN_UV (1 << 0)
+#define FAULT_ADIN_OV (1 << 1)
+#define FAULT_VIN_UV (1 << 2)
+#define FAULT_VIN_OV (1 << 3)
+#define FAULT_SENSE_UV (1 << 4)
+#define FAULT_SENSE_OV (1 << 5)
+#define FAULT_POWER_UV (1 << 6)
+#define FAULT_POWER_OV (1 << 7)
+
+/* Control register bits */
+
+#define CONTROL_MULT_SELECT (1 << 0)
+#define CONTROL_TEST_MODE (1 << 4)
+
+static inline bool is_power_reg(u8 reg)
+{
+ return reg < LTC2945_SENSE_H;
+}
+
+/* Return the value from the given register in uW, mV, or mA */
+static long long ltc2945_reg_to_val(struct device *dev, u8 reg)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int control;
+ u8 buf[3];
+ long long val;
+ int ret;
+
+ ret = regmap_bulk_read(regmap, reg, buf,
+ is_power_reg(reg) ? 3 : 2);
+ if (ret < 0)
+ return ret;
+
+ if (is_power_reg(reg)) {
+ /* power */
+ val = (buf[0] << 16) + (buf[1] << 8) + buf[2];
+ } else {
+ /* current, voltage */
+ val = (buf[0] << 4) + (buf[1] >> 4);
+ }
+
+ switch (reg) {
+ case LTC2945_POWER_H:
+ case LTC2945_MAX_POWER_H:
+ case LTC2945_MIN_POWER_H:
+ case LTC2945_MAX_POWER_THRES_H:
+ case LTC2945_MIN_POWER_THRES_H:
+ /*
+ * Convert to uW by assuming current is measured with
+ * an 1mOhm sense resistor, similar to current
+ * measurements.
+ * Control register bit 0 selects if voltage at SENSE+/VDD
+ * or voltage at ADIN is used to measure power.
+ */
+ ret = regmap_read(regmap, LTC2945_CONTROL, &control);
+ if (ret < 0)
+ return ret;
+ if (control & CONTROL_MULT_SELECT) {
+ /* 25 mV * 25 uV = 0.625 uV resolution. */
+ val *= 625LL;
+ } else {
+ /* 0.5 mV * 25 uV = 0.0125 uV resolution. */
+ val = (val * 25LL) >> 1;
+ }
+ break;
+ case LTC2945_VIN_H:
+ case LTC2945_MAX_VIN_H:
+ case LTC2945_MIN_VIN_H:
+ case LTC2945_MAX_VIN_THRES_H:
+ case LTC2945_MIN_VIN_THRES_H:
+ /* 25 mV resolution. Convert to mV. */
+ val *= 25;
+ break;
+ case LTC2945_ADIN_H:
+ case LTC2945_MAX_ADIN_H:
+ case LTC2945_MIN_ADIN_THRES_H:
+ case LTC2945_MAX_ADIN_THRES_H:
+ case LTC2945_MIN_ADIN_H:
+ /* 0.5mV resolution. Convert to mV. */
+ val = val >> 1;
+ break;
+ case LTC2945_SENSE_H:
+ case LTC2945_MAX_SENSE_H:
+ case LTC2945_MIN_SENSE_H:
+ case LTC2945_MAX_SENSE_THRES_H:
+ case LTC2945_MIN_SENSE_THRES_H:
+ /*
+ * 25 uV resolution. Convert to current as measured with
+ * an 1 mOhm sense resistor, in mA. If a different sense
+ * resistor is installed, calculate the actual current by
+ * dividing the reported current by the sense resistor value
+ * in mOhm.
+ */
+ val *= 25;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return val;
+}
+
+static int ltc2945_val_to_reg(struct device *dev, u8 reg,
+ unsigned long val)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int control;
+ int ret;
+
+ switch (reg) {
+ case LTC2945_POWER_H:
+ case LTC2945_MAX_POWER_H:
+ case LTC2945_MIN_POWER_H:
+ case LTC2945_MAX_POWER_THRES_H:
+ case LTC2945_MIN_POWER_THRES_H:
+ /*
+ * Convert to register value by assuming current is measured
+ * with an 1mOhm sense resistor, similar to current
+ * measurements.
+ * Control register bit 0 selects if voltage at SENSE+/VDD
+ * or voltage at ADIN is used to measure power, which in turn
+ * determines register calculations.
+ */
+ ret = regmap_read(regmap, LTC2945_CONTROL, &control);
+ if (ret < 0)
+ return ret;
+ if (control & CONTROL_MULT_SELECT) {
+ /* 25 mV * 25 uV = 0.625 uV resolution. */
+ val = DIV_ROUND_CLOSEST(val, 625);
+ } else {
+ /*
+ * 0.5 mV * 25 uV = 0.0125 uV resolution.
+ * Divide first to avoid overflow;
+ * accept loss of accuracy.
+ */
+ val = DIV_ROUND_CLOSEST(val, 25) * 2;
+ }
+ break;
+ case LTC2945_VIN_H:
+ case LTC2945_MAX_VIN_H:
+ case LTC2945_MIN_VIN_H:
+ case LTC2945_MAX_VIN_THRES_H:
+ case LTC2945_MIN_VIN_THRES_H:
+ /* 25 mV resolution. */
+ val /= 25;
+ break;
+ case LTC2945_ADIN_H:
+ case LTC2945_MAX_ADIN_H:
+ case LTC2945_MIN_ADIN_THRES_H:
+ case LTC2945_MAX_ADIN_THRES_H:
+ case LTC2945_MIN_ADIN_H:
+ /* 0.5mV resolution. */
+ val *= 2;
+ break;
+ case LTC2945_SENSE_H:
+ case LTC2945_MAX_SENSE_H:
+ case LTC2945_MIN_SENSE_H:
+ case LTC2945_MAX_SENSE_THRES_H:
+ case LTC2945_MIN_SENSE_THRES_H:
+ /*
+ * 25 uV resolution. Convert to current as measured with
+ * an 1 mOhm sense resistor, in mA. If a different sense
+ * resistor is installed, calculate the actual current by
+ * dividing the reported current by the sense resistor value
+ * in mOhm.
+ */
+ val = DIV_ROUND_CLOSEST(val, 25);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return val;
+}
+
+static ssize_t ltc2945_value_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ long long value;
+
+ value = ltc2945_reg_to_val(dev, attr->index);
+ if (value < 0)
+ return value;
+ return sysfs_emit(buf, "%lld\n", value);
+}
+
+static ssize_t ltc2945_value_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 regmap *regmap = dev_get_drvdata(dev);
+ u8 reg = attr->index;
+ unsigned long val;
+ u8 regbuf[3];
+ int num_regs;
+ int regval;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ /* convert to register value, then clamp and write result */
+ regval = ltc2945_val_to_reg(dev, reg, val);
+ if (regval < 0)
+ return regval;
+ if (is_power_reg(reg)) {
+ regval = clamp_val(regval, 0, 0xffffff);
+ regbuf[0] = regval >> 16;
+ regbuf[1] = (regval >> 8) & 0xff;
+ regbuf[2] = regval;
+ num_regs = 3;
+ } else {
+ regval = clamp_val(regval, 0, 0xfff) << 4;
+ regbuf[0] = regval >> 8;
+ regbuf[1] = regval & 0xff;
+ num_regs = 2;
+ }
+ ret = regmap_bulk_write(regmap, reg, regbuf, num_regs);
+ return ret < 0 ? ret : count;
+}
+
+static ssize_t ltc2945_history_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 regmap *regmap = dev_get_drvdata(dev);
+ u8 reg = attr->index;
+ int num_regs = is_power_reg(reg) ? 3 : 2;
+ u8 buf_min[3] = { 0xff, 0xff, 0xff };
+ u8 buf_max[3] = { 0, 0, 0 };
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+ if (val != 1)
+ return -EINVAL;
+
+ ret = regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE,
+ CONTROL_TEST_MODE);
+
+ /* Reset minimum */
+ ret = regmap_bulk_write(regmap, reg, buf_min, num_regs);
+ if (ret)
+ return ret;
+
+ switch (reg) {
+ case LTC2945_MIN_POWER_H:
+ reg = LTC2945_MAX_POWER_H;
+ break;
+ case LTC2945_MIN_SENSE_H:
+ reg = LTC2945_MAX_SENSE_H;
+ break;
+ case LTC2945_MIN_VIN_H:
+ reg = LTC2945_MAX_VIN_H;
+ break;
+ case LTC2945_MIN_ADIN_H:
+ reg = LTC2945_MAX_ADIN_H;
+ break;
+ default:
+ WARN_ONCE(1, "Bad register: 0x%x\n", reg);
+ return -EINVAL;
+ }
+ /* Reset maximum */
+ ret = regmap_bulk_write(regmap, reg, buf_max, num_regs);
+
+ /* Try resetting test mode even if there was an error */
+ regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 0);
+
+ return ret ? : count;
+}
+
+static ssize_t ltc2945_bool_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int fault;
+ int ret;
+
+ ret = regmap_read(regmap, LTC2945_FAULT, &fault);
+ if (ret < 0)
+ return ret;
+
+ fault &= attr->index;
+ if (fault) /* Clear reported faults in chip register */
+ regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0);
+
+ return sysfs_emit(buf, "%d\n", !!fault);
+}
+
+/* Input voltages */
+
+static SENSOR_DEVICE_ATTR_RO(in1_input, ltc2945_value, LTC2945_VIN_H);
+static SENSOR_DEVICE_ATTR_RW(in1_min, ltc2945_value, LTC2945_MIN_VIN_THRES_H);
+static SENSOR_DEVICE_ATTR_RW(in1_max, ltc2945_value, LTC2945_MAX_VIN_THRES_H);
+static SENSOR_DEVICE_ATTR_RO(in1_lowest, ltc2945_value, LTC2945_MIN_VIN_H);
+static SENSOR_DEVICE_ATTR_RO(in1_highest, ltc2945_value, LTC2945_MAX_VIN_H);
+static SENSOR_DEVICE_ATTR_WO(in1_reset_history, ltc2945_history,
+ LTC2945_MIN_VIN_H);
+
+static SENSOR_DEVICE_ATTR_RO(in2_input, ltc2945_value, LTC2945_ADIN_H);
+static SENSOR_DEVICE_ATTR_RW(in2_min, ltc2945_value, LTC2945_MIN_ADIN_THRES_H);
+static SENSOR_DEVICE_ATTR_RW(in2_max, ltc2945_value, LTC2945_MAX_ADIN_THRES_H);
+static SENSOR_DEVICE_ATTR_RO(in2_lowest, ltc2945_value, LTC2945_MIN_ADIN_H);
+static SENSOR_DEVICE_ATTR_RO(in2_highest, ltc2945_value, LTC2945_MAX_ADIN_H);
+static SENSOR_DEVICE_ATTR_WO(in2_reset_history, ltc2945_history,
+ LTC2945_MIN_ADIN_H);
+
+/* Voltage alarms */
+
+static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc2945_bool, FAULT_VIN_UV);
+static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc2945_bool, FAULT_VIN_OV);
+static SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc2945_bool, FAULT_ADIN_UV);
+static SENSOR_DEVICE_ATTR_RO(in2_max_alarm, ltc2945_bool, FAULT_ADIN_OV);
+
+/* Currents (via sense resistor) */
+
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc2945_value, LTC2945_SENSE_H);
+static SENSOR_DEVICE_ATTR_RW(curr1_min, ltc2945_value,
+ LTC2945_MIN_SENSE_THRES_H);
+static SENSOR_DEVICE_ATTR_RW(curr1_max, ltc2945_value,
+ LTC2945_MAX_SENSE_THRES_H);
+static SENSOR_DEVICE_ATTR_RO(curr1_lowest, ltc2945_value, LTC2945_MIN_SENSE_H);
+static SENSOR_DEVICE_ATTR_RO(curr1_highest, ltc2945_value,
+ LTC2945_MAX_SENSE_H);
+static SENSOR_DEVICE_ATTR_WO(curr1_reset_history, ltc2945_history,
+ LTC2945_MIN_SENSE_H);
+
+/* Current alarms */
+
+static SENSOR_DEVICE_ATTR_RO(curr1_min_alarm, ltc2945_bool, FAULT_SENSE_UV);
+static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc2945_bool, FAULT_SENSE_OV);
+
+/* Power */
+
+static SENSOR_DEVICE_ATTR_RO(power1_input, ltc2945_value, LTC2945_POWER_H);
+static SENSOR_DEVICE_ATTR_RW(power1_min, ltc2945_value,
+ LTC2945_MIN_POWER_THRES_H);
+static SENSOR_DEVICE_ATTR_RW(power1_max, ltc2945_value,
+ LTC2945_MAX_POWER_THRES_H);
+static SENSOR_DEVICE_ATTR_RO(power1_input_lowest, ltc2945_value,
+ LTC2945_MIN_POWER_H);
+static SENSOR_DEVICE_ATTR_RO(power1_input_highest, ltc2945_value,
+ LTC2945_MAX_POWER_H);
+static SENSOR_DEVICE_ATTR_WO(power1_reset_history, ltc2945_history,
+ LTC2945_MIN_POWER_H);
+
+/* Power alarms */
+
+static SENSOR_DEVICE_ATTR_RO(power1_min_alarm, ltc2945_bool, FAULT_POWER_UV);
+static SENSOR_DEVICE_ATTR_RO(power1_max_alarm, ltc2945_bool, FAULT_POWER_OV);
+
+static struct attribute *ltc2945_attrs[] = {
+ &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_lowest.dev_attr.attr,
+ &sensor_dev_attr_in1_highest.dev_attr.attr,
+ &sensor_dev_attr_in1_reset_history.dev_attr.attr,
+ &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_max_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_lowest.dev_attr.attr,
+ &sensor_dev_attr_in2_highest.dev_attr.attr,
+ &sensor_dev_attr_in2_reset_history.dev_attr.attr,
+ &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_min.dev_attr.attr,
+ &sensor_dev_attr_curr1_max.dev_attr.attr,
+ &sensor_dev_attr_curr1_lowest.dev_attr.attr,
+ &sensor_dev_attr_curr1_highest.dev_attr.attr,
+ &sensor_dev_attr_curr1_reset_history.dev_attr.attr,
+ &sensor_dev_attr_curr1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_power1_input.dev_attr.attr,
+ &sensor_dev_attr_power1_min.dev_attr.attr,
+ &sensor_dev_attr_power1_max.dev_attr.attr,
+ &sensor_dev_attr_power1_input_lowest.dev_attr.attr,
+ &sensor_dev_attr_power1_input_highest.dev_attr.attr,
+ &sensor_dev_attr_power1_reset_history.dev_attr.attr,
+ &sensor_dev_attr_power1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_power1_max_alarm.dev_attr.attr,
+
+ NULL,
+};
+ATTRIBUTE_GROUPS(ltc2945);
+
+static const struct regmap_config ltc2945_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = LTC2945_MIN_ADIN_THRES_L,
+};
+
+static int ltc2945_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &ltc2945_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ /* Clear faults */
+ regmap_write(regmap, LTC2945_FAULT, 0x00);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ regmap,
+ ltc2945_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc2945_id[] = {
+ {"ltc2945", 0},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, ltc2945_id);
+
+static struct i2c_driver ltc2945_driver = {
+ .driver = {
+ .name = "ltc2945",
+ },
+ .probe_new = ltc2945_probe,
+ .id_table = ltc2945_id,
+};
+
+module_i2c_driver(ltc2945_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LTC2945 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c
new file mode 100644
index 000000000..2dbbbac9d
--- /dev/null
+++ b/drivers/hwmon/ltc2947-core.c
@@ -0,0 +1,1171 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices LTC2947 high precision power and energy monitor
+ *
+ * Copyright 2019 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "ltc2947.h"
+
+/* register's */
+#define LTC2947_REG_PAGE_CTRL 0xFF
+#define LTC2947_REG_CTRL 0xF0
+#define LTC2947_REG_TBCTL 0xE9
+#define LTC2947_CONT_MODE_MASK BIT(3)
+#define LTC2947_CONT_MODE(x) FIELD_PREP(LTC2947_CONT_MODE_MASK, x)
+#define LTC2947_PRE_MASK GENMASK(2, 0)
+#define LTC2947_PRE(x) FIELD_PREP(LTC2947_PRE_MASK, x)
+#define LTC2947_DIV_MASK GENMASK(7, 3)
+#define LTC2947_DIV(x) FIELD_PREP(LTC2947_DIV_MASK, x)
+#define LTC2947_SHUTDOWN_MASK BIT(0)
+#define LTC2947_REG_ACCUM_POL 0xE1
+#define LTC2947_ACCUM_POL_1_MASK GENMASK(1, 0)
+#define LTC2947_ACCUM_POL_1(x) FIELD_PREP(LTC2947_ACCUM_POL_1_MASK, x)
+#define LTC2947_ACCUM_POL_2_MASK GENMASK(3, 2)
+#define LTC2947_ACCUM_POL_2(x) FIELD_PREP(LTC2947_ACCUM_POL_2_MASK, x)
+#define LTC2947_REG_ACCUM_DEADBAND 0xE4
+#define LTC2947_REG_GPIOSTATCTL 0x67
+#define LTC2947_GPIO_EN_MASK BIT(0)
+#define LTC2947_GPIO_EN(x) FIELD_PREP(LTC2947_GPIO_EN_MASK, x)
+#define LTC2947_GPIO_FAN_EN_MASK BIT(6)
+#define LTC2947_GPIO_FAN_EN(x) FIELD_PREP(LTC2947_GPIO_FAN_EN_MASK, x)
+#define LTC2947_GPIO_FAN_POL_MASK BIT(7)
+#define LTC2947_GPIO_FAN_POL(x) FIELD_PREP(LTC2947_GPIO_FAN_POL_MASK, x)
+#define LTC2947_REG_GPIO_ACCUM 0xE3
+/* 200Khz */
+#define LTC2947_CLK_MIN 200000
+/* 25Mhz */
+#define LTC2947_CLK_MAX 25000000
+#define LTC2947_PAGE0 0
+#define LTC2947_PAGE1 1
+/* Voltage registers */
+#define LTC2947_REG_VOLTAGE 0xA0
+#define LTC2947_REG_VOLTAGE_MAX 0x50
+#define LTC2947_REG_VOLTAGE_MIN 0x52
+#define LTC2947_REG_VOLTAGE_THRE_H 0x90
+#define LTC2947_REG_VOLTAGE_THRE_L 0x92
+#define LTC2947_REG_DVCC 0xA4
+#define LTC2947_REG_DVCC_MAX 0x58
+#define LTC2947_REG_DVCC_MIN 0x5A
+#define LTC2947_REG_DVCC_THRE_H 0x98
+#define LTC2947_REG_DVCC_THRE_L 0x9A
+#define LTC2947_VOLTAGE_GEN_CHAN 0
+#define LTC2947_VOLTAGE_DVCC_CHAN 1
+/* in mV */
+#define VOLTAGE_MAX 15500
+#define VOLTAGE_MIN -300
+#define VDVCC_MAX 15000
+#define VDVCC_MIN 4750
+/* Current registers */
+#define LTC2947_REG_CURRENT 0x90
+#define LTC2947_REG_CURRENT_MAX 0x40
+#define LTC2947_REG_CURRENT_MIN 0x42
+#define LTC2947_REG_CURRENT_THRE_H 0x80
+#define LTC2947_REG_CURRENT_THRE_L 0x82
+/* in mA */
+#define CURRENT_MAX 30000
+#define CURRENT_MIN -30000
+/* Power registers */
+#define LTC2947_REG_POWER 0x93
+#define LTC2947_REG_POWER_MAX 0x44
+#define LTC2947_REG_POWER_MIN 0x46
+#define LTC2947_REG_POWER_THRE_H 0x84
+#define LTC2947_REG_POWER_THRE_L 0x86
+/* in uW */
+#define POWER_MAX 450000000
+#define POWER_MIN -450000000
+/* Temperature registers */
+#define LTC2947_REG_TEMP 0xA2
+#define LTC2947_REG_TEMP_MAX 0x54
+#define LTC2947_REG_TEMP_MIN 0x56
+#define LTC2947_REG_TEMP_THRE_H 0x94
+#define LTC2947_REG_TEMP_THRE_L 0x96
+#define LTC2947_REG_TEMP_FAN_THRE_H 0x9C
+#define LTC2947_REG_TEMP_FAN_THRE_L 0x9E
+#define LTC2947_TEMP_FAN_CHAN 1
+/* in millidegress Celsius */
+#define TEMP_MAX 85000
+#define TEMP_MIN -40000
+/* Energy registers */
+#define LTC2947_REG_ENERGY1 0x06
+#define LTC2947_REG_ENERGY2 0x16
+/* Status/Alarm/Overflow registers */
+#define LTC2947_REG_STATUS 0x80
+#define LTC2947_REG_STATVT 0x81
+#define LTC2947_REG_STATIP 0x82
+#define LTC2947_REG_STATVDVCC 0x87
+
+#define LTC2947_ALERTS_SIZE (LTC2947_REG_STATVDVCC - LTC2947_REG_STATUS)
+#define LTC2947_MAX_VOLTAGE_MASK BIT(0)
+#define LTC2947_MIN_VOLTAGE_MASK BIT(1)
+#define LTC2947_MAX_CURRENT_MASK BIT(0)
+#define LTC2947_MIN_CURRENT_MASK BIT(1)
+#define LTC2947_MAX_POWER_MASK BIT(2)
+#define LTC2947_MIN_POWER_MASK BIT(3)
+#define LTC2947_MAX_TEMP_MASK BIT(2)
+#define LTC2947_MIN_TEMP_MASK BIT(3)
+#define LTC2947_MAX_TEMP_FAN_MASK BIT(4)
+#define LTC2947_MIN_TEMP_FAN_MASK BIT(5)
+
+struct ltc2947_data {
+ struct regmap *map;
+ struct device *dev;
+ /*
+ * The mutex is needed because the device has 2 memory pages. When
+ * reading/writing the correct page needs to be set so that, the
+ * complete sequence select_page->read/write needs to be protected.
+ */
+ struct mutex lock;
+ u32 lsb_energy;
+ bool gpio_out;
+};
+
+static int __ltc2947_val_read16(const struct ltc2947_data *st, const u8 reg,
+ u64 *val)
+{
+ __be16 __val = 0;
+ int ret;
+
+ ret = regmap_bulk_read(st->map, reg, &__val, 2);
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpu(__val);
+
+ return 0;
+}
+
+static int __ltc2947_val_read24(const struct ltc2947_data *st, const u8 reg,
+ u64 *val)
+{
+ __be32 __val = 0;
+ int ret;
+
+ ret = regmap_bulk_read(st->map, reg, &__val, 3);
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(__val) >> 8;
+
+ return 0;
+}
+
+static int __ltc2947_val_read64(const struct ltc2947_data *st, const u8 reg,
+ u64 *val)
+{
+ __be64 __val = 0;
+ int ret;
+
+ ret = regmap_bulk_read(st->map, reg, &__val, 6);
+ if (ret)
+ return ret;
+
+ *val = be64_to_cpu(__val) >> 16;
+
+ return 0;
+}
+
+static int ltc2947_val_read(struct ltc2947_data *st, const u8 reg,
+ const u8 page, const size_t size, s64 *val)
+{
+ int ret;
+ u64 __val = 0;
+
+ mutex_lock(&st->lock);
+
+ ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
+ if (ret) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ dev_dbg(st->dev, "Read val, reg:%02X, p:%d sz:%zu\n", reg, page,
+ size);
+
+ switch (size) {
+ case 2:
+ ret = __ltc2947_val_read16(st, reg, &__val);
+ break;
+ case 3:
+ ret = __ltc2947_val_read24(st, reg, &__val);
+ break;
+ case 6:
+ ret = __ltc2947_val_read64(st, reg, &__val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&st->lock);
+
+ if (ret)
+ return ret;
+
+ *val = sign_extend64(__val, (8 * size) - 1);
+
+ dev_dbg(st->dev, "Got s:%lld, u:%016llX\n", *val, __val);
+
+ return 0;
+}
+
+static int __ltc2947_val_write64(const struct ltc2947_data *st, const u8 reg,
+ const u64 val)
+{
+ __be64 __val;
+
+ __val = cpu_to_be64(val << 16);
+ return regmap_bulk_write(st->map, reg, &__val, 6);
+}
+
+static int __ltc2947_val_write16(const struct ltc2947_data *st, const u8 reg,
+ const u16 val)
+{
+ __be16 __val;
+
+ __val = cpu_to_be16(val);
+ return regmap_bulk_write(st->map, reg, &__val, 2);
+}
+
+static int ltc2947_val_write(struct ltc2947_data *st, const u8 reg,
+ const u8 page, const size_t size, const u64 val)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ /* set device on correct page */
+ ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
+ if (ret) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ dev_dbg(st->dev, "Write val, r:%02X, p:%d, sz:%zu, val:%016llX\n",
+ reg, page, size, val);
+
+ switch (size) {
+ case 2:
+ ret = __ltc2947_val_write16(st, reg, val);
+ break;
+ case 6:
+ ret = __ltc2947_val_write64(st, reg, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ltc2947_reset_history(struct ltc2947_data *st, const u8 reg_h,
+ const u8 reg_l)
+{
+ int ret;
+ /*
+ * let's reset the tracking register's. Tracking register's have all
+ * 2 bytes size
+ */
+ ret = ltc2947_val_write(st, reg_h, LTC2947_PAGE0, 2, 0x8000U);
+ if (ret)
+ return ret;
+
+ return ltc2947_val_write(st, reg_l, LTC2947_PAGE0, 2, 0x7FFFU);
+}
+
+static int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg,
+ const u32 mask, long *val)
+{
+ u8 offset = reg - LTC2947_REG_STATUS;
+ /* +1 to include status reg */
+ char alarms[LTC2947_ALERTS_SIZE + 1];
+ int ret = 0;
+
+ memset(alarms, 0, sizeof(alarms));
+
+ mutex_lock(&st->lock);
+
+ ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, LTC2947_PAGE0);
+ if (ret)
+ goto unlock;
+
+ dev_dbg(st->dev, "Read alarm, reg:%02X, mask:%02X\n", reg, mask);
+ /*
+ * As stated in the datasheet, when Threshold and Overflow registers
+ * are used, the status and all alert registers must be read in one
+ * multi-byte transaction.
+ */
+ ret = regmap_bulk_read(st->map, LTC2947_REG_STATUS, alarms,
+ sizeof(alarms));
+ if (ret)
+ goto unlock;
+
+ /* get the alarm */
+ *val = !!(alarms[offset] & mask);
+unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static ssize_t ltc2947_show_value(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int ret;
+ s64 val = 0;
+
+ ret = ltc2947_val_read(st, attr->index, LTC2947_PAGE0, 6, &val);
+ if (ret)
+ return ret;
+
+ /* value in microJoule. st->lsb_energy was multiplied by 10E9 */
+ val = div_s64(val * st->lsb_energy, 1000);
+
+ return sprintf(buf, "%lld\n", val);
+}
+
+static int ltc2947_read_temp(struct device *dev, const u32 attr, long *val,
+ const int channel)
+{
+ int ret;
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+ s64 __val = 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = ltc2947_val_read(st, LTC2947_REG_TEMP, LTC2947_PAGE0,
+ 2, &__val);
+ break;
+ case hwmon_temp_highest:
+ ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MAX, LTC2947_PAGE0,
+ 2, &__val);
+ break;
+ case hwmon_temp_lowest:
+ ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MIN, LTC2947_PAGE0,
+ 2, &__val);
+ break;
+ case hwmon_temp_max_alarm:
+ if (channel == LTC2947_TEMP_FAN_CHAN)
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
+ LTC2947_MAX_TEMP_FAN_MASK,
+ val);
+
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
+ LTC2947_MAX_TEMP_MASK, val);
+ case hwmon_temp_min_alarm:
+ if (channel == LTC2947_TEMP_FAN_CHAN)
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
+ LTC2947_MIN_TEMP_FAN_MASK,
+ val);
+
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
+ LTC2947_MIN_TEMP_MASK, val);
+ case hwmon_temp_max:
+ if (channel == LTC2947_TEMP_FAN_CHAN)
+ ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_H,
+ LTC2947_PAGE1, 2, &__val);
+ else
+ ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_H,
+ LTC2947_PAGE1, 2, &__val);
+ break;
+ case hwmon_temp_min:
+ if (channel == LTC2947_TEMP_FAN_CHAN)
+ ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_L,
+ LTC2947_PAGE1, 2, &__val);
+ else
+ ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_L,
+ LTC2947_PAGE1, 2, &__val);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ if (ret)
+ return ret;
+
+ /* in milidegrees celcius, temp is given by: */
+ *val = (__val * 204) + 5500;
+
+ return 0;
+}
+
+static int ltc2947_read_power(struct device *dev, const u32 attr, long *val)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+ int ret;
+ u32 lsb = 200000; /* in uW */
+ s64 __val = 0;
+
+ switch (attr) {
+ case hwmon_power_input:
+ ret = ltc2947_val_read(st, LTC2947_REG_POWER, LTC2947_PAGE0,
+ 3, &__val);
+ lsb = 50000;
+ break;
+ case hwmon_power_input_highest:
+ ret = ltc2947_val_read(st, LTC2947_REG_POWER_MAX, LTC2947_PAGE0,
+ 2, &__val);
+ break;
+ case hwmon_power_input_lowest:
+ ret = ltc2947_val_read(st, LTC2947_REG_POWER_MIN, LTC2947_PAGE0,
+ 2, &__val);
+ break;
+ case hwmon_power_max_alarm:
+ return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
+ LTC2947_MAX_POWER_MASK, val);
+ case hwmon_power_min_alarm:
+ return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
+ LTC2947_MIN_POWER_MASK, val);
+ case hwmon_power_max:
+ ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_H,
+ LTC2947_PAGE1, 2, &__val);
+ break;
+ case hwmon_power_min:
+ ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_L,
+ LTC2947_PAGE1, 2, &__val);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ if (ret)
+ return ret;
+
+ *val = __val * lsb;
+
+ return 0;
+}
+
+static int ltc2947_read_curr(struct device *dev, const u32 attr, long *val)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+ int ret;
+ u8 lsb = 12; /* in mA */
+ s64 __val = 0;
+
+ switch (attr) {
+ case hwmon_curr_input:
+ ret = ltc2947_val_read(st, LTC2947_REG_CURRENT,
+ LTC2947_PAGE0, 3, &__val);
+ lsb = 3;
+ break;
+ case hwmon_curr_highest:
+ ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MAX,
+ LTC2947_PAGE0, 2, &__val);
+ break;
+ case hwmon_curr_lowest:
+ ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MIN,
+ LTC2947_PAGE0, 2, &__val);
+ break;
+ case hwmon_curr_max_alarm:
+ return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
+ LTC2947_MAX_CURRENT_MASK, val);
+ case hwmon_curr_min_alarm:
+ return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
+ LTC2947_MIN_CURRENT_MASK, val);
+ case hwmon_curr_max:
+ ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_H,
+ LTC2947_PAGE1, 2, &__val);
+ break;
+ case hwmon_curr_min:
+ ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_L,
+ LTC2947_PAGE1, 2, &__val);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ if (ret)
+ return ret;
+
+ *val = __val * lsb;
+
+ return 0;
+}
+
+static int ltc2947_read_in(struct device *dev, const u32 attr, long *val,
+ const int channel)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+ int ret;
+ u8 lsb = 2; /* in mV */
+ s64 __val = 0;
+
+ if (channel < 0 || channel > LTC2947_VOLTAGE_DVCC_CHAN) {
+ dev_err(st->dev, "Invalid chan%d for voltage", channel);
+ return -EINVAL;
+ }
+
+ switch (attr) {
+ case hwmon_in_input:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
+ ret = ltc2947_val_read(st, LTC2947_REG_DVCC,
+ LTC2947_PAGE0, 2, &__val);
+ lsb = 145;
+ } else {
+ ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE,
+ LTC2947_PAGE0, 2, &__val);
+ }
+ break;
+ case hwmon_in_highest:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
+ ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MAX,
+ LTC2947_PAGE0, 2, &__val);
+ lsb = 145;
+ } else {
+ ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MAX,
+ LTC2947_PAGE0, 2, &__val);
+ }
+ break;
+ case hwmon_in_lowest:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
+ ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MIN,
+ LTC2947_PAGE0, 2, &__val);
+ lsb = 145;
+ } else {
+ ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MIN,
+ LTC2947_PAGE0, 2, &__val);
+ }
+ break;
+ case hwmon_in_max_alarm:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC,
+ LTC2947_MAX_VOLTAGE_MASK,
+ val);
+
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
+ LTC2947_MAX_VOLTAGE_MASK, val);
+ case hwmon_in_min_alarm:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC,
+ LTC2947_MIN_VOLTAGE_MASK,
+ val);
+
+ return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
+ LTC2947_MIN_VOLTAGE_MASK, val);
+ case hwmon_in_max:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
+ ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_H,
+ LTC2947_PAGE1, 2, &__val);
+ lsb = 145;
+ } else {
+ ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_H,
+ LTC2947_PAGE1, 2, &__val);
+ }
+ break;
+ case hwmon_in_min:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
+ ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_L,
+ LTC2947_PAGE1, 2, &__val);
+ lsb = 145;
+ } else {
+ ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_L,
+ LTC2947_PAGE1, 2, &__val);
+ }
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ if (ret)
+ return ret;
+
+ *val = __val * lsb;
+
+ return 0;
+}
+
+static int ltc2947_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_in:
+ return ltc2947_read_in(dev, attr, val, channel);
+ case hwmon_curr:
+ return ltc2947_read_curr(dev, attr, val);
+ case hwmon_power:
+ return ltc2947_read_power(dev, attr, val);
+ case hwmon_temp:
+ return ltc2947_read_temp(dev, attr, val, channel);
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int ltc2947_write_temp(struct device *dev, const u32 attr,
+ long val, const int channel)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+
+ if (channel < 0 || channel > LTC2947_TEMP_FAN_CHAN) {
+ dev_err(st->dev, "Invalid chan%d for temperature", channel);
+ return -EINVAL;
+ }
+
+ switch (attr) {
+ case hwmon_temp_reset_history:
+ if (val != 1)
+ return -EINVAL;
+ return ltc2947_reset_history(st, LTC2947_REG_TEMP_MAX,
+ LTC2947_REG_TEMP_MIN);
+ case hwmon_temp_max:
+ val = clamp_val(val, TEMP_MIN, TEMP_MAX);
+ if (channel == LTC2947_TEMP_FAN_CHAN) {
+ if (!st->gpio_out)
+ return -ENOTSUPP;
+
+ return ltc2947_val_write(st,
+ LTC2947_REG_TEMP_FAN_THRE_H,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val - 550, 204));
+ }
+
+ return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_H,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val - 550, 204));
+ case hwmon_temp_min:
+ val = clamp_val(val, TEMP_MIN, TEMP_MAX);
+ if (channel == LTC2947_TEMP_FAN_CHAN) {
+ if (!st->gpio_out)
+ return -ENOTSUPP;
+
+ return ltc2947_val_write(st,
+ LTC2947_REG_TEMP_FAN_THRE_L,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val - 550, 204));
+ }
+
+ return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_L,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val - 550, 204));
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int ltc2947_write_power(struct device *dev, const u32 attr,
+ long val)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_power_reset_history:
+ if (val != 1)
+ return -EINVAL;
+ return ltc2947_reset_history(st, LTC2947_REG_POWER_MAX,
+ LTC2947_REG_POWER_MIN);
+ case hwmon_power_max:
+ val = clamp_val(val, POWER_MIN, POWER_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 200000));
+ case hwmon_power_min:
+ val = clamp_val(val, POWER_MIN, POWER_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 200000));
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int ltc2947_write_curr(struct device *dev, const u32 attr,
+ long val)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_curr_reset_history:
+ if (val != 1)
+ return -EINVAL;
+ return ltc2947_reset_history(st, LTC2947_REG_CURRENT_MAX,
+ LTC2947_REG_CURRENT_MIN);
+ case hwmon_curr_max:
+ val = clamp_val(val, CURRENT_MIN, CURRENT_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_H,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 12));
+ case hwmon_curr_min:
+ val = clamp_val(val, CURRENT_MIN, CURRENT_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_L,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 12));
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int ltc2947_write_in(struct device *dev, const u32 attr, long val,
+ const int channel)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+
+ if (channel > LTC2947_VOLTAGE_DVCC_CHAN) {
+ dev_err(st->dev, "Invalid chan%d for voltage", channel);
+ return -EINVAL;
+ }
+
+ switch (attr) {
+ case hwmon_in_reset_history:
+ if (val != 1)
+ return -EINVAL;
+
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
+ return ltc2947_reset_history(st, LTC2947_REG_DVCC_MAX,
+ LTC2947_REG_DVCC_MIN);
+
+ return ltc2947_reset_history(st, LTC2947_REG_VOLTAGE_MAX,
+ LTC2947_REG_VOLTAGE_MIN);
+ case hwmon_in_max:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
+ val = clamp_val(val, VDVCC_MIN, VDVCC_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_H,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 145));
+ }
+
+ val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_H,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 2));
+ case hwmon_in_min:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
+ val = clamp_val(val, VDVCC_MIN, VDVCC_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_L,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 145));
+ }
+
+ val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX);
+ return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_L,
+ LTC2947_PAGE1, 2,
+ DIV_ROUND_CLOSEST(val, 2));
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int ltc2947_write(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_in:
+ return ltc2947_write_in(dev, attr, val, channel);
+ case hwmon_curr:
+ return ltc2947_write_curr(dev, attr, val);
+ case hwmon_power:
+ return ltc2947_write_power(dev, attr, val);
+ case hwmon_temp:
+ return ltc2947_write_temp(dev, attr, val, channel);
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int ltc2947_read_labels(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ switch (type) {
+ case hwmon_in:
+ if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
+ *str = "DVCC";
+ else
+ *str = "VP-VM";
+ return 0;
+ case hwmon_curr:
+ *str = "IP-IM";
+ return 0;
+ case hwmon_temp:
+ if (channel == LTC2947_TEMP_FAN_CHAN)
+ *str = "TEMPFAN";
+ else
+ *str = "Ambient";
+ return 0;
+ case hwmon_power:
+ *str = "Power";
+ return 0;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int ltc2947_in_is_visible(const u32 attr)
+{
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_highest:
+ case hwmon_in_lowest:
+ case hwmon_in_max_alarm:
+ case hwmon_in_min_alarm:
+ case hwmon_in_label:
+ return 0444;
+ case hwmon_in_reset_history:
+ return 0200;
+ case hwmon_in_max:
+ case hwmon_in_min:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static int ltc2947_curr_is_visible(const u32 attr)
+{
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_highest:
+ case hwmon_curr_lowest:
+ case hwmon_curr_max_alarm:
+ case hwmon_curr_min_alarm:
+ case hwmon_curr_label:
+ return 0444;
+ case hwmon_curr_reset_history:
+ return 0200;
+ case hwmon_curr_max:
+ case hwmon_curr_min:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static int ltc2947_power_is_visible(const u32 attr)
+{
+ switch (attr) {
+ case hwmon_power_input:
+ case hwmon_power_input_highest:
+ case hwmon_power_input_lowest:
+ case hwmon_power_label:
+ case hwmon_power_max_alarm:
+ case hwmon_power_min_alarm:
+ return 0444;
+ case hwmon_power_reset_history:
+ return 0200;
+ case hwmon_power_max:
+ case hwmon_power_min:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static int ltc2947_temp_is_visible(const u32 attr)
+{
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_highest:
+ case hwmon_temp_lowest:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_label:
+ return 0444;
+ case hwmon_temp_reset_history:
+ return 0200;
+ case hwmon_temp_max:
+ case hwmon_temp_min:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static umode_t ltc2947_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_in:
+ return ltc2947_in_is_visible(attr);
+ case hwmon_curr:
+ return ltc2947_curr_is_visible(attr);
+ case hwmon_power:
+ return ltc2947_power_is_visible(attr);
+ case hwmon_temp:
+ return ltc2947_temp_is_visible(attr);
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info *ltc2947_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |
+ HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY |
+ HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM |
+ HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |
+ HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY |
+ HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM |
+ HWMON_I_LABEL),
+ HWMON_CHANNEL_INFO(curr,
+ HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST |
+ HWMON_C_MAX | HWMON_C_MIN | HWMON_C_RESET_HISTORY |
+ HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM |
+ HWMON_C_LABEL),
+ HWMON_CHANNEL_INFO(power,
+ HWMON_P_INPUT | HWMON_P_INPUT_LOWEST |
+ HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN |
+ HWMON_P_RESET_HISTORY | HWMON_P_MAX_ALARM |
+ HWMON_P_MIN_ALARM | HWMON_P_LABEL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_LOWEST | HWMON_T_HIGHEST |
+ HWMON_T_MAX | HWMON_T_MIN | HWMON_T_RESET_HISTORY |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
+ HWMON_T_LABEL,
+ HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | HWMON_T_MAX |
+ HWMON_T_MIN | HWMON_T_LABEL),
+ NULL
+};
+
+static const struct hwmon_ops ltc2947_hwmon_ops = {
+ .is_visible = ltc2947_is_visible,
+ .read = ltc2947_read,
+ .write = ltc2947_write,
+ .read_string = ltc2947_read_labels,
+};
+
+static const struct hwmon_chip_info ltc2947_chip_info = {
+ .ops = &ltc2947_hwmon_ops,
+ .info = ltc2947_info,
+};
+
+/* energy attributes are 6bytes wide so we need u64 */
+static SENSOR_DEVICE_ATTR(energy1_input, 0444, ltc2947_show_value, NULL,
+ LTC2947_REG_ENERGY1);
+static SENSOR_DEVICE_ATTR(energy2_input, 0444, ltc2947_show_value, NULL,
+ LTC2947_REG_ENERGY2);
+
+static struct attribute *ltc2947_attrs[] = {
+ &sensor_dev_attr_energy1_input.dev_attr.attr,
+ &sensor_dev_attr_energy2_input.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ltc2947);
+
+static int ltc2947_setup(struct ltc2947_data *st)
+{
+ int ret;
+ struct clk *extclk;
+ u32 dummy, deadband, pol;
+ u32 accum[2];
+
+ /* clear status register by reading it */
+ ret = regmap_read(st->map, LTC2947_REG_STATUS, &dummy);
+ if (ret)
+ return ret;
+ /*
+ * Set max/min for power here since the default values x scale
+ * would overflow on 32bit arch
+ */
+ ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H, LTC2947_PAGE1, 2,
+ POWER_MAX / 200000);
+ if (ret)
+ return ret;
+
+ ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L, LTC2947_PAGE1, 2,
+ POWER_MIN / 200000);
+ if (ret)
+ return ret;
+
+ /* check external clock presence */
+ 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;
+
+ /* let's calculate and set the right valus in TBCTL */
+ rate_hz = clk_get_rate(extclk);
+ if (rate_hz < LTC2947_CLK_MIN || rate_hz > LTC2947_CLK_MAX) {
+ dev_err(st->dev, "Invalid rate:%lu for external clock",
+ rate_hz);
+ return -EINVAL;
+ }
+
+ /* as in table 1 of the datasheet */
+ if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000)
+ pre = 0;
+ else if (rate_hz > 1000000 && rate_hz <= 2000000)
+ pre = 1;
+ else if (rate_hz > 2000000 && rate_hz <= 4000000)
+ pre = 2;
+ else if (rate_hz > 4000000 && rate_hz <= 8000000)
+ pre = 3;
+ else if (rate_hz > 8000000 && rate_hz <= 16000000)
+ pre = 4;
+ else if (rate_hz > 16000000 && rate_hz <= LTC2947_CLK_MAX)
+ pre = 5;
+ /*
+ * Div is given by:
+ * floor(fref / (2^PRE * 32768))
+ */
+ div = rate_hz / ((1 << pre) * 32768);
+ tbctl = LTC2947_PRE(pre) | LTC2947_DIV(div);
+
+ ret = regmap_write(st->map, LTC2947_REG_TBCTL, tbctl);
+ if (ret)
+ return ret;
+ /*
+ * The energy lsb is given by (in W*s):
+ * 06416 * (1/fref) * 2^PRE * (DIV + 1)
+ * The value is multiplied by 10E9
+ */
+ aux = (div + 1) * ((1 << pre) * 641600000ULL);
+ st->lsb_energy = DIV_ROUND_CLOSEST_ULL(aux, rate_hz);
+ } else {
+ /* 19.89E-6 * 10E9 */
+ st->lsb_energy = 19890;
+ }
+ ret = of_property_read_u32_array(st->dev->of_node,
+ "adi,accumulator-ctl-pol", accum,
+ ARRAY_SIZE(accum));
+ if (!ret) {
+ u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) |
+ LTC2947_ACCUM_POL_2(accum[1]);
+
+ ret = regmap_write(st->map, LTC2947_REG_ACCUM_POL, accum_reg);
+ if (ret)
+ return ret;
+ }
+ ret = of_property_read_u32(st->dev->of_node,
+ "adi,accumulation-deadband-microamp",
+ &deadband);
+ if (!ret) {
+ /* the LSB is the same as the current, so 3mA */
+ ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND,
+ deadband / (1000 * 3));
+ if (ret)
+ return ret;
+ }
+ /* check gpio cfg */
+ ret = of_property_read_u32(st->dev->of_node, "adi,gpio-out-pol", &pol);
+ if (!ret) {
+ /* setup GPIO as output */
+ u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) |
+ LTC2947_GPIO_FAN_POL(pol);
+
+ st->gpio_out = true;
+ ret = regmap_write(st->map, LTC2947_REG_GPIOSTATCTL, gpio_ctl);
+ if (ret)
+ return ret;
+ }
+ ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum",
+ accum, ARRAY_SIZE(accum));
+ if (!ret) {
+ /*
+ * Setup the accum options. The gpioctl is already defined as
+ * input by default.
+ */
+ u32 accum_val = LTC2947_ACCUM_POL_1(accum[0]) |
+ LTC2947_ACCUM_POL_2(accum[1]);
+
+ if (st->gpio_out) {
+ dev_err(st->dev,
+ "Cannot have input gpio config if already configured as output");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(st->map, LTC2947_REG_GPIO_ACCUM, accum_val);
+ if (ret)
+ return ret;
+ }
+
+ /* set continuos mode */
+ return regmap_update_bits(st->map, LTC2947_REG_CTRL,
+ LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1));
+}
+
+int ltc2947_core_probe(struct regmap *map, const char *name)
+{
+ struct ltc2947_data *st;
+ struct device *dev = regmap_get_device(map);
+ struct device *hwmon;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->map = map;
+ st->dev = dev;
+ dev_set_drvdata(dev, st);
+ mutex_init(&st->lock);
+
+ ret = ltc2947_setup(st);
+ if (ret)
+ return ret;
+
+ hwmon = devm_hwmon_device_register_with_info(dev, name, st,
+ &ltc2947_chip_info,
+ ltc2947_groups);
+ return PTR_ERR_OR_ZERO(hwmon);
+}
+EXPORT_SYMBOL_GPL(ltc2947_core_probe);
+
+static int ltc2947_resume(struct device *dev)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+ u32 ctrl = 0;
+ int ret;
+
+ /* dummy read to wake the device */
+ ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl);
+ if (ret)
+ return ret;
+ /*
+ * Wait for the device. It takes 100ms to wake up so, 10ms extra
+ * should be enough.
+ */
+ msleep(110);
+ ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl);
+ if (ret)
+ return ret;
+ /* ctrl should be 0 */
+ if (ctrl != 0) {
+ dev_err(st->dev, "Device failed to wake up, ctl:%02X\n", ctrl);
+ return -ETIMEDOUT;
+ }
+
+ /* set continuous mode */
+ return regmap_update_bits(st->map, LTC2947_REG_CTRL,
+ LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1));
+}
+
+static int ltc2947_suspend(struct device *dev)
+{
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+
+ return regmap_update_bits(st->map, LTC2947_REG_CTRL,
+ LTC2947_SHUTDOWN_MASK, 1);
+}
+
+EXPORT_SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume);
+
+const struct of_device_id ltc2947_of_match[] = {
+ { .compatible = "adi,ltc2947" },
+ {}
+};
+EXPORT_SYMBOL_GPL(ltc2947_of_match);
+MODULE_DEVICE_TABLE(of, ltc2947_of_match);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("LTC2947 power and energy monitor core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc2947-i2c.c b/drivers/hwmon/ltc2947-i2c.c
new file mode 100644
index 000000000..96852bc8a
--- /dev/null
+++ b/drivers/hwmon/ltc2947-i2c.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices LTC2947 high precision power and energy monitor over I2C
+ *
+ * Copyright 2019 Analog Devices Inc.
+ */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "ltc2947.h"
+
+static const struct regmap_config ltc2947_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int ltc2947_probe(struct i2c_client *i2c)
+{
+ struct regmap *map;
+
+ map = devm_regmap_init_i2c(i2c, &ltc2947_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ return ltc2947_core_probe(map, i2c->name);
+}
+
+static const struct i2c_device_id ltc2947_id[] = {
+ {"ltc2947", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ltc2947_id);
+
+static struct i2c_driver ltc2947_driver = {
+ .driver = {
+ .name = "ltc2947",
+ .of_match_table = ltc2947_of_match,
+ .pm = pm_sleep_ptr(&ltc2947_pm_ops),
+ },
+ .probe_new = ltc2947_probe,
+ .id_table = ltc2947_id,
+};
+module_i2c_driver(ltc2947_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("LTC2947 I2C power and energy monitor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc2947-spi.c b/drivers/hwmon/ltc2947-spi.c
new file mode 100644
index 000000000..a33be1100
--- /dev/null
+++ b/drivers/hwmon/ltc2947-spi.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices LTC2947 high precision power and energy monitor over SPI
+ *
+ * Copyright 2019 Analog Devices Inc.
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "ltc2947.h"
+
+static const struct regmap_config ltc2947_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .read_flag_mask = BIT(0),
+};
+
+static int ltc2947_probe(struct spi_device *spi)
+{
+ struct regmap *map;
+
+ map = devm_regmap_init_spi(spi, &ltc2947_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ return ltc2947_core_probe(map, spi_get_device_id(spi)->name);
+}
+
+static const struct spi_device_id ltc2947_id[] = {
+ {"ltc2947", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ltc2947_id);
+
+static struct spi_driver ltc2947_driver = {
+ .driver = {
+ .name = "ltc2947",
+ .of_match_table = ltc2947_of_match,
+ .pm = pm_sleep_ptr(&ltc2947_pm_ops),
+ },
+ .probe = ltc2947_probe,
+ .id_table = ltc2947_id,
+};
+module_spi_driver(ltc2947_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("LTC2947 SPI power and energy monitor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc2947.h b/drivers/hwmon/ltc2947.h
new file mode 100644
index 000000000..5b8ff81a3
--- /dev/null
+++ b/drivers/hwmon/ltc2947.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_LTC2947_H
+#define _LINUX_LTC2947_H
+
+struct regmap;
+
+extern const struct of_device_id ltc2947_of_match[];
+extern const struct dev_pm_ops ltc2947_pm_ops;
+
+int ltc2947_core_probe(struct regmap *map, const char *name);
+
+#endif
diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
new file mode 100644
index 000000000..689f788b8
--- /dev/null
+++ b/drivers/hwmon/ltc2990.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Linear Technology LTC2990 power monitor
+ *
+ * Copyright (C) 2014 Topic Embedded Products
+ * Author: Mike Looijmans <mike.looijmans@topic.nl>
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/property.h>
+
+#define LTC2990_STATUS 0x00
+#define LTC2990_CONTROL 0x01
+#define LTC2990_TRIGGER 0x02
+#define LTC2990_TINT_MSB 0x04
+#define LTC2990_V1_MSB 0x06
+#define LTC2990_V2_MSB 0x08
+#define LTC2990_V3_MSB 0x0A
+#define LTC2990_V4_MSB 0x0C
+#define LTC2990_VCC_MSB 0x0E
+
+#define LTC2990_IN0 BIT(0)
+#define LTC2990_IN1 BIT(1)
+#define LTC2990_IN2 BIT(2)
+#define LTC2990_IN3 BIT(3)
+#define LTC2990_IN4 BIT(4)
+#define LTC2990_CURR1 BIT(5)
+#define LTC2990_CURR2 BIT(6)
+#define LTC2990_TEMP1 BIT(7)
+#define LTC2990_TEMP2 BIT(8)
+#define LTC2990_TEMP3 BIT(9)
+#define LTC2990_NONE 0
+#define LTC2990_ALL GENMASK(9, 0)
+
+#define LTC2990_MODE0_SHIFT 0
+#define LTC2990_MODE0_MASK GENMASK(2, 0)
+#define LTC2990_MODE1_SHIFT 3
+#define LTC2990_MODE1_MASK GENMASK(1, 0)
+
+/* Enabled measurements for mode bits 2..0 */
+static const int ltc2990_attrs_ena_0[] = {
+ LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3,
+ LTC2990_CURR1 | LTC2990_TEMP3,
+ LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4,
+ LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4,
+ LTC2990_TEMP2 | LTC2990_CURR2,
+ LTC2990_TEMP2 | LTC2990_TEMP3,
+ LTC2990_CURR1 | LTC2990_CURR2,
+ LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
+};
+
+/* Enabled measurements for mode bits 4..3 */
+static const int ltc2990_attrs_ena_1[] = {
+ LTC2990_NONE,
+ LTC2990_TEMP2 | LTC2990_IN1 | LTC2990_CURR1,
+ LTC2990_TEMP3 | LTC2990_IN3 | LTC2990_CURR2,
+ LTC2990_ALL
+};
+
+struct ltc2990_data {
+ struct i2c_client *i2c;
+ u32 mode[2];
+};
+
+/* Return the converted value from the given register in uV or mC */
+static int ltc2990_get_value(struct i2c_client *i2c, int index, int *result)
+{
+ int val;
+ u8 reg;
+
+ switch (index) {
+ case LTC2990_IN0:
+ reg = LTC2990_VCC_MSB;
+ break;
+ case LTC2990_IN1:
+ case LTC2990_CURR1:
+ case LTC2990_TEMP2:
+ reg = LTC2990_V1_MSB;
+ break;
+ case LTC2990_IN2:
+ reg = LTC2990_V2_MSB;
+ break;
+ case LTC2990_IN3:
+ case LTC2990_CURR2:
+ case LTC2990_TEMP3:
+ reg = LTC2990_V3_MSB;
+ break;
+ case LTC2990_IN4:
+ reg = LTC2990_V4_MSB;
+ break;
+ case LTC2990_TEMP1:
+ reg = LTC2990_TINT_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = i2c_smbus_read_word_swapped(i2c, reg);
+ if (unlikely(val < 0))
+ return val;
+
+ switch (index) {
+ case LTC2990_TEMP1:
+ case LTC2990_TEMP2:
+ case LTC2990_TEMP3:
+ /* temp, 0.0625 degrees/LSB */
+ *result = sign_extend32(val, 12) * 1000 / 16;
+ break;
+ case LTC2990_CURR1:
+ case LTC2990_CURR2:
+ /* Vx-Vy, 19.42uV/LSB */
+ *result = sign_extend32(val, 14) * 1942 / 100;
+ break;
+ case LTC2990_IN0:
+ /* Vcc, 305.18uV/LSB, 2.5V offset */
+ *result = sign_extend32(val, 14) * 30518 / (100 * 1000) + 2500;
+ break;
+ case LTC2990_IN1:
+ case LTC2990_IN2:
+ case LTC2990_IN3:
+ case LTC2990_IN4:
+ /* Vx, 305.18uV/LSB */
+ *result = sign_extend32(val, 14) * 30518 / (100 * 1000);
+ break;
+ default:
+ return -EINVAL; /* won't happen, keep compiler happy */
+ }
+
+ return 0;
+}
+
+static ssize_t ltc2990_value_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc2990_data *data = dev_get_drvdata(dev);
+ int value;
+ int ret;
+
+ ret = ltc2990_get_value(data->i2c, attr->index, &value);
+ if (unlikely(ret < 0))
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", value);
+}
+
+static umode_t ltc2990_attrs_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ 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);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+
+ int attrs_mask = LTC2990_IN0 | LTC2990_TEMP1 |
+ (ltc2990_attrs_ena_0[data->mode[0]] &
+ ltc2990_attrs_ena_1[data->mode[1]]);
+
+ if (attr->index & attrs_mask)
+ return a->mode;
+
+ return 0;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, ltc2990_value, LTC2990_TEMP1);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, ltc2990_value, LTC2990_TEMP2);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, ltc2990_value, LTC2990_TEMP3);
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc2990_value, LTC2990_CURR1);
+static SENSOR_DEVICE_ATTR_RO(curr2_input, ltc2990_value, LTC2990_CURR2);
+static SENSOR_DEVICE_ATTR_RO(in0_input, ltc2990_value, LTC2990_IN0);
+static SENSOR_DEVICE_ATTR_RO(in1_input, ltc2990_value, LTC2990_IN1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, ltc2990_value, LTC2990_IN2);
+static SENSOR_DEVICE_ATTR_RO(in3_input, ltc2990_value, LTC2990_IN3);
+static SENSOR_DEVICE_ATTR_RO(in4_input, ltc2990_value, LTC2990_IN4);
+
+static struct attribute *ltc2990_attrs[] = {
+ &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_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr2_input.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,
+ NULL,
+};
+
+static const struct attribute_group ltc2990_group = {
+ .attrs = ltc2990_attrs,
+ .is_visible = ltc2990_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(ltc2990);
+
+static int ltc2990_i2c_probe(struct i2c_client *i2c)
+{
+ int ret;
+ struct device *hwmon_dev;
+ struct ltc2990_data *data;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(&i2c->dev, sizeof(struct ltc2990_data), GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ data->i2c = i2c;
+
+ if (dev_fwnode(&i2c->dev)) {
+ ret = device_property_read_u32_array(&i2c->dev,
+ "lltc,meas-mode",
+ data->mode, 2);
+ if (ret < 0)
+ return ret;
+
+ if (data->mode[0] & ~LTC2990_MODE0_MASK ||
+ data->mode[1] & ~LTC2990_MODE1_MASK)
+ return -EINVAL;
+ } else {
+ ret = i2c_smbus_read_byte_data(i2c, LTC2990_CONTROL);
+ if (ret < 0)
+ return ret;
+
+ data->mode[0] = ret >> LTC2990_MODE0_SHIFT & LTC2990_MODE0_MASK;
+ data->mode[1] = ret >> LTC2990_MODE1_SHIFT & LTC2990_MODE1_MASK;
+ }
+
+ /* Setup continuous mode */
+ ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
+ data->mode[0] << LTC2990_MODE0_SHIFT |
+ data->mode[1] << LTC2990_MODE1_SHIFT);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
+ return ret;
+ }
+ /* Trigger once to start continuous conversion */
+ ret = i2c_smbus_write_byte_data(i2c, LTC2990_TRIGGER, 1);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Error: Failed to start acquisition.\n");
+ return ret;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
+ i2c->name,
+ data,
+ ltc2990_groups);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc2990_i2c_id[] = {
+ { "ltc2990", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ltc2990_i2c_id);
+
+static struct i2c_driver ltc2990_i2c_driver = {
+ .driver = {
+ .name = "ltc2990",
+ },
+ .probe_new = ltc2990_i2c_probe,
+ .id_table = ltc2990_i2c_id,
+};
+
+module_i2c_driver(ltc2990_i2c_driver);
+
+MODULE_DESCRIPTION("LTC2990 Sensor Driver");
+MODULE_AUTHOR("Topic Embedded Products");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
new file mode 100644
index 000000000..d88e883c7
--- /dev/null
+++ b/drivers/hwmon/ltc2992.c
@@ -0,0 +1,939 @@
+// 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.can_sleep = true;
+ 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 = &ltc2992_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, &ltc2992_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,
+ &ltc2992_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
new file mode 100644
index 000000000..e3ac004c1
--- /dev/null
+++ b/drivers/hwmon/ltc4151.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Linear Technology LTC4151 High Voltage I2C Current
+ * and Voltage Monitor
+ *
+ * Copyright (C) 2011 AppearTV AS
+ *
+ * Derived from:
+ *
+ * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot
+ * Swap Controller
+ * Copyright (C) 2010 Ericsson AB.
+ *
+ * Datasheet: http://www.linear.com/docs/Datasheet/4151fc.pdf
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+
+/* chip registers */
+#define LTC4151_SENSE_H 0x00
+#define LTC4151_SENSE_L 0x01
+#define LTC4151_VIN_H 0x02
+#define LTC4151_VIN_L 0x03
+#define LTC4151_ADIN_H 0x04
+#define LTC4151_ADIN_L 0x05
+
+struct ltc4151_data {
+ struct i2c_client *client;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+ unsigned int shunt; /* in micro ohms */
+
+ /* Registers */
+ u8 regs[6];
+};
+
+static struct ltc4151_data *ltc4151_update_device(struct device *dev)
+{
+ struct ltc4151_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct ltc4151_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ /*
+ * The chip's A/D updates 6 times per second
+ * (Conversion Rate 6 - 9 Hz)
+ */
+ if (time_after(jiffies, data->last_updated + HZ / 6) || !data->valid) {
+ int i;
+
+ dev_dbg(&client->dev, "Starting ltc4151 update\n");
+
+ /* Read all registers */
+ for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, i);
+ if (unlikely(val < 0)) {
+ dev_dbg(dev,
+ "Failed to read ADC value: error %d\n",
+ val);
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->regs[i] = val;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+/* Return the voltage from the given register in mV */
+static int ltc4151_get_value(struct ltc4151_data *data, u8 reg)
+{
+ u32 val;
+
+ val = (data->regs[reg] << 4) + (data->regs[reg + 1] >> 4);
+
+ switch (reg) {
+ case LTC4151_ADIN_H:
+ /* 500uV resolution. Convert to mV. */
+ val = val * 500 / 1000;
+ break;
+ case LTC4151_SENSE_H:
+ /*
+ * 20uV resolution. Convert to current as measured with
+ * a given sense resistor, in mA.
+ */
+ val = val * 20 * 1000 / data->shunt;
+ break;
+ case LTC4151_VIN_H:
+ /* 25 mV per increment */
+ val = val * 25;
+ break;
+ default:
+ /* If we get here, the developer messed up */
+ WARN_ON_ONCE(1);
+ val = 0;
+ break;
+ }
+
+ return val;
+}
+
+static ssize_t ltc4151_value_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc4151_data *data = ltc4151_update_device(dev);
+ int value;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ value = ltc4151_get_value(data, attr->index);
+ return sysfs_emit(buf, "%d\n", value);
+}
+
+/*
+ * Input voltages.
+ */
+static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4151_value, LTC4151_VIN_H);
+static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4151_value, LTC4151_ADIN_H);
+
+/* Currents (via sense resistor) */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4151_value, LTC4151_SENSE_H);
+
+/*
+ * Finally, construct an array of pointers to members of the above objects,
+ * as required for sysfs_create_group()
+ */
+static struct attribute *ltc4151_attrs[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+
+ NULL,
+};
+ATTRIBUTE_GROUPS(ltc4151);
+
+static int ltc4151_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct device *dev = &client->dev;
+ struct ltc4151_data *data;
+ struct device *hwmon_dev;
+ u32 shunt;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (of_property_read_u32(client->dev.of_node,
+ "shunt-resistor-micro-ohms", &shunt))
+ shunt = 1000; /* 1 mOhm if not set via DT */
+
+ if (shunt == 0)
+ return -EINVAL;
+
+ data->shunt = shunt;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ ltc4151_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc4151_id[] = {
+ { "ltc4151", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc4151_id);
+
+static const struct of_device_id __maybe_unused ltc4151_match[] = {
+ { .compatible = "lltc,ltc4151" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltc4151_match);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ltc4151_driver = {
+ .driver = {
+ .name = "ltc4151",
+ .of_match_table = of_match_ptr(ltc4151_match),
+ },
+ .probe_new = ltc4151_probe,
+ .id_table = ltc4151_id,
+};
+
+module_i2c_driver(ltc4151_driver);
+
+MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
+MODULE_DESCRIPTION("LTC4151 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c
new file mode 100644
index 000000000..fa43d26dd
--- /dev/null
+++ b/drivers/hwmon/ltc4215.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Linear Technology LTC4215 I2C Hot Swap Controller
+ *
+ * Copyright (C) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * Datasheet:
+ * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697
+ */
+
+#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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+
+/* Here are names of the chip's registers (a.k.a. commands) */
+enum ltc4215_cmd {
+ LTC4215_CONTROL = 0x00, /* rw */
+ LTC4215_ALERT = 0x01, /* rw */
+ LTC4215_STATUS = 0x02, /* ro */
+ LTC4215_FAULT = 0x03, /* rw */
+ LTC4215_SENSE = 0x04, /* rw */
+ LTC4215_SOURCE = 0x05, /* rw */
+ LTC4215_ADIN = 0x06, /* rw */
+};
+
+struct ltc4215_data {
+ struct i2c_client *client;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ /* Registers */
+ u8 regs[7];
+};
+
+static struct ltc4215_data *ltc4215_update_device(struct device *dev)
+{
+ struct ltc4215_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ s32 val;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ /* The chip's A/D updates 10 times per second */
+ if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
+
+ dev_dbg(&client->dev, "Starting ltc4215 update\n");
+
+ /* Read all registers */
+ for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
+ val = i2c_smbus_read_byte_data(client, i);
+ if (unlikely(val < 0))
+ data->regs[i] = 0;
+ else
+ data->regs[i] = val;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* Return the voltage from the given register in millivolts */
+static int ltc4215_get_voltage(struct device *dev, u8 reg)
+{
+ struct ltc4215_data *data = ltc4215_update_device(dev);
+ const u8 regval = data->regs[reg];
+ u32 voltage = 0;
+
+ switch (reg) {
+ case LTC4215_SENSE:
+ /* 151 uV per increment */
+ voltage = regval * 151 / 1000;
+ break;
+ case LTC4215_SOURCE:
+ /* 60.5 mV per increment */
+ voltage = regval * 605 / 10;
+ break;
+ case LTC4215_ADIN:
+ /*
+ * The ADIN input is divided by 12.5, and has 4.82 mV
+ * per increment, so we have the additional multiply
+ */
+ voltage = regval * 482 * 125 / 1000;
+ break;
+ default:
+ /* If we get here, the developer messed up */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ return voltage;
+}
+
+/* Return the current from the sense resistor in mA */
+static unsigned int ltc4215_get_current(struct device *dev)
+{
+ struct ltc4215_data *data = ltc4215_update_device(dev);
+
+ /*
+ * The strange looking conversions that follow are fixed-point
+ * math, since we cannot do floating point in the kernel.
+ *
+ * Step 1: convert sense register to microVolts
+ * Step 2: convert voltage to milliAmperes
+ *
+ * If you play around with the V=IR equation, you come up with
+ * the following: X uV / Y mOhm == Z mA
+ *
+ * With the resistors that are fractions of a milliOhm, we multiply
+ * the voltage and resistance by 10, to shift the decimal point.
+ * Now we can use the normal division operator again.
+ */
+
+ /* Calculate voltage in microVolts (151 uV per increment) */
+ const unsigned int voltage = data->regs[LTC4215_SENSE] * 151;
+
+ /* Calculate current in milliAmperes (4 milliOhm sense resistor) */
+ const unsigned int curr = voltage / 4;
+
+ return curr;
+}
+
+static ssize_t ltc4215_voltage_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ const int voltage = ltc4215_get_voltage(dev, attr->index);
+
+ return sysfs_emit(buf, "%d\n", voltage);
+}
+
+static ssize_t ltc4215_current_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ const unsigned int curr = ltc4215_get_current(dev);
+
+ return sysfs_emit(buf, "%u\n", curr);
+}
+
+static ssize_t ltc4215_power_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ const unsigned int curr = ltc4215_get_current(dev);
+ const int output_voltage = ltc4215_get_voltage(dev, LTC4215_ADIN);
+
+ /* current in mA * voltage in mV == power in uW */
+ const unsigned int power = abs(output_voltage * curr);
+
+ return sysfs_emit(buf, "%u\n", power);
+}
+
+static ssize_t ltc4215_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc4215_data *data = ltc4215_update_device(dev);
+ const u8 reg = data->regs[LTC4215_STATUS];
+ const u32 mask = attr->index;
+
+ return sysfs_emit(buf, "%u\n", !!(reg & mask));
+}
+
+/*
+ * These macros are used below in constructing device attribute objects
+ * for use with sysfs_create_group() to make a sysfs device file
+ * for each register.
+ */
+
+/* Construct a sensor_device_attribute structure for each register */
+
+/* Current */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4215_current, 0);
+static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4215_alarm, 1 << 2);
+
+/* Power (virtual) */
+static SENSOR_DEVICE_ATTR_RO(power1_input, ltc4215_power, 0);
+
+/* Input Voltage */
+static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4215_voltage, LTC4215_ADIN);
+static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4215_alarm, 1 << 0);
+static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4215_alarm, 1 << 1);
+
+/* Output Voltage */
+static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4215_voltage, LTC4215_SOURCE);
+static SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc4215_alarm, 1 << 3);
+
+/*
+ * Finally, construct an array of pointers to members of the above objects,
+ * as required for sysfs_create_group()
+ */
+static struct attribute *ltc4215_attrs[] = {
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_power1_input.dev_attr.attr,
+
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
+
+ NULL,
+};
+ATTRIBUTE_GROUPS(ltc4215);
+
+static int ltc4215_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct device *dev = &client->dev;
+ struct ltc4215_data *data;
+ struct device *hwmon_dev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LTC4215 chip */
+ i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ ltc4215_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc4215_id[] = {
+ { "ltc4215", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc4215_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ltc4215_driver = {
+ .driver = {
+ .name = "ltc4215",
+ },
+ .probe_new = ltc4215_probe,
+ .id_table = ltc4215_id,
+};
+
+module_i2c_driver(ltc4215_driver);
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("LTC4215 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c
new file mode 100644
index 000000000..d2027ca5c
--- /dev/null
+++ b/drivers/hwmon/ltc4222.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Linear Technology LTC4222 Dual Hot Swap controller
+ *
+ * Copyright (c) 2014 Guenter Roeck
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/regmap.h>
+
+/* chip registers */
+
+#define LTC4222_CONTROL1 0xd0
+#define LTC4222_ALERT1 0xd1
+#define LTC4222_STATUS1 0xd2
+#define LTC4222_FAULT1 0xd3
+#define LTC4222_CONTROL2 0xd4
+#define LTC4222_ALERT2 0xd5
+#define LTC4222_STATUS2 0xd6
+#define LTC4222_FAULT2 0xd7
+#define LTC4222_SOURCE1 0xd8
+#define LTC4222_SOURCE2 0xda
+#define LTC4222_ADIN1 0xdc
+#define LTC4222_ADIN2 0xde
+#define LTC4222_SENSE1 0xe0
+#define LTC4222_SENSE2 0xe2
+#define LTC4222_ADC_CONTROL 0xe4
+
+/*
+ * Fault register bits
+ */
+#define FAULT_OV BIT(0)
+#define FAULT_UV BIT(1)
+#define FAULT_OC BIT(2)
+#define FAULT_POWER_BAD BIT(3)
+#define FAULT_FET_BAD BIT(5)
+
+/* Return the voltage from the given register in mV or mA */
+static int ltc4222_get_value(struct device *dev, u8 reg)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int val;
+ u8 buf[2];
+ int ret;
+
+ ret = regmap_bulk_read(regmap, reg, buf, 2);
+ if (ret < 0)
+ return ret;
+
+ val = ((buf[0] << 8) + buf[1]) >> 6;
+
+ switch (reg) {
+ case LTC4222_ADIN1:
+ case LTC4222_ADIN2:
+ /* 1.25 mV resolution. Convert to mV. */
+ val = DIV_ROUND_CLOSEST(val * 5, 4);
+ break;
+ case LTC4222_SOURCE1:
+ case LTC4222_SOURCE2:
+ /* 31.25 mV resolution. Convert to mV. */
+ val = DIV_ROUND_CLOSEST(val * 125, 4);
+ break;
+ case LTC4222_SENSE1:
+ case LTC4222_SENSE2:
+ /*
+ * 62.5 uV resolution. Convert to current as measured with
+ * an 1 mOhm sense resistor, in mA. If a different sense
+ * resistor is installed, calculate the actual current by
+ * dividing the reported current by the sense resistor value
+ * in mOhm.
+ */
+ val = DIV_ROUND_CLOSEST(val * 125, 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return val;
+}
+
+static ssize_t ltc4222_value_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int value;
+
+ value = ltc4222_get_value(dev, attr->index);
+ if (value < 0)
+ return value;
+ return sysfs_emit(buf, "%d\n", value);
+}
+
+static ssize_t ltc4222_bool_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int fault;
+ int ret;
+
+ ret = regmap_read(regmap, attr->nr, &fault);
+ if (ret < 0)
+ return ret;
+ fault &= attr->index;
+ if (fault) /* Clear reported faults in chip register */
+ regmap_update_bits(regmap, attr->nr, attr->index, 0);
+
+ return sysfs_emit(buf, "%d\n", !!fault);
+}
+
+/* Voltages */
+static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4222_value, LTC4222_SOURCE1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4222_value, LTC4222_ADIN1);
+static SENSOR_DEVICE_ATTR_RO(in3_input, ltc4222_value, LTC4222_SOURCE2);
+static SENSOR_DEVICE_ATTR_RO(in4_input, ltc4222_value, LTC4222_ADIN2);
+
+/*
+ * Voltage alarms
+ * UV/OV faults are associated with the input voltage, and power bad and fet
+ * faults are associated with the output voltage.
+ */
+static SENSOR_DEVICE_ATTR_2_RO(in1_min_alarm, ltc4222_bool, LTC4222_FAULT1,
+ FAULT_UV);
+static SENSOR_DEVICE_ATTR_2_RO(in1_max_alarm, ltc4222_bool, LTC4222_FAULT1,
+ FAULT_OV);
+static SENSOR_DEVICE_ATTR_2_RO(in2_alarm, ltc4222_bool, LTC4222_FAULT1,
+ FAULT_POWER_BAD | FAULT_FET_BAD);
+
+static SENSOR_DEVICE_ATTR_2_RO(in3_min_alarm, ltc4222_bool, LTC4222_FAULT2,
+ FAULT_UV);
+static SENSOR_DEVICE_ATTR_2_RO(in3_max_alarm, ltc4222_bool, LTC4222_FAULT2,
+ FAULT_OV);
+static SENSOR_DEVICE_ATTR_2_RO(in4_alarm, ltc4222_bool, LTC4222_FAULT2,
+ FAULT_POWER_BAD | FAULT_FET_BAD);
+
+/* Current (via sense resistor) */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4222_value, LTC4222_SENSE1);
+static SENSOR_DEVICE_ATTR_RO(curr2_input, ltc4222_value, LTC4222_SENSE2);
+
+/* Overcurrent alarm */
+static SENSOR_DEVICE_ATTR_2_RO(curr1_max_alarm, ltc4222_bool, LTC4222_FAULT1,
+ FAULT_OC);
+static SENSOR_DEVICE_ATTR_2_RO(curr2_max_alarm, ltc4222_bool, LTC4222_FAULT2,
+ FAULT_OC);
+
+static struct attribute *ltc4222_attrs[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_curr2_input.dev_attr.attr,
+ &sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
+
+ NULL,
+};
+ATTRIBUTE_GROUPS(ltc4222);
+
+static const struct regmap_config ltc4222_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = LTC4222_ADC_CONTROL,
+};
+
+static int ltc4222_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &ltc4222_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ /* Clear faults */
+ regmap_write(regmap, LTC4222_FAULT1, 0x00);
+ regmap_write(regmap, LTC4222_FAULT2, 0x00);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ regmap,
+ ltc4222_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc4222_id[] = {
+ {"ltc4222", 0},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, ltc4222_id);
+
+static struct i2c_driver ltc4222_driver = {
+ .driver = {
+ .name = "ltc4222",
+ },
+ .probe_new = ltc4222_probe,
+ .id_table = ltc4222_id,
+};
+
+module_i2c_driver(ltc4222_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LTC4222 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
new file mode 100644
index 000000000..5088d28b3
--- /dev/null
+++ b/drivers/hwmon/ltc4245.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller
+ *
+ * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This driver is based on the ds1621 and ina209 drivers.
+ *
+ * Datasheet:
+ * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1140,P19392,D13517
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/platform_data/ltc4245.h>
+
+/* Here are names of the chip's registers (a.k.a. commands) */
+enum ltc4245_cmd {
+ LTC4245_STATUS = 0x00, /* readonly */
+ LTC4245_ALERT = 0x01,
+ LTC4245_CONTROL = 0x02,
+ LTC4245_ON = 0x03,
+ LTC4245_FAULT1 = 0x04,
+ LTC4245_FAULT2 = 0x05,
+ LTC4245_GPIO = 0x06,
+ LTC4245_ADCADR = 0x07,
+
+ LTC4245_12VIN = 0x10,
+ LTC4245_12VSENSE = 0x11,
+ LTC4245_12VOUT = 0x12,
+ LTC4245_5VIN = 0x13,
+ LTC4245_5VSENSE = 0x14,
+ LTC4245_5VOUT = 0x15,
+ LTC4245_3VIN = 0x16,
+ LTC4245_3VSENSE = 0x17,
+ LTC4245_3VOUT = 0x18,
+ LTC4245_VEEIN = 0x19,
+ LTC4245_VEESENSE = 0x1a,
+ LTC4245_VEEOUT = 0x1b,
+ LTC4245_GPIOADC = 0x1c,
+};
+
+struct ltc4245_data {
+ struct i2c_client *client;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ /* Control registers */
+ u8 cregs[0x08];
+
+ /* Voltage registers */
+ u8 vregs[0x0d];
+
+ /* GPIO ADC registers */
+ bool use_extra_gpios;
+ int gpios[3];
+};
+
+/*
+ * Update the readings from the GPIO pins. If the driver has been configured to
+ * sample all GPIO's as analog voltages, a round-robin sampling method is used.
+ * Otherwise, only the configured GPIO pin is sampled.
+ *
+ * LOCKING: must hold data->update_lock
+ */
+static void ltc4245_update_gpios(struct device *dev)
+{
+ struct ltc4245_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 gpio_curr, gpio_next, gpio_reg;
+ int i;
+
+ /* no extra gpio support, we're basically done */
+ if (!data->use_extra_gpios) {
+ data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10];
+ return;
+ }
+
+ /*
+ * If the last reading was too long ago, then we mark all old GPIO
+ * readings as stale by setting them to -EAGAIN
+ */
+ if (time_after(jiffies, data->last_updated + 5 * HZ)) {
+ for (i = 0; i < ARRAY_SIZE(data->gpios); i++)
+ data->gpios[i] = -EAGAIN;
+ }
+
+ /*
+ * Get the current GPIO pin
+ *
+ * The datasheet calls these GPIO[1-3], but we'll calculate the zero
+ * based array index instead, and call them GPIO[0-2]. This is much
+ * easier to think about.
+ */
+ gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6;
+ if (gpio_curr > 0)
+ gpio_curr -= 1;
+
+ /* Read the GPIO voltage from the GPIOADC register */
+ data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10];
+
+ /* Find the next GPIO pin to read */
+ gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios);
+
+ /*
+ * Calculate the correct setting for the GPIO register so it will
+ * sample the next GPIO pin
+ */
+ gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6);
+
+ /* Update the GPIO register */
+ i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg);
+
+ /* Update saved data */
+ data->cregs[LTC4245_GPIO] = gpio_reg;
+}
+
+static struct ltc4245_data *ltc4245_update_device(struct device *dev)
+{
+ struct ltc4245_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ s32 val;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+
+ /* Read control registers -- 0x00 to 0x07 */
+ for (i = 0; i < ARRAY_SIZE(data->cregs); i++) {
+ val = i2c_smbus_read_byte_data(client, i);
+ if (unlikely(val < 0))
+ data->cregs[i] = 0;
+ else
+ data->cregs[i] = val;
+ }
+
+ /* Read voltage registers -- 0x10 to 0x1c */
+ for (i = 0; i < ARRAY_SIZE(data->vregs); i++) {
+ val = i2c_smbus_read_byte_data(client, i+0x10);
+ if (unlikely(val < 0))
+ data->vregs[i] = 0;
+ else
+ data->vregs[i] = val;
+ }
+
+ /* Update GPIO readings */
+ ltc4245_update_gpios(dev);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* Return the voltage from the given register in millivolts */
+static int ltc4245_get_voltage(struct device *dev, u8 reg)
+{
+ struct ltc4245_data *data = ltc4245_update_device(dev);
+ const u8 regval = data->vregs[reg - 0x10];
+ u32 voltage = 0;
+
+ switch (reg) {
+ case LTC4245_12VIN:
+ case LTC4245_12VOUT:
+ voltage = regval * 55;
+ break;
+ case LTC4245_5VIN:
+ case LTC4245_5VOUT:
+ voltage = regval * 22;
+ break;
+ case LTC4245_3VIN:
+ case LTC4245_3VOUT:
+ voltage = regval * 15;
+ break;
+ case LTC4245_VEEIN:
+ case LTC4245_VEEOUT:
+ voltage = regval * -55;
+ break;
+ case LTC4245_GPIOADC:
+ voltage = regval * 10;
+ break;
+ default:
+ /* If we get here, the developer messed up */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ return voltage;
+}
+
+/* Return the current in the given sense register in milliAmperes */
+static unsigned int ltc4245_get_current(struct device *dev, u8 reg)
+{
+ struct ltc4245_data *data = ltc4245_update_device(dev);
+ const u8 regval = data->vregs[reg - 0x10];
+ unsigned int voltage;
+ unsigned int curr;
+
+ /*
+ * The strange looking conversions that follow are fixed-point
+ * math, since we cannot do floating point in the kernel.
+ *
+ * Step 1: convert sense register to microVolts
+ * Step 2: convert voltage to milliAmperes
+ *
+ * If you play around with the V=IR equation, you come up with
+ * the following: X uV / Y mOhm == Z mA
+ *
+ * With the resistors that are fractions of a milliOhm, we multiply
+ * the voltage and resistance by 10, to shift the decimal point.
+ * Now we can use the normal division operator again.
+ */
+
+ switch (reg) {
+ case LTC4245_12VSENSE:
+ voltage = regval * 250; /* voltage in uV */
+ curr = voltage / 50; /* sense resistor 50 mOhm */
+ break;
+ case LTC4245_5VSENSE:
+ voltage = regval * 125; /* voltage in uV */
+ curr = (voltage * 10) / 35; /* sense resistor 3.5 mOhm */
+ break;
+ case LTC4245_3VSENSE:
+ voltage = regval * 125; /* voltage in uV */
+ curr = (voltage * 10) / 25; /* sense resistor 2.5 mOhm */
+ break;
+ case LTC4245_VEESENSE:
+ voltage = regval * 250; /* voltage in uV */
+ curr = voltage / 100; /* sense resistor 100 mOhm */
+ break;
+ default:
+ /* If we get here, the developer messed up */
+ WARN_ON_ONCE(1);
+ curr = 0;
+ break;
+ }
+
+ return curr;
+}
+
+/* Map from voltage channel index to voltage register */
+
+static const s8 ltc4245_in_regs[] = {
+ LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN,
+ LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT,
+};
+
+/* Map from current channel index to current register */
+
+static const s8 ltc4245_curr_regs[] = {
+ LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE,
+};
+
+static int ltc4245_read_curr(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct ltc4245_data *data = ltc4245_update_device(dev);
+
+ switch (attr) {
+ case hwmon_curr_input:
+ *val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
+ return 0;
+ case hwmon_curr_max_alarm:
+ *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct ltc4245_data *data = ltc4245_update_device(dev);
+
+ switch (attr) {
+ case hwmon_in_input:
+ if (channel < 8) {
+ *val = ltc4245_get_voltage(dev,
+ ltc4245_in_regs[channel]);
+ } else {
+ int regval = data->gpios[channel - 8];
+
+ if (regval < 0)
+ return regval;
+ *val = regval * 10;
+ }
+ return 0;
+ case hwmon_in_min_alarm:
+ if (channel < 4)
+ *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel));
+ else
+ *val = !!(data->cregs[LTC4245_FAULT2] &
+ BIT(channel - 4));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ltc4245_read_power(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ unsigned long curr;
+ long voltage;
+
+ switch (attr) {
+ case hwmon_power_input:
+ (void)ltc4245_update_device(dev);
+ curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
+ voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]);
+ *val = abs(curr * voltage);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ltc4245_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+
+ switch (type) {
+ case hwmon_curr:
+ return ltc4245_read_curr(dev, attr, channel, val);
+ case hwmon_power:
+ return ltc4245_read_power(dev, attr, channel, val);
+ case hwmon_in:
+ return ltc4245_read_in(dev, attr, channel - 1, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t ltc4245_is_visible(const void *_data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct ltc4245_data *data = _data;
+
+ switch (type) {
+ case hwmon_in:
+ if (channel == 0)
+ return 0;
+ switch (attr) {
+ case hwmon_in_input:
+ if (channel > 9 && !data->use_extra_gpios)
+ return 0;
+ return 0444;
+ case hwmon_in_min_alarm:
+ if (channel > 8)
+ return 0;
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_max_alarm:
+ return 0444;
+ default:
+ return 0;
+ }
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ return 0444;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info *ltc4245_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT),
+ HWMON_CHANNEL_INFO(curr,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM),
+ HWMON_CHANNEL_INFO(power,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops ltc4245_hwmon_ops = {
+ .is_visible = ltc4245_is_visible,
+ .read = ltc4245_read,
+};
+
+static const struct hwmon_chip_info ltc4245_chip_info = {
+ .ops = &ltc4245_hwmon_ops,
+ .info = ltc4245_info,
+};
+
+static bool ltc4245_use_extra_gpios(struct i2c_client *client)
+{
+ struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct device_node *np = client->dev.of_node;
+
+ /* prefer platform data */
+ if (pdata)
+ return pdata->use_extra_gpios;
+
+ /* fallback on OF */
+ if (of_find_property(np, "ltc4245,use-extra-gpios", NULL))
+ return true;
+
+ return false;
+}
+
+static int ltc4245_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct ltc4245_data *data;
+ struct device *hwmon_dev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+ data->use_extra_gpios = ltc4245_use_extra_gpios(client);
+
+ /* Initialize the LTC4245 chip */
+ i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
+ i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
+ client->name, data,
+ &ltc4245_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc4245_id[] = {
+ { "ltc4245", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc4245_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ltc4245_driver = {
+ .driver = {
+ .name = "ltc4245",
+ },
+ .probe_new = ltc4245_probe,
+ .id_table = ltc4245_id,
+};
+
+module_i2c_driver(ltc4245_driver);
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("LTC4245 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c
new file mode 100644
index 000000000..75e89cec3
--- /dev/null
+++ b/drivers/hwmon/ltc4260.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller
+ *
+ * Copyright (c) 2014 Guenter Roeck
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/regmap.h>
+
+/* chip registers */
+#define LTC4260_CONTROL 0x00
+#define LTC4260_ALERT 0x01
+#define LTC4260_STATUS 0x02
+#define LTC4260_FAULT 0x03
+#define LTC4260_SENSE 0x04
+#define LTC4260_SOURCE 0x05
+#define LTC4260_ADIN 0x06
+
+/*
+ * Fault register bits
+ */
+#define FAULT_OV (1 << 0)
+#define FAULT_UV (1 << 1)
+#define FAULT_OC (1 << 2)
+#define FAULT_POWER_BAD (1 << 3)
+#define FAULT_FET_SHORT (1 << 5)
+
+/* Return the voltage from the given register in mV or mA */
+static int ltc4260_get_value(struct device *dev, u8 reg)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (reg) {
+ case LTC4260_ADIN:
+ /* 10 mV resolution. Convert to mV. */
+ val = val * 10;
+ break;
+ case LTC4260_SOURCE:
+ /* 400 mV resolution. Convert to mV. */
+ val = val * 400;
+ break;
+ case LTC4260_SENSE:
+ /*
+ * 300 uV resolution. Convert to current as measured with
+ * an 1 mOhm sense resistor, in mA. If a different sense
+ * resistor is installed, calculate the actual current by
+ * dividing the reported current by the sense resistor value
+ * in mOhm.
+ */
+ val = val * 300;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return val;
+}
+
+static ssize_t ltc4260_value_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int value;
+
+ value = ltc4260_get_value(dev, attr->index);
+ if (value < 0)
+ return value;
+ return sysfs_emit(buf, "%d\n", value);
+}
+
+static ssize_t ltc4260_bool_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int fault;
+ int ret;
+
+ ret = regmap_read(regmap, LTC4260_FAULT, &fault);
+ if (ret < 0)
+ return ret;
+
+ fault &= attr->index;
+ if (fault) /* Clear reported faults in chip register */
+ regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0);
+
+ return sysfs_emit(buf, "%d\n", !!fault);
+}
+
+/* Voltages */
+static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4260_value, LTC4260_SOURCE);
+static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4260_value, LTC4260_ADIN);
+
+/*
+ * Voltage alarms
+ * UV/OV faults are associated with the input voltage, and the POWER BAD and
+ * FET SHORT faults are associated with the output voltage.
+ */
+static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4260_bool, FAULT_UV);
+static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4260_bool, FAULT_OV);
+static SENSOR_DEVICE_ATTR_RO(in2_alarm, ltc4260_bool,
+ FAULT_POWER_BAD | FAULT_FET_SHORT);
+
+/* Current (via sense resistor) */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4260_value, LTC4260_SENSE);
+
+/* Overcurrent alarm */
+static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4260_bool, FAULT_OC);
+
+static struct attribute *ltc4260_attrs[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
+
+ NULL,
+};
+ATTRIBUTE_GROUPS(ltc4260);
+
+static const struct regmap_config ltc4260_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = LTC4260_ADIN,
+};
+
+static int ltc4260_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &ltc4260_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ /* Clear faults */
+ regmap_write(regmap, LTC4260_FAULT, 0x00);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ regmap,
+ ltc4260_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc4260_id[] = {
+ {"ltc4260", 0},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, ltc4260_id);
+
+static struct i2c_driver ltc4260_driver = {
+ .driver = {
+ .name = "ltc4260",
+ },
+ .probe_new = ltc4260_probe,
+ .id_table = ltc4260_id,
+};
+
+module_i2c_driver(ltc4260_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LTC4260 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c
new file mode 100644
index 000000000..b91cc4fe8
--- /dev/null
+++ b/drivers/hwmon/ltc4261.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller
+ *
+ * Copyright (C) 2010 Ericsson AB.
+ *
+ * Derived from:
+ *
+ * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller
+ * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf
+ */
+
+#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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+
+/* chip registers */
+#define LTC4261_STATUS 0x00 /* readonly */
+#define LTC4261_FAULT 0x01
+#define LTC4261_ALERT 0x02
+#define LTC4261_CONTROL 0x03
+#define LTC4261_SENSE_H 0x04
+#define LTC4261_SENSE_L 0x05
+#define LTC4261_ADIN2_H 0x06
+#define LTC4261_ADIN2_L 0x07
+#define LTC4261_ADIN_H 0x08
+#define LTC4261_ADIN_L 0x09
+
+/*
+ * Fault register bits
+ */
+#define FAULT_OV (1<<0)
+#define FAULT_UV (1<<1)
+#define FAULT_OC (1<<2)
+
+struct ltc4261_data {
+ struct i2c_client *client;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ /* Registers */
+ u8 regs[10];
+};
+
+static struct ltc4261_data *ltc4261_update_device(struct device *dev)
+{
+ struct ltc4261_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct ltc4261_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) {
+ int i;
+
+ /* Read registers -- 0x00 to 0x09 */
+ for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, i);
+ if (unlikely(val < 0)) {
+ dev_dbg(dev,
+ "Failed to read ADC value: error %d\n",
+ val);
+ ret = ERR_PTR(val);
+ data->valid = false;
+ goto abort;
+ }
+ data->regs[i] = val;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+/* Return the voltage from the given register in mV or mA */
+static int ltc4261_get_value(struct ltc4261_data *data, u8 reg)
+{
+ u32 val;
+
+ val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6);
+
+ switch (reg) {
+ case LTC4261_ADIN_H:
+ case LTC4261_ADIN2_H:
+ /* 2.5mV resolution. Convert to mV. */
+ val = val * 25 / 10;
+ break;
+ case LTC4261_SENSE_H:
+ /*
+ * 62.5uV resolution. Convert to current as measured with
+ * an 1 mOhm sense resistor, in mA. If a different sense
+ * resistor is installed, calculate the actual current by
+ * dividing the reported current by the sense resistor value
+ * in mOhm.
+ */
+ val = val * 625 / 10;
+ break;
+ default:
+ /* If we get here, the developer messed up */
+ WARN_ON_ONCE(1);
+ val = 0;
+ break;
+ }
+
+ return val;
+}
+
+static ssize_t ltc4261_value_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc4261_data *data = ltc4261_update_device(dev);
+ int value;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ value = ltc4261_get_value(data, attr->index);
+ return sysfs_emit(buf, "%d\n", value);
+}
+
+static ssize_t ltc4261_bool_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ltc4261_data *data = ltc4261_update_device(dev);
+ u8 fault;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ fault = data->regs[LTC4261_FAULT] & attr->index;
+ if (fault) /* Clear reported faults in chip register */
+ i2c_smbus_write_byte_data(data->client, LTC4261_FAULT, ~fault);
+
+ return sysfs_emit(buf, "%d\n", fault ? 1 : 0);
+}
+
+/*
+ * Input voltages.
+ */
+static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4261_value, LTC4261_ADIN_H);
+static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4261_value, LTC4261_ADIN2_H);
+
+/*
+ * Voltage alarms. The chip has only one set of voltage alarm status bits,
+ * triggered by input voltage alarms. In many designs, those alarms are
+ * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin
+ * to the OV pin. ADIN2 is, however, not available on all chip variants.
+ * To ensure that the alarm condition is reported to the user, report it
+ * with both voltage sensors.
+ */
+static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4261_bool, FAULT_UV);
+static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4261_bool, FAULT_OV);
+static SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc4261_bool, FAULT_UV);
+static SENSOR_DEVICE_ATTR_RO(in2_max_alarm, ltc4261_bool, FAULT_OV);
+
+/* Currents (via sense resistor) */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4261_value, LTC4261_SENSE_H);
+
+/* Overcurrent alarm */
+static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4261_bool, FAULT_OC);
+
+static struct attribute *ltc4261_attrs[] = {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
+
+ NULL,
+};
+ATTRIBUTE_GROUPS(ltc4261);
+
+static int ltc4261_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct device *dev = &client->dev;
+ struct ltc4261_data *data;
+ struct device *hwmon_dev;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) {
+ dev_err(dev, "Failed to read status register\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Clear faults */
+ i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ ltc4261_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id ltc4261_id[] = {
+ {"ltc4261", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ltc4261_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ltc4261_driver = {
+ .driver = {
+ .name = "ltc4261",
+ },
+ .probe_new = ltc4261_probe,
+ .id_table = ltc4261_id,
+};
+
+module_i2c_driver(ltc4261_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LTC4261 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltq-cputemp.c b/drivers/hwmon/ltq-cputemp.c
new file mode 100644
index 000000000..019e770d4
--- /dev/null
+++ b/drivers/hwmon/ltq-cputemp.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Lantiq cpu temperature sensor driver
+ *
+ * Copyright (C) 2017 Florian Eckert <fe@dev.tdt.de>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <lantiq_soc.h>
+
+/* gphy1 configuration register contains cpu temperature */
+#define CGU_GPHY1_CR 0x0040
+#define CGU_TEMP_PD BIT(19)
+
+static void ltq_cputemp_enable(void)
+{
+ ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) | CGU_TEMP_PD, CGU_GPHY1_CR);
+}
+
+static void ltq_cputemp_disable(void *data)
+{
+ ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) & ~CGU_TEMP_PD, CGU_GPHY1_CR);
+}
+
+static int ltq_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ int value;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ /* get the temperature including one decimal place */
+ value = (ltq_cgu_r32(CGU_GPHY1_CR) >> 9) & 0x01FF;
+ value = value * 5;
+ /* range -38 to +154 °C, register value zero is -38.0 °C */
+ value -= 380;
+ /* scale temp to millidegree */
+ value = value * 100;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ *temp = value;
+ return 0;
+}
+
+static umode_t ltq_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 *ltq_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops ltq_hwmon_ops = {
+ .is_visible = ltq_is_visible,
+ .read = ltq_read,
+};
+
+static const struct hwmon_chip_info ltq_chip_info = {
+ .ops = &ltq_hwmon_ops,
+ .info = ltq_info,
+};
+
+static int ltq_cputemp_probe(struct platform_device *pdev)
+{
+ struct device *hwmon_dev;
+ int err = 0;
+
+ /* available on vr9 v1.2 SoCs only */
+ if (ltq_soc_type() != SOC_TYPE_VR9_2)
+ return -ENODEV;
+
+ err = devm_add_action(&pdev->dev, ltq_cputemp_disable, NULL);
+ if (err)
+ return err;
+
+ ltq_cputemp_enable();
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+ "ltq_cputemp",
+ NULL,
+ &ltq_chip_info,
+ NULL);
+
+ if (IS_ERR(hwmon_dev)) {
+ dev_err(&pdev->dev, "Failed to register as hwmon device");
+ return PTR_ERR(hwmon_dev);
+ }
+
+ return 0;
+}
+
+const struct of_device_id ltq_cputemp_match[] = {
+ { .compatible = "lantiq,cputemp" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltq_cputemp_match);
+
+static struct platform_driver ltq_cputemp_driver = {
+ .probe = ltq_cputemp_probe,
+ .driver = {
+ .name = "ltq-cputemp",
+ .of_match_table = ltq_cputemp_match,
+ },
+};
+
+module_platform_driver(ltq_cputemp_driver);
+
+MODULE_AUTHOR("Florian Eckert <fe@dev.tdt.de>");
+MODULE_DESCRIPTION("Lantiq cpu temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c
new file mode 100644
index 000000000..4c5487aeb
--- /dev/null
+++ b/drivers/hwmon/max1111.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max1111.c - +2.7V, Low-Power, Multichannel, Serial 8-bit ADCs
+ *
+ * Based on arch/arm/mach-pxa/corgi_ssp.c
+ *
+ * Copyright (C) 2004-2005 Richard Purdie
+ *
+ * Copyright (C) 2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+
+enum chips { max1110, max1111, max1112, max1113 };
+
+#define MAX1111_TX_BUF_SIZE 1
+#define MAX1111_RX_BUF_SIZE 2
+
+/* MAX1111 Commands */
+#define MAX1111_CTRL_PD0 (1u << 0)
+#define MAX1111_CTRL_PD1 (1u << 1)
+#define MAX1111_CTRL_SGL (1u << 2)
+#define MAX1111_CTRL_UNI (1u << 3)
+#define MAX1110_CTRL_SEL_SH (4)
+#define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */
+#define MAX1111_CTRL_STR (1u << 7)
+
+struct max1111_data {
+ struct spi_device *spi;
+ struct device *hwmon_dev;
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ uint8_t tx_buf[MAX1111_TX_BUF_SIZE];
+ uint8_t rx_buf[MAX1111_RX_BUF_SIZE];
+ struct mutex drvdata_lock;
+ /* protect msg, xfer and buffers from multiple access */
+ int sel_sh;
+ int lsb;
+};
+
+static int max1111_read(struct device *dev, int channel)
+{
+ struct max1111_data *data = dev_get_drvdata(dev);
+ uint8_t v1, v2;
+ int err;
+
+ /* writing to drvdata struct is not thread safe, wait on mutex */
+ mutex_lock(&data->drvdata_lock);
+
+ data->tx_buf[0] = (channel << data->sel_sh) |
+ MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 |
+ MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR;
+
+ err = spi_sync(data->spi, &data->msg);
+ if (err < 0) {
+ dev_err(dev, "spi_sync failed with %d\n", err);
+ mutex_unlock(&data->drvdata_lock);
+ return err;
+ }
+
+ v1 = data->rx_buf[0];
+ v2 = data->rx_buf[1];
+
+ mutex_unlock(&data->drvdata_lock);
+
+ if ((v1 & 0xc0) || (v2 & 0x3f))
+ return -EINVAL;
+
+ return (v1 << 2) | (v2 >> 6);
+}
+
+#ifdef CONFIG_SHARPSL_PM
+static struct max1111_data *the_max1111;
+
+int max1111_read_channel(int channel)
+{
+ if (!the_max1111 || !the_max1111->spi)
+ return -ENODEV;
+
+ return max1111_read(&the_max1111->spi->dev, channel);
+}
+EXPORT_SYMBOL(max1111_read_channel);
+#endif
+
+/*
+ * NOTE: SPI devices do not have a default 'name' attribute, which is
+ * likely to be used by hwmon applications to distinguish between
+ * different devices, explicitly add a name attribute here.
+ */
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
+static ssize_t show_adc(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct max1111_data *data = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(attr)->index;
+ int ret;
+
+ ret = max1111_read(dev, channel);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Assume the reference voltage to be 2.048V or 4.096V, with an 8-bit
+ * sample. The LSB weight is 8mV or 16mV depending on the chip type.
+ */
+ return sprintf(buf, "%d\n", ret * data->lsb);
+}
+
+#define MAX1111_ADC_ATTR(_id) \
+ SENSOR_DEVICE_ATTR(in##_id##_input, S_IRUGO, show_adc, NULL, _id)
+
+static DEVICE_ATTR_RO(name);
+static MAX1111_ADC_ATTR(0);
+static MAX1111_ADC_ATTR(1);
+static MAX1111_ADC_ATTR(2);
+static MAX1111_ADC_ATTR(3);
+static MAX1111_ADC_ATTR(4);
+static MAX1111_ADC_ATTR(5);
+static MAX1111_ADC_ATTR(6);
+static MAX1111_ADC_ATTR(7);
+
+static struct attribute *max1111_attributes[] = {
+ &dev_attr_name.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,
+ NULL,
+};
+
+static const struct attribute_group max1111_attr_group = {
+ .attrs = max1111_attributes,
+};
+
+static struct attribute *max1110_attributes[] = {
+ &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,
+ NULL,
+};
+
+static const struct attribute_group max1110_attr_group = {
+ .attrs = max1110_attributes,
+};
+
+static int setup_transfer(struct max1111_data *data)
+{
+ struct spi_message *m;
+ struct spi_transfer *x;
+
+ m = &data->msg;
+ x = &data->xfer[0];
+
+ spi_message_init(m);
+
+ x->tx_buf = &data->tx_buf[0];
+ x->len = MAX1111_TX_BUF_SIZE;
+ spi_message_add_tail(x, m);
+
+ x++;
+ x->rx_buf = &data->rx_buf[0];
+ x->len = MAX1111_RX_BUF_SIZE;
+ spi_message_add_tail(x, m);
+
+ return 0;
+}
+
+static int max1111_probe(struct spi_device *spi)
+{
+ enum chips chip = spi_get_device_id(spi)->driver_data;
+ struct max1111_data *data;
+ int err;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_0;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ data = devm_kzalloc(&spi->dev, sizeof(struct max1111_data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ switch (chip) {
+ case max1110:
+ data->lsb = 8;
+ data->sel_sh = MAX1110_CTRL_SEL_SH;
+ break;
+ case max1111:
+ data->lsb = 8;
+ data->sel_sh = MAX1111_CTRL_SEL_SH;
+ break;
+ case max1112:
+ data->lsb = 16;
+ data->sel_sh = MAX1110_CTRL_SEL_SH;
+ break;
+ case max1113:
+ data->lsb = 16;
+ data->sel_sh = MAX1111_CTRL_SEL_SH;
+ break;
+ }
+ err = setup_transfer(data);
+ if (err)
+ return err;
+
+ mutex_init(&data->drvdata_lock);
+
+ data->spi = spi;
+ spi_set_drvdata(spi, data);
+
+ err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group);
+ if (err) {
+ dev_err(&spi->dev, "failed to create attribute group\n");
+ return err;
+ }
+ if (chip == max1110 || chip == max1112) {
+ err = sysfs_create_group(&spi->dev.kobj, &max1110_attr_group);
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to create extended attribute group\n");
+ goto err_remove;
+ }
+ }
+
+ data->hwmon_dev = hwmon_device_register(&spi->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_err(&spi->dev, "failed to create hwmon device\n");
+ err = PTR_ERR(data->hwmon_dev);
+ goto err_remove;
+ }
+
+#ifdef CONFIG_SHARPSL_PM
+ the_max1111 = data;
+#endif
+ return 0;
+
+err_remove:
+ sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
+ sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
+ return err;
+}
+
+static void max1111_remove(struct spi_device *spi)
+{
+ struct max1111_data *data = spi_get_drvdata(spi);
+
+#ifdef CONFIG_SHARPSL_PM
+ the_max1111 = NULL;
+#endif
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
+ sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
+ mutex_destroy(&data->drvdata_lock);
+}
+
+static const struct spi_device_id max1111_ids[] = {
+ { "max1110", max1110 },
+ { "max1111", max1111 },
+ { "max1112", max1112 },
+ { "max1113", max1113 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, max1111_ids);
+
+static struct spi_driver max1111_driver = {
+ .driver = {
+ .name = "max1111",
+ },
+ .id_table = max1111_ids,
+ .probe = max1111_probe,
+ .remove = max1111_remove,
+};
+
+module_spi_driver(max1111_driver);
+
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_DESCRIPTION("MAX1110/MAX1111/MAX1112/MAX1113 ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c
new file mode 100644
index 000000000..402ffdc2f
--- /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
new file mode 100644
index 000000000..daa5d8af1
--- /dev/null
+++ b/drivers/hwmon/max16065.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for
+ * Maxim MAX16065/MAX16066 12-Channel/8-Channel, Flash-Configurable
+ * System Managers with Nonvolatile Fault Registers
+ * Maxim MAX16067/MAX16068 6-Channel, Flash-Configurable System Managers
+ * with Nonvolatile Fault Registers
+ * Maxim MAX16070/MAX16071 12-Channel/8-Channel, Flash-Configurable System
+ * Monitors with Nonvolatile Fault Registers
+ *
+ * Copyright (C) 2011 Ericsson AB.
+ */
+
+#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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+
+enum chips { max16065, max16066, max16067, max16068, max16070, max16071 };
+
+/*
+ * Registers
+ */
+#define MAX16065_ADC(x) ((x) * 2)
+
+#define MAX16065_CURR_SENSE 0x18
+#define MAX16065_CSP_ADC 0x19
+#define MAX16065_FAULT(x) (0x1b + (x))
+#define MAX16065_SCALE(x) (0x43 + (x))
+#define MAX16065_CURR_CONTROL 0x47
+#define MAX16065_LIMIT(l, x) (0x48 + (l) + (x) * 3) /*
+ * l: limit
+ * 0: min/max
+ * 1: crit
+ * 2: lcrit
+ * x: ADC index
+ */
+
+#define MAX16065_SW_ENABLE 0x73
+
+#define MAX16065_WARNING_OV (1 << 3) /* Set if secondary threshold is OV
+ warning */
+
+#define MAX16065_CURR_ENABLE (1 << 0)
+
+#define MAX16065_NUM_LIMIT 3
+#define MAX16065_NUM_ADC 12 /* maximum number of ADC channels */
+
+static const int max16065_num_adc[] = {
+ [max16065] = 12,
+ [max16066] = 8,
+ [max16067] = 6,
+ [max16068] = 6,
+ [max16070] = 12,
+ [max16071] = 8,
+};
+
+static const bool max16065_have_secondary[] = {
+ [max16065] = true,
+ [max16066] = true,
+ [max16067] = false,
+ [max16068] = false,
+ [max16070] = true,
+ [max16071] = true,
+};
+
+static const bool max16065_have_current[] = {
+ [max16065] = true,
+ [max16066] = true,
+ [max16067] = false,
+ [max16068] = false,
+ [max16070] = true,
+ [max16071] = true,
+};
+
+struct max16065_data {
+ enum chips type;
+ struct i2c_client *client;
+ const struct attribute_group *groups[4];
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+ int num_adc;
+ bool have_current;
+ int curr_gain;
+ /* limits are in mV */
+ int limit[MAX16065_NUM_LIMIT][MAX16065_NUM_ADC];
+ int range[MAX16065_NUM_ADC + 1];/* voltage range */
+ int adc[MAX16065_NUM_ADC + 1]; /* adc values (raw) including csp_adc */
+ int curr_sense;
+ int fault[2];
+};
+
+static const int max16065_adc_range[] = { 5560, 2780, 1390, 0 };
+static const int max16065_csp_adc_range[] = { 7000, 14000 };
+
+/* ADC registers have 10 bit resolution. */
+static inline int ADC_TO_MV(int adc, int range)
+{
+ return (adc * range) / 1024;
+}
+
+/*
+ * Limit registers have 8 bit resolution and match upper 8 bits of ADC
+ * registers.
+ */
+static inline int LIMIT_TO_MV(int limit, int range)
+{
+ return limit * range / 256;
+}
+
+static inline int MV_TO_LIMIT(int mv, int range)
+{
+ return clamp_val(DIV_ROUND_CLOSEST(mv * 256, range), 0, 255);
+}
+
+static inline int ADC_TO_CURR(int adc, int gain)
+{
+ return adc * 1400000 / (gain * 255);
+}
+
+/*
+ * max16065_read_adc()
+ *
+ * Read 16 bit value from <reg>, <reg+1>.
+ * Upper 8 bits are in <reg>, lower 2 bits are in bits 7:6 of <reg+1>.
+ */
+static int max16065_read_adc(struct i2c_client *client, int reg)
+{
+ int rv;
+
+ rv = i2c_smbus_read_word_swapped(client, reg);
+ if (unlikely(rv < 0))
+ return rv;
+ return rv >> 6;
+}
+
+static struct max16065_data *max16065_update_device(struct device *dev)
+{
+ struct max16065_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->update_lock);
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ int i;
+
+ for (i = 0; i < data->num_adc; i++)
+ data->adc[i]
+ = max16065_read_adc(client, MAX16065_ADC(i));
+
+ if (data->have_current) {
+ data->adc[MAX16065_NUM_ADC]
+ = max16065_read_adc(client, MAX16065_CSP_ADC);
+ data->curr_sense
+ = i2c_smbus_read_byte_data(client,
+ MAX16065_CURR_SENSE);
+ }
+
+ for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++)
+ data->fault[i]
+ = i2c_smbus_read_byte_data(client, MAX16065_FAULT(i));
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+static ssize_t max16065_alarm_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
+ struct max16065_data *data = max16065_update_device(dev);
+ int val = data->fault[attr2->nr];
+
+ if (val < 0)
+ return val;
+
+ val &= (1 << attr2->index);
+ if (val)
+ i2c_smbus_write_byte_data(data->client,
+ MAX16065_FAULT(attr2->nr), val);
+
+ return sysfs_emit(buf, "%d\n", !!val);
+}
+
+static ssize_t max16065_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct max16065_data *data = max16065_update_device(dev);
+ int adc = data->adc[attr->index];
+
+ if (unlikely(adc < 0))
+ return adc;
+
+ return sysfs_emit(buf, "%d\n",
+ ADC_TO_MV(adc, data->range[attr->index]));
+}
+
+static ssize_t max16065_current_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct max16065_data *data = max16065_update_device(dev);
+
+ if (unlikely(data->curr_sense < 0))
+ return data->curr_sense;
+
+ return sysfs_emit(buf, "%d\n",
+ ADC_TO_CURR(data->curr_sense, data->curr_gain));
+}
+
+static ssize_t max16065_limit_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
+ struct max16065_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+ int limit;
+
+ err = kstrtoul(buf, 10, &val);
+ if (unlikely(err < 0))
+ return err;
+
+ limit = MV_TO_LIMIT(val, data->range[attr2->index]);
+
+ mutex_lock(&data->update_lock);
+ data->limit[attr2->nr][attr2->index]
+ = LIMIT_TO_MV(limit, data->range[attr2->index]);
+ i2c_smbus_write_byte_data(data->client,
+ MAX16065_LIMIT(attr2->nr, attr2->index),
+ limit);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t max16065_limit_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
+ struct max16065_data *data = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n",
+ data->limit[attr2->nr][attr2->index]);
+}
+
+/* Construct a sensor_device_attribute structure for each register */
+
+/* Input voltages */
+static SENSOR_DEVICE_ATTR_RO(in0_input, max16065_input, 0);
+static SENSOR_DEVICE_ATTR_RO(in1_input, max16065_input, 1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, max16065_input, 2);
+static SENSOR_DEVICE_ATTR_RO(in3_input, max16065_input, 3);
+static SENSOR_DEVICE_ATTR_RO(in4_input, max16065_input, 4);
+static SENSOR_DEVICE_ATTR_RO(in5_input, max16065_input, 5);
+static SENSOR_DEVICE_ATTR_RO(in6_input, max16065_input, 6);
+static SENSOR_DEVICE_ATTR_RO(in7_input, max16065_input, 7);
+static SENSOR_DEVICE_ATTR_RO(in8_input, max16065_input, 8);
+static SENSOR_DEVICE_ATTR_RO(in9_input, max16065_input, 9);
+static SENSOR_DEVICE_ATTR_RO(in10_input, max16065_input, 10);
+static SENSOR_DEVICE_ATTR_RO(in11_input, max16065_input, 11);
+static SENSOR_DEVICE_ATTR_RO(in12_input, max16065_input, 12);
+
+/* Input voltages lcrit */
+static SENSOR_DEVICE_ATTR_2_RW(in0_lcrit, max16065_limit, 2, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in1_lcrit, max16065_limit, 2, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_lcrit, max16065_limit, 2, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in3_lcrit, max16065_limit, 2, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in4_lcrit, max16065_limit, 2, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in5_lcrit, max16065_limit, 2, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in6_lcrit, max16065_limit, 2, 6);
+static SENSOR_DEVICE_ATTR_2_RW(in7_lcrit, max16065_limit, 2, 7);
+static SENSOR_DEVICE_ATTR_2_RW(in8_lcrit, max16065_limit, 2, 8);
+static SENSOR_DEVICE_ATTR_2_RW(in9_lcrit, max16065_limit, 2, 9);
+static SENSOR_DEVICE_ATTR_2_RW(in10_lcrit, max16065_limit, 2, 10);
+static SENSOR_DEVICE_ATTR_2_RW(in11_lcrit, max16065_limit, 2, 11);
+
+/* Input voltages crit */
+static SENSOR_DEVICE_ATTR_2_RW(in0_crit, max16065_limit, 1, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in1_crit, max16065_limit, 1, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_crit, max16065_limit, 1, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in3_crit, max16065_limit, 1, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in4_crit, max16065_limit, 1, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in5_crit, max16065_limit, 1, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in6_crit, max16065_limit, 1, 6);
+static SENSOR_DEVICE_ATTR_2_RW(in7_crit, max16065_limit, 1, 7);
+static SENSOR_DEVICE_ATTR_2_RW(in8_crit, max16065_limit, 1, 8);
+static SENSOR_DEVICE_ATTR_2_RW(in9_crit, max16065_limit, 1, 9);
+static SENSOR_DEVICE_ATTR_2_RW(in10_crit, max16065_limit, 1, 10);
+static SENSOR_DEVICE_ATTR_2_RW(in11_crit, max16065_limit, 1, 11);
+
+/* Input voltages min */
+static SENSOR_DEVICE_ATTR_2_RW(in0_min, max16065_limit, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in1_min, max16065_limit, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_min, max16065_limit, 0, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in3_min, max16065_limit, 0, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in4_min, max16065_limit, 0, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in5_min, max16065_limit, 0, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in6_min, max16065_limit, 0, 6);
+static SENSOR_DEVICE_ATTR_2_RW(in7_min, max16065_limit, 0, 7);
+static SENSOR_DEVICE_ATTR_2_RW(in8_min, max16065_limit, 0, 8);
+static SENSOR_DEVICE_ATTR_2_RW(in9_min, max16065_limit, 0, 9);
+static SENSOR_DEVICE_ATTR_2_RW(in10_min, max16065_limit, 0, 10);
+static SENSOR_DEVICE_ATTR_2_RW(in11_min, max16065_limit, 0, 11);
+
+/* Input voltages max */
+static SENSOR_DEVICE_ATTR_2_RW(in0_max, max16065_limit, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in1_max, max16065_limit, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_max, max16065_limit, 0, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in3_max, max16065_limit, 0, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in4_max, max16065_limit, 0, 4);
+static SENSOR_DEVICE_ATTR_2_RW(in5_max, max16065_limit, 0, 5);
+static SENSOR_DEVICE_ATTR_2_RW(in6_max, max16065_limit, 0, 6);
+static SENSOR_DEVICE_ATTR_2_RW(in7_max, max16065_limit, 0, 7);
+static SENSOR_DEVICE_ATTR_2_RW(in8_max, max16065_limit, 0, 8);
+static SENSOR_DEVICE_ATTR_2_RW(in9_max, max16065_limit, 0, 9);
+static SENSOR_DEVICE_ATTR_2_RW(in10_max, max16065_limit, 0, 10);
+static SENSOR_DEVICE_ATTR_2_RW(in11_max, max16065_limit, 0, 11);
+
+/* alarms */
+static SENSOR_DEVICE_ATTR_2_RO(in0_alarm, max16065_alarm, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RO(in1_alarm, max16065_alarm, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RO(in2_alarm, max16065_alarm, 0, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in3_alarm, max16065_alarm, 0, 3);
+static SENSOR_DEVICE_ATTR_2_RO(in4_alarm, max16065_alarm, 0, 4);
+static SENSOR_DEVICE_ATTR_2_RO(in5_alarm, max16065_alarm, 0, 5);
+static SENSOR_DEVICE_ATTR_2_RO(in6_alarm, max16065_alarm, 0, 6);
+static SENSOR_DEVICE_ATTR_2_RO(in7_alarm, max16065_alarm, 0, 7);
+static SENSOR_DEVICE_ATTR_2_RO(in8_alarm, max16065_alarm, 1, 0);
+static SENSOR_DEVICE_ATTR_2_RO(in9_alarm, max16065_alarm, 1, 1);
+static SENSOR_DEVICE_ATTR_2_RO(in10_alarm, max16065_alarm, 1, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in11_alarm, max16065_alarm, 1, 3);
+
+/* Current and alarm */
+static SENSOR_DEVICE_ATTR_RO(curr1_input, max16065_current, 0);
+static SENSOR_DEVICE_ATTR_2_RO(curr1_alarm, max16065_alarm, 1, 4);
+
+/*
+ * Finally, construct an array of pointers to members of the above objects,
+ * as required for sysfs_create_group()
+ */
+static struct attribute *max16065_basic_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in0_crit.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in1_crit.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in2_crit.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in3_crit.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in4_crit.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in5_crit.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in6_crit.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in7_crit.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in8_crit.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in9_crit.dev_attr.attr,
+ &sensor_dev_attr_in9_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in10_crit.dev_attr.attr,
+ &sensor_dev_attr_in10_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in11_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in11_crit.dev_attr.attr,
+ &sensor_dev_attr_in11_alarm.dev_attr.attr,
+
+ NULL
+};
+
+static struct attribute *max16065_current_attributes[] = {
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *max16065_min_attributes[] = {
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *max16065_max_attributes[] = {
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ NULL
+};
+
+static umode_t max16065_basic_is_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct max16065_data *data = dev_get_drvdata(dev);
+ int index = n / 4;
+
+ if (index >= data->num_adc || !data->range[index])
+ return 0;
+ return a->mode;
+}
+
+static umode_t max16065_secondary_is_visible(struct kobject *kobj,
+ struct attribute *a, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct max16065_data *data = dev_get_drvdata(dev);
+
+ if (index >= data->num_adc)
+ return 0;
+ return a->mode;
+}
+
+static const struct attribute_group max16065_basic_group = {
+ .attrs = max16065_basic_attributes,
+ .is_visible = max16065_basic_is_visible,
+};
+
+static const struct attribute_group max16065_current_group = {
+ .attrs = max16065_current_attributes,
+};
+
+static const struct attribute_group max16065_min_group = {
+ .attrs = max16065_min_attributes,
+ .is_visible = max16065_secondary_is_visible,
+};
+
+static const struct attribute_group max16065_max_group = {
+ .attrs = max16065_max_attributes,
+ .is_visible = max16065_secondary_is_visible,
+};
+
+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;
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ int i, j, val;
+ 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))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ data->num_adc = max16065_num_adc[id->driver_data];
+ data->have_current = max16065_have_current[id->driver_data];
+ have_secondary = max16065_have_secondary[id->driver_data];
+
+ if (have_secondary) {
+ val = i2c_smbus_read_byte_data(client, MAX16065_SW_ENABLE);
+ if (unlikely(val < 0))
+ return val;
+ secondary_is_max = val & MAX16065_WARNING_OV;
+ }
+
+ /* Read scale registers, convert to range */
+ for (i = 0; i < DIV_ROUND_UP(data->num_adc, 4); i++) {
+ val = i2c_smbus_read_byte_data(client, MAX16065_SCALE(i));
+ if (unlikely(val < 0))
+ return val;
+ for (j = 0; j < 4 && i * 4 + j < data->num_adc; j++) {
+ data->range[i * 4 + j] =
+ max16065_adc_range[(val >> (j * 2)) & 0x3];
+ }
+ }
+
+ /* Read limits */
+ for (i = 0; i < MAX16065_NUM_LIMIT; i++) {
+ if (i == 0 && !have_secondary)
+ continue;
+
+ for (j = 0; j < data->num_adc; j++) {
+ val = i2c_smbus_read_byte_data(client,
+ MAX16065_LIMIT(i, j));
+ if (unlikely(val < 0))
+ return val;
+ data->limit[i][j] = LIMIT_TO_MV(val, data->range[j]);
+ }
+ }
+
+ /* sysfs hooks */
+ data->groups[groups++] = &max16065_basic_group;
+ if (have_secondary)
+ data->groups[groups++] = secondary_is_max ?
+ &max16065_max_group : &max16065_min_group;
+
+ if (data->have_current) {
+ val = i2c_smbus_read_byte_data(client, MAX16065_CURR_CONTROL);
+ if (unlikely(val < 0))
+ return val;
+ if (val & MAX16065_CURR_ENABLE) {
+ /*
+ * Current gain is 6, 12, 24, 48 based on values in
+ * bit 2,3.
+ */
+ data->curr_gain = 6 << ((val >> 2) & 0x03);
+ data->range[MAX16065_NUM_ADC]
+ = max16065_csp_adc_range[(val >> 1) & 0x01];
+ data->groups[groups++] = &max16065_current_group;
+ } else {
+ data->have_current = false;
+ }
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max16065_id[] = {
+ { "max16065", max16065 },
+ { "max16066", max16066 },
+ { "max16067", max16067 },
+ { "max16068", max16068 },
+ { "max16070", max16070 },
+ { "max16071", max16071 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max16065_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max16065_driver = {
+ .driver = {
+ .name = "max16065",
+ },
+ .probe_new = max16065_probe,
+ .id_table = max16065_id,
+};
+
+module_i2c_driver(max16065_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("MAX16065 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
new file mode 100644
index 000000000..445c77197
--- /dev/null
+++ b/drivers/hwmon/max1619.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max1619.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2003-2004 Oleksij Rempel <bug-track@fisher-privat.net>
+ * Jean Delvare <jdelvare@suse.de>
+ *
+ * Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim.
+ * It reports up to two temperatures (its own plus up to
+ * one external one). Complete datasheet can be
+ * obtained from Maxim's website at:
+ * http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/*
+ * The MAX1619 registers
+ */
+
+#define MAX1619_REG_R_MAN_ID 0xFE
+#define MAX1619_REG_R_CHIP_ID 0xFF
+#define MAX1619_REG_R_CONFIG 0x03
+#define MAX1619_REG_W_CONFIG 0x09
+#define MAX1619_REG_R_CONVRATE 0x04
+#define MAX1619_REG_W_CONVRATE 0x0A
+#define MAX1619_REG_R_STATUS 0x02
+#define MAX1619_REG_R_LOCAL_TEMP 0x00
+#define MAX1619_REG_R_REMOTE_TEMP 0x01
+#define MAX1619_REG_R_REMOTE_HIGH 0x07
+#define MAX1619_REG_W_REMOTE_HIGH 0x0D
+#define MAX1619_REG_R_REMOTE_LOW 0x08
+#define MAX1619_REG_W_REMOTE_LOW 0x0E
+#define MAX1619_REG_R_REMOTE_CRIT 0x10
+#define MAX1619_REG_W_REMOTE_CRIT 0x12
+#define MAX1619_REG_R_TCRIT_HYST 0x11
+#define MAX1619_REG_W_TCRIT_HYST 0x13
+
+/*
+ * Conversions
+ */
+
+static int temp_from_reg(int val)
+{
+ return (val & 0x80 ? val-0x100 : val) * 1000;
+}
+
+static int temp_to_reg(int val)
+{
+ return (val < 0 ? val+0x100*1000 : val) / 1000;
+}
+
+enum temp_index {
+ t_input1 = 0,
+ t_input2,
+ t_low2,
+ t_high2,
+ t_crit2,
+ t_hyst2,
+ t_num_regs
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct max1619_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ u8 temp[t_num_regs]; /* index with enum temp_index */
+ u8 alarms;
+};
+
+static const u8 regs_read[t_num_regs] = {
+ [t_input1] = MAX1619_REG_R_LOCAL_TEMP,
+ [t_input2] = MAX1619_REG_R_REMOTE_TEMP,
+ [t_low2] = MAX1619_REG_R_REMOTE_LOW,
+ [t_high2] = MAX1619_REG_R_REMOTE_HIGH,
+ [t_crit2] = MAX1619_REG_R_REMOTE_CRIT,
+ [t_hyst2] = MAX1619_REG_R_TCRIT_HYST,
+};
+
+static const u8 regs_write[t_num_regs] = {
+ [t_low2] = MAX1619_REG_W_REMOTE_LOW,
+ [t_high2] = MAX1619_REG_W_REMOTE_HIGH,
+ [t_crit2] = MAX1619_REG_W_REMOTE_CRIT,
+ [t_hyst2] = MAX1619_REG_W_TCRIT_HYST,
+};
+
+static struct max1619_data *max1619_update_device(struct device *dev)
+{
+ struct max1619_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int config, i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
+ dev_dbg(&client->dev, "Updating max1619 data.\n");
+ for (i = 0; i < t_num_regs; i++)
+ data->temp[i] = i2c_smbus_read_byte_data(client,
+ regs_read[i]);
+ data->alarms = i2c_smbus_read_byte_data(client,
+ MAX1619_REG_R_STATUS);
+ /* If OVERT polarity is low, reverse alarm bit */
+ config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
+ if (!(config & 0x20))
+ data->alarms ^= 0x02;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+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 max1619_data *data = max1619_update_device(dev);
+
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index]));
+}
+
+static ssize_t temp_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 max1619_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp[attr->index] = temp_to_reg(val);
+ i2c_smbus_write_byte_data(client, regs_write[attr->index],
+ data->temp[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct max1619_data *data = max1619_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct max1619_data *data = max1619_update_device(dev);
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input1);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2);
+static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2);
+static SENSOR_DEVICE_ATTR_RW(temp2_crit_hyst, temp, t_hyst2);
+
+static DEVICE_ATTR_RO(alarms);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4);
+
+static struct attribute *max1619_attrs[] = {
+ &sensor_dev_attr_temp1_input.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,
+
+ &dev_attr_alarms.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(max1619);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max1619_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u8 reg_config, reg_convrate, reg_status, man_id, chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* detection */
+ reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
+ reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE);
+ reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS);
+ if ((reg_config & 0x03) != 0x00
+ || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) {
+ dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ /* identification */
+ man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID);
+ if (man_id != 0x4D || chip_id != 0x04) {
+ dev_info(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n",
+ man_id, chip_id);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "max1619", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void max1619_init_client(struct i2c_client *client)
+{
+ u8 config;
+
+ /*
+ * Start the conversions.
+ */
+ i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE,
+ 5); /* 2 Hz */
+ config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
+ if (config & 0x40)
+ i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG,
+ config & 0xBF); /* run */
+}
+
+static int max1619_probe(struct i2c_client *new_client)
+{
+ struct max1619_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = new_client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the MAX1619 chip */
+ max1619_init_client(new_client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
+ new_client->name,
+ data,
+ max1619_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max1619_id[] = {
+ { "max1619", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max1619_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max1619_of_match[] = {
+ { .compatible = "maxim,max1619", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, max1619_of_match);
+#endif
+
+static struct i2c_driver max1619_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max1619",
+ .of_match_table = of_match_ptr(max1619_of_match),
+ },
+ .probe_new = max1619_probe,
+ .id_table = max1619_id,
+ .detect = max1619_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(max1619_driver);
+
+MODULE_AUTHOR("Oleksij Rempel <bug-track@fisher-privat.net>, Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("MAX1619 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c
new file mode 100644
index 000000000..9f748973d
--- /dev/null
+++ b/drivers/hwmon/max1668.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2011 David George <david.george@ska.ac.za>
+ *
+ * based on adm1021.c
+ * some credit to Christoph Scheurer, but largely a rewrite
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short max1668_addr_list[] = {
+ 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* max1668 registers */
+
+#define MAX1668_REG_TEMP(nr) (nr)
+#define MAX1668_REG_STAT1 0x05
+#define MAX1668_REG_STAT2 0x06
+#define MAX1668_REG_MAN_ID 0xfe
+#define MAX1668_REG_DEV_ID 0xff
+
+/* limits */
+
+/* write high limits */
+#define MAX1668_REG_LIMH_WR(nr) (0x13 + 2 * (nr))
+/* write low limits */
+#define MAX1668_REG_LIML_WR(nr) (0x14 + 2 * (nr))
+/* read high limits */
+#define MAX1668_REG_LIMH_RD(nr) (0x08 + 2 * (nr))
+/* read low limits */
+#define MAX1668_REG_LIML_RD(nr) (0x09 + 2 * (nr))
+
+/* manufacturer and device ID Constants */
+#define MAN_ID_MAXIM 0x4d
+#define DEV_ID_MAX1668 0x3
+#define DEV_ID_MAX1805 0x5
+#define DEV_ID_MAX1989 0xb
+
+/* read only mode module parameter */
+static bool read_only;
+module_param(read_only, bool, 0);
+MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
+
+enum chips { max1668, max1805, max1989 };
+
+struct max1668_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+ enum chips type;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* 1x local and 4x remote */
+ s8 temp_max[5];
+ s8 temp_min[5];
+ s8 temp[5];
+ u16 alarms;
+};
+
+static struct max1668_data *max1668_update_device(struct device *dev)
+{
+ struct max1668_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct max1668_data *ret = data;
+ s32 val;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (data->valid && !time_after(jiffies,
+ data->last_updated + HZ + HZ / 2))
+ goto abort;
+
+ for (i = 0; i < 5; i++) {
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i] = (s8) val;
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_max[i] = (s8) val;
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_min[i] = (s8) val;
+ }
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->alarms = val << 8;
+
+ val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->alarms |= val;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temp[index] * 1000);
+}
+
+static ssize_t show_temp_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temp_max[index] * 1000);
+}
+
+static ssize_t show_temp_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temp_min[index] * 1000);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
+}
+
+static ssize_t show_fault(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = max1668_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n",
+ (data->alarms & (1 << 12)) && data->temp[index] == 127);
+}
+
+static ssize_t set_temp_max(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[index] = clamp_val(temp/1000, -128, 127);
+ ret = i2c_smbus_write_byte_data(client,
+ MAX1668_REG_LIMH_WR(index),
+ data->temp_max[index]);
+ if (ret < 0)
+ count = ret;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t set_temp_min(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max1668_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[index] = clamp_val(temp/1000, -128, 127);
+ ret = i2c_smbus_write_byte_data(client,
+ MAX1668_REG_LIML_WR(index),
+ data->temp_min[index]);
+ if (ret < 0)
+ count = ret;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max,
+ set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min,
+ set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max,
+ set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min,
+ set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max,
+ set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min,
+ set_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max,
+ set_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min,
+ set_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max,
+ set_temp_max, 4);
+static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min,
+ set_temp_min, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4);
+
+/* Attributes common to MAX1668, MAX1989 and MAX1805 */
+static struct attribute *max1668_attribute_common[] = {
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ NULL
+};
+
+/* Attributes not present on MAX1805 */
+static struct attribute *max1668_attribute_unique[] = {
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ NULL
+};
+
+static umode_t max1668_attribute_mode(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ umode_t ret = S_IRUGO;
+ if (read_only)
+ return ret;
+ if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp2_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp3_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp4_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp5_max.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp2_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp3_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp4_min.dev_attr.attr ||
+ attr == &sensor_dev_attr_temp5_min.dev_attr.attr)
+ ret |= S_IWUSR;
+ return ret;
+}
+
+static const struct attribute_group max1668_group_common = {
+ .attrs = max1668_attribute_common,
+ .is_visible = max1668_attribute_mode
+};
+
+static const struct attribute_group max1668_group_unique = {
+ .attrs = max1668_attribute_unique,
+ .is_visible = max1668_attribute_mode
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max1668_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ const char *type_name;
+ int man_id, dev_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Check for unsupported part */
+ man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID);
+ if (man_id != MAN_ID_MAXIM)
+ return -ENODEV;
+
+ dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID);
+ if (dev_id < 0)
+ return -ENODEV;
+
+ type_name = NULL;
+ if (dev_id == DEV_ID_MAX1668)
+ type_name = "max1668";
+ else if (dev_id == DEV_ID_MAX1805)
+ type_name = "max1805";
+ else if (dev_id == DEV_ID_MAX1989)
+ type_name = "max1989";
+
+ if (!type_name)
+ return -ENODEV;
+
+ strscpy(info->type, type_name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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;
+ struct device *hwmon_dev;
+ struct max1668_data *data;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct max1668_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->type = i2c_match_id(max1668_id, client)->driver_data;
+ mutex_init(&data->update_lock);
+
+ /* sysfs hooks */
+ data->groups[0] = &max1668_group_common;
+ if (data->type == max1668 || data->type == max1989)
+ data->groups[1] = &max1668_group_unique;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max1668_id[] = {
+ { "max1668", max1668 },
+ { "max1805", max1805 },
+ { "max1989", max1989 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max1668_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max1668_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max1668",
+ },
+ .probe_new = max1668_probe,
+ .id_table = max1668_id,
+ .detect = max1668_detect,
+ .address_list = max1668_addr_list,
+};
+
+module_i2c_driver(max1668_driver);
+
+MODULE_AUTHOR("David George <david.george@ska.ac.za>");
+MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c
new file mode 100644
index 000000000..56add579e
--- /dev/null
+++ b/drivers/hwmon/max197.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Maxim MAX197 A/D Converter driver
+ *
+ * Copyright (c) 2012 Savoir-faire Linux Inc.
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
+ * For further information, see the Documentation/hwmon/max197.rst file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/max197.h>
+
+#define MAX199_LIMIT 4000 /* 4V */
+#define MAX197_LIMIT 10000 /* 10V */
+
+#define MAX197_NUM_CH 8 /* 8 Analog Input Channels */
+
+/* Control byte format */
+#define MAX197_BIP (1 << 3) /* Bipolarity */
+#define MAX197_RNG (1 << 4) /* Full range */
+
+#define MAX197_SCALE 12207 /* Scale coefficient for raw data */
+
+/* List of supported chips */
+enum max197_chips { max197, max199 };
+
+/**
+ * struct max197_data - device instance specific data
+ * @pdata: Platform data.
+ * @hwmon_dev: The hwmon device.
+ * @lock: Read/Write mutex.
+ * @limit: Max range value (10V for MAX197, 4V for MAX199).
+ * @scale: Need to scale.
+ * @ctrl_bytes: Channels control byte.
+ */
+struct max197_data {
+ struct max197_platform_data *pdata;
+ struct device *hwmon_dev;
+ struct mutex lock;
+ int limit;
+ bool scale;
+ u8 ctrl_bytes[MAX197_NUM_CH];
+};
+
+static inline void max197_set_unipolarity(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] &= ~MAX197_BIP;
+}
+
+static inline void max197_set_bipolarity(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] |= MAX197_BIP;
+}
+
+static inline void max197_set_half_range(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] &= ~MAX197_RNG;
+}
+
+static inline void max197_set_full_range(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] |= MAX197_RNG;
+}
+
+static inline bool max197_is_bipolar(struct max197_data *data, int channel)
+{
+ return data->ctrl_bytes[channel] & MAX197_BIP;
+}
+
+static inline bool max197_is_full_range(struct max197_data *data, int channel)
+{
+ return data->ctrl_bytes[channel] & MAX197_RNG;
+}
+
+/* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */
+static ssize_t max197_show_range(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct max197_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int channel = attr->index;
+ bool is_min = attr->nr;
+ int range;
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -ERESTARTSYS;
+
+ range = max197_is_full_range(data, channel) ?
+ data->limit : data->limit / 2;
+ if (is_min) {
+ if (max197_is_bipolar(data, channel))
+ range = -range;
+ else
+ range = 0;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return sprintf(buf, "%d\n", range);
+}
+
+/* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */
+static ssize_t max197_store_range(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct max197_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int channel = attr->index;
+ bool is_min = attr->nr;
+ long value;
+ int half = data->limit / 2;
+ int full = data->limit;
+
+ if (kstrtol(buf, 10, &value))
+ return -EINVAL;
+
+ if (is_min) {
+ if (value <= -full)
+ value = -full;
+ else if (value < 0)
+ value = -half;
+ else
+ value = 0;
+ } else {
+ if (value >= full)
+ value = full;
+ else
+ value = half;
+ }
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -ERESTARTSYS;
+
+ if (value == 0) {
+ /* We can deduce only the polarity */
+ max197_set_unipolarity(data, channel);
+ } else if (value == -half) {
+ max197_set_bipolarity(data, channel);
+ max197_set_half_range(data, channel);
+ } else if (value == -full) {
+ max197_set_bipolarity(data, channel);
+ max197_set_full_range(data, channel);
+ } else if (value == half) {
+ /* We can deduce only the range */
+ max197_set_half_range(data, channel);
+ } else if (value == full) {
+ /* We can deduce only the range */
+ max197_set_full_range(data, channel);
+ }
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/* Function called on read access on in{0,1,2,3,4,5,6,7}_input */
+static ssize_t max197_show_input(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct max197_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int channel = attr->index;
+ s32 value;
+ int ret;
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -ERESTARTSYS;
+
+ ret = data->pdata->convert(data->ctrl_bytes[channel]);
+ if (ret < 0) {
+ dev_err(dev, "conversion failed\n");
+ goto unlock;
+ }
+ value = ret;
+
+ /*
+ * Coefficient to apply on raw value.
+ * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet.
+ */
+ if (data->scale) {
+ value *= MAX197_SCALE;
+ if (max197_is_full_range(data, channel))
+ value *= 2;
+ value /= 10000;
+ }
+
+ ret = sprintf(buf, "%d\n", value);
+
+unlock:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ return sprintf(buf, "%s\n", pdev->name);
+}
+
+#define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \
+ static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \
+ max197_show_input, NULL, chan); \
+ static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \
+ max197_show_range, \
+ max197_store_range, \
+ true, chan); \
+ static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \
+ max197_show_range, \
+ max197_store_range, \
+ false, chan)
+
+#define MAX197_SENSOR_DEV_ATTR_IN(chan) \
+ &sensor_dev_attr_in##chan##_input.dev_attr.attr, \
+ &sensor_dev_attr_in##chan##_max.dev_attr.attr, \
+ &sensor_dev_attr_in##chan##_min.dev_attr.attr
+
+static DEVICE_ATTR_RO(name);
+
+MAX197_SENSOR_DEVICE_ATTR_CH(0);
+MAX197_SENSOR_DEVICE_ATTR_CH(1);
+MAX197_SENSOR_DEVICE_ATTR_CH(2);
+MAX197_SENSOR_DEVICE_ATTR_CH(3);
+MAX197_SENSOR_DEVICE_ATTR_CH(4);
+MAX197_SENSOR_DEVICE_ATTR_CH(5);
+MAX197_SENSOR_DEVICE_ATTR_CH(6);
+MAX197_SENSOR_DEVICE_ATTR_CH(7);
+
+static const struct attribute_group max197_sysfs_group = {
+ .attrs = (struct attribute *[]) {
+ &dev_attr_name.attr,
+ MAX197_SENSOR_DEV_ATTR_IN(0),
+ MAX197_SENSOR_DEV_ATTR_IN(1),
+ MAX197_SENSOR_DEV_ATTR_IN(2),
+ MAX197_SENSOR_DEV_ATTR_IN(3),
+ MAX197_SENSOR_DEV_ATTR_IN(4),
+ MAX197_SENSOR_DEV_ATTR_IN(5),
+ MAX197_SENSOR_DEV_ATTR_IN(6),
+ MAX197_SENSOR_DEV_ATTR_IN(7),
+ NULL
+ },
+};
+
+static int max197_probe(struct platform_device *pdev)
+{
+ int ch, ret;
+ struct max197_data *data;
+ struct max197_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ enum max197_chips chip = platform_get_device_id(pdev)->driver_data;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ if (pdata->convert == NULL) {
+ dev_err(&pdev->dev, "no convert function supplied\n");
+ return -EINVAL;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->pdata = pdata;
+ mutex_init(&data->lock);
+
+ if (chip == max197) {
+ data->limit = MAX197_LIMIT;
+ data->scale = true;
+ } else {
+ data->limit = MAX199_LIMIT;
+ data->scale = false;
+ }
+
+ for (ch = 0; ch < MAX197_NUM_CH; ch++)
+ data->ctrl_bytes[ch] = (u8) ch;
+
+ platform_set_drvdata(pdev, data);
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group);
+ if (ret) {
+ dev_err(&pdev->dev, "sysfs create group failed\n");
+ return ret;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "hwmon device register failed\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group);
+ return ret;
+}
+
+static int max197_remove(struct platform_device *pdev)
+{
+ struct max197_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group);
+
+ return 0;
+}
+
+static const struct platform_device_id max197_device_ids[] = {
+ { "max197", max197 },
+ { "max199", max199 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, max197_device_ids);
+
+static struct platform_driver max197_driver = {
+ .driver = {
+ .name = "max197",
+ },
+ .probe = max197_probe,
+ .remove = max197_remove,
+ .id_table = max197_device_ids,
+};
+module_platform_driver(max197_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
+MODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver");
diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c
new file mode 100644
index 000000000..9a31ef388
--- /dev/null
+++ b/drivers/hwmon/max31722.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * max31722 - hwmon driver for Maxim Integrated MAX31722/MAX31723 SPI
+ * digital thermometer and thermostats.
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#define MAX31722_REG_CFG 0x00
+#define MAX31722_REG_TEMP_LSB 0x01
+
+#define MAX31722_MODE_CONTINUOUS 0x00
+#define MAX31722_MODE_STANDBY 0x01
+#define MAX31722_MODE_MASK 0xFE
+#define MAX31722_RESOLUTION_12BIT 0x06
+#define MAX31722_WRITE_MASK 0x80
+
+struct max31722_data {
+ struct device *hwmon_dev;
+ struct spi_device *spi_device;
+ u8 mode;
+};
+
+static int max31722_set_mode(struct max31722_data *data, u8 mode)
+{
+ int ret;
+ struct spi_device *spi = data->spi_device;
+ u8 buf[2] = {
+ MAX31722_REG_CFG | MAX31722_WRITE_MASK,
+ (data->mode & MAX31722_MODE_MASK) | mode
+ };
+
+ ret = spi_write(spi, &buf, sizeof(buf));
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to set sensor mode.\n");
+ return ret;
+ }
+ data->mode = (data->mode & MAX31722_MODE_MASK) | mode;
+
+ return 0;
+}
+
+static ssize_t max31722_temp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+ struct max31722_data *data = dev_get_drvdata(dev);
+
+ ret = spi_w8r16(data->spi_device, MAX31722_REG_TEMP_LSB);
+ if (ret < 0)
+ return ret;
+ /* Keep 12 bits and multiply by the scale of 62.5 millidegrees/bit. */
+ return sprintf(buf, "%d\n", (s16)le16_to_cpu(ret) * 125 / 32);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, max31722_temp, 0);
+
+static struct attribute *max31722_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(max31722);
+
+static int max31722_probe(struct spi_device *spi)
+{
+ int ret;
+ struct max31722_data *data;
+
+ data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, data);
+ data->spi_device = spi;
+ /*
+ * Set SD bit to 0 so we can have continuous measurements.
+ * Set resolution to 12 bits for maximum precision.
+ */
+ data->mode = MAX31722_MODE_CONTINUOUS | MAX31722_RESOLUTION_12BIT;
+ ret = max31722_set_mode(data, MAX31722_MODE_CONTINUOUS);
+ if (ret < 0)
+ return ret;
+
+ data->hwmon_dev = hwmon_device_register_with_groups(&spi->dev,
+ spi->modalias,
+ data,
+ max31722_groups);
+ if (IS_ERR(data->hwmon_dev)) {
+ max31722_set_mode(data, MAX31722_MODE_STANDBY);
+ return PTR_ERR(data->hwmon_dev);
+ }
+
+ return 0;
+}
+
+static void max31722_remove(struct spi_device *spi)
+{
+ struct max31722_data *data = spi_get_drvdata(spi);
+ int ret;
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ 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 max31722_suspend(struct device *dev)
+{
+ struct spi_device *spi_device = to_spi_device(dev);
+ struct max31722_data *data = spi_get_drvdata(spi_device);
+
+ return max31722_set_mode(data, MAX31722_MODE_STANDBY);
+}
+
+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);
+
+ return max31722_set_mode(data, MAX31722_MODE_CONTINUOUS);
+}
+
+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},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, max31722_spi_id);
+
+static struct spi_driver max31722_driver = {
+ .driver = {
+ .name = "max31722",
+ .pm = pm_sleep_ptr(&max31722_pm_ops),
+ },
+ .probe = max31722_probe,
+ .remove = max31722_remove,
+ .id_table = max31722_spi_id,
+};
+
+module_spi_driver(max31722_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("max31722 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c
new file mode 100644
index 000000000..746a767c9
--- /dev/null
+++ b/drivers/hwmon/max31730.c
@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for MAX31730 3-Channel Remote Temperature Sensor
+ *
+ * Copyright (c) 2019 Guenter Roeck <linux@roeck-us.net>
+ */
+
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x1c, 0x1d, 0x1e, 0x1f, 0x4c,
+ 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
+
+/* The MAX31730 registers */
+#define MAX31730_REG_TEMP 0x00
+#define MAX31730_REG_CONF 0x13
+#define MAX31730_STOP BIT(7)
+#define MAX31730_EXTRANGE BIT(1)
+#define MAX31730_REG_TEMP_OFFSET 0x16
+#define MAX31730_TEMP_OFFSET_BASELINE 0x77
+#define MAX31730_REG_OFFSET_ENABLE 0x17
+#define MAX31730_REG_TEMP_MAX 0x20
+#define MAX31730_REG_TEMP_MIN 0x30
+#define MAX31730_REG_STATUS_HIGH 0x32
+#define MAX31730_REG_STATUS_LOW 0x33
+#define MAX31730_REG_CHANNEL_ENABLE 0x35
+#define MAX31730_REG_TEMP_FAULT 0x36
+
+#define MAX31730_REG_MFG_ID 0x50
+#define MAX31730_MFG_ID 0x4d
+#define MAX31730_REG_MFG_REV 0x51
+#define MAX31730_MFG_REV 0x01
+
+#define MAX31730_TEMP_MIN (-128000)
+#define MAX31730_TEMP_MAX 127937
+
+/* Each client has this additional data */
+struct max31730_data {
+ struct i2c_client *client;
+ u8 orig_conf;
+ u8 current_conf;
+ u8 offset_enable;
+ u8 channel_enable;
+};
+
+/*-----------------------------------------------------------------------*/
+
+static inline long max31730_reg_to_mc(s16 temp)
+{
+ return DIV_ROUND_CLOSEST((temp >> 4) * 1000, 16);
+}
+
+static int max31730_write_config(struct max31730_data *data, u8 set_mask,
+ u8 clr_mask)
+{
+ u8 value;
+
+ clr_mask |= MAX31730_EXTRANGE;
+ value = data->current_conf & ~clr_mask;
+ value |= set_mask;
+
+ if (data->current_conf != value) {
+ s32 err;
+
+ err = i2c_smbus_write_byte_data(data->client, MAX31730_REG_CONF,
+ value);
+ if (err)
+ return err;
+ data->current_conf = value;
+ }
+ return 0;
+}
+
+static int max31730_set_enable(struct i2c_client *client, int reg,
+ u8 *confdata, int channel, bool enable)
+{
+ u8 regval = *confdata;
+ int err;
+
+ if (enable)
+ regval |= BIT(channel);
+ else
+ regval &= ~BIT(channel);
+
+ if (regval != *confdata) {
+ err = i2c_smbus_write_byte_data(client, reg, regval);
+ if (err)
+ return err;
+ *confdata = regval;
+ }
+ return 0;
+}
+
+static int max31730_set_offset_enable(struct max31730_data *data, int channel,
+ bool enable)
+{
+ return max31730_set_enable(data->client, MAX31730_REG_OFFSET_ENABLE,
+ &data->offset_enable, channel, enable);
+}
+
+static int max31730_set_channel_enable(struct max31730_data *data, int channel,
+ bool enable)
+{
+ return max31730_set_enable(data->client, MAX31730_REG_CHANNEL_ENABLE,
+ &data->channel_enable, channel, enable);
+}
+
+static int max31730_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct max31730_data *data = dev_get_drvdata(dev);
+ int regval, reg, offset;
+
+ if (type != hwmon_temp)
+ return -EINVAL;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ if (!(data->channel_enable & BIT(channel)))
+ return -ENODATA;
+ reg = MAX31730_REG_TEMP + (channel * 2);
+ break;
+ case hwmon_temp_max:
+ reg = MAX31730_REG_TEMP_MAX + (channel * 2);
+ break;
+ case hwmon_temp_min:
+ reg = MAX31730_REG_TEMP_MIN;
+ break;
+ case hwmon_temp_enable:
+ *val = !!(data->channel_enable & BIT(channel));
+ return 0;
+ case hwmon_temp_offset:
+ if (!channel)
+ return -EINVAL;
+ if (!(data->offset_enable & BIT(channel))) {
+ *val = 0;
+ return 0;
+ }
+ offset = i2c_smbus_read_byte_data(data->client,
+ MAX31730_REG_TEMP_OFFSET);
+ if (offset < 0)
+ return offset;
+ *val = (offset - MAX31730_TEMP_OFFSET_BASELINE) * 125;
+ return 0;
+ case hwmon_temp_fault:
+ regval = i2c_smbus_read_byte_data(data->client,
+ MAX31730_REG_TEMP_FAULT);
+ if (regval < 0)
+ return regval;
+ *val = !!(regval & BIT(channel));
+ return 0;
+ case hwmon_temp_min_alarm:
+ regval = i2c_smbus_read_byte_data(data->client,
+ MAX31730_REG_STATUS_LOW);
+ if (regval < 0)
+ return regval;
+ *val = !!(regval & BIT(channel));
+ return 0;
+ case hwmon_temp_max_alarm:
+ regval = i2c_smbus_read_byte_data(data->client,
+ MAX31730_REG_STATUS_HIGH);
+ if (regval < 0)
+ return regval;
+ *val = !!(regval & BIT(channel));
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ regval = i2c_smbus_read_word_swapped(data->client, reg);
+ if (regval < 0)
+ return regval;
+
+ *val = max31730_reg_to_mc(regval);
+
+ return 0;
+}
+
+static int max31730_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct max31730_data *data = dev_get_drvdata(dev);
+ int reg, err;
+
+ if (type != hwmon_temp)
+ return -EINVAL;
+
+ switch (attr) {
+ case hwmon_temp_max:
+ reg = MAX31730_REG_TEMP_MAX + channel * 2;
+ break;
+ case hwmon_temp_min:
+ reg = MAX31730_REG_TEMP_MIN;
+ break;
+ case hwmon_temp_enable:
+ if (val != 0 && val != 1)
+ return -EINVAL;
+ return max31730_set_channel_enable(data, channel, val);
+ case hwmon_temp_offset:
+ val = clamp_val(val, -14875, 17000) + 14875;
+ val = DIV_ROUND_CLOSEST(val, 125);
+ err = max31730_set_offset_enable(data, channel,
+ val != MAX31730_TEMP_OFFSET_BASELINE);
+ if (err)
+ return err;
+ return i2c_smbus_write_byte_data(data->client,
+ MAX31730_REG_TEMP_OFFSET, val);
+ default:
+ return -EINVAL;
+ }
+
+ val = clamp_val(val, MAX31730_TEMP_MIN, MAX31730_TEMP_MAX);
+ val = DIV_ROUND_CLOSEST(val << 4, 1000) << 4;
+
+ return i2c_smbus_write_word_swapped(data->client, reg, (u16)val);
+}
+
+static umode_t max31730_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_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_fault:
+ return 0444;
+ case hwmon_temp_min:
+ return channel ? 0444 : 0644;
+ case hwmon_temp_offset:
+ case hwmon_temp_enable:
+ case hwmon_temp_max:
+ return 0644;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *max31730_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_ENABLE |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_OFFSET | HWMON_T_ENABLE |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
+ HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_OFFSET | HWMON_T_ENABLE |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
+ HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_OFFSET | HWMON_T_ENABLE |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
+ HWMON_T_FAULT
+ ),
+ NULL
+};
+
+static const struct hwmon_ops max31730_hwmon_ops = {
+ .is_visible = max31730_is_visible,
+ .read = max31730_read,
+ .write = max31730_write,
+};
+
+static const struct hwmon_chip_info max31730_chip_info = {
+ .ops = &max31730_hwmon_ops,
+ .info = max31730_info,
+};
+
+static void max31730_remove(void *data)
+{
+ struct max31730_data *max31730 = data;
+ struct i2c_client *client = max31730->client;
+
+ i2c_smbus_write_byte_data(client, MAX31730_REG_CONF,
+ max31730->orig_conf);
+}
+
+static int
+max31730_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct max31730_data *data;
+ int status, err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+ return -EIO;
+
+ data = devm_kzalloc(dev, sizeof(struct max31730_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+
+ /* Cache original configuration and enable status */
+ status = i2c_smbus_read_byte_data(client, MAX31730_REG_CHANNEL_ENABLE);
+ if (status < 0)
+ return status;
+ data->channel_enable = status;
+
+ status = i2c_smbus_read_byte_data(client, MAX31730_REG_OFFSET_ENABLE);
+ if (status < 0)
+ return status;
+ data->offset_enable = status;
+
+ status = i2c_smbus_read_byte_data(client, MAX31730_REG_CONF);
+ if (status < 0)
+ return status;
+ data->orig_conf = status;
+ data->current_conf = status;
+
+ err = max31730_write_config(data,
+ data->channel_enable ? 0 : MAX31730_STOP,
+ data->channel_enable ? MAX31730_STOP : 0);
+ if (err)
+ return err;
+
+ dev_set_drvdata(dev, data);
+
+ err = devm_add_action_or_reset(dev, max31730_remove, data);
+ if (err)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &max31730_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max31730_ids[] = {
+ { "max31730", 0, },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max31730_ids);
+
+static const struct of_device_id __maybe_unused max31730_of_match[] = {
+ {
+ .compatible = "maxim,max31730",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max31730_of_match);
+
+static bool max31730_check_reg_temp(struct i2c_client *client,
+ int reg)
+{
+ int regval;
+
+ regval = i2c_smbus_read_byte_data(client, reg + 1);
+ return regval < 0 || (regval & 0x0f);
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max31730_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int regval;
+ int i;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, MAX31730_REG_MFG_ID);
+ if (regval != MAX31730_MFG_ID)
+ return -ENODEV;
+ regval = i2c_smbus_read_byte_data(client, MAX31730_REG_MFG_REV);
+ if (regval != MAX31730_MFG_REV)
+ return -ENODEV;
+
+ /* lower 4 bit of temperature and limit registers must be 0 */
+ if (max31730_check_reg_temp(client, MAX31730_REG_TEMP_MIN))
+ return -ENODEV;
+
+ for (i = 0; i < 4; i++) {
+ if (max31730_check_reg_temp(client, MAX31730_REG_TEMP + i * 2))
+ return -ENODEV;
+ if (max31730_check_reg_temp(client,
+ MAX31730_REG_TEMP_MAX + i * 2))
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "max31730", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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 max31730_resume(struct device *dev)
+{
+ struct max31730_data *data = dev_get_drvdata(dev);
+
+ return max31730_write_config(data, 0, MAX31730_STOP);
+}
+
+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 = pm_sleep_ptr(&max31730_pm_ops),
+ },
+ .probe_new = max31730_probe,
+ .id_table = max31730_ids,
+ .detect = max31730_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(max31730_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("MAX31730 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max31760.c b/drivers/hwmon/max31760.c
new file mode 100644
index 000000000..06d5f39dc
--- /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, &regval);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(STATUS_RDFA, regval);
+
+ return 0;
+ case hwmon_temp_max_alarm:
+ ret = regmap_read(state->regmap, REG_STATUS, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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, &regval);
+ if (ret)
+ return ret;
+
+ *val = regval;
+
+ return 0;
+ case hwmon_pwm_freq:
+ ret = regmap_read(state->regmap, REG_CR1, &regval);
+ 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, &regval);
+ 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, &regval);
+ 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), &regval);
+ 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, &regval);
+ 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, &regmap_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
new file mode 100644
index 000000000..20bf5ffad
--- /dev/null
+++ b/drivers/hwmon/max31790.c
@@ -0,0 +1,558 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max31790.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring.
+ *
+ * (C) 2015 by Il Han <corone.il.han@gmail.com>
+ */
+
+#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>
+
+/* MAX31790 registers */
+#define MAX31790_REG_GLOBAL_CONFIG 0x00
+#define MAX31790_REG_FAN_CONFIG(ch) (0x02 + (ch))
+#define MAX31790_REG_FAN_DYNAMICS(ch) (0x08 + (ch))
+#define MAX31790_REG_FAN_FAULT_STATUS2 0x10
+#define MAX31790_REG_FAN_FAULT_STATUS1 0x11
+#define MAX31790_REG_TACH_COUNT(ch) (0x18 + (ch) * 2)
+#define MAX31790_REG_PWM_DUTY_CYCLE(ch) (0x30 + (ch) * 2)
+#define MAX31790_REG_PWMOUT(ch) (0x40 + (ch) * 2)
+#define MAX31790_REG_TARGET_COUNT(ch) (0x50 + (ch) * 2)
+
+/* 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
+
+/* Fan Dynamics register bits */
+#define MAX31790_FAN_DYN_SR_SHIFT 5
+#define MAX31790_FAN_DYN_SR_MASK 0xE0
+#define SR_FROM_REG(reg) (((reg) & MAX31790_FAN_DYN_SR_MASK) \
+ >> MAX31790_FAN_DYN_SR_SHIFT)
+
+#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)
+#define RPM_TO_REG(rpm, sr) ((60 * (sr) * 8192) / ((rpm) * 2))
+
+#define NR_CHANNEL 6
+
+/*
+ * Client data (each client gets its own)
+ */
+struct max31790_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* register values */
+ u8 fan_config[NR_CHANNEL];
+ u8 fan_dynamics[NR_CHANNEL];
+ u16 fault_status;
+ u16 tach[NR_CHANNEL * 2];
+ u16 pwm[NR_CHANNEL];
+ u16 target_count[NR_CHANNEL];
+};
+
+static struct max31790_data *max31790_update_device(struct device *dev)
+{
+ struct max31790_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct max31790_data *ret = data;
+ int i;
+ int rv;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ rv = i2c_smbus_read_byte_data(client,
+ MAX31790_REG_FAN_FAULT_STATUS1);
+ if (rv < 0)
+ goto abort;
+ data->fault_status |= rv & 0x3F;
+
+ rv = i2c_smbus_read_byte_data(client,
+ MAX31790_REG_FAN_FAULT_STATUS2);
+ if (rv < 0)
+ goto abort;
+ data->fault_status |= (rv & 0x3F) << 6;
+
+ for (i = 0; i < NR_CHANNEL; i++) {
+ rv = i2c_smbus_read_word_swapped(client,
+ MAX31790_REG_TACH_COUNT(i));
+ if (rv < 0)
+ goto abort;
+ data->tach[i] = rv;
+
+ if (data->fan_config[i]
+ & MAX31790_FAN_CFG_TACH_INPUT) {
+ rv = i2c_smbus_read_word_swapped(client,
+ MAX31790_REG_TACH_COUNT(NR_CHANNEL
+ + i));
+ if (rv < 0)
+ goto abort;
+ data->tach[NR_CHANNEL + i] = rv;
+ } else {
+ rv = i2c_smbus_read_word_swapped(client,
+ MAX31790_REG_PWM_DUTY_CYCLE(i));
+ if (rv < 0)
+ goto abort;
+ data->pwm[i] = rv;
+
+ rv = i2c_smbus_read_word_swapped(client,
+ MAX31790_REG_TARGET_COUNT(i));
+ if (rv < 0)
+ goto abort;
+ data->target_count[i] = rv;
+ }
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+ goto done;
+
+abort:
+ data->valid = false;
+ ret = ERR_PTR(rv);
+
+done:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 };
+
+static u8 get_tach_period(u8 fan_dynamics)
+{
+ return tach_period[SR_FROM_REG(fan_dynamics)];
+}
+
+static u8 bits_for_tach_period(int rpm)
+{
+ u8 bits;
+
+ if (rpm < 500)
+ bits = 0x0;
+ else if (rpm < 1000)
+ bits = 0x1;
+ else if (rpm < 2000)
+ bits = 0x2;
+ else if (rpm < 4000)
+ bits = 0x3;
+ else if (rpm < 8000)
+ bits = 0x4;
+ else
+ bits = 0x5;
+
+ return bits;
+}
+
+static int max31790_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct max31790_data *data = max31790_update_device(dev);
+ int sr, rpm;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ switch (attr) {
+ case hwmon_fan_input:
+ 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:
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ rpm = RPM_FROM_REG(data->target_count[channel], sr);
+ *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;
+ }
+}
+
+static int max31790_write_fan(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct max31790_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int target_count;
+ int err = 0;
+ u8 bits, fan_config;
+ int sr;
+
+ mutex_lock(&data->update_lock);
+
+ switch (attr) {
+ case hwmon_fan_target:
+ val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX);
+ bits = bits_for_tach_period(val);
+ data->fan_dynamics[channel] =
+ ((data->fan_dynamics[channel] &
+ ~MAX31790_FAN_DYN_SR_MASK) |
+ (bits << MAX31790_FAN_DYN_SR_SHIFT));
+ err = i2c_smbus_write_byte_data(client,
+ MAX31790_REG_FAN_DYNAMICS(channel),
+ data->fan_dynamics[channel]);
+ if (err < 0)
+ break;
+
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ target_count = RPM_TO_REG(val, sr);
+ target_count = clamp_val(target_count, 0x1, 0x7FF);
+
+ data->target_count[channel] = target_count << 5;
+
+ err = i2c_smbus_write_word_swapped(client,
+ 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;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct max31790_data *data = _data;
+ u8 fan_config = data->fan_config[channel % NR_CHANNEL];
+
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_fault:
+ if (channel < NR_CHANNEL ||
+ (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return 0444;
+ return 0;
+ case hwmon_fan_target:
+ if (channel < NR_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;
+ }
+}
+
+static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct max31790_data *data = max31790_update_device(dev);
+ u8 fan_config;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ fan_config = data->fan_config[channel];
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = data->pwm[channel] >> 8;
+ return 0;
+ case hwmon_pwm_enable:
+ if (fan_config & MAX31790_FAN_CFG_CTRL_MON)
+ *val = 0;
+ else if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
+ *val = 2;
+ else
+ *val = 1;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct max31790_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 fan_config;
+ int err = 0;
+
+ mutex_lock(&data->update_lock);
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > 255) {
+ err = -EINVAL;
+ break;
+ }
+ data->valid = false;
+ err = i2c_smbus_write_word_swapped(client,
+ MAX31790_REG_PWMOUT(channel),
+ val << 8);
+ break;
+ case hwmon_pwm_enable:
+ fan_config = data->fan_config[channel];
+ if (val == 0) {
+ 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 &= ~(MAX31790_FAN_CFG_CTRL_MON | MAX31790_FAN_CFG_RPM_MODE);
+ } else if (val == 2) {
+ 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;
+ }
+ 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;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static umode_t max31790_pwm_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct max31790_data *data = _data;
+ u8 fan_config = data->fan_config[channel];
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ if (!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return 0644;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static int max31790_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_fan:
+ return max31790_read_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return max31790_read_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int max31790_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_fan:
+ return max31790_write_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return max31790_write_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t max31790_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ return max31790_fan_is_visible(data, attr, channel);
+ case hwmon_pwm:
+ return max31790_pwm_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info *max31790_info[] = {
+ HWMON_CHANNEL_INFO(fan,
+ 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,
+ 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_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
+ NULL
+};
+
+static const struct hwmon_ops max31790_hwmon_ops = {
+ .is_visible = max31790_is_visible,
+ .read = max31790_read,
+ .write = max31790_write,
+};
+
+static const struct hwmon_chip_info max31790_chip_info = {
+ .ops = &max31790_hwmon_ops,
+ .info = max31790_info,
+};
+
+static int max31790_init_client(struct i2c_client *client,
+ struct max31790_data *data)
+{
+ int i, rv;
+
+ for (i = 0; i < NR_CHANNEL; i++) {
+ rv = i2c_smbus_read_byte_data(client,
+ MAX31790_REG_FAN_CONFIG(i));
+ if (rv < 0)
+ return rv;
+ data->fan_config[i] = rv;
+
+ rv = i2c_smbus_read_byte_data(client,
+ MAX31790_REG_FAN_DYNAMICS(i));
+ if (rv < 0)
+ return rv;
+ data->fan_dynamics[i] = rv;
+ }
+
+ return 0;
+}
+
+static int max31790_probe(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct device *dev = &client->dev;
+ struct max31790_data *data;
+ struct device *hwmon_dev;
+ int err;
+
+ if (!i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct max31790_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /*
+ * Initialize the max31790 chip
+ */
+ err = max31790_init_client(client, data);
+ if (err)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &max31790_chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max31790_id[] = {
+ { "max31790", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max31790_id);
+
+static struct i2c_driver max31790_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe_new = max31790_probe,
+ .driver = {
+ .name = "max31790",
+ },
+ .id_table = max31790_id,
+};
+
+module_i2c_driver(max31790_driver);
+
+MODULE_AUTHOR("Il Han <corone.il.han@gmail.com>");
+MODULE_DESCRIPTION("MAX31790 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
new file mode 100644
index 000000000..202b64381
--- /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
new file mode 100644
index 000000000..7821132e1
--- /dev/null
+++ b/drivers/hwmon/max6621.c
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Maxim MAX6621
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#define MAX6621_DRV_NAME "max6621"
+#define MAX6621_TEMP_INPUT_REG_NUM 9
+#define MAX6621_TEMP_INPUT_MIN -127000
+#define MAX6621_TEMP_INPUT_MAX 128000
+#define MAX6621_TEMP_ALERT_CHAN_SHIFT 1
+
+#define MAX6621_TEMP_S0D0_REG 0x00
+#define MAX6621_TEMP_S0D1_REG 0x01
+#define MAX6621_TEMP_S1D0_REG 0x02
+#define MAX6621_TEMP_S1D1_REG 0x03
+#define MAX6621_TEMP_S2D0_REG 0x04
+#define MAX6621_TEMP_S2D1_REG 0x05
+#define MAX6621_TEMP_S3D0_REG 0x06
+#define MAX6621_TEMP_S3D1_REG 0x07
+#define MAX6621_TEMP_MAX_REG 0x08
+#define MAX6621_TEMP_MAX_ADDR_REG 0x0a
+#define MAX6621_TEMP_ALERT_CAUSE_REG 0x0b
+#define MAX6621_CONFIG0_REG 0x0c
+#define MAX6621_CONFIG1_REG 0x0d
+#define MAX6621_CONFIG2_REG 0x0e
+#define MAX6621_CONFIG3_REG 0x0f
+#define MAX6621_TEMP_S0_ALERT_REG 0x10
+#define MAX6621_TEMP_S1_ALERT_REG 0x11
+#define MAX6621_TEMP_S2_ALERT_REG 0x12
+#define MAX6621_TEMP_S3_ALERT_REG 0x13
+#define MAX6621_CLEAR_ALERT_REG 0x15
+#define MAX6621_REG_MAX (MAX6621_CLEAR_ALERT_REG + 1)
+#define MAX6621_REG_TEMP_SHIFT 0x06
+
+#define MAX6621_ENABLE_TEMP_ALERTS_BIT 4
+#define MAX6621_ENABLE_I2C_CRC_BIT 5
+#define MAX6621_ENABLE_ALTERNATE_DATA 6
+#define MAX6621_ENABLE_LOCKUP_TO 7
+#define MAX6621_ENABLE_S0D0_BIT 8
+#define MAX6621_ENABLE_S3D1_BIT 15
+#define MAX6621_ENABLE_TEMP_ALL GENMASK(MAX6621_ENABLE_S3D1_BIT, \
+ MAX6621_ENABLE_S0D0_BIT)
+#define MAX6621_POLL_DELAY_MASK 0x5
+#define MAX6621_CONFIG0_INIT (MAX6621_ENABLE_TEMP_ALL | \
+ BIT(MAX6621_ENABLE_LOCKUP_TO) | \
+ BIT(MAX6621_ENABLE_I2C_CRC_BIT) | \
+ MAX6621_POLL_DELAY_MASK)
+#define MAX6621_PECI_BIT_TIME 0x2
+#define MAX6621_PECI_RETRY_NUM 0x3
+#define MAX6621_CONFIG1_INIT ((MAX6621_PECI_BIT_TIME << 8) | \
+ MAX6621_PECI_RETRY_NUM)
+
+/* Error codes */
+#define MAX6621_TRAN_FAILED 0x8100 /*
+ * PECI transaction failed for more
+ * than the configured number of
+ * consecutive retries.
+ */
+#define MAX6621_POOL_DIS 0x8101 /*
+ * Polling disabled for requested
+ * socket/domain.
+ */
+#define MAX6621_POOL_UNCOMPLETE 0x8102 /*
+ * First poll not yet completed for
+ * requested socket/domain (on
+ * startup).
+ */
+#define MAX6621_SD_DIS 0x8103 /*
+ * Read maximum temperature requested,
+ * but no sockets/domains enabled or
+ * all enabled sockets/domains have
+ * errors; or read maximum temperature
+ * address requested, but read maximum
+ * temperature was not called.
+ */
+#define MAX6621_ALERT_DIS 0x8104 /*
+ * Get alert socket/domain requested,
+ * but no alert active.
+ */
+#define MAX6621_PECI_ERR_MIN 0x8000 /* Intel spec PECI error min value. */
+#define MAX6621_PECI_ERR_MAX 0x80ff /* Intel spec PECI error max value. */
+
+static const u32 max6621_temp_regs[] = {
+ MAX6621_TEMP_MAX_REG, MAX6621_TEMP_S0D0_REG, MAX6621_TEMP_S1D0_REG,
+ MAX6621_TEMP_S2D0_REG, MAX6621_TEMP_S3D0_REG, MAX6621_TEMP_S0D1_REG,
+ MAX6621_TEMP_S1D1_REG, MAX6621_TEMP_S2D1_REG, MAX6621_TEMP_S3D1_REG,
+};
+
+static const char *const max6621_temp_labels[] = {
+ "maximum",
+ "socket0_0",
+ "socket1_0",
+ "socket2_0",
+ "socket3_0",
+ "socket0_1",
+ "socket1_1",
+ "socket2_1",
+ "socket3_1",
+};
+
+static const int max6621_temp_alert_chan2reg[] = {
+ MAX6621_TEMP_S0_ALERT_REG,
+ MAX6621_TEMP_S1_ALERT_REG,
+ MAX6621_TEMP_S2_ALERT_REG,
+ MAX6621_TEMP_S3_ALERT_REG,
+};
+
+/**
+ * struct max6621_data - private data:
+ *
+ * @client: I2C client;
+ * @regmap: register map handle;
+ * @input_chan2reg: mapping from channel to register;
+ */
+struct max6621_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ int input_chan2reg[MAX6621_TEMP_INPUT_REG_NUM + 1];
+};
+
+static long max6621_temp_mc2reg(long val)
+{
+ return (val / 1000L) << MAX6621_REG_TEMP_SHIFT;
+}
+
+static umode_t
+max6621_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ /* Skip channels which are not physically conncted. */
+ if (((struct max6621_data *)data)->input_chan2reg[channel] < 0)
+ return 0;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_label:
+ case hwmon_temp_crit_alarm:
+ return 0444;
+ case hwmon_temp_offset:
+ case hwmon_temp_crit:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int max6621_verify_reg_data(struct device *dev, int regval)
+{
+ if (regval >= MAX6621_PECI_ERR_MIN &&
+ regval <= MAX6621_PECI_ERR_MAX) {
+ dev_dbg(dev, "PECI error code - err 0x%04x.\n",
+ regval);
+
+ return -EIO;
+ }
+
+ switch (regval) {
+ case MAX6621_TRAN_FAILED:
+ dev_dbg(dev, "PECI transaction failed - err 0x%04x.\n",
+ regval);
+ return -EIO;
+ case MAX6621_POOL_DIS:
+ dev_dbg(dev, "Polling disabled - err 0x%04x.\n", regval);
+ return -EOPNOTSUPP;
+ case MAX6621_POOL_UNCOMPLETE:
+ dev_dbg(dev, "First poll not completed on startup - err 0x%04x.\n",
+ regval);
+ return -EIO;
+ case MAX6621_SD_DIS:
+ dev_dbg(dev, "Resource is disabled - err 0x%04x.\n", regval);
+ return -EOPNOTSUPP;
+ case MAX6621_ALERT_DIS:
+ dev_dbg(dev, "No alert active - err 0x%04x.\n", regval);
+ return -EOPNOTSUPP;
+ default:
+ return 0;
+ }
+}
+
+static int
+max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, long *val)
+{
+ struct max6621_data *data = dev_get_drvdata(dev);
+ u32 regval;
+ int reg;
+ s8 temp;
+ int ret;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ reg = data->input_chan2reg[channel];
+ ret = regmap_read(data->regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ ret = max6621_verify_reg_data(dev, regval);
+ if (ret)
+ return ret;
+
+ /*
+ * Bit MAX6621_REG_TEMP_SHIFT represents 1 degree step.
+ * The temperature is given in two's complement and 8
+ * bits is used for the register conversion.
+ */
+ temp = (regval >> MAX6621_REG_TEMP_SHIFT);
+ *val = temp * 1000L;
+
+ break;
+ case hwmon_temp_offset:
+ ret = regmap_read(data->regmap, MAX6621_CONFIG2_REG,
+ &regval);
+ if (ret)
+ return ret;
+
+ ret = max6621_verify_reg_data(dev, regval);
+ if (ret)
+ return ret;
+
+ *val = (regval >> MAX6621_REG_TEMP_SHIFT) *
+ 1000L;
+
+ break;
+ case hwmon_temp_crit:
+ channel -= MAX6621_TEMP_ALERT_CHAN_SHIFT;
+ reg = max6621_temp_alert_chan2reg[channel];
+ ret = regmap_read(data->regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ ret = max6621_verify_reg_data(dev, regval);
+ if (ret)
+ return ret;
+
+ *val = regval * 1000L;
+
+ break;
+ case hwmon_temp_crit_alarm:
+ /*
+ * Set val to zero to recover the case, when reading
+ * MAX6621_TEMP_ALERT_CAUSE_REG results in for example
+ * MAX6621_ALERT_DIS. Reading will return with error,
+ * but in such case alarm should be returned as 0.
+ */
+ *val = 0;
+ ret = regmap_read(data->regmap,
+ MAX6621_TEMP_ALERT_CAUSE_REG,
+ &regval);
+ if (ret)
+ return ret;
+
+ ret = max6621_verify_reg_data(dev, regval);
+ if (ret) {
+ /* Do not report error if alert is disabled. */
+ if (regval == MAX6621_ALERT_DIS)
+ return 0;
+ else
+ return ret;
+ }
+
+ /*
+ * Clear the alert automatically, using send-byte
+ * smbus protocol for clearing alert.
+ */
+ if (regval) {
+ ret = i2c_smbus_write_byte(data->client,
+ MAX6621_CLEAR_ALERT_REG);
+ if (ret)
+ return ret;
+ }
+
+ *val = !!regval;
+
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int
+max6621_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, long val)
+{
+ struct max6621_data *data = dev_get_drvdata(dev);
+ u32 reg;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_offset:
+ /* Clamp to allowed range to prevent overflow. */
+ val = clamp_val(val, MAX6621_TEMP_INPUT_MIN,
+ MAX6621_TEMP_INPUT_MAX);
+ val = max6621_temp_mc2reg(val);
+
+ return regmap_write(data->regmap,
+ MAX6621_CONFIG2_REG, val);
+ case hwmon_temp_crit:
+ channel -= MAX6621_TEMP_ALERT_CHAN_SHIFT;
+ reg = max6621_temp_alert_chan2reg[channel];
+ /* Clamp to allowed range to prevent overflow. */
+ val = clamp_val(val, MAX6621_TEMP_INPUT_MIN,
+ MAX6621_TEMP_INPUT_MAX);
+ val = val / 1000L;
+
+ return regmap_write(data->regmap, reg, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int
+max6621_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_label:
+ *str = max6621_temp_labels[channel];
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static bool max6621_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX6621_CONFIG0_REG:
+ case MAX6621_CONFIG1_REG:
+ case MAX6621_CONFIG2_REG:
+ case MAX6621_CONFIG3_REG:
+ case MAX6621_TEMP_S0_ALERT_REG:
+ case MAX6621_TEMP_S1_ALERT_REG:
+ case MAX6621_TEMP_S2_ALERT_REG:
+ case MAX6621_TEMP_S3_ALERT_REG:
+ case MAX6621_TEMP_ALERT_CAUSE_REG:
+ return true;
+ }
+ return false;
+}
+
+static bool max6621_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX6621_TEMP_S0D0_REG:
+ case MAX6621_TEMP_S0D1_REG:
+ case MAX6621_TEMP_S1D0_REG:
+ case MAX6621_TEMP_S1D1_REG:
+ case MAX6621_TEMP_S2D0_REG:
+ case MAX6621_TEMP_S2D1_REG:
+ case MAX6621_TEMP_S3D0_REG:
+ case MAX6621_TEMP_S3D1_REG:
+ case MAX6621_TEMP_MAX_REG:
+ case MAX6621_TEMP_MAX_ADDR_REG:
+ case MAX6621_CONFIG0_REG:
+ case MAX6621_CONFIG1_REG:
+ case MAX6621_CONFIG2_REG:
+ case MAX6621_CONFIG3_REG:
+ case MAX6621_TEMP_S0_ALERT_REG:
+ case MAX6621_TEMP_S1_ALERT_REG:
+ case MAX6621_TEMP_S2_ALERT_REG:
+ case MAX6621_TEMP_S3_ALERT_REG:
+ return true;
+ }
+ return false;
+}
+
+static bool max6621_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX6621_TEMP_S0D0_REG:
+ case MAX6621_TEMP_S0D1_REG:
+ case MAX6621_TEMP_S1D0_REG:
+ case MAX6621_TEMP_S1D1_REG:
+ case MAX6621_TEMP_S2D0_REG:
+ case MAX6621_TEMP_S2D1_REG:
+ case MAX6621_TEMP_S3D0_REG:
+ case MAX6621_TEMP_S3D1_REG:
+ case MAX6621_TEMP_MAX_REG:
+ case MAX6621_TEMP_S0_ALERT_REG:
+ case MAX6621_TEMP_S1_ALERT_REG:
+ case MAX6621_TEMP_S2_ALERT_REG:
+ case MAX6621_TEMP_S3_ALERT_REG:
+ case MAX6621_TEMP_ALERT_CAUSE_REG:
+ return true;
+ }
+ return false;
+}
+
+static const struct reg_default max6621_regmap_default[] = {
+ { MAX6621_CONFIG0_REG, MAX6621_CONFIG0_INIT },
+ { MAX6621_CONFIG1_REG, MAX6621_CONFIG1_INIT },
+};
+
+static const struct regmap_config max6621_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = MAX6621_REG_MAX,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .cache_type = REGCACHE_FLAT,
+ .writeable_reg = max6621_writeable_reg,
+ .readable_reg = max6621_readable_reg,
+ .volatile_reg = max6621_volatile_reg,
+ .reg_defaults = max6621_regmap_default,
+ .num_reg_defaults = ARRAY_SIZE(max6621_regmap_default),
+};
+
+static const struct hwmon_channel_info *max6621_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | 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),
+ NULL
+};
+
+static const struct hwmon_ops max6621_hwmon_ops = {
+ .read = max6621_read,
+ .write = max6621_write,
+ .read_string = max6621_read_string,
+ .is_visible = max6621_is_visible,
+};
+
+static const struct hwmon_chip_info max6621_chip_info = {
+ .ops = &max6621_hwmon_ops,
+ .info = max6621_info,
+};
+
+static int max6621_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct max6621_data *data;
+ struct device *hwmon_dev;
+ int i;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &max6621_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+
+ /* Set CONFIG0 register masking temperature alerts and PEC. */
+ ret = regmap_write(data->regmap, MAX6621_CONFIG0_REG,
+ MAX6621_CONFIG0_INIT);
+ if (ret)
+ return ret;
+
+ /* Set CONFIG1 register for PEC access retry number. */
+ ret = regmap_write(data->regmap, MAX6621_CONFIG1_REG,
+ MAX6621_CONFIG1_INIT);
+ if (ret)
+ return ret;
+
+ /* Sync registers with hardware. */
+ regcache_mark_dirty(data->regmap);
+ ret = regcache_sync(data->regmap);
+ if (ret)
+ return ret;
+
+ /* Verify which temperature input registers are enabled. */
+ for (i = 0; i < MAX6621_TEMP_INPUT_REG_NUM; i++) {
+ ret = i2c_smbus_read_word_data(client, max6621_temp_regs[i]);
+ if (ret < 0)
+ return ret;
+ ret = max6621_verify_reg_data(dev, ret);
+ if (ret) {
+ data->input_chan2reg[i] = -1;
+ continue;
+ }
+
+ data->input_chan2reg[i] = max6621_temp_regs[i];
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &max6621_chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max6621_id[] = {
+ { MAX6621_DRV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6621_id);
+
+static const struct of_device_id __maybe_unused max6621_of_match[] = {
+ { .compatible = "maxim,max6621" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max6621_of_match);
+
+static struct i2c_driver max6621_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = MAX6621_DRV_NAME,
+ .of_match_table = of_match_ptr(max6621_of_match),
+ },
+ .probe_new = max6621_probe,
+ .id_table = max6621_id,
+};
+
+module_i2c_driver(max6621_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("Driver for Maxim MAX6621");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c
new file mode 100644
index 000000000..9b895402c
--- /dev/null
+++ b/drivers/hwmon/max6639.c
@@ -0,0 +1,637 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max6639.c - Support for Maxim MAX6639
+ *
+ * 2-Channel Temperature Monitor with Dual PWM Fan-Speed Controller
+ *
+ * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
+ *
+ * based on the initial MAX6639 support from semptian.net
+ * by He Changqing <hechangqing@semptian.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/max6639.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
+
+/* The MAX6639 registers, valid channel numbers: 0, 1 */
+#define MAX6639_REG_TEMP(ch) (0x00 + (ch))
+#define MAX6639_REG_STATUS 0x02
+#define MAX6639_REG_OUTPUT_MASK 0x03
+#define MAX6639_REG_GCONFIG 0x04
+#define MAX6639_REG_TEMP_EXT(ch) (0x05 + (ch))
+#define MAX6639_REG_ALERT_LIMIT(ch) (0x08 + (ch))
+#define MAX6639_REG_OT_LIMIT(ch) (0x0A + (ch))
+#define MAX6639_REG_THERM_LIMIT(ch) (0x0C + (ch))
+#define MAX6639_REG_FAN_CONFIG1(ch) (0x10 + (ch) * 4)
+#define MAX6639_REG_FAN_CONFIG2a(ch) (0x11 + (ch) * 4)
+#define MAX6639_REG_FAN_CONFIG2b(ch) (0x12 + (ch) * 4)
+#define MAX6639_REG_FAN_CONFIG3(ch) (0x13 + (ch) * 4)
+#define MAX6639_REG_FAN_CNT(ch) (0x20 + (ch))
+#define MAX6639_REG_TARGET_CNT(ch) (0x22 + (ch))
+#define MAX6639_REG_FAN_PPR(ch) (0x24 + (ch))
+#define MAX6639_REG_TARGTDUTY(ch) (0x26 + (ch))
+#define MAX6639_REG_FAN_START_TEMP(ch) (0x28 + (ch))
+#define MAX6639_REG_DEVID 0x3D
+#define MAX6639_REG_MANUID 0x3E
+#define MAX6639_REG_DEVREV 0x3F
+
+/* Register bits */
+#define MAX6639_GCONFIG_STANDBY 0x80
+#define MAX6639_GCONFIG_POR 0x40
+#define MAX6639_GCONFIG_DISABLE_TIMEOUT 0x20
+#define MAX6639_GCONFIG_CH2_LOCAL 0x10
+#define MAX6639_GCONFIG_PWM_FREQ_HI 0x08
+
+#define MAX6639_FAN_CONFIG1_PWM 0x80
+
+#define MAX6639_FAN_CONFIG3_THERM_FULL_SPEED 0x40
+
+static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 };
+
+#define FAN_FROM_REG(val, rpm_range) ((val) == 0 || (val) == 255 ? \
+ 0 : (rpm_ranges[rpm_range] * 30) / (val))
+#define TEMP_LIMIT_TO_REG(val) clamp_val((val) / 1000, 0, 255)
+
+/*
+ * Client data (each client gets its own)
+ */
+struct max6639_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Register values sampled regularly */
+ u16 temp[2]; /* Temperature, in 1/8 C, 0..255 C */
+ bool temp_fault[2]; /* Detected temperature diode failure */
+ u8 fan[2]; /* Register value: TACH count for fans >=30 */
+ u8 status; /* Detected channel alarms and fan failures */
+
+ /* Register values only written to */
+ u8 pwm[2]; /* Register value: Duty cycle 0..120 */
+ u8 temp_therm[2]; /* THERM Temperature, 0..255 C (->_max) */
+ u8 temp_alert[2]; /* ALERT Temperature, 0..255 C (->_crit) */
+ u8 temp_ot[2]; /* OT Temperature, 0..255 C (->_emergency) */
+
+ /* 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)
+{
+ struct max6639_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct max6639_data *ret = data;
+ int i;
+ int status_reg;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
+ int res;
+
+ dev_dbg(&client->dev, "Starting max6639 update\n");
+
+ status_reg = i2c_smbus_read_byte_data(client,
+ MAX6639_REG_STATUS);
+ if (status_reg < 0) {
+ ret = ERR_PTR(status_reg);
+ goto abort;
+ }
+
+ data->status = status_reg;
+
+ for (i = 0; i < 2; i++) {
+ res = i2c_smbus_read_byte_data(client,
+ MAX6639_REG_FAN_CNT(i));
+ if (res < 0) {
+ ret = ERR_PTR(res);
+ goto abort;
+ }
+ data->fan[i] = res;
+
+ res = i2c_smbus_read_byte_data(client,
+ MAX6639_REG_TEMP_EXT(i));
+ if (res < 0) {
+ ret = ERR_PTR(res);
+ goto abort;
+ }
+ data->temp[i] = res >> 5;
+ data->temp_fault[i] = res & 0x01;
+
+ res = i2c_smbus_read_byte_data(client,
+ MAX6639_REG_TEMP(i));
+ if (res < 0) {
+ ret = ERR_PTR(res);
+ goto abort;
+ }
+ data->temp[i] |= res << 3;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t temp_input_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ long temp;
+ struct max6639_data *data = max6639_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ temp = data->temp[attr->index] * 125;
+ return sprintf(buf, "%ld\n", temp);
+}
+
+static ssize_t temp_fault_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct max6639_data *data = max6639_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temp_fault[attr->index]);
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", (data->temp_therm[attr->index] * 1000));
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *dev_attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+ int res;
+
+ res = kstrtoul(buf, 10, &val);
+ if (res)
+ return res;
+
+ mutex_lock(&data->update_lock);
+ data->temp_therm[attr->index] = TEMP_LIMIT_TO_REG(val);
+ i2c_smbus_write_byte_data(client,
+ MAX6639_REG_THERM_LIMIT(attr->index),
+ data->temp_therm[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_crit_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", (data->temp_alert[attr->index] * 1000));
+}
+
+static ssize_t temp_crit_store(struct device *dev,
+ struct device_attribute *dev_attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+ int res;
+
+ res = kstrtoul(buf, 10, &val);
+ if (res)
+ return res;
+
+ mutex_lock(&data->update_lock);
+ data->temp_alert[attr->index] = TEMP_LIMIT_TO_REG(val);
+ i2c_smbus_write_byte_data(client,
+ MAX6639_REG_ALERT_LIMIT(attr->index),
+ data->temp_alert[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_emergency_show(struct device *dev,
+ struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", (data->temp_ot[attr->index] * 1000));
+}
+
+static ssize_t temp_emergency_store(struct device *dev,
+ struct device_attribute *dev_attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+ int res;
+
+ res = kstrtoul(buf, 10, &val);
+ if (res)
+ return res;
+
+ mutex_lock(&data->update_lock);
+ data->temp_ot[attr->index] = TEMP_LIMIT_TO_REG(val);
+ i2c_smbus_write_byte_data(client,
+ MAX6639_REG_OT_LIMIT(attr->index),
+ data->temp_ot[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t pwm_show(struct device *dev, struct device_attribute *dev_attr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", data->pwm[attr->index] * 255 / 120);
+}
+
+static ssize_t pwm_store(struct device *dev,
+ struct device_attribute *dev_attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6639_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned long val;
+ int res;
+
+ res = kstrtoul(buf, 10, &val);
+ if (res)
+ return res;
+
+ val = clamp_val(val, 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->pwm[attr->index] = (u8)(val * 120 / 255);
+ i2c_smbus_write_byte_data(client,
+ MAX6639_REG_TARGTDUTY(attr->index),
+ data->pwm[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_input_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct max6639_data *data = max6639_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index],
+ data->rpm_range));
+}
+
+static ssize_t alarm_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct max6639_data *data = max6639_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", !!(data->status & (1 << attr->index)));
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1);
+static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
+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(temp1_crit, temp_crit, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp_crit, 1);
+static SENSOR_DEVICE_ATTR_RW(temp1_emergency, temp_emergency, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_emergency, temp_emergency, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1);
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1);
+static SENSOR_DEVICE_ATTR_RO(fan1_fault, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(fan2_fault, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(temp1_emergency_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(temp2_emergency_alarm, alarm, 4);
+
+
+static struct attribute *max6639_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_emergency.dev_attr.attr,
+ &sensor_dev_attr_temp2_emergency.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_fault.dev_attr.attr,
+ &sensor_dev_attr_fan2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(max6639);
+
+/*
+ * returns respective index in rpm_ranges table
+ * 1 by default on invalid range
+ */
+static int rpm_range_to_reg(int range)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rpm_ranges); i++) {
+ if (rpm_ranges[i] == range)
+ return i;
+ }
+
+ return 1; /* default: 4000 RPM */
+}
+
+static int max6639_init_client(struct i2c_client *client,
+ struct max6639_data *data)
+{
+ struct max6639_platform_data *max6639_info =
+ dev_get_platdata(&client->dev);
+ int i;
+ int rpm_range = 1; /* default: 4000 RPM */
+ int err;
+
+ /* Reset chip to default values, see below for GCONFIG setup */
+ err = i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG,
+ MAX6639_GCONFIG_POR);
+ if (err)
+ goto exit;
+
+ /* Fans pulse per revolution is 2 by default */
+ if (max6639_info && max6639_info->ppr > 0 &&
+ max6639_info->ppr < 5)
+ data->ppr = max6639_info->ppr;
+ else
+ data->ppr = 2;
+ data->ppr -= 1;
+
+ if (max6639_info)
+ rpm_range = rpm_range_to_reg(max6639_info->rpm_range);
+ data->rpm_range = rpm_range;
+
+ for (i = 0; i < 2; i++) {
+
+ /* Set Fan pulse per revolution */
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_FAN_PPR(i),
+ data->ppr << 6);
+ if (err)
+ goto exit;
+
+ /* Fans config PWM, RPM */
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_FAN_CONFIG1(i),
+ MAX6639_FAN_CONFIG1_PWM | rpm_range);
+ if (err)
+ goto exit;
+
+ /* Fans PWM polarity high by default */
+ if (max6639_info && max6639_info->pwm_polarity == 0)
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_FAN_CONFIG2a(i), 0x00);
+ else
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_FAN_CONFIG2a(i), 0x02);
+ if (err)
+ goto exit;
+
+ /*
+ * /THERM full speed enable,
+ * PWM frequency 25kHz, see also GCONFIG below
+ */
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_FAN_CONFIG3(i),
+ MAX6639_FAN_CONFIG3_THERM_FULL_SPEED | 0x03);
+ if (err)
+ goto exit;
+
+ /* Max. temp. 80C/90C/100C */
+ data->temp_therm[i] = 80;
+ data->temp_alert[i] = 90;
+ data->temp_ot[i] = 100;
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_THERM_LIMIT(i),
+ data->temp_therm[i]);
+ if (err)
+ goto exit;
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_ALERT_LIMIT(i),
+ data->temp_alert[i]);
+ if (err)
+ goto exit;
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_OT_LIMIT(i), data->temp_ot[i]);
+ if (err)
+ goto exit;
+
+ /* PWM 120/120 (i.e. 100%) */
+ data->pwm[i] = 120;
+ err = i2c_smbus_write_byte_data(client,
+ MAX6639_REG_TARGTDUTY(i), data->pwm[i]);
+ if (err)
+ goto exit;
+ }
+ /* Start monitoring */
+ err = i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG,
+ MAX6639_GCONFIG_DISABLE_TIMEOUT | MAX6639_GCONFIG_CH2_LOCAL |
+ MAX6639_GCONFIG_PWM_FREQ_HI);
+exit:
+ return err;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max6639_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int dev_id, manu_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Actual detection via device and manufacturer ID */
+ dev_id = i2c_smbus_read_byte_data(client, MAX6639_REG_DEVID);
+ manu_id = i2c_smbus_read_byte_data(client, MAX6639_REG_MANUID);
+ if (dev_id != 0x58 || manu_id != 0x4D)
+ return -ENODEV;
+
+ strscpy(info->type, "max6639", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+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;
+ struct device *hwmon_dev;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct max6639_data), GFP_KERNEL);
+ if (!data)
+ 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 */
+ err = max6639_init_client(client, data);
+ if (err < 0)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ max6639_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static int max6639_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ 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, ret | MAX6639_GCONFIG_STANDBY);
+}
+
+static int max6639_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ 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, ret & ~MAX6639_GCONFIG_STANDBY);
+}
+
+static const struct i2c_device_id max6639_id[] = {
+ {"max6639", 0},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max6639_id);
+
+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 = pm_sleep_ptr(&max6639_pm_ops),
+ },
+ .probe_new = max6639_probe,
+ .id_table = max6639_id,
+ .detect = max6639_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(max6639_driver);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("max6639 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c
new file mode 100644
index 000000000..47ea34ff7
--- /dev/null
+++ b/drivers/hwmon/max6642.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for +/-1 degree C, SMBus-Compatible Remote/Local Temperature Sensor
+ * with Overtemperature Alarm
+ *
+ * Copyright (C) 2011 AppearTV AS
+ *
+ * Derived from:
+ *
+ * Based on the max1619 driver.
+ * Copyright (C) 2003-2004 Oleksij Rempel <bug-track@fisher-privat.net>
+ * Jean Delvare <jdelvare@suse.de>
+ *
+ * The MAX6642 is a sensor chip made by Maxim.
+ * It reports up to two temperatures (its own plus up to
+ * one external one). Complete datasheet can be
+ * obtained from Maxim's website at:
+ * http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+static const unsigned short normal_i2c[] = {
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
+
+/*
+ * The MAX6642 registers
+ */
+
+#define MAX6642_REG_R_MAN_ID 0xFE
+#define MAX6642_REG_R_CONFIG 0x03
+#define MAX6642_REG_W_CONFIG 0x09
+#define MAX6642_REG_R_STATUS 0x02
+#define MAX6642_REG_R_LOCAL_TEMP 0x00
+#define MAX6642_REG_R_LOCAL_TEMPL 0x11
+#define MAX6642_REG_R_LOCAL_HIGH 0x05
+#define MAX6642_REG_W_LOCAL_HIGH 0x0B
+#define MAX6642_REG_R_REMOTE_TEMP 0x01
+#define MAX6642_REG_R_REMOTE_TEMPL 0x10
+#define MAX6642_REG_R_REMOTE_HIGH 0x07
+#define MAX6642_REG_W_REMOTE_HIGH 0x0D
+
+/*
+ * Conversions
+ */
+
+static int temp_from_reg10(int val)
+{
+ return val * 250;
+}
+
+static int temp_from_reg(int val)
+{
+ return val * 1000;
+}
+
+static int temp_to_reg(int val)
+{
+ return val / 1000;
+}
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct max6642_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ u16 temp_input[2]; /* local/remote */
+ u16 temp_high[2]; /* local/remote */
+ u8 alarms;
+};
+
+/*
+ * Real code
+ */
+
+static void max6642_init_client(struct max6642_data *data,
+ struct i2c_client *client)
+{
+ u8 config;
+
+ /*
+ * Start the conversions.
+ */
+ config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
+ if (config & 0x40)
+ i2c_smbus_write_byte_data(client, MAX6642_REG_W_CONFIG,
+ config & 0xBF); /* run */
+
+ data->temp_high[0] = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_LOCAL_HIGH);
+ data->temp_high[1] = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_REMOTE_HIGH);
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max6642_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u8 reg_config, reg_status, man_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* identification */
+ man_id = i2c_smbus_read_byte_data(client, MAX6642_REG_R_MAN_ID);
+ if (man_id != 0x4D)
+ return -ENODEV;
+
+ /* sanity check */
+ if (i2c_smbus_read_byte_data(client, 0x04) != 0x4D
+ || i2c_smbus_read_byte_data(client, 0x06) != 0x4D
+ || i2c_smbus_read_byte_data(client, 0xff) != 0x4D)
+ return -ENODEV;
+
+ /*
+ * We read the config and status register, the 4 lower bits in the
+ * config register should be zero and bit 5, 3, 1 and 0 should be
+ * zero in the status register.
+ */
+ reg_config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
+ if ((reg_config & 0x0f) != 0x00)
+ return -ENODEV;
+
+ /* in between, another round of sanity checks */
+ if (i2c_smbus_read_byte_data(client, 0x04) != reg_config
+ || i2c_smbus_read_byte_data(client, 0x06) != reg_config
+ || i2c_smbus_read_byte_data(client, 0xff) != reg_config)
+ return -ENODEV;
+
+ reg_status = i2c_smbus_read_byte_data(client, MAX6642_REG_R_STATUS);
+ if ((reg_status & 0x2b) != 0x00)
+ return -ENODEV;
+
+ strscpy(info->type, "max6642", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static struct max6642_data *max6642_update_device(struct device *dev)
+{
+ struct max6642_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u16 val, tmp;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ dev_dbg(dev, "Updating max6642 data.\n");
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_LOCAL_TEMPL);
+ tmp = (val >> 6) & 3;
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_LOCAL_TEMP);
+ val = (val << 2) | tmp;
+ data->temp_input[0] = val;
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_REMOTE_TEMPL);
+ tmp = (val >> 6) & 3;
+ val = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_REMOTE_TEMP);
+ val = (val << 2) | tmp;
+ data->temp_input[1] = val;
+ data->alarms = i2c_smbus_read_byte_data(client,
+ MAX6642_REG_R_STATUS);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t temp_max10_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
+ struct max6642_data *data = max6642_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ temp_from_reg10(data->temp_input[attr->index]));
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+ struct max6642_data *data = max6642_update_device(dev);
+
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[attr2->nr]));
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+ struct max6642_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_high[attr2->nr] = clamp_val(temp_to_reg(val), 0, 255);
+ i2c_smbus_write_byte_data(data->client, attr2->index,
+ data->temp_high[attr2->nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ struct max6642_data *data = max6642_update_device(dev);
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_max10, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_max10, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp_max, 0,
+ MAX6642_REG_W_LOCAL_HIGH);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp_max, 1,
+ MAX6642_REG_W_REMOTE_HIGH);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4);
+
+static struct attribute *max6642_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(max6642);
+
+static int max6642_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct max6642_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct max6642_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the MAX6642 chip */
+ max6642_init_client(data, client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ max6642_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id max6642_id[] = {
+ { "max6642", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6642_id);
+
+static struct i2c_driver max6642_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max6642",
+ },
+ .probe_new = max6642_probe,
+ .id_table = max6642_id,
+ .detect = max6642_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(max6642_driver);
+
+MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
+MODULE_DESCRIPTION("MAX6642 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
new file mode 100644
index 000000000..f8d4534ce
--- /dev/null
+++ b/drivers/hwmon/max6650.c
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max6650.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring.
+ *
+ * (C) 2007 by Hans J. Koch <hjk@hansjkoch.de>
+ *
+ * based on code written by 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 MAX6650 chip. It should
+ * also work with the MAX6651. It does not distinguish max6650 and max6651
+ * chips.
+ *
+ * The datasheet was last seen at:
+ *
+ * http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.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/thermal.h>
+
+/*
+ * Insmod parameters
+ */
+
+/* fan_voltage: 5=5V fan, 12=12V fan, 0=don't change */
+static int fan_voltage;
+/* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */
+static int prescaler;
+/* clock: The clock frequency of the chip (max6651 can be clocked externally) */
+static int clock = 254000;
+
+module_param(fan_voltage, int, 0444);
+module_param(prescaler, int, 0444);
+module_param(clock, int, 0444);
+
+/*
+ * MAX 6650/6651 registers
+ */
+
+#define MAX6650_REG_SPEED 0x00
+#define MAX6650_REG_CONFIG 0x02
+#define MAX6650_REG_GPIO_DEF 0x04
+#define MAX6650_REG_DAC 0x06
+#define MAX6650_REG_ALARM_EN 0x08
+#define MAX6650_REG_ALARM 0x0A
+#define MAX6650_REG_TACH0 0x0C
+#define MAX6650_REG_TACH1 0x0E
+#define MAX6650_REG_TACH2 0x10
+#define MAX6650_REG_TACH3 0x12
+#define MAX6650_REG_GPIO_STAT 0x14
+#define MAX6650_REG_COUNT 0x16
+
+/*
+ * Config register bits
+ */
+
+#define MAX6650_CFG_V12 0x08
+#define MAX6650_CFG_PRESCALER_MASK 0x07
+#define MAX6650_CFG_PRESCALER_2 0x01
+#define MAX6650_CFG_PRESCALER_4 0x02
+#define MAX6650_CFG_PRESCALER_8 0x03
+#define MAX6650_CFG_PRESCALER_16 0x04
+#define MAX6650_CFG_MODE_MASK 0x30
+#define MAX6650_CFG_MODE_ON 0x00
+#define MAX6650_CFG_MODE_OFF 0x10
+#define MAX6650_CFG_MODE_CLOSED_LOOP 0x20
+#define MAX6650_CFG_MODE_OPEN_LOOP 0x30
+#define MAX6650_COUNT_MASK 0x03
+
+/*
+ * Alarm status register bits
+ */
+
+#define MAX6650_ALRM_MAX 0x01
+#define MAX6650_ALRM_MIN 0x02
+#define MAX6650_ALRM_TACH 0x04
+#define MAX6650_ALRM_GPIO1 0x08
+#define MAX6650_ALRM_GPIO2 0x10
+
+/* Minimum and maximum values of the FAN-RPM */
+#define FAN_RPM_MIN 240
+#define FAN_RPM_MAX 30000
+
+#define DIV_FROM_REG(reg) (1 << ((reg) & 7))
+#define DAC_LIMIT(v12) ((v12) ? 180 : 76)
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct max6650_data {
+ struct i2c_client *client;
+ struct mutex update_lock; /* protect alarm register updates */
+ int nr_fans;
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* register values */
+ u8 speed;
+ u8 config;
+ u8 tach[4];
+ u8 count;
+ u8 dac;
+ u8 alarm;
+ u8 alarm_en;
+ unsigned long cooling_dev_state;
+};
+
+static const u8 tach_reg[] = {
+ MAX6650_REG_TACH0,
+ MAX6650_REG_TACH1,
+ MAX6650_REG_TACH2,
+ MAX6650_REG_TACH3,
+};
+
+static const struct of_device_id __maybe_unused max6650_dt_match[] = {
+ {
+ .compatible = "maxim,max6650",
+ .data = (void *)1
+ },
+ {
+ .compatible = "maxim,max6651",
+ .data = (void *)4
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max6650_dt_match);
+
+static int dac_to_pwm(int dac, bool v12)
+{
+ /*
+ * Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans.
+ * Lower DAC values mean higher speeds.
+ */
+ return clamp_val(255 - (255 * dac) / DAC_LIMIT(v12), 0, 255);
+}
+
+static u8 pwm_to_dac(unsigned int pwm, bool v12)
+{
+ int limit = DAC_LIMIT(v12);
+
+ return limit - (limit * pwm) / 255;
+}
+
+static struct max6650_data *max6650_update_device(struct device *dev)
+{
+ struct max6650_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int reg, err = 0;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ for (i = 0; i < data->nr_fans; i++) {
+ reg = i2c_smbus_read_byte_data(client, tach_reg[i]);
+ if (reg < 0) {
+ err = reg;
+ goto error;
+ }
+ data->tach[i] = reg;
+ }
+
+ /*
+ * 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.
+ */
+ reg = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM);
+ if (reg < 0) {
+ err = reg;
+ goto error;
+ }
+ data->alarm |= reg;
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+error:
+ mutex_unlock(&data->update_lock);
+ if (err)
+ data = ERR_PTR(err);
+ return data;
+}
+
+/*
+ * Change the operating mode of the chip (if needed).
+ * mode is one of the MAX6650_CFG_MODE_* values.
+ */
+static int max6650_set_operating_mode(struct max6650_data *data, u8 mode)
+{
+ int result;
+ u8 config = data->config;
+
+ if (mode == (config & MAX6650_CFG_MODE_MASK))
+ return 0;
+
+ config = (config & ~MAX6650_CFG_MODE_MASK) | mode;
+
+ result = i2c_smbus_write_byte_data(data->client, MAX6650_REG_CONFIG,
+ config);
+ if (result < 0)
+ return result;
+
+ data->config = config;
+
+ return 0;
+}
+
+/*
+ * Set the fan speed to the specified RPM (or read back the RPM setting).
+ * This works in closed loop mode only. Use pwm1 for open loop speed setting.
+ *
+ * The MAX6650/1 will automatically control fan speed when in closed loop
+ * mode.
+ *
+ * Assumptions:
+ *
+ * 1) The MAX6650/1 internal 254kHz clock frequency is set correctly. Use
+ * the clock module parameter if you need to fine tune this.
+ *
+ * 2) The prescaler (low three bits of the config register) has already
+ * been set to an appropriate value. Use the prescaler module parameter
+ * if your BIOS doesn't initialize the chip properly.
+ *
+ * The relevant equations are given on pages 21 and 22 of the datasheet.
+ *
+ * From the datasheet, the relevant equation when in regulation is:
+ *
+ * [fCLK / (128 x (KTACH + 1))] = 2 x FanSpeed / KSCALE
+ *
+ * where:
+ *
+ * fCLK is the oscillator frequency (either the 254kHz internal
+ * oscillator or the externally applied clock)
+ *
+ * KTACH is the value in the speed register
+ *
+ * FanSpeed is the speed of the fan in rps
+ *
+ * KSCALE is the prescaler value (1, 2, 4, 8, or 16)
+ *
+ * When reading, we need to solve for FanSpeed. When writing, we need to
+ * solve for KTACH.
+ *
+ * Note: this tachometer is completely separate from the tachometers
+ * used to measure the fan speeds. Only one fan's speed (fan1) is
+ * controlled.
+ */
+
+static int max6650_set_target(struct max6650_data *data, unsigned long rpm)
+{
+ int kscale, ktach;
+
+ if (rpm == 0)
+ return max6650_set_operating_mode(data, MAX6650_CFG_MODE_OFF);
+
+ rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
+
+ /*
+ * Divide the required speed by 60 to get from rpm to rps, then
+ * use the datasheet equation:
+ *
+ * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
+ */
+
+ kscale = DIV_FROM_REG(data->config);
+ ktach = ((clock * kscale) / (256 * rpm / 60)) - 1;
+ if (ktach < 0)
+ ktach = 0;
+ if (ktach > 255)
+ ktach = 255;
+ data->speed = ktach;
+
+ return i2c_smbus_write_byte_data(data->client, MAX6650_REG_SPEED,
+ data->speed);
+}
+
+/*
+ * Get gpio alarm status:
+ * Possible values:
+ * 0 = no alarm
+ * 1 = alarm
+ */
+
+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 max6650_data *data = max6650_update_device(dev);
+ bool alarm;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ alarm = data->alarm & attr->index;
+ if (alarm) {
+ mutex_lock(&data->update_lock);
+ data->alarm &= ~attr->index;
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+ }
+
+ return sprintf(buf, "%d\n", alarm);
+}
+
+static SENSOR_DEVICE_ATTR_RO(gpio1_alarm, alarm, MAX6650_ALRM_GPIO1);
+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 = kobj_to_dev(kobj);
+ struct max6650_data *data = dev_get_drvdata(dev);
+ struct device_attribute *devattr;
+
+ /*
+ * Hide the alarms that have not been enabled by the firmware
+ */
+
+ devattr = container_of(a, struct device_attribute, attr);
+ if (devattr == &sensor_dev_attr_gpio1_alarm.dev_attr ||
+ devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) {
+ if (!(data->alarm_en & to_sensor_dev_attr(devattr)->index))
+ return 0;
+ }
+
+ return a->mode;
+}
+
+static struct attribute *max6650_attrs[] = {
+ &sensor_dev_attr_gpio1_alarm.dev_attr.attr,
+ &sensor_dev_attr_gpio2_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group max6650_group = {
+ .attrs = max6650_attrs,
+ .is_visible = max6650_attrs_visible,
+};
+
+static const struct attribute_group *max6650_groups[] = {
+ &max6650_group,
+ NULL
+};
+
+static int max6650_init_client(struct max6650_data *data,
+ struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int reg;
+ int err;
+ u32 voltage;
+ u32 prescale;
+ u32 target_rpm;
+
+ if (of_property_read_u32(dev->of_node, "maxim,fan-microvolt",
+ &voltage))
+ voltage = fan_voltage;
+ else
+ voltage /= 1000000; /* Microvolts to volts */
+ if (of_property_read_u32(dev->of_node, "maxim,fan-prescale",
+ &prescale))
+ prescale = prescaler;
+
+ reg = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
+ if (reg < 0) {
+ dev_err(dev, "Error reading config register, aborting.\n");
+ return reg;
+ }
+
+ switch (voltage) {
+ case 0:
+ break;
+ case 5:
+ reg &= ~MAX6650_CFG_V12;
+ break;
+ case 12:
+ reg |= MAX6650_CFG_V12;
+ break;
+ default:
+ dev_err(dev, "illegal value for fan_voltage (%d)\n", voltage);
+ }
+
+ switch (prescale) {
+ case 0:
+ break;
+ case 1:
+ reg &= ~MAX6650_CFG_PRESCALER_MASK;
+ break;
+ case 2:
+ reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
+ | MAX6650_CFG_PRESCALER_2;
+ break;
+ case 4:
+ reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
+ | MAX6650_CFG_PRESCALER_4;
+ break;
+ case 8:
+ reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
+ | MAX6650_CFG_PRESCALER_8;
+ break;
+ case 16:
+ reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
+ | MAX6650_CFG_PRESCALER_16;
+ break;
+ default:
+ dev_err(dev, "illegal value for prescaler (%d)\n", prescale);
+ }
+
+ dev_info(dev, "Fan voltage: %dV, prescaler: %d.\n",
+ (reg & MAX6650_CFG_V12) ? 12 : 5,
+ 1 << (reg & MAX6650_CFG_PRESCALER_MASK));
+
+ err = i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, reg);
+ if (err) {
+ dev_err(dev, "Config write error, aborting.\n");
+ return err;
+ }
+ data->config = reg;
+
+ reg = i2c_smbus_read_byte_data(client, MAX6650_REG_SPEED);
+ if (reg < 0) {
+ dev_err(dev, "Failed to read speed register, aborting.\n");
+ return reg;
+ }
+ data->speed = reg;
+
+ reg = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC);
+ if (reg < 0) {
+ dev_err(dev, "Failed to read DAC register, aborting.\n");
+ return reg;
+ }
+ data->dac = reg;
+
+ reg = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT);
+ if (reg < 0) {
+ dev_err(dev, "Failed to read count register, aborting.\n");
+ return reg;
+ }
+ data->count = reg;
+
+ reg = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN);
+ if (reg < 0) {
+ dev_err(dev, "Failed to read alarm configuration, aborting.\n");
+ return reg;
+ }
+ data->alarm_en = reg;
+
+ if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm",
+ &target_rpm)) {
+ max6650_set_target(data, target_rpm);
+ max6650_set_operating_mode(data, MAX6650_CFG_MODE_CLOSED_LOOP);
+ }
+
+ return 0;
+}
+
+static int max6650_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = 255;
+
+ return 0;
+}
+
+static int max6650_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct max6650_data *data = cdev->devdata;
+
+ *state = data->cooling_dev_state;
+
+ return 0;
+}
+
+static int max6650_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct max6650_data *data = cdev->devdata;
+ struct i2c_client *client = data->client;
+ int err;
+
+ state = clamp_val(state, 0, 255);
+
+ mutex_lock(&data->update_lock);
+
+ data->dac = pwm_to_dac(state, data->config & MAX6650_CFG_V12);
+ err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
+ if (!err) {
+ max6650_set_operating_mode(data, state ?
+ MAX6650_CFG_MODE_OPEN_LOOP :
+ MAX6650_CFG_MODE_OFF);
+ data->cooling_dev_state = state;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static const struct thermal_cooling_device_ops max6650_cooling_ops = {
+ .get_max_state = max6650_get_max_state,
+ .get_cur_state = max6650_get_cur_state,
+ .set_cur_state = max6650_set_cur_state,
+};
+
+static int max6650_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct max6650_data *data = max6650_update_device(dev);
+ int mode;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ switch (type) {
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = dac_to_pwm(data->dac,
+ data->config & MAX6650_CFG_V12);
+ break;
+ case hwmon_pwm_enable:
+ /*
+ * Possible values:
+ * 0 = Fan always on
+ * 1 = Open loop, Voltage is set according to speed,
+ * not regulated.
+ * 2 = Closed loop, RPM for all fans regulated by fan1
+ * tachometer
+ * 3 = Fan off
+ */
+ mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
+ *val = (4 - mode) & 3; /* {0 1 2 3} -> {0 3 2 1} */
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ /*
+ * Calculation details:
+ *
+ * Each tachometer counts over an interval given by the
+ * "count" register (0.25, 0.5, 1 or 2 seconds).
+ * The driver assumes that the fans produce two pulses
+ * per revolution (this seems to be the most common).
+ */
+ *val = DIV_ROUND_CLOSEST(data->tach[channel] * 120,
+ DIV_FROM_REG(data->count));
+ break;
+ case hwmon_fan_div:
+ *val = DIV_FROM_REG(data->count);
+ break;
+ case hwmon_fan_target:
+ /*
+ * Use the datasheet equation:
+ * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
+ * then multiply by 60 to give rpm.
+ */
+ *val = 60 * DIV_FROM_REG(data->config) * clock /
+ (256 * (data->speed + 1));
+ break;
+ case hwmon_fan_min_alarm:
+ *val = !!(data->alarm & MAX6650_ALRM_MIN);
+ data->alarm &= ~MAX6650_ALRM_MIN;
+ data->valid = false;
+ break;
+ case hwmon_fan_max_alarm:
+ *val = !!(data->alarm & MAX6650_ALRM_MAX);
+ data->alarm &= ~MAX6650_ALRM_MAX;
+ data->valid = false;
+ break;
+ case hwmon_fan_fault:
+ *val = !!(data->alarm & MAX6650_ALRM_TACH);
+ data->alarm &= ~MAX6650_ALRM_TACH;
+ data->valid = false;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static const u8 max6650_pwm_modes[] = {
+ MAX6650_CFG_MODE_ON,
+ MAX6650_CFG_MODE_OPEN_LOOP,
+ MAX6650_CFG_MODE_CLOSED_LOOP,
+ MAX6650_CFG_MODE_OFF,
+};
+
+static int max6650_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct max6650_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+
+ switch (type) {
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ reg = pwm_to_dac(clamp_val(val, 0, 255),
+ data->config & MAX6650_CFG_V12);
+ ret = i2c_smbus_write_byte_data(data->client,
+ MAX6650_REG_DAC, reg);
+ if (ret)
+ break;
+ data->dac = reg;
+ break;
+ case hwmon_pwm_enable:
+ if (val < 0 || val >= ARRAY_SIZE(max6650_pwm_modes)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = max6650_set_operating_mode(data,
+ max6650_pwm_modes[val]);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_div:
+ switch (val) {
+ case 1:
+ reg = 0;
+ break;
+ case 2:
+ reg = 1;
+ break;
+ case 4:
+ reg = 2;
+ break;
+ case 8:
+ reg = 3;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = i2c_smbus_write_byte_data(data->client,
+ MAX6650_REG_COUNT, reg);
+ if (ret)
+ break;
+ data->count = reg;
+ break;
+ case hwmon_fan_target:
+ if (val < 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = max6650_set_target(data, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+error:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static umode_t max6650_is_visible(const void *_data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ const struct max6650_data *data = _data;
+
+ if (channel && (channel >= data->nr_fans || type != hwmon_fan))
+ return 0;
+
+ switch (type) {
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ return 0444;
+ case hwmon_fan_target:
+ case hwmon_fan_div:
+ return 0644;
+ case hwmon_fan_min_alarm:
+ if (data->alarm_en & MAX6650_ALRM_MIN)
+ return 0444;
+ break;
+ case hwmon_fan_max_alarm:
+ if (data->alarm_en & MAX6650_ALRM_MAX)
+ return 0444;
+ break;
+ case hwmon_fan_fault:
+ if (data->alarm_en & MAX6650_ALRM_TACH)
+ return 0444;
+ break;
+ default:
+ break;
+ }
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *max6650_info[] = {
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
+ HWMON_F_MIN_ALARM | HWMON_F_MAX_ALARM |
+ HWMON_F_FAULT,
+ HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
+ NULL
+};
+
+static const struct hwmon_ops max6650_hwmon_ops = {
+ .read = max6650_read,
+ .write = max6650_write,
+ .is_visible = max6650_is_visible,
+};
+
+static const struct hwmon_chip_info max6650_chip_info = {
+ .ops = &max6650_hwmon_ops,
+ .info = max6650_info,
+};
+
+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;
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(max6650_dt_match), dev);
+ struct max6650_data *data;
+ struct device *hwmon_dev;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct max6650_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ data->nr_fans = of_id ? (int)(uintptr_t)of_id->data :
+ i2c_match_id(max6650_id, client)->driver_data;
+
+ /*
+ * Initialize the max6650 chip
+ */
+ err = max6650_init_client(data, client);
+ if (err)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev,
+ client->name, data,
+ &max6650_chip_info,
+ max6650_groups);
+ err = PTR_ERR_OR_ZERO(hwmon_dev);
+ if (err)
+ return err;
+
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+ cooling_dev = devm_thermal_of_cooling_device_register(dev,
+ dev->of_node, client->name,
+ data, &max6650_cooling_ops);
+ if (IS_ERR(cooling_dev)) {
+ dev_warn(dev, "thermal cooling device register failed: %ld\n",
+ PTR_ERR(cooling_dev));
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id max6650_id[] = {
+ { "max6650", 1 },
+ { "max6651", 4 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6650_id);
+
+static struct i2c_driver max6650_driver = {
+ .driver = {
+ .name = "max6650",
+ .of_match_table = of_match_ptr(max6650_dt_match),
+ },
+ .probe_new = max6650_probe,
+ .id_table = max6650_id,
+};
+
+module_i2c_driver(max6650_driver);
+
+MODULE_AUTHOR("Hans J. Koch");
+MODULE_DESCRIPTION("MAX6650 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c
new file mode 100644
index 000000000..2895cea54
--- /dev/null
+++ b/drivers/hwmon/max6697.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net>
+ *
+ * based on max1668.c
+ * Copyright (c) 2011 David George <david.george@ska.ac.za>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+
+#include <linux/platform_data/max6697.h>
+
+enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694,
+ max6697, max6698, max6699 };
+
+/* Report local sensor as temp1 */
+
+static const u8 MAX6697_REG_TEMP[] = {
+ 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 };
+static const u8 MAX6697_REG_TEMP_EXT[] = {
+ 0x57, 0x09, 0x52, 0x53, 0x54, 0x55, 0x56, 0 };
+static const u8 MAX6697_REG_MAX[] = {
+ 0x17, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18 };
+static const u8 MAX6697_REG_CRIT[] = {
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 };
+
+/*
+ * Map device tree / platform data register bit map to chip bit map.
+ * Applies to alert register and over-temperature register.
+ */
+#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))
+
+#define MAX6697_REG_CONFIG 0x41
+#define MAX6581_CONF_EXTENDED (1 << 1)
+#define MAX6693_CONF_BETA (1 << 2)
+#define MAX6697_CONF_RESISTANCE (1 << 3)
+#define MAX6697_CONF_TIMEOUT (1 << 5)
+#define MAX6697_REG_ALERT_MASK 0x42
+#define MAX6697_REG_OVERT_MASK 0x43
+
+#define MAX6581_REG_RESISTANCE 0x4a
+#define MAX6581_REG_IDEALITY 0x4b
+#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 */
+
+struct max6697_chip_data {
+ int channels;
+ u32 have_ext;
+ u32 have_crit;
+ u32 have_fault;
+ u8 valid_conf;
+ const u8 *alarm_map;
+};
+
+struct max6697_data {
+ struct i2c_client *client;
+
+ enum chips type;
+ const struct max6697_chip_data *chip;
+
+ int update_interval; /* in milli-seconds */
+ int temp_offset; /* in degrees C */
+
+ struct mutex update_lock;
+ unsigned long last_updated; /* In jiffies */
+ bool valid; /* true if following fields are valid */
+
+ /* 1x local and up to 7x remote */
+ u8 temp[8][4]; /* [nr][0]=temp [1]=ext [2]=max [3]=crit */
+#define MAX6697_TEMP_INPUT 0
+#define MAX6697_TEMP_EXT 1
+#define MAX6697_TEMP_MAX 2
+#define MAX6697_TEMP_CRIT 3
+ u32 alarms;
+};
+
+/* Diode fault status bits on MAX6581 are right shifted by one bit */
+static const u8 max6581_alarm_map[] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23 };
+
+static const struct max6697_chip_data max6697_chip_data[] = {
+ [max6581] = {
+ .channels = 8,
+ .have_crit = 0xff,
+ .have_ext = 0x7f,
+ .have_fault = 0xfe,
+ .valid_conf = MAX6581_CONF_EXTENDED | MAX6697_CONF_TIMEOUT,
+ .alarm_map = max6581_alarm_map,
+ },
+ [max6602] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6622] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6636] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6689] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6693] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA |
+ MAX6697_CONF_TIMEOUT,
+ },
+ [max6694] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA |
+ MAX6697_CONF_TIMEOUT,
+ },
+ [max6697] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x7e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6698] = {
+ .channels = 7,
+ .have_crit = 0x72,
+ .have_ext = 0x02,
+ .have_fault = 0x0e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+ [max6699] = {
+ .channels = 5,
+ .have_crit = 0x12,
+ .have_ext = 0x02,
+ .have_fault = 0x1e,
+ .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT,
+ },
+};
+
+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);
+ struct i2c_client *client = data->client;
+ struct max6697_data *ret = data;
+ int val;
+ int i;
+ u32 alarms;
+
+ mutex_lock(&data->update_lock);
+
+ if (data->valid &&
+ !time_after(jiffies, data->last_updated
+ + msecs_to_jiffies(data->update_interval)))
+ goto abort;
+
+ for (i = 0; i < data->chip->channels; i++) {
+ if (data->chip->have_ext & (1 << i)) {
+ val = i2c_smbus_read_byte_data(client,
+ MAX6697_REG_TEMP_EXT[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_EXT] = val;
+ }
+
+ val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_INPUT] = val;
+
+ val = i2c_smbus_read_byte_data(client, MAX6697_REG_MAX[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_MAX] = val;
+
+ if (data->chip->have_crit & (1 << i)) {
+ val = i2c_smbus_read_byte_data(client,
+ MAX6697_REG_CRIT[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp[i][MAX6697_TEMP_CRIT] = val;
+ }
+ }
+
+ alarms = 0;
+ for (i = 0; i < 3; i++) {
+ val = i2c_smbus_read_byte_data(client, MAX6697_REG_STAT(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ alarms = (alarms << 8) | val;
+ }
+ data->alarms = alarms;
+ data->last_updated = jiffies;
+ data->valid = true;
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t temp_input_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ struct max6697_data *data = max6697_update_device(dev);
+ int temp;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ temp = (data->temp[index][MAX6697_TEMP_INPUT] - data->temp_offset) << 3;
+ temp |= data->temp[index][MAX6697_TEMP_EXT] >> 5;
+
+ return sprintf(buf, "%d\n", temp * 125);
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int index = to_sensor_dev_attr_2(devattr)->index;
+ struct max6697_data *data = max6697_update_device(dev);
+ int temp;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ temp = data->temp[nr][index];
+ temp -= data->temp_offset;
+
+ return sprintf(buf, "%d\n", temp * 1000);
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct max6697_data *data = max6697_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (data->chip->alarm_map)
+ index = data->chip->alarm_map[index];
+
+ return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
+}
+
+static ssize_t temp_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int index = to_sensor_dev_attr_2(devattr)->index;
+ struct max6697_data *data = dev_get_drvdata(dev);
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset;
+ temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127);
+ data->temp[nr][index] = temp;
+ ret = i2c_smbus_write_byte_data(data->client,
+ index == 2 ? MAX6697_REG_MAX[nr]
+ : MAX6697_REG_CRIT[nr],
+ temp);
+ mutex_unlock(&data->update_lock);
+
+ 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);
+
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, 1, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_crit, temp, 1, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_input, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, 2, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_crit, temp, 2, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR_RO(temp4_input, temp_input, 3);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_max, temp, 3, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_crit, temp, 3, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR_RO(temp5_input, temp_input, 4);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_max, temp, 4, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_crit, temp, 4, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR_RO(temp6_input, temp_input, 5);
+static SENSOR_DEVICE_ATTR_2_RW(temp6_max, temp, 5, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2_RW(temp6_crit, temp, 5, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR_RO(temp7_input, temp_input, 6);
+static SENSOR_DEVICE_ATTR_2_RW(temp7_max, temp, 6, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2_RW(temp7_crit, temp, 6, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR_RO(temp8_input, temp_input, 7);
+static SENSOR_DEVICE_ATTR_2_RW(temp8_max, temp, 7, MAX6697_TEMP_MAX);
+static SENSOR_DEVICE_ATTR_2_RW(temp8_crit, temp, 7, MAX6697_TEMP_CRIT);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 22);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 16);
+static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, 17);
+static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, 18);
+static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, alarm, 19);
+static SENSOR_DEVICE_ATTR_RO(temp6_max_alarm, alarm, 20);
+static SENSOR_DEVICE_ATTR_RO(temp7_max_alarm, alarm, 21);
+static SENSOR_DEVICE_ATTR_RO(temp8_max_alarm, alarm, 23);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 14);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 8);
+static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 9);
+static SENSOR_DEVICE_ATTR_RO(temp4_crit_alarm, alarm, 10);
+static SENSOR_DEVICE_ATTR_RO(temp5_crit_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(temp6_crit_alarm, alarm, 12);
+static SENSOR_DEVICE_ATTR_RO(temp7_crit_alarm, alarm, 13);
+static SENSOR_DEVICE_ATTR_RO(temp8_crit_alarm, alarm, 15);
+
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, 4);
+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 = kobj_to_dev(kobj);
+ struct max6697_data *data = dev_get_drvdata(dev);
+ const struct max6697_chip_data *chip = data->chip;
+ int channel = index / 7; /* channel number */
+ int nr = index % 7; /* attribute index within channel */
+
+ if (channel >= chip->channels)
+ return 0;
+
+ if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel)))
+ 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;
+}
+
+/*
+ * max6697_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 in max6697_is_visible.
+ */
+static struct attribute *max6697_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &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,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &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,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &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,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &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,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &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,
+ &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
+ &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,
+ &sensor_dev_attr_temp7_max_alarm.dev_attr.attr,
+ &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,
+ &sensor_dev_attr_temp8_max_alarm.dev_attr.attr,
+ &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
+};
+
+static const struct attribute_group max6697_group = {
+ .attrs = max6697_attributes, .is_visible = max6697_is_visible,
+};
+__ATTRIBUTE_GROUPS(max6697);
+
+static void max6697_get_config_of(struct device_node *node,
+ struct max6697_platform_data *pdata)
+{
+ int len;
+ const __be32 *prop;
+
+ pdata->smbus_timeout_disable =
+ of_property_read_bool(node, "smbus-timeout-disable");
+ pdata->extended_range_enable =
+ of_property_read_bool(node, "extended-range-enable");
+ pdata->beta_compensation =
+ of_property_read_bool(node, "beta-compensation-enable");
+
+ prop = of_get_property(node, "alert-mask", &len);
+ if (prop && len == sizeof(u32))
+ pdata->alert_mask = be32_to_cpu(prop[0]);
+ prop = of_get_property(node, "over-temperature-mask", &len);
+ if (prop && len == sizeof(u32))
+ pdata->over_temperature_mask = be32_to_cpu(prop[0]);
+ prop = of_get_property(node, "resistance-cancellation", &len);
+ if (prop) {
+ if (len == sizeof(u32))
+ pdata->resistance_cancellation = be32_to_cpu(prop[0]);
+ else
+ pdata->resistance_cancellation = 0xfe;
+ }
+ prop = of_get_property(node, "transistor-ideality", &len);
+ if (prop && len == 2 * sizeof(u32)) {
+ pdata->ideality_mask = be32_to_cpu(prop[0]);
+ pdata->ideality_value = be32_to_cpu(prop[1]);
+ }
+}
+
+static int max6697_init_chip(struct max6697_data *data,
+ struct i2c_client *client)
+{
+ struct max6697_platform_data *pdata = dev_get_platdata(&client->dev);
+ struct max6697_platform_data p;
+ const struct max6697_chip_data *chip = data->chip;
+ int factor = chip->channels;
+ int ret, reg;
+
+ /*
+ * Don't touch configuration if neither platform data nor OF
+ * configuration was specified. If that is the case, use the
+ * current chip configuration.
+ */
+ if (!pdata && !client->dev.of_node) {
+ reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG);
+ if (reg < 0)
+ return reg;
+ if (data->type == max6581) {
+ if (reg & MAX6581_CONF_EXTENDED)
+ data->temp_offset = 64;
+ reg = i2c_smbus_read_byte_data(client,
+ MAX6581_REG_RESISTANCE);
+ if (reg < 0)
+ return reg;
+ factor += hweight8(reg);
+ } else {
+ if (reg & MAX6697_CONF_RESISTANCE)
+ factor++;
+ }
+ goto done;
+ }
+
+ if (client->dev.of_node) {
+ memset(&p, 0, sizeof(p));
+ max6697_get_config_of(client->dev.of_node, &p);
+ pdata = &p;
+ }
+
+ reg = 0;
+ if (pdata->smbus_timeout_disable &&
+ (chip->valid_conf & MAX6697_CONF_TIMEOUT)) {
+ reg |= MAX6697_CONF_TIMEOUT;
+ }
+ if (pdata->extended_range_enable &&
+ (chip->valid_conf & MAX6581_CONF_EXTENDED)) {
+ reg |= MAX6581_CONF_EXTENDED;
+ data->temp_offset = 64;
+ }
+ if (pdata->resistance_cancellation &&
+ (chip->valid_conf & MAX6697_CONF_RESISTANCE)) {
+ reg |= MAX6697_CONF_RESISTANCE;
+ factor++;
+ }
+ if (pdata->beta_compensation &&
+ (chip->valid_conf & MAX6693_CONF_BETA)) {
+ reg |= MAX6693_CONF_BETA;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, MAX6697_REG_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_OVERT_MAP_BITS(pdata->over_temperature_mask));
+ if (ret < 0)
+ return ret;
+
+ if (data->type == max6581) {
+ factor += hweight8(pdata->resistance_cancellation >> 1);
+ ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE,
+ pdata->resistance_cancellation >> 1);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY,
+ pdata->ideality_value);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client,
+ MAX6581_REG_IDEALITY_SELECT,
+ pdata->ideality_mask >> 1);
+ if (ret < 0)
+ return ret;
+ }
+done:
+ data->update_interval = factor * MAX6697_CONV_TIME;
+ return 0;
+}
+
+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;
+ struct max6697_data *data;
+ struct device *hwmon_dev;
+ int err;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct max6697_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (client->dev.of_node)
+ data->type = (enum chips)of_device_get_match_data(&client->dev);
+ else
+ 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);
+
+ err = max6697_init_chip(data, client);
+ if (err)
+ return err;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ max6697_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max6697_id[] = {
+ { "max6581", max6581 },
+ { "max6602", max6602 },
+ { "max6622", max6622 },
+ { "max6636", max6636 },
+ { "max6689", max6689 },
+ { "max6693", max6693 },
+ { "max6694", max6694 },
+ { "max6697", max6697 },
+ { "max6698", max6698 },
+ { "max6699", max6699 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max6697_id);
+
+static const struct of_device_id __maybe_unused max6697_of_match[] = {
+ {
+ .compatible = "maxim,max6581",
+ .data = (void *)max6581
+ },
+ {
+ .compatible = "maxim,max6602",
+ .data = (void *)max6602
+ },
+ {
+ .compatible = "maxim,max6622",
+ .data = (void *)max6622
+ },
+ {
+ .compatible = "maxim,max6636",
+ .data = (void *)max6636
+ },
+ {
+ .compatible = "maxim,max6689",
+ .data = (void *)max6689
+ },
+ {
+ .compatible = "maxim,max6693",
+ .data = (void *)max6693
+ },
+ {
+ .compatible = "maxim,max6694",
+ .data = (void *)max6694
+ },
+ {
+ .compatible = "maxim,max6697",
+ .data = (void *)max6697
+ },
+ {
+ .compatible = "maxim,max6698",
+ .data = (void *)max6698
+ },
+ {
+ .compatible = "maxim,max6699",
+ .data = (void *)max6699
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max6697_of_match);
+
+static struct i2c_driver max6697_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max6697",
+ .of_match_table = of_match_ptr(max6697_of_match),
+ },
+ .probe_new = max6697_probe,
+ .id_table = max6697_id,
+};
+
+module_i2c_driver(max6697_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("MAX6697 temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
new file mode 100644
index 000000000..ff147e5e1
--- /dev/null
+++ b/drivers/hwmon/mc13783-adc.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for the ADC on Freescale Semiconductor MC13783 and MC13892 PMICs.
+ *
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ */
+
+#include <linux/mfd/mc13xxx.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/hwmon.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+
+#define DRIVER_NAME "mc13783-adc"
+
+/* platform device id driver data */
+#define MC13783_ADC_16CHANS 1
+#define MC13783_ADC_BPDIV2 2
+
+struct mc13783_adc_priv {
+ struct mc13xxx *mc13xxx;
+ struct device *hwmon_dev;
+ char name[PLATFORM_NAME_SIZE];
+};
+
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct mc13783_adc_priv *priv = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", priv->name);
+}
+
+static int mc13783_adc_read(struct device *dev,
+ struct device_attribute *devattr, unsigned int *val)
+{
+ struct mc13783_adc_priv *priv = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ unsigned int channel = attr->index;
+ unsigned int sample[4];
+ int ret;
+
+ ret = mc13xxx_adc_do_conversion(priv->mc13xxx,
+ MC13XXX_ADC_MODE_MULT_CHAN,
+ channel, 0, 0, sample);
+ if (ret)
+ return ret;
+
+ /* ADIN7 subchannels */
+ if (channel >= 16)
+ channel = 7;
+
+ channel &= 0x7;
+
+ *val = (sample[channel % 4] >> (channel > 3 ? 14 : 2)) & 0x3ff;
+
+ return 0;
+}
+
+static ssize_t mc13783_adc_bp_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ unsigned val;
+ struct platform_device *pdev = to_platform_device(dev);
+ kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data;
+ int ret = mc13783_adc_read(dev, devattr, &val);
+
+ if (ret)
+ return ret;
+
+ if (driver_data & MC13783_ADC_BPDIV2)
+ val = DIV_ROUND_CLOSEST(val * 9, 2);
+ else
+ /*
+ * BP (channel 2) reports with offset 2.4V to the actual value
+ * to fit the input range of the ADC. unit = 2.25mV = 9/4 mV.
+ */
+ val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t mc13783_adc_gp_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ unsigned val;
+ int ret = mc13783_adc_read(dev, devattr, &val);
+
+ if (ret)
+ return ret;
+
+ /*
+ * input range is [0, 2.3V], val has 10 bits, so each bit
+ * is worth 9/4 mV.
+ */
+ val = DIV_ROUND_CLOSEST(val * 9, 4);
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t mc13783_adc_uid_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ unsigned int val;
+ struct platform_device *pdev = to_platform_device(dev);
+ kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data;
+ int ret = mc13783_adc_read(dev, devattr, &val);
+
+ if (ret)
+ return ret;
+
+ if (driver_data & MC13783_ADC_BPDIV2)
+ /* MC13892 have 1/2 divider, input range is [0, 4.800V] */
+ val = DIV_ROUND_CLOSEST(val * 4800, 1024);
+ else
+ /* MC13783 have 0.9 divider, input range is [0, 2.555V] */
+ val = DIV_ROUND_CLOSEST(val * 2555, 1024);
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t mc13783_adc_temp_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ unsigned int val;
+ struct platform_device *pdev = to_platform_device(dev);
+ kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data;
+ int ret = mc13783_adc_read(dev, devattr, &val);
+
+ if (ret)
+ return ret;
+
+ if (driver_data & MC13783_ADC_BPDIV2) {
+ /*
+ * MC13892:
+ * Die Temperature Read Out Code at 25C 680
+ * Temperature change per LSB +0.4244C
+ */
+ ret = DIV_ROUND_CLOSEST(-2635920 + val * 4244, 10);
+ } else {
+ /*
+ * MC13783:
+ * Die Temperature Read Out Code at 25C 282
+ * Temperature change per LSB -1.14C
+ */
+ ret = 346480 - 1140 * val;
+ }
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static DEVICE_ATTR_RO(name);
+static SENSOR_DEVICE_ATTR_RO(in2_input, mc13783_adc_bp, 2);
+static SENSOR_DEVICE_ATTR_RO(in5_input, mc13783_adc_gp, 5);
+static SENSOR_DEVICE_ATTR_RO(in6_input, mc13783_adc_gp, 6);
+static SENSOR_DEVICE_ATTR_RO(in7_input, mc13783_adc_gp, 7);
+static SENSOR_DEVICE_ATTR_RO(in8_input, mc13783_adc_gp, 8);
+static SENSOR_DEVICE_ATTR_RO(in9_input, mc13783_adc_gp, 9);
+static SENSOR_DEVICE_ATTR_RO(in10_input, mc13783_adc_gp, 10);
+static SENSOR_DEVICE_ATTR_RO(in11_input, mc13783_adc_gp, 11);
+static SENSOR_DEVICE_ATTR_RO(in12_input, mc13783_adc_gp, 12);
+static SENSOR_DEVICE_ATTR_RO(in13_input, mc13783_adc_gp, 13);
+static SENSOR_DEVICE_ATTR_RO(in14_input, mc13783_adc_gp, 14);
+static SENSOR_DEVICE_ATTR_RO(in15_input, mc13783_adc_gp, 15);
+static SENSOR_DEVICE_ATTR_RO(in16_input, mc13783_adc_uid, 16);
+static SENSOR_DEVICE_ATTR_RO(temp1_input, mc13783_adc_temp, 17);
+
+static struct attribute *mc13783_attr_base[] = {
+ &dev_attr_name.attr,
+ &sensor_dev_attr_in2_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_in16_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mc13783_group_base = {
+ .attrs = mc13783_attr_base,
+};
+
+/* these are only used if MC13783_ADC_16CHANS is provided in driver data */
+static struct attribute *mc13783_attr_16chans[] = {
+ &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,
+ NULL
+};
+
+static const struct attribute_group mc13783_group_16chans = {
+ .attrs = mc13783_attr_16chans,
+};
+
+/* last four channels may be occupied by the touchscreen */
+static struct attribute *mc13783_attr_ts[] = {
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in13_input.dev_attr.attr,
+ &sensor_dev_attr_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mc13783_group_ts = {
+ .attrs = mc13783_attr_ts,
+};
+
+static int mc13783_adc_use_touchscreen(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ unsigned flags = mc13xxx_get_flags(priv->mc13xxx);
+
+ return flags & MC13XXX_USE_TOUCHSCREEN;
+}
+
+static int __init mc13783_adc_probe(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv;
+ int ret;
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ char *dash;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
+ snprintf(priv->name, ARRAY_SIZE(priv->name), "%s", id->name);
+ dash = strchr(priv->name, '-');
+ if (dash)
+ *dash = '\0';
+
+ platform_set_drvdata(pdev, priv);
+
+ /* Register sysfs hooks */
+ ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_base);
+ if (ret)
+ return ret;
+
+ if (id->driver_data & MC13783_ADC_16CHANS) {
+ ret = sysfs_create_group(&pdev->dev.kobj,
+ &mc13783_group_16chans);
+ if (ret)
+ goto out_err_create_16chans;
+ }
+
+ if (!mc13783_adc_use_touchscreen(pdev)) {
+ ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
+ if (ret)
+ goto out_err_create_ts;
+ }
+
+ priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(priv->hwmon_dev)) {
+ ret = PTR_ERR(priv->hwmon_dev);
+ dev_err(&pdev->dev,
+ "hwmon_device_register failed with %d.\n", ret);
+ goto out_err_register;
+ }
+
+ return 0;
+
+out_err_register:
+
+ if (!mc13783_adc_use_touchscreen(pdev))
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
+out_err_create_ts:
+
+ if (id->driver_data & MC13783_ADC_16CHANS)
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans);
+out_err_create_16chans:
+
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base);
+ return ret;
+}
+
+static int mc13783_adc_remove(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data;
+
+ hwmon_device_unregister(priv->hwmon_dev);
+
+ if (!mc13783_adc_use_touchscreen(pdev))
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
+
+ if (driver_data & MC13783_ADC_16CHANS)
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans);
+
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base);
+
+ return 0;
+}
+
+static const struct platform_device_id mc13783_adc_idtable[] = {
+ {
+ .name = "mc13783-adc",
+ .driver_data = MC13783_ADC_16CHANS,
+ }, {
+ .name = "mc13892-adc",
+ .driver_data = MC13783_ADC_BPDIV2,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable);
+
+static struct platform_driver mc13783_adc_driver = {
+ .remove = mc13783_adc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .id_table = mc13783_adc_idtable,
+};
+
+module_platform_driver_probe(mc13783_adc_driver, mc13783_adc_probe);
+
+MODULE_DESCRIPTION("MC13783 ADC driver");
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c
new file mode 100644
index 000000000..e093b1998
--- /dev/null
+++ b/drivers/hwmon/mcp3021.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * mcp3021.c - driver for Microchip MCP3021 and MCP3221
+ *
+ * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc.
+ * Author: Mingkai Hu <Mingkai.hu@freescale.com>
+ * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
+ * DT support added by Clemens Gruber <clemens.gruber@pqgruber.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+/* Vdd / reference voltage in millivolt */
+#define MCP3021_VDD_REF_MAX 5500
+#define MCP3021_VDD_REF_MIN 2700
+#define MCP3021_VDD_REF_DEFAULT 3300
+
+/* output format */
+#define MCP3021_SAR_SHIFT 2
+#define MCP3021_SAR_MASK 0x3ff
+#define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */
+
+#define MCP3221_SAR_SHIFT 0
+#define MCP3221_SAR_MASK 0xfff
+#define MCP3221_OUTPUT_RES 12 /* 12-bit resolution */
+
+enum chips {
+ mcp3021,
+ mcp3221
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+struct mcp3021_data {
+ struct i2c_client *client;
+ u32 vdd; /* supply and reference voltage in millivolt */
+ u16 sar_shift;
+ u16 sar_mask;
+ u8 output_res;
+};
+
+static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
+{
+ 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)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ /* The output code of the MCP3021 is transmitted with MSB first. */
+ reg = be16_to_cpu(buf);
+
+ /*
+ * The ten-bit output code is composed of the lower 4-bit of the
+ * first byte and the upper 6-bit of the second byte.
+ */
+ reg = (reg >> data->sar_shift) & data->sar_mask;
+
+ *val = volts_from_reg(data, reg);
+
+ return 0;
+}
+
+static umode_t mcp3021_is_visible(const void *_data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type != hwmon_in)
+ return 0;
+
+ if (attr != hwmon_in_input)
+ return 0;
+
+ return 0444;
+}
+
+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)
+{
+ 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;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct mcp3021_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+
+ if (np) {
+ if (!of_property_read_u32(np, "reference-voltage-microvolt",
+ &data->vdd))
+ data->vdd /= 1000;
+ else
+ data->vdd = MCP3021_VDD_REF_DEFAULT;
+ } else {
+ u32 *pdata = dev_get_platdata(&client->dev);
+
+ if (pdata)
+ data->vdd = *pdata;
+ else
+ data->vdd = MCP3021_VDD_REF_DEFAULT;
+ }
+
+ switch (i2c_match_id(mcp3021_id, client)->driver_data) {
+ case mcp3021:
+ data->sar_shift = MCP3021_SAR_SHIFT;
+ data->sar_mask = MCP3021_SAR_MASK;
+ data->output_res = MCP3021_OUTPUT_RES;
+ break;
+
+ case mcp3221:
+ data->sar_shift = MCP3221_SAR_SHIFT;
+ data->sar_mask = MCP3221_SAR_MASK;
+ data->output_res = MCP3221_OUTPUT_RES;
+ break;
+ }
+
+ data->client = client;
+
+ if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN)
+ return -EINVAL;
+
+ 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[] = {
+ { "mcp3021", mcp3021 },
+ { "mcp3221", mcp3221 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp3021_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_mcp3021_match[] = {
+ { .compatible = "microchip,mcp3021", .data = (void *)mcp3021 },
+ { .compatible = "microchip,mcp3221", .data = (void *)mcp3221 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_mcp3021_match);
+#endif
+
+static struct i2c_driver mcp3021_driver = {
+ .driver = {
+ .name = "mcp3021",
+ .of_match_table = of_match_ptr(of_mcp3021_match),
+ },
+ .probe_new = mcp3021_probe,
+ .id_table = mcp3021_id,
+};
+
+module_i2c_driver(mcp3021_driver);
+
+MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>");
+MODULE_DESCRIPTION("Microchip MCP3021/MCP3221 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/menf21bmc_hwmon.c b/drivers/hwmon/menf21bmc_hwmon.c
new file mode 100644
index 000000000..adb50115c
--- /dev/null
+++ b/drivers/hwmon/menf21bmc_hwmon.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MEN 14F021P00 Board Management Controller (BMC) hwmon driver.
+ *
+ * This is the core hwmon driver of the MEN 14F021P00 BMC.
+ * The BMC monitors the board voltages which can be access with this
+ * driver through sysfs.
+ *
+ * Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#define DRV_NAME "menf21bmc_hwmon"
+
+#define BMC_VOLT_COUNT 5
+#define MENF21BMC_V33 0
+#define MENF21BMC_V5 1
+#define MENF21BMC_V12 2
+#define MENF21BMC_V5_SB 3
+#define MENF21BMC_VBAT 4
+
+#define IDX_TO_VOLT_MIN_CMD(idx) (0x40 + idx)
+#define IDX_TO_VOLT_MAX_CMD(idx) (0x50 + idx)
+#define IDX_TO_VOLT_INP_CMD(idx) (0x60 + idx)
+
+struct menf21bmc_hwmon {
+ bool valid;
+ struct i2c_client *i2c_client;
+ unsigned long last_update;
+ int in_val[BMC_VOLT_COUNT];
+ int in_min[BMC_VOLT_COUNT];
+ int in_max[BMC_VOLT_COUNT];
+};
+
+static const char *const input_names[] = {
+ [MENF21BMC_V33] = "MON_3_3V",
+ [MENF21BMC_V5] = "MON_5V",
+ [MENF21BMC_V12] = "MON_12V",
+ [MENF21BMC_V5_SB] = "5V_STANDBY",
+ [MENF21BMC_VBAT] = "VBAT"
+};
+
+static struct menf21bmc_hwmon *menf21bmc_hwmon_update(struct device *dev)
+{
+ int i;
+ int val;
+ struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+ struct menf21bmc_hwmon *data_ret = drv_data;
+
+ if (time_after(jiffies, drv_data->last_update + HZ)
+ || !drv_data->valid) {
+ for (i = 0; i < BMC_VOLT_COUNT; i++) {
+ val = i2c_smbus_read_word_data(drv_data->i2c_client,
+ IDX_TO_VOLT_INP_CMD(i));
+ if (val < 0) {
+ data_ret = ERR_PTR(val);
+ goto abort;
+ }
+ drv_data->in_val[i] = val;
+ }
+ drv_data->last_update = jiffies;
+ drv_data->valid = true;
+ }
+abort:
+ return data_ret;
+}
+
+static int menf21bmc_hwmon_get_volt_limits(struct menf21bmc_hwmon *drv_data)
+{
+ int i, val;
+
+ for (i = 0; i < BMC_VOLT_COUNT; i++) {
+ val = i2c_smbus_read_word_data(drv_data->i2c_client,
+ IDX_TO_VOLT_MIN_CMD(i));
+ if (val < 0)
+ return val;
+
+ drv_data->in_min[i] = val;
+
+ val = i2c_smbus_read_word_data(drv_data->i2c_client,
+ IDX_TO_VOLT_MAX_CMD(i));
+ if (val < 0)
+ return val;
+
+ drv_data->in_max[i] = val;
+ }
+ return 0;
+}
+
+static ssize_t
+label_show(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%s\n", input_names[attr->index]);
+}
+
+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 menf21bmc_hwmon *drv_data = menf21bmc_hwmon_update(dev);
+
+ if (IS_ERR(drv_data))
+ return PTR_ERR(drv_data);
+
+ return sprintf(buf, "%d\n", drv_data->in_val[attr->index]);
+}
+
+static ssize_t
+min_show(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", drv_data->in_min[attr->index]);
+}
+
+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 menf21bmc_hwmon *drv_data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", drv_data->in_max[attr->index]);
+}
+
+static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0);
+static SENSOR_DEVICE_ATTR_RO(in0_min, min, 0);
+static SENSOR_DEVICE_ATTR_RO(in0_max, max, 0);
+static SENSOR_DEVICE_ATTR_RO(in0_label, label, 0);
+static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1);
+static SENSOR_DEVICE_ATTR_RO(in1_min, min, 1);
+static SENSOR_DEVICE_ATTR_RO(in1_max, max, 1);
+static SENSOR_DEVICE_ATTR_RO(in1_label, label, 1);
+static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2);
+static SENSOR_DEVICE_ATTR_RO(in2_min, min, 2);
+static SENSOR_DEVICE_ATTR_RO(in2_max, max, 2);
+static SENSOR_DEVICE_ATTR_RO(in2_label, label, 2);
+static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3);
+static SENSOR_DEVICE_ATTR_RO(in3_min, min, 3);
+static SENSOR_DEVICE_ATTR_RO(in3_max, max, 3);
+static SENSOR_DEVICE_ATTR_RO(in3_label, label, 3);
+static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4);
+static SENSOR_DEVICE_ATTR_RO(in4_min, min, 4);
+static SENSOR_DEVICE_ATTR_RO(in4_max, max, 4);
+static SENSOR_DEVICE_ATTR_RO(in4_label, label, 4);
+
+static struct attribute *menf21bmc_hwmon_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_label.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_label.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_label.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_label.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_label.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(menf21bmc_hwmon);
+
+static int menf21bmc_hwmon_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct menf21bmc_hwmon *drv_data;
+ struct i2c_client *i2c_client = to_i2c_client(pdev->dev.parent);
+ struct device *hwmon_dev;
+
+ drv_data = devm_kzalloc(&pdev->dev, sizeof(struct menf21bmc_hwmon),
+ GFP_KERNEL);
+ if (!drv_data)
+ return -ENOMEM;
+
+ drv_data->i2c_client = i2c_client;
+
+ ret = menf21bmc_hwmon_get_volt_limits(drv_data);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read sensor limits");
+ return ret;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+ "menf21bmc", drv_data,
+ menf21bmc_hwmon_groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(&pdev->dev, "MEN 14F021P00 BMC hwmon device enabled");
+
+ return 0;
+}
+
+static struct platform_driver menf21bmc_hwmon = {
+ .probe = menf21bmc_hwmon_probe,
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+module_platform_driver(menf21bmc_hwmon);
+
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_DESCRIPTION("MEN 14F021P00 BMC hwmon");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:menf21bmc_hwmon");
diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c
new file mode 100644
index 000000000..96017cc8d
--- /dev/null
+++ b/drivers/hwmon/mlxreg-fan.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+//
+// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2018 Vadim Pasternak <vadimp@mellanox.com>
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/platform_data/mlxreg.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+
+#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% */
+#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
+#define MLXREG_FAN_TACHO_DIV_DEF (MLXREG_FAN_TACHO_DIV_MIN * 4)
+#define MLXREG_FAN_TACHO_DIV_SCALE_MAX 64
+/*
+ * FAN datasheet defines the formula for RPM calculations as RPM = 15/t-high.
+ * The logic in a programmable device measures the time t-high by sampling the
+ * tachometer every t-sample (with the default value 11.32 uS) and increment
+ * a counter (N) as long as the pulse has not change:
+ * RPM = 15 / (t-sample * (K + Regval)), where:
+ * Regval: is the value read from the programmable device register;
+ * - 0xff - represents tachometer fault;
+ * - 0xfe - represents tachometer minimum value , which is 4444 RPM;
+ * - 0x00 - represents tachometer maximum value , which is 300000 RPM;
+ * K: is 44 and it represents the minimum allowed samples per pulse;
+ * N: is equal K + Regval;
+ * In order to calculate RPM from the register value the following formula is
+ * used: RPM = 15 / ((Regval + K) * 11.32) * 10^(-6)), which in the
+ * default case is modified to:
+ * RPM = 15000000 * 100 / ((Regval + 44) * 1132);
+ * - for Regval 0x00, RPM will be 15000000 * 100 / (44 * 1132) = 30115;
+ * - for Regval 0xfe, RPM will be 15000000 * 100 / ((254 + 44) * 1132) = 4446;
+ * In common case the formula is modified to:
+ * RPM = 15000000 * 100 / ((Regval + samples) * divider).
+ */
+#define MLXREG_FAN_GET_RPM(rval, d, s) (DIV_ROUND_CLOSEST(15000000 * 100, \
+ ((rval) + (s)) * (d)))
+#define MLXREG_FAN_GET_FAULT(val, mask) ((val) == (mask))
+#define MLXREG_FAN_PWM_DUTY2STATE(duty) (DIV_ROUND_CLOSEST((duty) * \
+ MLXREG_FAN_MAX_STATE, \
+ MLXREG_FAN_MAX_DUTY))
+#define MLXREG_FAN_PWM_STATE2DUTY(stat) (DIV_ROUND_CLOSEST((stat) * \
+ 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;
+};
+
+/*
+ * struct mlxreg_fan - private data (internal use):
+ *
+ * @dev: basic device;
+ * @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;
+ */
+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[MLXREG_FAN_MAX_PWM];
+ int tachos_per_drwr;
+ int samples;
+ int divider;
+};
+
+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;
+
+ switch (type) {
+ case hwmon_fan:
+ 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, &regval);
+ 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, &regval);
+ if (err)
+ return err;
+
+ if (MLXREG_FAN_GET_FAULT(regval, tacho->mask)) {
+ /* FAN is broken - return zero for FAN speed. */
+ *val = 0;
+ return 0;
+ }
+
+ *val = MLXREG_FAN_GET_RPM(regval, fan->divider,
+ fan->samples);
+ break;
+
+ case hwmon_fan_fault:
+ err = regmap_read(fan->regmap, tacho->reg, &regval);
+ if (err)
+ return err;
+
+ *val = MLXREG_FAN_GET_FAULT(regval, tacho->mask);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ case hwmon_pwm:
+ pwm = &fan->pwm[channel];
+ switch (attr) {
+ case hwmon_pwm_input:
+ err = regmap_read(fan->regmap, pwm->reg, &regval);
+ if (err)
+ return err;
+
+ *val = regval;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int
+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:
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < MLXREG_FAN_MIN_DUTY ||
+ val > MLXREG_FAN_MAX_DUTY)
+ return -EINVAL;
+ 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;
+ }
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static umode_t
+mlxreg_fan_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ if (!(((struct mlxreg_fan *)data)->tacho[channel].connected))
+ return 0;
+
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_fault:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+
+ case hwmon_pwm:
+ if (!(((struct mlxreg_fan *)data)->pwm[channel].connected))
+ return 0;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ 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,
+ 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_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_F_INPUT | HWMON_F_FAULT),
+ HWMON_CHANNEL_INFO(pwm,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops mlxreg_fan_hwmon_hwmon_ops = {
+ .is_visible = mlxreg_fan_is_visible,
+ .read = mlxreg_fan_read,
+ .write = mlxreg_fan_write,
+};
+
+static const struct hwmon_chip_info mlxreg_fan_hwmon_chip_info = {
+ .ops = &mlxreg_fan_hwmon_hwmon_ops,
+ .info = mlxreg_fan_hwmon_info,
+};
+
+static int mlxreg_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = MLXREG_FAN_MAX_STATE;
+ return 0;
+}
+
+static int mlxreg_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+
+{
+ struct mlxreg_fan_pwm *pwm = cdev->devdata;
+ struct mlxreg_fan *fan = pwm->fan;
+ u32 regval;
+ int err;
+
+ err = regmap_read(fan->regmap, pwm->reg, &regval);
+ if (err) {
+ dev_err(fan->dev, "Failed to query PWM duty\n");
+ return err;
+ }
+
+ *state = MLXREG_FAN_PWM_DUTY2STATE(regval);
+
+ return 0;
+}
+
+static int mlxreg_fan_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+
+{
+ struct mlxreg_fan_pwm *pwm = cdev->devdata;
+ struct mlxreg_fan *fan = pwm->fan;
+ int err;
+
+ if (state > MLXREG_FAN_MAX_STATE)
+ return -EINVAL;
+
+ /* 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");
+ return err;
+ }
+ return 0;
+}
+
+static const struct thermal_cooling_device_ops mlxreg_fan_cooling_ops = {
+ .get_max_state = mlxreg_fan_get_max_state,
+ .get_cur_state = mlxreg_fan_get_cur_state,
+ .set_cur_state = mlxreg_fan_set_cur_state,
+};
+
+static int mlxreg_fan_connect_verify(struct mlxreg_fan *fan,
+ struct mlxreg_core_data *data)
+{
+ u32 regval;
+ int err;
+
+ err = regmap_read(fan->regmap, data->capability, &regval);
+ if (err) {
+ dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
+ data->capability);
+ return err;
+ }
+
+ 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, &regval);
+ 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)
+{
+ u32 regval;
+ int err;
+
+ err = regmap_read(fan->regmap, data->capability, &regval);
+ if (err) {
+ dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
+ data->capability);
+ return err;
+ }
+
+ /*
+ * Set divider value according to the capability register, in case it
+ * contains valid value. Otherwise use default value. The purpose of
+ * this validation is to protect against the old hardware, in which
+ * this register can return zero.
+ */
+ if (regval > 0 && regval <= MLXREG_FAN_TACHO_DIV_SCALE_MAX)
+ fan->divider = regval * MLXREG_FAN_TACHO_DIV_MIN;
+
+ return 0;
+}
+
+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 err;
+
+ fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
+ fan->divider = MLXREG_FAN_TACHO_DIV_DEF;
+ for (i = 0; i < pdata->counter; i++, data++) {
+ if (strnstr(data->label, "tacho", sizeof(data->label))) {
+ if (tacho_num == MLXREG_FAN_MAX_TACHO) {
+ dev_err(fan->dev, "too many tacho entries: %s\n",
+ data->label);
+ return -EINVAL;
+ }
+
+ if (data->capability) {
+ err = mlxreg_fan_connect_verify(fan, data);
+ if (err < 0)
+ return err;
+ else if (!err) {
+ tacho_num++;
+ continue;
+ }
+ }
+
+ 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 (pwm_num == MLXREG_FAN_MAX_TACHO) {
+ dev_err(fan->dev, "too many pwm entries: %s\n",
+ data->label);
+ return -EINVAL;
+ }
+
+ /* 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",
+ data->label);
+ return -EINVAL;
+ }
+ /* Validate that conf parameters are not zeros. */
+ if (!data->mask && !data->bit && !data->capability) {
+ dev_err(fan->dev, "invalid conf entry params: %s\n",
+ data->label);
+ return -EINVAL;
+ }
+ if (data->capability) {
+ err = mlxreg_fan_speed_divider_get(fan, data);
+ if (err)
+ return err;
+ } else {
+ if (data->mask)
+ fan->samples = data->mask;
+ if (data->bit)
+ fan->divider = data->bit;
+ }
+ configured = true;
+ } else {
+ dev_err(fan->dev, "invalid label: %s\n", data->label);
+ return -EINVAL;
+ }
+ }
+
+ if (pdata->capability) {
+ int drwr_avail;
+ u32 regval;
+
+ /* Obtain the number of FAN drawers, supported by system. */
+ err = regmap_read(fan->regmap, pdata->capability, &regval);
+ 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;
+}
+
+static int mlxreg_fan_probe(struct platform_device *pdev)
+{
+ struct mlxreg_core_platform_data *pdata;
+ struct device *dev = &pdev->dev;
+ struct mlxreg_fan *fan;
+ struct device *hwm;
+ int err;
+
+ pdata = dev_get_platdata(dev);
+ if (!pdata) {
+ dev_err(dev, "Failed to get platform data.\n");
+ return -EINVAL;
+ }
+
+ fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL);
+ if (!fan)
+ return -ENOMEM;
+
+ fan->dev = dev;
+ fan->regmap = pdata->regmap;
+
+ err = mlxreg_fan_config(fan, pdata);
+ if (err)
+ return err;
+
+ hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan",
+ fan,
+ &mlxreg_fan_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwm)) {
+ dev_err(dev, "Failed to register hwmon device\n");
+ return PTR_ERR(hwm);
+ }
+
+ if (IS_REACHABLE(CONFIG_THERMAL))
+ err = mlxreg_fan_cooling_config(dev, fan);
+
+ return err;
+}
+
+static struct platform_driver mlxreg_fan_driver = {
+ .driver = {
+ .name = "mlxreg-fan",
+ },
+ .probe = mlxreg_fan_probe,
+};
+
+module_platform_driver(mlxreg_fan_driver);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox FAN driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mlxreg-fan");
diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c
new file mode 100644
index 000000000..394a4c7e4
--- /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
new file mode 100644
index 000000000..a872f783e
--- /dev/null
+++ b/drivers/hwmon/nct6683.c
@@ -0,0 +1,1505 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * nct6683 - Driver for the hardware monitoring functionality of
+ * Nuvoton NCT6683D/NCT6686D/NCT6687D eSIO
+ *
+ * Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from nct6775 driver
+ * Copyright (C) 2012, 2013 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Supports the following chips:
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+enum kinds { nct6683, nct6686, nct6687 };
+
+static bool force;
+module_param(force, bool, 0);
+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"
+
+/*
+ * Super-I/O constants and functions
+ */
+
+#define NCT6683_LD_ACPI 0x0a
+#define NCT6683_LD_HWM 0x0b
+#define NCT6683_LD_VID 0x0d
+
+#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_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
+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 4 /* Use EC port 1 */
+#define IOREGION_LENGTH 4
+
+#define EC_PAGE_REG 0
+#define EC_INDEX_REG 1
+#define EC_DATA_REG 2
+#define EC_EVENT_REG 3
+
+/* Common and NCT6683 specific data */
+
+#define NCT6683_NUM_REG_MON 32
+#define NCT6683_NUM_REG_FAN 16
+#define NCT6683_NUM_REG_PWM 8
+
+#define NCT6683_REG_MON(x) (0x100 + (x) * 2)
+#define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2)
+#define NCT6683_REG_PWM(x) (0x160 + (x))
+#define NCT6683_REG_PWM_WRITE(x) (0xa28 + (x))
+
+#define NCT6683_REG_MON_STS(x) (0x174 + (x))
+#define NCT6683_REG_IDLE(x) (0x178 + (x))
+
+#define NCT6683_REG_FAN_STS(x) (0x17c + (x))
+#define NCT6683_REG_FAN_ERRSTS 0x17e
+#define NCT6683_REG_FAN_INITSTS 0x17f
+
+#define NCT6683_HWM_CFG 0x180
+
+#define NCT6683_REG_MON_CFG(x) (0x1a0 + (x))
+#define NCT6683_REG_FANIN_CFG(x) (0x1c0 + (x))
+#define NCT6683_REG_FANOUT_CFG(x) (0x1d0 + (x))
+
+#define NCT6683_REG_INTEL_TEMP_MAX(x) (0x901 + (x) * 16)
+#define NCT6683_REG_INTEL_TEMP_CRIT(x) (0x90d + (x) * 16)
+
+#define NCT6683_REG_TEMP_HYST(x) (0x330 + (x)) /* 8 bit */
+#define NCT6683_REG_TEMP_MAX(x) (0x350 + (x)) /* 8 bit */
+#define NCT6683_REG_MON_HIGH(x) (0x370 + (x) * 2) /* 8 bit */
+#define NCT6683_REG_MON_LOW(x) (0x371 + (x) * 2) /* 8 bit */
+
+#define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */
+
+#define NCT6683_REG_FAN_CFG_CTRL 0xa01
+#define NCT6683_FAN_CFG_REQ 0x80
+#define NCT6683_FAN_CFG_DONE 0x40
+
+#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
+#define NCT6683_REG_BUILD_DAY 0x606
+#define NCT6683_REG_SERIAL 0x607
+#define NCT6683_REG_VERSION_HI 0x608
+#define NCT6683_REG_VERSION_LO 0x609
+
+#define NCT6683_REG_CR_CASEOPEN 0xe8
+#define NCT6683_CR_CASEOPEN_MASK (1 << 7)
+
+#define NCT6683_REG_CR_BEEP 0xe0
+#define NCT6683_CR_BEEP_MASK (1 << 6)
+
+static const char *const nct6683_mon_label[] = {
+ NULL, /* disabled */
+ "Local",
+ "Diode 0 (curr)",
+ "Diode 1 (curr)",
+ "Diode 2 (curr)",
+ "Diode 0 (volt)",
+ "Diode 1 (volt)",
+ "Diode 2 (volt)",
+ "Thermistor 14",
+ "Thermistor 15",
+ "Thermistor 16",
+ "Thermistor 0",
+ "Thermistor 1",
+ "Thermistor 2",
+ "Thermistor 3",
+ "Thermistor 4",
+ "Thermistor 5", /* 0x10 */
+ "Thermistor 6",
+ "Thermistor 7",
+ "Thermistor 8",
+ "Thermistor 9",
+ "Thermistor 10",
+ "Thermistor 11",
+ "Thermistor 12",
+ "Thermistor 13",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "PECI 0.0", /* 0x20 */
+ "PECI 1.0",
+ "PECI 2.0",
+ "PECI 3.0",
+ "PECI 0.1",
+ "PECI 1.1",
+ "PECI 2.1",
+ "PECI 3.1",
+ "PECI DIMM 0",
+ "PECI DIMM 1",
+ "PECI DIMM 2",
+ "PECI DIMM 3",
+ NULL, NULL, NULL, NULL,
+ "PCH CPU", /* 0x30 */
+ "PCH CHIP",
+ "PCH CHIP CPU MAX",
+ "PCH MCH",
+ "PCH DIMM 0",
+ "PCH DIMM 1",
+ "PCH DIMM 2",
+ "PCH DIMM 3",
+ "SMBus 0",
+ "SMBus 1",
+ "SMBus 2",
+ "SMBus 3",
+ "SMBus 4",
+ "SMBus 5",
+ "DIMM 0",
+ "DIMM 1",
+ "DIMM 2", /* 0x40 */
+ "DIMM 3",
+ "AMD TSI Addr 90h",
+ "AMD TSI Addr 92h",
+ "AMD TSI Addr 94h",
+ "AMD TSI Addr 96h",
+ "AMD TSI Addr 98h",
+ "AMD TSI Addr 9ah",
+ "AMD TSI Addr 9ch",
+ "AMD TSI Addr 9dh",
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "Virtual 0", /* 0x50 */
+ "Virtual 1",
+ "Virtual 2",
+ "Virtual 3",
+ "Virtual 4",
+ "Virtual 5",
+ "Virtual 6",
+ "Virtual 7",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "VCC", /* 0x60 voltage sensors */
+ "VSB",
+ "AVSB",
+ "VTT",
+ "VBAT",
+ "VREF",
+ "VIN0",
+ "VIN1",
+ "VIN2",
+ "VIN3",
+ "VIN4",
+ "VIN5",
+ "VIN6",
+ "VIN7",
+ "VIN8",
+ "VIN9",
+ "VIN10",
+ "VIN11",
+ "VIN12",
+ "VIN13",
+ "VIN14",
+ "VIN15",
+ "VIN16",
+};
+
+#define NUM_MON_LABELS ARRAY_SIZE(nct6683_mon_label)
+#define MON_VOLTAGE_START 0x60
+
+/* ------------------------------------------------------- */
+
+struct nct6683_data {
+ int addr; /* IO base of EC space */
+ int sioreg; /* SIO register */
+ enum kinds kind;
+ u16 customer_id;
+
+ struct device *hwmon_dev;
+ const struct attribute_group *groups[6];
+
+ int temp_num; /* number of temperature attributes */
+ u8 temp_index[NCT6683_NUM_REG_MON];
+ u8 temp_src[NCT6683_NUM_REG_MON];
+
+ u8 in_num; /* number of voltage attributes */
+ u8 in_index[NCT6683_NUM_REG_MON];
+ u8 in_src[NCT6683_NUM_REG_MON];
+
+ struct mutex update_lock; /* used to protect sensor updates */
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Voltage attribute values */
+ u8 in[3][NCT6683_NUM_REG_MON]; /* [0]=in, [1]=in_max, [2]=in_min */
+
+ /* Temperature attribute values */
+ s16 temp_in[NCT6683_NUM_REG_MON];
+ s8 temp[4][NCT6683_NUM_REG_MON];/* [0]=min, [1]=max, [2]=hyst,
+ * [3]=crit
+ */
+
+ /* Fan attribute values */
+ unsigned int rpm[NCT6683_NUM_REG_FAN];
+ u16 fan_min[NCT6683_NUM_REG_FAN];
+ u8 fanin_cfg[NCT6683_NUM_REG_FAN];
+ u8 fanout_cfg[NCT6683_NUM_REG_FAN];
+ u16 have_fan; /* some fan inputs can be disabled */
+
+ u8 have_pwm;
+ u8 pwm[NCT6683_NUM_REG_PWM];
+
+#ifdef CONFIG_PM
+ /* Remember extra register values over suspend/resume */
+ u8 hwm_cfg;
+#endif
+};
+
+struct nct6683_sio_data {
+ int sioreg;
+ enum kinds kind;
+};
+
+struct sensor_device_template {
+ struct device_attribute dev_attr;
+ union {
+ struct {
+ u8 nr;
+ u8 index;
+ } s;
+ int index;
+ } u;
+ bool s2; /* true if both index and nr are used */
+};
+
+struct sensor_device_attr_u {
+ union {
+ struct sensor_device_attribute a1;
+ struct sensor_device_attribute_2 a2;
+ } u;
+ char name[32];
+};
+
+#define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \
+ .attr = {.name = _template, .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \
+ { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
+ .u.index = _index, \
+ .s2 = false }
+
+#define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
+ _nr, _index) \
+ { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
+ .u.s.index = _index, \
+ .u.s.nr = _nr, \
+ .s2 = true }
+
+#define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \
+static struct sensor_device_template sensor_dev_template_##_name \
+ = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \
+ _index)
+
+#define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \
+ _nr, _index) \
+static struct sensor_device_template sensor_dev_template_##_name \
+ = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
+ _nr, _index)
+
+struct sensor_template_group {
+ struct sensor_device_template **templates;
+ umode_t (*is_visible)(struct kobject *, struct attribute *, int);
+ int base;
+};
+
+static struct attribute_group *
+nct6683_create_attr_group(struct device *dev,
+ const struct sensor_template_group *tg,
+ int repeat)
+{
+ struct sensor_device_attribute_2 *a2;
+ struct sensor_device_attribute *a;
+ struct sensor_device_template **t;
+ struct sensor_device_attr_u *su;
+ struct attribute_group *group;
+ struct attribute **attrs;
+ int i, count;
+
+ if (repeat <= 0)
+ return ERR_PTR(-EINVAL);
+
+ t = tg->templates;
+ for (count = 0; *t; t++, count++)
+ ;
+
+ if (count == 0)
+ return ERR_PTR(-EINVAL);
+
+ group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
+ if (group == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs),
+ GFP_KERNEL);
+ if (attrs == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)),
+ GFP_KERNEL);
+ if (su == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ group->attrs = attrs;
+ group->is_visible = tg->is_visible;
+
+ for (i = 0; i < repeat; i++) {
+ t = tg->templates;
+ while (*t) {
+ snprintf(su->name, sizeof(su->name),
+ (*t)->dev_attr.attr.name, tg->base + i);
+ if ((*t)->s2) {
+ a2 = &su->u.a2;
+ sysfs_attr_init(&a2->dev_attr.attr);
+ a2->dev_attr.attr.name = su->name;
+ a2->nr = (*t)->u.s.nr + i;
+ a2->index = (*t)->u.s.index;
+ a2->dev_attr.attr.mode =
+ (*t)->dev_attr.attr.mode;
+ a2->dev_attr.show = (*t)->dev_attr.show;
+ a2->dev_attr.store = (*t)->dev_attr.store;
+ *attrs = &a2->dev_attr.attr;
+ } else {
+ a = &su->u.a1;
+ sysfs_attr_init(&a->dev_attr.attr);
+ a->dev_attr.attr.name = su->name;
+ a->index = (*t)->u.index + i;
+ a->dev_attr.attr.mode =
+ (*t)->dev_attr.attr.mode;
+ a->dev_attr.show = (*t)->dev_attr.show;
+ a->dev_attr.store = (*t)->dev_attr.store;
+ *attrs = &a->dev_attr.attr;
+ }
+ attrs++;
+ su++;
+ t++;
+ }
+ }
+
+ return group;
+}
+
+/* LSB is 16 mV, except for the following sources, where it is 32 mV */
+#define MON_SRC_VCC 0x60
+#define MON_SRC_VSB 0x61
+#define MON_SRC_AVSB 0x62
+#define MON_SRC_VBAT 0x64
+
+static inline long in_from_reg(u16 reg, 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 reg * scale;
+}
+
+static u16 nct6683_read(struct nct6683_data *data, u16 reg)
+{
+ int res;
+
+ outb_p(0xff, data->addr + EC_PAGE_REG); /* unlock */
+ outb_p(reg >> 8, data->addr + EC_PAGE_REG);
+ outb_p(reg & 0xff, data->addr + EC_INDEX_REG);
+ res = inb_p(data->addr + EC_DATA_REG);
+ return res;
+}
+
+static u16 nct6683_read16(struct nct6683_data *data, u16 reg)
+{
+ return (nct6683_read(data, reg) << 8) | nct6683_read(data, reg + 1);
+}
+
+static void nct6683_write(struct nct6683_data *data, u16 reg, u16 value)
+{
+ outb_p(0xff, data->addr + EC_PAGE_REG); /* unlock */
+ outb_p(reg >> 8, data->addr + EC_PAGE_REG);
+ outb_p(reg & 0xff, data->addr + EC_INDEX_REG);
+ outb_p(value & 0xff, data->addr + EC_DATA_REG);
+}
+
+static int get_in_reg(struct nct6683_data *data, int nr, int index)
+{
+ int ch = data->in_index[index];
+ int reg = -EINVAL;
+
+ switch (nr) {
+ case 0:
+ reg = NCT6683_REG_MON(ch);
+ break;
+ case 1:
+ if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL)
+ reg = NCT6683_REG_MON_LOW(ch);
+ break;
+ case 2:
+ if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL)
+ reg = NCT6683_REG_MON_HIGH(ch);
+ break;
+ default:
+ break;
+ }
+ return reg;
+}
+
+static int get_temp_reg(struct nct6683_data *data, int nr, int index)
+{
+ int ch = data->temp_index[index];
+ int reg = -EINVAL;
+
+ switch (data->customer_id) {
+ case NCT6683_CUSTOMER_ID_INTEL:
+ switch (nr) {
+ default:
+ case 1: /* max */
+ reg = NCT6683_REG_INTEL_TEMP_MAX(ch);
+ break;
+ case 3: /* crit */
+ reg = NCT6683_REG_INTEL_TEMP_CRIT(ch);
+ break;
+ }
+ break;
+ case NCT6683_CUSTOMER_ID_MITAC:
+ default:
+ switch (nr) {
+ default:
+ case 0: /* min */
+ reg = NCT6683_REG_MON_LOW(ch);
+ break;
+ case 1: /* max */
+ reg = NCT6683_REG_TEMP_MAX(ch);
+ break;
+ case 2: /* hyst */
+ reg = NCT6683_REG_TEMP_HYST(ch);
+ break;
+ case 3: /* crit */
+ reg = NCT6683_REG_MON_HIGH(ch);
+ break;
+ }
+ break;
+ }
+ return reg;
+}
+
+static void nct6683_update_pwm(struct device *dev)
+{
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < NCT6683_NUM_REG_PWM; i++) {
+ if (!(data->have_pwm & (1 << i)))
+ continue;
+ data->pwm[i] = nct6683_read(data, NCT6683_REG_PWM(i));
+ }
+}
+
+static struct nct6683_data *nct6683_update_device(struct device *dev)
+{
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ int i, j;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ /* Measured voltages and limits */
+ for (i = 0; i < data->in_num; i++) {
+ for (j = 0; j < 3; j++) {
+ int reg = get_in_reg(data, j, i);
+
+ if (reg >= 0)
+ data->in[j][i] =
+ nct6683_read(data, reg);
+ }
+ }
+
+ /* Measured temperatures and limits */
+ for (i = 0; i < data->temp_num; i++) {
+ u8 ch = data->temp_index[i];
+
+ data->temp_in[i] = nct6683_read16(data,
+ NCT6683_REG_MON(ch));
+ for (j = 0; j < 4; j++) {
+ int reg = get_temp_reg(data, j, i);
+
+ if (reg >= 0)
+ data->temp[j][i] =
+ nct6683_read(data, reg);
+ }
+ }
+
+ /* Measured fan speeds and limits */
+ for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
+ if (!(data->have_fan & (1 << i)))
+ continue;
+
+ data->rpm[i] = nct6683_read16(data,
+ NCT6683_REG_FAN_RPM(i));
+ data->fan_min[i] = nct6683_read16(data,
+ NCT6683_REG_FAN_MIN(i));
+ }
+
+ nct6683_update_pwm(dev);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+/*
+ * Sysfs callback functions
+ */
+static ssize_t
+show_in_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+ int nr = sattr->index;
+
+ return sprintf(buf, "%s\n", nct6683_mon_label[data->in_src[nr]]);
+}
+
+static ssize_t
+show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+ int index = sattr->index;
+ int nr = sattr->nr;
+
+ return sprintf(buf, "%ld\n",
+ in_from_reg(data->in[index][nr], data->in_index[index]));
+}
+
+static umode_t nct6683_in_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ int nr = index % 4; /* attribute */
+
+ /*
+ * Voltage limits exist for Intel boards,
+ * but register location and encoding is unknown
+ */
+ if ((nr == 2 || nr == 3) &&
+ data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
+ return 0;
+
+ return attr->mode;
+}
+
+SENSOR_TEMPLATE(in_label, "in%d_label", S_IRUGO, show_in_label, NULL, 0);
+SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0);
+SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IRUGO, show_in_reg, NULL, 0, 1);
+SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IRUGO, show_in_reg, NULL, 0, 2);
+
+static struct sensor_device_template *nct6683_attributes_in_template[] = {
+ &sensor_dev_template_in_label,
+ &sensor_dev_template_in_input,
+ &sensor_dev_template_in_min,
+ &sensor_dev_template_in_max,
+ NULL
+};
+
+static const struct sensor_template_group nct6683_in_template_group = {
+ .templates = nct6683_attributes_in_template,
+ .is_visible = nct6683_in_is_visible,
+};
+
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->rpm[sattr->index]);
+}
+
+static ssize_t
+show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6683_data *data = nct6683_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+
+ return sprintf(buf, "%d\n", data->fan_min[nr]);
+}
+
+static ssize_t
+show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ ((data->fanin_cfg[sattr->index] >> 5) & 0x03) + 1);
+}
+
+static umode_t nct6683_fan_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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 */
+
+ if (!(data->have_fan & (1 << fan)))
+ return 0;
+
+ /*
+ * Intel may have minimum fan speed limits,
+ * but register location and encoding are unknown.
+ */
+ if (nr == 2 && data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
+ return 0;
+
+ return attr->mode;
+}
+
+SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0);
+SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IRUGO, show_fan_pulses, NULL, 0);
+SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IRUGO, show_fan_min, NULL, 0);
+
+/*
+ * nct6683_fan_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 sensor_device_template *nct6683_attributes_fan_template[] = {
+ &sensor_dev_template_fan_input,
+ &sensor_dev_template_fan_pulses,
+ &sensor_dev_template_fan_min,
+ NULL
+};
+
+static const struct sensor_template_group nct6683_fan_template_group = {
+ .templates = nct6683_attributes_fan_template,
+ .is_visible = nct6683_fan_is_visible,
+ .base = 1,
+};
+
+static ssize_t
+show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+ int nr = sattr->index;
+
+ return sprintf(buf, "%s\n", nct6683_mon_label[data->temp_src[nr]]);
+}
+
+static ssize_t
+show_temp8(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+ int index = sattr->index;
+ int nr = sattr->nr;
+
+ return sprintf(buf, "%d\n", data->temp[index][nr] * 1000);
+}
+
+static ssize_t
+show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+ int nr = sattr->index;
+ int temp = data->temp[1][nr] - data->temp[2][nr];
+
+ return sprintf(buf, "%d\n", temp * 1000);
+}
+
+static ssize_t
+show_temp16(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6683_data *data = nct6683_update_device(dev);
+ int index = sattr->index;
+
+ return sprintf(buf, "%d\n", (data->temp_in[index] / 128) * 500);
+}
+
+/*
+ * Temperature sensor type is determined by temperature source
+ * and can not be modified.
+ * 0x02..0x07: Thermal diode
+ * 0x08..0x18: Thermistor
+ * 0x20..0x2b: Intel PECI
+ * 0x42..0x49: AMD TSI
+ * Others are unspecified (not visible)
+ */
+
+static int get_temp_type(u8 src)
+{
+ if (src >= 0x02 && src <= 0x07)
+ return 3; /* thermal diode */
+ else if (src >= 0x08 && src <= 0x18)
+ return 4; /* thermistor */
+ else if (src >= 0x20 && src <= 0x2b)
+ return 6; /* PECI */
+ else if (src >= 0x42 && src <= 0x49)
+ return 5;
+
+ return 0;
+}
+
+static ssize_t
+show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6683_data *data = nct6683_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ return sprintf(buf, "%d\n", get_temp_type(data->temp_src[nr]));
+}
+
+static umode_t nct6683_temp_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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 */
+
+ /*
+ * Intel does not have low temperature limits or temperature hysteresis
+ * registers, or at least register location and encoding is unknown.
+ */
+ if ((nr == 2 || nr == 4) &&
+ data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
+ return 0;
+
+ if (nr == 6 && get_temp_type(data->temp_src[temp]) == 0)
+ return 0; /* type */
+
+ return attr->mode;
+}
+
+SENSOR_TEMPLATE(temp_input, "temp%d_input", S_IRUGO, show_temp16, NULL, 0);
+SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0);
+SENSOR_TEMPLATE_2(temp_min, "temp%d_min", S_IRUGO, show_temp8, NULL, 0, 0);
+SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO, show_temp8, NULL, 0, 1);
+SENSOR_TEMPLATE(temp_max_hyst, "temp%d_max_hyst", S_IRUGO, show_temp_hyst, NULL,
+ 0);
+SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO, show_temp8, NULL, 0, 3);
+SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO, show_temp_type, NULL, 0);
+
+/*
+ * nct6683_temp_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 sensor_device_template *nct6683_attributes_temp_template[] = {
+ &sensor_dev_template_temp_input,
+ &sensor_dev_template_temp_label,
+ &sensor_dev_template_temp_min, /* 2 */
+ &sensor_dev_template_temp_max, /* 3 */
+ &sensor_dev_template_temp_max_hyst, /* 4 */
+ &sensor_dev_template_temp_crit, /* 5 */
+ &sensor_dev_template_temp_type, /* 6 */
+ NULL
+};
+
+static const struct sensor_template_group nct6683_temp_template_group = {
+ .templates = nct6683_attributes_temp_template,
+ .is_visible = nct6683_temp_is_visible,
+ .base = 1,
+};
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6683_data *data = nct6683_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int index = sattr->index;
+
+ return sprintf(buf, "%d\n", data->pwm[index]);
+}
+
+static ssize_t
+store_pwm(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 nct6683_data *data = dev_get_drvdata(dev);
+ int index = sattr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) || val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_REQ);
+ usleep_range(1000, 2000);
+ nct6683_write(data, NCT6683_REG_PWM_WRITE(index), val);
+ nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_DONE);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+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 = kobj_to_dev(kobj);
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ int pwm = index; /* pwm index */
+
+ if (!(data->have_pwm & (1 << pwm)))
+ return 0;
+
+ /* Only update pwm values for Mitac boards */
+ if (data->customer_id == NCT6683_CUSTOMER_ID_MITAC)
+ return attr->mode | S_IWUSR;
+
+ return attr->mode;
+}
+
+static struct sensor_device_template *nct6683_attributes_pwm_template[] = {
+ &sensor_dev_template_pwm,
+ NULL
+};
+
+static const struct sensor_template_group nct6683_pwm_template_group = {
+ .templates = nct6683_attributes_pwm_template,
+ .is_visible = nct6683_pwm_is_visible,
+ .base = 1,
+};
+
+static ssize_t
+beep_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ int ret;
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+
+ ret = superio_enter(data->sioreg);
+ if (ret)
+ goto error;
+ superio_select(data->sioreg, NCT6683_LD_HWM);
+ reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP);
+ superio_exit(data->sioreg);
+
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%u\n", !!(reg & NCT6683_CR_BEEP_MASK));
+
+error:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t
+beep_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ u8 reg;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) || (val != 0 && val != 1))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ ret = superio_enter(data->sioreg);
+ if (ret) {
+ count = ret;
+ goto error;
+ }
+
+ superio_select(data->sioreg, NCT6683_LD_HWM);
+ reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP);
+ if (val)
+ reg |= NCT6683_CR_BEEP_MASK;
+ else
+ reg &= ~NCT6683_CR_BEEP_MASK;
+ superio_outb(data->sioreg, NCT6683_REG_CR_BEEP, reg);
+ superio_exit(data->sioreg);
+error:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* Case open detection */
+
+static ssize_t
+intrusion0_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ int ret;
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+
+ ret = superio_enter(data->sioreg);
+ if (ret)
+ goto error;
+ superio_select(data->sioreg, NCT6683_LD_ACPI);
+ reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN);
+ superio_exit(data->sioreg);
+
+ mutex_unlock(&data->update_lock);
+
+ return sprintf(buf, "%u\n", !(reg & NCT6683_CR_CASEOPEN_MASK));
+
+error:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static ssize_t
+intrusion0_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ 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.
+ * Caseopen is activ low, clear by writing 1 into the register.
+ */
+
+ ret = superio_enter(data->sioreg);
+ if (ret) {
+ count = ret;
+ goto error;
+ }
+
+ superio_select(data->sioreg, NCT6683_LD_ACPI);
+ reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN);
+ reg |= NCT6683_CR_CASEOPEN_MASK;
+ superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg);
+ reg &= ~NCT6683_CR_CASEOPEN_MASK;
+ superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg);
+ superio_exit(data->sioreg);
+
+ data->valid = false; /* Force cache refresh */
+error:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(intrusion0_alarm);
+static DEVICE_ATTR_RW(beep_enable);
+
+static struct attribute *nct6683_attributes_other[] = {
+ &dev_attr_intrusion0_alarm.attr,
+ &dev_attr_beep_enable.attr,
+ NULL
+};
+
+static const struct attribute_group nct6683_group_other = {
+ .attrs = nct6683_attributes_other,
+};
+
+/* Get the monitoring functions started */
+static inline void nct6683_init_device(struct nct6683_data *data)
+{
+ u8 tmp;
+
+ /* Start hardware monitoring if needed */
+ tmp = nct6683_read(data, NCT6683_HWM_CFG);
+ if (!(tmp & 0x80))
+ nct6683_write(data, NCT6683_HWM_CFG, tmp | 0x80);
+}
+
+/*
+ * There are a total of 24 fan inputs. Each can be configured as input
+ * or as output. A maximum of 16 inputs and 8 outputs is configurable.
+ */
+static void
+nct6683_setup_fans(struct nct6683_data *data)
+{
+ int i;
+ u8 reg;
+
+ for (i = 0; i < NCT6683_NUM_REG_FAN; i++) {
+ reg = nct6683_read(data, NCT6683_REG_FANIN_CFG(i));
+ if (reg & 0x80)
+ data->have_fan |= 1 << i;
+ data->fanin_cfg[i] = reg;
+ }
+ for (i = 0; i < NCT6683_NUM_REG_PWM; i++) {
+ reg = nct6683_read(data, NCT6683_REG_FANOUT_CFG(i));
+ if (reg & 0x80)
+ data->have_pwm |= 1 << i;
+ data->fanout_cfg[i] = reg;
+ }
+}
+
+/*
+ * Translation from monitoring register to temperature and voltage attributes
+ * ==========================================================================
+ *
+ * There are a total of 32 monitoring registers. Each can be assigned to either
+ * a temperature or voltage monitoring source.
+ * NCT6683_REG_MON_CFG(x) defines assignment for each monitoring source.
+ *
+ * Temperature and voltage attribute mapping is determined by walking through
+ * the NCT6683_REG_MON_CFG registers. If the assigned source is
+ * a temperature, temp_index[n] is set to the monitor register index, and
+ * temp_src[n] is set to the temperature source. If the assigned source is
+ * a voltage, the respective values are stored in in_index[] and in_src[],
+ * respectively.
+ */
+
+static void nct6683_setup_sensors(struct nct6683_data *data)
+{
+ u8 reg;
+ int i;
+
+ data->temp_num = 0;
+ data->in_num = 0;
+ for (i = 0; i < NCT6683_NUM_REG_MON; i++) {
+ reg = nct6683_read(data, NCT6683_REG_MON_CFG(i)) & 0x7f;
+ /* Ignore invalid assignments */
+ if (reg >= NUM_MON_LABELS)
+ continue;
+ /* Skip if disabled or reserved */
+ if (nct6683_mon_label[reg] == NULL)
+ continue;
+ if (reg < MON_VOLTAGE_START) {
+ data->temp_index[data->temp_num] = i;
+ data->temp_src[data->temp_num] = reg;
+ data->temp_num++;
+ } else {
+ data->in_index[data->in_num] = i;
+ data->in_src[data->in_num] = reg;
+ data->in_num++;
+ }
+ }
+}
+
+static int nct6683_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6683_sio_data *sio_data = dev->platform_data;
+ struct attribute_group *group;
+ struct nct6683_data *data;
+ struct device *hwmon_dev;
+ struct resource *res;
+ int groups = 0;
+ char build[16];
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME))
+ return -EBUSY;
+
+ data = devm_kzalloc(dev, sizeof(struct nct6683_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->kind = sio_data->kind;
+ data->sioreg = sio_data->sioreg;
+ data->addr = res->start;
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID);
+
+ /* By default only instantiate driver if the customer ID is known */
+ switch (data->customer_id) {
+ case NCT6683_CUSTOMER_ID_INTEL:
+ 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;
+ }
+
+ nct6683_init_device(data);
+ nct6683_setup_fans(data);
+ nct6683_setup_sensors(data);
+
+ /* Register sysfs hooks */
+
+ if (data->have_pwm) {
+ group = nct6683_create_attr_group(dev,
+ &nct6683_pwm_template_group,
+ fls(data->have_pwm));
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+ data->groups[groups++] = group;
+ }
+
+ if (data->in_num) {
+ group = nct6683_create_attr_group(dev,
+ &nct6683_in_template_group,
+ data->in_num);
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+ data->groups[groups++] = group;
+ }
+
+ if (data->have_fan) {
+ group = nct6683_create_attr_group(dev,
+ &nct6683_fan_template_group,
+ fls(data->have_fan));
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+ data->groups[groups++] = group;
+ }
+
+ if (data->temp_num) {
+ group = nct6683_create_attr_group(dev,
+ &nct6683_temp_template_group,
+ data->temp_num);
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+ data->groups[groups++] = group;
+ }
+ data->groups[groups++] = &nct6683_group_other;
+
+ if (data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
+ scnprintf(build, sizeof(build), "%02x/%02x/%02x",
+ nct6683_read(data, NCT6683_REG_BUILD_MONTH),
+ nct6683_read(data, NCT6683_REG_BUILD_DAY),
+ nct6683_read(data, NCT6683_REG_BUILD_YEAR));
+ else
+ scnprintf(build, sizeof(build), "%02d/%02d/%02d",
+ nct6683_read(data, NCT6683_REG_BUILD_MONTH),
+ nct6683_read(data, NCT6683_REG_BUILD_DAY),
+ nct6683_read(data, NCT6683_REG_BUILD_YEAR));
+
+ dev_info(dev, "%s EC firmware version %d.%d build %s\n",
+ nct6683_chip_names[data->kind],
+ nct6683_read(data, NCT6683_REG_VERSION_HI),
+ nct6683_read(data, NCT6683_REG_VERSION_LO),
+ build);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ nct6683_device_names[data->kind], data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+#ifdef CONFIG_PM
+static int nct6683_suspend(struct device *dev)
+{
+ struct nct6683_data *data = nct6683_update_device(dev);
+
+ mutex_lock(&data->update_lock);
+ data->hwm_cfg = nct6683_read(data, NCT6683_HWM_CFG);
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static int nct6683_resume(struct device *dev)
+{
+ struct nct6683_data *data = dev_get_drvdata(dev);
+
+ mutex_lock(&data->update_lock);
+
+ nct6683_write(data, NCT6683_HWM_CFG, data->hwm_cfg);
+
+ /* Force re-reading all values */
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops nct6683_dev_pm_ops = {
+ .suspend = nct6683_suspend,
+ .resume = nct6683_resume,
+ .freeze = nct6683_suspend,
+ .restore = nct6683_resume,
+};
+
+#define NCT6683_DEV_PM_OPS (&nct6683_dev_pm_ops)
+#else
+#define NCT6683_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver nct6683_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .pm = NCT6683_DEV_PM_OPS,
+ },
+ .probe = nct6683_probe,
+};
+
+static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
+{
+ int addr;
+ u16 val;
+ int err;
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
+ | superio_inb(sioaddr, SIO_REG_DEVID + 1);
+
+ switch (val & SIO_ID_MASK) {
+ 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);
+ goto fail;
+ }
+
+ /* We have a known chip, find the HWM I/O address */
+ superio_select(sioaddr, NCT6683_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("EC base I/O port unconfigured\n");
+ goto fail;
+ }
+
+ /* Activate logical device if needed */
+ val = superio_inb(sioaddr, SIO_REG_ENABLE);
+ if (!(val & 0x01)) {
+ pr_warn("Forcibly enabling EC access. Data may be unusable.\n");
+ superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
+ }
+
+ superio_exit(sioaddr);
+ pr_info("Found %s or compatible chip at %#x:%#x\n",
+ nct6683_chip_names[sio_data->kind], sioaddr, addr);
+ sio_data->sioreg = sioaddr;
+
+ return addr;
+
+fail:
+ superio_exit(sioaddr);
+ return -ENODEV;
+}
+
+/*
+ * 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 nct6683 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_nct6683_init(void)
+{
+ struct nct6683_sio_data sio_data;
+ int sioaddr[2] = { 0x2e, 0x4e };
+ struct resource res;
+ bool found = false;
+ int address;
+ int i, err;
+
+ err = platform_driver_register(&nct6683_driver);
+ 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
+ * nct6683 hardware monitor, and call probe()
+ */
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ address = nct6683_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 nct6683_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;
+
+ /* 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 >= 0) {
+ if (pdev[i])
+ platform_device_unregister(pdev[i]);
+ }
+exit_unregister:
+ platform_driver_unregister(&nct6683_driver);
+ return err;
+}
+
+static void __exit sensors_nct6683_exit(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ if (pdev[i])
+ platform_device_unregister(pdev[i]);
+ }
+ platform_driver_unregister(&nct6683_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("NCT6683D driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_nct6683_init);
+module_exit(sensors_nct6683_exit);
diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
new file mode 100644
index 000000000..80310845f
--- /dev/null
+++ b/drivers/hwmon/nct6775-core.c
@@ -0,0 +1,4210 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * nct6775 - Driver for the hardware monitoring functionality of
+ * Nuvoton NCT677x Super-I/O chips
+ *
+ * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from w83627ehf driver
+ * Copyright (C) 2005-2012 Jean Delvare <jdelvare@suse.de>
+ * Copyright (C) 2006 Yuan Mu (Winbond),
+ * Rudolf Marek <r.marek@assembler.cz>
+ * David Hubbard <david.c.hubbard@gmail.com>
+ * Daniel J Blueman <daniel.blueman@gmail.com>
+ * Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00)
+ *
+ * Shamelessly ripped from the w83627hf driver
+ * Copyright (C) 2003 Mark Studebaker
+ *
+ * Supports the following chips:
+ *
+ * Chip #vin #fan #pwm #temp chip IDs man ID
+ * nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3
+ * nct6116d 9 5 5 3+3 0xd280 0xc1 0x5ca3
+ * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3
+ * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3
+ * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3
+ * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3
+ * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3
+ * nct6793d 15 6 6 2+6 0xd120 0xc1 0x5ca3
+ * nct6795d 14 6 6 2+6 0xd350 0xc1 0x5ca3
+ * nct6796d 14 7 7 2+6 0xd420 0xc1 0x5ca3
+ * nct6797d 14 7 7 2+6 0xd450 0xc1 0x5ca3
+ * (0xd451)
+ * nct6798d 14 7 7 2+6 0xd428 0xc1 0x5ca3
+ * (0xd429)
+ *
+ * #temp lists the number of monitored temperature sources (first value) plus
+ * the number of directly connectable temperature sensors (second value).
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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/bitops.h>
+#include <linux/nospec.h>
+#include <linux/regmap.h>
+#include "lm75.h"
+#include "nct6775.h"
+
+#undef DEFAULT_SYMBOL_NAMESPACE
+#define DEFAULT_SYMBOL_NAMESPACE HWMON_NCT6775
+
+#define USE_ALTERNATE
+
+/* used to set data->name = nct6775_device_names[data->sio_kind] */
+static const char * const nct6775_device_names[] = {
+ "nct6106",
+ "nct6116",
+ "nct6775",
+ "nct6776",
+ "nct6779",
+ "nct6791",
+ "nct6792",
+ "nct6793",
+ "nct6795",
+ "nct6796",
+ "nct6797",
+ "nct6798",
+};
+
+/* Common and NCT6775 specific data */
+
+/* Voltage min/max registers for nr=7..14 are in bank 5 */
+
+static const u16 NCT6775_REG_IN_MAX[] = {
+ 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a,
+ 0x55c, 0x55e, 0x560, 0x562 };
+static const u16 NCT6775_REG_IN_MIN[] = {
+ 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b,
+ 0x55d, 0x55f, 0x561, 0x563 };
+static const u16 NCT6775_REG_IN[] = {
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552
+};
+
+#define NCT6775_REG_VBAT 0x5D
+#define NCT6775_REG_DIODE 0x5E
+#define NCT6775_DIODE_MASK 0x02
+
+static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B };
+
+/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */
+
+static const s8 NCT6775_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, -1, -1, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, -1 }; /* intrusion0, intrusion1 */
+
+static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e };
+
+/*
+ * 0..14 voltages, 15 global beep enable, 16..23 fans, 24..29 temperatures,
+ * 30..31 intrusion
+ */
+static const s8 NCT6775_BEEP_BITS[] = {
+ 0, 1, 2, 3, 8, 9, 10, 16, /* in0.. in7 */
+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ 21, /* global beep enable */
+ 6, 7, 11, 28, -1, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, -1 }; /* intrusion0, intrusion1 */
+
+/* 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 };
+
+/* Advanced Fan control, some values are common for all fans */
+
+static const u16 NCT6775_REG_TARGET[] = {
+ 0x101, 0x201, 0x301, 0x801, 0x901, 0xa01, 0xb01 };
+static const u16 NCT6775_REG_FAN_MODE[] = {
+ 0x102, 0x202, 0x302, 0x802, 0x902, 0xa02, 0xb02 };
+static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = {
+ 0x103, 0x203, 0x303, 0x803, 0x903, 0xa03, 0xb03 };
+static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = {
+ 0x104, 0x204, 0x304, 0x804, 0x904, 0xa04, 0xb04 };
+static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = {
+ 0x105, 0x205, 0x305, 0x805, 0x905, 0xa05, 0xb05 };
+static const u16 NCT6775_REG_FAN_START_OUTPUT[] = {
+ 0x106, 0x206, 0x306, 0x806, 0x906, 0xa06, 0xb06 };
+static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a };
+static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b };
+
+static const u16 NCT6775_REG_FAN_STOP_TIME[] = {
+ 0x107, 0x207, 0x307, 0x807, 0x907, 0xa07, 0xb07 };
+static const u16 NCT6775_REG_PWM[] = {
+ 0x109, 0x209, 0x309, 0x809, 0x909, 0xa09, 0xb09 };
+static const u16 NCT6775_REG_PWM_READ[] = {
+ 0x01, 0x03, 0x11, 0x13, 0x15, 0xa09, 0xb09 };
+
+static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 };
+static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d };
+static const u16 NCT6775_REG_FAN_PULSES[NUM_FAN] = {
+ 0x641, 0x642, 0x643, 0x644 };
+static const u16 NCT6775_FAN_PULSE_SHIFT[NUM_FAN] = { };
+
+static const u16 NCT6775_REG_TEMP[] = {
+ 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d };
+
+static const u16 NCT6775_REG_TEMP_MON[] = { 0x73, 0x75, 0x77 };
+
+static const u16 NCT6775_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0, 0x152, 0x252, 0x628, 0x629, 0x62A };
+static const u16 NCT6775_REG_TEMP_HYST[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x3a, 0x153, 0x253, 0x673, 0x678, 0x67D };
+static const u16 NCT6775_REG_TEMP_OVER[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x39, 0x155, 0x255, 0x672, 0x677, 0x67C };
+
+static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x621, 0x622, 0x623, 0x624, 0x625, 0x626 };
+
+static const u16 NCT6775_REG_TEMP_SEL[] = {
+ 0x100, 0x200, 0x300, 0x800, 0x900, 0xa00, 0xb00 };
+
+static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = {
+ 0x139, 0x239, 0x339, 0x839, 0x939, 0xa39 };
+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = {
+ 0x13a, 0x23a, 0x33a, 0x83a, 0x93a, 0xa3a };
+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = {
+ 0x13b, 0x23b, 0x33b, 0x83b, 0x93b, 0xa3b };
+static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = {
+ 0x13c, 0x23c, 0x33c, 0x83c, 0x93c, 0xa3c };
+static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = {
+ 0x13d, 0x23d, 0x33d, 0x83d, 0x93d, 0xa3d };
+
+static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 };
+
+static const u16 NCT6775_REG_AUTO_TEMP[] = {
+ 0x121, 0x221, 0x321, 0x821, 0x921, 0xa21, 0xb21 };
+static const u16 NCT6775_REG_AUTO_PWM[] = {
+ 0x127, 0x227, 0x327, 0x827, 0x927, 0xa27, 0xb27 };
+
+#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p))
+#define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p))
+
+static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 };
+
+static const u16 NCT6775_REG_CRITICAL_TEMP[] = {
+ 0x135, 0x235, 0x335, 0x835, 0x935, 0xa35, 0xb35 };
+static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = {
+ 0x138, 0x238, 0x338, 0x838, 0x938, 0xa38, 0xb38 };
+
+static const char *const nct6775_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN",
+ "AMD SB-TSI",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PECI Agent 2",
+ "PECI Agent 3",
+ "PECI Agent 4",
+ "PECI Agent 5",
+ "PECI Agent 6",
+ "PECI Agent 7",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "PCH_DIM0_TEMP",
+ "PCH_DIM1_TEMP",
+ "PCH_DIM2_TEMP",
+ "PCH_DIM3_TEMP"
+};
+
+#define NCT6775_TEMP_MASK 0x001ffffe
+#define NCT6775_VIRT_TEMP_MASK 0x00000000
+
+static const u16 NCT6775_REG_TEMP_ALTERNATE[32] = {
+ [13] = 0x661,
+ [14] = 0x662,
+ [15] = 0x664,
+};
+
+static const u16 NCT6775_REG_TEMP_CRIT[32] = {
+ [4] = 0xa00,
+ [5] = 0xa01,
+ [6] = 0xa02,
+ [7] = 0xa03,
+ [8] = 0xa04,
+ [9] = 0xa05,
+ [10] = 0xa06,
+ [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 */
+#define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME
+#define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME
+
+static const s8 NCT6776_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, 10, 23, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, 9 }; /* intrusion0, intrusion1 */
+
+static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 };
+
+static const s8 NCT6776_BEEP_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */
+ 8, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ 24, /* global beep enable */
+ 25, 26, 27, 28, 29, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, 19, 20, 21, /* temp1..temp6 */
+ 30, 31 }; /* intrusion0, intrusion1 */
+
+static const u16 NCT6776_REG_TOLERANCE_H[] = {
+ 0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c, 0xb0c };
+
+static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 };
+static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 };
+
+static const u16 NCT6776_REG_FAN_MIN[] = {
+ 0x63a, 0x63c, 0x63e, 0x640, 0x642, 0x64a, 0x64c };
+static const u16 NCT6776_REG_FAN_PULSES[NUM_FAN] = {
+ 0x644, 0x645, 0x646, 0x647, 0x648, 0x649 };
+
+static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = {
+ 0x13e, 0x23e, 0x33e, 0x83e, 0x93e, 0xa3e };
+
+static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A };
+
+static const char *const nct6776_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "SMBUSMASTER 2",
+ "SMBUSMASTER 3",
+ "SMBUSMASTER 4",
+ "SMBUSMASTER 5",
+ "SMBUSMASTER 6",
+ "SMBUSMASTER 7",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "PCH_DIM0_TEMP",
+ "PCH_DIM1_TEMP",
+ "PCH_DIM2_TEMP",
+ "PCH_DIM3_TEMP",
+ "BYTE_TEMP"
+};
+
+#define NCT6776_TEMP_MASK 0x007ffffe
+#define NCT6776_VIRT_TEMP_MASK 0x00000000
+
+static const u16 NCT6776_REG_TEMP_ALTERNATE[32] = {
+ [14] = 0x401,
+ [15] = 0x402,
+ [16] = 0x404,
+};
+
+static const u16 NCT6776_REG_TEMP_CRIT[32] = {
+ [11] = 0x709,
+ [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[] = {
+ 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487,
+ 0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e };
+
+static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = {
+ 0x459, 0x45A, 0x45B, 0x568 };
+
+static const s8 NCT6779_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, 10, 23, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, 9 }; /* intrusion0, intrusion1 */
+
+static const s8 NCT6779_BEEP_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */
+ 8, 9, 10, 11, 12, 13, 14, /* in8..in14 */
+ 24, /* global beep enable */
+ 25, 26, 27, 28, 29, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, -1, -1, -1, -1, /* temp1..temp6 */
+ 30, 31 }; /* intrusion0, intrusion1 */
+
+static const u16 NCT6779_REG_FAN[] = {
+ 0x4c0, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4ce };
+static const u16 NCT6779_REG_FAN_PULSES[NUM_FAN] = {
+ 0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0x64f };
+
+static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = {
+ 0x136, 0x236, 0x336, 0x836, 0x936, 0xa36, 0xb36 };
+#define NCT6779_CRITICAL_PWM_ENABLE_MASK 0x01
+static const u16 NCT6779_REG_CRITICAL_PWM[] = {
+ 0x137, 0x237, 0x337, 0x837, 0x937, 0xa37, 0xb37 };
+
+static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 };
+static const u16 NCT6779_REG_TEMP_MON[] = { 0x73, 0x75, 0x77, 0x79, 0x7b };
+static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+ 0x18, 0x152 };
+static const u16 NCT6779_REG_TEMP_HYST[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+ 0x3a, 0x153 };
+static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+ 0x39, 0x155 };
+
+static const u16 NCT6779_REG_TEMP_OFFSET[] = {
+ 0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c };
+
+static const char *const nct6779_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN0",
+ "AUXTIN1",
+ "AUXTIN2",
+ "AUXTIN3",
+ "",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "SMBUSMASTER 2",
+ "SMBUSMASTER 3",
+ "SMBUSMASTER 4",
+ "SMBUSMASTER 5",
+ "SMBUSMASTER 6",
+ "SMBUSMASTER 7",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "PCH_DIM0_TEMP",
+ "PCH_DIM1_TEMP",
+ "PCH_DIM2_TEMP",
+ "PCH_DIM3_TEMP",
+ "BYTE_TEMP",
+ "",
+ "",
+ "",
+ "",
+ "Virtual_TEMP"
+};
+
+#define NCT6779_TEMP_MASK 0x07ffff7e
+#define NCT6779_VIRT_TEMP_MASK 0x00000000
+#define NCT6791_TEMP_MASK 0x87ffff7e
+#define NCT6791_VIRT_TEMP_MASK 0x80000000
+
+static const u16 NCT6779_REG_TEMP_ALTERNATE[32]
+ = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
+ 0x408, 0 };
+
+static const u16 NCT6779_REG_TEMP_CRIT[32] = {
+ [15] = 0x709,
+ [16] = 0x70a,
+};
+
+/* NCT6791 specific data */
+
+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 };
+static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[NUM_FAN] = { 0, 0x23c };
+static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[NUM_FAN] = { 0, 0x23d };
+static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[NUM_FAN] = { 0, 0x23e };
+
+static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = {
+ 0x459, 0x45A, 0x45B, 0x568, 0x45D };
+
+static const s8 NCT6791_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, 10, 23, 33, /* fan1..fan6 */
+ -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, 9 }; /* intrusion0, intrusion1 */
+
+/* NCT6792/NCT6793 specific data */
+
+static const u16 NCT6792_REG_TEMP_MON[] = {
+ 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d };
+static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
+
+static const char *const nct6792_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN0",
+ "AUXTIN1",
+ "AUXTIN2",
+ "AUXTIN3",
+ "",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "SMBUSMASTER 2",
+ "SMBUSMASTER 3",
+ "SMBUSMASTER 4",
+ "SMBUSMASTER 5",
+ "SMBUSMASTER 6",
+ "SMBUSMASTER 7",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "PCH_DIM0_TEMP",
+ "PCH_DIM1_TEMP",
+ "PCH_DIM2_TEMP",
+ "PCH_DIM3_TEMP",
+ "BYTE_TEMP",
+ "PECI Agent 0 Calibration",
+ "PECI Agent 1 Calibration",
+ "",
+ "",
+ "Virtual_TEMP"
+};
+
+#define NCT6792_TEMP_MASK 0x9fffff7e
+#define NCT6792_VIRT_TEMP_MASK 0x80000000
+
+static const char *const nct6793_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN0",
+ "AUXTIN1",
+ "AUXTIN2",
+ "AUXTIN3",
+ "",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "Agent0 Dimm0 ",
+ "Agent0 Dimm1",
+ "Agent1 Dimm0",
+ "Agent1 Dimm1",
+ "BYTE_TEMP0",
+ "BYTE_TEMP1",
+ "PECI Agent 0 Calibration",
+ "PECI Agent 1 Calibration",
+ "",
+ "Virtual_TEMP"
+};
+
+#define NCT6793_TEMP_MASK 0xbfff037e
+#define NCT6793_VIRT_TEMP_MASK 0x80000000
+
+static const char *const nct6795_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN0",
+ "AUXTIN1",
+ "AUXTIN2",
+ "AUXTIN3",
+ "",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "SMBUSMASTER 2",
+ "SMBUSMASTER 3",
+ "SMBUSMASTER 4",
+ "SMBUSMASTER 5",
+ "SMBUSMASTER 6",
+ "SMBUSMASTER 7",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "Agent0 Dimm0",
+ "Agent0 Dimm1",
+ "Agent1 Dimm0",
+ "Agent1 Dimm1",
+ "BYTE_TEMP0",
+ "BYTE_TEMP1",
+ "PECI Agent 0 Calibration",
+ "PECI Agent 1 Calibration",
+ "",
+ "Virtual_TEMP"
+};
+
+#define NCT6795_TEMP_MASK 0xbfffff7e
+#define NCT6795_VIRT_TEMP_MASK 0x80000000
+
+static const char *const nct6796_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN0",
+ "AUXTIN1",
+ "AUXTIN2",
+ "AUXTIN3",
+ "AUXTIN4",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "Virtual_TEMP",
+ "Virtual_TEMP",
+ "",
+ "",
+ "",
+ "",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "Agent0 Dimm0",
+ "Agent0 Dimm1",
+ "Agent1 Dimm0",
+ "Agent1 Dimm1",
+ "BYTE_TEMP0",
+ "BYTE_TEMP1",
+ "PECI Agent 0 Calibration",
+ "PECI Agent 1 Calibration",
+ "",
+ "Virtual_TEMP"
+};
+
+#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",
+ "CPUTIN",
+ "AUXTIN0",
+ "AUXTIN1",
+ "AUXTIN2",
+ "AUXTIN3",
+ "AUXTIN4",
+ "SMBUSMASTER 0",
+ "SMBUSMASTER 1",
+ "Virtual_TEMP",
+ "Virtual_TEMP",
+ "",
+ "",
+ "",
+ "",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PCH_CHIP_CPU_MAX_TEMP",
+ "PCH_CHIP_TEMP",
+ "PCH_CPU_TEMP",
+ "PCH_MCH_TEMP",
+ "Agent0 Dimm0",
+ "Agent0 Dimm1",
+ "Agent1 Dimm0",
+ "Agent1 Dimm1",
+ "BYTE_TEMP0",
+ "BYTE_TEMP1",
+ "PECI Agent 0 Calibration", /* undocumented */
+ "PECI Agent 1 Calibration", /* undocumented */
+ "",
+ "Virtual_TEMP"
+};
+
+#define NCT6798_TEMP_MASK 0xbfff0ffe
+#define NCT6798_VIRT_TEMP_MASK 0x80000c00
+
+/* NCT6102D/NCT6106D specific data */
+
+#define NCT6106_REG_VBAT 0x318
+#define NCT6106_REG_DIODE 0x319
+#define NCT6106_DIODE_MASK 0x01
+
+static const u16 NCT6106_REG_IN_MAX[] = {
+ 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9e, 0xa0, 0xa2 };
+static const u16 NCT6106_REG_IN_MIN[] = {
+ 0x91, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9f, 0xa1, 0xa3 };
+static const u16 NCT6106_REG_IN[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09 };
+
+static const u16 NCT6106_REG_TEMP[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 };
+static const u16 NCT6106_REG_TEMP_MON[] = { 0x18, 0x19, 0x1a };
+static const u16 NCT6106_REG_TEMP_HYST[] = {
+ 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7 };
+static const u16 NCT6106_REG_TEMP_OVER[] = {
+ 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd6 };
+static const u16 NCT6106_REG_TEMP_CRIT_L[] = {
+ 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4 };
+static const u16 NCT6106_REG_TEMP_CRIT_H[] = {
+ 0xc1, 0xc5, 0xc9, 0xcf, 0xd1, 0xd5 };
+static const u16 NCT6106_REG_TEMP_OFFSET[] = { 0x311, 0x312, 0x313 };
+static const u16 NCT6106_REG_TEMP_CONFIG[] = {
+ 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc };
+
+static const u16 NCT6106_REG_FAN[] = { 0x20, 0x22, 0x24 };
+static const u16 NCT6106_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4 };
+static const u16 NCT6106_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6 };
+static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4 };
+
+static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 };
+static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 };
+static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c };
+static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 };
+static const u16 NCT6106_REG_TEMP_SOURCE[] = {
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 };
+
+static const u16 NCT6106_REG_CRITICAL_TEMP[] = { 0x11a, 0x12a, 0x13a };
+static const u16 NCT6106_REG_CRITICAL_TEMP_TOLERANCE[] = {
+ 0x11b, 0x12b, 0x13b };
+
+static const u16 NCT6106_REG_CRITICAL_PWM_ENABLE[] = { 0x11c, 0x12c, 0x13c };
+#define NCT6106_CRITICAL_PWM_ENABLE_MASK 0x10
+static const u16 NCT6106_REG_CRITICAL_PWM[] = { 0x11d, 0x12d, 0x13d };
+
+static const u16 NCT6106_REG_FAN_STEP_UP_TIME[] = { 0x114, 0x124, 0x134 };
+static const u16 NCT6106_REG_FAN_STEP_DOWN_TIME[] = { 0x115, 0x125, 0x135 };
+static const u16 NCT6106_REG_FAN_STOP_OUTPUT[] = { 0x116, 0x126, 0x136 };
+static const u16 NCT6106_REG_FAN_START_OUTPUT[] = { 0x117, 0x127, 0x137 };
+static const u16 NCT6106_REG_FAN_STOP_TIME[] = { 0x118, 0x128, 0x138 };
+static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+
+static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+
+static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
+static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
+
+static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+
+static const u16 NCT6106_REG_ALARM[NUM_REG_ALARM] = {
+ 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d };
+
+static const s8 NCT6106_ALARM_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
+ 9, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ -1, /* unused */
+ 32, 33, 34, -1, -1, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, 19, 20, 21, /* temp1..temp6 */
+ 48, -1 /* intrusion0, intrusion1 */
+};
+
+static const u16 NCT6106_REG_BEEP[NUM_REG_BEEP] = {
+ 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4 };
+
+static const s8 NCT6106_BEEP_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
+ 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */
+ 32, /* global beep enable */
+ 24, 25, 26, 27, 28, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, 19, 20, 21, /* temp1..temp6 */
+ 34, -1 /* intrusion0, intrusion1 */
+};
+
+static const u16 NCT6106_REG_TEMP_ALTERNATE[32] = {
+ [14] = 0x51,
+ [15] = 0x52,
+ [16] = 0x54,
+};
+
+static const u16 NCT6106_REG_TEMP_CRIT[32] = {
+ [11] = 0x204,
+ [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 };
+static const u16 NCT6116_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4, 0xe6, 0xe8 };
+static const u16 NCT6116_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6, 0xf6, 0xf5 };
+static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
+
+static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
+static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
+static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
+static const u16 NCT6116_REG_TEMP_SOURCE[] = {
+ 0xb0, 0xb1, 0xb2 };
+
+static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
+ 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
+static const u16 NCT6116_REG_CRITICAL_TEMP_TOLERANCE[] = {
+ 0x11b, 0x12b, 0x13b, 0x19b, 0x1ab };
+
+static const u16 NCT6116_REG_CRITICAL_PWM_ENABLE[] = {
+ 0x11c, 0x12c, 0x13c, 0x19c, 0x1ac };
+static const u16 NCT6116_REG_CRITICAL_PWM[] = {
+ 0x11d, 0x12d, 0x13d, 0x19d, 0x1ad };
+
+static const u16 NCT6116_REG_FAN_STEP_UP_TIME[] = {
+ 0x114, 0x124, 0x134, 0x194, 0x1a4 };
+static const u16 NCT6116_REG_FAN_STEP_DOWN_TIME[] = {
+ 0x115, 0x125, 0x135, 0x195, 0x1a5 };
+static const u16 NCT6116_REG_FAN_STOP_OUTPUT[] = {
+ 0x116, 0x126, 0x136, 0x196, 0x1a6 };
+static const u16 NCT6116_REG_FAN_START_OUTPUT[] = {
+ 0x117, 0x127, 0x137, 0x197, 0x1a7 };
+static const u16 NCT6116_REG_FAN_STOP_TIME[] = {
+ 0x118, 0x128, 0x138, 0x198, 0x1a8 };
+static const u16 NCT6116_REG_TOLERANCE_H[] = {
+ 0x112, 0x122, 0x132, 0x192, 0x1a2 };
+
+static const u16 NCT6116_REG_TARGET[] = {
+ 0x111, 0x121, 0x131, 0x191, 0x1a1 };
+
+static const u16 NCT6116_REG_AUTO_TEMP[] = {
+ 0x160, 0x170, 0x180, 0x1d0, 0x1e0 };
+static const u16 NCT6116_REG_AUTO_PWM[] = {
+ 0x164, 0x174, 0x184, 0x1d4, 0x1e4 };
+
+static const s8 NCT6116_ALARM_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
+ 9, -1, -1, -1, -1, -1, -1, /* in8..in9 */
+ -1, /* unused */
+ 32, 33, 34, 35, 36, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, -1, -1, -1, /* temp1..temp6 */
+ 48, -1 /* intrusion0, intrusion1 */
+};
+
+static const s8 NCT6116_BEEP_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
+ 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */
+ 32, /* global beep enable */
+ 24, 25, 26, 27, 28, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, -1, -1, -1, /* temp1..temp6 */
+ 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)
+ return off;
+ return mode + 1;
+}
+
+static int pwm_enable_to_reg(enum pwm_enable mode)
+{
+ if (mode == off)
+ return 0;
+ return mode - 1;
+}
+
+/*
+ * Conversions
+ */
+
+/* 1 is DC mode, output in ms */
+static unsigned int step_time_from_reg(u8 reg, u8 mode)
+{
+ return mode ? 400 * reg : 100 * reg;
+}
+
+static u8 step_time_to_reg(unsigned int msec, u8 mode)
+{
+ return clamp_val((mode ? (msec + 200) / 400 :
+ (msec + 50) / 100), 1, 255);
+}
+
+static unsigned int fan_from_reg8(u16 reg, unsigned int divreg)
+{
+ if (reg == 0 || reg == 255)
+ return 0;
+ return 1350000U / (reg << divreg);
+}
+
+static unsigned int fan_from_reg13(u16 reg, unsigned int divreg)
+{
+ if ((reg & 0xff1f) == 0xff1f)
+ return 0;
+
+ reg = (reg & 0x1f) | ((reg & 0xff00) >> 3);
+
+ if (reg == 0)
+ return 0;
+
+ return 1350000U / reg;
+}
+
+static unsigned int fan_from_reg16(u16 reg, unsigned int divreg)
+{
+ if (reg == 0 || reg == 0xffff)
+ return 0;
+
+ /*
+ * Even though the registers are 16 bit wide, the fan divisor
+ * still applies.
+ */
+ return 1350000U / (reg << divreg);
+}
+
+static unsigned int fan_from_reg_rpm(u16 reg, unsigned int divreg)
+{
+ return reg;
+}
+
+static u16 fan_to_reg(u32 fan, unsigned int divreg)
+{
+ if (!fan)
+ return 0;
+
+ return (1350000U / fan) >> divreg;
+}
+
+static inline unsigned int
+div_from_reg(u8 reg)
+{
+ return BIT(reg);
+}
+
+/*
+ * Some of the voltage inputs have internal scaling, the tables below
+ * contain 8 (the ADC LSB in mV) * scaling factor * 100
+ */
+static const u16 scale_in[15] = {
+ 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800, 800, 800, 800,
+ 800, 800
+};
+
+static inline long in_from_reg(u8 reg, u8 nr)
+{
+ return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
+}
+
+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 sensor_device_template {
+ struct device_attribute dev_attr;
+ union {
+ struct {
+ u8 nr;
+ u8 index;
+ } s;
+ int index;
+ } u;
+ bool s2; /* true if both index and nr are used */
+};
+
+struct sensor_device_attr_u {
+ union {
+ struct sensor_device_attribute a1;
+ struct sensor_device_attribute_2 a2;
+ } u;
+ char name[32];
+};
+
+#define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \
+ .attr = {.name = _template, .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \
+ { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
+ .u.index = _index, \
+ .s2 = false }
+
+#define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
+ _nr, _index) \
+ { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
+ .u.s.index = _index, \
+ .u.s.nr = _nr, \
+ .s2 = true }
+
+#define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \
+static struct sensor_device_template sensor_dev_template_##_name \
+ = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \
+ _index)
+
+#define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \
+ _nr, _index) \
+static struct sensor_device_template sensor_dev_template_##_name \
+ = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
+ _nr, _index)
+
+struct sensor_template_group {
+ struct sensor_device_template **templates;
+ umode_t (*is_visible)(struct kobject *, struct attribute *, int);
+ int base;
+};
+
+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;
+ struct sensor_device_attribute *a;
+ struct sensor_device_attribute_2 *a2;
+ struct attribute **attrs;
+ struct sensor_device_template **t;
+ int i, count;
+
+ if (repeat <= 0)
+ return -EINVAL;
+
+ t = tg->templates;
+ for (count = 0; *t; t++, count++)
+ ;
+
+ if (count == 0)
+ return -EINVAL;
+
+ group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
+ if (group == NULL)
+ return -ENOMEM;
+
+ attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs),
+ GFP_KERNEL);
+ if (attrs == NULL)
+ return -ENOMEM;
+
+ su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)),
+ GFP_KERNEL);
+ if (su == NULL)
+ return -ENOMEM;
+
+ group->attrs = attrs;
+ group->is_visible = tg->is_visible;
+
+ for (i = 0; i < repeat; i++) {
+ t = tg->templates;
+ while (*t != NULL) {
+ snprintf(su->name, sizeof(su->name),
+ (*t)->dev_attr.attr.name, tg->base + i);
+ if ((*t)->s2) {
+ a2 = &su->u.a2;
+ sysfs_attr_init(&a2->dev_attr.attr);
+ a2->dev_attr.attr.name = su->name;
+ a2->nr = (*t)->u.s.nr + i;
+ a2->index = (*t)->u.s.index;
+ a2->dev_attr.attr.mode =
+ (*t)->dev_attr.attr.mode;
+ a2->dev_attr.show = (*t)->dev_attr.show;
+ a2->dev_attr.store = (*t)->dev_attr.store;
+ *attrs = &a2->dev_attr.attr;
+ } else {
+ a = &su->u.a1;
+ sysfs_attr_init(&a->dev_attr.attr);
+ a->dev_attr.attr.name = su->name;
+ a->index = (*t)->u.index + i;
+ a->dev_attr.attr.mode =
+ (*t)->dev_attr.attr.mode;
+ a->dev_attr.show = (*t)->dev_attr.show;
+ a->dev_attr.store = (*t)->dev_attr.store;
+ *attrs = &a->dev_attr.attr;
+ }
+ attrs++;
+ su++;
+ t++;
+ }
+ }
+
+ return nct6775_add_attr_group(data, group);
+}
+
+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 == 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) &&
+ ((reg & 0x00ff) == 0x50 ||
+ (reg & 0x00ff) == 0x53 ||
+ (reg & 0x00ff) == 0x55)) ||
+ (reg & 0xfff0) == 0x630 ||
+ reg == 0x640 || reg == 0x642 ||
+ reg == 0x662 || reg == 0x669 ||
+ ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) ||
+ reg == 0x73 || reg == 0x75 || reg == 0x77;
+ case nct6776:
+ return (((reg & 0xff00) == 0x100 ||
+ (reg & 0xff00) == 0x200) &&
+ ((reg & 0x00ff) == 0x50 ||
+ (reg & 0x00ff) == 0x53 ||
+ (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;
+ case nct6779:
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ 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 ||
+ reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
+ reg == 0x7b || reg == 0x7d;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(nct6775_reg_is_word_sized);
+
+/* We left-align 8-bit temperature values to make the code simpler */
+static int nct6775_read_temp(struct nct6775_data *data, u16 reg, u16 *val)
+{
+ int err;
+
+ err = nct6775_read_value(data, reg, val);
+ if (err)
+ return err;
+
+ if (!nct6775_reg_is_word_sized(data, reg))
+ *val <<= 8;
+
+ return 0;
+}
+
+/* This function assumes that the caller holds data->update_lock */
+static int nct6775_write_fan_div(struct nct6775_data *data, int nr)
+{
+ 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 */
+
+ err = nct6775_read_value(data, fandiv_reg, &reg);
+ if (err)
+ return err;
+ reg &= 0x70 >> oddshift;
+ reg |= (data->fan_div[nr] & 0x7) << oddshift;
+ return nct6775_write_value(data, fandiv_reg, reg);
+}
+
+static int nct6775_write_fan_div_common(struct nct6775_data *data, int nr)
+{
+ if (data->kind == nct6775)
+ return nct6775_write_fan_div(data, nr);
+ return 0;
+}
+
+static int nct6775_update_fan_div(struct nct6775_data *data)
+{
+ int err;
+ u16 i;
+
+ 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;
+ 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 int nct6775_update_fan_div_common(struct nct6775_data *data)
+{
+ if (data->kind == nct6775)
+ return nct6775_update_fan_div(data);
+ return 0;
+}
+
+static int nct6775_init_fan_div(struct nct6775_data *data)
+{
+ int i, err;
+
+ err = nct6775_update_fan_div_common(data);
+ if (err)
+ return err;
+
+ /*
+ * For all fans, start with highest divider value if the divider
+ * register is not initialized. This ensures that we get a
+ * reading from the fan count register, even if it is not optimal.
+ * We'll compute a better divider later on.
+ */
+ for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) {
+ if (!(data->has_fan & BIT(i)))
+ continue;
+ if (data->fan_div[i] == 0) {
+ data->fan_div[i] = 7;
+ err = nct6775_write_fan_div_common(data, i);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int nct6775_init_fan_common(struct device *dev,
+ struct nct6775_data *data)
+{
+ int i, err;
+ u16 reg;
+
+ 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
+ * prevents the unnecessary warning when fanX_min is reported as 0.
+ */
+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+ if (data->has_fan_min & BIT(i)) {
+ err = nct6775_read_value(data, data->REG_FAN_MIN[i], &reg);
+ 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 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 0;
+
+ /*
+ * If we failed to measure the fan speed, or the reported value is not
+ * in the optimal range, and the clock divider can be modified,
+ * let's try that for next time.
+ */
+ if (reg == 0x00 && fan_div < 0x07)
+ fan_div++;
+ else if (reg != 0x00 && reg < 0x30 && fan_div > 0)
+ fan_div--;
+
+ if (fan_div != data->fan_div[nr]) {
+ dev_dbg(dev, "Modifying fan%d clock divider from %u to %u\n",
+ nr + 1, div_from_reg(data->fan_div[nr]),
+ div_from_reg(fan_div));
+
+ /* Preserve min limit if possible */
+ if (data->has_fan_min & BIT(nr)) {
+ fan_min = data->fan_min[nr];
+ if (fan_div > data->fan_div[nr]) {
+ if (fan_min != 255 && fan_min > 1)
+ fan_min >>= 1;
+ } else {
+ if (fan_min != 255) {
+ fan_min <<= 1;
+ if (fan_min > 254)
+ fan_min = 254;
+ }
+ }
+ if (fan_min != data->fan_min[nr]) {
+ data->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;
+ err = nct6775_write_fan_div_common(data, nr);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int nct6775_update_pwm(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ 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;
+
+ err = nct6775_read_value(data, data->REG_PWM_MODE[i], &reg);
+ 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;
+
+ 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]) {
+ err = nct6775_read_value(data, data->REG_PWM[j][i], &reg);
+ if (err)
+ return err;
+ data->pwm[j][i] = reg;
+ }
+ }
+
+ data->pwm_enable[i] = reg_to_pwm_enable(data->pwm[0][i],
+ (fanmodecfg >> 4) & 7);
+
+ if (!data->temp_tolerance[0][i] ||
+ data->pwm_enable[i] != speed_cruise)
+ data->temp_tolerance[0][i] = fanmodecfg & 0x0f;
+ if (!data->target_speed_tolerance[i] ||
+ data->pwm_enable[i] == speed_cruise) {
+ u8 t = fanmodecfg & 0x0f;
+
+ if (data->REG_TOLERANCE_H) {
+ err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], &reg);
+ if (err)
+ return err;
+ t |= (reg & 0x70) >> 1;
+ }
+ data->target_speed_tolerance[i] = t;
+ }
+
+ err = nct6775_read_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[i], &reg);
+ if (err)
+ return err;
+ data->temp_tolerance[1][i] = reg;
+
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[i], &reg);
+ if (err)
+ return err;
+ data->pwm_temp_sel[i] = reg & 0x1f;
+ /* If fan can stop, report floor as 0 */
+ if (reg & 0x80)
+ data->pwm[2][i] = 0;
+
+ if (!data->REG_WEIGHT_TEMP_SEL[i])
+ continue;
+
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i], &reg);
+ if (err)
+ return err;
+ data->pwm_weight_temp_sel[i] = reg & 0x1f;
+ /* If weight is disabled, report weight source as 0 */
+ if (!(reg & 0x80))
+ data->pwm_weight_temp_sel[i] = 0;
+
+ /* Weight temp data */
+ for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) {
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP[j][i], &reg);
+ if (err)
+ return err;
+ data->weight_temp[j][i] = reg;
+ }
+ }
+
+ return 0;
+}
+
+static int nct6775_update_pwm_limits(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ 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++) {
+ err = nct6775_read_value(data, data->REG_FAN_TIME[j][i], &reg);
+ if (err)
+ return err;
+ data->fan_time[j][i] = reg;
+ }
+
+ err = nct6775_read_value(data, data->REG_TARGET[i], &reg_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)
+ data->target_temp[i] = reg_t & data->target_temp_mask;
+ if (!data->target_speed[i] ||
+ data->pwm_enable[i] == speed_cruise) {
+ if (data->REG_TOLERANCE_H) {
+ err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], &reg);
+ if (err)
+ return err;
+ reg_t |= (reg & 0x0f) << 8;
+ }
+ data->target_speed[i] = reg_t;
+ }
+
+ for (j = 0; j < data->auto_pwm_num; j++) {
+ err = nct6775_read_value(data, NCT6775_AUTO_PWM(data, i, j), &reg);
+ if (err)
+ return err;
+ data->auto_pwm[i][j] = reg;
+
+ err = nct6775_read_value(data, NCT6775_AUTO_TEMP(data, i, j), &reg);
+ if (err)
+ return err;
+ data->auto_temp[i][j] = reg;
+ }
+
+ /* critical auto_pwm temperature data */
+ err = nct6775_read_value(data, data->REG_CRITICAL_TEMP[i], &reg);
+ if (err)
+ return err;
+ data->auto_temp[i][data->auto_pwm_num] = reg;
+
+ switch (data->kind) {
+ case nct6775:
+ err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[i], &reg);
+ if (err)
+ return err;
+ data->auto_pwm[i][data->auto_pwm_num] =
+ (reg & 0x02) ? 0xff : 0x00;
+ break;
+ case nct6776:
+ data->auto_pwm[i][data->auto_pwm_num] = 0xff;
+ break;
+ case nct6106:
+ case nct6116:
+ case nct6779:
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i], &reg);
+ if (err)
+ return err;
+ if (reg & data->CRITICAL_PWM_ENABLE_MASK) {
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM[i], &reg);
+ if (err)
+ return err;
+ } else {
+ reg = 0xff;
+ }
+ data->auto_pwm[i][data->auto_pwm_num] = reg;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+struct nct6775_data *nct6775_update_device(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ 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 */
+ 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;
+
+ err = nct6775_read_value(data, data->REG_VIN[i], &reg);
+ if (err)
+ goto out;
+ data->in[i][0] = reg;
+
+ err = nct6775_read_value(data, data->REG_IN_MINMAX[0][i], &reg);
+ if (err)
+ goto out;
+ data->in[i][1] = reg;
+
+ err = nct6775_read_value(data, data->REG_IN_MINMAX[1][i], &reg);
+ if (err)
+ goto out;
+ data->in[i][2] = reg;
+ }
+
+ /* Measured fan speeds and limits */
+ for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
+ if (!(data->has_fan & BIT(i)))
+ continue;
+
+ err = nct6775_read_value(data, data->REG_FAN[i], &reg);
+ if (err)
+ goto out;
+ data->rpm[i] = data->fan_from_reg(reg,
+ data->fan_div[i]);
+
+ if (data->has_fan_min & BIT(i)) {
+ u16 tmp;
+
+ err = nct6775_read_value(data, data->REG_FAN_MIN[i], &tmp);
+ if (err)
+ goto out;
+ data->fan_min[i] = tmp;
+ }
+
+ if (data->REG_FAN_PULSES[i]) {
+ u16 tmp;
+
+ err = nct6775_read_value(data, data->REG_FAN_PULSES[i], &tmp);
+ if (err)
+ goto out;
+ data->fan_pulses[i] = (tmp >> data->FAN_PULSE_SHIFT[i]) & 0x03;
+ }
+
+ err = nct6775_select_fan_div(dev, data, i, reg);
+ if (err)
+ goto out;
+ }
+
+ 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]) {
+ err = nct6775_read_temp(data, data->reg_temp[j][i], &reg);
+ if (err)
+ goto out;
+ data->temp[j][i] = reg;
+ }
+ }
+ if (i >= NUM_TEMP_FIXED ||
+ !(data->have_temp_fixed & BIT(i)))
+ continue;
+ err = nct6775_read_value(data, data->REG_TEMP_OFFSET[i], &reg);
+ 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], &reg);
+ if (err)
+ goto out;
+ data->tsi_temp[i] = reg;
+ }
+
+ data->alarms = 0;
+ for (i = 0; i < NUM_REG_ALARM; i++) {
+ u16 alarm;
+
+ if (!data->REG_ALARM[i])
+ continue;
+ 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++) {
+ u16 beep;
+
+ if (!data->REG_BEEP[i])
+ continue;
+ 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 err ? ERR_PTR(err) : data;
+}
+EXPORT_SYMBOL_GPL(nct6775_update_device);
+
+/*
+ * Sysfs callback functions
+ */
+static ssize_t
+show_in_reg(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);
+ 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));
+}
+
+static ssize_t
+store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int index = sattr->index;
+ int nr = sattr->nr;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ mutex_lock(&data->update_lock);
+ data->in[nr][index] = in_to_reg(val, nr);
+ err = nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], data->in[nr][index]);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+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;
+
+ 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, err;
+
+ for (nr = 0; nr < count; nr++) {
+ u16 src;
+
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[nr], &src);
+ if (err)
+ return err;
+ if ((src & 0x1f) == source)
+ return nr;
+ }
+ return -ENODEV;
+}
+
+static ssize_t
+show_temp_alarm(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);
+ 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.
+ */
+ nr = find_temp_source(data, sattr->index, data->num_temp_alarms);
+ if (nr >= 0) {
+ int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE];
+
+ alarm = (data->alarms >> bit) & 0x01;
+ }
+ return sprintf(buf, "%u\n", alarm);
+}
+
+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;
+
+ 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);
+
+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);
+ int nr = data->BEEP_BITS[sattr->index];
+ int regindex = nr >> 3;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (val)
+ data->beeps |= (1ULL << nr);
+ else
+ data->beeps &= ~(1ULL << nr);
+ err = nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+EXPORT_SYMBOL_GPL(nct6775_store_beep);
+
+static ssize_t
+show_temp_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);
+ 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
+ * source mapping.
+ */
+ nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
+ if (nr >= 0) {
+ int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
+
+ beep = (data->beeps >> bit) & 0x01;
+ }
+ return sprintf(buf, "%u\n", beep);
+}
+
+static ssize_t
+store_temp_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);
+ int nr, bit, regindex;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
+ if (nr < 0)
+ return nr;
+
+ bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
+ regindex = bit >> 3;
+
+ mutex_lock(&data->update_lock);
+ if (val)
+ data->beeps |= (1ULL << bit);
+ else
+ data->beeps &= ~(1ULL << bit);
+ err = nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
+ mutex_unlock(&data->update_lock);
+
+ return err ? : count;
+}
+
+static umode_t nct6775_in_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 in = index / 5; /* voltage index */
+
+ if (!(data->have_in & BIT(in)))
+ return 0;
+
+ return nct6775_attr_mode(data, attr);
+}
+
+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
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct sensor_device_template *nct6775_attributes_in_template[] = {
+ &sensor_dev_template_in_input,
+ &sensor_dev_template_in_alarm,
+ &sensor_dev_template_in_beep,
+ &sensor_dev_template_in_min,
+ &sensor_dev_template_in_max,
+ NULL
+};
+
+static const struct sensor_template_group nct6775_in_template_group = {
+ .templates = nct6775_attributes_in_template,
+ .is_visible = nct6775_in_is_visible,
+};
+
+static ssize_t
+show_fan(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 = sattr->index;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->rpm[nr]);
+}
+
+static ssize_t
+show_fan_min(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 = 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]));
+}
+
+static ssize_t
+show_fan_div(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 = sattr->index;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
+}
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ unsigned int reg;
+ u8 new_div;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if (!data->has_fan_div) {
+ /* NCT6776F or NCT6779D; we know this is a 13 bit register */
+ if (!val) {
+ val = 0xff1f;
+ } else {
+ if (val > 1350000U)
+ val = 135000U;
+ val = 1350000U / val;
+ val = (val & 0x1f) | ((val << 3) & 0xff00);
+ }
+ data->fan_min[nr] = val;
+ goto write_min; /* Leave fan divider alone */
+ }
+ if (!val) {
+ /* No min limit, alarm disabled */
+ data->fan_min[nr] = 255;
+ new_div = data->fan_div[nr]; /* No change */
+ dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
+ goto write_div;
+ }
+ reg = 1350000U / val;
+ if (reg >= 128 * 255) {
+ /*
+ * Speed below this value cannot possibly be represented,
+ * even with the highest divider (128)
+ */
+ data->fan_min[nr] = 254;
+ new_div = 7; /* 128 == BIT(7) */
+ dev_warn(dev,
+ "fan%u low limit %lu below minimum %u, set to minimum\n",
+ nr + 1, val, data->fan_from_reg_min(254, 7));
+ } else if (!reg) {
+ /*
+ * Speed above this value cannot possibly be represented,
+ * even with the lowest divider (1)
+ */
+ data->fan_min[nr] = 1;
+ new_div = 0; /* 1 == BIT(0) */
+ dev_warn(dev,
+ "fan%u low limit %lu above maximum %u, set to maximum\n",
+ nr + 1, val, data->fan_from_reg_min(1, 0));
+ } else {
+ /*
+ * Automatically pick the best divider, i.e. the one such
+ * that the min limit will correspond to a register value
+ * in the 96..192 range
+ */
+ new_div = 0;
+ while (reg > 192 && new_div < 7) {
+ reg >>= 1;
+ new_div++;
+ }
+ data->fan_min[nr] = reg;
+ }
+
+write_div:
+ /*
+ * Write both the fan clock divider (if it changed) and the new
+ * fan min (unconditionally)
+ */
+ if (new_div != data->fan_div[nr]) {
+ dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
+ nr + 1, div_from_reg(data->fan_div[nr]),
+ div_from_reg(new_div));
+ data->fan_div[nr] = new_div;
+ 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:
+ err = nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return err ? : count;
+}
+
+static ssize_t
+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;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ p = data->fan_pulses[sattr->index];
+ return sprintf(buf, "%d\n", p ? : 4);
+}
+
+static ssize_t
+store_fan_pulses(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u16 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val > 4)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->fan_pulses[nr] = val & 3;
+ err = nct6775_read_value(data, data->REG_FAN_PULSES[nr], &reg);
+ if (err)
+ goto out;
+ reg &= ~(0x03 << data->FAN_PULSE_SHIFT[nr]);
+ reg |= (val & 3) << data->FAN_PULSE_SHIFT[nr];
+ err = nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg);
+out:
+ mutex_unlock(&data->update_lock);
+
+ return err ? : count;
+}
+
+static umode_t nct6775_fan_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 fan = index / 6; /* fan index */
+ int nr = index % 6; /* attribute index */
+
+ if (!(data->has_fan & BIT(fan)))
+ return 0;
+
+ if (nr == 1 && data->ALARM_BITS[FAN_ALARM_BASE + fan] == -1)
+ return 0;
+ if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1)
+ return 0;
+ if (nr == 3 && !data->REG_FAN_PULSES[fan])
+ return 0;
+ if (nr == 4 && !(data->has_fan_min & BIT(fan)))
+ return 0;
+ if (nr == 5 && data->kind != nct6775)
+ return 0;
+
+ return nct6775_attr_mode(data, attr);
+}
+
+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
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct sensor_device_template *nct6775_attributes_fan_template[] = {
+ &sensor_dev_template_fan_input,
+ &sensor_dev_template_fan_alarm, /* 1 */
+ &sensor_dev_template_fan_beep, /* 2 */
+ &sensor_dev_template_fan_pulses,
+ &sensor_dev_template_fan_min, /* 4 */
+ &sensor_dev_template_fan_div, /* 5 */
+ NULL
+};
+
+static const struct sensor_template_group nct6775_fan_template_group = {
+ .templates = nct6775_attributes_fan_template,
+ .is_visible = nct6775_fan_is_visible,
+ .base = 1,
+};
+
+static ssize_t
+show_temp_label(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 = sattr->index;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
+}
+
+static ssize_t
+show_temp(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);
+ 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]));
+}
+
+static ssize_t
+store_temp(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ int err;
+ long val;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp[index][nr] = LM75_TEMP_TO_REG(val);
+ err = nct6775_write_temp(data, data->reg_temp[index][nr], data->temp[index][nr]);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static ssize_t
+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);
+}
+
+static ssize_t
+store_temp_offset(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->temp_offset[nr] = val;
+ err = nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return err ? : count;
+}
+
+static ssize_t
+show_temp_type(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 = sattr->index;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
+}
+
+static ssize_t
+store_temp_type(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u8 vbit, dbit;
+ u16 vbat, diode;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val != 1 && val != 3 && val != 4)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ data->temp_type[nr] = val;
+ vbit = 0x02 << nr;
+ dbit = data->DIODE_MASK << nr;
+
+ 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;
+ diode |= dbit;
+ break;
+ case 3: /* diode, voltage mode */
+ vbat |= dbit;
+ break;
+ case 4: /* thermistor */
+ break;
+ }
+ 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 err ? : count;
+}
+
+static umode_t nct6775_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 / 10; /* temp index */
+ int nr = index % 10; /* attribute index */
+
+ if (!(data->have_temp & BIT(temp)))
+ return 0;
+
+ if (nr == 1 && !data->temp_label)
+ return 0;
+
+ if (nr == 2 && find_temp_source(data, temp, data->num_temp_alarms) < 0)
+ return 0; /* alarm */
+
+ if (nr == 3 && find_temp_source(data, temp, data->num_temp_beeps) < 0)
+ return 0; /* beep */
+
+ if (nr == 4 && !data->reg_temp[1][temp]) /* max */
+ return 0;
+
+ if (nr == 5 && !data->reg_temp[2][temp]) /* max_hyst */
+ return 0;
+
+ if (nr == 6 && !data->reg_temp[3][temp]) /* crit */
+ return 0;
+
+ if (nr == 7 && !data->reg_temp[4][temp]) /* lcrit */
+ return 0;
+
+ /* offset and type only apply to fixed sensors */
+ if (nr > 7 && !(data->have_temp_fixed & BIT(temp)))
+ return 0;
+
+ return nct6775_attr_mode(data, attr);
+}
+
+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
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct sensor_device_template *nct6775_attributes_temp_template[] = {
+ &sensor_dev_template_temp_input,
+ &sensor_dev_template_temp_label,
+ &sensor_dev_template_temp_alarm, /* 2 */
+ &sensor_dev_template_temp_beep, /* 3 */
+ &sensor_dev_template_temp_max, /* 4 */
+ &sensor_dev_template_temp_max_hyst, /* 5 */
+ &sensor_dev_template_temp_crit, /* 6 */
+ &sensor_dev_template_temp_lcrit, /* 7 */
+ &sensor_dev_template_temp_offset, /* 8 */
+ &sensor_dev_template_temp_type, /* 9 */
+ NULL
+};
+
+static const struct sensor_template_group nct6775_temp_template_group = {
+ .templates = nct6775_attributes_temp_template,
+ .is_visible = nct6775_temp_is_visible,
+ .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]);
+}
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u16 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val > 1)
+ return -EINVAL;
+
+ /* Setting DC mode (0) is not supported for all chips/channels */
+ if (data->REG_PWM_MODE[nr] == 0) {
+ if (!val)
+ return -EINVAL;
+ return count;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->pwm_mode[nr] = val;
+ err = nct6775_read_value(data, data->REG_PWM_MODE[nr], &reg);
+ if (err)
+ goto out;
+ reg &= ~data->PWM_MODE_MASK[nr];
+ if (!val)
+ reg |= data->PWM_MODE_MASK[nr];
+ err = nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
+out:
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static ssize_t
+show_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);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ 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) {
+ 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);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int minval[7] = { 0, 1, 1, data->pwm[2][nr], 0, 0, 0 };
+ int maxval[7]
+ = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 };
+ int err;
+ u16 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ val = clamp_val(val, minval[index], maxval[index]);
+
+ mutex_lock(&data->update_lock);
+ data->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 */
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
+ reg &= 0x7f;
+ if (val)
+ reg |= 0x80;
+ err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+ }
+out:
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+/* Returns 0 if OK, -EINVAL otherwise */
+static int check_trip_points(struct nct6775_data *data, int nr)
+{
+ int i;
+
+ for (i = 0; i < data->auto_pwm_num - 1; i++) {
+ if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1])
+ return -EINVAL;
+ }
+ for (i = 0; i < data->auto_pwm_num - 1; i++) {
+ if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1])
+ return -EINVAL;
+ }
+ /* validate critical temperature and pwm if enabled (pwm > 0) */
+ if (data->auto_pwm[nr][data->auto_pwm_num]) {
+ if (data->auto_temp[nr][data->auto_pwm_num - 1] >
+ data->auto_temp[nr][data->auto_pwm_num] ||
+ data->auto_pwm[nr][data->auto_pwm_num - 1] >
+ data->auto_pwm[nr][data->auto_pwm_num])
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int pwm_update_registers(struct nct6775_data *data, int nr)
+{
+ u16 reg;
+ int err;
+
+ switch (data->pwm_enable[nr]) {
+ case off:
+ case manual:
+ break;
+ case speed_cruise:
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ return err;
+ reg = (reg & ~data->tolerance_mask) |
+ (data->target_speed_tolerance[nr] & data->tolerance_mask);
+ 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;
+ err = nct6775_write_value(data, data->REG_TOLERANCE_H[nr], reg);
+ if (err)
+ return err;
+ }
+ break;
+ case thermal_cruise:
+ err = nct6775_write_value(data, data->REG_TARGET[nr], data->target_temp[nr]);
+ if (err)
+ return err;
+ fallthrough;
+ default:
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ return err;
+ reg = (reg & ~data->tolerance_mask) |
+ data->temp_tolerance[0][nr];
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ if (err)
+ return err;
+ break;
+ }
+
+ return 0;
+}
+
+static ssize_t
+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]);
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u16 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val > sf4)
+ return -EINVAL;
+
+ if (val == sf3 && data->kind != nct6775)
+ return -EINVAL;
+
+ if (val == sf4 && check_trip_points(data, nr)) {
+ dev_err(dev, "Inconsistent trip points, not switching to SmartFan IV mode\n");
+ dev_err(dev, "Adjust trip points and try again\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->pwm_enable[nr] = val;
+ if (val == off) {
+ /*
+ * turn off pwm control: select manual mode, set pwm to maximum
+ */
+ data->pwm[0][nr] = 255;
+ err = nct6775_write_value(data, data->REG_PWM[0][nr], 255);
+ if (err)
+ goto out;
+ }
+ err = pwm_update_registers(data, nr);
+ if (err)
+ goto out;
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ goto out;
+ reg &= 0x0f;
+ reg |= pwm_enable_to_reg(val) << 4;
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+out:
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static ssize_t
+show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src)
+{
+ int i, sel = 0;
+
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & BIT(i)))
+ continue;
+ if (src == data->temp_src[i]) {
+ sel = i + 1;
+ break;
+ }
+ }
+
+ return sprintf(buf, "%d\n", sel);
+}
+
+static ssize_t
+show_pwm_temp_sel(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 index = sattr->index;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]);
+}
+
+static ssize_t
+store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err, src;
+ u16 reg;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val == 0 || val > NUM_TEMP)
+ return -EINVAL;
+ if (!(data->have_temp & BIT(val - 1)) || !data->temp_src[val - 1])
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ src = data->temp_src[val - 1];
+ data->pwm_temp_sel[nr] = src;
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
+ reg &= 0xe0;
+ reg |= src;
+ err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+out:
+ mutex_unlock(&data->update_lock);
+
+ return err ? : count;
+}
+
+static ssize_t
+show_pwm_weight_temp_sel(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 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]);
+}
+
+static ssize_t
+store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err, src;
+ u16 reg;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > NUM_TEMP)
+ return -EINVAL;
+ val = array_index_nospec(val, NUM_TEMP + 1);
+ if (val && (!(data->have_temp & BIT(val - 1)) ||
+ !data->temp_src[val - 1]))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (val) {
+ src = data->temp_src[val - 1];
+ data->pwm_weight_temp_sel[nr] = src;
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
+ reg &= 0xe0;
+ reg |= (src | 0x80);
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ } else {
+ data->pwm_weight_temp_sel[nr] = 0;
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
+ reg &= 0x7f;
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ }
+out:
+ mutex_unlock(&data->update_lock);
+
+ return err ? : count;
+}
+
+static ssize_t
+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);
+}
+
+static ssize_t
+store_target_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0,
+ data->target_temp_mask);
+
+ mutex_lock(&data->update_lock);
+ data->target_temp[nr] = val;
+ err = pwm_update_registers(data, nr);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static ssize_t
+show_target_speed(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 = 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]));
+}
+
+static ssize_t
+store_target_speed(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u16 speed;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(val, 0, 1350000U);
+ speed = fan_to_reg(val, data->fan_div[nr]);
+
+ mutex_lock(&data->update_lock);
+ data->target_speed[nr] = speed;
+ err = pwm_update_registers(data, nr);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static ssize_t
+show_temp_tolerance(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);
+ 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);
+}
+
+static ssize_t
+store_temp_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ /* Limit tolerance as needed */
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask);
+
+ mutex_lock(&data->update_lock);
+ data->temp_tolerance[index][nr] = val;
+ if (index)
+ err = pwm_update_registers(data, nr);
+ else
+ err = nct6775_write_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[nr], val);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+/*
+ * Fan speed tolerance is a tricky beast, since the associated register is
+ * a tick counter, but the value is reported and configured as rpm.
+ * Compute resulting low and high rpm values and report the difference.
+ * A fan speed tolerance only makes sense if a fan target speed has been
+ * configured, so only display values other than 0 if that is the case.
+ */
+static ssize_t
+show_speed_tolerance(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 = sattr->index;
+ 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];
+ int high = target + data->target_speed_tolerance[nr];
+
+ if (low <= 0)
+ low = 1;
+ if (high > 0xffff)
+ high = 0xffff;
+ if (high < low)
+ high = low;
+
+ tolerance = (fan_from_reg16(low, data->fan_div[nr])
+ - fan_from_reg16(high, data->fan_div[nr])) / 2;
+ }
+
+ return sprintf(buf, "%d\n", tolerance);
+}
+
+static ssize_t
+store_speed_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ int low, high;
+
+ err = kstrtoul(buf, 10, &val);
+ 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;
+ if (low <= 0)
+ low = 1;
+ if (high < low)
+ high = low;
+
+ val = (fan_to_reg(low, data->fan_div[nr]) -
+ fan_to_reg(high, data->fan_div[nr])) / 2;
+
+ /* Limit tolerance as needed */
+ val = clamp_val(val, 0, data->speed_tolerance_limit);
+
+ mutex_lock(&data->update_lock);
+ data->target_speed_tolerance[nr] = val;
+ err = pwm_update_registers(data, nr);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+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 */
+
+static ssize_t
+show_weight_temp(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);
+ 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);
+}
+
+static ssize_t
+store_weight_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->weight_temp[index][nr] = val;
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+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",
+ 0644, show_weight_temp, store_weight_temp, 0, 0);
+SENSOR_TEMPLATE_2(pwm_weight_temp_step_tol, "pwm%d_weight_temp_step_tol",
+ 0644, show_weight_temp, store_weight_temp, 0, 1);
+SENSOR_TEMPLATE_2(pwm_weight_temp_step_base, "pwm%d_weight_temp_step_base",
+ 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)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ 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]));
+}
+
+static ssize_t
+store_fan_time(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = step_time_to_reg(val, data->pwm_mode[nr]);
+ mutex_lock(&data->update_lock);
+ data->fan_time[index][nr] = val;
+ err = nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val);
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static ssize_t
+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]);
+}
+
+static ssize_t
+store_auto_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int point = sattr->index;
+ unsigned long val;
+ int err;
+ u16 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 255)
+ return -EINVAL;
+
+ if (point == data->auto_pwm_num) {
+ if (data->kind != nct6775 && !val)
+ return -EINVAL;
+ if (data->kind != nct6779 && val)
+ val = 0xff;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->auto_pwm[nr][point] = val;
+ if (point < data->auto_pwm_num) {
+ 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) */
+ err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[nr], &reg);
+ if (err)
+ break;
+ if (val)
+ reg |= 0x02;
+ else
+ reg &= ~0x02;
+ err = nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], reg);
+ break;
+ case nct6776:
+ break; /* always enabled, nothing to do */
+ case nct6106:
+ case nct6116:
+ case nct6779:
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ 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], &reg);
+ if (err)
+ break;
+ if (val == 255)
+ reg &= ~data->CRITICAL_PWM_ENABLE_MASK;
+ else
+ reg |= data->CRITICAL_PWM_ENABLE_MASK;
+ err = nct6775_write_value(data, data->REG_CRITICAL_PWM_ENABLE[nr], reg);
+ break;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static ssize_t
+show_auto_temp(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);
+ 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.
+ */
+ return sprintf(buf, "%d\n", data->auto_temp[nr][point] * 1000);
+}
+
+static ssize_t
+store_auto_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int point = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val > 255000)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000);
+ if (point < data->auto_pwm_num) {
+ err = nct6775_write_value(data, NCT6775_AUTO_TEMP(data, nr, point),
+ data->auto_temp[nr][point]);
+ } else {
+ err = nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr],
+ data->auto_temp[nr][point]);
+ }
+ mutex_unlock(&data->update_lock);
+ return err ? : count;
+}
+
+static umode_t nct6775_pwm_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 pwm = index / 36; /* pwm index */
+ int nr = index % 36; /* attribute index */
+
+ if (!(data->has_pwm & BIT(pwm)))
+ return 0;
+
+ if ((nr >= 14 && nr <= 18) || nr == 21) /* weight */
+ if (!data->REG_WEIGHT_TEMP_SEL[pwm])
+ return 0;
+ if (nr == 19 && data->REG_PWM[3] == NULL) /* pwm_max */
+ return 0;
+ if (nr == 20 && data->REG_PWM[4] == NULL) /* pwm_step */
+ return 0;
+ if (nr == 21 && data->REG_PWM[6] == NULL) /* weight_duty_base */
+ return 0;
+
+ if (nr >= 22 && nr <= 35) { /* auto point */
+ int api = (nr - 22) / 2; /* auto point index */
+
+ if (api > data->auto_pwm_num)
+ return 0;
+ }
+ return nct6775_attr_mode(data, attr);
+}
+
+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", 0644,
+ show_fan_time, store_fan_time, 0, 2);
+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",
+ 0644, show_temp_tolerance, store_temp_tolerance, 0, 1);
+
+SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", 0644, show_pwm, store_pwm, 0, 3);
+
+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",
+ 0644, show_auto_pwm, store_auto_pwm, 0, 0);
+SENSOR_TEMPLATE_2(pwm_auto_point1_temp, "pwm%d_auto_point1_temp",
+ 0644, show_auto_temp, store_auto_temp, 0, 0);
+
+SENSOR_TEMPLATE_2(pwm_auto_point2_pwm, "pwm%d_auto_point2_pwm",
+ 0644, show_auto_pwm, store_auto_pwm, 0, 1);
+SENSOR_TEMPLATE_2(pwm_auto_point2_temp, "pwm%d_auto_point2_temp",
+ 0644, show_auto_temp, store_auto_temp, 0, 1);
+
+SENSOR_TEMPLATE_2(pwm_auto_point3_pwm, "pwm%d_auto_point3_pwm",
+ 0644, show_auto_pwm, store_auto_pwm, 0, 2);
+SENSOR_TEMPLATE_2(pwm_auto_point3_temp, "pwm%d_auto_point3_temp",
+ 0644, show_auto_temp, store_auto_temp, 0, 2);
+
+SENSOR_TEMPLATE_2(pwm_auto_point4_pwm, "pwm%d_auto_point4_pwm",
+ 0644, show_auto_pwm, store_auto_pwm, 0, 3);
+SENSOR_TEMPLATE_2(pwm_auto_point4_temp, "pwm%d_auto_point4_temp",
+ 0644, show_auto_temp, store_auto_temp, 0, 3);
+
+SENSOR_TEMPLATE_2(pwm_auto_point5_pwm, "pwm%d_auto_point5_pwm",
+ 0644, show_auto_pwm, store_auto_pwm, 0, 4);
+SENSOR_TEMPLATE_2(pwm_auto_point5_temp, "pwm%d_auto_point5_temp",
+ 0644, show_auto_temp, store_auto_temp, 0, 4);
+
+SENSOR_TEMPLATE_2(pwm_auto_point6_pwm, "pwm%d_auto_point6_pwm",
+ 0644, show_auto_pwm, store_auto_pwm, 0, 5);
+SENSOR_TEMPLATE_2(pwm_auto_point6_temp, "pwm%d_auto_point6_temp",
+ 0644, show_auto_temp, store_auto_temp, 0, 5);
+
+SENSOR_TEMPLATE_2(pwm_auto_point7_pwm, "pwm%d_auto_point7_pwm",
+ 0644, show_auto_pwm, store_auto_pwm, 0, 6);
+SENSOR_TEMPLATE_2(pwm_auto_point7_temp, "pwm%d_auto_point7_temp",
+ 0644, show_auto_temp, store_auto_temp, 0, 6);
+
+/*
+ * nct6775_pwm_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 sensor_device_template *nct6775_attributes_pwm_template[] = {
+ &sensor_dev_template_pwm,
+ &sensor_dev_template_pwm_mode,
+ &sensor_dev_template_pwm_enable,
+ &sensor_dev_template_pwm_temp_sel,
+ &sensor_dev_template_pwm_temp_tolerance,
+ &sensor_dev_template_pwm_crit_temp_tolerance,
+ &sensor_dev_template_pwm_target_temp,
+ &sensor_dev_template_fan_target,
+ &sensor_dev_template_fan_tolerance,
+ &sensor_dev_template_pwm_stop_time,
+ &sensor_dev_template_pwm_step_up_time,
+ &sensor_dev_template_pwm_step_down_time,
+ &sensor_dev_template_pwm_start,
+ &sensor_dev_template_pwm_floor,
+ &sensor_dev_template_pwm_weight_temp_sel, /* 14 */
+ &sensor_dev_template_pwm_weight_temp_step,
+ &sensor_dev_template_pwm_weight_temp_step_tol,
+ &sensor_dev_template_pwm_weight_temp_step_base,
+ &sensor_dev_template_pwm_weight_duty_step, /* 18 */
+ &sensor_dev_template_pwm_max, /* 19 */
+ &sensor_dev_template_pwm_step, /* 20 */
+ &sensor_dev_template_pwm_weight_duty_base, /* 21 */
+ &sensor_dev_template_pwm_auto_point1_pwm, /* 22 */
+ &sensor_dev_template_pwm_auto_point1_temp,
+ &sensor_dev_template_pwm_auto_point2_pwm,
+ &sensor_dev_template_pwm_auto_point2_temp,
+ &sensor_dev_template_pwm_auto_point3_pwm,
+ &sensor_dev_template_pwm_auto_point3_temp,
+ &sensor_dev_template_pwm_auto_point4_pwm,
+ &sensor_dev_template_pwm_auto_point4_temp,
+ &sensor_dev_template_pwm_auto_point5_pwm,
+ &sensor_dev_template_pwm_auto_point5_temp,
+ &sensor_dev_template_pwm_auto_point6_pwm,
+ &sensor_dev_template_pwm_auto_point6_temp,
+ &sensor_dev_template_pwm_auto_point7_pwm,
+ &sensor_dev_template_pwm_auto_point7_temp, /* 35 */
+
+ NULL
+};
+
+static const struct sensor_template_group nct6775_pwm_template_group = {
+ .templates = nct6775_attributes_pwm_template,
+ .is_visible = nct6775_pwm_is_visible,
+ .base = 1,
+};
+
+static inline int nct6775_init_device(struct nct6775_data *data)
+{
+ int i, err;
+ u16 tmp, diode;
+
+ /* Start monitoring if needed */
+ if (data->REG_CONFIG) {
+ 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 */
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & BIT(i)))
+ continue;
+ if (!data->reg_temp_config[i])
+ continue;
+ 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 */
+ 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;
+ }
+
+ 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)))
+ continue;
+ if ((tmp & (data->DIODE_MASK << i))) /* diode */
+ data->temp_type[i]
+ = 3 - ((diode >> i) & data->DIODE_MASK);
+ else /* thermistor */
+ data->temp_type[i] = 4;
+ }
+
+ return 0;
+}
+
+static int add_temp_sensors(struct nct6775_data *data, const u16 *regp,
+ int *available, int *mask)
+{
+ int i, err;
+ u16 src;
+
+ for (i = 0; i < data->pwm_num && *available; i++) {
+ int index;
+
+ if (!regp[i])
+ continue;
+ err = nct6775_read_value(data, regp[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
+ if (!src || (*mask & BIT(src)))
+ continue;
+ if (!(data->temp_mask & BIT(src)))
+ continue;
+
+ index = __ffs(*available);
+ err = nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
+ if (err)
+ return err;
+ *available &= ~BIT(index);
+ *mask |= BIT(src);
+ }
+
+ return 0;
+}
+
+int nct6775_probe(struct device *dev, struct nct6775_data *data,
+ const struct regmap_config *regmapcfg)
+{
+ int i, s, err = 0;
+ 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, num_reg_tsi_temp;
+ struct device *hwmon_dev;
+ struct sensor_template_group tsi_temp_tg;
+
+ data->regmap = devm_regmap_init(dev, NULL, data, regmapcfg);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ mutex_init(&data->update_lock);
+ data->name = nct6775_device_names[data->kind];
+ data->bank = 0xff; /* Force initial bank selection */
+
+ switch (data->kind) {
+ case nct6106:
+ data->in_num = 9;
+ data->pwm_num = 3;
+ data->auto_pwm_num = 4;
+ data->temp_fixed_num = 3;
+ data->num_temp_alarms = 6;
+ data->num_temp_beeps = 6;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+
+ data->temp_label = nct6776_temp_label;
+ data->temp_mask = NCT6776_TEMP_MASK;
+ data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK;
+
+ data->REG_VBAT = NCT6106_REG_VBAT;
+ data->REG_DIODE = NCT6106_REG_DIODE;
+ data->DIODE_MASK = NCT6106_DIODE_MASK;
+ data->REG_VIN = NCT6106_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX;
+ data->REG_TARGET = NCT6106_REG_TARGET;
+ data->REG_FAN = NCT6106_REG_FAN;
+ data->REG_FAN_MODE = NCT6106_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6106_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6106_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6106_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6106_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6106_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6116_REG_PWM;
+ data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6106_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6106_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6106_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6106_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6106_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6106_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6106_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM;
+ data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6106_REG_ALARM;
+ 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;
+ reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+ reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+ reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
+
+ break;
+ case nct6116:
+ data->in_num = 9;
+ data->pwm_num = 3;
+ data->auto_pwm_num = 4;
+ data->temp_fixed_num = 3;
+ data->num_temp_alarms = 3;
+ data->num_temp_beeps = 3;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+
+ data->temp_label = nct6776_temp_label;
+ data->temp_mask = NCT6776_TEMP_MASK;
+ data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK;
+
+ data->REG_VBAT = NCT6106_REG_VBAT;
+ data->REG_DIODE = NCT6106_REG_DIODE;
+ data->DIODE_MASK = NCT6106_DIODE_MASK;
+ data->REG_VIN = NCT6106_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX;
+ data->REG_TARGET = NCT6116_REG_TARGET;
+ data->REG_FAN = NCT6116_REG_FAN;
+ data->REG_FAN_MODE = NCT6116_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6116_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6116_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6116_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6116_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6116_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6116_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6116_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6116_REG_PWM;
+ data->REG_PWM[1] = NCT6116_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6116_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6106_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6106_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6116_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6116_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6116_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6116_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6116_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
+ data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6106_REG_ALARM;
+ 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;
+ reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+ reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+ reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
+
+ break;
+ case nct6775:
+ data->in_num = 9;
+ data->pwm_num = 3;
+ data->auto_pwm_num = 6;
+ data->has_fan_div = true;
+ data->temp_fixed_num = 3;
+ data->num_temp_alarms = 3;
+ data->num_temp_beeps = 3;
+
+ data->ALARM_BITS = NCT6775_ALARM_BITS;
+ data->BEEP_BITS = NCT6775_BEEP_BITS;
+
+ data->fan_from_reg = fan_from_reg16;
+ data->fan_from_reg_min = fan_from_reg8;
+ data->target_temp_mask = 0x7f;
+ data->tolerance_mask = 0x0f;
+ data->speed_tolerance_limit = 15;
+
+ data->temp_label = nct6775_temp_label;
+ data->temp_mask = NCT6775_TEMP_MASK;
+ data->virt_temp_mask = NCT6775_VIRT_TEMP_MASK;
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
+ data->REG_VIN = NCT6775_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6775_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6775_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[3] = NCT6775_REG_FAN_MAX_OUTPUT;
+ data->REG_PWM[4] = NCT6775_REG_FAN_STEP_OUTPUT;
+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6775_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+ 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;
+ reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6775_REG_TEMP_CRIT;
+
+ break;
+ case nct6776:
+ data->in_num = 9;
+ data->pwm_num = 3;
+ data->auto_pwm_num = 4;
+ data->has_fan_div = false;
+ data->temp_fixed_num = 3;
+ data->num_temp_alarms = 3;
+ data->num_temp_beeps = 6;
+
+ data->ALARM_BITS = NCT6776_ALARM_BITS;
+ data->BEEP_BITS = NCT6776_BEEP_BITS;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+ data->target_temp_mask = 0xff;
+ data->tolerance_mask = 0x07;
+ data->speed_tolerance_limit = 63;
+
+ data->temp_label = nct6776_temp_label;
+ data->temp_mask = NCT6776_TEMP_MASK;
+ data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK;
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
+ data->REG_VIN = NCT6775_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6775_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+ 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;
+ reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6776_REG_TEMP_CRIT;
+
+ break;
+ case nct6779:
+ data->in_num = 15;
+ data->pwm_num = 5;
+ data->auto_pwm_num = 4;
+ data->has_fan_div = false;
+ data->temp_fixed_num = 6;
+ data->num_temp_alarms = 2;
+ data->num_temp_beeps = 2;
+
+ data->ALARM_BITS = NCT6779_ALARM_BITS;
+ data->BEEP_BITS = NCT6779_BEEP_BITS;
+
+ data->fan_from_reg = fan_from_reg_rpm;
+ data->fan_from_reg_min = fan_from_reg13;
+ data->target_temp_mask = 0xff;
+ data->tolerance_mask = 0x07;
+ data->speed_tolerance_limit = 63;
+
+ data->temp_label = nct6779_temp_label;
+ data->temp_mask = NCT6779_TEMP_MASK;
+ data->virt_temp_mask = NCT6779_VIRT_TEMP_MASK;
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
+ data->REG_VIN = NCT6779_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6779_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6779_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM;
+ data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+ 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;
+ reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6779_REG_TEMP_CRIT;
+
+ break;
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ data->in_num = 15;
+ data->pwm_num = (data->kind == nct6796 ||
+ data->kind == nct6797 ||
+ data->kind == nct6798) ? 7 : 6;
+ data->auto_pwm_num = 4;
+ data->has_fan_div = false;
+ data->temp_fixed_num = 6;
+ data->num_temp_alarms = 2;
+ data->num_temp_beeps = 2;
+
+ data->ALARM_BITS = NCT6791_ALARM_BITS;
+ data->BEEP_BITS = NCT6779_BEEP_BITS;
+
+ data->fan_from_reg = fan_from_reg_rpm;
+ data->fan_from_reg_min = fan_from_reg13;
+ data->target_temp_mask = 0xff;
+ data->tolerance_mask = 0x07;
+ data->speed_tolerance_limit = 63;
+
+ switch (data->kind) {
+ default:
+ case nct6791:
+ data->temp_label = nct6779_temp_label;
+ data->temp_mask = NCT6791_TEMP_MASK;
+ data->virt_temp_mask = NCT6791_VIRT_TEMP_MASK;
+ break;
+ case nct6792:
+ data->temp_label = nct6792_temp_label;
+ data->temp_mask = NCT6792_TEMP_MASK;
+ data->virt_temp_mask = NCT6792_VIRT_TEMP_MASK;
+ break;
+ case nct6793:
+ data->temp_label = nct6793_temp_label;
+ data->temp_mask = NCT6793_TEMP_MASK;
+ data->virt_temp_mask = NCT6793_VIRT_TEMP_MASK;
+ break;
+ case nct6795:
+ case nct6797:
+ data->temp_label = nct6795_temp_label;
+ data->temp_mask = NCT6795_TEMP_MASK;
+ data->virt_temp_mask = NCT6795_VIRT_TEMP_MASK;
+ break;
+ case nct6796:
+ data->temp_label = nct6796_temp_label;
+ data->temp_mask = NCT6796_TEMP_MASK;
+ data->virt_temp_mask = NCT6796_VIRT_TEMP_MASK;
+ break;
+ case nct6798:
+ data->temp_label = nct6798_temp_label;
+ data->temp_mask = NCT6798_TEMP_MASK;
+ data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK;
+ break;
+ }
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
+ data->REG_VIN = NCT6779_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6779_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6791_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6791_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6779_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM;
+ data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6791_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6791_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6791_REG_ALARM;
+ if (data->kind == nct6791)
+ 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);
+ if (data->kind == nct6791) {
+ reg_temp_mon = NCT6779_REG_TEMP_MON;
+ num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
+ } else {
+ reg_temp_mon = NCT6792_REG_TEMP_MON;
+ num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON);
+ }
+ reg_temp_over = NCT6779_REG_TEMP_OVER;
+ reg_temp_hyst = NCT6779_REG_TEMP_HYST;
+ reg_temp_config = NCT6779_REG_TEMP_CONFIG;
+ reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6779_REG_TEMP_CRIT;
+
+ break;
+ default:
+ return -ENODEV;
+ }
+ data->have_in = BIT(data->in_num) - 1;
+ data->have_temp = 0;
+
+ /*
+ * On some boards, not all available temperature sources are monitored,
+ * even though some of the monitoring registers are unused.
+ * Get list of unused monitoring registers, then detect if any fan
+ * controls are configured to use unmonitored temperature sources.
+ * If so, assign the unmonitored temperature sources to available
+ * monitoring registers.
+ */
+ mask = 0;
+ available = 0;
+ for (i = 0; i < num_reg_temp; i++) {
+ if (reg_temp[i] == 0)
+ continue;
+
+ 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);
+
+ mask |= BIT(src);
+ }
+
+ /*
+ * Now find unmonitored temperature registers and enable monitoring
+ * if additional monitoring registers are available.
+ */
+ 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 */
+ for (i = 0; i < num_reg_temp; i++) {
+ if (reg_temp[i] == 0)
+ continue;
+
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
+ if (!src || (mask & BIT(src)))
+ continue;
+
+ if (!(data->temp_mask & BIT(src))) {
+ dev_info(dev,
+ "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
+ src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]);
+ continue;
+ }
+
+ mask |= BIT(src);
+
+ /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
+ if (src <= data->temp_fixed_num) {
+ data->have_temp |= BIT(src - 1);
+ data->have_temp_fixed |= BIT(src - 1);
+ data->reg_temp[0][src - 1] = reg_temp[i];
+ data->reg_temp[1][src - 1] = reg_temp_over[i];
+ data->reg_temp[2][src - 1] = reg_temp_hyst[i];
+ if (reg_temp_crit_h && reg_temp_crit_h[i])
+ data->reg_temp[3][src - 1] = reg_temp_crit_h[i];
+ else if (reg_temp_crit[src - 1])
+ data->reg_temp[3][src - 1]
+ = reg_temp_crit[src - 1];
+ if (reg_temp_crit_l && reg_temp_crit_l[i])
+ data->reg_temp[4][src - 1] = reg_temp_crit_l[i];
+ data->reg_temp_config[src - 1] = reg_temp_config[i];
+ data->temp_src[src - 1] = src;
+ continue;
+ }
+
+ if (s >= NUM_TEMP)
+ continue;
+
+ /* Use dynamic index for other sources */
+ data->have_temp |= BIT(s);
+ data->reg_temp[0][s] = reg_temp[i];
+ data->reg_temp[1][s] = reg_temp_over[i];
+ data->reg_temp[2][s] = reg_temp_hyst[i];
+ data->reg_temp_config[s] = reg_temp_config[i];
+ if (reg_temp_crit_h && reg_temp_crit_h[i])
+ data->reg_temp[3][s] = reg_temp_crit_h[i];
+ else if (reg_temp_crit[src - 1])
+ data->reg_temp[3][s] = reg_temp_crit[src - 1];
+ if (reg_temp_crit_l && reg_temp_crit_l[i])
+ data->reg_temp[4][s] = reg_temp_crit_l[i];
+
+ data->temp_src[s] = src;
+ s++;
+ }
+
+ /*
+ * Repeat with temperatures used for fan control.
+ * This set of registers does not support limits.
+ */
+ for (i = 0; i < num_reg_temp_mon; i++) {
+ if (reg_temp_mon[i] == 0)
+ continue;
+
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
+ if (!src)
+ continue;
+
+ if (!(data->temp_mask & BIT(src))) {
+ dev_info(dev,
+ "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
+ src, i, data->REG_TEMP_SEL[i],
+ reg_temp_mon[i]);
+ continue;
+ }
+
+ /*
+ * For virtual temperature sources, the 'virtual' temperature
+ * for each fan reflects a different temperature, and there
+ * are no duplicates.
+ */
+ if (!(data->virt_temp_mask & BIT(src))) {
+ if (mask & BIT(src))
+ continue;
+ mask |= BIT(src);
+ }
+
+ /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
+ if (src <= data->temp_fixed_num) {
+ if (data->have_temp & BIT(src - 1))
+ continue;
+ data->have_temp |= BIT(src - 1);
+ data->have_temp_fixed |= BIT(src - 1);
+ data->reg_temp[0][src - 1] = reg_temp_mon[i];
+ data->temp_src[src - 1] = src;
+ continue;
+ }
+
+ if (s >= NUM_TEMP)
+ continue;
+
+ /* Use dynamic index for other sources */
+ data->have_temp |= BIT(s);
+ data->reg_temp[0][s] = reg_temp_mon[i];
+ data->temp_src[s] = src;
+ s++;
+ }
+
+#ifdef USE_ALTERNATE
+ /*
+ * Go through the list of alternate temp registers and enable
+ * if possible.
+ * The temperature is already monitored if the respective bit in <mask>
+ * is set.
+ */
+ for (i = 0; i < 31; i++) {
+ if (!(data->temp_mask & BIT(i + 1)))
+ continue;
+ if (!reg_temp_alternate[i])
+ continue;
+ if (mask & BIT(i + 1))
+ continue;
+ if (i < data->temp_fixed_num) {
+ if (data->have_temp & BIT(i))
+ continue;
+ data->have_temp |= BIT(i);
+ data->have_temp_fixed |= BIT(i);
+ data->reg_temp[0][i] = reg_temp_alternate[i];
+ if (i < num_reg_temp) {
+ data->reg_temp[1][i] = reg_temp_over[i];
+ data->reg_temp[2][i] = reg_temp_hyst[i];
+ }
+ data->temp_src[i] = i + 1;
+ continue;
+ }
+
+ if (s >= NUM_TEMP) /* Abort if no more space */
+ break;
+
+ data->have_temp |= BIT(s);
+ data->reg_temp[0][s] = reg_temp_alternate[i];
+ data->temp_src[s] = i + 1;
+ s++;
+ }
+#endif /* USE_ALTERNATE */
+
+ /* Check which TSIx_TEMP registers are active */
+ for (i = 0; i < num_reg_tsi_temp; i++) {
+ u16 tmp;
+
+ err = nct6775_read_value(data, data->REG_TSI_TEMP[i], &tmp);
+ if (err)
+ return err;
+ if (tmp)
+ data->have_tsi_temp |= BIT(i);
+ }
+
+ /* Initialize the chip */
+ err = nct6775_init_device(data);
+ if (err)
+ return err;
+
+ if (data->driver_init) {
+ err = data->driver_init(data);
+ if (err)
+ return err;
+ }
+
+ /* Read fan clock dividers immediately */
+ err = nct6775_init_fan_common(dev, data);
+ if (err)
+ return err;
+
+ /* Register sysfs hooks */
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_pwm_template_group,
+ data->pwm_num);
+ if (err)
+ return err;
+
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_in_template_group,
+ fls(data->have_in));
+ if (err)
+ return err;
+
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_fan_template_group,
+ fls(data->has_fan));
+ if (err)
+ return err;
+
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_temp_template_group,
+ fls(data->have_temp));
+ if (err)
+ return err;
+
+ 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)
+ return err;
+ }
+
+ 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("Core driver for NCT6775F and compatible chips");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c
new file mode 100644
index 000000000..e1bcd1146
--- /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 000000000..76c6b564d
--- /dev/null
+++ b/drivers/hwmon/nct6775-platform.c
@@ -0,0 +1,1325 @@
+// 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 "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_METHOD "WMBD"
+#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
+#define ASUSWMI_DEVICE_HID "PNP0C14"
+#define ASUSWMI_DEVICE_UID "ASUSWMI"
+#define ASUSMSI_DEVICE_UID "AsusMbSwInterface"
+
+#if IS_ENABLED(CONFIG_ACPI)
+/*
+ * ASUS boards have only one device with WMI "WMBD" method and have provided
+ * access to only one SuperIO chip at 0x0290.
+ */
+static struct acpi_device *asus_acpi_dev;
+#endif
+
+static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
+{
+#if IS_ENABLED(CONFIG_ACPI)
+ acpi_handle handle = acpi_device_handle(asus_acpi_dev);
+ u32 args = bank | (reg << 8) | (val << 16);
+ struct acpi_object_list input;
+ union acpi_object params[3];
+ unsigned long long result;
+ acpi_status status;
+
+ params[0].type = ACPI_TYPE_INTEGER;
+ params[0].integer.value = 0;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = method_id;
+ params[2].type = ACPI_TYPE_BUFFER;
+ params[2].buffer.length = sizeof(args);
+ params[2].buffer.pointer = (void *)&args;
+ input.count = 3;
+ input.pointer = params;
+
+ status = acpi_evaluate_integer(handle, ASUSWMI_METHOD, &input, &result);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (retval)
+ *retval = (u32)result & 0xFFFFFFFF;
+
+ 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 B550-CREATOR",
+ "ProArt X570-CREATOR WIFI",
+ "ProArt Z490-CREATOR 10G",
+ "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 EXTREME",
+ "ROG CROSSHAIR VIII FORMULA",
+ "ROG CROSSHAIR VIII HERO",
+ "ROG CROSSHAIR VIII HERO (WI-FI)",
+ "ROG CROSSHAIR VIII IMPACT",
+ "ROG STRIX B550-A GAMING",
+ "ROG STRIX B550-E GAMING",
+ "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-E",
+ "TUF GAMING B550M-E (WI-FI)",
+ "TUF GAMING B550M-PLUS",
+ "TUF GAMING B550M-PLUS (WI-FI)",
+ "TUF GAMING B550M-PLUS WIFI II",
+ "TUF GAMING B550-PLUS",
+ "TUF GAMING B550-PLUS WIFI II",
+ "TUF GAMING B550-PRO",
+ "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 const char * const asus_msi_boards[] = {
+ "EX-B660M-V5 PRO D4",
+ "PRIME B650-PLUS",
+ "PRIME B650M-A",
+ "PRIME B650M-A AX",
+ "PRIME B650M-A II",
+ "PRIME B650M-A WIFI",
+ "PRIME B650M-A WIFI II",
+ "PRIME B660M-A D4",
+ "PRIME B660M-A WIFI D4",
+ "PRIME X670-P",
+ "PRIME X670-P WIFI",
+ "PRIME X670E-PRO WIFI",
+ "Pro B660M-C-D4",
+ "ProArt B660-CREATOR D4",
+ "ProArt X670E-CREATOR WIFI",
+ "ROG CROSSHAIR X670E EXTREME",
+ "ROG CROSSHAIR X670E GENE",
+ "ROG CROSSHAIR X670E HERO",
+ "ROG MAXIMUS XIII EXTREME GLACIAL",
+ "ROG MAXIMUS Z690 EXTREME",
+ "ROG MAXIMUS Z690 EXTREME GLACIAL",
+ "ROG STRIX B650-A GAMING WIFI",
+ "ROG STRIX B650E-E GAMING WIFI",
+ "ROG STRIX B650E-F GAMING WIFI",
+ "ROG STRIX B650E-I GAMING WIFI",
+ "ROG STRIX B660-A GAMING WIFI D4",
+ "ROG STRIX B660-F GAMING WIFI",
+ "ROG STRIX B660-G GAMING WIFI",
+ "ROG STRIX B660-I GAMING WIFI",
+ "ROG STRIX X670E-A GAMING WIFI",
+ "ROG STRIX X670E-E GAMING WIFI",
+ "ROG STRIX X670E-F GAMING WIFI",
+ "ROG STRIX X670E-I GAMING WIFI",
+ "ROG STRIX Z590-A GAMING WIFI II",
+ "ROG STRIX Z690-A GAMING WIFI D4",
+ "TUF GAMING B650-PLUS",
+ "TUF GAMING B650-PLUS WIFI",
+ "TUF GAMING B650M-PLUS",
+ "TUF GAMING B650M-PLUS WIFI",
+ "TUF GAMING B660M-PLUS WIFI",
+ "TUF GAMING X670E-PLUS",
+ "TUF GAMING X670E-PLUS WIFI",
+ "TUF GAMING Z590-PLUS WIFI",
+};
+
+#if IS_ENABLED(CONFIG_ACPI)
+/*
+ * Callback for acpi_bus_for_each_dev() to find the right device
+ * by _UID and _HID and return 1 to stop iteration.
+ */
+static int nct6775_asuswmi_device_match(struct device *dev, void *data)
+{
+ struct acpi_device *adev = to_acpi_device(dev);
+ const char *uid = acpi_device_uid(adev);
+ const char *hid = acpi_device_hid(adev);
+
+ if (hid && !strcmp(hid, ASUSWMI_DEVICE_HID) && uid && !strcmp(uid, data)) {
+ asus_acpi_dev = adev;
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+static enum sensor_access nct6775_determine_access(const char *device_uid)
+{
+#if IS_ENABLED(CONFIG_ACPI)
+ u8 tmp;
+
+ acpi_bus_for_each_dev(nct6775_asuswmi_device_match, (void *)device_uid);
+ if (!asus_acpi_dev)
+ return access_direct;
+
+ /* if reading chip id via ACPI succeeds, use WMI "WMBD" method for access */
+ if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) {
+ pr_debug("Using Asus WMBD method of %s to access %#x chip.\n", device_uid, tmp);
+ return access_asuswmi;
+ }
+#endif
+
+ return access_direct;
+}
+
+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;
+
+ 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)
+ access = nct6775_determine_access(ASUSWMI_DEVICE_UID);
+
+ err = match_string(asus_msi_boards, ARRAY_SIZE(asus_msi_boards),
+ board_name);
+ if (err >= 0)
+ access = nct6775_determine_access(ASUSMSI_DEVICE_UID);
+ }
+
+ /*
+ * 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 000000000..be41848c3
--- /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
new file mode 100644
index 000000000..e64c12d90
--- /dev/null
+++ b/drivers/hwmon/nct7802.c
@@ -0,0 +1,1235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * nct7802 - Driver for Nuvoton NCT7802Y
+ *
+ * Copyright (C) 2014 Guenter Roeck <linux@roeck-us.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRVNAME "nct7802"
+
+static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e };
+
+static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = {
+ { 0x46, 0x00, 0x40, 0x42, 0x44 },
+ { 0x45, 0x00, 0x3f, 0x41, 0x43 },
+};
+
+static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 };
+
+static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = {
+ { 0, 0, 4, 0, 4 },
+ { 2, 0, 6, 2, 6 },
+};
+
+#define REG_BANK 0x00
+#define REG_TEMP_LSB 0x05
+#define REG_TEMP_PECI_LSB 0x08
+#define REG_VOLTAGE_LOW 0x0f
+#define REG_FANCOUNT_LOW 0x13
+#define REG_START 0x21
+#define REG_MODE 0x22 /* 7.2.32 Mode Selection Register */
+#define REG_PECI_ENABLE 0x23
+#define REG_FAN_ENABLE 0x24
+#define REG_VMON_ENABLE 0x25
+#define REG_PWM(x) (0x60 + (x))
+#define REG_SMARTFAN_EN(x) (0x64 + (x) / 2)
+#define SMARTFAN_EN_SHIFT(x) ((x) % 2 * 4)
+#define REG_VENDOR_ID 0xfd
+#define REG_CHIP_ID 0xfe
+#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
+ */
+
+struct nct7802_data {
+ struct regmap *regmap;
+ struct mutex access_lock; /* for multi-byte read and write operations */
+ u8 in_status;
+ struct mutex in_alarm_lock;
+};
+
+static ssize_t temp_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ unsigned int mode;
+ int ret;
+
+ ret = regmap_read(data->regmap, REG_MODE, &mode);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", (mode >> (2 * sattr->index) & 3) + 2);
+}
+
+static ssize_t temp_type_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ unsigned int type;
+ int err;
+
+ err = kstrtouint(buf, 0, &type);
+ if (err < 0)
+ return err;
+ if (sattr->index == 2 && type != 4) /* RD3 */
+ return -EINVAL;
+ if (type < 3 || type > 4)
+ return -EINVAL;
+ err = regmap_update_bits(data->regmap, REG_MODE,
+ 3 << 2 * sattr->index, (type - 2) << 2 * sattr->index);
+ return err ? : count;
+}
+
+static ssize_t pwm_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int ret;
+
+ if (sattr->index > 1)
+ return sprintf(buf, "1\n");
+
+ ret = regmap_read(data->regmap, 0x5E, &regval);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", !(regval & (1 << sattr->index)));
+}
+
+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 nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (!attr->index)
+ return sprintf(buf, "255\n");
+
+ ret = regmap_read(data->regmap, attr->index, &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+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 nct7802_data *data = dev_get_drvdata(dev);
+ int err;
+ u8 val;
+
+ err = kstrtou8(buf, 0, &val);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(data->regmap, attr->index, val);
+ return err ? : count;
+}
+
+static ssize_t pwm_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ unsigned int reg, enabled;
+ int ret;
+
+ ret = regmap_read(data->regmap, REG_SMARTFAN_EN(sattr->index), &reg);
+ if (ret < 0)
+ return ret;
+ enabled = reg >> SMARTFAN_EN_SHIFT(sattr->index) & 1;
+ return sprintf(buf, "%u\n", enabled + 1);
+}
+
+static ssize_t pwm_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ u8 val;
+ int ret;
+
+ ret = kstrtou8(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ if (val < 1 || val > 2)
+ return -EINVAL;
+ ret = regmap_update_bits(data->regmap, REG_SMARTFAN_EN(sattr->index),
+ 1 << SMARTFAN_EN_SHIFT(sattr->index),
+ (val - 1) << SMARTFAN_EN_SHIFT(sattr->index));
+ return ret ? : count;
+}
+
+static int nct7802_read_temp(struct nct7802_data *data,
+ u8 reg_temp, u8 reg_temp_low, int *temp)
+{
+ unsigned int t1, t2 = 0;
+ int err;
+
+ *temp = 0;
+
+ mutex_lock(&data->access_lock);
+ err = regmap_read(data->regmap, reg_temp, &t1);
+ if (err < 0)
+ goto abort;
+ t1 <<= 8;
+ if (reg_temp_low) { /* 11 bit data */
+ err = regmap_read(data->regmap, reg_temp_low, &t2);
+ if (err < 0)
+ goto abort;
+ }
+ t1 |= t2 & 0xe0;
+ *temp = (s16)t1 / 32 * 125;
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan)
+{
+ unsigned int f1, f2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ ret = regmap_read(data->regmap, reg_fan, &f1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2);
+ if (ret < 0)
+ goto abort;
+ ret = (f1 << 5) | (f2 >> 3);
+ /* convert fan count to rpm */
+ if (ret == 0x1fff) /* maximum value, assume fan is stopped */
+ ret = 0;
+ else if (ret)
+ ret = DIV_ROUND_CLOSEST(1350000U, ret);
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
+ u8 reg_fan_high)
+{
+ unsigned int f1, f2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ ret = regmap_read(data->regmap, reg_fan_low, &f1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, reg_fan_high, &f2);
+ if (ret < 0)
+ goto abort;
+ ret = f1 | ((f2 & 0xf8) << 5);
+ /* convert fan count to rpm */
+ if (ret == 0x1fff) /* maximum value, assume no limit */
+ ret = 0;
+ else if (ret)
+ ret = DIV_ROUND_CLOSEST(1350000U, ret);
+ else
+ ret = 1350000U;
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
+ u8 reg_fan_high, unsigned long limit)
+{
+ int err;
+
+ if (limit)
+ limit = DIV_ROUND_CLOSEST(1350000U, limit);
+ else
+ limit = 0x1fff;
+ limit = clamp_val(limit, 0, 0x1fff);
+
+ mutex_lock(&data->access_lock);
+ err = regmap_write(data->regmap, reg_fan_low, limit & 0xff);
+ if (err < 0)
+ goto abort;
+
+ err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5);
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 };
+
+static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index)
+{
+ unsigned int v1, v2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ if (index == 0) { /* voltage */
+ ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2);
+ if (ret < 0)
+ goto abort;
+ ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr];
+ } else { /* limit */
+ int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
+
+ ret = regmap_read(data->regmap,
+ REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
+ &v2);
+ if (ret < 0)
+ goto abort;
+ ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr];
+ }
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index,
+ unsigned long voltage)
+{
+ int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
+ int err;
+
+ voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]);
+ voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
+
+ mutex_lock(&data->access_lock);
+ err = regmap_write(data->regmap,
+ REG_VOLTAGE_LIMIT_LSB[index - 1][nr],
+ voltage & 0xff);
+ if (err < 0)
+ goto abort;
+
+ err = regmap_update_bits(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
+ 0x0300 >> shift, (voltage & 0x0300) >> shift);
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static ssize_t in_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int voltage;
+
+ voltage = nct7802_read_voltage(data, sattr->nr, sattr->index);
+ if (voltage < 0)
+ return voltage;
+
+ return sprintf(buf, "%d\n", voltage);
+}
+
+static ssize_t in_store(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 nct7802_data *data = dev_get_drvdata(dev);
+ int index = sattr->index;
+ int nr = sattr->nr;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ err = nct7802_write_voltage(data, nr, index, val);
+ return err ? : count;
+}
+
+static ssize_t in_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int volt, min, max, ret;
+ unsigned int val;
+
+ mutex_lock(&data->in_alarm_lock);
+
+ /*
+ * The SMI Voltage status register is the only register giving a status
+ * for voltages. A bit is set for each input crossing a threshold, in
+ * both direction, but the "inside" or "outside" limits info is not
+ * available. Also this register is cleared on read.
+ * Note: this is not explicitly spelled out in the datasheet, but
+ * from experiment.
+ * To deal with this we use a status cache with one validity bit and
+ * one status bit for each input. Validity is cleared at startup and
+ * each time the register reports a change, and the status is processed
+ * by software based on current input value and limits.
+ */
+ ret = regmap_read(data->regmap, 0x1e, &val); /* SMI Voltage status */
+ if (ret < 0)
+ goto abort;
+
+ /* invalidate cached status for all inputs crossing a threshold */
+ data->in_status &= ~((val & 0x0f) << 4);
+
+ /* if cached status for requested input is invalid, update it */
+ if (!(data->in_status & (0x10 << sattr->index))) {
+ ret = nct7802_read_voltage(data, sattr->nr, 0);
+ if (ret < 0)
+ goto abort;
+ volt = ret;
+
+ ret = nct7802_read_voltage(data, sattr->nr, 1);
+ if (ret < 0)
+ goto abort;
+ min = ret;
+
+ ret = nct7802_read_voltage(data, sattr->nr, 2);
+ if (ret < 0)
+ goto abort;
+ max = ret;
+
+ if (volt < min || volt > max)
+ data->in_status |= (1 << sattr->index);
+ else
+ data->in_status &= ~(1 << sattr->index);
+
+ data->in_status |= 0x10 << sattr->index;
+ }
+
+ ret = sprintf(buf, "%u\n", !!(data->in_status & (1 << sattr->index)));
+abort:
+ mutex_unlock(&data->in_alarm_lock);
+ return ret;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int err, temp;
+
+ err = nct7802_read_temp(data, sattr->nr, sattr->index, &temp);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t temp_store(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 nct7802_data *data = dev_get_drvdata(dev);
+ int nr = sattr->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
+
+ err = regmap_write(data->regmap, nr, val & 0xff);
+ return err ? : count;
+}
+
+static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int speed;
+
+ speed = nct7802_read_fan(data, sattr->index);
+ if (speed < 0)
+ return speed;
+
+ return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int speed;
+
+ speed = nct7802_read_fan_min(data, sattr->nr, sattr->index);
+ if (speed < 0)
+ return speed;
+
+ return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t fan_min_store(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 nct7802_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ err = nct7802_write_fan_min(data, sattr->nr, sattr->index, val);
+ return err ? : count;
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int bit = sattr->index;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(data->regmap, sattr->nr, &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", !!(val & (1 << bit)));
+}
+
+static ssize_t
+beep_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err;
+
+ err = regmap_read(data->regmap, sattr->nr, &regval);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%u\n", !!(regval & (1 << sattr->index)));
+}
+
+static ssize_t
+beep_store(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 nct7802_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ err = regmap_update_bits(data->regmap, sattr->nr, 1 << sattr->index,
+ val ? 1 << sattr->index : 0);
+ return err ? : count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_type, temp_type, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0x01, REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, 0x31, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, 0x30, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp1_crit, temp, 0x3a, 0);
+
+static SENSOR_DEVICE_ATTR_RW(temp2_type, temp_type, 1);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, 0x02, REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_min, temp, 0x33, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, 0x32, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_crit, temp, 0x3b, 0);
+
+static SENSOR_DEVICE_ATTR_RW(temp3_type, temp_type, 2);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, 0x03, REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_min, temp, 0x35, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, 0x34, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_crit, temp, 0x3c, 0);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp4_input, temp, 0x04, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_min, temp, 0x37, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_max, temp, 0x36, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_crit, temp, 0x3d, 0);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp5_input, temp, 0x06, REG_TEMP_PECI_LSB);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_min, temp, 0x39, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_max, temp, 0x38, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_crit, temp, 0x3e, 0);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp6_input, temp, 0x07, REG_TEMP_PECI_LSB);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp1_min_alarm, alarm, 0x18, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_min_alarm, alarm, 0x18, 1);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_min_alarm, alarm, 0x18, 2);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_min_alarm, alarm, 0x18, 3);
+static SENSOR_DEVICE_ATTR_2_RO(temp5_min_alarm, alarm, 0x18, 4);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp1_max_alarm, alarm, 0x19, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_max_alarm, alarm, 0x19, 1);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_max_alarm, alarm, 0x19, 2);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_max_alarm, alarm, 0x19, 3);
+static SENSOR_DEVICE_ATTR_2_RO(temp5_max_alarm, alarm, 0x19, 4);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp1_crit_alarm, alarm, 0x1b, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_crit_alarm, alarm, 0x1b, 1);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_crit_alarm, alarm, 0x1b, 2);
+static SENSOR_DEVICE_ATTR_2_RO(temp4_crit_alarm, alarm, 0x1b, 3);
+static SENSOR_DEVICE_ATTR_2_RO(temp5_crit_alarm, alarm, 0x1b, 4);
+
+static SENSOR_DEVICE_ATTR_2_RO(temp1_fault, alarm, 0x17, 0);
+static SENSOR_DEVICE_ATTR_2_RO(temp2_fault, alarm, 0x17, 1);
+static SENSOR_DEVICE_ATTR_2_RO(temp3_fault, alarm, 0x17, 2);
+
+static SENSOR_DEVICE_ATTR_2_RW(temp1_beep, beep, 0x5c, 0);
+static SENSOR_DEVICE_ATTR_2_RW(temp2_beep, beep, 0x5c, 1);
+static SENSOR_DEVICE_ATTR_2_RW(temp3_beep, beep, 0x5c, 2);
+static SENSOR_DEVICE_ATTR_2_RW(temp4_beep, beep, 0x5c, 3);
+static SENSOR_DEVICE_ATTR_2_RW(temp5_beep, beep, 0x5c, 4);
+static SENSOR_DEVICE_ATTR_2_RW(temp6_beep, beep, 0x5c, 5);
+
+static struct attribute *nct7802_temp_attrs[] = {
+ &sensor_dev_attr_temp1_type.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_crit.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,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_type.dev_attr.attr, /* 10 */
+ &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_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_type.dev_attr.attr, /* 20 */
+ &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_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_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_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_input.dev_attr.attr, /* 30 */
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp5_input.dev_attr.attr, /* 38 */
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp6_input.dev_attr.attr, /* 46 */
+ &sensor_dev_attr_temp6_beep.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t nct7802_temp_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int reg;
+ int err;
+
+ err = regmap_read(data->regmap, REG_MODE, &reg);
+ if (err < 0)
+ return 0;
+
+ if (index < 10 &&
+ (reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */
+ return 0;
+
+ if (index >= 10 && index < 20 &&
+ (reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */
+ return 0;
+ if (index >= 20 && index < 30 && (reg & 0x30) != 0x20) /* RD3 */
+ return 0;
+
+ if (index >= 30 && index < 38) /* local */
+ return attr->mode;
+
+ err = regmap_read(data->regmap, REG_PECI_ENABLE, &reg);
+ if (err < 0)
+ return 0;
+
+ if (index >= 38 && index < 46 && !(reg & 0x01)) /* PECI 0 */
+ return 0;
+
+ if (index >= 46 && !(reg & 0x02)) /* PECI 1 */
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group nct7802_temp_group = {
+ .attrs = nct7802_temp_attrs,
+ .is_visible = nct7802_temp_is_visible,
+};
+
+static SENSOR_DEVICE_ATTR_2_RO(in0_input, in, 0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in0_min, in, 0, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in0_max, in, 0, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in0_alarm, in_alarm, 0, 3);
+static SENSOR_DEVICE_ATTR_2_RW(in0_beep, beep, 0x5a, 3);
+
+static SENSOR_DEVICE_ATTR_2_RO(in1_input, in, 1, 0);
+
+static SENSOR_DEVICE_ATTR_2_RO(in2_input, in, 2, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in2_min, in, 2, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in2_max, in, 2, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in2_alarm, in_alarm, 2, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in2_beep, beep, 0x5a, 0);
+
+static SENSOR_DEVICE_ATTR_2_RO(in3_input, in, 3, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in3_min, in, 3, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in3_max, in, 3, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in3_alarm, in_alarm, 3, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in3_beep, beep, 0x5a, 1);
+
+static SENSOR_DEVICE_ATTR_2_RO(in4_input, in, 4, 0);
+static SENSOR_DEVICE_ATTR_2_RW(in4_min, in, 4, 1);
+static SENSOR_DEVICE_ATTR_2_RW(in4_max, in, 4, 2);
+static SENSOR_DEVICE_ATTR_2_RO(in4_alarm, in_alarm, 4, 2);
+static SENSOR_DEVICE_ATTR_2_RW(in4_beep, beep, 0x5a, 2);
+
+static struct attribute *nct7802_in_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_in0_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in1_input.dev_attr.attr, /* 5 */
+
+ &sensor_dev_attr_in2_input.dev_attr.attr, /* 6 */
+ &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_in2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in3_input.dev_attr.attr, /* 11 */
+ &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_in3_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in4_input.dev_attr.attr, /* 16 */
+ &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_in4_beep.dev_attr.attr,
+
+ NULL,
+};
+
+static umode_t nct7802_in_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int reg;
+ int err;
+
+ if (index < 6) /* VCC, VCORE */
+ return attr->mode;
+
+ err = regmap_read(data->regmap, REG_MODE, &reg);
+ if (err < 0)
+ return 0;
+
+ if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */
+ return 0;
+ if (index >= 11 && index < 16 && (reg & 0x0c) != 0x0c) /* VSEN2 */
+ return 0;
+ if (index >= 16 && (reg & 0x30) != 0x30) /* VSEN3 */
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group nct7802_in_group = {
+ .attrs = nct7802_in_attrs,
+ .is_visible = nct7802_in_is_visible,
+};
+
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0x10);
+static SENSOR_DEVICE_ATTR_2_RW(fan1_min, fan_min, 0x49, 0x4c);
+static SENSOR_DEVICE_ATTR_2_RO(fan1_alarm, alarm, 0x1a, 0);
+static SENSOR_DEVICE_ATTR_2_RW(fan1_beep, beep, 0x5b, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 0x11);
+static SENSOR_DEVICE_ATTR_2_RW(fan2_min, fan_min, 0x4a, 0x4d);
+static SENSOR_DEVICE_ATTR_2_RO(fan2_alarm, alarm, 0x1a, 1);
+static SENSOR_DEVICE_ATTR_2_RW(fan2_beep, beep, 0x5b, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 0x12);
+static SENSOR_DEVICE_ATTR_2_RW(fan3_min, fan_min, 0x4b, 0x4e);
+static SENSOR_DEVICE_ATTR_2_RO(fan3_alarm, alarm, 0x1a, 2);
+static SENSOR_DEVICE_ATTR_2_RW(fan3_beep, beep, 0x5b, 2);
+
+/* 7.2.89 Fan Control Output Type */
+static SENSOR_DEVICE_ATTR_RO(pwm1_mode, pwm_mode, 0);
+static SENSOR_DEVICE_ATTR_RO(pwm2_mode, pwm_mode, 1);
+static SENSOR_DEVICE_ATTR_RO(pwm3_mode, pwm_mode, 2);
+
+/* 7.2.91... Fan Control Output Value */
+static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, REG_PWM(0));
+static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, REG_PWM(1));
+static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, REG_PWM(2));
+
+/* 7.2.95... Temperature to Fan mapping Relationships Register */
+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 *nct7802_fan_attrs[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_beep.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_beep.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_beep.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t nct7802_fan_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ 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;
+ int err;
+
+ err = regmap_read(data->regmap, REG_FAN_ENABLE, &reg);
+ if (err < 0 || !(reg & (1 << fan)))
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group nct7802_fan_group = {
+ .attrs = nct7802_fan_attrs,
+ .is_visible = nct7802_fan_is_visible,
+};
+
+static struct attribute *nct7802_pwm_attrs[] = {
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group nct7802_pwm_group = {
+ .attrs = nct7802_pwm_attrs,
+};
+
+/* 7.2.115... 0x80-0x83, 0x84 Temperature (X-axis) transition */
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_temp, temp, 0x80, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_temp, temp, 0x81, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_temp, temp, 0x82, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_temp, temp, 0x83, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_temp, temp, 0x84, 0);
+
+/* 7.2.120... 0x85-0x88 PWM (Y-axis) transition */
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm, 0x85);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm, 0x86);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_pwm, pwm, 0x87);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_pwm, pwm, 0x88);
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point5_pwm, pwm, 0);
+
+/* 7.2.124 Table 2 X-axis Transition Point 1 Register */
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_temp, temp, 0x90, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_temp, temp, 0x91, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_temp, temp, 0x92, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_temp, temp, 0x93, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_temp, temp, 0x94, 0);
+
+/* 7.2.129 Table 2 Y-axis Transition Point 1 Register */
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_pwm, pwm, 0x95);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point2_pwm, pwm, 0x96);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point3_pwm, pwm, 0x97);
+static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point4_pwm, pwm, 0x98);
+static SENSOR_DEVICE_ATTR_RO(pwm2_auto_point5_pwm, pwm, 0);
+
+/* 7.2.133 Table 3 X-axis Transition Point 1 Register */
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point1_temp, temp, 0xA0, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point2_temp, temp, 0xA1, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point3_temp, temp, 0xA2, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point4_temp, temp, 0xA3, 0);
+static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point5_temp, temp, 0xA4, 0);
+
+/* 7.2.138 Table 3 Y-axis Transition Point 1 Register */
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_pwm, pwm, 0xA5);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point2_pwm, pwm, 0xA6);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point3_pwm, pwm, 0xA7);
+static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point4_pwm, pwm, 0xA8);
+static SENSOR_DEVICE_ATTR_RO(pwm3_auto_point5_pwm, pwm, 0);
+
+static struct attribute *nct7802_auto_point_attrs[] = {
+ &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr,
+
+ &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr,
+
+ &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point5_temp.dev_attr.attr,
+
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point5_pwm.dev_attr.attr,
+
+ NULL
+};
+
+static const struct attribute_group nct7802_auto_point_group = {
+ .attrs = nct7802_auto_point_attrs,
+};
+
+static const struct attribute_group *nct7802_groups[] = {
+ &nct7802_temp_group,
+ &nct7802_in_group,
+ &nct7802_fan_group,
+ &nct7802_pwm_group,
+ &nct7802_auto_point_group,
+ NULL
+};
+
+static int nct7802_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int reg;
+
+ /*
+ * Chip identification registers are only available in bank 0,
+ * so only attempt chip detection if bank 0 is selected
+ */
+ reg = i2c_smbus_read_byte_data(client, REG_BANK);
+ if (reg != 0x00)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID);
+ if (reg != 0x50)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID);
+ if (reg != 0xc3)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VERSION_ID);
+ if (reg < 0 || (reg & 0xf0) != 0x20)
+ return -ENODEV;
+
+ /* Also validate lower bits of voltage and temperature registers */
+ reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB);
+ if (reg < 0 || (reg & 0x1f))
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_TEMP_PECI_LSB);
+ if (reg < 0 || (reg & 0x3f))
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VOLTAGE_LOW);
+ if (reg < 0 || (reg & 0x3f))
+ return -ENODEV;
+
+ strscpy(info->type, "nct7802", I2C_NAME_SIZE);
+ return 0;
+}
+
+static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ return (reg != REG_BANK && reg <= 0x20) ||
+ (reg >= REG_PWM(0) && reg <= REG_PWM(2));
+}
+
+static const struct regmap_config nct7802_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = nct7802_regmap_is_volatile,
+};
+
+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", &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;
+
+ /* Enable ADC */
+ err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01);
+ if (err)
+ return err;
+
+ err = nct7802_configure_channels(dev, data);
+ if (err)
+ return err;
+
+ /* Enable Vcore and VCC voltage monitoring */
+ return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03);
+}
+
+static int nct7802_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct nct7802_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &nct7802_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ mutex_init(&data->access_lock);
+ mutex_init(&data->in_alarm_lock);
+
+ ret = nct7802_init_chip(dev, data);
+ if (ret < 0)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ nct7802_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const unsigned short nct7802_address_list[] = {
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END
+};
+
+static const struct i2c_device_id nct7802_idtable[] = {
+ { "nct7802", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nct7802_idtable);
+
+static struct i2c_driver nct7802_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRVNAME,
+ },
+ .detect = nct7802_detect,
+ .probe_new = nct7802_probe,
+ .id_table = nct7802_idtable,
+ .address_list = nct7802_address_list,
+};
+
+module_i2c_driver(nct7802_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("NCT7802Y Hardware Monitoring Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c
new file mode 100644
index 000000000..ecc5db001
--- /dev/null
+++ b/drivers/hwmon/nct7904.c
@@ -0,0 +1,1184 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * nct7904.c - driver for Nuvoton NCT7904D.
+ *
+ * Copyright (c) 2015 Kontron
+ * Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>
+ *
+ * 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>
+#include <linux/device.h>
+#include <linux/init.h>
+#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
+#define CHIP_ID_REG 0x7B /* Any bank */
+#define NCT7904_ID 0xC5
+#define DEVICE_ID_REG 0x7C /* Any bank */
+
+#define BANK_SEL_REG 0xFF
+#define BANK_0 0x00
+#define BANK_1 0x01
+#define BANK_2 0x02
+#define BANK_3 0x03
+#define BANK_4 0x04
+#define BANK_MAX 0x04
+
+#define FANIN_MAX 12 /* Counted from 1 */
+#define VSEN_MAX 21 /* VSEN1..14, 3VDD, VBAT, V3VSB,
+ LTD (not a voltage), VSEN17..19 */
+#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 */
+#define VT_ADC_CTRL2_REG 0x22 /* Bank 0 */
+#define FANIN_CTRL0_REG 0x24
+#define FANIN_CTRL1_REG 0x25
+#define DTS_T_CTRL0_REG 0x26
+#define DTS_T_CTRL1_REG 0x27
+#define VT_ADC_MD_REG 0x2E
+
+#define VSEN1_HV_LL_REG 0x02 /* Bank 1; 2 regs (HV/LV) per sensor */
+#define VSEN1_LV_LL_REG 0x03 /* Bank 1; 2 regs (HV/LV) per sensor */
+#define VSEN1_HV_HL_REG 0x00 /* Bank 1; 2 regs (HV/LV) per sensor */
+#define VSEN1_LV_HL_REG 0x01 /* Bank 1; 2 regs (HV/LV) per sensor */
+#define SMI_STS1_REG 0xC1 /* Bank 0; SMI Status Register */
+#define SMI_STS3_REG 0xC3 /* Bank 0; SMI Status Register */
+#define SMI_STS5_REG 0xC5 /* Bank 0; SMI Status Register */
+#define SMI_STS7_REG 0xC7 /* Bank 0; SMI Status Register */
+#define SMI_STS8_REG 0xC8 /* Bank 0; SMI Status Register */
+
+#define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */
+#define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */
+#define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */
+#define LTD_HV_HL_REG 0x44 /* Bank 1; 1 reg for LTD */
+#define LTD_LV_HL_REG 0x45 /* Bank 1; 1 reg for LTD */
+#define LTD_HV_LL_REG 0x46 /* Bank 1; 1 reg for LTD */
+#define LTD_LV_LL_REG 0x47 /* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_CH_REG 0x05 /* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_W_REG 0x06 /* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_WH_REG 0x07 /* Bank 1; 1 reg for LTD */
+#define TEMP_CH1_C_REG 0x04 /* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_C_REG 0x90 /* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_CH_REG 0x91 /* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_W_REG 0x92 /* Bank 1; 1 reg per sensor */
+#define DTS_T_CPU1_WH_REG 0x93 /* Bank 1; 1 reg per sensor */
+#define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */
+#define FANIN1_HV_HL_REG 0x60 /* Bank 1; 2 regs (HV/LV) per sensor */
+#define FANIN1_LV_HL_REG 0x61 /* Bank 1; 2 regs (HV/LV) per sensor */
+#define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */
+
+#define PRTS_REG 0x03 /* Bank 2 */
+#define PFE_REG 0x00 /* Bank 2; PECI Function Enable */
+#define TSI_CTRL_REG 0x50 /* Bank 2; TSI Control Register */
+#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;
+ u32 vsen_mask;
+ u32 tcpu_mask;
+ u8 fan_mode[FANCTL_MAX];
+ u8 enable_dts;
+ u8 has_dts;
+ u8 temp_mode; /* 0: TR mode, 1: TD mode */
+ u8 fan_alarm[2];
+ u8 vsen_alarm[3];
+};
+
+/* Access functions */
+static int nct7904_bank_lock(struct nct7904_data *data, unsigned int bank)
+{
+ int ret;
+
+ mutex_lock(&data->bank_lock);
+ if (data->bank_sel == bank)
+ return 0;
+ ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank);
+ if (ret == 0)
+ data->bank_sel = bank;
+ else
+ data->bank_sel = -1;
+ return ret;
+}
+
+static inline void nct7904_bank_release(struct nct7904_data *data)
+{
+ mutex_unlock(&data->bank_lock);
+}
+
+/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */
+static int nct7904_read_reg(struct nct7904_data *data,
+ unsigned int bank, unsigned int reg)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = nct7904_bank_lock(data, bank);
+ if (ret == 0)
+ ret = i2c_smbus_read_byte_data(client, reg);
+
+ nct7904_bank_release(data);
+ return ret;
+}
+
+/*
+ * Read 2-byte register. Returns register in big-endian format or
+ * -ERRNO on error.
+ */
+static int nct7904_read_reg16(struct nct7904_data *data,
+ unsigned int bank, unsigned int reg)
+{
+ struct i2c_client *client = data->client;
+ int ret, hi;
+
+ ret = nct7904_bank_lock(data, bank);
+ if (ret == 0) {
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret >= 0) {
+ hi = ret;
+ ret = i2c_smbus_read_byte_data(client, reg + 1);
+ if (ret >= 0)
+ ret |= hi << 8;
+ }
+ }
+
+ nct7904_bank_release(data);
+ return ret;
+}
+
+/* Write 1-byte register. Returns 0 or -ERRNO on error. */
+static int nct7904_write_reg(struct nct7904_data *data,
+ unsigned int bank, unsigned int reg, u8 val)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = nct7904_bank_lock(data, bank);
+ if (ret == 0)
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ nct7904_bank_release(data);
+ return ret;
+}
+
+static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ unsigned int cnt, rpm;
+ int ret;
+
+ switch (attr) {
+ case hwmon_fan_input:
+ ret = nct7904_read_reg16(data, BANK_0,
+ FANIN1_HV_REG + channel * 2);
+ if (ret < 0)
+ return ret;
+ cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
+ if (cnt == 0 || cnt == 0x1fff)
+ rpm = 0;
+ else
+ rpm = 1350000 / cnt;
+ *val = rpm;
+ return 0;
+ case hwmon_fan_min:
+ ret = nct7904_read_reg16(data, BANK_1,
+ FANIN1_HV_HL_REG + channel * 2);
+ if (ret < 0)
+ return ret;
+ cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
+ if (cnt == 0 || cnt == 0x1fff)
+ rpm = 0;
+ else
+ rpm = 1350000 / cnt;
+ *val = rpm;
+ return 0;
+ case hwmon_fan_alarm:
+ ret = nct7904_read_reg(data, BANK_0,
+ SMI_STS5_REG + (channel >> 3));
+ if (ret < 0)
+ return ret;
+ if (!data->fan_alarm[channel >> 3])
+ data->fan_alarm[channel >> 3] = ret & 0xff;
+ else
+ /* If there is new alarm showing up */
+ data->fan_alarm[channel >> 3] |= (ret & 0xff);
+ *val = (data->fan_alarm[channel >> 3] >> (channel & 0x07)) & 1;
+ /* Needs to clean the alarm if alarm existing */
+ if (*val)
+ data->fan_alarm[channel >> 3] ^= 1 << (channel & 0x07);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct nct7904_data *data = _data;
+
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_alarm:
+ if (data->fanin_mask & (1 << channel))
+ return 0444;
+ break;
+ case hwmon_fan_min:
+ if (data->fanin_mask & (1 << channel))
+ return 0644;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static u8 nct7904_chan_to_index[] = {
+ 0, /* Not used */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 18, 19, 20, 16
+};
+
+static int nct7904_read_in(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret, volt, index;
+
+ index = nct7904_chan_to_index[channel];
+
+ switch (attr) {
+ case hwmon_in_input:
+ ret = nct7904_read_reg16(data, BANK_0,
+ VSEN1_HV_REG + index * 2);
+ if (ret < 0)
+ return ret;
+ volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ if (index < 14)
+ volt *= 2; /* 0.002V scale */
+ else
+ volt *= 6; /* 0.006V scale */
+ *val = volt;
+ return 0;
+ case hwmon_in_min:
+ ret = nct7904_read_reg16(data, BANK_1,
+ VSEN1_HV_LL_REG + index * 4);
+ if (ret < 0)
+ return ret;
+ volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ if (index < 14)
+ volt *= 2; /* 0.002V scale */
+ else
+ volt *= 6; /* 0.006V scale */
+ *val = volt;
+ return 0;
+ case hwmon_in_max:
+ ret = nct7904_read_reg16(data, BANK_1,
+ VSEN1_HV_HL_REG + index * 4);
+ if (ret < 0)
+ return ret;
+ volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ if (index < 14)
+ volt *= 2; /* 0.002V scale */
+ else
+ volt *= 6; /* 0.006V scale */
+ *val = volt;
+ return 0;
+ case hwmon_in_alarm:
+ ret = nct7904_read_reg(data, BANK_0,
+ SMI_STS1_REG + (index >> 3));
+ if (ret < 0)
+ return ret;
+ if (!data->vsen_alarm[index >> 3])
+ data->vsen_alarm[index >> 3] = ret & 0xff;
+ else
+ /* If there is new alarm showing up */
+ data->vsen_alarm[index >> 3] |= (ret & 0xff);
+ *val = (data->vsen_alarm[index >> 3] >> (index & 0x07)) & 1;
+ /* Needs to clean the alarm if alarm existing */
+ if (*val)
+ data->vsen_alarm[index >> 3] ^= 1 << (index & 0x07);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct nct7904_data *data = _data;
+ int index = nct7904_chan_to_index[channel];
+
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_alarm:
+ if (channel > 0 && (data->vsen_mask & BIT(index)))
+ return 0444;
+ break;
+ case hwmon_in_min:
+ case hwmon_in_max:
+ if (channel > 0 && (data->vsen_mask & BIT(index)))
+ return 0644;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret, temp;
+ unsigned int reg1, reg2, reg3;
+ s8 temps;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ if (channel == 4)
+ ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
+ else if (channel < 5)
+ ret = nct7904_read_reg16(data, BANK_0,
+ TEMP_CH1_HV_REG + channel * 4);
+ else
+ ret = nct7904_read_reg16(data, BANK_0,
+ T_CPU1_HV_REG + (channel - 5)
+ * 2);
+ if (ret < 0)
+ return ret;
+ temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ *val = sign_extend32(temp, 10) * 125;
+ return 0;
+ case hwmon_temp_alarm:
+ if (channel == 4) {
+ ret = nct7904_read_reg(data, BANK_0,
+ SMI_STS3_REG);
+ if (ret < 0)
+ return ret;
+ *val = (ret >> 1) & 1;
+ } else if (channel < 4) {
+ ret = nct7904_read_reg(data, BANK_0,
+ SMI_STS1_REG);
+ if (ret < 0)
+ return ret;
+ *val = (ret >> (((channel * 2) + 1) & 0x07)) & 1;
+ } else {
+ if ((channel - 5) < 4) {
+ ret = nct7904_read_reg(data, BANK_0,
+ SMI_STS7_REG +
+ ((channel - 5) >> 3));
+ if (ret < 0)
+ return ret;
+ *val = (ret >> ((channel - 5) & 0x07)) & 1;
+ } else {
+ ret = nct7904_read_reg(data, BANK_0,
+ SMI_STS8_REG +
+ ((channel - 5) >> 3));
+ if (ret < 0)
+ return ret;
+ *val = (ret >> (((channel - 5) & 0x07) - 4))
+ & 1;
+ }
+ }
+ return 0;
+ case hwmon_temp_type:
+ if (channel < 5) {
+ if ((data->tcpu_mask >> channel) & 0x01) {
+ if ((data->temp_mode >> channel) & 0x01)
+ *val = 3; /* TD */
+ else
+ *val = 4; /* TR */
+ } else {
+ *val = 0;
+ }
+ } else {
+ if ((data->has_dts >> (channel - 5)) & 0x01) {
+ if (data->enable_dts & ENABLE_TSI)
+ *val = 5; /* TSI */
+ else
+ *val = 6; /* PECI */
+ } else {
+ *val = 0;
+ }
+ }
+ return 0;
+ case hwmon_temp_max:
+ reg1 = LTD_HV_LL_REG;
+ reg2 = TEMP_CH1_W_REG;
+ reg3 = DTS_T_CPU1_W_REG;
+ break;
+ case hwmon_temp_max_hyst:
+ reg1 = LTD_LV_LL_REG;
+ reg2 = TEMP_CH1_WH_REG;
+ reg3 = DTS_T_CPU1_WH_REG;
+ break;
+ case hwmon_temp_crit:
+ reg1 = LTD_HV_HL_REG;
+ reg2 = TEMP_CH1_C_REG;
+ reg3 = DTS_T_CPU1_C_REG;
+ break;
+ case hwmon_temp_crit_hyst:
+ reg1 = LTD_LV_HL_REG;
+ reg2 = TEMP_CH1_CH_REG;
+ reg3 = DTS_T_CPU1_CH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (channel == 4)
+ ret = nct7904_read_reg(data, BANK_1, reg1);
+ else if (channel < 5)
+ ret = nct7904_read_reg(data, BANK_1,
+ reg2 + channel * 8);
+ else
+ ret = nct7904_read_reg(data, BANK_1,
+ reg3 + (channel - 5) * 4);
+
+ if (ret < 0)
+ return ret;
+ temps = ret;
+ *val = temps * 1000;
+ return 0;
+}
+
+static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct nct7904_data *data = _data;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_alarm:
+ case hwmon_temp_type:
+ if (channel < 5) {
+ if (data->tcpu_mask & BIT(channel))
+ return 0444;
+ } else {
+ if (data->has_dts & BIT(channel - 5))
+ return 0444;
+ }
+ break;
+ case hwmon_temp_max:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_crit:
+ case hwmon_temp_crit_hyst:
+ if (channel < 5) {
+ if (data->tcpu_mask & BIT(channel))
+ return 0644;
+ } else {
+ if (data->has_dts & BIT(channel - 5))
+ return 0644;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return 0;
+ case hwmon_pwm_enable:
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + channel);
+ if (ret < 0)
+ return ret;
+
+ *val = ret ? 2 : 1;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int nct7904_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+ unsigned int reg1, reg2, reg3;
+
+ val = clamp_val(val / 1000, -128, 127);
+
+ switch (attr) {
+ case hwmon_temp_max:
+ reg1 = LTD_HV_LL_REG;
+ reg2 = TEMP_CH1_W_REG;
+ reg3 = DTS_T_CPU1_W_REG;
+ break;
+ case hwmon_temp_max_hyst:
+ reg1 = LTD_LV_LL_REG;
+ reg2 = TEMP_CH1_WH_REG;
+ reg3 = DTS_T_CPU1_WH_REG;
+ break;
+ case hwmon_temp_crit:
+ reg1 = LTD_HV_HL_REG;
+ reg2 = TEMP_CH1_C_REG;
+ reg3 = DTS_T_CPU1_C_REG;
+ break;
+ case hwmon_temp_crit_hyst:
+ reg1 = LTD_LV_HL_REG;
+ reg2 = TEMP_CH1_CH_REG;
+ reg3 = DTS_T_CPU1_CH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ if (channel == 4)
+ ret = nct7904_write_reg(data, BANK_1, reg1, val);
+ else if (channel < 5)
+ ret = nct7904_write_reg(data, BANK_1,
+ reg2 + channel * 8, val);
+ else
+ ret = nct7904_write_reg(data, BANK_1,
+ reg3 + (channel - 5) * 4, val);
+
+ return ret;
+}
+
+static int nct7904_write_fan(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+ u8 tmp;
+
+ switch (attr) {
+ case hwmon_fan_min:
+ if (val <= 0)
+ return -EINVAL;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(1350000, val), 1, 0x1fff);
+ tmp = (val >> 5) & 0xff;
+ ret = nct7904_write_reg(data, BANK_1,
+ FANIN1_HV_HL_REG + channel * 2, tmp);
+ if (ret < 0)
+ return ret;
+ tmp = val & 0x1f;
+ ret = nct7904_write_reg(data, BANK_1,
+ FANIN1_LV_HL_REG + channel * 2, tmp);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int nct7904_write_in(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret, index, tmp;
+
+ index = nct7904_chan_to_index[channel];
+
+ if (index < 14)
+ val = val / 2; /* 0.002V scale */
+ else
+ val = val / 6; /* 0.006V scale */
+
+ val = clamp_val(val, 0, 0x7ff);
+
+ switch (attr) {
+ case hwmon_in_min:
+ tmp = nct7904_read_reg(data, BANK_1,
+ VSEN1_LV_LL_REG + index * 4);
+ if (tmp < 0)
+ return tmp;
+ tmp &= ~0x7;
+ tmp |= val & 0x7;
+ ret = nct7904_write_reg(data, BANK_1,
+ VSEN1_LV_LL_REG + index * 4, tmp);
+ if (ret < 0)
+ return ret;
+ tmp = nct7904_read_reg(data, BANK_1,
+ VSEN1_HV_LL_REG + index * 4);
+ if (tmp < 0)
+ return tmp;
+ tmp = (val >> 3) & 0xff;
+ ret = nct7904_write_reg(data, BANK_1,
+ VSEN1_HV_LL_REG + index * 4, tmp);
+ return ret;
+ case hwmon_in_max:
+ tmp = nct7904_read_reg(data, BANK_1,
+ VSEN1_LV_HL_REG + index * 4);
+ if (tmp < 0)
+ return tmp;
+ tmp &= ~0x7;
+ tmp |= val & 0x7;
+ ret = nct7904_write_reg(data, BANK_1,
+ VSEN1_LV_HL_REG + index * 4, tmp);
+ if (ret < 0)
+ return ret;
+ tmp = nct7904_read_reg(data, BANK_1,
+ VSEN1_HV_HL_REG + index * 4);
+ if (tmp < 0)
+ return tmp;
+ tmp = (val >> 3) & 0xff;
+ ret = nct7904_write_reg(data, BANK_1,
+ VSEN1_HV_HL_REG + index * 4, tmp);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct nct7904_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + channel,
+ val);
+ return ret;
+ case hwmon_pwm_enable:
+ if (val < 1 || val > 2 ||
+ (val == 2 && !data->fan_mode[channel]))
+ return -EINVAL;
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + channel,
+ val == 2 ? data->fan_mode[channel] : 0);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel)
+{
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static int nct7904_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_in:
+ return nct7904_read_in(dev, attr, channel, val);
+ case hwmon_fan:
+ return nct7904_read_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return nct7904_read_pwm(dev, attr, channel, val);
+ case hwmon_temp:
+ return nct7904_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_in:
+ return nct7904_write_in(dev, attr, channel, val);
+ case hwmon_fan:
+ return nct7904_write_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return nct7904_write_pwm(dev, attr, channel, val);
+ case hwmon_temp:
+ return nct7904_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t nct7904_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_in:
+ return nct7904_in_is_visible(data, attr, channel);
+ case hwmon_fan:
+ return nct7904_fan_is_visible(data, attr, channel);
+ case hwmon_pwm:
+ return nct7904_pwm_is_visible(data, attr, channel);
+ case hwmon_temp:
+ return nct7904_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int nct7904_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -ENODEV;
+
+ /* Determine the chip type. */
+ if (i2c_smbus_read_byte_data(client, VENDOR_ID_REG) != NUVOTON_ID ||
+ i2c_smbus_read_byte_data(client, CHIP_ID_REG) != NCT7904_ID ||
+ (i2c_smbus_read_byte_data(client, DEVICE_ID_REG) & 0xf0) != 0x50 ||
+ (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00)
+ return -ENODEV;
+
+ strscpy(info->type, "nct7904", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static const struct hwmon_channel_info *nct7904_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ /* dummy, skipped in is_visible */
+ 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_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_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_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_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_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,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
+ HWMON_CHANNEL_INFO(temp,
+ 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,
+ 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,
+ 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
+};
+
+static const struct hwmon_ops nct7904_hwmon_ops = {
+ .is_visible = nct7904_is_visible,
+ .read = nct7904_read,
+ .write = nct7904_write,
+};
+
+static const struct hwmon_chip_info nct7904_chip_info = {
+ .ops = &nct7904_hwmon_ops,
+ .info = nct7904_info,
+};
+
+/*
+ * 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;
+ struct device *dev = &client->dev;
+ int ret, i;
+ u32 mask;
+ u8 val, bit;
+
+ data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->bank_lock);
+ data->bank_sel = -1;
+
+ /* Setup sensor groups. */
+ /* FANIN attributes */
+ ret = nct7904_read_reg16(data, BANK_0, FANIN_CTRL0_REG);
+ if (ret < 0)
+ return ret;
+ data->fanin_mask = (ret >> 8) | ((ret & 0xff) << 8);
+
+ /*
+ * VSEN attributes
+ *
+ * Note: voltage sensors overlap with external temperature
+ * sensors. So, if we ever decide to support the latter
+ * we will have to adjust 'vsen_mask' accordingly.
+ */
+ mask = 0;
+ ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG);
+ if (ret >= 0)
+ mask = (ret >> 8) | ((ret & 0xff) << 8);
+ ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
+ if (ret >= 0)
+ mask |= (ret << 16);
+ data->vsen_mask = mask;
+
+ /* CPU_TEMP attributes */
+ ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG);
+ if (ret < 0)
+ return ret;
+
+ if ((ret & 0x6) == 0x6)
+ data->tcpu_mask |= 1; /* TR1 */
+ if ((ret & 0x18) == 0x18)
+ data->tcpu_mask |= 2; /* TR2 */
+ if ((ret & 0x20) == 0x20)
+ data->tcpu_mask |= 4; /* TR3 */
+ if ((ret & 0x80) == 0x80)
+ data->tcpu_mask |= 8; /* TR4 */
+
+ /* LTD */
+ ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
+ if (ret < 0)
+ return ret;
+ if ((ret & 0x02) == 0x02)
+ data->tcpu_mask |= 0x10;
+
+ /* Multi-Function detecting for Volt and TR/TD */
+ ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG);
+ if (ret < 0)
+ return ret;
+
+ data->temp_mode = 0;
+ for (i = 0; i < 4; i++) {
+ val = (ret >> (i * 2)) & 0x03;
+ bit = (1 << i);
+ if (val == VOLT_MONITOR_MODE) {
+ data->tcpu_mask &= ~bit;
+ } else if (val == THERMAL_DIODE_MODE && i < 2) {
+ data->temp_mode |= bit;
+ data->vsen_mask &= ~(0x06 << (i * 2));
+ } else if (val == THERMISTOR_MODE) {
+ data->vsen_mask &= ~(0x02 << (i * 2));
+ } else {
+ /* Reserved */
+ data->tcpu_mask &= ~bit;
+ data->vsen_mask &= ~(0x06 << (i * 2));
+ }
+ }
+
+ /* PECI */
+ ret = nct7904_read_reg(data, BANK_2, PFE_REG);
+ if (ret < 0)
+ return ret;
+ if (ret & 0x80) {
+ data->enable_dts = 1; /* Enable DTS & PECI */
+ } else {
+ ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG);
+ if (ret < 0)
+ return ret;
+ if (ret & 0x80)
+ data->enable_dts = 0x3; /* Enable DTS & TSI */
+ }
+
+ /* Check DTS enable status */
+ if (data->enable_dts) {
+ ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG);
+ if (ret < 0)
+ return ret;
+ data->has_dts = ret & 0xF;
+ if (data->enable_dts & ENABLE_TSI) {
+ ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG);
+ if (ret < 0)
+ return ret;
+ data->has_dts |= (ret & 0xF) << 4;
+ }
+ }
+
+ for (i = 0; i < FANCTL_MAX; i++) {
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i);
+ if (ret < 0)
+ return ret;
+ 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);
+ 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[] = {
+ {"nct7904", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, nct7904_id);
+
+static struct i2c_driver nct7904_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "nct7904",
+ },
+ .probe_new = nct7904_probe,
+ .id_table = nct7904_id,
+ .detect = nct7904_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(nct7904_driver);
+
+MODULE_AUTHOR("Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>");
+MODULE_DESCRIPTION("Hwmon driver for NUVOTON NCT7904");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
new file mode 100644
index 000000000..11a28609d
--- /dev/null
+++ b/drivers/hwmon/npcm750-pwm-fan.c
@@ -0,0 +1,1035 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+/* NPCM7XX PWM registers */
+#define NPCM7XX_PWM_REG_BASE(base, n) ((base) + ((n) * 0x1000L))
+
+#define NPCM7XX_PWM_REG_PR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x00)
+#define NPCM7XX_PWM_REG_CSR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x04)
+#define NPCM7XX_PWM_REG_CR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x08)
+#define NPCM7XX_PWM_REG_CNRx(base, n, ch) \
+ (NPCM7XX_PWM_REG_BASE(base, n) + 0x0C + (12 * (ch)))
+#define NPCM7XX_PWM_REG_CMRx(base, n, ch) \
+ (NPCM7XX_PWM_REG_BASE(base, n) + 0x10 + (12 * (ch)))
+#define NPCM7XX_PWM_REG_PDRx(base, n, ch) \
+ (NPCM7XX_PWM_REG_BASE(base, n) + 0x14 + (12 * (ch)))
+#define NPCM7XX_PWM_REG_PIER(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x3C)
+#define NPCM7XX_PWM_REG_PIIR(base, n) (NPCM7XX_PWM_REG_BASE(base, n) + 0x40)
+
+#define NPCM7XX_PWM_CTRL_CH0_MODE_BIT BIT(3)
+#define NPCM7XX_PWM_CTRL_CH1_MODE_BIT BIT(11)
+#define NPCM7XX_PWM_CTRL_CH2_MODE_BIT BIT(15)
+#define NPCM7XX_PWM_CTRL_CH3_MODE_BIT BIT(19)
+
+#define NPCM7XX_PWM_CTRL_CH0_INV_BIT BIT(2)
+#define NPCM7XX_PWM_CTRL_CH1_INV_BIT BIT(10)
+#define NPCM7XX_PWM_CTRL_CH2_INV_BIT BIT(14)
+#define NPCM7XX_PWM_CTRL_CH3_INV_BIT BIT(18)
+
+#define NPCM7XX_PWM_CTRL_CH0_EN_BIT BIT(0)
+#define NPCM7XX_PWM_CTRL_CH1_EN_BIT BIT(8)
+#define NPCM7XX_PWM_CTRL_CH2_EN_BIT BIT(12)
+#define NPCM7XX_PWM_CTRL_CH3_EN_BIT BIT(16)
+
+/* Define the maximum PWM channel number */
+#define NPCM7XX_PWM_MAX_CHN_NUM 8
+#define NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE 4
+#define NPCM7XX_PWM_MAX_MODULES 2
+
+/* Define the Counter Register, value = 100 for match 100% */
+#define NPCM7XX_PWM_COUNTER_DEFAULT_NUM 255
+#define NPCM7XX_PWM_CMR_DEFAULT_NUM 255
+#define NPCM7XX_PWM_CMR_MAX 255
+
+/* default all PWM channels PRESCALE2 = 1 */
+#define NPCM7XX_PWM_PRESCALE2_DEFAULT_CH0 0x4
+#define NPCM7XX_PWM_PRESCALE2_DEFAULT_CH1 0x40
+#define NPCM7XX_PWM_PRESCALE2_DEFAULT_CH2 0x400
+#define NPCM7XX_PWM_PRESCALE2_DEFAULT_CH3 0x4000
+
+#define PWM_OUTPUT_FREQ_25KHZ 25000
+#define PWN_CNT_DEFAULT 256
+#define MIN_PRESCALE1 2
+#define NPCM7XX_PWM_PRESCALE_SHIFT_CH01 8
+
+#define NPCM7XX_PWM_PRESCALE2_DEFAULT (NPCM7XX_PWM_PRESCALE2_DEFAULT_CH0 | \
+ NPCM7XX_PWM_PRESCALE2_DEFAULT_CH1 | \
+ NPCM7XX_PWM_PRESCALE2_DEFAULT_CH2 | \
+ NPCM7XX_PWM_PRESCALE2_DEFAULT_CH3)
+
+#define NPCM7XX_PWM_CTRL_MODE_DEFAULT (NPCM7XX_PWM_CTRL_CH0_MODE_BIT | \
+ NPCM7XX_PWM_CTRL_CH1_MODE_BIT | \
+ NPCM7XX_PWM_CTRL_CH2_MODE_BIT | \
+ NPCM7XX_PWM_CTRL_CH3_MODE_BIT)
+
+/* NPCM7XX FAN Tacho registers */
+#define NPCM7XX_FAN_REG_BASE(base, n) ((base) + ((n) * 0x1000L))
+
+#define NPCM7XX_FAN_REG_TCNT1(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x00)
+#define NPCM7XX_FAN_REG_TCRA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x02)
+#define NPCM7XX_FAN_REG_TCRB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x04)
+#define NPCM7XX_FAN_REG_TCNT2(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x06)
+#define NPCM7XX_FAN_REG_TPRSC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x08)
+#define NPCM7XX_FAN_REG_TCKC(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0A)
+#define NPCM7XX_FAN_REG_TMCTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0C)
+#define NPCM7XX_FAN_REG_TICTRL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x0E)
+#define NPCM7XX_FAN_REG_TICLR(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x10)
+#define NPCM7XX_FAN_REG_TIEN(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x12)
+#define NPCM7XX_FAN_REG_TCPA(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x14)
+#define NPCM7XX_FAN_REG_TCPB(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x16)
+#define NPCM7XX_FAN_REG_TCPCFG(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x18)
+#define NPCM7XX_FAN_REG_TINASEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1A)
+#define NPCM7XX_FAN_REG_TINBSEL(base, n) (NPCM7XX_FAN_REG_BASE(base, n) + 0x1C)
+
+#define NPCM7XX_FAN_TCKC_CLKX_NONE 0
+#define NPCM7XX_FAN_TCKC_CLK1_APB BIT(0)
+#define NPCM7XX_FAN_TCKC_CLK2_APB BIT(3)
+
+#define NPCM7XX_FAN_TMCTRL_TBEN BIT(6)
+#define NPCM7XX_FAN_TMCTRL_TAEN BIT(5)
+#define NPCM7XX_FAN_TMCTRL_TBEDG BIT(4)
+#define NPCM7XX_FAN_TMCTRL_TAEDG BIT(3)
+#define NPCM7XX_FAN_TMCTRL_MODE_5 BIT(2)
+
+#define NPCM7XX_FAN_TICLR_CLEAR_ALL GENMASK(5, 0)
+#define NPCM7XX_FAN_TICLR_TFCLR BIT(5)
+#define NPCM7XX_FAN_TICLR_TECLR BIT(4)
+#define NPCM7XX_FAN_TICLR_TDCLR BIT(3)
+#define NPCM7XX_FAN_TICLR_TCCLR BIT(2)
+#define NPCM7XX_FAN_TICLR_TBCLR BIT(1)
+#define NPCM7XX_FAN_TICLR_TACLR BIT(0)
+
+#define NPCM7XX_FAN_TIEN_ENABLE_ALL GENMASK(5, 0)
+#define NPCM7XX_FAN_TIEN_TFIEN BIT(5)
+#define NPCM7XX_FAN_TIEN_TEIEN BIT(4)
+#define NPCM7XX_FAN_TIEN_TDIEN BIT(3)
+#define NPCM7XX_FAN_TIEN_TCIEN BIT(2)
+#define NPCM7XX_FAN_TIEN_TBIEN BIT(1)
+#define NPCM7XX_FAN_TIEN_TAIEN BIT(0)
+
+#define NPCM7XX_FAN_TICTRL_TFPND BIT(5)
+#define NPCM7XX_FAN_TICTRL_TEPND BIT(4)
+#define NPCM7XX_FAN_TICTRL_TDPND BIT(3)
+#define NPCM7XX_FAN_TICTRL_TCPND BIT(2)
+#define NPCM7XX_FAN_TICTRL_TBPND BIT(1)
+#define NPCM7XX_FAN_TICTRL_TAPND BIT(0)
+
+#define NPCM7XX_FAN_TCPCFG_HIBEN BIT(7)
+#define NPCM7XX_FAN_TCPCFG_EQBEN BIT(6)
+#define NPCM7XX_FAN_TCPCFG_LOBEN BIT(5)
+#define NPCM7XX_FAN_TCPCFG_CPBSEL BIT(4)
+#define NPCM7XX_FAN_TCPCFG_HIAEN BIT(3)
+#define NPCM7XX_FAN_TCPCFG_EQAEN BIT(2)
+#define NPCM7XX_FAN_TCPCFG_LOAEN BIT(1)
+#define NPCM7XX_FAN_TCPCFG_CPASEL BIT(0)
+
+/* FAN General Definition */
+/* Define the maximum FAN channel number */
+#define NPCM7XX_FAN_MAX_MODULE 8
+#define NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE 2
+#define NPCM7XX_FAN_MAX_CHN_NUM 16
+
+/*
+ * Get Fan Tach Timeout (base on clock 214843.75Hz, 1 cnt = 4.654us)
+ * Timeout 94ms ~= 0x5000
+ * (The minimum FAN speed could to support ~640RPM/pulse 1,
+ * 320RPM/pulse 2, ...-- 10.6Hz)
+ */
+#define NPCM7XX_FAN_TIMEOUT 0x5000
+#define NPCM7XX_FAN_TCNT 0xFFFF
+#define NPCM7XX_FAN_TCPA (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
+#define NPCM7XX_FAN_TCPB (NPCM7XX_FAN_TCNT - NPCM7XX_FAN_TIMEOUT)
+
+#define NPCM7XX_FAN_POLL_TIMER_200MS 200
+#define NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION 2
+#define NPCM7XX_FAN_TINASEL_FANIN_DEFAULT 0
+#define NPCM7XX_FAN_CLK_PRESCALE 255
+
+#define NPCM7XX_FAN_CMPA 0
+#define NPCM7XX_FAN_CMPB 1
+
+/* Obtain the fan number */
+#define NPCM7XX_FAN_INPUT(fan, cmp) (((fan) << 1) + (cmp))
+
+/* fan sample status */
+#define FAN_DISABLE 0xFF
+#define FAN_INIT 0x00
+#define FAN_PREPARE_TO_GET_FIRST_CAPTURE 0x01
+#define FAN_ENOUGH_SAMPLE 0x02
+
+struct npcm7xx_fan_dev {
+ u8 fan_st_flg;
+ u8 fan_pls_per_rev;
+ u16 fan_cnt;
+ u32 fan_cnt_tmp;
+};
+
+struct npcm7xx_cooling_device {
+ char name[THERMAL_NAME_LENGTH];
+ struct npcm7xx_pwm_fan_data *data;
+ struct thermal_cooling_device *tcdev;
+ int pwm_port;
+ u8 *cooling_levels;
+ u8 max_state;
+ u8 cur_state;
+};
+
+struct npcm7xx_pwm_fan_data {
+ void __iomem *pwm_base;
+ void __iomem *fan_base;
+ unsigned long pwm_clk_freq;
+ unsigned long fan_clk_freq;
+ struct clk *pwm_clk;
+ struct clk *fan_clk;
+ struct mutex pwm_lock[NPCM7XX_PWM_MAX_MODULES];
+ spinlock_t fan_lock[NPCM7XX_FAN_MAX_MODULE];
+ int fan_irq[NPCM7XX_FAN_MAX_MODULE];
+ bool pwm_present[NPCM7XX_PWM_MAX_CHN_NUM];
+ bool fan_present[NPCM7XX_FAN_MAX_CHN_NUM];
+ u32 input_clk_freq;
+ struct timer_list fan_timer;
+ struct npcm7xx_fan_dev fan_dev[NPCM7XX_FAN_MAX_CHN_NUM];
+ struct npcm7xx_cooling_device *cdev[NPCM7XX_PWM_MAX_CHN_NUM];
+ u8 fan_select;
+};
+
+static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
+ int channel, u16 val)
+{
+ u32 pwm_ch = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+ u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+ u32 tmp_buf, ctrl_en_bit, env_bit;
+
+ /*
+ * Config PWM Comparator register for setting duty cycle
+ */
+ mutex_lock(&data->pwm_lock[module]);
+
+ /* write new CMR value */
+ iowrite32(val, NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, pwm_ch));
+ tmp_buf = ioread32(NPCM7XX_PWM_REG_CR(data->pwm_base, module));
+
+ switch (pwm_ch) {
+ case 0:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH0_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH0_INV_BIT;
+ break;
+ case 1:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH1_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH1_INV_BIT;
+ break;
+ case 2:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH2_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH2_INV_BIT;
+ break;
+ case 3:
+ ctrl_en_bit = NPCM7XX_PWM_CTRL_CH3_EN_BIT;
+ env_bit = NPCM7XX_PWM_CTRL_CH3_INV_BIT;
+ break;
+ default:
+ mutex_unlock(&data->pwm_lock[module]);
+ return -ENODEV;
+ }
+
+ if (val == 0) {
+ /* Disable PWM */
+ tmp_buf &= ~ctrl_en_bit;
+ tmp_buf |= env_bit;
+ } else {
+ /* Enable PWM */
+ tmp_buf |= ctrl_en_bit;
+ tmp_buf &= ~env_bit;
+ }
+
+ iowrite32(tmp_buf, NPCM7XX_PWM_REG_CR(data->pwm_base, module));
+ mutex_unlock(&data->pwm_lock[module]);
+
+ return 0;
+}
+
+static inline void npcm7xx_fan_start_capture(struct npcm7xx_pwm_fan_data *data,
+ u8 fan, u8 cmp)
+{
+ u8 fan_id;
+ u8 reg_mode;
+ u8 reg_int;
+ unsigned long flags;
+
+ fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
+
+ /* to check whether any fan tach is enable */
+ if (data->fan_dev[fan_id].fan_st_flg != FAN_DISABLE) {
+ /* reset status */
+ spin_lock_irqsave(&data->fan_lock[fan], flags);
+
+ data->fan_dev[fan_id].fan_st_flg = FAN_INIT;
+ reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ /*
+ * the interrupt enable bits do not need to be cleared before
+ * it sets, the interrupt enable bits are cleared only on reset.
+ * the clock unit control register is behaving in the same
+ * manner that the interrupt enable register behave.
+ */
+ if (cmp == NPCM7XX_FAN_CMPA) {
+ /* enable interrupt */
+ iowrite8(reg_int | (NPCM7XX_FAN_TIEN_TAIEN |
+ NPCM7XX_FAN_TIEN_TEIEN),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ reg_mode = NPCM7XX_FAN_TCKC_CLK1_APB
+ | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
+ fan));
+
+ /* start to Capture */
+ iowrite8(reg_mode, NPCM7XX_FAN_REG_TCKC(data->fan_base,
+ fan));
+ } else {
+ /* enable interrupt */
+ iowrite8(reg_int | (NPCM7XX_FAN_TIEN_TBIEN |
+ NPCM7XX_FAN_TIEN_TFIEN),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ reg_mode =
+ NPCM7XX_FAN_TCKC_CLK2_APB
+ | ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base,
+ fan));
+
+ /* start to Capture */
+ iowrite8(reg_mode,
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+ }
+
+ spin_unlock_irqrestore(&data->fan_lock[fan], flags);
+ }
+}
+
+/*
+ * Enable a background timer to poll fan tach value, (200ms * 4)
+ * to polling all fan
+ */
+static void npcm7xx_fan_polling(struct timer_list *t)
+{
+ struct npcm7xx_pwm_fan_data *data;
+ int i;
+
+ data = from_timer(data, t, fan_timer);
+
+ /*
+ * Polling two module per one round,
+ * FAN01 & FAN89 / FAN23 & FAN1011 / FAN45 & FAN1213 / FAN67 & FAN1415
+ */
+ for (i = data->fan_select; i < NPCM7XX_FAN_MAX_MODULE;
+ i = i + 4) {
+ /* clear the flag and reset the counter (TCNT) */
+ iowrite8(NPCM7XX_FAN_TICLR_CLEAR_ALL,
+ NPCM7XX_FAN_REG_TICLR(data->fan_base, i));
+
+ if (data->fan_present[i * 2]) {
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT1(data->fan_base, i));
+ npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPA);
+ }
+ if (data->fan_present[(i * 2) + 1]) {
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT2(data->fan_base, i));
+ npcm7xx_fan_start_capture(data, i, NPCM7XX_FAN_CMPB);
+ }
+ }
+
+ data->fan_select++;
+ data->fan_select &= 0x3;
+
+ /* reset the timer interval */
+ data->fan_timer.expires = jiffies +
+ msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
+ add_timer(&data->fan_timer);
+}
+
+static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data,
+ u8 fan, u8 cmp, u8 fan_id, u8 flag_int,
+ u8 flag_mode, u8 flag_clear)
+{
+ u8 reg_int;
+ u8 reg_mode;
+ u16 fan_cap;
+
+ if (cmp == NPCM7XX_FAN_CMPA)
+ fan_cap = ioread16(NPCM7XX_FAN_REG_TCRA(data->fan_base, fan));
+ else
+ fan_cap = ioread16(NPCM7XX_FAN_REG_TCRB(data->fan_base, fan));
+
+ /* clear capature flag, H/W will auto reset the NPCM7XX_FAN_TCNTx */
+ iowrite8(flag_clear, NPCM7XX_FAN_REG_TICLR(data->fan_base, fan));
+
+ if (data->fan_dev[fan_id].fan_st_flg == FAN_INIT) {
+ /* First capture, drop it */
+ data->fan_dev[fan_id].fan_st_flg =
+ FAN_PREPARE_TO_GET_FIRST_CAPTURE;
+
+ /* reset counter */
+ data->fan_dev[fan_id].fan_cnt_tmp = 0;
+ } else if (data->fan_dev[fan_id].fan_st_flg < FAN_ENOUGH_SAMPLE) {
+ /*
+ * collect the enough sample,
+ * (ex: 2 pulse fan need to get 2 sample)
+ */
+ data->fan_dev[fan_id].fan_cnt_tmp +=
+ (NPCM7XX_FAN_TCNT - fan_cap);
+
+ data->fan_dev[fan_id].fan_st_flg++;
+ } else {
+ /* get enough sample or fan disable */
+ if (data->fan_dev[fan_id].fan_st_flg == FAN_ENOUGH_SAMPLE) {
+ data->fan_dev[fan_id].fan_cnt_tmp +=
+ (NPCM7XX_FAN_TCNT - fan_cap);
+
+ /* compute finial average cnt per pulse */
+ data->fan_dev[fan_id].fan_cnt =
+ data->fan_dev[fan_id].fan_cnt_tmp /
+ FAN_ENOUGH_SAMPLE;
+
+ data->fan_dev[fan_id].fan_st_flg = FAN_INIT;
+ }
+
+ reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ /* disable interrupt */
+ iowrite8((reg_int & ~flag_int),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+ reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+
+ /* stop capturing */
+ iowrite8((reg_mode & ~flag_mode),
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+ }
+}
+
+static inline void npcm7xx_check_cmp(struct npcm7xx_pwm_fan_data *data,
+ u8 fan, u8 cmp, u8 flag)
+{
+ u8 reg_int;
+ u8 reg_mode;
+ u8 flag_timeout;
+ u8 flag_cap;
+ u8 flag_clear;
+ u8 flag_int;
+ u8 flag_mode;
+ u8 fan_id;
+
+ fan_id = NPCM7XX_FAN_INPUT(fan, cmp);
+
+ if (cmp == NPCM7XX_FAN_CMPA) {
+ flag_cap = NPCM7XX_FAN_TICTRL_TAPND;
+ flag_timeout = NPCM7XX_FAN_TICTRL_TEPND;
+ flag_int = NPCM7XX_FAN_TIEN_TAIEN | NPCM7XX_FAN_TIEN_TEIEN;
+ flag_mode = NPCM7XX_FAN_TCKC_CLK1_APB;
+ flag_clear = NPCM7XX_FAN_TICLR_TACLR | NPCM7XX_FAN_TICLR_TECLR;
+ } else {
+ flag_cap = NPCM7XX_FAN_TICTRL_TBPND;
+ flag_timeout = NPCM7XX_FAN_TICTRL_TFPND;
+ flag_int = NPCM7XX_FAN_TIEN_TBIEN | NPCM7XX_FAN_TIEN_TFIEN;
+ flag_mode = NPCM7XX_FAN_TCKC_CLK2_APB;
+ flag_clear = NPCM7XX_FAN_TICLR_TBCLR | NPCM7XX_FAN_TICLR_TFCLR;
+ }
+
+ if (flag & flag_timeout) {
+ reg_int = ioread8(NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ /* disable interrupt */
+ iowrite8((reg_int & ~flag_int),
+ NPCM7XX_FAN_REG_TIEN(data->fan_base, fan));
+
+ /* clear interrupt flag */
+ iowrite8(flag_clear,
+ NPCM7XX_FAN_REG_TICLR(data->fan_base, fan));
+
+ reg_mode = ioread8(NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+
+ /* stop capturing */
+ iowrite8((reg_mode & ~flag_mode),
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, fan));
+
+ /*
+ * If timeout occurs (NPCM7XX_FAN_TIMEOUT), the fan doesn't
+ * connect or speed is lower than 10.6Hz (320RPM/pulse2).
+ * In these situation, the RPM output should be zero.
+ */
+ data->fan_dev[fan_id].fan_cnt = 0;
+ } else {
+ /* input capture is occurred */
+ if (flag & flag_cap)
+ npcm7xx_fan_compute(data, fan, cmp, fan_id, flag_int,
+ flag_mode, flag_clear);
+ }
+}
+
+static irqreturn_t npcm7xx_fan_isr(int irq, void *dev_id)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_id;
+ unsigned long flags;
+ int module;
+ u8 flag;
+
+ module = irq - data->fan_irq[0];
+ spin_lock_irqsave(&data->fan_lock[module], flags);
+
+ flag = ioread8(NPCM7XX_FAN_REG_TICTRL(data->fan_base, module));
+ if (flag > 0) {
+ npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPA, flag);
+ npcm7xx_check_cmp(data, module, NPCM7XX_FAN_CMPB, flag);
+ spin_unlock_irqrestore(&data->fan_lock[module], flags);
+ return IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&data->fan_lock[module], flags);
+
+ return IRQ_NONE;
+}
+
+static int npcm7xx_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
+ u32 pmw_ch = (channel % NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+ u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = ioread32
+ (NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, pmw_ch));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int npcm7xx_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
+ int err;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > NPCM7XX_PWM_CMR_MAX)
+ return -EINVAL;
+ err = npcm7xx_pwm_config_set(data, channel, (u16)val);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static umode_t npcm7xx_pwm_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct npcm7xx_pwm_fan_data *data = _data;
+
+ if (!data->pwm_present[channel])
+ return 0;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static int npcm7xx_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ struct npcm7xx_pwm_fan_data *data = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_fan_input:
+ *val = 0;
+ if (data->fan_dev[channel].fan_cnt <= 0)
+ return data->fan_dev[channel].fan_cnt;
+
+ /* Convert the raw reading to RPM */
+ if (data->fan_dev[channel].fan_cnt > 0 &&
+ data->fan_dev[channel].fan_pls_per_rev > 0)
+ *val = ((data->input_clk_freq * 60) /
+ (data->fan_dev[channel].fan_cnt *
+ data->fan_dev[channel].fan_pls_per_rev));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t npcm7xx_fan_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct npcm7xx_pwm_fan_data *data = _data;
+
+ if (!data->fan_present[channel])
+ return 0;
+
+ switch (attr) {
+ case hwmon_fan_input:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static int npcm7xx_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return npcm7xx_read_pwm(dev, attr, channel, val);
+ case hwmon_fan:
+ return npcm7xx_read_fan(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int npcm7xx_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return npcm7xx_write_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t npcm7xx_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return npcm7xx_pwm_is_visible(data, attr, channel);
+ case hwmon_fan:
+ return npcm7xx_fan_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info *npcm7xx_info[] = {
+ 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(fan,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops npcm7xx_hwmon_ops = {
+ .is_visible = npcm7xx_is_visible,
+ .read = npcm7xx_read,
+ .write = npcm7xx_write,
+};
+
+static const struct hwmon_chip_info npcm7xx_chip_info = {
+ .ops = &npcm7xx_hwmon_ops,
+ .info = npcm7xx_info,
+};
+
+static u32 npcm7xx_pwm_init(struct npcm7xx_pwm_fan_data *data)
+{
+ int m, ch;
+ u32 prescale_val, output_freq;
+
+ data->pwm_clk_freq = clk_get_rate(data->pwm_clk);
+
+ /* Adjust NPCM7xx PWMs output frequency to ~25Khz */
+ output_freq = data->pwm_clk_freq / PWN_CNT_DEFAULT;
+ prescale_val = DIV_ROUND_CLOSEST(output_freq, PWM_OUTPUT_FREQ_25KHZ);
+
+ /* If prescale_val = 0, then the prescale output clock is stopped */
+ if (prescale_val < MIN_PRESCALE1)
+ prescale_val = MIN_PRESCALE1;
+ /*
+ * prescale_val need to decrement in one because in the PWM Prescale
+ * register the Prescale value increment by one
+ */
+ prescale_val--;
+
+ /* Setting PWM Prescale Register value register to both modules */
+ prescale_val |= (prescale_val << NPCM7XX_PWM_PRESCALE_SHIFT_CH01);
+
+ for (m = 0; m < NPCM7XX_PWM_MAX_MODULES ; m++) {
+ iowrite32(prescale_val, NPCM7XX_PWM_REG_PR(data->pwm_base, m));
+ iowrite32(NPCM7XX_PWM_PRESCALE2_DEFAULT,
+ NPCM7XX_PWM_REG_CSR(data->pwm_base, m));
+ iowrite32(NPCM7XX_PWM_CTRL_MODE_DEFAULT,
+ NPCM7XX_PWM_REG_CR(data->pwm_base, m));
+
+ for (ch = 0; ch < NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE; ch++) {
+ iowrite32(NPCM7XX_PWM_COUNTER_DEFAULT_NUM,
+ NPCM7XX_PWM_REG_CNRx(data->pwm_base, m, ch));
+ }
+ }
+
+ return output_freq / ((prescale_val & 0xf) + 1);
+}
+
+static void npcm7xx_fan_init(struct npcm7xx_pwm_fan_data *data)
+{
+ int md;
+ int ch;
+ int i;
+ u32 apb_clk_freq;
+
+ for (md = 0; md < NPCM7XX_FAN_MAX_MODULE; md++) {
+ /* stop FAN0~7 clock */
+ iowrite8(NPCM7XX_FAN_TCKC_CLKX_NONE,
+ NPCM7XX_FAN_REG_TCKC(data->fan_base, md));
+
+ /* disable all interrupt */
+ iowrite8(0x00, NPCM7XX_FAN_REG_TIEN(data->fan_base, md));
+
+ /* clear all interrupt */
+ iowrite8(NPCM7XX_FAN_TICLR_CLEAR_ALL,
+ NPCM7XX_FAN_REG_TICLR(data->fan_base, md));
+
+ /* set FAN0~7 clock prescaler */
+ iowrite8(NPCM7XX_FAN_CLK_PRESCALE,
+ NPCM7XX_FAN_REG_TPRSC(data->fan_base, md));
+
+ /* set FAN0~7 mode (high-to-low transition) */
+ iowrite8((NPCM7XX_FAN_TMCTRL_MODE_5 | NPCM7XX_FAN_TMCTRL_TBEN |
+ NPCM7XX_FAN_TMCTRL_TAEN),
+ NPCM7XX_FAN_REG_TMCTRL(data->fan_base, md));
+
+ /* set FAN0~7 Initial Count/Cap */
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT1(data->fan_base, md));
+ iowrite16(NPCM7XX_FAN_TCNT,
+ NPCM7XX_FAN_REG_TCNT2(data->fan_base, md));
+
+ /* set FAN0~7 compare (equal to count) */
+ iowrite8((NPCM7XX_FAN_TCPCFG_EQAEN | NPCM7XX_FAN_TCPCFG_EQBEN),
+ NPCM7XX_FAN_REG_TCPCFG(data->fan_base, md));
+
+ /* set FAN0~7 compare value */
+ iowrite16(NPCM7XX_FAN_TCPA,
+ NPCM7XX_FAN_REG_TCPA(data->fan_base, md));
+ iowrite16(NPCM7XX_FAN_TCPB,
+ NPCM7XX_FAN_REG_TCPB(data->fan_base, md));
+
+ /* set FAN0~7 fan input FANIN 0~15 */
+ iowrite8(NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
+ NPCM7XX_FAN_REG_TINASEL(data->fan_base, md));
+ iowrite8(NPCM7XX_FAN_TINASEL_FANIN_DEFAULT,
+ NPCM7XX_FAN_REG_TINBSEL(data->fan_base, md));
+
+ for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE; i++) {
+ ch = md * NPCM7XX_FAN_MAX_CHN_NUM_IN_A_MODULE + i;
+ data->fan_dev[ch].fan_st_flg = FAN_DISABLE;
+ data->fan_dev[ch].fan_pls_per_rev =
+ NPCM7XX_FAN_DEFAULT_PULSE_PER_REVOLUTION;
+ data->fan_dev[ch].fan_cnt = 0;
+ }
+ }
+
+ apb_clk_freq = clk_get_rate(data->fan_clk);
+
+ /* Fan tach input clock = APB clock / prescalar, default is 255. */
+ data->input_clk_freq = apb_clk_freq / (NPCM7XX_FAN_CLK_PRESCALE + 1);
+}
+
+static int
+npcm7xx_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct npcm7xx_cooling_device *cdev = tcdev->devdata;
+
+ *state = cdev->max_state;
+
+ return 0;
+}
+
+static int
+npcm7xx_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long *state)
+{
+ struct npcm7xx_cooling_device *cdev = tcdev->devdata;
+
+ *state = cdev->cur_state;
+
+ return 0;
+}
+
+static int
+npcm7xx_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev,
+ unsigned long state)
+{
+ struct npcm7xx_cooling_device *cdev = tcdev->devdata;
+ int ret;
+
+ if (state > cdev->max_state)
+ return -EINVAL;
+
+ cdev->cur_state = state;
+ ret = npcm7xx_pwm_config_set(cdev->data, cdev->pwm_port,
+ cdev->cooling_levels[cdev->cur_state]);
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops npcm7xx_pwm_cool_ops = {
+ .get_max_state = npcm7xx_pwm_cz_get_max_state,
+ .get_cur_state = npcm7xx_pwm_cz_get_cur_state,
+ .set_cur_state = npcm7xx_pwm_cz_set_cur_state,
+};
+
+static int npcm7xx_create_pwm_cooling(struct device *dev,
+ struct device_node *child,
+ struct npcm7xx_pwm_fan_data *data,
+ u32 pwm_port, u8 num_levels)
+{
+ int ret;
+ struct npcm7xx_cooling_device *cdev;
+
+ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL);
+ if (!cdev->cooling_levels)
+ return -ENOMEM;
+
+ cdev->max_state = num_levels - 1;
+ ret = of_property_read_u8_array(child, "cooling-levels",
+ cdev->cooling_levels,
+ num_levels);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read.\n");
+ return ret;
+ }
+ snprintf(cdev->name, THERMAL_NAME_LENGTH, "%pOFn%d", child,
+ pwm_port);
+
+ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child,
+ cdev->name, cdev, &npcm7xx_pwm_cool_ops);
+ if (IS_ERR(cdev->tcdev))
+ return PTR_ERR(cdev->tcdev);
+
+ cdev->data = data;
+ cdev->pwm_port = pwm_port;
+
+ data->cdev[pwm_port] = cdev;
+
+ return 0;
+}
+
+static int npcm7xx_en_pwm_fan(struct device *dev,
+ struct device_node *child,
+ struct npcm7xx_pwm_fan_data *data)
+{
+ u8 *fan_ch;
+ u32 pwm_port;
+ int ret, fan_cnt;
+ u8 index, ch;
+
+ ret = of_property_read_u32(child, "reg", &pwm_port);
+ if (ret)
+ return ret;
+
+ data->pwm_present[pwm_port] = true;
+ ret = npcm7xx_pwm_config_set(data, pwm_port,
+ NPCM7XX_PWM_CMR_DEFAULT_NUM);
+
+ ret = of_property_count_u8_elems(child, "cooling-levels");
+ if (ret > 0) {
+ ret = npcm7xx_create_pwm_cooling(dev, child, data, pwm_port,
+ ret);
+ if (ret)
+ return ret;
+ }
+
+ fan_cnt = of_property_count_u8_elems(child, "fan-tach-ch");
+ if (fan_cnt < 1)
+ return -EINVAL;
+
+ fan_ch = devm_kcalloc(dev, fan_cnt, sizeof(*fan_ch), GFP_KERNEL);
+ if (!fan_ch)
+ return -ENOMEM;
+
+ ret = of_property_read_u8_array(child, "fan-tach-ch", fan_ch, fan_cnt);
+ if (ret)
+ return ret;
+
+ for (ch = 0; ch < fan_cnt; ch++) {
+ index = fan_ch[ch];
+ data->fan_present[index] = true;
+ data->fan_dev[index].fan_st_flg = FAN_INIT;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np, *child;
+ struct npcm7xx_pwm_fan_data *data;
+ struct resource *res;
+ struct device *hwmon;
+ char name[20];
+ int ret, cnt;
+ u32 output_freq;
+ u32 i;
+
+ np = dev->of_node;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
+ if (!res) {
+ dev_err(dev, "pwm resource not found\n");
+ return -ENODEV;
+ }
+
+ data->pwm_base = devm_ioremap_resource(dev, res);
+ dev_dbg(dev, "pwm base resource is %pR\n", res);
+ if (IS_ERR(data->pwm_base))
+ return PTR_ERR(data->pwm_base);
+
+ data->pwm_clk = devm_clk_get(dev, "pwm");
+ if (IS_ERR(data->pwm_clk)) {
+ dev_err(dev, "couldn't get pwm clock\n");
+ return PTR_ERR(data->pwm_clk);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fan");
+ if (!res) {
+ dev_err(dev, "fan resource not found\n");
+ return -ENODEV;
+ }
+
+ data->fan_base = devm_ioremap_resource(dev, res);
+ dev_dbg(dev, "fan base resource is %pR\n", res);
+ if (IS_ERR(data->fan_base))
+ return PTR_ERR(data->fan_base);
+
+ data->fan_clk = devm_clk_get(dev, "fan");
+ if (IS_ERR(data->fan_clk)) {
+ dev_err(dev, "couldn't get fan clock\n");
+ return PTR_ERR(data->fan_clk);
+ }
+
+ output_freq = npcm7xx_pwm_init(data);
+ npcm7xx_fan_init(data);
+
+ for (cnt = 0; cnt < NPCM7XX_PWM_MAX_MODULES ; cnt++)
+ mutex_init(&data->pwm_lock[cnt]);
+
+ for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
+ spin_lock_init(&data->fan_lock[i]);
+
+ data->fan_irq[i] = platform_get_irq(pdev, i);
+ if (data->fan_irq[i] < 0)
+ return data->fan_irq[i];
+
+ sprintf(name, "NPCM7XX-FAN-MD%d", i);
+ ret = devm_request_irq(dev, data->fan_irq[i], npcm7xx_fan_isr,
+ 0, name, (void *)data);
+ if (ret) {
+ dev_err(dev, "register IRQ fan%d failed\n", i);
+ return ret;
+ }
+ }
+
+ for_each_child_of_node(np, child) {
+ ret = npcm7xx_en_pwm_fan(dev, child, data);
+ if (ret) {
+ dev_err(dev, "enable pwm and fan failed\n");
+ of_node_put(child);
+ return ret;
+ }
+ }
+
+ hwmon = devm_hwmon_device_register_with_info(dev, "npcm7xx_pwm_fan",
+ data, &npcm7xx_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ dev_err(dev, "unable to register hwmon device\n");
+ return PTR_ERR(hwmon);
+ }
+
+ for (i = 0; i < NPCM7XX_FAN_MAX_CHN_NUM; i++) {
+ if (data->fan_present[i]) {
+ /* fan timer initialization */
+ data->fan_timer.expires = jiffies +
+ msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
+ timer_setup(&data->fan_timer,
+ npcm7xx_fan_polling, 0);
+ add_timer(&data->fan_timer);
+ break;
+ }
+ }
+
+ pr_info("NPCM7XX PWM-FAN Driver probed, output Freq %dHz[PWM], input Freq %dHz[FAN]\n",
+ output_freq, data->input_clk_freq);
+
+ return 0;
+}
+
+static const struct of_device_id of_pwm_fan_match_table[] = {
+ { .compatible = "nuvoton,npcm750-pwm-fan", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_pwm_fan_match_table);
+
+static struct platform_driver npcm7xx_pwm_fan_driver = {
+ .probe = npcm7xx_pwm_fan_probe,
+ .driver = {
+ .name = "npcm7xx_pwm_fan",
+ .of_match_table = of_pwm_fan_match_table,
+ },
+};
+
+module_platform_driver(npcm7xx_pwm_fan_driver);
+
+MODULE_DESCRIPTION("Nuvoton NPCM7XX PWM and Fan Tacho driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/nsa320-hwmon.c b/drivers/hwmon/nsa320-hwmon.c
new file mode 100644
index 000000000..ebe6b031e
--- /dev/null
+++ b/drivers/hwmon/nsa320-hwmon.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/hwmon/nsa320-hwmon.c
+ *
+ * ZyXEL NSA320 Media Servers
+ * hardware monitoring
+ *
+ * Copyright (C) 2016 Adam Baker <linux@baker-net.org.uk>
+ * based on a board file driver
+ * Copyright (C) 2012 Peter Schildmann <linux@schildmann.info>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+/* Tests for error return values rely upon this value being < 0x80 */
+#define MAGIC_NUMBER 0x55
+
+/*
+ * The Zyxel hwmon MCU is a Holtek HT46R065 that is factory programmed
+ * to perform temperature and fan speed monitoring. It is read by taking
+ * the active pin low. The 32 bit output word is then clocked onto the
+ * data line. The MSB of the data word is a magic nuber to indicate it
+ * has been read correctly, the next byte is the fan speed (in hundreds
+ * of RPM) and the last two bytes are the temperature (in tenths of a
+ * degree)
+ */
+
+struct nsa320_hwmon {
+ struct mutex update_lock; /* lock GPIO operations */
+ unsigned long last_updated; /* jiffies */
+ unsigned long mcu_data;
+ struct gpio_desc *act;
+ struct gpio_desc *clk;
+ struct gpio_desc *data;
+};
+
+enum nsa320_inputs {
+ NSA320_TEMP = 0,
+ NSA320_FAN = 1,
+};
+
+static const char * const nsa320_input_names[] = {
+ [NSA320_TEMP] = "System Temperature",
+ [NSA320_FAN] = "Chassis Fan",
+};
+
+/*
+ * Although this protocol looks similar to SPI the long delay
+ * between the active (aka chip select) signal and the shorter
+ * delay between clock pulses are needed for reliable operation.
+ * The delays provided are taken from the manufacturer kernel,
+ * testing suggest they probably incorporate a reasonable safety
+ * margin. (The single device tested became unreliable if the
+ * delay was reduced to 1/10th of this value.)
+ */
+static s32 nsa320_hwmon_update(struct device *dev)
+{
+ u32 mcu_data;
+ u32 mask;
+ struct nsa320_hwmon *hwmon = dev_get_drvdata(dev);
+
+ mutex_lock(&hwmon->update_lock);
+
+ mcu_data = hwmon->mcu_data;
+
+ if (time_after(jiffies, hwmon->last_updated + HZ) || mcu_data == 0) {
+ gpiod_set_value(hwmon->act, 1);
+ msleep(100);
+
+ mcu_data = 0;
+ for (mask = BIT(31); mask; mask >>= 1) {
+ gpiod_set_value(hwmon->clk, 0);
+ usleep_range(100, 200);
+ gpiod_set_value(hwmon->clk, 1);
+ usleep_range(100, 200);
+ if (gpiod_get_value(hwmon->data))
+ mcu_data |= mask;
+ }
+
+ gpiod_set_value(hwmon->act, 0);
+ dev_dbg(dev, "Read raw MCU data %08x\n", mcu_data);
+
+ if ((mcu_data >> 24) != MAGIC_NUMBER) {
+ dev_dbg(dev, "Read invalid MCU data %08x\n", mcu_data);
+ mcu_data = -EIO;
+ } else {
+ hwmon->mcu_data = mcu_data;
+ hwmon->last_updated = jiffies;
+ }
+ }
+
+ mutex_unlock(&hwmon->update_lock);
+
+ return mcu_data;
+}
+
+static ssize_t label_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int channel = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", nsa320_input_names[channel]);
+}
+
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ s32 mcu_data = nsa320_hwmon_update(dev);
+
+ if (mcu_data < 0)
+ return mcu_data;
+
+ return sprintf(buf, "%d\n", (mcu_data & 0xffff) * 100);
+}
+
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ s32 mcu_data = nsa320_hwmon_update(dev);
+
+ if (mcu_data < 0)
+ return mcu_data;
+
+ return sprintf(buf, "%d\n", ((mcu_data & 0xff0000) >> 16) * 100);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_label, label, NSA320_TEMP);
+static DEVICE_ATTR_RO(temp1_input);
+static SENSOR_DEVICE_ATTR_RO(fan1_label, label, NSA320_FAN);
+static DEVICE_ATTR_RO(fan1_input);
+
+static struct attribute *nsa320_attrs[] = {
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &dev_attr_temp1_input.attr,
+ &sensor_dev_attr_fan1_label.dev_attr.attr,
+ &dev_attr_fan1_input.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(nsa320);
+
+static const struct of_device_id of_nsa320_hwmon_match[] = {
+ { .compatible = "zyxel,nsa320-mcu", },
+ { },
+};
+
+static int nsa320_hwmon_probe(struct platform_device *pdev)
+{
+ struct nsa320_hwmon *hwmon;
+ struct device *classdev;
+
+ hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ /* Look up the GPIO pins to use */
+ hwmon->act = devm_gpiod_get(&pdev->dev, "act", GPIOD_OUT_LOW);
+ if (IS_ERR(hwmon->act))
+ return PTR_ERR(hwmon->act);
+
+ hwmon->clk = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_HIGH);
+ if (IS_ERR(hwmon->clk))
+ return PTR_ERR(hwmon->clk);
+
+ hwmon->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN);
+ if (IS_ERR(hwmon->data))
+ return PTR_ERR(hwmon->data);
+
+ mutex_init(&hwmon->update_lock);
+
+ classdev = devm_hwmon_device_register_with_groups(&pdev->dev,
+ "nsa320", hwmon, nsa320_groups);
+
+ return PTR_ERR_OR_ZERO(classdev);
+
+}
+
+/* All allocations use devres so remove() is not needed. */
+
+static struct platform_driver nsa320_hwmon_driver = {
+ .probe = nsa320_hwmon_probe,
+ .driver = {
+ .name = "nsa320-hwmon",
+ .of_match_table = of_match_ptr(of_nsa320_hwmon_match),
+ },
+};
+
+module_platform_driver(nsa320_hwmon_driver);
+
+MODULE_DEVICE_TABLE(of, of_nsa320_hwmon_match);
+MODULE_AUTHOR("Peter Schildmann <linux@schildmann.info>");
+MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>");
+MODULE_DESCRIPTION("NSA320 Hardware Monitoring");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:nsa320-hwmon");
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
new file mode 100644
index 000000000..9c9e9f4cc
--- /dev/null
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ntc_thermistor.c - NTC Thermistors
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.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/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;
+};
+
+/*
+ * Used as index in a zero-terminated array, holes not allowed so
+ * that NTC_LAST is the first empty array entry.
+ */
+enum {
+ NTC_B57330V2103,
+ NTC_B57891S0103,
+ NTC_NCP03WB473,
+ NTC_NCP03WF104,
+ NTC_NCP15WB473,
+ NTC_NCP15WL333,
+ NTC_NCP15XH103,
+ NTC_NCP18WB473,
+ NTC_NCP21WB473,
+ NTC_SSG1404001221,
+ NTC_LAST,
+};
+
+static const struct platform_device_id ntc_thermistor_id[] = {
+ [NTC_B57330V2103] = { "b57330v2103", TYPE_B57330V2103 },
+ [NTC_B57891S0103] = { "b57891s0103", TYPE_B57891S0103 },
+ [NTC_NCP03WB473] = { "ncp03wb473", TYPE_NCPXXWB473 },
+ [NTC_NCP03WF104] = { "ncp03wf104", TYPE_NCPXXWF104 },
+ [NTC_NCP15WB473] = { "ncp15wb473", TYPE_NCPXXWB473 },
+ [NTC_NCP15WL333] = { "ncp15wl333", TYPE_NCPXXWL333 },
+ [NTC_NCP15XH103] = { "ncp15xh103", TYPE_NCPXXXH103 },
+ [NTC_NCP18WB473] = { "ncp18wb473", TYPE_NCPXXWB473 },
+ [NTC_NCP21WB473] = { "ncp21wb473", TYPE_NCPXXWB473 },
+ [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 },
+ [NTC_LAST] = { },
+};
+
+/*
+ * A compensation table should be sorted by the values of .ohm
+ * in descending order.
+ * The following compensation tables are from the specification of Murata NTC
+ * Thermistors Datasheet
+ */
+static const struct ntc_compensation ncpXXwb473[] = {
+ { .temp_c = -40, .ohm = 1747920 },
+ { .temp_c = -35, .ohm = 1245428 },
+ { .temp_c = -30, .ohm = 898485 },
+ { .temp_c = -25, .ohm = 655802 },
+ { .temp_c = -20, .ohm = 483954 },
+ { .temp_c = -15, .ohm = 360850 },
+ { .temp_c = -10, .ohm = 271697 },
+ { .temp_c = -5, .ohm = 206463 },
+ { .temp_c = 0, .ohm = 158214 },
+ { .temp_c = 5, .ohm = 122259 },
+ { .temp_c = 10, .ohm = 95227 },
+ { .temp_c = 15, .ohm = 74730 },
+ { .temp_c = 20, .ohm = 59065 },
+ { .temp_c = 25, .ohm = 47000 },
+ { .temp_c = 30, .ohm = 37643 },
+ { .temp_c = 35, .ohm = 30334 },
+ { .temp_c = 40, .ohm = 24591 },
+ { .temp_c = 45, .ohm = 20048 },
+ { .temp_c = 50, .ohm = 16433 },
+ { .temp_c = 55, .ohm = 13539 },
+ { .temp_c = 60, .ohm = 11209 },
+ { .temp_c = 65, .ohm = 9328 },
+ { .temp_c = 70, .ohm = 7798 },
+ { .temp_c = 75, .ohm = 6544 },
+ { .temp_c = 80, .ohm = 5518 },
+ { .temp_c = 85, .ohm = 4674 },
+ { .temp_c = 90, .ohm = 3972 },
+ { .temp_c = 95, .ohm = 3388 },
+ { .temp_c = 100, .ohm = 2902 },
+ { .temp_c = 105, .ohm = 2494 },
+ { .temp_c = 110, .ohm = 2150 },
+ { .temp_c = 115, .ohm = 1860 },
+ { .temp_c = 120, .ohm = 1615 },
+ { .temp_c = 125, .ohm = 1406 },
+};
+static const struct ntc_compensation ncpXXwl333[] = {
+ { .temp_c = -40, .ohm = 1610154 },
+ { .temp_c = -35, .ohm = 1130850 },
+ { .temp_c = -30, .ohm = 802609 },
+ { .temp_c = -25, .ohm = 575385 },
+ { .temp_c = -20, .ohm = 416464 },
+ { .temp_c = -15, .ohm = 304219 },
+ { .temp_c = -10, .ohm = 224193 },
+ { .temp_c = -5, .ohm = 166623 },
+ { .temp_c = 0, .ohm = 124850 },
+ { .temp_c = 5, .ohm = 94287 },
+ { .temp_c = 10, .ohm = 71747 },
+ { .temp_c = 15, .ohm = 54996 },
+ { .temp_c = 20, .ohm = 42455 },
+ { .temp_c = 25, .ohm = 33000 },
+ { .temp_c = 30, .ohm = 25822 },
+ { .temp_c = 35, .ohm = 20335 },
+ { .temp_c = 40, .ohm = 16115 },
+ { .temp_c = 45, .ohm = 12849 },
+ { .temp_c = 50, .ohm = 10306 },
+ { .temp_c = 55, .ohm = 8314 },
+ { .temp_c = 60, .ohm = 6746 },
+ { .temp_c = 65, .ohm = 5503 },
+ { .temp_c = 70, .ohm = 4513 },
+ { .temp_c = 75, .ohm = 3721 },
+ { .temp_c = 80, .ohm = 3084 },
+ { .temp_c = 85, .ohm = 2569 },
+ { .temp_c = 90, .ohm = 2151 },
+ { .temp_c = 95, .ohm = 1809 },
+ { .temp_c = 100, .ohm = 1529 },
+ { .temp_c = 105, .ohm = 1299 },
+ { .temp_c = 110, .ohm = 1108 },
+ { .temp_c = 115, .ohm = 949 },
+ { .temp_c = 120, .ohm = 817 },
+ { .temp_c = 125, .ohm = 707 },
+};
+
+static const struct ntc_compensation ncpXXwf104[] = {
+ { .temp_c = -40, .ohm = 4397119 },
+ { .temp_c = -35, .ohm = 3088599 },
+ { .temp_c = -30, .ohm = 2197225 },
+ { .temp_c = -25, .ohm = 1581881 },
+ { .temp_c = -20, .ohm = 1151037 },
+ { .temp_c = -15, .ohm = 846579 },
+ { .temp_c = -10, .ohm = 628988 },
+ { .temp_c = -5, .ohm = 471632 },
+ { .temp_c = 0, .ohm = 357012 },
+ { .temp_c = 5, .ohm = 272500 },
+ { .temp_c = 10, .ohm = 209710 },
+ { .temp_c = 15, .ohm = 162651 },
+ { .temp_c = 20, .ohm = 127080 },
+ { .temp_c = 25, .ohm = 100000 },
+ { .temp_c = 30, .ohm = 79222 },
+ { .temp_c = 35, .ohm = 63167 },
+ { .temp_c = 40, .ohm = 50677 },
+ { .temp_c = 45, .ohm = 40904 },
+ { .temp_c = 50, .ohm = 33195 },
+ { .temp_c = 55, .ohm = 27091 },
+ { .temp_c = 60, .ohm = 22224 },
+ { .temp_c = 65, .ohm = 18323 },
+ { .temp_c = 70, .ohm = 15184 },
+ { .temp_c = 75, .ohm = 12635 },
+ { .temp_c = 80, .ohm = 10566 },
+ { .temp_c = 85, .ohm = 8873 },
+ { .temp_c = 90, .ohm = 7481 },
+ { .temp_c = 95, .ohm = 6337 },
+ { .temp_c = 100, .ohm = 5384 },
+ { .temp_c = 105, .ohm = 4594 },
+ { .temp_c = 110, .ohm = 3934 },
+ { .temp_c = 115, .ohm = 3380 },
+ { .temp_c = 120, .ohm = 2916 },
+ { .temp_c = 125, .ohm = 2522 },
+};
+
+static const struct ntc_compensation ncpXXxh103[] = {
+ { .temp_c = -40, .ohm = 247565 },
+ { .temp_c = -35, .ohm = 181742 },
+ { .temp_c = -30, .ohm = 135128 },
+ { .temp_c = -25, .ohm = 101678 },
+ { .temp_c = -20, .ohm = 77373 },
+ { .temp_c = -15, .ohm = 59504 },
+ { .temp_c = -10, .ohm = 46222 },
+ { .temp_c = -5, .ohm = 36244 },
+ { .temp_c = 0, .ohm = 28674 },
+ { .temp_c = 5, .ohm = 22878 },
+ { .temp_c = 10, .ohm = 18399 },
+ { .temp_c = 15, .ohm = 14910 },
+ { .temp_c = 20, .ohm = 12169 },
+ { .temp_c = 25, .ohm = 10000 },
+ { .temp_c = 30, .ohm = 8271 },
+ { .temp_c = 35, .ohm = 6883 },
+ { .temp_c = 40, .ohm = 5762 },
+ { .temp_c = 45, .ohm = 4851 },
+ { .temp_c = 50, .ohm = 4105 },
+ { .temp_c = 55, .ohm = 3492 },
+ { .temp_c = 60, .ohm = 2985 },
+ { .temp_c = 65, .ohm = 2563 },
+ { .temp_c = 70, .ohm = 2211 },
+ { .temp_c = 75, .ohm = 1915 },
+ { .temp_c = 80, .ohm = 1666 },
+ { .temp_c = 85, .ohm = 1454 },
+ { .temp_c = 90, .ohm = 1275 },
+ { .temp_c = 95, .ohm = 1121 },
+ { .temp_c = 100, .ohm = 990 },
+ { .temp_c = 105, .ohm = 876 },
+ { .temp_c = 110, .ohm = 779 },
+ { .temp_c = 115, .ohm = 694 },
+ { .temp_c = 120, .ohm = 620 },
+ { .temp_c = 125, .ohm = 556 },
+};
+
+/*
+ * The following compensation tables are from the specifications in EPCOS NTC
+ * Thermistors Datasheets
+ */
+static const struct ntc_compensation b57330v2103[] = {
+ { .temp_c = -40, .ohm = 190030 },
+ { .temp_c = -35, .ohm = 145360 },
+ { .temp_c = -30, .ohm = 112060 },
+ { .temp_c = -25, .ohm = 87041 },
+ { .temp_c = -20, .ohm = 68104 },
+ { .temp_c = -15, .ohm = 53665 },
+ { .temp_c = -10, .ohm = 42576 },
+ { .temp_c = -5, .ohm = 34001 },
+ { .temp_c = 0, .ohm = 27326 },
+ { .temp_c = 5, .ohm = 22096 },
+ { .temp_c = 10, .ohm = 17973 },
+ { .temp_c = 15, .ohm = 14703 },
+ { .temp_c = 20, .ohm = 12090 },
+ { .temp_c = 25, .ohm = 10000 },
+ { .temp_c = 30, .ohm = 8311 },
+ { .temp_c = 35, .ohm = 6941 },
+ { .temp_c = 40, .ohm = 5825 },
+ { .temp_c = 45, .ohm = 4911 },
+ { .temp_c = 50, .ohm = 4158 },
+ { .temp_c = 55, .ohm = 3536 },
+ { .temp_c = 60, .ohm = 3019 },
+ { .temp_c = 65, .ohm = 2588 },
+ { .temp_c = 70, .ohm = 2227 },
+ { .temp_c = 75, .ohm = 1924 },
+ { .temp_c = 80, .ohm = 1668 },
+ { .temp_c = 85, .ohm = 1451 },
+ { .temp_c = 90, .ohm = 1266 },
+ { .temp_c = 95, .ohm = 1108 },
+ { .temp_c = 100, .ohm = 973 },
+ { .temp_c = 105, .ohm = 857 },
+ { .temp_c = 110, .ohm = 757 },
+ { .temp_c = 115, .ohm = 671 },
+ { .temp_c = 120, .ohm = 596 },
+ { .temp_c = 125, .ohm = 531 },
+};
+
+static const struct ntc_compensation b57891s0103[] = {
+ { .temp_c = -55.0, .ohm = 878900 },
+ { .temp_c = -50.0, .ohm = 617590 },
+ { .temp_c = -45.0, .ohm = 439340 },
+ { .temp_c = -40.0, .ohm = 316180 },
+ { .temp_c = -35.0, .ohm = 230060 },
+ { .temp_c = -30.0, .ohm = 169150 },
+ { .temp_c = -25.0, .ohm = 125550 },
+ { .temp_c = -20.0, .ohm = 94143 },
+ { .temp_c = -15.0, .ohm = 71172 },
+ { .temp_c = -10.0, .ohm = 54308 },
+ { .temp_c = -5.0, .ohm = 41505 },
+ { .temp_c = 0.0, .ohm = 32014 },
+ { .temp_c = 5.0, .ohm = 25011 },
+ { .temp_c = 10.0, .ohm = 19691 },
+ { .temp_c = 15.0, .ohm = 15618 },
+ { .temp_c = 20.0, .ohm = 12474 },
+ { .temp_c = 25.0, .ohm = 10000 },
+ { .temp_c = 30.0, .ohm = 8080 },
+ { .temp_c = 35.0, .ohm = 6569 },
+ { .temp_c = 40.0, .ohm = 5372 },
+ { .temp_c = 45.0, .ohm = 4424 },
+ { .temp_c = 50.0, .ohm = 3661 },
+ { .temp_c = 55.0, .ohm = 3039 },
+ { .temp_c = 60.0, .ohm = 2536 },
+ { .temp_c = 65.0, .ohm = 2128 },
+ { .temp_c = 70.0, .ohm = 1794 },
+ { .temp_c = 75.0, .ohm = 1518 },
+ { .temp_c = 80.0, .ohm = 1290 },
+ { .temp_c = 85.0, .ohm = 1100 },
+ { .temp_c = 90.0, .ohm = 942 },
+ { .temp_c = 95.0, .ohm = 809 },
+ { .temp_c = 100.0, .ohm = 697 },
+ { .temp_c = 105.0, .ohm = 604 },
+ { .temp_c = 110.0, .ohm = 525 },
+ { .temp_c = 115.0, .ohm = 457 },
+ { .temp_c = 120.0, .ohm = 400 },
+ { .temp_c = 125.0, .ohm = 351 },
+ { .temp_c = 130.0, .ohm = 308 },
+ { .temp_c = 135.0, .ohm = 272 },
+ { .temp_c = 140.0, .ohm = 240 },
+ { .temp_c = 145.0, .ohm = 213 },
+ { .temp_c = 150.0, .ohm = 189 },
+ { .temp_c = 155.0, .ohm = 168 },
+};
+
+struct ntc_type {
+ const struct ntc_compensation *comp;
+ int n_comp;
+};
+
+#define NTC_TYPE(ntc, compensation) \
+[(ntc)] = { .comp = (compensation), .n_comp = ARRAY_SIZE(compensation) }
+
+static const struct ntc_type ntc_type[] = {
+ NTC_TYPE(TYPE_B57330V2103, b57330v2103),
+ NTC_TYPE(TYPE_B57891S0103, b57891s0103),
+ NTC_TYPE(TYPE_NCPXXWB473, ncpXXwb473),
+ NTC_TYPE(TYPE_NCPXXWF104, ncpXXwf104),
+ NTC_TYPE(TYPE_NCPXXWL333, ncpXXwl333),
+ 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 {
+ 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;
+};
+
+static int ntc_adc_iio_read(struct ntc_data *data)
+{
+ struct iio_channel *channel = data->chan;
+ int uv, ret;
+
+ ret = iio_read_channel_processed_scale(channel, &uv, 1000);
+ if (ret < 0) {
+ int raw;
+
+ /*
+ * 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 inline u64 div64_u64_safe(u64 dividend, u64 divisor)
+{
+ if (divisor == 0 && dividend == 0)
+ return 0;
+ if (divisor == 0)
+ return UINT_MAX;
+ return div64_u64(dividend, divisor);
+}
+
+static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
+{
+ u32 puv = data->pullup_uv;
+ u64 n, puo, pdo;
+ puo = data->pullup_ohm;
+ pdo = data->pulldown_ohm;
+
+ if (uv == 0)
+ return (data->connect == NTC_CONNECTED_POSITIVE) ?
+ INT_MAX : 0;
+ if (uv >= puv)
+ return (data->connect == NTC_CONNECTED_POSITIVE) ?
+ 0 : INT_MAX;
+
+ if (data->connect == NTC_CONNECTED_POSITIVE && puo == 0)
+ n = div_u64(pdo * (puv - uv), uv);
+ else if (data->connect == NTC_CONNECTED_GROUND && pdo == 0)
+ n = div_u64(puo * uv, puv - uv);
+ else if (data->connect == NTC_CONNECTED_POSITIVE)
+ n = div64_u64_safe(pdo * puo * (puv - uv),
+ puo * uv - pdo * (puv - uv));
+ else
+ n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
+
+ if (n > INT_MAX)
+ n = INT_MAX;
+ return n;
+}
+
+static void lookup_comp(struct ntc_data *data, unsigned int ohm,
+ int *i_low, int *i_high)
+{
+ int start, end, mid;
+
+ /*
+ * Handle special cases: Resistance is higher than or equal to
+ * resistance in first table entry, or resistance is lower or equal
+ * to resistance in last table entry.
+ * In these cases, return i_low == i_high, either pointing to the
+ * beginning or to the end of the table depending on the condition.
+ */
+ if (ohm >= data->comp[0].ohm) {
+ *i_low = 0;
+ *i_high = 0;
+ return;
+ }
+ if (ohm <= data->comp[data->n_comp - 1].ohm) {
+ *i_low = data->n_comp - 1;
+ *i_high = data->n_comp - 1;
+ return;
+ }
+
+ /* Do a binary search on compensation table */
+ start = 0;
+ end = data->n_comp;
+ while (start < end) {
+ mid = start + (end - start) / 2;
+ /*
+ * start <= mid < end
+ * data->comp[start].ohm > ohm >= data->comp[end].ohm
+ *
+ * We could check for "ohm == data->comp[mid].ohm" here, but
+ * that is a quite unlikely condition, and we would have to
+ * check again after updating start. Check it at the end instead
+ * for simplicity.
+ */
+ if (ohm >= data->comp[mid].ohm) {
+ end = mid;
+ } else {
+ start = mid + 1;
+ /*
+ * ohm >= data->comp[start].ohm might be true here,
+ * since we set start to mid + 1. In that case, we are
+ * done. We could keep going, but the condition is quite
+ * likely to occur, so it is worth checking for it.
+ */
+ if (ohm >= data->comp[start].ohm)
+ end = start;
+ }
+ /*
+ * start <= end
+ * data->comp[start].ohm >= ohm >= data->comp[end].ohm
+ */
+ }
+ /*
+ * start == end
+ * ohm >= data->comp[end].ohm
+ */
+ *i_low = end;
+ if (ohm == data->comp[end].ohm)
+ *i_high = end;
+ else
+ *i_high = end - 1;
+}
+
+static int get_temp_mc(struct ntc_data *data, unsigned int ohm)
+{
+ int low, high;
+ int temp;
+
+ lookup_comp(data, ohm, &low, &high);
+ /*
+ * 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;
+}
+
+static int ntc_thermistor_get_ohm(struct ntc_data *data)
+{
+ int read_uv;
+
+ 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,
+ u32 attr, int channel, long *val)
+{
+ struct ntc_data *data = dev_get_drvdata(dev);
+ int ohm;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ ohm = ntc_thermistor_get_ohm(data);
+ if (ohm < 0)
+ return ohm;
+ *val = get_temp_mc(data, ohm);
+ return 0;
+ case hwmon_temp_type:
+ *val = 4;
+ return 0;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static umode_t ntc_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_temp) {
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_type:
+ return 0444;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *ntc_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_TYPE),
+ NULL
+};
+
+static const struct hwmon_ops ntc_hwmon_ops = {
+ .is_visible = ntc_is_visible,
+ .read = ntc_read,
+};
+
+static const struct hwmon_chip_info ntc_chip_info = {
+ .ops = &ntc_hwmon_ops,
+ .info = ntc_info,
+};
+
+static int ntc_thermistor_parse_props(struct device *dev,
+ struct ntc_data *data)
+{
+ struct iio_channel *chan;
+ enum iio_chan_type type;
+ int ret;
+
+ chan = devm_iio_channel_get(dev, NULL);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ ret = iio_get_channel_type(chan, &type);
+ if (ret < 0)
+ return ret;
+
+ if (type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ ret = device_property_read_u32(dev, "pullup-uv", &data->pullup_uv);
+ if (ret)
+ return dev_err_probe(dev, ret, "pullup-uv not specified\n");
+
+ 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(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = ntc_thermistor_parse_props(dev, data);
+ if (ret)
+ return ret;
+
+ 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",
+ pdev_id->driver_data, pdev_id->name);
+ return -EINVAL;
+ }
+
+ data->comp = ntc_type[pdev_id->driver_data].comp;
+ data->n_comp = ntc_type[pdev_id->driver_data].n_comp;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, pdev_id->name,
+ data, &ntc_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev)) {
+ dev_err(dev, "unable to register as hwmon device.\n");
+ return PTR_ERR(hwmon_dev);
+ }
+
+ dev_info(dev, "Thermistor type: %s successfully probed.\n",
+ pdev_id->name);
+
+ 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 = ntc_match,
+ },
+ .probe = ntc_thermistor_probe,
+ .id_table = ntc_thermistor_id,
+};
+
+module_platform_driver(ntc_thermistor_driver);
+
+MODULE_DESCRIPTION("NTC Thermistor Driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ntc-thermistor");
diff --git a/drivers/hwmon/nzxt-kraken2.c b/drivers/hwmon/nzxt-kraken2.c
new file mode 100644
index 000000000..badbcaf01
--- /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);
+ return ret;
+ }
+
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev, "hid hw open failed with %d\n", ret);
+ goto fail_and_stop;
+ }
+
+ 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 000000000..a8e72d8fd
--- /dev/null
+++ b/drivers/hwmon/nzxt-smart2.c
@@ -0,0 +1,832 @@
+// 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 */
+ { HID_USB_DEVICE(0x1e71, 0x2011) }, /* NZXT RGB & Fan Controller (6 RGB) */
+ { HID_USB_DEVICE(0x1e71, 0x2019) }, /* NZXT RGB & Fan Controller (6 RGB) */
+ {},
+};
+
+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/Kconfig b/drivers/hwmon/occ/Kconfig
new file mode 100644
index 000000000..35a7070db
--- /dev/null
+++ b/drivers/hwmon/occ/Kconfig
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# On-Chip Controller configuration
+#
+
+config SENSORS_OCC_P8_I2C
+ tristate "POWER8 OCC through I2C"
+ depends on I2C
+ depends on ARM || ARM64 || COMPILE_TEST
+ select SENSORS_OCC
+ help
+ This option enables support for monitoring sensors provided by the
+ On-Chip Controller (OCC) on a POWER8 processor. However, this driver
+ can only run on a baseboard management controller (BMC) connected to
+ the P8, not the POWER processor itself. Communications with the OCC are
+ established through I2C bus.
+
+ This driver can also be built as a module. If so, the module will be
+ called occ-p8-hwmon.
+
+config SENSORS_OCC_P9_SBE
+ tristate "POWER9 OCC through SBE"
+ depends on FSI_OCC
+ depends on ARM || ARM64 || COMPILE_TEST
+ select SENSORS_OCC
+ help
+ This option enables support for monitoring sensors provided by the
+ On-Chip Controller (OCC) on a POWER9 processor. However, this driver
+ can only run on a baseboard management controller (BMC) connected to
+ the P9, not the POWER processor itself. Communications with the OCC are
+ established through SBE fifo on an FSI bus.
+
+ This driver can also be built as a module. If so, the module will be
+ called occ-p9-hwmon.
+
+config SENSORS_OCC
+ tristate
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
new file mode 100644
index 000000000..810061765
--- /dev/null
+++ b/drivers/hwmon/occ/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+occ-hwmon-common-objs := common.o sysfs.o
+occ-p8-hwmon-objs := p8_i2c.o
+occ-p9-hwmon-objs := p9_sbe.o
+
+obj-$(CONFIG_SENSORS_OCC) += occ-hwmon-common.o
+obj-$(CONFIG_SENSORS_OCC_P8_I2C) += occ-p8-hwmon.o
+obj-$(CONFIG_SENSORS_OCC_P9_SBE) += occ-p9-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
new file mode 100644
index 000000000..dd690f700
--- /dev/null
+++ b/drivers/hwmon/occ/common.c
@@ -0,0 +1,1251 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/sysfs.h>
+#include <asm/unaligned.h>
+
+#include "common.h"
+
+#define EXTN_FLAG_SENSOR_ID BIT(7)
+
+#define OCC_ERROR_COUNT_THRESHOLD 2 /* required by OCC spec */
+
+#define OCC_STATE_SAFE 4
+#define OCC_SAFE_TIMEOUT msecs_to_jiffies(60000) /* 1 min */
+
+#define OCC_UPDATE_FREQUENCY msecs_to_jiffies(1000)
+
+#define OCC_TEMP_SENSOR_FAULT 0xFF
+
+#define OCC_FRU_TYPE_VRM 3
+
+/* OCC sensor type and version definitions */
+
+struct temp_sensor_1 {
+ u16 sensor_id;
+ u16 value;
+} __packed;
+
+struct temp_sensor_2 {
+ u32 sensor_id;
+ u8 fru_type;
+ 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;
+} __packed;
+
+struct freq_sensor_2 {
+ u32 sensor_id;
+ u16 value;
+} __packed;
+
+struct power_sensor_1 {
+ u16 sensor_id;
+ u32 update_tag;
+ u32 accumulator;
+ u16 value;
+} __packed;
+
+struct power_sensor_2 {
+ u32 sensor_id;
+ u8 function_id;
+ u8 apss_channel;
+ u16 reserved;
+ u32 update_tag;
+ u64 accumulator;
+ u16 value;
+} __packed;
+
+struct power_sensor_data {
+ u16 value;
+ u32 update_tag;
+ u64 accumulator;
+} __packed;
+
+struct power_sensor_data_and_time {
+ u16 update_time;
+ u16 value;
+ u32 update_tag;
+ u64 accumulator;
+} __packed;
+
+struct power_sensor_a0 {
+ u32 sensor_id;
+ struct power_sensor_data_and_time system;
+ u32 reserved;
+ struct power_sensor_data_and_time proc;
+ struct power_sensor_data vdd;
+ struct power_sensor_data vdn;
+} __packed;
+
+struct caps_sensor_2 {
+ u16 cap;
+ u16 system_power;
+ u16 n_cap;
+ u16 max;
+ u16 min;
+ u16 user;
+ u8 user_source;
+} __packed;
+
+struct caps_sensor_3 {
+ u16 cap;
+ u16 system_power;
+ u16 n_cap;
+ u16 max;
+ u16 hard_min;
+ u16 soft_min;
+ u16 user;
+ u8 user_source;
+} __packed;
+
+struct extended_sensor {
+ union {
+ u8 name[4];
+ u32 sensor_id;
+ };
+ u8 flags;
+ u8 reserved;
+ u8 data[6];
+} __packed;
+
+static int occ_poll(struct occ *occ)
+{
+ int rc;
+ u8 cmd[7];
+ struct occ_poll_response_header *header;
+
+ /* big endian */
+ 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] = 0; /* checksum msb */
+ cmd[6] = 0; /* checksum lsb */
+
+ /* mutex should already be locked if necessary */
+ 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)
+ occ->error = rc;
+
+ goto done;
+ }
+
+ /* clear error since communication was successful */
+ occ->error_count = 0;
+ occ->last_error = 0;
+ occ->error = 0;
+
+ /* check for safe state */
+ header = (struct occ_poll_response_header *)occ->resp.data;
+ if (header->occ_state == OCC_STATE_SAFE) {
+ if (occ->last_safe) {
+ if (time_after(jiffies,
+ occ->last_safe + OCC_SAFE_TIMEOUT))
+ occ->error = -EHOSTDOWN;
+ } else {
+ occ->last_safe = jiffies;
+ }
+ } else {
+ occ->last_safe = 0;
+ }
+
+done:
+ occ_sysfs_poll_done(occ);
+ return rc;
+}
+
+static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
+{
+ int rc;
+ u8 cmd[8];
+ u8 resp[8];
+ __be16 user_power_cap_be = cpu_to_be16(user_power_cap);
+
+ 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);
+
+ 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, sizeof(cmd), resp, sizeof(resp));
+
+ mutex_unlock(&occ->lock);
+
+ return rc;
+}
+
+int occ_update_response(struct occ *occ)
+{
+ int rc = mutex_lock_interruptible(&occ->lock);
+
+ if (rc)
+ return rc;
+
+ /* limit the maximum rate of polling the OCC */
+ if (time_after(jiffies, occ->next_update)) {
+ rc = occ_poll(occ);
+ occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
+ } else {
+ rc = occ->last_error;
+ }
+
+ mutex_unlock(&occ->lock);
+ return rc;
+}
+
+static ssize_t occ_show_temp_1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u32 val = 0;
+ struct temp_sensor_1 *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_1 *)sensors->temp.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ val = get_unaligned_be16(&temp->sensor_id);
+ break;
+ case 1:
+ /*
+ * If a sensor reading has expired and couldn't be refreshed,
+ * OCC returns 0xFFFF for that sensor.
+ */
+ if (temp->value == 0xFFFF)
+ return -EREMOTEIO;
+ val = get_unaligned_be16(&temp->value) * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t occ_show_temp_2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u32 val = 0;
+ struct temp_sensor_2 *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_2 *)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;
+
+ /*
+ * VRM doesn't return temperature, only alarm bit. This
+ * attribute maps to tempX_alarm instead of tempX_input for
+ * VRM
+ */
+ if (temp->fru_type != OCC_FRU_TYPE_VRM) {
+ /* 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;
+ default:
+ return -EINVAL;
+ }
+
+ 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,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u16 val = 0;
+ struct freq_sensor_1 *freq;
+ 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;
+
+ freq = ((struct freq_sensor_1 *)sensors->freq.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ val = get_unaligned_be16(&freq->sensor_id);
+ break;
+ case 1:
+ val = get_unaligned_be16(&freq->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t occ_show_freq_2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u32 val = 0;
+ struct freq_sensor_2 *freq;
+ 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;
+
+ freq = ((struct freq_sensor_2 *)sensors->freq.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ val = get_unaligned_be32(&freq->sensor_id);
+ break;
+ case 1:
+ val = get_unaligned_be16(&freq->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t occ_show_power_1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u64 val = 0;
+ struct power_sensor_1 *power;
+ 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;
+
+ power = ((struct power_sensor_1 *)sensors->power.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ val = get_unaligned_be16(&power->sensor_id);
+ break;
+ case 1:
+ val = get_unaligned_be32(&power->accumulator) /
+ get_unaligned_be32(&power->update_tag);
+ val *= 1000000ULL;
+ break;
+ case 2:
+ val = (u64)get_unaligned_be32(&power->update_tag) *
+ occ->powr_sample_time_us;
+ break;
+ case 3:
+ val = get_unaligned_be16(&power->value) * 1000000ULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%llu\n", val);
+}
+
+static u64 occ_get_powr_avg(u64 *accum, u32 *samples)
+{
+ u64 divisor = get_unaligned_be32(samples);
+
+ return (divisor == 0) ? 0 :
+ div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor);
+}
+
+static ssize_t occ_show_power_2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u64 val = 0;
+ struct power_sensor_2 *power;
+ 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;
+
+ power = ((struct power_sensor_2 *)sensors->power.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ 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);
+ break;
+ case 2:
+ val = (u64)get_unaligned_be32(&power->update_tag) *
+ occ->powr_sample_time_us;
+ break;
+ case 3:
+ val = get_unaligned_be16(&power->value) * 1000000ULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%llu\n", val);
+}
+
+static ssize_t occ_show_power_a0(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u64 val = 0;
+ struct power_sensor_a0 *power;
+ 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;
+
+ power = ((struct power_sensor_a0 *)sensors->power.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ 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);
+ break;
+ case 2:
+ val = (u64)get_unaligned_be32(&power->system.update_tag) *
+ occ->powr_sample_time_us;
+ break;
+ case 3:
+ val = get_unaligned_be16(&power->system.value) * 1000000ULL;
+ break;
+ case 4:
+ 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);
+ break;
+ case 6:
+ val = (u64)get_unaligned_be32(&power->proc.update_tag) *
+ occ->powr_sample_time_us;
+ break;
+ case 7:
+ val = get_unaligned_be16(&power->proc.value) * 1000000ULL;
+ break;
+ case 8:
+ 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);
+ break;
+ case 10:
+ val = (u64)get_unaligned_be32(&power->vdd.update_tag) *
+ occ->powr_sample_time_us;
+ break;
+ case 11:
+ val = get_unaligned_be16(&power->vdd.value) * 1000000ULL;
+ break;
+ case 12:
+ 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);
+ break;
+ case 14:
+ val = (u64)get_unaligned_be32(&power->vdn.update_tag) *
+ occ->powr_sample_time_us;
+ break;
+ case 15:
+ val = get_unaligned_be16(&power->vdn.value) * 1000000ULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%llu\n", val);
+}
+
+static ssize_t occ_show_caps_1_2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u64 val = 0;
+ struct caps_sensor_2 *caps;
+ 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;
+
+ caps = ((struct caps_sensor_2 *)sensors->caps.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ return sysfs_emit(buf, "system\n");
+ case 1:
+ val = get_unaligned_be16(&caps->cap) * 1000000ULL;
+ break;
+ case 2:
+ val = get_unaligned_be16(&caps->system_power) * 1000000ULL;
+ break;
+ case 3:
+ val = get_unaligned_be16(&caps->n_cap) * 1000000ULL;
+ break;
+ case 4:
+ val = get_unaligned_be16(&caps->max) * 1000000ULL;
+ break;
+ case 5:
+ val = get_unaligned_be16(&caps->min) * 1000000ULL;
+ break;
+ case 6:
+ val = get_unaligned_be16(&caps->user) * 1000000ULL;
+ break;
+ case 7:
+ if (occ->sensors.caps.version == 1)
+ return -EINVAL;
+
+ val = caps->user_source;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%llu\n", val);
+}
+
+static ssize_t occ_show_caps_3(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ u64 val = 0;
+ struct caps_sensor_3 *caps;
+ 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;
+
+ caps = ((struct caps_sensor_3 *)sensors->caps.data) + sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ return sysfs_emit(buf, "system\n");
+ case 1:
+ val = get_unaligned_be16(&caps->cap) * 1000000ULL;
+ break;
+ case 2:
+ val = get_unaligned_be16(&caps->system_power) * 1000000ULL;
+ break;
+ case 3:
+ val = get_unaligned_be16(&caps->n_cap) * 1000000ULL;
+ break;
+ case 4:
+ val = get_unaligned_be16(&caps->max) * 1000000ULL;
+ break;
+ case 5:
+ val = get_unaligned_be16(&caps->hard_min) * 1000000ULL;
+ break;
+ case 6:
+ val = get_unaligned_be16(&caps->user) * 1000000ULL;
+ break;
+ case 7:
+ val = caps->user_source;
+ break;
+ case 8:
+ val = get_unaligned_be16(&caps->soft_min) * 1000000ULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%llu\n", val);
+}
+
+static ssize_t occ_store_caps_user(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ u16 user_power_cap;
+ unsigned long long value;
+ struct occ *occ = dev_get_drvdata(dev);
+
+ rc = kstrtoull(buf, 0, &value);
+ if (rc)
+ return rc;
+
+ user_power_cap = div64_u64(value, 1000000ULL); /* microwatt to watt */
+
+ rc = occ_set_user_power_cap(occ, user_power_cap);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
+static ssize_t occ_show_extended(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ struct extended_sensor *extn;
+ 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;
+
+ extn = ((struct extended_sensor *)sensors->extended.data) +
+ sattr->index;
+
+ switch (sattr->nr) {
+ case 0:
+ 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 = sysfs_emit(buf, "%02x\n", extn->flags);
+ break;
+ case 2:
+ rc = sysfs_emit(buf, "%6phN\n", extn->data);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+/*
+ * Some helper macros to make it easier to define an occ_attribute. Since these
+ * are dynamically allocated, we shouldn't use the existing kernel macros which
+ * stringify the name argument.
+ */
+#define ATTR_OCC(_name, _mode, _show, _store) { \
+ .attr = { \
+ .name = _name, \
+ .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \
+ }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \
+ .dev_attr = ATTR_OCC(_name, _mode, _show, _store), \
+ .index = _index, \
+ .nr = _nr, \
+}
+
+#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \
+ ((struct sensor_device_attribute_2) \
+ SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index))
+
+/*
+ * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
+ * use our own instead of the built-in hwmon attribute types.
+ */
+static int occ_setup_sensor_attrs(struct occ *occ)
+{
+ unsigned int i, s, num_attrs = 0;
+ struct device *dev = occ->bus_dev;
+ struct occ_sensors *sensors = &occ->sensors;
+ struct occ_attribute *attr;
+ struct temp_sensor_2 *temp;
+ ssize_t (*show_temp)(struct device *, struct device_attribute *,
+ char *) = occ_show_temp_1;
+ ssize_t (*show_freq)(struct device *, struct device_attribute *,
+ char *) = occ_show_freq_1;
+ ssize_t (*show_power)(struct device *, struct device_attribute *,
+ char *) = occ_show_power_1;
+ ssize_t (*show_caps)(struct device *, struct device_attribute *,
+ char *) = occ_show_caps_1_2;
+
+ switch (sensors->temp.version) {
+ case 1:
+ num_attrs += (sensors->temp.num_sensors * 2);
+ break;
+ case 2:
+ 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;
+ }
+
+ switch (sensors->freq.version) {
+ case 2:
+ show_freq = occ_show_freq_2;
+ fallthrough;
+ case 1:
+ num_attrs += (sensors->freq.num_sensors * 2);
+ break;
+ default:
+ sensors->freq.num_sensors = 0;
+ }
+
+ switch (sensors->power.version) {
+ case 2:
+ show_power = occ_show_power_2;
+ fallthrough;
+ case 1:
+ num_attrs += (sensors->power.num_sensors * 4);
+ break;
+ case 0xA0:
+ num_attrs += (sensors->power.num_sensors * 16);
+ show_power = occ_show_power_a0;
+ break;
+ default:
+ sensors->power.num_sensors = 0;
+ }
+
+ switch (sensors->caps.version) {
+ case 1:
+ num_attrs += (sensors->caps.num_sensors * 7);
+ break;
+ 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;
+ }
+
+ switch (sensors->extended.version) {
+ case 1:
+ num_attrs += (sensors->extended.num_sensors * 3);
+ break;
+ default:
+ sensors->extended.num_sensors = 0;
+ }
+
+ occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs,
+ GFP_KERNEL);
+ if (!occ->attrs)
+ return -ENOMEM;
+
+ /* null-terminated list */
+ occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) *
+ num_attrs + 1, GFP_KERNEL);
+ if (!occ->group.attrs)
+ return -ENOMEM;
+
+ attr = occ->attrs;
+
+ for (i = 0; i < sensors->temp.num_sensors; ++i) {
+ s = i + 1;
+ temp = ((struct temp_sensor_2 *)sensors->temp.data) + i;
+
+ snprintf(attr->name, sizeof(attr->name), "temp%d_label", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
+ 0, i);
+ attr++;
+
+ if (sensors->temp.version == 2 &&
+ temp->fru_type == OCC_FRU_TYPE_VRM) {
+ snprintf(attr->name, sizeof(attr->name),
+ "temp%d_alarm", s);
+ } else {
+ snprintf(attr->name, sizeof(attr->name),
+ "temp%d_input", s);
+ }
+
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
+ 1, i);
+ attr++;
+
+ if (sensors->temp.version > 1) {
+ snprintf(attr->name, sizeof(attr->name),
+ "temp%d_fru_type", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_temp, NULL, 2, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "temp%d_fault", s);
+ 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++;
+ }
+ }
+ }
+
+ for (i = 0; i < sensors->freq.num_sensors; ++i) {
+ s = i + 1;
+
+ snprintf(attr->name, sizeof(attr->name), "freq%d_label", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
+ 0, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "freq%d_input", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
+ 1, i);
+ attr++;
+ }
+
+ if (sensors->power.version == 0xA0) {
+ /*
+ * Special case for many-attribute power sensor. Split it into
+ * a sensor number per power type, emulating several sensors.
+ */
+ for (i = 0; i < sensors->power.num_sensors; ++i) {
+ unsigned int j;
+ unsigned int nr = 0;
+
+ s = (i * 4) + 1;
+
+ for (j = 0; j < 4; ++j) {
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_label", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL,
+ nr++, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_average", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL,
+ nr++, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_average_interval", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL,
+ nr++, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_input", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL,
+ nr++, i);
+ attr++;
+
+ s++;
+ }
+ }
+
+ s = (sensors->power.num_sensors * 4) + 1;
+ } else {
+ for (i = 0; i < sensors->power.num_sensors; ++i) {
+ s = i + 1;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_label", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL, 0, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_average", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL, 1, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_average_interval", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL, 2, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_input", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_power, NULL, 3, i);
+ attr++;
+ }
+
+ s = sensors->power.num_sensors + 1;
+ }
+
+ if (sensors->caps.num_sensors >= 1) {
+ snprintf(attr->name, sizeof(attr->name), "power%d_label", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+ 0, 0);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "power%d_cap", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+ 1, 0);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "power%d_input", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+ 2, 0);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_cap_not_redundant", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+ 3, 0);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+ 4, 0);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+ 5, 0);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "power%d_cap_user",
+ s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps,
+ occ_store_caps_user, 6, 0);
+ attr++;
+
+ if (sensors->caps.version > 1) {
+ snprintf(attr->name, sizeof(attr->name),
+ "power%d_cap_user_source", s);
+ 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++;
+ }
+ }
+ }
+
+ for (i = 0; i < sensors->extended.num_sensors; ++i) {
+ s = i + 1;
+
+ snprintf(attr->name, sizeof(attr->name), "extn%d_label", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ occ_show_extended, NULL, 0, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ occ_show_extended, NULL, 1, i);
+ attr++;
+
+ snprintf(attr->name, sizeof(attr->name), "extn%d_input", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ occ_show_extended, NULL, 2, i);
+ attr++;
+ }
+
+ /* put the sensors in the group */
+ for (i = 0; i < num_attrs; ++i) {
+ sysfs_attr_init(&occ->attrs[i].sensor.dev_attr.attr);
+ occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr;
+ }
+
+ return 0;
+}
+
+/* only need to do this once at startup, as OCC won't change sensors on us */
+static void occ_parse_poll_response(struct occ *occ)
+{
+ unsigned int i, old_offset, offset = 0, size = 0;
+ struct occ_sensor *sensor;
+ struct occ_sensors *sensors = &occ->sensors;
+ struct occ_response *resp = &occ->resp;
+ struct occ_poll_response *poll =
+ (struct occ_poll_response *)&resp->data[0];
+ struct occ_poll_response_header *header = &poll->header;
+ struct occ_sensor_data_block *block = &poll->block;
+
+ dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
+ header->occ_code_level);
+
+ for (i = 0; i < header->num_sensor_data_blocks; ++i) {
+ block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+ old_offset = offset;
+ offset = (block->header.num_sensors *
+ block->header.sensor_length) + sizeof(block->header);
+ size += offset;
+
+ /* validate all the length/size fields */
+ if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
+ dev_warn(occ->bus_dev, "exceeded response buffer\n");
+ return;
+ }
+
+ dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
+ old_offset, offset - 1, block->header.eye_catcher,
+ block->header.num_sensors);
+
+ /* match sensor block type */
+ if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
+ sensor = &sensors->temp;
+ else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
+ sensor = &sensors->freq;
+ else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
+ sensor = &sensors->power;
+ else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
+ sensor = &sensors->caps;
+ else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
+ sensor = &sensors->extended;
+ else {
+ dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
+ block->header.eye_catcher);
+ continue;
+ }
+
+ sensor->num_sensors = block->header.num_sensors;
+ sensor->version = block->header.sensor_format;
+ sensor->data = &block->data;
+ }
+
+ dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
+ sizeof(*header), size + sizeof(*header));
+}
+
+int occ_active(struct occ *occ, bool active)
+{
+ int rc = mutex_lock_interruptible(&occ->lock);
+
+ 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;
+ }
+
+unlock:
+ mutex_unlock(&occ->lock);
+ return rc;
+}
+
+int occ_setup(struct occ *occ)
+{
+ int rc;
+
+ mutex_init(&occ->lock);
+ occ->groups[0] = &occ->group;
+
+ rc = occ_setup_sysfs(occ);
+ if (rc) {
+ dev_err(occ->bus_dev, "failed to setup sysfs: %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);
+ }
+
+ 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
new file mode 100644
index 000000000..7ac4b2feb
--- /dev/null
+++ b/drivers/hwmon/occ/common.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright IBM Corp 2019 */
+
+#ifndef OCC_COMMON_H
+#define OCC_COMMON_H
+
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+struct device;
+
+#define OCC_RESP_DATA_BYTES 4089
+
+/*
+ * Same response format for all OCC versions.
+ * Allocate the largest possible response.
+ */
+struct occ_response {
+ u8 seq_no;
+ u8 cmd_type;
+ u8 return_status;
+ __be16 data_length;
+ u8 data[OCC_RESP_DATA_BYTES];
+ __be16 checksum;
+} __packed;
+
+struct occ_sensor_data_block_header {
+ u8 eye_catcher[4];
+ u8 reserved;
+ u8 sensor_format;
+ u8 sensor_length;
+ u8 num_sensors;
+} __packed;
+
+struct occ_sensor_data_block {
+ struct occ_sensor_data_block_header header;
+ u32 data;
+} __packed;
+
+struct occ_poll_response_header {
+ u8 status;
+ u8 ext_status;
+ u8 occs_present;
+ u8 config_data;
+ u8 occ_state;
+ u8 mode;
+ u8 ips_status;
+ u8 error_log_id;
+ __be32 error_log_start_address;
+ __be16 error_log_length;
+ u16 reserved;
+ u8 occ_code_level[16];
+ u8 eye_catcher[6];
+ u8 num_sensor_data_blocks;
+ u8 sensor_data_block_header_version;
+} __packed;
+
+struct occ_poll_response {
+ struct occ_poll_response_header header;
+ struct occ_sensor_data_block block;
+} __packed;
+
+struct occ_sensor {
+ u8 num_sensors;
+ u8 version;
+ void *data; /* pointer to sensor data start within response */
+};
+
+/*
+ * OCC only provides one sensor data block of each type, but any number of
+ * sensors within that block.
+ */
+struct occ_sensors {
+ struct occ_sensor temp;
+ struct occ_sensor freq;
+ struct occ_sensor power;
+ struct occ_sensor caps;
+ struct occ_sensor extended;
+};
+
+/*
+ * Use our own attribute struct so we can dynamically allocate space for the
+ * name.
+ */
+struct occ_attribute {
+ char name[32];
+ struct sensor_device_attribute_2 sensor;
+};
+
+struct occ {
+ struct device *bus_dev;
+
+ struct occ_response resp;
+ struct occ_sensors sensors;
+
+ int powr_sample_time_us; /* average power sample time */
+ u8 poll_cmd_data; /* to perform OCC poll command */
+ int (*send_cmd)(struct occ *occ, u8 *cmd, size_t len, void *resp,
+ size_t resp_len);
+
+ unsigned long next_update;
+ struct mutex lock; /* lock OCC access */
+
+ struct device *hwmon;
+ struct occ_attribute *attrs;
+ 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 */
+ unsigned long last_safe; /* time OCC entered "safe" state */
+
+ /*
+ * Store the previous state data for comparison in order to notify
+ * sysfs readers of state changes.
+ */
+ int prev_error;
+ u8 prev_stat;
+ u8 prev_ext_stat;
+ u8 prev_occs_present;
+ u8 prev_ips_status;
+ u8 prev_mode;
+};
+
+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);
+
+#endif /* OCC_COMMON_H */
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
new file mode 100644
index 000000000..9e1744fcc
--- /dev/null
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fsi-occ.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>
+
+#include "common.h"
+
+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+/* OCB (on-chip control bridge - interface to OCC) registers */
+#define OCB_DATA1 0x6B035
+#define OCB_ADDR 0x6B070
+#define OCB_DATA3 0x6B075
+
+/* OCC SRAM address space */
+#define OCC_SRAM_ADDR_CMD 0xFFFF6000
+#define OCC_SRAM_ADDR_RESP 0xFFFF7000
+
+#define OCC_DATA_ATTN 0x20010000
+
+struct p8_i2c_occ {
+ struct occ occ;
+ struct i2c_client *client;
+};
+
+#define to_p8_i2c_occ(x) container_of((x), struct p8_i2c_occ, occ)
+
+static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data)
+{
+ ssize_t rc;
+ __be64 buf;
+ struct i2c_msg msgs[2];
+
+ /* p8 i2c slave requires shift */
+ address <<= 1;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = sizeof(u32);
+ /* address is a scom address; bus-endian */
+ msgs[0].buf = (char *)&address;
+
+ /* data from OCC is big-endian */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+ msgs[1].len = sizeof(u64);
+ msgs[1].buf = (char *)&buf;
+
+ rc = i2c_transfer(client->adapter, msgs, 2);
+ if (rc < 0)
+ return rc;
+
+ *(u64 *)data = be64_to_cpu(buf);
+
+ return 0;
+}
+
+static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
+{
+ u32 buf[3];
+ ssize_t rc;
+
+ /* p8 i2c slave requires shift */
+ address <<= 1;
+
+ /* address is bus-endian; data passed through from user as-is */
+ buf[0] = address;
+ memcpy(&buf[1], &data[4], sizeof(u32));
+ memcpy(&buf[2], data, sizeof(u32));
+
+ rc = i2c_master_send(client, (const char *)buf, sizeof(buf));
+ if (rc < 0)
+ return rc;
+ else if (rc != sizeof(buf))
+ return -EIO;
+
+ return 0;
+}
+
+static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
+ u32 data0, u32 data1)
+{
+ u8 buf[8];
+
+ memcpy(buf, &data0, 4);
+ memcpy(buf + 4, &data1, 4);
+
+ return p8_i2c_occ_putscom(client, address, buf);
+}
+
+static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
+ u8 *data, size_t len)
+{
+ __be32 data0 = 0, data1 = 0;
+
+ 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, size_t len,
+ void *resp, size_t resp_len)
+{
+ int i, rc;
+ unsigned long start;
+ u16 data_length;
+ const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
+ 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 *or = (struct occ_response *)resp;
+
+ start = jiffies;
+
+ /* set sram address for command */
+ rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, 0);
+ if (rc)
+ return rc;
+
+ /* write command (expected to already be BE), we need bus-endian... */
+ rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd, len);
+ if (rc)
+ return rc;
+
+ /* trigger OCC attention */
+ rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, 0);
+ if (rc)
+ return rc;
+
+ do {
+ /* set sram address for response */
+ rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR,
+ OCC_SRAM_ADDR_RESP, 0);
+ if (rc)
+ return rc;
+
+ rc = p8_i2c_occ_getscom(client, OCB_DATA3, (u8 *)resp);
+ if (rc)
+ return rc;
+
+ /* wait for OCC */
+ if (or->return_status == OCC_RESP_CMD_IN_PRG) {
+ rc = -EALREADY;
+
+ if (time_after(jiffies, start + timeout))
+ break;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(wait_time);
+ }
+ } while (rc);
+
+ /* check the OCC response */
+ switch (or->return_status) {
+ case OCC_RESP_CMD_IN_PRG:
+ rc = -ETIMEDOUT;
+ break;
+ case OCC_RESP_SUCCESS:
+ rc = 0;
+ break;
+ case OCC_RESP_CMD_INVAL:
+ case OCC_RESP_CMD_LEN_INVAL:
+ case OCC_RESP_DATA_INVAL:
+ case OCC_RESP_CHKSUM_ERR:
+ rc = -EINVAL;
+ break;
+ case OCC_RESP_INT_ERR:
+ case OCC_RESP_BAD_STATE:
+ case OCC_RESP_CRIT_EXCEPT:
+ case OCC_RESP_CRIT_INIT:
+ case OCC_RESP_CRIT_WATCHDOG:
+ case OCC_RESP_CRIT_OCB:
+ case OCC_RESP_CRIT_HW:
+ rc = -EREMOTEIO;
+ break;
+ default:
+ rc = -EPROTO;
+ }
+
+ if (rc < 0)
+ return rc;
+
+ data_length = get_unaligned_be16(&or->data_length);
+ if ((data_length + 7) > resp_len)
+ return -EMSGSIZE;
+
+ /* fetch the rest of the response data */
+ for (i = 8; i < data_length + 7; i += 8) {
+ rc = p8_i2c_occ_getscom(client, OCB_DATA3, ((u8 *)resp) + i);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int p8_i2c_occ_probe(struct i2c_client *client)
+{
+ struct occ *occ;
+ struct p8_i2c_occ *ctx = devm_kzalloc(&client->dev, sizeof(*ctx),
+ GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->client = client;
+ occ = &ctx->occ;
+ occ->bus_dev = &client->dev;
+ dev_set_drvdata(&client->dev, occ);
+
+ occ->powr_sample_time_us = 250;
+ occ->poll_cmd_data = 0x10; /* P8 OCC poll data */
+ occ->send_cmd = p8_i2c_occ_send_cmd;
+
+ return occ_setup(occ);
+}
+
+static void p8_i2c_occ_remove(struct i2c_client *client)
+{
+ struct occ *occ = dev_get_drvdata(&client->dev);
+
+ occ_shutdown(occ);
+}
+
+static const struct of_device_id p8_i2c_occ_of_match[] = {
+ { .compatible = "ibm,p8-occ-hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, p8_i2c_occ_of_match);
+
+static struct i2c_driver p8_i2c_occ_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "occ-hwmon",
+ .of_match_table = p8_i2c_occ_of_match,
+ },
+ .probe_new = p8_i2c_occ_probe,
+ .remove = p8_i2c_occ_remove,
+};
+
+module_i2c_driver(p8_i2c_occ_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
+MODULE_DESCRIPTION("BMC P8 OCC hwmon driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
new file mode 100644
index 000000000..96521363b
--- /dev/null
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
+
+#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 ssize_t ffdc_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *battr, char *buf, loff_t pos,
+ size_t count)
+{
+ ssize_t rc = 0;
+ struct occ *occ = dev_get_drvdata(kobj_to_dev(kobj));
+ struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
+
+ 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 (((struct occ_response *)resp)->return_status) {
+ case OCC_RESP_CMD_IN_PRG:
+ rc = -ETIMEDOUT;
+ break;
+ case OCC_RESP_SUCCESS:
+ rc = 0;
+ break;
+ case OCC_RESP_CMD_INVAL:
+ case OCC_RESP_CMD_LEN_INVAL:
+ case OCC_RESP_DATA_INVAL:
+ case OCC_RESP_CHKSUM_ERR:
+ rc = -EINVAL;
+ break;
+ case OCC_RESP_INT_ERR:
+ case OCC_RESP_BAD_STATE:
+ case OCC_RESP_CRIT_EXCEPT:
+ case OCC_RESP_CRIT_INIT:
+ case OCC_RESP_CRIT_WATCHDOG:
+ case OCC_RESP_CRIT_OCB:
+ case OCC_RESP_CRIT_HW:
+ rc = -EREMOTEIO;
+ break;
+ default:
+ rc = -EPROTO;
+ }
+
+ return rc;
+}
+
+static int p9_sbe_occ_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct occ *occ;
+ struct p9_sbe_occ *ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx),
+ GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ mutex_init(&ctx->sbe_error_lock);
+
+ ctx->sbe = pdev->dev.parent;
+ occ = &ctx->occ;
+ occ->bus_dev = &pdev->dev;
+ platform_set_drvdata(pdev, occ);
+
+ occ->powr_sample_time_us = 500;
+ occ->poll_cmd_data = 0x20; /* P9 OCC poll data */
+ occ->send_cmd = p9_sbe_occ_send_cmd;
+
+ 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;
+}
+
+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,
+};
+
+module_platform_driver(p9_sbe_occ_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
+MODULE_DESCRIPTION("BMC P9 OCC hwmon driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
new file mode 100644
index 000000000..2317301fc
--- /dev/null
+++ b/drivers/hwmon/occ/sysfs.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#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)
+
+/* 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)
+{
+ int rc;
+ int val = 0;
+ struct occ *occ = dev_get_drvdata(dev);
+ struct occ_poll_response_header *header;
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+ 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 = 1;
+ 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 sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t occ_error_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct occ *occ = dev_get_drvdata(dev);
+
+ occ_update_response(occ);
+
+ 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, 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[] = {
+ &sensor_dev_attr_occ_master.dev_attr.attr,
+ &sensor_dev_attr_occ_active.dev_attr.attr,
+ &sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr,
+ &sensor_dev_attr_occ_dvfs_power.dev_attr.attr,
+ &sensor_dev_attr_occ_mem_throttle.dev_attr.attr,
+ &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
+};
+
+static const struct attribute_group occ_sysfs = {
+ .attrs = occ_attributes,
+};
+
+void occ_sysfs_poll_done(struct occ *occ)
+{
+ const char *name;
+ struct occ_poll_response_header *header =
+ (struct occ_poll_response_header *)occ->resp.data;
+
+ /*
+ * On the first poll response, we haven't yet created the sysfs
+ * attributes, so don't make any notify calls.
+ */
+ if (!occ->active)
+ goto done;
+
+ if ((header->status & OCC_STAT_MASTER) !=
+ (occ->prev_stat & OCC_STAT_MASTER)) {
+ name = sensor_dev_attr_occ_master.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;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+ if ((header->ext_status & OCC_EXT_STAT_DVFS_POWER) !=
+ (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_POWER)) {
+ name = sensor_dev_attr_occ_dvfs_power.dev_attr.attr.name;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+ if ((header->ext_status & OCC_EXT_STAT_MEM_THROTTLE) !=
+ (occ->prev_ext_stat & OCC_EXT_STAT_MEM_THROTTLE)) {
+ name = sensor_dev_attr_occ_mem_throttle.dev_attr.attr.name;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+ if ((header->ext_status & OCC_EXT_STAT_QUICK_DROP) !=
+ (occ->prev_ext_stat & OCC_EXT_STAT_QUICK_DROP)) {
+ name = sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr.name;
+ 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);
+ }
+
+ /* no notifications for OCC state; doesn't indicate error condition */
+
+done:
+ occ->prev_error = occ->error;
+ 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)
+{
+ return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs);
+}
+
+void occ_shutdown_sysfs(struct occ *occ)
+{
+ sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs);
+}
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
new file mode 100644
index 000000000..a4adc8bd5
--- /dev/null
+++ b/drivers/hwmon/pc87360.c
@@ -0,0 +1,1803 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pc87360.c - Part of lm_sensors, Linux kernel modules
+ * for hardware monitoring
+ * Copyright (C) 2004, 2007 Jean Delvare <jdelvare@suse.de>
+ *
+ * Copied from smsc47m1.c:
+ * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ *
+ * Supports the following chips:
+ *
+ * Chip #vin #fan #pwm #temp devid
+ * PC87360 - 2 2 - 0xE1
+ * PC87363 - 2 2 - 0xE8
+ * PC87364 - 3 3 - 0xE4
+ * PC87365 11 3 3 2 0xE5
+ * PC87366 11 3 3 3-4 0xE9
+ *
+ * This driver assumes that no more than one chip is present, and one of
+ * the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F).
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.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/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#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];
+static u8 confreg[4];
+
+static int init = 1;
+module_param(init, int, 0);
+MODULE_PARM_DESC(init,
+"Chip initialization level:\n"
+" 0: None\n"
+"*1: Forcibly enable internal voltage and temperature channels, except in9\n"
+" 2: Forcibly enable all voltage and temperature channels, except in9\n"
+" 3: Forcibly enable all voltage and temperature channels, including in9");
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+/*
+ * Super-I/O registers and operations
+ */
+
+#define DEV 0x07 /* Register: Logical device select */
+#define DEVID 0x20 /* Register: Device ID */
+#define ACT 0x30 /* Register: Device activation */
+#define BASE 0x60 /* Register: Base address */
+
+#define FSCM 0x09 /* Logical device: fans */
+#define VLM 0x0d /* Logical device: voltages */
+#define TMS 0x0e /* Logical device: temperatures */
+#define LDNI_MAX 3
+static const u8 logdev[LDNI_MAX] = { FSCM, VLM, TMS };
+
+#define LD_FAN 0
+#define LD_IN 1
+#define LD_TEMP 2
+
+static inline void superio_outb(int sioaddr, int reg, int val)
+{
+ outb(reg, sioaddr);
+ outb(val, sioaddr + 1);
+}
+
+static inline int superio_inb(int sioaddr, int reg)
+{
+ outb(reg, sioaddr);
+ return inb(sioaddr + 1);
+}
+
+static inline void superio_exit(int sioaddr)
+{
+ outb(0x02, sioaddr);
+ outb(0x02, sioaddr + 1);
+}
+
+/*
+ * Logical devices
+ */
+
+#define PC87360_EXTENT 0x10
+#define PC87365_REG_BANK 0x09
+#define NO_BANK 0xff
+
+/*
+ * Fan registers and conversions
+ */
+
+/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */
+#define PC87360_REG_PRESCALE(nr) (0x00 + 2 * (nr))
+#define PC87360_REG_PWM(nr) (0x01 + 2 * (nr))
+#define PC87360_REG_FAN_MIN(nr) (0x06 + 3 * (nr))
+#define PC87360_REG_FAN(nr) (0x07 + 3 * (nr))
+#define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr))
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : \
+ 480000 / ((val) * (div)))
+#define FAN_TO_REG(val, div) ((val) <= 100 ? 0 : \
+ 480000 / ((val) * (div)))
+#define FAN_DIV_FROM_REG(val) (1 << (((val) >> 5) & 0x03))
+#define FAN_STATUS_FROM_REG(val) ((val) & 0x07)
+
+#define FAN_CONFIG_MONITOR(val, nr) (((val) >> (2 + (nr) * 3)) & 1)
+#define FAN_CONFIG_CONTROL(val, nr) (((val) >> (3 + (nr) * 3)) & 1)
+#define FAN_CONFIG_INVERT(val, nr) (((val) >> (4 + (nr) * 3)) & 1)
+
+#define PWM_FROM_REG(val, inv) ((inv) ? 255 - (val) : (val))
+static inline u8 PWM_TO_REG(int val, int inv)
+{
+ if (inv)
+ val = 255 - val;
+ if (val < 0)
+ return 0;
+ if (val > 255)
+ return 255;
+ return val;
+}
+
+/*
+ * Voltage registers and conversions
+ */
+
+#define PC87365_REG_IN_CONVRATE 0x07
+#define PC87365_REG_IN_CONFIG 0x08
+#define PC87365_REG_IN 0x0B
+#define PC87365_REG_IN_MIN 0x0D
+#define PC87365_REG_IN_MAX 0x0C
+#define PC87365_REG_IN_STATUS 0x0A
+#define PC87365_REG_IN_ALARMS1 0x00
+#define PC87365_REG_IN_ALARMS2 0x01
+#define PC87365_REG_VID 0x06
+
+#define IN_FROM_REG(val, ref) (((val) * (ref) + 128) / 256)
+#define IN_TO_REG(val, ref) ((val) < 0 ? 0 : \
+ (val) * 256 >= (ref) * 255 ? 255 : \
+ ((val) * 256 + (ref) / 2) / (ref))
+
+/*
+ * Temperature registers and conversions
+ */
+
+#define PC87365_REG_TEMP_CONFIG 0x08
+#define PC87365_REG_TEMP 0x0B
+#define PC87365_REG_TEMP_MIN 0x0D
+#define PC87365_REG_TEMP_MAX 0x0C
+#define PC87365_REG_TEMP_CRIT 0x0E
+#define PC87365_REG_TEMP_STATUS 0x0A
+#define PC87365_REG_TEMP_ALARMS 0x00
+
+#define TEMP_FROM_REG(val) ((val) * 1000)
+#define TEMP_TO_REG(val) ((val) < -55000 ? -55 : \
+ (val) > 127000 ? 127 : \
+ (val) < 0 ? ((val) - 500) / 1000 : \
+ ((val) + 500) / 1000)
+
+/*
+ * Device data
+ */
+
+struct pc87360_data {
+ const char *name;
+ struct device *hwmon_dev;
+ struct mutex lock;
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ int address[3];
+
+ u8 fannr, innr, tempnr;
+
+ u8 fan[3]; /* Register value */
+ u8 fan_min[3]; /* Register value */
+ u8 fan_status[3]; /* Register value */
+ u8 pwm[3]; /* Register value */
+ u16 fan_conf; /* Configuration register values, combined */
+
+ u16 in_vref; /* 1 mV/bit */
+ u8 in[14]; /* Register value */
+ u8 in_min[14]; /* Register value */
+ u8 in_max[14]; /* Register value */
+ u8 in_crit[3]; /* Register value */
+ u8 in_status[14]; /* Register value */
+ u16 in_alarms; /* Register values, combined, masked */
+ u8 vid_conf; /* Configuration register value */
+ u8 vrm;
+ u8 vid; /* Register value */
+
+ s8 temp[3]; /* Register value */
+ s8 temp_min[3]; /* Register value */
+ s8 temp_max[3]; /* Register value */
+ s8 temp_crit[3]; /* Register value */
+ u8 temp_status[3]; /* Register value */
+ u8 temp_alarms; /* Register value, masked */
+};
+
+/*
+ * ldi is the logical device index
+ * bank is for voltages and temperatures only
+ */
+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));
+
+ return res;
+}
+
+static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
+ u8 reg, u8 value)
+{
+ 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 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 = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t in_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", IN_FROM_REG(data->in[attr->index],
+ data->in_vref));
+}
+
+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", data->in_status[attr->index]);
+}
+
+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_min[attr->index],
+ data->in_vref));
+}
+
+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 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->in_min[attr->index] = IN_TO_REG(val, data->in_vref);
+ pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MIN,
+ data->in_min[attr->index]);
+ 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)
+{
+ 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->in_max[attr->index] = IN_TO_REG(val,
+ data->in_vref);
+ pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MAX,
+ data->in_max[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute in_max[] = {
+ SENSOR_ATTR_RW(in0_max, in_max, 0),
+ SENSOR_ATTR_RW(in1_max, in_max, 1),
+ SENSOR_ATTR_RW(in2_max, in_max, 2),
+ SENSOR_ATTR_RW(in3_max, in_max, 3),
+ SENSOR_ATTR_RW(in4_max, in_max, 4),
+ SENSOR_ATTR_RW(in5_max, in_max, 5),
+ SENSOR_ATTR_RW(in6_max, in_max, 6),
+ SENSOR_ATTR_RW(in7_max, in_max, 7),
+ SENSOR_ATTR_RW(in8_max, in_max, 8),
+ SENSOR_ATTR_RW(in9_max, in_max, 9),
+ SENSOR_ATTR_RW(in10_max, in_max, 10),
+};
+
+/* (temp & vin) channel status register alarm bits (pdf sec.11.5.12) */
+#define CHAN_ALM_MIN 0x02 /* min limit crossed */
+#define CHAN_ALM_MAX 0x04 /* max limit exceeded */
+#define TEMP_ALM_CRIT 0x08 /* temp crit exceeded (temp only) */
+
+/*
+ * show_in_min/max_alarm() reads data from the per-channel status
+ * register (sec 11.5.12), not the vin event status registers (sec
+ * 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms)
+ */
+
+static ssize_t in_min_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_MIN));
+}
+
+static struct sensor_device_attribute in_min_alarm[] = {
+ SENSOR_ATTR_RO(in0_min_alarm, in_min_alarm, 0),
+ SENSOR_ATTR_RO(in1_min_alarm, in_min_alarm, 1),
+ SENSOR_ATTR_RO(in2_min_alarm, in_min_alarm, 2),
+ SENSOR_ATTR_RO(in3_min_alarm, in_min_alarm, 3),
+ SENSOR_ATTR_RO(in4_min_alarm, in_min_alarm, 4),
+ SENSOR_ATTR_RO(in5_min_alarm, in_min_alarm, 5),
+ SENSOR_ATTR_RO(in6_min_alarm, in_min_alarm, 6),
+ SENSOR_ATTR_RO(in7_min_alarm, in_min_alarm, 7),
+ SENSOR_ATTR_RO(in8_min_alarm, in_min_alarm, 8),
+ 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),
+ SENSOR_ATTR_RO(in2_max_alarm, in_max_alarm, 2),
+ SENSOR_ATTR_RO(in3_max_alarm, in_max_alarm, 3),
+ SENSOR_ATTR_RO(in4_max_alarm, in_max_alarm, 4),
+ SENSOR_ATTR_RO(in5_max_alarm, in_max_alarm, 5),
+ SENSOR_ATTR_RO(in6_max_alarm, in_max_alarm, 6),
+ SENSOR_ATTR_RO(in7_max_alarm, in_max_alarm, 7),
+ SENSOR_ATTR_RO(in8_max_alarm, in_max_alarm, 8),
+ SENSOR_ATTR_RO(in9_max_alarm, in_max_alarm, 9),
+ SENSOR_ATTR_RO(in10_max_alarm, in_max_alarm, 10),
+};
+
+#define VIN_UNIT_ATTRS(X) \
+ &in_input[X].dev_attr.attr, \
+ &in_status[X].dev_attr.attr, \
+ &in_min[X].dev_attr.attr, \
+ &in_max[X].dev_attr.attr, \
+ &in_min_alarm[X].dev_attr.attr, \
+ &in_max_alarm[X].dev_attr.attr
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", 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 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)
+{
+ struct pc87360_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_in_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", data->in_alarms);
+}
+static DEVICE_ATTR_RO(alarms_in);
+
+static struct attribute *pc8736x_vin_attr_array[] = {
+ VIN_UNIT_ATTRS(0),
+ VIN_UNIT_ATTRS(1),
+ VIN_UNIT_ATTRS(2),
+ VIN_UNIT_ATTRS(3),
+ VIN_UNIT_ATTRS(4),
+ VIN_UNIT_ATTRS(5),
+ VIN_UNIT_ATTRS(6),
+ VIN_UNIT_ATTRS(7),
+ VIN_UNIT_ATTRS(8),
+ VIN_UNIT_ATTRS(9),
+ VIN_UNIT_ATTRS(10),
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms_in.attr,
+ NULL
+};
+static const struct attribute_group pc8736x_vin_group = {
+ .attrs = pc8736x_vin_attr_array,
+};
+
+static ssize_t therm_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", IN_FROM_REG(data->in[attr->index],
+ data->in_vref));
+}
+
+/*
+ * 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", data->in_status[attr->index]);
+}
+
+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_min[attr->index],
+ data->in_vref));
+}
+
+static ssize_t therm_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 val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[attr->index] = IN_TO_REG(val, data->in_vref);
+ pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_MIN,
+ data->in_min[attr->index]);
+ mutex_unlock(&data->update_lock);
+ 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)
+{
+ 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->in_max[attr->index] = IN_TO_REG(val, data->in_vref);
+ pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_MAX,
+ data->in_max[attr->index]);
+ 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)
+{
+ 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->in_crit[attr->index-11] = IN_TO_REG(val, data->in_vref);
+ pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_CRIT,
+ data->in_crit[attr->index-11]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+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),
+ SENSOR_ATTR_RW(temp6_crit, therm_crit, 2 + 11),
+};
+
+/*
+ * 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)
+{
+ 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_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)
+{
+ 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 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)
+{
+ 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] & TEMP_ALM_CRIT));
+}
+
+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),
+ SENSOR_ATTR_RO(temp6_crit_alarm, therm_crit_alarm, 2 + 11),
+};
+
+#define THERM_UNIT_ATTRS(X) \
+ &therm_input[X].dev_attr.attr, \
+ &therm_status[X].dev_attr.attr, \
+ &therm_min[X].dev_attr.attr, \
+ &therm_max[X].dev_attr.attr, \
+ &therm_crit[X].dev_attr.attr, \
+ &therm_min_alarm[X].dev_attr.attr, \
+ &therm_max_alarm[X].dev_attr.attr, \
+ &therm_crit_alarm[X].dev_attr.attr
+
+static struct attribute *pc8736x_therm_attr_array[] = {
+ THERM_UNIT_ATTRS(0),
+ THERM_UNIT_ATTRS(1),
+ THERM_UNIT_ATTRS(2),
+ NULL
+};
+static const struct attribute_group pc8736x_therm_group = {
+ .attrs = pc8736x_therm_attr_array,
+};
+
+static ssize_t temp_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, "%d\n", TEMP_FROM_REG(data->temp[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_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", data->temp_status[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_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 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 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->temp_min[attr->index] = TEMP_TO_REG(val);
+ pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_MIN,
+ data->temp_min[attr->index]);
+ mutex_unlock(&data->update_lock);
+ 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)
+{
+ 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->temp_max[attr->index] = TEMP_TO_REG(val);
+ pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_MAX,
+ data->temp_max[attr->index]);
+ mutex_unlock(&data->update_lock);
+ 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)
+{
+ 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->temp_crit[attr->index] = TEMP_TO_REG(val);
+ pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_CRIT,
+ data->temp_crit[attr->index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+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),
+};
+
+/*
+ * 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)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ unsigned nr = to_sensor_dev_attr(devattr)->index;
+
+ 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)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ unsigned nr = to_sensor_dev_attr(devattr)->index;
+
+ 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)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ unsigned nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT));
+}
+
+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),
+ SENSOR_ATTR_RO(temp3_crit_alarm, temp_crit_alarm, 2),
+};
+
+#define TEMP_FAULT 0x40 /* open diode */
+static ssize_t temp_fault_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->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),
+ SENSOR_ATTR_RO(temp3_fault, temp_fault, 2),
+};
+
+#define TEMP_UNIT_ATTRS(X) \
+{ &temp_input[X].dev_attr.attr, \
+ &temp_status[X].dev_attr.attr, \
+ &temp_min[X].dev_attr.attr, \
+ &temp_max[X].dev_attr.attr, \
+ &temp_crit[X].dev_attr.attr, \
+ &temp_min_alarm[X].dev_attr.attr, \
+ &temp_max_alarm[X].dev_attr.attr, \
+ &temp_crit_alarm[X].dev_attr.attr, \
+ &temp_fault[X].dev_attr.attr, \
+ NULL \
+}
+
+static struct attribute *pc8736x_temp_attr[][10] = {
+ TEMP_UNIT_ATTRS(0),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2)
+};
+
+static const struct attribute_group pc8736x_temp_attr_group[] = {
+ { .attrs = pc8736x_temp_attr[0] },
+ { .attrs = pc8736x_temp_attr[1] },
+ { .attrs = pc8736x_temp_attr[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);
+
+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 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)
+{
+ 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]));
+}
+
+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 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;
+ }
+ 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_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 \
+}
+
+static struct attribute *pc8736x_fan_attr[][5] = {
+ FAN_UNIT_ATTRS(0),
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2)
+};
+
+static const struct attribute_group pc8736x_fan_attr_group[] = {
+ { .attrs = pc8736x_fan_attr[0], },
+ { .attrs = pc8736x_fan_attr[1], },
+ { .attrs = pc8736x_fan_attr[2], },
+};
+
+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)
+{
+ 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;
+
+ device_remove_file(dev, &dev_attr_name);
+ device_remove_file(dev, &dev_attr_alarms_temp);
+ for (i = 0; i < ARRAY_SIZE(pc8736x_temp_attr_group); i++)
+ sysfs_remove_group(&dev->kobj, &pc8736x_temp_attr_group[i]);
+ for (i = 0; i < ARRAY_SIZE(pc8736x_fan_attr_group); i++) {
+ sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_attr_group[i]);
+ device_remove_file(dev, &pwm[i].dev_attr);
+ }
+ sysfs_remove_group(&dev->kobj, &pc8736x_therm_group);
+ 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;
+ struct pc87360_data *data;
+ int err = 0;
+ const char *name;
+ int use_thermistors = 0;
+ struct device *dev = &pdev->dev;
+
+ data = devm_kzalloc(dev, sizeof(struct pc87360_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ switch (devid) {
+ default:
+ name = "pc87360";
+ data->fannr = 2;
+ break;
+ case 0xe8:
+ name = "pc87363";
+ data->fannr = 2;
+ break;
+ case 0xe4:
+ name = "pc87364";
+ data->fannr = 3;
+ break;
+ case 0xe5:
+ name = "pc87365";
+ data->fannr = extra_isa[0] ? 3 : 0;
+ data->innr = extra_isa[1] ? 11 : 0;
+ data->tempnr = extra_isa[2] ? 2 : 0;
+ break;
+ case 0xe9:
+ name = "pc87366";
+ data->fannr = extra_isa[0] ? 3 : 0;
+ data->innr = extra_isa[1] ? 14 : 0;
+ data->tempnr = extra_isa[2] ? 3 : 0;
+ break;
+ }
+
+ data->name = name;
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ for (i = 0; i < LDNI_MAX; i++) {
+ data->address[i] = extra_isa[i];
+ if (data->address[i]
+ && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT,
+ DRIVER_NAME)) {
+ dev_err(dev,
+ "Region 0x%x-0x%x already in use!\n",
+ extra_isa[i], extra_isa[i]+PC87360_EXTENT-1);
+ return -EBUSY;
+ }
+ }
+
+ /* Retrieve the fans configuration from Super-I/O space */
+ if (data->fannr)
+ data->fan_conf = confreg[0] | (confreg[1] << 8);
+
+ /*
+ * Use the correct reference voltage
+ * Unless both the VLM and the TMS logical devices agree to
+ * use an external Vref, the internal one is used.
+ */
+ if (data->innr) {
+ i = pc87360_read_value(data, LD_IN, NO_BANK,
+ PC87365_REG_IN_CONFIG);
+ if (data->tempnr) {
+ i &= pc87360_read_value(data, LD_TEMP, NO_BANK,
+ PC87365_REG_TEMP_CONFIG);
+ }
+ data->in_vref = (i&0x02) ? 3025 : 2966;
+ dev_dbg(dev, "Using %s reference voltage\n",
+ (i&0x02) ? "external" : "internal");
+
+ data->vid_conf = confreg[3];
+ data->vrm = vid_which_vrm();
+ }
+
+ /* Fan clock dividers may be needed before any data is read */
+ 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));
+ }
+
+ if (init > 0) {
+ if (devid == 0xe9 && data->address[1]) /* PC87366 */
+ use_thermistors = confreg[2] & 0x40;
+
+ pc87360_init_device(pdev, use_thermistors);
+ }
+
+ /* Register all-or-nothing sysfs groups */
+
+ if (data->innr) {
+ err = sysfs_create_group(&dev->kobj, &pc8736x_vin_group);
+ if (err)
+ goto error;
+ }
+
+ if (data->innr == 14) {
+ err = sysfs_create_group(&dev->kobj, &pc8736x_therm_group);
+ if (err)
+ goto error;
+ }
+
+ /* create device attr-files for varying sysfs groups */
+
+ if (data->tempnr) {
+ for (i = 0; i < data->tempnr; i++) {
+ err = sysfs_create_group(&dev->kobj,
+ &pc8736x_temp_attr_group[i]);
+ if (err)
+ goto error;
+ }
+ err = device_create_file(dev, &dev_attr_alarms_temp);
+ if (err)
+ goto error;
+ }
+
+ for (i = 0; i < data->fannr; i++) {
+ if (FAN_CONFIG_MONITOR(data->fan_conf, i)) {
+ err = sysfs_create_group(&dev->kobj,
+ &pc8736x_fan_attr_group[i]);
+ if (err)
+ goto error;
+ }
+ if (FAN_CONFIG_CONTROL(data->fan_conf, i)) {
+ err = device_create_file(dev, &pwm[i].dev_attr);
+ if (err)
+ goto error;
+ }
+ }
+
+ err = device_create_file(dev, &dev_attr_name);
+ if (err)
+ goto error;
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto error;
+ }
+ return 0;
+
+error:
+ pc87360_remove_files(dev);
+ return err;
+}
+
+static int pc87360_remove(struct platform_device *pdev)
+{
+ struct pc87360_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ pc87360_remove_files(&pdev->dev);
+
+ return 0;
+}
+
+/*
+ * Driver data
+ */
+static struct platform_driver pc87360_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = pc87360_probe,
+ .remove = pc87360_remove,
+};
+
+/*
+ * Device detection, registration and update
+ */
+
+static int __init pc87360_find(int sioaddr, u8 *devid,
+ unsigned short *addresses)
+{
+ u16 val;
+ int i;
+ int nrdev; /* logical device count */
+
+ /* No superio_enter */
+
+ /* 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;
+
+ for (i = 0; i < nrdev; i++) {
+ /* select logical device */
+ superio_outb(sioaddr, DEV, logdev[i]);
+
+ val = superio_inb(sioaddr, ACT);
+ if (!(val & 0x01)) {
+ pr_info("Device 0x%02x not activated\n", logdev[i]);
+ continue;
+ }
+
+ 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;
+ }
+
+ addresses[i] = val;
+
+ if (i == 0) { /* Fans */
+ confreg[0] = superio_inb(sioaddr, 0xF0);
+ confreg[1] = superio_inb(sioaddr, 0xF1);
+
+ 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);
+
+ 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);
+ }
+ }
+ }
+ }
+
+ superio_exit(sioaddr);
+ return 0;
+}
+
+static int __init pc87360_device_add(unsigned short address)
+{
+ struct resource res[3];
+ int err, i, res_count;
+
+ pdev = platform_device_alloc("pc87360", address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ memset(res, 0, 3 * sizeof(struct resource));
+ res_count = 0;
+ for (i = 0; i < 3; i++) {
+ if (!extra_isa[i])
+ 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;
+
+ err = acpi_check_resource_conflict(&res[res_count]);
+ if (err)
+ goto exit_device_put;
+
+ res_count++;
+ }
+
+ err = platform_device_add_resources(pdev, res, res_count);
+ if (err) {
+ pr_err("Device resources addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int __init pc87360_init(void)
+{
+ int err, i;
+ unsigned short address = 0;
+
+ if (pc87360_find(0x2e, &devid, extra_isa)
+ && pc87360_find(0x4e, &devid, extra_isa)) {
+ pr_warn("PC8736x not detected, module not inserted\n");
+ return -ENODEV;
+ }
+
+ /* Arbitrarily pick one of the addresses */
+ for (i = 0; i < 3; i++) {
+ if (extra_isa[i] != 0x0000) {
+ address = extra_isa[i];
+ break;
+ }
+ }
+
+ if (address == 0x0000) {
+ pr_warn("No active logical device, module not inserted\n");
+ return -ENODEV;
+ }
+
+ err = platform_driver_register(&pc87360_driver);
+ if (err)
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ err = pc87360_device_add(address);
+ if (err)
+ goto exit_driver;
+
+ return 0;
+
+ exit_driver:
+ platform_driver_unregister(&pc87360_driver);
+ exit:
+ return err;
+}
+
+static void __exit pc87360_exit(void)
+{
+ platform_device_unregister(pdev);
+ 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/pc87427.c b/drivers/hwmon/pc87427.c
new file mode 100644
index 000000000..eaab83d87
--- /dev/null
+++ b/drivers/hwmon/pc87427.c
@@ -0,0 +1,1331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * pc87427.c - hardware monitoring driver for the
+ * National Semiconductor PC87427 Super-I/O chip
+ * Copyright (C) 2006, 2008, 2010 Jean Delvare <jdelvare@suse.de>
+ *
+ * Supports the following chips:
+ *
+ * Chip #vin #fan #pwm #temp devid
+ * PC87427 - 8 4 6 0xF2
+ *
+ * This driver assumes that no more than one chip is present.
+ * Only fans are fully supported so far. Temperatures are in read-only
+ * mode, and voltages aren't supported at all.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.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 <linux/sysfs.h>
+#include <linux/ioport.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static struct platform_device *pdev;
+
+#define DRVNAME "pc87427"
+
+/*
+ * The lock mutex protects both the I/O accesses (needed because the
+ * device is using banked registers) and the register cache (needed to keep
+ * the data in the registers and the cache in sync at any time).
+ */
+struct pc87427_data {
+ struct device *hwmon_dev;
+ struct mutex lock;
+ int address[2];
+ const char *name;
+
+ unsigned long last_updated; /* in jiffies */
+ u8 fan_enabled; /* bit vector */
+ u16 fan[8]; /* register values */
+ u16 fan_min[8]; /* register values */
+ u8 fan_status[8]; /* register values */
+
+ u8 pwm_enabled; /* bit vector */
+ u8 pwm_auto_ok; /* bit vector */
+ u8 pwm_enable[4]; /* register values */
+ u8 pwm[4]; /* register values */
+
+ u8 temp_enabled; /* bit vector */
+ s16 temp[6]; /* register values */
+ s8 temp_min[6]; /* register values */
+ s8 temp_max[6]; /* register values */
+ s8 temp_crit[6]; /* register values */
+ u8 temp_status[6]; /* register values */
+ u8 temp_type[6]; /* register values */
+};
+
+struct pc87427_sio_data {
+ unsigned short address[2];
+ u8 has_fanin;
+ u8 has_fanout;
+};
+
+/*
+ * Super-I/O registers and operations
+ */
+
+#define SIOREG_LDSEL 0x07 /* Logical device select */
+#define SIOREG_DEVID 0x20 /* Device ID */
+#define SIOREG_CF2 0x22 /* Configuration 2 */
+#define SIOREG_CF3 0x23 /* Configuration 3 */
+#define SIOREG_CF4 0x24 /* Configuration 4 */
+#define SIOREG_CF5 0x25 /* Configuration 5 */
+#define SIOREG_CFB 0x2B /* Configuration B */
+#define SIOREG_CFC 0x2C /* Configuration C */
+#define SIOREG_CFD 0x2D /* Configuration D */
+#define SIOREG_ACT 0x30 /* Device activation */
+#define SIOREG_MAP 0x50 /* I/O or memory mapping */
+#define SIOREG_IOBASE 0x60 /* I/O base address */
+
+static const u8 logdev[2] = { 0x09, 0x14 };
+static const char *logdev_str[2] = { DRVNAME " FMC", DRVNAME " HMC" };
+#define LD_FAN 0
+#define LD_IN 1
+#define LD_TEMP 1
+
+static inline int superio_enter(int sioaddr)
+{
+ if (!request_muxed_region(sioaddr, 2, DRVNAME))
+ return -EBUSY;
+ return 0;
+}
+
+static inline void superio_outb(int sioaddr, int reg, int val)
+{
+ outb(reg, sioaddr);
+ outb(val, sioaddr + 1);
+}
+
+static inline int superio_inb(int sioaddr, int reg)
+{
+ outb(reg, sioaddr);
+ return inb(sioaddr + 1);
+}
+
+static inline void superio_exit(int sioaddr)
+{
+ outb(0x02, sioaddr);
+ outb(0x02, sioaddr + 1);
+ release_region(sioaddr, 2);
+}
+
+/*
+ * Logical devices
+ */
+
+#define REGION_LENGTH 32
+#define PC87427_REG_BANK 0x0f
+#define BANK_FM(nr) (nr)
+#define BANK_FT(nr) (0x08 + (nr))
+#define BANK_FC(nr) (0x10 + (nr) * 2)
+#define BANK_TM(nr) (nr)
+#define BANK_VM(nr) (0x08 + (nr))
+
+/*
+ * I/O access functions
+ */
+
+/* ldi is the logical device index */
+static inline int pc87427_read8(struct pc87427_data *data, u8 ldi, u8 reg)
+{
+ return inb(data->address[ldi] + reg);
+}
+
+/* Must be called with data->lock held, except during init */
+static inline int pc87427_read8_bank(struct pc87427_data *data, u8 ldi,
+ u8 bank, u8 reg)
+{
+ outb(bank, data->address[ldi] + PC87427_REG_BANK);
+ return inb(data->address[ldi] + reg);
+}
+
+/* Must be called with data->lock held, except during init */
+static inline void pc87427_write8_bank(struct pc87427_data *data, u8 ldi,
+ u8 bank, u8 reg, u8 value)
+{
+ outb(bank, data->address[ldi] + PC87427_REG_BANK);
+ outb(value, data->address[ldi] + reg);
+}
+
+/*
+ * Fan registers and conversions
+ */
+
+/* fan data registers are 16-bit wide */
+#define PC87427_REG_FAN 0x12
+#define PC87427_REG_FAN_MIN 0x14
+#define PC87427_REG_FAN_STATUS 0x10
+
+#define FAN_STATUS_STALL (1 << 3)
+#define FAN_STATUS_LOSPD (1 << 1)
+#define FAN_STATUS_MONEN (1 << 0)
+
+/*
+ * Dedicated function to read all registers related to a given fan input.
+ * This saves us quite a few locks and bank selections.
+ * Must be called with data->lock held.
+ * nr is from 0 to 7
+ */
+static void pc87427_readall_fan(struct pc87427_data *data, u8 nr)
+{
+ int iobase = data->address[LD_FAN];
+
+ outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
+ data->fan[nr] = inw(iobase + PC87427_REG_FAN);
+ data->fan_min[nr] = inw(iobase + PC87427_REG_FAN_MIN);
+ data->fan_status[nr] = inb(iobase + PC87427_REG_FAN_STATUS);
+ /* Clear fan alarm bits */
+ outb(data->fan_status[nr], iobase + PC87427_REG_FAN_STATUS);
+}
+
+/*
+ * The 2 LSB of fan speed registers are used for something different.
+ * The actual 2 LSB of the measurements are not available.
+ */
+static inline unsigned long fan_from_reg(u16 reg)
+{
+ reg &= 0xfffc;
+ if (reg == 0x0000 || reg == 0xfffc)
+ return 0;
+ return 5400000UL / reg;
+}
+
+/* The 2 LSB of the fan speed limit registers are not significant. */
+static inline u16 fan_to_reg(unsigned long val)
+{
+ if (val < 83UL)
+ return 0xffff;
+ if (val >= 1350000UL)
+ return 0x0004;
+ return ((1350000UL + val / 2) / val) << 2;
+}
+
+/*
+ * PWM registers and conversions
+ */
+
+#define PC87427_REG_PWM_ENABLE 0x10
+#define PC87427_REG_PWM_DUTY 0x12
+
+#define PWM_ENABLE_MODE_MASK (7 << 4)
+#define PWM_ENABLE_CTLEN (1 << 0)
+
+#define PWM_MODE_MANUAL (0 << 4)
+#define PWM_MODE_AUTO (1 << 4)
+#define PWM_MODE_OFF (2 << 4)
+#define PWM_MODE_ON (7 << 4)
+
+/*
+ * Dedicated function to read all registers related to a given PWM output.
+ * This saves us quite a few locks and bank selections.
+ * Must be called with data->lock held.
+ * nr is from 0 to 3
+ */
+static void pc87427_readall_pwm(struct pc87427_data *data, u8 nr)
+{
+ int iobase = data->address[LD_FAN];
+
+ outb(BANK_FC(nr), iobase + PC87427_REG_BANK);
+ data->pwm_enable[nr] = inb(iobase + PC87427_REG_PWM_ENABLE);
+ data->pwm[nr] = inb(iobase + PC87427_REG_PWM_DUTY);
+}
+
+static inline int pwm_enable_from_reg(u8 reg)
+{
+ switch (reg & PWM_ENABLE_MODE_MASK) {
+ case PWM_MODE_ON:
+ return 0;
+ case PWM_MODE_MANUAL:
+ case PWM_MODE_OFF:
+ return 1;
+ case PWM_MODE_AUTO:
+ return 2;
+ default:
+ return -EPROTO;
+ }
+}
+
+static inline u8 pwm_enable_to_reg(unsigned long val, u8 pwmval)
+{
+ switch (val) {
+ default:
+ return PWM_MODE_ON;
+ case 1:
+ return pwmval ? PWM_MODE_MANUAL : PWM_MODE_OFF;
+ case 2:
+ return PWM_MODE_AUTO;
+ }
+}
+
+/*
+ * Temperature registers and conversions
+ */
+
+#define PC87427_REG_TEMP_STATUS 0x10
+#define PC87427_REG_TEMP 0x14
+#define PC87427_REG_TEMP_MAX 0x18
+#define PC87427_REG_TEMP_MIN 0x19
+#define PC87427_REG_TEMP_CRIT 0x1a
+#define PC87427_REG_TEMP_TYPE 0x1d
+
+#define TEMP_STATUS_CHANEN (1 << 0)
+#define TEMP_STATUS_LOWFLG (1 << 1)
+#define TEMP_STATUS_HIGHFLG (1 << 2)
+#define TEMP_STATUS_CRITFLG (1 << 3)
+#define TEMP_STATUS_SENSERR (1 << 5)
+#define TEMP_TYPE_MASK (3 << 5)
+
+#define TEMP_TYPE_THERMISTOR (1 << 5)
+#define TEMP_TYPE_REMOTE_DIODE (2 << 5)
+#define TEMP_TYPE_LOCAL_DIODE (3 << 5)
+
+/*
+ * Dedicated function to read all registers related to a given temperature
+ * input. This saves us quite a few locks and bank selections.
+ * Must be called with data->lock held.
+ * nr is from 0 to 5
+ */
+static void pc87427_readall_temp(struct pc87427_data *data, u8 nr)
+{
+ int iobase = data->address[LD_TEMP];
+
+ outb(BANK_TM(nr), iobase + PC87427_REG_BANK);
+ data->temp[nr] = le16_to_cpu(inw(iobase + PC87427_REG_TEMP));
+ data->temp_max[nr] = inb(iobase + PC87427_REG_TEMP_MAX);
+ data->temp_min[nr] = inb(iobase + PC87427_REG_TEMP_MIN);
+ data->temp_crit[nr] = inb(iobase + PC87427_REG_TEMP_CRIT);
+ data->temp_type[nr] = inb(iobase + PC87427_REG_TEMP_TYPE);
+ data->temp_status[nr] = inb(iobase + PC87427_REG_TEMP_STATUS);
+ /* Clear fan alarm bits */
+ outb(data->temp_status[nr], iobase + PC87427_REG_TEMP_STATUS);
+}
+
+static inline unsigned int temp_type_from_reg(u8 reg)
+{
+ switch (reg & TEMP_TYPE_MASK) {
+ case TEMP_TYPE_THERMISTOR:
+ return 4;
+ case TEMP_TYPE_REMOTE_DIODE:
+ case TEMP_TYPE_LOCAL_DIODE:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * We assume 8-bit thermal sensors; 9-bit thermal sensors are possible
+ * too, but I have no idea how to figure out when they are used.
+ */
+static inline long temp_from_reg(s16 reg)
+{
+ return reg * 1000 / 256;
+}
+
+static inline long temp_from_reg8(s8 reg)
+{
+ return reg * 1000;
+}
+
+/*
+ * Data interface
+ */
+
+static struct pc87427_data *pc87427_update_device(struct device *dev)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int i;
+
+ mutex_lock(&data->lock);
+ if (!time_after(jiffies, data->last_updated + HZ)
+ && data->last_updated)
+ goto done;
+
+ /* Fans */
+ for (i = 0; i < 8; i++) {
+ if (!(data->fan_enabled & (1 << i)))
+ continue;
+ pc87427_readall_fan(data, i);
+ }
+
+ /* PWM outputs */
+ for (i = 0; i < 4; i++) {
+ if (!(data->pwm_enabled & (1 << i)))
+ continue;
+ pc87427_readall_pwm(data, i);
+ }
+
+ /* Temperature channels */
+ for (i = 0; i < 6; i++) {
+ if (!(data->temp_enabled & (1 << i)))
+ continue;
+ pc87427_readall_temp(data, i);
+ }
+
+ data->last_updated = jiffies;
+
+done:
+ mutex_unlock(&data->lock);
+ return data;
+}
+
+static ssize_t fan_input_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr]));
+}
+
+static ssize_t fan_min_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr]));
+}
+
+static ssize_t fan_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->fan_status[nr]
+ & FAN_STATUS_LOSPD));
+}
+
+static ssize_t fan_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->fan_status[nr]
+ & FAN_STATUS_STALL));
+}
+
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ unsigned long val;
+ int iobase = data->address[LD_FAN];
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
+ /*
+ * The low speed limit registers are read-only while monitoring
+ * is enabled, so we have to disable monitoring, then change the
+ * limit, and finally enable monitoring again.
+ */
+ outb(0, iobase + PC87427_REG_FAN_STATUS);
+ data->fan_min[nr] = fan_to_reg(val);
+ outw(data->fan_min[nr], iobase + PC87427_REG_FAN_MIN);
+ outb(FAN_STATUS_MONEN, iobase + PC87427_REG_FAN_STATUS);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_input, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_input, fan_input, 3);
+static SENSOR_DEVICE_ATTR_RO(fan5_input, fan_input, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_input, fan_input, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_input, fan_input, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_input, fan_input, 7);
+
+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_RW(fan5_min, fan_min, 4);
+static SENSOR_DEVICE_ATTR_RW(fan6_min, fan_min, 5);
+static SENSOR_DEVICE_ATTR_RW(fan7_min, fan_min, 6);
+static SENSOR_DEVICE_ATTR_RW(fan8_min, fan_min, 7);
+
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, fan_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, fan_alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_alarm, fan_alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(fan4_alarm, fan_alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(fan5_alarm, fan_alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_alarm, fan_alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_alarm, fan_alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_alarm, fan_alarm, 7);
+
+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(fan5_fault, fan_fault, 4);
+static SENSOR_DEVICE_ATTR_RO(fan6_fault, fan_fault, 5);
+static SENSOR_DEVICE_ATTR_RO(fan7_fault, fan_fault, 6);
+static SENSOR_DEVICE_ATTR_RO(fan8_fault, fan_fault, 7);
+
+static struct attribute *pc87427_attributes_fan[8][5] = {
+ {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan4_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan5_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan6_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan7_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_min.dev_attr.attr,
+ &sensor_dev_attr_fan8_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan8_fault.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group pc87427_group_fan[8] = {
+ { .attrs = pc87427_attributes_fan[0] },
+ { .attrs = pc87427_attributes_fan[1] },
+ { .attrs = pc87427_attributes_fan[2] },
+ { .attrs = pc87427_attributes_fan[3] },
+ { .attrs = pc87427_attributes_fan[4] },
+ { .attrs = pc87427_attributes_fan[5] },
+ { .attrs = pc87427_attributes_fan[6] },
+ { .attrs = pc87427_attributes_fan[7] },
+};
+
+/*
+ * Must be called with data->lock held and pc87427_readall_pwm() freshly
+ * called
+ */
+static void update_pwm_enable(struct pc87427_data *data, int nr, u8 mode)
+{
+ int iobase = data->address[LD_FAN];
+ data->pwm_enable[nr] &= ~PWM_ENABLE_MODE_MASK;
+ data->pwm_enable[nr] |= mode;
+ outb(data->pwm_enable[nr], iobase + PC87427_REG_PWM_ENABLE);
+}
+
+static ssize_t pwm_enable_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ int pwm_enable;
+
+ pwm_enable = pwm_enable_from_reg(data->pwm_enable[nr]);
+ if (pwm_enable < 0)
+ return pwm_enable;
+ return sprintf(buf, "%d\n", pwm_enable);
+}
+
+static ssize_t pwm_enable_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0 || val > 2)
+ return -EINVAL;
+ /* Can't go to automatic mode if it isn't configured */
+ if (val == 2 && !(data->pwm_auto_ok & (1 << nr)))
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ pc87427_readall_pwm(data, nr);
+ update_pwm_enable(data, nr, pwm_enable_to_reg(val, data->pwm[nr]));
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", (int)data->pwm[nr]);
+}
+
+static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+ unsigned long val;
+ int iobase = data->address[LD_FAN];
+ u8 mode;
+
+ if (kstrtoul(buf, 10, &val) < 0 || val > 0xff)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ pc87427_readall_pwm(data, nr);
+ mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK;
+ if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) {
+ dev_notice(dev,
+ "Can't set PWM%d duty cycle while not in manual mode\n",
+ nr + 1);
+ mutex_unlock(&data->lock);
+ return -EPERM;
+ }
+
+ /* We may have to change the mode */
+ if (mode == PWM_MODE_MANUAL && val == 0) {
+ /* Transition from Manual to Off */
+ update_pwm_enable(data, nr, PWM_MODE_OFF);
+ mode = PWM_MODE_OFF;
+ dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
+ "manual", "off");
+ } else if (mode == PWM_MODE_OFF && val != 0) {
+ /* Transition from Off to Manual */
+ update_pwm_enable(data, nr, PWM_MODE_MANUAL);
+ mode = PWM_MODE_MANUAL;
+ dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
+ "off", "manual");
+ }
+
+ data->pwm[nr] = val;
+ if (mode == PWM_MODE_MANUAL)
+ outb(val, iobase + PC87427_REG_PWM_DUTY);
+ mutex_unlock(&data->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 SENSOR_DEVICE_ATTR_RW(pwm4_enable, pwm_enable, 3);
+
+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 struct attribute *pc87427_attributes_pwm[4][3] = {
+ {
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group pc87427_group_pwm[4] = {
+ { .attrs = pc87427_attributes_pwm[0] },
+ { .attrs = pc87427_attributes_pwm[1] },
+ { .attrs = pc87427_attributes_pwm[2] },
+ { .attrs = pc87427_attributes_pwm[3] },
+};
+
+static ssize_t temp_input_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg(data->temp[nr]));
+}
+
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_min[nr]));
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_max[nr]));
+}
+
+static ssize_t temp_crit_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_crit[nr]));
+}
+
+static ssize_t temp_type_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%u\n", temp_type_from_reg(data->temp_type[nr]));
+}
+
+static ssize_t temp_min_alarm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_LOWFLG));
+}
+
+static ssize_t temp_max_alarm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_HIGHFLG));
+}
+
+static ssize_t temp_crit_alarm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_CRITFLG));
+}
+
+static ssize_t temp_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct pc87427_data *data = pc87427_update_device(dev);
+ int nr = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", !!(data->temp_status[nr]
+ & TEMP_STATUS_SENSERR));
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_input, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_input, temp_input, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_input, temp_input, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_input, temp_input, 5);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_min, temp_min, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_min, temp_min, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_min, temp_min, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_min, temp_min, 5);
+
+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(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(temp1_type, temp_type, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_type, temp_type, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_type, temp_type, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_type, temp_type, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_type, temp_type, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_type, temp_type, 5);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_min_alarm, temp_min_alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_min_alarm, temp_min_alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_min_alarm, temp_min_alarm, 5);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, temp_max_alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, temp_max_alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_max_alarm, temp_max_alarm, 5);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, temp_crit_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, temp_crit_alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, temp_crit_alarm, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_crit_alarm, temp_crit_alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_crit_alarm, temp_crit_alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_crit_alarm, temp_crit_alarm, 5);
+
+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 struct attribute *pc87427_attributes_temp[6][10] = {
+ {
+ &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_type.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,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ NULL
+ }, {
+ &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_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ NULL
+ }, {
+ &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_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ NULL
+ }, {
+ &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_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_type.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit.dev_attr.attr,
+ &sensor_dev_attr_temp5_type.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_min.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit.dev_attr.attr,
+ &sensor_dev_attr_temp6_type.dev_attr.attr,
+ &sensor_dev_attr_temp6_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_fault.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group pc87427_group_temp[6] = {
+ { .attrs = pc87427_attributes_temp[0] },
+ { .attrs = pc87427_attributes_temp[1] },
+ { .attrs = pc87427_attributes_temp[2] },
+ { .attrs = pc87427_attributes_temp[3] },
+ { .attrs = pc87427_attributes_temp[4] },
+ { .attrs = pc87427_attributes_temp[5] },
+};
+
+static ssize_t name_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+static DEVICE_ATTR_RO(name);
+
+
+/*
+ * Device detection, attach and detach
+ */
+
+static int pc87427_request_regions(struct platform_device *pdev,
+ int count)
+{
+ struct resource *res;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_IO, i);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing resource #%d\n", i);
+ return -ENOENT;
+ }
+ if (!devm_request_region(&pdev->dev, res->start,
+ resource_size(res), DRVNAME)) {
+ dev_err(&pdev->dev,
+ "Failed to request region 0x%lx-0x%lx\n",
+ (unsigned long)res->start,
+ (unsigned long)res->end);
+ return -EBUSY;
+ }
+ }
+ return 0;
+}
+
+static void pc87427_init_device(struct device *dev)
+{
+ struct pc87427_sio_data *sio_data = dev_get_platdata(dev);
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int i;
+ u8 reg;
+
+ /* The FMC module should be ready */
+ reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK);
+ if (!(reg & 0x80))
+ dev_warn(dev, "%s module not ready!\n", "FMC");
+
+ /* Check which fans are enabled */
+ for (i = 0; i < 8; i++) {
+ if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
+ continue;
+ reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i),
+ PC87427_REG_FAN_STATUS);
+ if (reg & FAN_STATUS_MONEN)
+ data->fan_enabled |= (1 << i);
+ }
+
+ if (!data->fan_enabled) {
+ dev_dbg(dev, "Enabling monitoring of all fans\n");
+ for (i = 0; i < 8; i++) {
+ if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
+ continue;
+ pc87427_write8_bank(data, LD_FAN, BANK_FM(i),
+ PC87427_REG_FAN_STATUS,
+ FAN_STATUS_MONEN);
+ }
+ data->fan_enabled = sio_data->has_fanin;
+ }
+
+ /* Check which PWM outputs are enabled */
+ for (i = 0; i < 4; i++) {
+ if (!(sio_data->has_fanout & (1 << i))) /* Not wired */
+ continue;
+ reg = pc87427_read8_bank(data, LD_FAN, BANK_FC(i),
+ PC87427_REG_PWM_ENABLE);
+ if (reg & PWM_ENABLE_CTLEN)
+ data->pwm_enabled |= (1 << i);
+
+ /*
+ * We don't expose an interface to reconfigure the automatic
+ * fan control mode, so only allow to return to this mode if
+ * it was originally set.
+ */
+ if ((reg & PWM_ENABLE_MODE_MASK) == PWM_MODE_AUTO) {
+ dev_dbg(dev, "PWM%d is in automatic control mode\n",
+ i + 1);
+ data->pwm_auto_ok |= (1 << i);
+ }
+ }
+
+ /* The HMC module should be ready */
+ reg = pc87427_read8(data, LD_TEMP, PC87427_REG_BANK);
+ if (!(reg & 0x80))
+ dev_warn(dev, "%s module not ready!\n", "HMC");
+
+ /* Check which temperature channels are enabled */
+ for (i = 0; i < 6; i++) {
+ reg = pc87427_read8_bank(data, LD_TEMP, BANK_TM(i),
+ PC87427_REG_TEMP_STATUS);
+ if (reg & TEMP_STATUS_CHANEN)
+ data->temp_enabled |= (1 << i);
+ }
+}
+
+static void pc87427_remove_files(struct device *dev)
+{
+ struct pc87427_data *data = dev_get_drvdata(dev);
+ int i;
+
+ device_remove_file(dev, &dev_attr_name);
+ for (i = 0; i < 8; i++) {
+ if (!(data->fan_enabled & (1 << i)))
+ continue;
+ sysfs_remove_group(&dev->kobj, &pc87427_group_fan[i]);
+ }
+ for (i = 0; i < 4; i++) {
+ if (!(data->pwm_enabled & (1 << i)))
+ continue;
+ sysfs_remove_group(&dev->kobj, &pc87427_group_pwm[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ if (!(data->temp_enabled & (1 << i)))
+ continue;
+ sysfs_remove_group(&dev->kobj, &pc87427_group_temp[i]);
+ }
+}
+
+static int pc87427_probe(struct platform_device *pdev)
+{
+ struct pc87427_sio_data *sio_data = dev_get_platdata(&pdev->dev);
+ struct pc87427_data *data;
+ int i, err, res_count;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct pc87427_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->address[0] = sio_data->address[0];
+ data->address[1] = sio_data->address[1];
+ res_count = (data->address[0] != 0) + (data->address[1] != 0);
+
+ err = pc87427_request_regions(pdev, res_count);
+ if (err)
+ return err;
+
+ mutex_init(&data->lock);
+ data->name = "pc87427";
+ platform_set_drvdata(pdev, data);
+ pc87427_init_device(&pdev->dev);
+
+ /* Register sysfs hooks */
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
+ return err;
+ for (i = 0; i < 8; i++) {
+ if (!(data->fan_enabled & (1 << i)))
+ continue;
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &pc87427_group_fan[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+ for (i = 0; i < 4; i++) {
+ if (!(data->pwm_enabled & (1 << i)))
+ continue;
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &pc87427_group_pwm[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+ for (i = 0; i < 6; i++) {
+ if (!(data->temp_enabled & (1 << i)))
+ continue;
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &pc87427_group_temp[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+
+ 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_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ pc87427_remove_files(&pdev->dev);
+ return err;
+}
+
+static int pc87427_remove(struct platform_device *pdev)
+{
+ struct pc87427_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ pc87427_remove_files(&pdev->dev);
+
+ return 0;
+}
+
+
+static struct platform_driver pc87427_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = pc87427_probe,
+ .remove = pc87427_remove,
+};
+
+static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data)
+{
+ struct resource res[2] = {
+ { .flags = IORESOURCE_IO },
+ { .flags = IORESOURCE_IO },
+ };
+ int err, i, res_count;
+
+ res_count = 0;
+ for (i = 0; i < 2; i++) {
+ if (!sio_data->address[i])
+ continue;
+ res[res_count].start = sio_data->address[i];
+ res[res_count].end = sio_data->address[i] + REGION_LENGTH - 1;
+ res[res_count].name = logdev_str[i];
+
+ err = acpi_check_resource_conflict(&res[res_count]);
+ if (err)
+ goto exit;
+
+ res_count++;
+ }
+
+ pdev = platform_device_alloc(DRVNAME, res[0].start);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, res, res_count);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add_data(pdev, sio_data,
+ sizeof(struct pc87427_sio_data));
+ if (err) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data)
+{
+ u16 val;
+ u8 cfg, cfg_b;
+ int i, err;
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ /* Identify device */
+ val = force_id ? force_id : superio_inb(sioaddr, SIOREG_DEVID);
+ if (val != 0xf2) { /* PC87427 */
+ err = -ENODEV;
+ goto exit;
+ }
+
+ for (i = 0; i < 2; i++) {
+ sio_data->address[i] = 0;
+ /* Select logical device */
+ superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]);
+
+ val = superio_inb(sioaddr, SIOREG_ACT);
+ if (!(val & 0x01)) {
+ pr_info("Logical device 0x%02x not activated\n",
+ logdev[i]);
+ continue;
+ }
+
+ val = superio_inb(sioaddr, SIOREG_MAP);
+ if (val & 0x01) {
+ pr_warn("Logical device 0x%02x is memory-mapped, can't use\n",
+ logdev[i]);
+ continue;
+ }
+
+ val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8)
+ | superio_inb(sioaddr, SIOREG_IOBASE + 1);
+ if (!val) {
+ pr_info("I/O base address not set for logical device 0x%02x\n",
+ logdev[i]);
+ continue;
+ }
+ sio_data->address[i] = val;
+ }
+
+ /* No point in loading the driver if everything is disabled */
+ if (!sio_data->address[0] && !sio_data->address[1]) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /* Check which fan inputs are wired */
+ sio_data->has_fanin = (1 << 2) | (1 << 3); /* FANIN2, FANIN3 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CF2);
+ if (!(cfg & (1 << 3)))
+ sio_data->has_fanin |= (1 << 0); /* FANIN0 */
+ if (!(cfg & (1 << 2)))
+ sio_data->has_fanin |= (1 << 4); /* FANIN4 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CFD);
+ if (!(cfg & (1 << 0)))
+ sio_data->has_fanin |= (1 << 1); /* FANIN1 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CF4);
+ if (!(cfg & (1 << 0)))
+ sio_data->has_fanin |= (1 << 7); /* FANIN7 */
+ cfg_b = superio_inb(sioaddr, SIOREG_CFB);
+ if (!(cfg & (1 << 1)) && (cfg_b & (1 << 3)))
+ sio_data->has_fanin |= (1 << 5); /* FANIN5 */
+ cfg = superio_inb(sioaddr, SIOREG_CF3);
+ if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5)))
+ sio_data->has_fanin |= (1 << 6); /* FANIN6 */
+
+ /* Check which fan outputs are wired */
+ sio_data->has_fanout = (1 << 0); /* FANOUT0 */
+ if (cfg_b & (1 << 0))
+ sio_data->has_fanout |= (1 << 3); /* FANOUT3 */
+
+ cfg = superio_inb(sioaddr, SIOREG_CFC);
+ if (!(cfg & (1 << 4))) {
+ if (cfg_b & (1 << 1))
+ sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
+ if (cfg_b & (1 << 2))
+ sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
+ }
+
+ /* FANOUT1 and FANOUT2 can each be routed to 2 different pins */
+ cfg = superio_inb(sioaddr, SIOREG_CF5);
+ if (cfg & (1 << 6))
+ sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
+ if (cfg & (1 << 5))
+ sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
+
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+static int __init pc87427_init(void)
+{
+ int err;
+ struct pc87427_sio_data sio_data;
+
+ if (pc87427_find(0x2e, &sio_data)
+ && pc87427_find(0x4e, &sio_data))
+ return -ENODEV;
+
+ err = platform_driver_register(&pc87427_driver);
+ if (err)
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ err = pc87427_device_add(&sio_data);
+ if (err)
+ goto exit_driver;
+
+ return 0;
+
+exit_driver:
+ platform_driver_unregister(&pc87427_driver);
+exit:
+ return err;
+}
+
+static void __exit pc87427_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&pc87427_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("PC87427 hardware monitoring driver");
+MODULE_LICENSE("GPL");
+
+module_init(pc87427_init);
+module_exit(pc87427_exit);
diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c
new file mode 100644
index 000000000..af9614e91
--- /dev/null
+++ b/drivers/hwmon/pcf8591.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net>
+ * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
+ * the help of Jean Delvare <jdelvare@suse.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+
+/* Insmod parameters */
+
+static int input_mode;
+module_param(input_mode, int, 0);
+MODULE_PARM_DESC(input_mode,
+ "Analog input mode:\n"
+ " 0 = four single ended inputs\n"
+ " 1 = three differential inputs\n"
+ " 2 = single ended and differential mixed\n"
+ " 3 = two differential inputs\n");
+
+/*
+ * The PCF8591 control byte
+ * 7 6 5 4 3 2 1 0
+ * | 0 |AOEF| AIP | 0 |AINC| AICH |
+ */
+
+/* Analog Output Enable Flag (analog output active if 1) */
+#define PCF8591_CONTROL_AOEF 0x40
+
+/*
+ * Analog Input Programming
+ * 0x00 = four single ended inputs
+ * 0x10 = three differential inputs
+ * 0x20 = single ended and differential mixed
+ * 0x30 = two differential inputs
+ */
+#define PCF8591_CONTROL_AIP_MASK 0x30
+
+/* Autoincrement Flag (switch on if 1) */
+#define PCF8591_CONTROL_AINC 0x04
+
+/*
+ * Channel selection
+ * 0x00 = channel 0
+ * 0x01 = channel 1
+ * 0x02 = channel 2
+ * 0x03 = channel 3
+ */
+#define PCF8591_CONTROL_AICH_MASK 0x03
+
+/* Initial values */
+#define PCF8591_INIT_CONTROL ((input_mode << 4) | PCF8591_CONTROL_AOEF)
+#define PCF8591_INIT_AOUT 0 /* DAC out = 0 */
+
+/* Conversions */
+#define REG_TO_SIGNED(reg) (((reg) & 0x80) ? ((reg) - 256) : (reg))
+
+struct pcf8591_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+
+ u8 control;
+ u8 aout;
+};
+
+static void pcf8591_init_client(struct i2c_client *client);
+static int pcf8591_read_channel(struct device *dev, int channel);
+
+/* following are the sysfs callback functions */
+#define show_in_channel(channel) \
+static ssize_t show_in##channel##_input(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\
+} \
+static DEVICE_ATTR(in##channel##_input, S_IRUGO, \
+ show_in##channel##_input, NULL);
+
+show_in_channel(0);
+show_in_channel(1);
+show_in_channel(2);
+show_in_channel(3);
+
+static ssize_t out0_output_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ return sprintf(buf, "%d\n", data->aout * 10);
+}
+
+static ssize_t out0_output_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ val /= 10;
+ if (val > 255)
+ return -EINVAL;
+
+ data->aout = val;
+ i2c_smbus_write_byte_data(client, data->control, data->aout);
+ return count;
+}
+
+static DEVICE_ATTR_RW(out0_output);
+
+static ssize_t out0_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF)));
+}
+
+static ssize_t out0_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if (val)
+ data->control |= PCF8591_CONTROL_AOEF;
+ else
+ data->control &= ~PCF8591_CONTROL_AOEF;
+ i2c_smbus_write_byte(client, data->control);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(out0_enable);
+
+static struct attribute *pcf8591_attributes[] = {
+ &dev_attr_out0_enable.attr,
+ &dev_attr_out0_output.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group = {
+ .attrs = pcf8591_attributes,
+};
+
+static struct attribute *pcf8591_attributes_opt[] = {
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group_opt = {
+ .attrs = pcf8591_attributes_opt,
+};
+
+/*
+ * Real code
+ */
+
+static int pcf8591_probe(struct i2c_client *client)
+{
+ struct pcf8591_data *data;
+ int err;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct pcf8591_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the PCF8591 chip */
+ pcf8591_init_client(client);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group);
+ if (err)
+ return err;
+
+ /* Register input2 if not in "two differential inputs" mode */
+ if (input_mode != 3) {
+ err = device_create_file(&client->dev, &dev_attr_in2_input);
+ if (err)
+ goto exit_sysfs_remove;
+ }
+
+ /* Register input3 only in "four single ended inputs" mode */
+ if (input_mode == 0) {
+ err = device_create_file(&client->dev, &dev_attr_in3_input);
+ if (err)
+ goto exit_sysfs_remove;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_sysfs_remove;
+ }
+
+ return 0;
+
+exit_sysfs_remove:
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
+ return err;
+}
+
+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);
+}
+
+/* Called when we have found a new PCF8591. */
+static void pcf8591_init_client(struct i2c_client *client)
+{
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+ data->control = PCF8591_INIT_CONTROL;
+ data->aout = PCF8591_INIT_AOUT;
+
+ i2c_smbus_write_byte_data(client, data->control, data->aout);
+
+ /*
+ * The first byte transmitted contains the conversion code of the
+ * previous read cycle. FLUSH IT!
+ */
+ i2c_smbus_read_byte(client);
+}
+
+static int pcf8591_read_channel(struct device *dev, int channel)
+{
+ u8 value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pcf8591_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if ((data->control & PCF8591_CONTROL_AICH_MASK) != channel) {
+ data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK)
+ | channel;
+ i2c_smbus_write_byte(client, data->control);
+
+ /*
+ * The first byte transmitted contains the conversion code of
+ * the previous read cycle. FLUSH IT!
+ */
+ i2c_smbus_read_byte(client);
+ }
+ value = i2c_smbus_read_byte(client);
+
+ mutex_unlock(&data->update_lock);
+
+ if ((channel == 2 && input_mode == 2) ||
+ (channel != 3 && (input_mode == 1 || input_mode == 3)))
+ return 10 * REG_TO_SIGNED(value);
+ else
+ return 10 * value;
+}
+
+static const struct i2c_device_id pcf8591_id[] = {
+ { "pcf8591", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8591_id);
+
+static struct i2c_driver pcf8591_driver = {
+ .driver = {
+ .name = "pcf8591",
+ },
+ .probe_new = pcf8591_probe,
+ .remove = pcf8591_remove,
+ .id_table = pcf8591_id,
+};
+
+static int __init pcf8591_init(void)
+{
+ if (input_mode < 0 || input_mode > 3) {
+ pr_warn("invalid input_mode (%d)\n", input_mode);
+ input_mode = 0;
+ }
+ return i2c_add_driver(&pcf8591_driver);
+}
+
+static void __exit pcf8591_exit(void)
+{
+ i2c_del_driver(&pcf8591_driver);
+}
+
+MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_DESCRIPTION("PCF8591 driver");
+MODULE_LICENSE("GPL");
+
+module_init(pcf8591_init);
+module_exit(pcf8591_exit);
diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig
new file mode 100644
index 000000000..9d32a57ba
--- /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 000000000..191cfa022
--- /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 000000000..734506b0e
--- /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 000000000..87d56f0fc
--- /dev/null
+++ b/drivers/hwmon/peci/cputemp.c
@@ -0,0 +1,592 @@
+// 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 + 1) * 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_skx = {
+ .reg = &resolved_cores_reg_hsx,
+ .min_peci_revision = 0x33,
+ .thermal_margin_to_millidegree = &dts_ten_dot_six_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_skx,
+ },
+ {
+ .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 000000000..0a633bda3
--- /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, &reg_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, &reg_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
new file mode 100644
index 000000000..89668af67
--- /dev/null
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -0,0 +1,464 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# PMBus chip drivers configuration
+#
+
+menuconfig PMBUS
+ tristate "PMBus support"
+ depends on I2C
+ help
+ Say yes here if you want to enable PMBus support.
+
+ This driver can also be built as a module. If so, the module will
+ be called pmbus_core.
+
+if PMBUS
+
+config SENSORS_PMBUS
+ tristate "Generic PMBus devices"
+ default y
+ help
+ If you say yes here you get hardware monitoring support for generic
+ 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
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADM1075, ADM1272, ADM1275, ADM1276, ADM1278, ADM1293,
+ and ADM1294 Hot-Swap Controller and Digital Power Monitors.
+
+ This driver can also be built as a module. If so, the module will
+ be called adm1275.
+
+config SENSORS_BEL_PFE
+ tristate "Bel PFE Compatible Power Supplies"
+ help
+ If you say yes here you get hardware monitoring support for BEL
+ PFE1100 and PFE3000 Power Supplies.
+
+ 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
+ help
+ If you say yes here you get hardware monitoring support for the IBM
+ Common Form Factor power supply.
+
+ 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
+ If you say yes here you get hardware monitoring support for the INSPUR
+ Power System power supply.
+
+ This driver can also be built as a module. If so, the module will
+ be called inspur-ipsps.
+
+config SENSORS_IR35221
+ tristate "Infineon IR35221"
+ help
+ If you say yes here you get hardware monitoring support for the
+ Infineon IR35221 controller.
+
+ This driver can also be built as a module. If so, the module will
+ be called 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 and compatibles"
+ help
+ If you say yes here you get hardware monitoring support for Infineon
+ 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
+ If you say yes here you get hardware monitoring support for the
+ Infineon IRPS5401 controller.
+
+ This driver can also be built as a module. If so, the module will
+ be called irps5401.
+
+config SENSORS_ISL68137
+ tristate "Renesas Digital Multiphase Voltage Regulators"
+ help
+ 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.
+
+config SENSORS_LM25066
+ tristate "National Semiconductor LM25066 and compatibles"
+ help
+ If you say yes here you get hardware monitoring support for National
+ Semiconductor LM25056, LM25066, LM5064, and LM5066.
+
+ 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 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.
+
+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
+ LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7880,
+ LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4686,
+ and LTM4700.
+
+config SENSORS_LTC3815
+ tristate "Linear Technologies LTC3815"
+ help
+ If you say yes here you get hardware monitoring support for Linear
+ Technology 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
+ If you say yes here you get hardware monitoring support for Maxim
+ 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 MAX20710, MAX20730, MAX20734, MAX20743"
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ MAX20710, MAX20730, MAX20734, and MAX20743.
+
+ This driver can also be built as a module. If so, the module will
+ be called max20730.
+
+config SENSORS_MAX20751
+ tristate "Maxim MAX20751"
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ MAX20751.
+
+ This driver can also be built as a module. If so, the module will
+ be called max20751.
+
+config SENSORS_MAX31785
+ tristate "Maxim MAX31785 and compatibles"
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ MAX31785.
+
+ This driver can also be built as a module. If so, the module will
+ be called max31785.
+
+config SENSORS_MAX34440
+ tristate "Maxim MAX34440 and compatibles"
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461.
+
+ This driver can also be built as a module. If so, the module will
+ be called max34440.
+
+config SENSORS_MAX8688
+ tristate "Maxim MAX8688"
+ help
+ If you say yes here you get hardware monitoring support for Maxim
+ 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
+ If you say yes here you get hardware monitoring support for Infineon
+ 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
+ If you say yes here you get hardware monitoring support for TI
+ TPS40422.
+
+ This driver can also be built as a module. If so, the module will
+ be called tps40422.
+
+config SENSORS_TPS53679
+ tristate "TI TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, TPS53688"
+ help
+ If you say yes here you get hardware monitoring support for TI
+ 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
+ If you say yes here you get hardware monitoring support for TI
+ UCD90120, UCD90124, UCD90160, UCD90320, UCD9090, UCD90910, Sequencer
+ and System Health Controllers.
+
+ This driver can also be built as a module. If so, the module will
+ be called ucd9000.
+
+config SENSORS_UCD9200
+ tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
+ help
+ If you say yes here you get hardware monitoring support for TI
+ UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
+ Digital PWM System Controllers.
+
+ 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
+ If you say yes here you get hardware monitoring support for Infineon
+ XDPE12254, XDPE12284, device.
+
+ 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
+ If you say yes here you get hardware monitoring support for Intersil
+ ZL2004, ZL2005, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, ZL6105,
+ ZL9101M, and ZL9117M Digital DC/DC Controllers, as well as for
+ Ericsson BMR450, BMR451, BMR462, BMR463, and BMR464.
+
+ This driver can also be built as a module. If so, the module will
+ be called zl6100.
+
+endif # PMBUS
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
new file mode 100644
index 000000000..0002dbe22
--- /dev/null
+++ b/drivers/hwmon/pmbus/Makefile
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for PMBus chip drivers.
+#
+
+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 000000000..1ac2b2f4c
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -0,0 +1,514 @@
+// 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.can_sleep = true;
+ 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
new file mode 100644
index 000000000..b8543c06d
--- /dev/null
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -0,0 +1,844 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller
+ * and Digital Power Monitor
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2018 Guenter Roeck
+ */
+
+#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/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/log2.h>
+#include "pmbus.h"
+
+enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 };
+
+#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0)
+#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5)
+#define ADM1293_MFR_STATUS_VAUX_OV_WARN BIT(6)
+
+#define ADM1275_PEAK_IOUT 0xd0
+#define ADM1275_PEAK_VIN 0xd1
+#define ADM1275_PEAK_VOUT 0xd2
+#define ADM1275_PMON_CONFIG 0xd4
+
+#define ADM1275_VIN_VOUT_SELECT BIT(6)
+#define ADM1275_VRANGE BIT(5)
+#define ADM1075_IRANGE_50 BIT(4)
+#define ADM1075_IRANGE_25 BIT(3)
+#define ADM1075_IRANGE_MASK (BIT(3) | BIT(4))
+
+#define ADM1272_IRANGE BIT(0)
+
+#define ADM1278_TSFILT BIT(15)
+#define ADM1278_TEMP1_EN BIT(3)
+#define ADM1278_VIN_EN BIT(2)
+#define ADM1278_VOUT_EN BIT(1)
+
+#define ADM1278_PMON_DEFCONFIG (ADM1278_VOUT_EN | ADM1278_TEMP1_EN | ADM1278_TSFILT)
+
+#define ADM1293_IRANGE_25 0
+#define ADM1293_IRANGE_50 BIT(6)
+#define ADM1293_IRANGE_100 BIT(7)
+#define ADM1293_IRANGE_200 (BIT(6) | BIT(7))
+#define ADM1293_IRANGE_MASK (BIT(6) | BIT(7))
+
+#define ADM1293_VIN_SEL_012 BIT(2)
+#define ADM1293_VIN_SEL_074 BIT(3)
+#define ADM1293_VIN_SEL_210 (BIT(2) | BIT(3))
+#define ADM1293_VIN_SEL_MASK (BIT(2) | BIT(3))
+
+#define ADM1293_VAUX_EN BIT(1)
+
+#define ADM1278_PEAK_TEMP 0xd7
+#define ADM1275_IOUT_WARN2_LIMIT 0xd7
+#define ADM1275_DEVICE_CONFIG 0xd8
+
+#define ADM1275_IOUT_WARN2_SELECT BIT(4)
+
+#define ADM1276_PEAK_PIN 0xda
+#define ADM1075_READ_VAUX 0xdd
+#define ADM1075_VAUX_OV_WARN_LIMIT 0xde
+#define ADM1075_VAUX_UV_WARN_LIMIT 0xdf
+#define ADM1293_IOUT_MIN 0xe3
+#define ADM1293_PIN_MIN 0xe4
+#define ADM1075_VAUX_STATUS 0xf6
+
+#define ADM1075_VAUX_OV_WARN BIT(7)
+#define ADM1075_VAUX_UV_WARN BIT(6)
+
+#define ADM1275_VI_AVG_SHIFT 0
+#define ADM1275_VI_AVG_MASK GENMASK(ADM1275_VI_AVG_SHIFT + 2, \
+ ADM1275_VI_AVG_SHIFT)
+#define ADM1275_SAMPLES_AVG_MAX 128
+
+#define ADM1278_PWR_AVG_SHIFT 11
+#define ADM1278_PWR_AVG_MASK GENMASK(ADM1278_PWR_AVG_SHIFT + 2, \
+ ADM1278_PWR_AVG_SHIFT)
+#define ADM1278_VI_AVG_SHIFT 8
+#define ADM1278_VI_AVG_MASK GENMASK(ADM1278_VI_AVG_SHIFT + 2, \
+ ADM1278_VI_AVG_SHIFT)
+
+struct adm1275_data {
+ int id;
+ bool have_oc_fault;
+ bool have_uc_fault;
+ bool have_vout;
+ bool have_vaux_status;
+ bool have_mfr_vaux_status;
+ bool have_iout_min;
+ bool have_pin_min;
+ bool have_pin_max;
+ bool have_temp_max;
+ bool have_power_sampling;
+ struct pmbus_driver_info info;
+};
+
+#define to_adm1275_data(x) container_of(x, struct adm1275_data, info)
+
+struct coefficients {
+ s16 m;
+ s16 b;
+ s16 R;
+};
+
+static const struct coefficients adm1075_coefficients[] = {
+ [0] = { 27169, 0, -1 }, /* voltage */
+ [1] = { 806, 20475, -1 }, /* current, irange25 */
+ [2] = { 404, 20475, -1 }, /* current, irange50 */
+ [3] = { 8549, 0, -1 }, /* power, irange25 */
+ [4] = { 4279, 0, -1 }, /* power, irange50 */
+};
+
+static const struct coefficients adm1272_coefficients[] = {
+ [0] = { 6770, 0, -2 }, /* voltage, vrange 60V */
+ [1] = { 4062, 0, -2 }, /* voltage, vrange 100V */
+ [2] = { 1326, 20480, -1 }, /* current, vsense range 15mV */
+ [3] = { 663, 20480, -1 }, /* current, vsense range 30mV */
+ [4] = { 3512, 0, -2 }, /* power, vrange 60V, irange 15mV */
+ [5] = { 21071, 0, -3 }, /* power, vrange 100V, irange 15mV */
+ [6] = { 17561, 0, -3 }, /* power, vrange 60V, irange 30mV */
+ [7] = { 10535, 0, -3 }, /* power, vrange 100V, irange 30mV */
+ [8] = { 42, 31871, -1 }, /* temperature */
+
+};
+
+static const struct coefficients adm1275_coefficients[] = {
+ [0] = { 19199, 0, -2 }, /* voltage, vrange set */
+ [1] = { 6720, 0, -1 }, /* voltage, vrange not set */
+ [2] = { 807, 20475, -1 }, /* current */
+};
+
+static const struct coefficients adm1276_coefficients[] = {
+ [0] = { 19199, 0, -2 }, /* voltage, vrange set */
+ [1] = { 6720, 0, -1 }, /* voltage, vrange not set */
+ [2] = { 807, 20475, -1 }, /* current */
+ [3] = { 6043, 0, -2 }, /* power, vrange set */
+ [4] = { 2115, 0, -1 }, /* power, vrange not set */
+};
+
+static const struct coefficients adm1278_coefficients[] = {
+ [0] = { 19599, 0, -2 }, /* voltage */
+ [1] = { 800, 20475, -1 }, /* current */
+ [2] = { 6123, 0, -2 }, /* power */
+ [3] = { 42, 31880, -1 }, /* temperature */
+};
+
+static const struct coefficients adm1293_coefficients[] = {
+ [0] = { 3333, -1, 0 }, /* voltage, vrange 1.2V */
+ [1] = { 5552, -5, -1 }, /* voltage, vrange 7.4V */
+ [2] = { 19604, -50, -2 }, /* voltage, vrange 21V */
+ [3] = { 8000, -100, -2 }, /* current, irange25 */
+ [4] = { 4000, -100, -2 }, /* current, irange50 */
+ [5] = { 20000, -1000, -3 }, /* current, irange100 */
+ [6] = { 10000, -1000, -3 }, /* current, irange200 */
+ [7] = { 10417, 0, -1 }, /* power, 1.2V, irange25 */
+ [8] = { 5208, 0, -1 }, /* power, 1.2V, irange50 */
+ [9] = { 26042, 0, -2 }, /* power, 1.2V, irange100 */
+ [10] = { 13021, 0, -2 }, /* power, 1.2V, irange200 */
+ [11] = { 17351, 0, -2 }, /* power, 7.4V, irange25 */
+ [12] = { 8676, 0, -2 }, /* power, 7.4V, irange50 */
+ [13] = { 4338, 0, -2 }, /* power, 7.4V, irange100 */
+ [14] = { 21689, 0, -3 }, /* power, 7.4V, irange200 */
+ [15] = { 6126, 0, -2 }, /* power, 21V, irange25 */
+ [16] = { 30631, 0, -3 }, /* power, 21V, irange50 */
+ [17] = { 15316, 0, -3 }, /* power, 21V, irange100 */
+ [18] = { 7658, 0, -3 }, /* power, 21V, irange200 */
+};
+
+static int adm1275_read_pmon_config(const struct adm1275_data *data,
+ struct i2c_client *client, bool is_power)
+{
+ int shift, ret;
+ u16 mask;
+
+ /*
+ * The PMON configuration register is a 16-bit register only on chips
+ * supporting power average sampling. On other chips it is an 8-bit
+ * register.
+ */
+ if (data->have_power_sampling) {
+ ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG);
+ mask = is_power ? ADM1278_PWR_AVG_MASK : ADM1278_VI_AVG_MASK;
+ shift = is_power ? ADM1278_PWR_AVG_SHIFT : ADM1278_VI_AVG_SHIFT;
+ } else {
+ ret = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
+ mask = ADM1275_VI_AVG_MASK;
+ shift = ADM1275_VI_AVG_SHIFT;
+ }
+ if (ret < 0)
+ return ret;
+
+ return (ret & mask) >> shift;
+}
+
+static int adm1275_write_pmon_config(const struct adm1275_data *data,
+ struct i2c_client *client,
+ bool is_power, u16 word)
+{
+ int shift, ret;
+ u16 mask;
+
+ if (data->have_power_sampling) {
+ ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG);
+ mask = is_power ? ADM1278_PWR_AVG_MASK : ADM1278_VI_AVG_MASK;
+ shift = is_power ? ADM1278_PWR_AVG_SHIFT : ADM1278_VI_AVG_SHIFT;
+ } else {
+ ret = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
+ mask = ADM1275_VI_AVG_MASK;
+ shift = ADM1275_VI_AVG_SHIFT;
+ }
+ if (ret < 0)
+ return ret;
+
+ word = (ret & ~mask) | ((word << shift) & mask);
+ if (data->have_power_sampling)
+ ret = i2c_smbus_write_word_data(client, ADM1275_PMON_CONFIG,
+ word);
+ else
+ ret = i2c_smbus_write_byte_data(client, ADM1275_PMON_CONFIG,
+ word);
+
+ return ret;
+}
+
+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);
+ int ret = 0;
+
+ if (page > 0)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_IOUT_UC_FAULT_LIMIT:
+ if (!data->have_uc_fault)
+ return -ENXIO;
+ 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, 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, 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, 0xff,
+ ADM1075_VAUX_UV_WARN_LIMIT);
+ break;
+ case PMBUS_READ_VOUT:
+ if (data->have_vout)
+ return -ENODATA;
+ 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, 0xff,
+ ADM1293_IOUT_MIN);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ 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, 0xff,
+ ADM1275_PEAK_VOUT);
+ break;
+ case PMBUS_VIRT_READ_VIN_MAX:
+ 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, 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, 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, 0xff,
+ ADM1278_PEAK_TEMP);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ if (!data->have_pin_max)
+ return -ENXIO;
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ if (!data->have_temp_max)
+ return -ENXIO;
+ break;
+ case PMBUS_VIRT_POWER_SAMPLES:
+ if (!data->have_power_sampling)
+ return -ENXIO;
+ ret = adm1275_read_pmon_config(data, client, true);
+ if (ret < 0)
+ break;
+ ret = BIT(ret);
+ break;
+ case PMBUS_VIRT_IN_SAMPLES:
+ case PMBUS_VIRT_CURR_SAMPLES:
+ ret = adm1275_read_pmon_config(data, client, false);
+ if (ret < 0)
+ break;
+ ret = BIT(ret);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct adm1275_data *data = to_adm1275_data(info);
+ int ret;
+
+ if (page > 0)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_IOUT_UC_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT,
+ word);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
+ if (!ret && data->have_iout_min)
+ ret = pmbus_write_word_data(client, 0,
+ ADM1293_IOUT_MIN, 0);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0);
+ break;
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0);
+ if (!ret && data->have_pin_min)
+ ret = pmbus_write_word_data(client, 0,
+ ADM1293_PIN_MIN, 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, 0, ADM1278_PEAK_TEMP, 0);
+ break;
+ case PMBUS_VIRT_POWER_SAMPLES:
+ if (!data->have_power_sampling)
+ return -ENXIO;
+ word = clamp_val(word, 1, ADM1275_SAMPLES_AVG_MAX);
+ ret = adm1275_write_pmon_config(data, client, true,
+ ilog2(word));
+ break;
+ case PMBUS_VIRT_IN_SAMPLES:
+ case PMBUS_VIRT_CURR_SAMPLES:
+ word = clamp_val(word, 1, ADM1275_SAMPLES_AVG_MAX);
+ ret = adm1275_write_pmon_config(data, client, false,
+ ilog2(word));
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct adm1275_data *data = to_adm1275_data(info);
+ int mfr_status, ret;
+
+ if (page > 0)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_STATUS_IOUT:
+ ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT);
+ if (ret < 0)
+ break;
+ if (!data->have_oc_fault && !data->have_uc_fault)
+ break;
+ mfr_status = pmbus_read_byte_data(client, page,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (mfr_status < 0)
+ return mfr_status;
+ if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) {
+ ret |= data->have_oc_fault ?
+ PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT;
+ }
+ break;
+ case PMBUS_STATUS_VOUT:
+ if (data->have_vout)
+ return -ENODATA;
+ ret = 0;
+ if (data->have_vaux_status) {
+ mfr_status = pmbus_read_byte_data(client, 0,
+ ADM1075_VAUX_STATUS);
+ if (mfr_status < 0)
+ return mfr_status;
+ if (mfr_status & ADM1075_VAUX_OV_WARN)
+ ret |= PB_VOLTAGE_OV_WARNING;
+ if (mfr_status & ADM1075_VAUX_UV_WARN)
+ ret |= PB_VOLTAGE_UV_WARNING;
+ } else if (data->have_mfr_vaux_status) {
+ mfr_status = pmbus_read_byte_data(client, page,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (mfr_status < 0)
+ return mfr_status;
+ if (mfr_status & ADM1293_MFR_STATUS_VAUX_OV_WARN)
+ ret |= PB_VOLTAGE_OV_WARNING;
+ if (mfr_status & ADM1293_MFR_STATUS_VAUX_UV_WARN)
+ ret |= PB_VOLTAGE_UV_WARNING;
+ }
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id adm1275_id[] = {
+ { "adm1075", adm1075 },
+ { "adm1272", adm1272 },
+ { "adm1275", adm1275 },
+ { "adm1276", adm1276 },
+ { "adm1278", adm1278 },
+ { "adm1293", adm1293 },
+ { "adm1294", adm1294 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adm1275_id);
+
+/* Enable VOUT & TEMP1 if not enabled (disabled by default) */
+static int adm1275_enable_vout_temp(struct i2c_client *client, int config)
+{
+ int ret;
+
+ if ((config & ADM1278_PMON_DEFCONFIG) != ADM1278_PMON_DEFCONFIG) {
+ config |= ADM1278_PMON_DEFCONFIG;
+ ret = i2c_smbus_write_word_data(client, ADM1275_PMON_CONFIG, config);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to enable VOUT/TEMP1 monitoring\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+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;
+ struct pmbus_driver_info *info;
+ struct adm1275_data *data;
+ const struct i2c_device_id *mid;
+ const struct coefficients *coefficients;
+ 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
+ | I2C_FUNC_SMBUS_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read Manufacturer ID\n");
+ return ret;
+ }
+ if (ret != 3 || strncmp(block_buffer, "ADI", 3)) {
+ dev_err(&client->dev, "Unsupported Manufacturer ID\n");
+ return -ENODEV;
+ }
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read Manufacturer Model\n");
+ return ret;
+ }
+ for (mid = adm1275_id; mid->name[0]; mid++) {
+ if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
+ break;
+ }
+ if (!mid->name[0]) {
+ dev_err(&client->dev, "Unsupported device\n");
+ return -ENODEV;
+ }
+
+ if (strcmp(client->name, mid->name) != 0)
+ dev_notice(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ client->name, mid->name);
+
+ 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 = config_read_fn(client, ADM1275_DEVICE_CONFIG);
+ if (device_config < 0)
+ return device_config;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct adm1275_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (of_property_read_u32(client->dev.of_node,
+ "shunt-resistor-micro-ohms", &shunt))
+ shunt = 1000; /* 1 mOhm if not set via DT */
+
+ if (shunt == 0)
+ return -EINVAL;
+
+ data->id = mid->driver_data;
+
+ info = &data->info;
+
+ info->pages = 1;
+ info->format[PSC_VOLTAGE_IN] = direct;
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ info->format[PSC_CURRENT_OUT] = direct;
+ info->format[PSC_POWER] = direct;
+ info->format[PSC_TEMPERATURE] = direct;
+ info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_SAMPLES;
+
+ info->read_word_data = adm1275_read_word_data;
+ info->read_byte_data = adm1275_read_byte_data;
+ info->write_word_data = adm1275_write_word_data;
+
+ switch (data->id) {
+ case adm1075:
+ if (device_config & ADM1275_IOUT_WARN2_SELECT)
+ data->have_oc_fault = true;
+ else
+ data->have_uc_fault = true;
+ data->have_pin_max = true;
+ data->have_vaux_status = true;
+
+ coefficients = adm1075_coefficients;
+ vindex = 0;
+ switch (config & ADM1075_IRANGE_MASK) {
+ case ADM1075_IRANGE_25:
+ cindex = 1;
+ pindex = 3;
+ break;
+ case ADM1075_IRANGE_50:
+ cindex = 2;
+ pindex = 4;
+ break;
+ default:
+ dev_err(&client->dev, "Invalid input current range");
+ break;
+ }
+
+ info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
+ | PMBUS_HAVE_STATUS_INPUT;
+ if (config & ADM1275_VIN_VOUT_SELECT)
+ info->func[0] |=
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ break;
+ case adm1272:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+ data->have_power_sampling = true;
+
+ coefficients = adm1272_coefficients;
+ vindex = (config & ADM1275_VRANGE) ? 1 : 0;
+ cindex = (config & ADM1272_IRANGE) ? 3 : 2;
+ /* pindex depends on the combination of the above */
+ switch (config & (ADM1275_VRANGE | ADM1272_IRANGE)) {
+ case 0:
+ default:
+ pindex = 4;
+ break;
+ case ADM1275_VRANGE:
+ pindex = 5;
+ break;
+ case ADM1272_IRANGE:
+ pindex = 6;
+ break;
+ case ADM1275_VRANGE | ADM1272_IRANGE:
+ pindex = 7;
+ break;
+ }
+ tindex = 8;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = adm1275_enable_vout_temp(client, config);
+ if (ret)
+ return ret;
+
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+ break;
+ case adm1275:
+ if (device_config & ADM1275_IOUT_WARN2_SELECT)
+ data->have_oc_fault = true;
+ else
+ data->have_uc_fault = true;
+ data->have_vout = true;
+
+ coefficients = adm1275_coefficients;
+ vindex = (config & ADM1275_VRANGE) ? 0 : 1;
+ cindex = 2;
+
+ if (config & ADM1275_VIN_VOUT_SELECT)
+ info->func[0] |=
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ else
+ info->func[0] |=
+ PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
+ break;
+ case adm1276:
+ if (device_config & ADM1275_IOUT_WARN2_SELECT)
+ data->have_oc_fault = true;
+ else
+ data->have_uc_fault = true;
+ data->have_vout = true;
+ data->have_pin_max = true;
+
+ coefficients = adm1276_coefficients;
+ vindex = (config & ADM1275_VRANGE) ? 0 : 1;
+ cindex = 2;
+ pindex = (config & ADM1275_VRANGE) ? 3 : 4;
+
+ info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
+ | PMBUS_HAVE_STATUS_INPUT;
+ if (config & ADM1275_VIN_VOUT_SELECT)
+ info->func[0] |=
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ break;
+ case adm1278:
+ data->have_vout = true;
+ data->have_pin_max = true;
+ data->have_temp_max = true;
+ data->have_power_sampling = true;
+
+ coefficients = adm1278_coefficients;
+ vindex = 0;
+ cindex = 1;
+ pindex = 2;
+ tindex = 3;
+
+ info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ ret = adm1275_enable_vout_temp(client, config);
+ if (ret)
+ return ret;
+
+ if (config & ADM1278_VIN_EN)
+ info->func[0] |= PMBUS_HAVE_VIN;
+ break;
+ case adm1293:
+ case adm1294:
+ data->have_iout_min = true;
+ data->have_pin_min = true;
+ data->have_pin_max = true;
+ data->have_mfr_vaux_status = true;
+ data->have_power_sampling = true;
+
+ coefficients = adm1293_coefficients;
+
+ voindex = 0;
+ switch (config & ADM1293_VIN_SEL_MASK) {
+ case ADM1293_VIN_SEL_012: /* 1.2V */
+ vindex = 0;
+ break;
+ case ADM1293_VIN_SEL_074: /* 7.4V */
+ vindex = 1;
+ break;
+ case ADM1293_VIN_SEL_210: /* 21V */
+ vindex = 2;
+ break;
+ default: /* disabled */
+ break;
+ }
+
+ switch (config & ADM1293_IRANGE_MASK) {
+ case ADM1293_IRANGE_25:
+ cindex = 3;
+ break;
+ case ADM1293_IRANGE_50:
+ cindex = 4;
+ break;
+ case ADM1293_IRANGE_100:
+ cindex = 5;
+ break;
+ case ADM1293_IRANGE_200:
+ cindex = 6;
+ break;
+ }
+
+ if (vindex >= 0)
+ pindex = 7 + vindex * 4 + (cindex - 3);
+
+ if (config & ADM1293_VAUX_EN)
+ info->func[0] |=
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+
+ info->func[0] |= PMBUS_HAVE_PIN |
+ PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
+
+ break;
+ default:
+ dev_err(&client->dev, "Unsupported device\n");
+ 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) {
+ info->m[PSC_VOLTAGE_IN] = coefficients[vindex].m;
+ info->b[PSC_VOLTAGE_IN] = coefficients[vindex].b;
+ info->R[PSC_VOLTAGE_IN] = coefficients[vindex].R;
+ }
+ if (voindex >= 0) {
+ info->m[PSC_VOLTAGE_OUT] = coefficients[voindex].m;
+ info->b[PSC_VOLTAGE_OUT] = coefficients[voindex].b;
+ info->R[PSC_VOLTAGE_OUT] = coefficients[voindex].R;
+ }
+ if (cindex >= 0) {
+ /* Scale current with sense resistor value */
+ info->m[PSC_CURRENT_OUT] =
+ coefficients[cindex].m * shunt / 1000;
+ info->b[PSC_CURRENT_OUT] = coefficients[cindex].b;
+ info->R[PSC_CURRENT_OUT] = coefficients[cindex].R;
+ }
+ if (pindex >= 0) {
+ info->m[PSC_POWER] =
+ coefficients[pindex].m * shunt / 1000;
+ info->b[PSC_POWER] = coefficients[pindex].b;
+ info->R[PSC_POWER] = coefficients[pindex].R;
+ }
+ if (tindex >= 0) {
+ info->m[PSC_TEMPERATURE] = coefficients[tindex].m;
+ info->b[PSC_TEMPERATURE] = coefficients[tindex].b;
+ info->R[PSC_TEMPERATURE] = coefficients[tindex].R;
+ }
+
+ return pmbus_do_probe(client, info);
+}
+
+static struct i2c_driver adm1275_driver = {
+ .driver = {
+ .name = "adm1275",
+ },
+ .probe_new = adm1275_probe,
+ .id_table = adm1275_id,
+};
+
+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
new file mode 100644
index 000000000..61c195f8f
--- /dev/null
+++ b/drivers/hwmon/pmbus/bel-pfe.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for BEL PFE family power supplies.
+ *
+ * Copyright (c) 2019 Facebook Inc.
+ */
+
+#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"
+
+enum chips {pfe1100, pfe3000};
+
+/*
+ * Disable status check because some devices report communication error
+ * (invalid command) for VOUT_MODE command (0x20) although the correct
+ * VOUT_MODE (0x16) is returned: it leads to incorrect exponent in linear
+ * mode.
+ * This affects both pfe3000 and pfe1100.
+ */
+static struct pmbus_platform_data pfe_plat_data = {
+ .flags = PMBUS_SKIP_STATUS_CHECK,
+};
+
+static struct pmbus_driver_info pfe_driver_info[] = {
+ [pfe1100] = {
+ .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_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT |
+ PMBUS_HAVE_VIN | PMBUS_HAVE_IIN |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
+ PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_FAN12,
+ },
+
+ [pfe3000] = {
+ .pages = 7,
+ .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,
+
+ /* Page 0: V1. */
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_FAN12 |
+ PMBUS_HAVE_VIN | PMBUS_HAVE_IIN |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
+ PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_VCAP,
+
+ /* Page 1: Vsb. */
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_POUT,
+
+ /*
+ * Page 2: V1 Ishare.
+ * Page 3: Reserved.
+ * Page 4: V1 Cathode.
+ * Page 5: Vsb Cathode.
+ * Page 6: V1 Sense.
+ */
+ .func[2] = PMBUS_HAVE_VOUT,
+ .func[4] = PMBUS_HAVE_VOUT,
+ .func[5] = PMBUS_HAVE_VOUT,
+ .func[6] = PMBUS_HAVE_VOUT,
+ },
+};
+
+static const struct i2c_device_id pfe_device_id[];
+
+static int pfe_pmbus_probe(struct i2c_client *client)
+{
+ int model;
+
+ model = (int)i2c_match_id(pfe_device_id, client)->driver_data;
+ client->dev.platform_data = &pfe_plat_data;
+
+ /*
+ * PFE3000-12-069RA devices may not stay in page 0 during device
+ * probe which leads to probe failure (read status word failed).
+ * So let's set the device to page 0 at the beginning.
+ */
+ if (model == pfe3000)
+ i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+
+ return pmbus_do_probe(client, &pfe_driver_info[model]);
+}
+
+static const struct i2c_device_id pfe_device_id[] = {
+ {"pfe1100", pfe1100},
+ {"pfe3000", pfe3000},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, pfe_device_id);
+
+static struct i2c_driver pfe_pmbus_driver = {
+ .driver = {
+ .name = "bel-pfe",
+ },
+ .probe_new = pfe_pmbus_probe,
+ .id_table = pfe_device_id,
+};
+
+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 000000000..f2d4e378a
--- /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 000000000..f546f0c12
--- /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 000000000..d3941f6eb
--- /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 000000000..c7469d2cd
--- /dev/null
+++ b/drivers/hwmon/pmbus/fsp-3y.c
@@ -0,0 +1,294 @@
+// 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,
+ .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
new file mode 100644
index 000000000..e3294a1a5
--- /dev/null
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2017 IBM Corp.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/pmbus.h>
+
+#include "pmbus.h"
+
+#define CFFPS_MFG_ID_CMD 0x99
+#define CFFPS_FRU_CMD 0x9A
+#define CFFPS_PN_CMD 0x9B
+#define CFFPS_HEADER_CMD 0x9C
+#define CFFPS_SN_CMD 0x9E
+#define CFFPS_MAX_POWER_OUT_CMD 0xA7
+#define CFFPS_CCIN_CMD 0xBD
+#define CFFPS_FW_CMD 0xFA
+#define CFFPS1_FW_NUM_BYTES 4
+#define CFFPS2_FW_NUM_WORDS 3
+#define CFFPS_SYS_CONFIG_CMD 0xDA
+#define CFFPS_12VCS_VOUT_CMD 0xDE
+
+#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)
+#define CFFPS_MFR_THERMAL_FAULT BIT(1)
+#define CFFPS_MFR_OV_FAULT BIT(2)
+#define CFFPS_MFR_UV_FAULT BIT(3)
+#define CFFPS_MFR_PS_KILL BIT(4)
+#define CFFPS_MFR_OC_FAULT BIT(5)
+#define CFFPS_MFR_VAUX_FAULT BIT(6)
+#define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7)
+
+#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,
+ CFFPS_DEBUGFS_SN,
+ CFFPS_DEBUGFS_MAX_POWER_OUT,
+ CFFPS_DEBUGFS_CCIN,
+ CFFPS_DEBUGFS_FW,
+ CFFPS_DEBUGFS_ON_OFF_CONFIG,
+ CFFPS_DEBUGFS_NUM_ENTRIES
+};
+
+enum versions { cffps1, cffps2, cffps_unknown };
+
+struct ibm_cffps_input_history {
+ struct mutex update_lock;
+ unsigned long last_update;
+
+ u8 byte_count;
+ u8 data[CFFPS_INPUT_HISTORY_SIZE];
+};
+
+struct ibm_cffps {
+ enum versions version;
+ struct i2c_client *client;
+
+ struct ibm_cffps_input_history input_history;
+
+ int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
+
+ char led_name[32];
+ u8 led_state;
+ 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,
+ char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int rc;
+ u8 msgbuf0[1] = { CFFPS_INPUT_HISTORY_CMD };
+ u8 msgbuf1[CFFPS_INPUT_HISTORY_SIZE + 1] = { 0 };
+ struct i2c_msg msg[2] = {
+ {
+ .addr = psu->client->addr,
+ .flags = psu->client->flags,
+ .len = 1,
+ .buf = msgbuf0,
+ }, {
+ .addr = psu->client->addr,
+ .flags = psu->client->flags | I2C_M_RD,
+ .len = CFFPS_INPUT_HISTORY_SIZE + 1,
+ .buf = msgbuf1,
+ },
+ };
+
+ if (!*ppos) {
+ mutex_lock(&psu->input_history.update_lock);
+ if (time_after(jiffies, psu->input_history.last_update + HZ)) {
+ /*
+ * Use a raw i2c transfer, since we need more bytes
+ * than Linux I2C supports through smbus xfr (only 32).
+ */
+ rc = i2c_transfer(psu->client->adapter, msg, 2);
+ if (rc < 0) {
+ mutex_unlock(&psu->input_history.update_lock);
+ return rc;
+ }
+
+ psu->input_history.byte_count = msgbuf1[0];
+ memcpy(psu->input_history.data, &msgbuf1[1],
+ CFFPS_INPUT_HISTORY_SIZE);
+ psu->input_history.last_update = jiffies;
+ }
+
+ mutex_unlock(&psu->input_history.update_lock);
+ }
+
+ return simple_read_from_buffer(buf, count, ppos,
+ psu->input_history.data,
+ psu->input_history.byte_count);
+}
+
+static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ u8 cmd;
+ int i, rc;
+ int *idxp = file->private_data;
+ int idx = *idxp;
+ struct ibm_cffps *psu = to_psu(idxp, idx);
+ char data[I2C_SMBUS_BLOCK_MAX + 2] = { 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;
+ case CFFPS_DEBUGFS_PN:
+ cmd = CFFPS_PN_CMD;
+ break;
+ case CFFPS_DEBUGFS_HEADER:
+ cmd = CFFPS_HEADER_CMD;
+ break;
+ case CFFPS_DEBUGFS_SN:
+ cmd = CFFPS_SN_CMD;
+ break;
+ case CFFPS_DEBUGFS_MAX_POWER_OUT:
+ 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;
+
+ rc = snprintf(data, I2C_SMBUS_BLOCK_MAX, "%d", rc);
+ goto done;
+ case CFFPS_DEBUGFS_CCIN:
+ rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD);
+ if (rc < 0)
+ return rc;
+
+ rc = snprintf(data, 5, "%04X", rc);
+ goto done;
+ case CFFPS_DEBUGFS_FW:
+ switch (psu->version) {
+ case cffps1:
+ for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {
+ rc = i2c_smbus_read_byte_data(psu->client,
+ CFFPS_FW_CMD +
+ i);
+ if (rc < 0)
+ return rc;
+
+ snprintf(&data[i * 2], 3, "%02X", rc);
+ }
+
+ rc = i * 2;
+ break;
+ case cffps2:
+ for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {
+ rc = i2c_smbus_read_word_data(psu->client,
+ CFFPS_FW_CMD +
+ i);
+ if (rc < 0)
+ return rc;
+
+ snprintf(&data[i * 4], 5, "%04X", rc);
+ }
+
+ rc = i * 4;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ goto done;
+ case CFFPS_DEBUGFS_ON_OFF_CONFIG:
+ rc = i2c_smbus_read_byte_data(psu->client,
+ PMBUS_ON_OFF_CONFIG);
+ if (rc < 0)
+ return rc;
+
+ rc = snprintf(data, 3, "%02x", rc);
+ goto done;
+ default:
+ return -EINVAL;
+ }
+
+ rc = i2c_smbus_read_block_data(psu->client, cmd, data);
+ if (rc < 0)
+ return rc;
+
+done:
+ data[rc] = '\n';
+ rc += 2;
+
+ return simple_read_from_buffer(buf, count, ppos, data, rc);
+}
+
+static ssize_t ibm_cffps_debugfs_write(struct file *file,
+ const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ u8 data;
+ ssize_t rc;
+ int *idxp = file->private_data;
+ int idx = *idxp;
+ struct ibm_cffps *psu = to_psu(idxp, idx);
+
+ switch (idx) {
+ case CFFPS_DEBUGFS_ON_OFF_CONFIG:
+ pmbus_set_page(psu->client, 0, 0xff);
+
+ rc = simple_write_to_buffer(&data, 1, ppos, buf, count);
+ if (rc <= 0)
+ return rc;
+
+ rc = i2c_smbus_write_byte_data(psu->client,
+ PMBUS_ON_OFF_CONFIG, data);
+ if (rc)
+ return rc;
+
+ rc = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static const struct file_operations ibm_cffps_fops = {
+ .llseek = noop_llseek,
+ .read = ibm_cffps_debugfs_read,
+ .write = ibm_cffps_debugfs_write,
+ .open = simple_open,
+};
+
+static int ibm_cffps_read_byte_data(struct i2c_client *client, int page,
+ int reg)
+{
+ int rc, mfr;
+
+ switch (reg) {
+ case PMBUS_STATUS_VOUT:
+ case PMBUS_STATUS_IOUT:
+ case PMBUS_STATUS_TEMPERATURE:
+ case PMBUS_STATUS_FAN_12:
+ rc = pmbus_read_byte_data(client, page, reg);
+ if (rc < 0)
+ return rc;
+
+ mfr = pmbus_read_byte_data(client, page,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (mfr < 0)
+ /*
+ * Return the status register instead of an error,
+ * since we successfully read status.
+ */
+ return rc;
+
+ /* Add MFR_SPECIFIC bits to the standard pmbus status regs. */
+ if (reg == PMBUS_STATUS_FAN_12) {
+ if (mfr & CFFPS_MFR_FAN_FAULT)
+ rc |= PB_FAN_FAN1_FAULT;
+ } else if (reg == PMBUS_STATUS_TEMPERATURE) {
+ if (mfr & CFFPS_MFR_THERMAL_FAULT)
+ rc |= PB_TEMP_OT_FAULT;
+ } else if (reg == PMBUS_STATUS_VOUT) {
+ if (mfr & (CFFPS_MFR_OV_FAULT | CFFPS_MFR_VAUX_FAULT))
+ rc |= PB_VOLTAGE_OV_FAULT;
+ if (mfr & CFFPS_MFR_UV_FAULT)
+ rc |= PB_VOLTAGE_UV_FAULT;
+ } else if (reg == PMBUS_STATUS_IOUT) {
+ if (mfr & CFFPS_MFR_OC_FAULT)
+ rc |= PB_IOUT_OC_FAULT;
+ if (mfr & CFFPS_MFR_CURRENT_SHARE_WARNING)
+ rc |= PB_CURRENT_SHARE_FAULT;
+ }
+ break;
+ default:
+ rc = -ENODATA;
+ break;
+ }
+
+ return rc;
+}
+
+static int ibm_cffps_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ int rc, mfr;
+
+ switch (reg) {
+ case PMBUS_STATUS_WORD:
+ rc = pmbus_read_word_data(client, page, phase, reg);
+ if (rc < 0)
+ return rc;
+
+ mfr = pmbus_read_byte_data(client, page,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (mfr < 0)
+ /*
+ * Return the status register instead of an error,
+ * since we successfully read status.
+ */
+ return rc;
+
+ if (mfr & CFFPS_MFR_PS_KILL)
+ rc |= PB_STATUS_OFF;
+ break;
+ case PMBUS_VIRT_READ_VMON:
+ rc = pmbus_read_word_data(client, page, phase,
+ CFFPS_12VCS_VOUT_CMD);
+ break;
+ default:
+ rc = -ENODATA;
+ break;
+ }
+
+ return rc;
+}
+
+static int ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int rc;
+ u8 next_led_state;
+ struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+ if (brightness == LED_OFF) {
+ next_led_state = CFFPS_LED_OFF;
+ } else {
+ brightness = LED_FULL;
+
+ if (psu->led_state != CFFPS_LED_BLINK)
+ next_led_state = CFFPS_LED_ON;
+ else
+ next_led_state = CFFPS_LED_BLINK;
+ }
+
+ dev_dbg(&psu->client->dev, "LED brightness set: %d. Command: %d.\n",
+ brightness, next_led_state);
+
+ pmbus_set_page(psu->client, 0, 0xff);
+
+ rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+ next_led_state);
+ if (rc < 0)
+ return rc;
+
+ psu->led_state = next_led_state;
+ led_cdev->brightness = brightness;
+
+ return 0;
+}
+
+static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ int rc;
+ struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);
+
+ dev_dbg(&psu->client->dev, "LED blink set.\n");
+
+ pmbus_set_page(psu->client, 0, 0xff);
+
+ rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
+ CFFPS_LED_BLINK);
+ if (rc < 0)
+ return rc;
+
+ psu->led_state = CFFPS_LED_BLINK;
+ led_cdev->brightness = LED_FULL;
+ *delay_on = CFFPS_BLINK_RATE_MS;
+ *delay_off = CFFPS_BLINK_RATE_MS;
+
+ return 0;
+}
+
+static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
+{
+ int rc;
+ struct i2c_client *client = psu->client;
+ struct device *dev = &client->dev;
+
+ snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,
+ client->addr);
+ psu->led.name = psu->led_name;
+ psu->led.max_brightness = LED_FULL;
+ psu->led.brightness_set_blocking = ibm_cffps_led_brightness_set;
+ psu->led.blink_set = ibm_cffps_led_blink_set;
+
+ rc = devm_led_classdev_register(dev, &psu->led);
+ if (rc)
+ dev_warn(dev, "failed to register led class: %d\n", rc);
+ else
+ i2c_smbus_write_byte_data(client, CFFPS_SYS_CONFIG_CMD,
+ CFFPS_LED_OFF);
+}
+
+static struct pmbus_driver_info ibm_cffps_info[] = {
+ [cffps1] = {
+ .pages = 1,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
+ PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
+ PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_STATUS_FAN12,
+ .read_byte_data = ibm_cffps_read_byte_data,
+ .read_word_data = ibm_cffps_read_word_data,
+ },
+ [cffps2] = {
+ .pages = 2,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
+ PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
+ PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_VMON,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
+ PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT,
+ .read_byte_data = ibm_cffps_read_byte_data,
+ .read_word_data = ibm_cffps_read_word_data,
+ },
+};
+
+static struct pmbus_platform_data ibm_cffps_pdata = {
+ .flags = PMBUS_SKIP_STATUS_CHECK | PMBUS_NO_CAPABILITY,
+};
+
+static int ibm_cffps_probe(struct i2c_client *client)
+{
+ int i, rc;
+ enum versions vs = cffps_unknown;
+ struct dentry *debugfs;
+ 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) {
+ vs = (enum versions)md;
+ } 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) {
+ 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:
+ 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. */
+ snprintf(client->name, I2C_NAME_SIZE, "cffps%d", vs + 1);
+ }
+
+ client->dev.platform_data = &ibm_cffps_pdata;
+ rc = pmbus_do_probe(client, &ibm_cffps_info[vs]);
+ if (rc)
+ return rc;
+
+ /*
+ * Don't fail the probe if there isn't enough memory for leds and
+ * debugfs.
+ */
+ psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
+ if (!psu)
+ return 0;
+
+ psu->version = vs;
+ psu->client = client;
+ mutex_init(&psu->input_history.update_lock);
+ psu->input_history.last_update = jiffies - HZ;
+
+ ibm_cffps_create_led_class(psu);
+
+ /* Don't fail the probe if we can't create debugfs */
+ debugfs = pmbus_get_debugfs_dir(client);
+ if (!debugfs)
+ return 0;
+
+ ibm_cffps_dir = debugfs_create_dir(client->name, debugfs);
+ if (!ibm_cffps_dir)
+ return 0;
+
+ for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)
+ psu->debugfs_entries[i] = i;
+
+ 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);
+ debugfs_create_file("part_number", 0444, ibm_cffps_dir,
+ &psu->debugfs_entries[CFFPS_DEBUGFS_PN],
+ &ibm_cffps_fops);
+ debugfs_create_file("header", 0444, ibm_cffps_dir,
+ &psu->debugfs_entries[CFFPS_DEBUGFS_HEADER],
+ &ibm_cffps_fops);
+ debugfs_create_file("serial_number", 0444, ibm_cffps_dir,
+ &psu->debugfs_entries[CFFPS_DEBUGFS_SN],
+ &ibm_cffps_fops);
+ debugfs_create_file("max_power_out", 0444, ibm_cffps_dir,
+ &psu->debugfs_entries[CFFPS_DEBUGFS_MAX_POWER_OUT],
+ &ibm_cffps_fops);
+ debugfs_create_file("ccin", 0444, ibm_cffps_dir,
+ &psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],
+ &ibm_cffps_fops);
+ debugfs_create_file("fw_version", 0444, ibm_cffps_dir,
+ &psu->debugfs_entries[CFFPS_DEBUGFS_FW],
+ &ibm_cffps_fops);
+ debugfs_create_file("on_off_config", 0644, ibm_cffps_dir,
+ &psu->debugfs_entries[CFFPS_DEBUGFS_ON_OFF_CONFIG],
+ &ibm_cffps_fops);
+
+ return 0;
+}
+
+static const struct i2c_device_id ibm_cffps_id[] = {
+ { "ibm_cffps1", cffps1 },
+ { "ibm_cffps2", cffps2 },
+ { "ibm_cffps", cffps_unknown },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
+
+static const struct of_device_id ibm_cffps_of_match[] = {
+ {
+ .compatible = "ibm,cffps1",
+ .data = (void *)cffps1
+ },
+ {
+ .compatible = "ibm,cffps2",
+ .data = (void *)cffps2
+ },
+ {
+ .compatible = "ibm,cffps",
+ .data = (void *)cffps_unknown
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
+
+static struct i2c_driver ibm_cffps_driver = {
+ .driver = {
+ .name = "ibm-cffps",
+ .of_match_table = ibm_cffps_of_match,
+ },
+ .probe_new = ibm_cffps_probe,
+ .id_table = ibm_cffps_id,
+};
+
+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
new file mode 100644
index 000000000..0f614e8d9
--- /dev/null
+++ b/drivers/hwmon/pmbus/inspur-ipsps.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Inspur Corp.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pmbus.h>
+#include <linux/hwmon-sysfs.h>
+
+#include "pmbus.h"
+
+#define IPSPS_REG_VENDOR_ID 0x99
+#define IPSPS_REG_MODEL 0x9A
+#define IPSPS_REG_FW_VERSION 0x9B
+#define IPSPS_REG_PN 0x9C
+#define IPSPS_REG_SN 0x9E
+#define IPSPS_REG_HW_VERSION 0xB0
+#define IPSPS_REG_MODE 0xFC
+
+#define MODE_ACTIVE 0x55
+#define MODE_STANDBY 0x0E
+#define MODE_REDUNDANCY 0x00
+
+#define MODE_ACTIVE_STRING "active"
+#define MODE_STANDBY_STRING "standby"
+#define MODE_REDUNDANCY_STRING "redundancy"
+
+enum ipsps_index {
+ vendor,
+ model,
+ fw_version,
+ part_number,
+ serial_number,
+ hw_version,
+ mode,
+ num_regs,
+};
+
+static const u8 ipsps_regs[num_regs] = {
+ [vendor] = IPSPS_REG_VENDOR_ID,
+ [model] = IPSPS_REG_MODEL,
+ [fw_version] = IPSPS_REG_FW_VERSION,
+ [part_number] = IPSPS_REG_PN,
+ [serial_number] = IPSPS_REG_SN,
+ [hw_version] = IPSPS_REG_HW_VERSION,
+ [mode] = IPSPS_REG_MODE,
+};
+
+static ssize_t ipsps_string_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ u8 reg;
+ int rc;
+ char *p;
+ char data[I2C_SMBUS_BLOCK_MAX + 1];
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ reg = ipsps_regs[attr->index];
+ rc = i2c_smbus_read_block_data(client, reg, data);
+ if (rc < 0)
+ return rc;
+
+ /* filled with printable characters, ending with # */
+ p = memscan(data, '#', rc);
+ *p = '\0';
+
+ return sysfs_emit(buf, "%s\n", data);
+}
+
+static ssize_t ipsps_fw_version_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ u8 reg;
+ int rc;
+ u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 };
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ reg = ipsps_regs[attr->index];
+ rc = i2c_smbus_read_block_data(client, reg, data);
+ if (rc < 0)
+ return rc;
+
+ if (rc != 6)
+ return -EPROTO;
+
+ 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,
+ struct device_attribute *devattr, char *buf)
+{
+ u8 reg;
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ reg = ipsps_regs[attr->index];
+ rc = i2c_smbus_read_byte_data(client, reg);
+ if (rc < 0)
+ return rc;
+
+ switch (rc) {
+ case MODE_ACTIVE:
+ return sysfs_emit(buf, "[%s] %s %s\n",
+ MODE_ACTIVE_STRING,
+ MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
+ case MODE_STANDBY:
+ return sysfs_emit(buf, "%s [%s] %s\n",
+ MODE_ACTIVE_STRING,
+ MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
+ case MODE_REDUNDANCY:
+ return sysfs_emit(buf, "%s %s [%s]\n",
+ MODE_ACTIVE_STRING,
+ MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
+ default:
+ return sysfs_emit(buf, "unspecified\n");
+ }
+}
+
+static ssize_t ipsps_mode_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ u8 reg;
+ int rc;
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ reg = ipsps_regs[attr->index];
+ if (sysfs_streq(MODE_STANDBY_STRING, buf)) {
+ rc = i2c_smbus_write_byte_data(client, reg,
+ MODE_STANDBY);
+ if (rc < 0)
+ return rc;
+ return count;
+ } else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) {
+ rc = i2c_smbus_write_byte_data(client, reg,
+ MODE_ACTIVE);
+ if (rc < 0)
+ return rc;
+ return count;
+ }
+
+ return -EINVAL;
+}
+
+static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor);
+static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model);
+static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number);
+static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number);
+static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version);
+static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version);
+static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode);
+
+static struct attribute *ipsps_attrs[] = {
+ &sensor_dev_attr_vendor.dev_attr.attr,
+ &sensor_dev_attr_model.dev_attr.attr,
+ &sensor_dev_attr_part_number.dev_attr.attr,
+ &sensor_dev_attr_serial_number.dev_attr.attr,
+ &sensor_dev_attr_hw_version.dev_attr.attr,
+ &sensor_dev_attr_fw_version.dev_attr.attr,
+ &sensor_dev_attr_mode.dev_attr.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(ipsps);
+
+static struct pmbus_driver_info ipsps_info = {
+ .pages = 1,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
+ PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12,
+ .groups = ipsps_groups,
+};
+
+static struct pmbus_platform_data ipsps_pdata = {
+ .flags = PMBUS_SKIP_STATUS_CHECK,
+};
+
+static int ipsps_probe(struct i2c_client *client)
+{
+ client->dev.platform_data = &ipsps_pdata;
+ return pmbus_do_probe(client, &ipsps_info);
+}
+
+static const struct i2c_device_id ipsps_id[] = {
+ { "ipsps1", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ipsps_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id ipsps_of_match[] = {
+ { .compatible = "inspur,ipsps1" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ipsps_of_match);
+#endif
+
+static struct i2c_driver ipsps_driver = {
+ .driver = {
+ .name = "inspur-ipsps",
+ .of_match_table = of_match_ptr(ipsps_of_match),
+ },
+ .probe_new = ipsps_probe,
+ .id_table = ipsps_id,
+};
+
+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
new file mode 100644
index 000000000..a6cf98e49
--- /dev/null
+++ b/drivers/hwmon/pmbus/ir35221.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for IR35221
+ *
+ * Copyright (C) IBM Corporation 2017.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define IR35221_MFR_VIN_PEAK 0xc5
+#define IR35221_MFR_VOUT_PEAK 0xc6
+#define IR35221_MFR_IOUT_PEAK 0xc7
+#define IR35221_MFR_TEMP_PEAK 0xc8
+#define IR35221_MFR_VIN_VALLEY 0xc9
+#define IR35221_MFR_VOUT_VALLEY 0xca
+#define IR35221_MFR_IOUT_VALLEY 0xcb
+#define IR35221_MFR_TEMP_VALLEY 0xcc
+
+static 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, phase,
+ IR35221_MFR_VIN_PEAK);
+ break;
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ 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, phase,
+ IR35221_MFR_IOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ 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, phase,
+ IR35221_MFR_VIN_VALLEY);
+ break;
+ case PMBUS_VIRT_READ_VOUT_MIN:
+ 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, phase,
+ IR35221_MFR_IOUT_VALLEY);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MIN:
+ ret = pmbus_read_word_data(client, page, phase,
+ IR35221_MFR_TEMP_VALLEY);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int ir35221_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ u8 buf[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA
+ | I2C_FUNC_SMBUS_READ_WORD_DATA
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
+ return ret;
+ }
+ if (ret != 2 || strncmp(buf, "RI", strlen("RI"))) {
+ dev_err(&client->dev, "MFR_ID unrecognised\n");
+ return -ENODEV;
+ }
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n");
+ return ret;
+ }
+ if (ret != 2 || !(buf[0] == 0x6c && buf[1] == 0x00)) {
+ dev_err(&client->dev, "MFR_MODEL unrecognised\n");
+ return -ENODEV;
+ }
+
+ info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->read_word_data = ir35221_read_word_data;
+
+ info->pages = 2;
+ info->format[PSC_VOLTAGE_IN] = linear;
+ info->format[PSC_VOLTAGE_OUT] = linear;
+ info->format[PSC_CURRENT_IN] = linear;
+ info->format[PSC_CURRENT_OUT] = linear;
+ info->format[PSC_POWER] = linear;
+ info->format[PSC_TEMPERATURE] = linear;
+
+ info->func[0] = PMBUS_HAVE_VIN
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
+ | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP;
+ info->func[1] = info->func[0];
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id ir35221_id[] = {
+ {"ir35221", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ir35221_id);
+
+static struct i2c_driver ir35221_driver = {
+ .driver = {
+ .name = "ir35221",
+ },
+ .probe_new = ir35221_probe,
+ .id_table = ir35221_id,
+};
+
+module_i2c_driver(ir35221_driver);
+
+MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com");
+MODULE_DESCRIPTION("PMBus driver for IR35221");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c
new file mode 100644
index 000000000..4dca4767f
--- /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
new file mode 100644
index 000000000..09276e397
--- /dev/null
+++ b/drivers/hwmon/pmbus/ir38064.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Infineon IR38064
+ *
+ * Copyright (c) 2017 Google Inc
+ *
+ * VOUT_MODE is not supported by the device. The driver fakes VOUT linear16
+ * mode with exponent value -8 as direct mode with m=256/b=0/R=0.
+ *
+ * The device supports VOUT_PEAK, IOUT_PEAK, and TEMPERATURE_PEAK, however
+ * this driver does not currently support them.
+ */
+
+#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 <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,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .m[PSC_VOLTAGE_OUT] = 256,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 0,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
+ | 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)
+{
+ 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_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 and compatible chips");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c
new file mode 100644
index 000000000..de3449e4d
--- /dev/null
+++ b/drivers/hwmon/pmbus/irps5401.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for the Infineon IRPS5401M PMIC.
+ *
+ * Copyright (c) 2019 SED Systems, a division of Calian Ltd.
+ *
+ * The device supports VOUT_PEAK, IOUT_PEAK, and TEMPERATURE_PEAK, however
+ * this driver does not currently support them.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define IRPS5401_SW_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | \
+ PMBUS_HAVE_STATUS_INPUT | \
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
+
+#define IRPS5401_LDO_FUNC (PMBUS_HAVE_VIN | \
+ PMBUS_HAVE_STATUS_INPUT | \
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
+
+static struct pmbus_driver_info irps5401_info = {
+ .pages = 5,
+ .func[0] = IRPS5401_SW_FUNC,
+ .func[1] = IRPS5401_SW_FUNC,
+ .func[2] = IRPS5401_SW_FUNC,
+ .func[3] = IRPS5401_SW_FUNC,
+ .func[4] = IRPS5401_LDO_FUNC,
+};
+
+static int irps5401_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &irps5401_info);
+}
+
+static const struct i2c_device_id irps5401_id[] = {
+ {"irps5401", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, irps5401_id);
+
+static struct i2c_driver irps5401_driver = {
+ .driver = {
+ .name = "irps5401",
+ },
+ .probe_new = irps5401_probe,
+ .id_table = irps5401_id,
+};
+
+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
new file mode 100644
index 000000000..1a8caff1a
--- /dev/null
+++ b/drivers/hwmon/pmbus/isl68137.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Renesas Digital Multiphase Voltage Regulators
+ *
+ * Copyright (c) 2017 Google Inc
+ * Copyright (c) 2020 Renesas Electronics America
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#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,
+ char *buf)
+{
+ int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+
+ return sprintf(buf, "%d\n",
+ (val & ISL68137_VOUT_AVS) == ISL68137_VOUT_AVS ? 1 : 0);
+}
+
+static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client,
+ int page,
+ const char *buf, size_t count)
+{
+ int rc, op_val;
+ bool result;
+
+ rc = kstrtobool(buf, &result);
+ if (rc)
+ return rc;
+
+ op_val = result ? ISL68137_VOUT_AVS : 0;
+
+ /*
+ * Writes to VOUT setpoint over AVSBus will persist after the VRM is
+ * switched to PMBus control. Switching back to AVSBus control
+ * restores this persisted setpoint rather than re-initializing to
+ * PMBus VOUT_COMMAND. Writing VOUT_COMMAND first over PMBus before
+ * enabling AVS control is the workaround.
+ */
+ if (op_val == ISL68137_VOUT_AVS) {
+ rc = pmbus_read_word_data(client, page, 0xff,
+ PMBUS_VOUT_COMMAND);
+ if (rc < 0)
+ return rc;
+
+ rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND,
+ rc);
+ if (rc < 0)
+ return rc;
+ }
+
+ rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
+ ISL68137_VOUT_AVS, op_val);
+
+ return (rc < 0) ? rc : count;
+}
+
+static ssize_t isl68137_avs_enable_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return isl68137_avs_enable_show_page(client, attr->index, buf);
+}
+
+static ssize_t isl68137_avs_enable_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return isl68137_avs_enable_store_page(client, attr->index, buf, count);
+}
+
+static SENSOR_DEVICE_ATTR_RW(avs0_enable, isl68137_avs_enable, 0);
+static SENSOR_DEVICE_ATTR_RW(avs1_enable, isl68137_avs_enable, 1);
+
+static struct attribute *enable_attrs[] = {
+ &sensor_dev_attr_avs0_enable.dev_attr.attr,
+ &sensor_dev_attr_avs1_enable.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group enable_group = {
+ .attrs = enable_attrs,
+};
+
+static const struct attribute_group *isl68137_attribute_groups[] = {
+ &enable_group,
+ NULL,
+};
+
+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,
+ .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] = 2,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_CURRENT_IN] = 1,
+ .b[PSC_CURRENT_IN] = 0,
+ .R[PSC_CURRENT_IN] = 2,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 1,
+ .m[PSC_POWER] = 1,
+ .b[PSC_POWER] = 0,
+ .R[PSC_POWER] = 0,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 0,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN
+ | 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
+ | 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)
+{
+ 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 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, raa_dmpvr_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver isl68137_driver = {
+ .driver = {
+ .name = "isl68137",
+ },
+ .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 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
new file mode 100644
index 000000000..09792cd03
--- /dev/null
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for LM25056 / LM25066 / LM5064 / LM5066
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2013 Guenter Roeck
+ */
+
+#include <linux/bitops.h>
+#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/log2.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i };
+
+#define LM25066_READ_VAUX 0xd0
+#define LM25066_MFR_READ_IIN 0xd1
+#define LM25066_MFR_READ_PIN 0xd2
+#define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3
+#define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4
+#define LM25066_READ_PIN_PEAK 0xd5
+#define LM25066_CLEAR_PIN_PEAK 0xd6
+#define LM25066_DEVICE_SETUP 0xd9
+#define LM25066_READ_AVG_VIN 0xdc
+#define LM25066_SAMPLES_FOR_AVG 0xdb
+#define LM25066_READ_AVG_VOUT 0xdd
+#define LM25066_READ_AVG_IIN 0xde
+#define LM25066_READ_AVG_PIN 0xdf
+
+#define LM25066_DEV_SETUP_CL BIT(4) /* Current limit */
+
+#define LM25066_SAMPLES_FOR_AVG_MAX 4096
+
+/* LM25056 only */
+
+#define LM25056_VAUX_OV_WARN_LIMIT 0xe3
+#define LM25056_VAUX_UV_WARN_LIMIT 0xe4
+
+#define LM25056_MFR_STS_VAUX_OV_WARN BIT(1)
+#define LM25056_MFR_STS_VAUX_UV_WARN BIT(0)
+
+struct __coeff {
+ short m, b, R;
+};
+
+#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES)
+#define PSC_POWER_L (PSC_NUM_CLASSES + 1)
+
+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] = {
+ .m = 1580,
+ .b = -14500,
+ .R = -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 = 6854,
+ .b = -3100,
+ .R = -2,
+ },
+ [PSC_POWER] = {
+ .m = 736,
+ .b = -3300,
+ .R = -2,
+ },
+ [PSC_POWER_L] = {
+ .m = 369,
+ .b = -1900,
+ .R = -2,
+ },
+ [PSC_TEMPERATURE] = {
+ .m = 16,
+ },
+ },
+ [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] = {
+ .m = 16,
+ },
+ },
+ [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] = {
+ .m = 16,
+ },
+ },
+ [lm5066i] = {
+ [PSC_VOLTAGE_IN] = {
+ .m = 4617,
+ .b = -140,
+ .R = -2,
+ },
+ [PSC_VOLTAGE_OUT] = {
+ .m = 4602,
+ .b = 500,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN] = {
+ .m = 15076,
+ .b = -504,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN_L] = {
+ .m = 7645,
+ .b = 100,
+ .R = -2,
+ },
+ [PSC_POWER] = {
+ .m = 1701,
+ .b = -4000,
+ .R = -3,
+ },
+ [PSC_POWER_L] = {
+ .m = 861,
+ .b = -965,
+ .R = -3,
+ },
+ [PSC_TEMPERATURE] = {
+ .m = 16,
+ },
+ },
+};
+
+struct lm25066_data {
+ int id;
+ u16 rlimit; /* Maximum register value */
+ struct pmbus_driver_info info;
+};
+
+#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 phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct lm25066_data *data = to_lm25066_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VMON:
+ ret = pmbus_read_word_data(client, 0, 0xff, LM25066_READ_VAUX);
+ if (ret < 0)
+ break;
+ /* Adjust returned value to match VIN coefficients */
+ switch (data->id) {
+ case lm25056:
+ /* VIN: 6.14 mV VAUX: 293 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
+ break;
+ case lm25066:
+ /* VIN: 4.54 mV VAUX: 283.2 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
+ break;
+ case lm5064:
+ /* VIN: 4.53 mV VAUX: 700 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 70, 453);
+ break;
+ case lm5066:
+ case lm5066i:
+ /* VIN: 2.18 mV VAUX: 725 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
+ break;
+ }
+ break;
+ case PMBUS_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, 0xff,
+ LM25066_MFR_READ_PIN);
+ break;
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ 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, 0xff,
+ LM25066_MFR_PIN_OP_WARN_LIMIT);
+ break;
+ case PMBUS_VIRT_READ_VIN_AVG:
+ 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, 0xff,
+ LM25066_READ_AVG_VOUT);
+ break;
+ case PMBUS_VIRT_READ_IIN_AVG:
+ 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, 0xff,
+ LM25066_READ_AVG_PIN);
+ break;
+ case PMBUS_VIRT_READ_PIN_MAX:
+ ret = pmbus_read_word_data(client, 0, 0xff,
+ LM25066_READ_PIN_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ ret = 0;
+ break;
+ case PMBUS_VIRT_SAMPLES:
+ ret = pmbus_read_byte_data(client, 0, LM25066_SAMPLES_FOR_AVG);
+ if (ret < 0)
+ break;
+ ret = 1 << ret;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+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, 0xff,
+ LM25056_VAUX_UV_WARN_LIMIT);
+ if (ret < 0)
+ break;
+ /* Adjust returned value to match VIN coefficients */
+ ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
+ break;
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ ret = pmbus_read_word_data(client, 0, 0xff,
+ LM25056_VAUX_OV_WARN_LIMIT);
+ if (ret < 0)
+ break;
+ /* Adjust returned value to match VIN coefficients */
+ ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
+ break;
+ default:
+ ret = lm25066_read_word_data(client, page, phase, reg);
+ break;
+ }
+ return ret;
+}
+
+static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret, s;
+
+ switch (reg) {
+ case PMBUS_VIRT_STATUS_VMON:
+ ret = pmbus_read_byte_data(client, 0,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (ret < 0)
+ break;
+ s = 0;
+ if (ret & LM25056_MFR_STS_VAUX_UV_WARN)
+ s |= PB_VOLTAGE_UV_WARNING;
+ if (ret & LM25056_MFR_STS_VAUX_OV_WARN)
+ s |= PB_VOLTAGE_OV_WARNING;
+ ret = s;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct lm25066_data *data = to_lm25066_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_POUT_OP_FAULT_LIMIT:
+ case PMBUS_POUT_OP_WARN_LIMIT:
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ case PMBUS_IIN_OC_FAULT_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ 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);
+ 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);
+ 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);
+ break;
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ /* Adjust from VIN coefficients (for LM25056) */
+ word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
+ word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
+ ret = pmbus_write_word_data(client, 0,
+ LM25056_VAUX_UV_WARN_LIMIT, word);
+ break;
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ /* Adjust from VIN coefficients (for LM25056) */
+ word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
+ word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
+ ret = pmbus_write_word_data(client, 0,
+ LM25056_VAUX_OV_WARN_LIMIT, word);
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
+ break;
+ case PMBUS_VIRT_SAMPLES:
+ word = clamp_val(word, 1, LM25066_SAMPLES_FOR_AVG_MAX);
+ ret = pmbus_write_byte_data(client, 0, LM25066_SAMPLES_FOR_AVG,
+ ilog2(word));
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+#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;
+ 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))
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct lm25066_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP);
+ if (config < 0)
+ return config;
+
+ 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;
+ info->format[PSC_VOLTAGE_IN] = direct;
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ info->format[PSC_CURRENT_IN] = direct;
+ info->format[PSC_TEMPERATURE] = direct;
+ info->format[PSC_POWER] = direct;
+
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON
+ | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_SAMPLES;
+
+ if (data->id == lm25056) {
+ info->func[0] |= PMBUS_HAVE_STATUS_VMON;
+ info->read_word_data = lm25056_read_word_data;
+ info->read_byte_data = lm25056_read_byte_data;
+ data->rlimit = 0x0fff;
+ } else {
+ info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ info->read_word_data = lm25066_read_word_data;
+ data->rlimit = 0x0fff;
+ }
+ info->write_word_data = lm25066_write_word_data;
+
+ coeff = &lm25066_coeff[data->id][0];
+ info->m[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].m;
+ info->b[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].b;
+ info->R[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].R;
+ info->m[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].m;
+ info->b[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].b;
+ info->R[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].R;
+ info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m;
+ info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b;
+ info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R;
+ info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R;
+ info->R[PSC_POWER] = coeff[PSC_POWER].R;
+ if (config & LM25066_DEV_SETUP_CL) {
+ info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m;
+ info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].b;
+ info->m[PSC_POWER] = coeff[PSC_POWER_L].m;
+ info->b[PSC_POWER] = coeff[PSC_POWER_L].b;
+ } else {
+ info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m;
+ info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b;
+ info->m[PSC_POWER] = coeff[PSC_POWER].m;
+ info->b[PSC_POWER] = coeff[PSC_POWER].b;
+ }
+
+ /*
+ * 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
+
+ return pmbus_do_probe(client, info);
+}
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm25066_driver = {
+ .driver = {
+ .name = "lm25066",
+ .of_match_table = of_match_ptr(lm25066_of_match),
+ },
+ .probe_new = lm25066_probe,
+ .id_table = lm25066_id,
+};
+
+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 000000000..4cfe476fc
--- /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, &lt7182s_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
new file mode 100644
index 000000000..6d2592731
--- /dev/null
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -0,0 +1,934 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for LTC2978 and compatible chips.
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2013, 2014, 2015 Guenter Roeck
+ * Copyright (c) 2015 Linear Technology
+ * Copyright (c) 2018 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#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/regulator/driver.h>
+#include "pmbus.h"
+
+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
+#define LTC2978_MFR_VIN_PEAK 0xde
+#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf
+#define LTC2978_MFR_SPECIAL_ID 0xe7 /* Undocumented on LTC3882 */
+#define LTC2978_MFR_COMMON 0xef
+
+/* LTC2974, LTC2975, LCT2977, LTC2980, LTC2978, and LTM2987 */
+#define LTC2978_MFR_VOUT_MIN 0xfb
+#define LTC2978_MFR_VIN_MIN 0xfc
+#define LTC2978_MFR_TEMPERATURE_MIN 0xfd
+
+/* LTC2974, LTC2975 */
+#define LTC2974_MFR_IOUT_PEAK 0xd7
+#define LTC2974_MFR_IOUT_MIN 0xd8
+
+/* LTC3880, LTC3882, LTC3883, LTC3887, LTM4675, and LTM4676 */
+#define LTC3880_MFR_IOUT_PEAK 0xd7
+#define LTC3880_MFR_CLEAR_PEAKS 0xe3
+#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
+
+/* LTC3883, LTC3884, LTC3886, LTC3889 and LTC7880 only */
+#define LTC3883_MFR_IIN_PEAK 0xe1
+
+
+/* LTC2975 only */
+#define LTC2975_MFR_IIN_PEAK 0xc4
+#define LTC2975_MFR_IIN_MIN 0xc5
+#define LTC2975_MFR_PIN_PEAK 0xc6
+#define LTC2975_MFR_PIN_MIN 0xc7
+
+#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
+#define LTC3883_NUM_PAGES 1
+
+#define LTC_POLL_TIMEOUT 100 /* in milli-seconds */
+
+#define LTC_NOT_BUSY BIT(6)
+#define LTC_NOT_PENDING BIT(5)
+
+/*
+ * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which
+ * happens pretty much each time chip data is updated. Raw peak data therefore
+ * does not provide much value. To be able to provide useful peak data, keep an
+ * internal cache of measured peak data, which is only cleared if an explicit
+ * "clear peak" command is executed for the sensor in question.
+ */
+
+struct ltc2978_data {
+ enum chips id;
+ u16 vin_min, vin_max;
+ u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES];
+ u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES];
+ u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES];
+ u16 iin_min, iin_max;
+ u16 pin_min, pin_max;
+ u16 temp2_max;
+ struct pmbus_driver_info info;
+ u32 features;
+};
+#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info)
+
+#define FEAT_CLEAR_PEAKS BIT(0)
+#define FEAT_NEEDS_POLLING BIT(1)
+
+#define has_clear_peaks(d) ((d)->features & FEAT_CLEAR_PEAKS)
+#define needs_polling(d) ((d)->features & FEAT_NEEDS_POLLING)
+
+static int ltc_wait_ready(struct i2c_client *client)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(LTC_POLL_TIMEOUT);
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int status;
+ u8 mask;
+
+ if (!needs_polling(data))
+ return 0;
+
+ /*
+ * LTC3883 does not support LTC_NOT_PENDING, even though
+ * the datasheet claims that it does.
+ */
+ mask = LTC_NOT_BUSY;
+ if (data->id != ltc3883)
+ mask |= LTC_NOT_PENDING;
+
+ do {
+ status = pmbus_read_byte_data(client, 0, LTC2978_MFR_COMMON);
+ if (status == -EBADMSG || status == -ENXIO) {
+ /* PEC error or NACK: chip may be busy, try again */
+ usleep_range(50, 100);
+ continue;
+ }
+ if (status < 0)
+ return status;
+
+ if ((status & mask) == mask)
+ return 0;
+
+ usleep_range(50, 100);
+ } while (time_before(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+static int ltc_read_word_data(struct i2c_client *client, int page, int phase,
+ int reg)
+{
+ int ret;
+
+ ret = ltc_wait_ready(client);
+ if (ret < 0)
+ return ret;
+
+ return pmbus_read_word_data(client, page, 0xff, reg);
+}
+
+static int ltc_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ ret = ltc_wait_ready(client);
+ if (ret < 0)
+ return ret;
+
+ 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;
+
+ ret = ltc_wait_ready(client);
+ if (ret < 0)
+ return ret;
+
+ return pmbus_write_byte(client, page, byte);
+}
+
+static inline int lin11_to_val(int data)
+{
+ s16 e = ((s16)data) >> 11;
+ s32 m = (((s16)(data << 5)) >> 5);
+
+ /*
+ * mantissa is 10 bit + sign, exponent adds up to 15 bit.
+ * Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31).
+ */
+ e += 6;
+ return (e < 0 ? m >> -e : m << e);
+}
+
+static int ltc_get_max(struct ltc2978_data *data, struct i2c_client *client,
+ int page, int reg, u16 *pmax)
+{
+ int ret;
+
+ ret = ltc_read_word_data(client, page, 0xff, reg);
+ if (ret >= 0) {
+ if (lin11_to_val(ret) > lin11_to_val(*pmax))
+ *pmax = ret;
+ ret = *pmax;
+ }
+ return ret;
+}
+
+static int ltc_get_min(struct ltc2978_data *data, struct i2c_client *client,
+ int page, int reg, u16 *pmin)
+{
+ int ret;
+
+ ret = ltc_read_word_data(client, page, 0xff, reg);
+ if (ret >= 0) {
+ if (lin11_to_val(ret) < lin11_to_val(*pmin))
+ *pmin = ret;
+ ret = *pmin;
+ }
+ return ret;
+}
+
+static int ltc2978_read_word_data_common(struct i2c_client *client, int page,
+ int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VIN_MAX:
+ ret = ltc_get_max(data, client, page, LTC2978_MFR_VIN_PEAK,
+ &data->vin_max);
+ break;
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ ret = ltc_read_word_data(client, page, 0xff,
+ LTC2978_MFR_VOUT_PEAK);
+ if (ret >= 0) {
+ /*
+ * VOUT is 16 bit unsigned with fixed exponent,
+ * so we can compare it directly
+ */
+ if (ret > data->vout_max[page])
+ data->vout_max[page] = ret;
+ ret = data->vout_max[page];
+ }
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = ltc_get_max(data, client, page,
+ LTC2978_MFR_TEMPERATURE_PEAK,
+ &data->temp_max[page]);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = ltc_wait_ready(client);
+ if (ret < 0)
+ return ret;
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+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);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VIN_MIN:
+ ret = ltc_get_min(data, client, page, LTC2978_MFR_VIN_MIN,
+ &data->vin_min);
+ break;
+ case PMBUS_VIRT_READ_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
+ * of LTC2978 revision 1, and will return the maximum
+ * possible voltage if read. If VOUT_MAX is valid and
+ * lower than the reading of VOUT_MIN, use it instead.
+ */
+ if (data->vout_max[page] && ret > data->vout_max[page])
+ ret = data->vout_max[page];
+ if (ret < data->vout_min[page])
+ data->vout_min[page] = ret;
+ ret = data->vout_min[page];
+ }
+ break;
+ case PMBUS_VIRT_READ_TEMP_MIN:
+ ret = ltc_get_min(data, client, page,
+ LTC2978_MFR_TEMPERATURE_MIN,
+ &data->temp_min[page]);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_READ_TEMP2_MAX:
+ case PMBUS_VIRT_RESET_TEMP2_HISTORY:
+ ret = -ENXIO;
+ break;
+ default:
+ ret = ltc2978_read_word_data_common(client, page, reg);
+ break;
+ }
+ return ret;
+}
+
+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);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = ltc_get_max(data, client, page, LTC2974_MFR_IOUT_PEAK,
+ &data->iout_max[page]);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MIN:
+ ret = ltc_get_min(data, client, page, LTC2974_MFR_IOUT_MIN,
+ &data->iout_min[page]);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = 0;
+ break;
+ default:
+ 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 phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_IIN_MAX:
+ ret = ltc_get_max(data, client, page, LTC2975_MFR_IIN_PEAK,
+ &data->iin_max);
+ break;
+ case PMBUS_VIRT_READ_IIN_MIN:
+ ret = ltc_get_min(data, client, page, LTC2975_MFR_IIN_MIN,
+ &data->iin_min);
+ break;
+ case PMBUS_VIRT_READ_PIN_MAX:
+ ret = ltc_get_max(data, client, page, LTC2975_MFR_PIN_PEAK,
+ &data->pin_max);
+ break;
+ case PMBUS_VIRT_READ_PIN_MIN:
+ ret = ltc_get_min(data, client, page, LTC2975_MFR_PIN_MIN,
+ &data->pin_min);
+ break;
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ ret = 0;
+ break;
+ default:
+ 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 phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = ltc_get_max(data, client, page, LTC3880_MFR_IOUT_PEAK,
+ &data->iout_max[page]);
+ break;
+ case PMBUS_VIRT_READ_TEMP2_MAX:
+ ret = ltc_get_max(data, client, page,
+ LTC3880_MFR_TEMPERATURE2_PEAK,
+ &data->temp2_max);
+ break;
+ case PMBUS_VIRT_READ_VIN_MIN:
+ case PMBUS_VIRT_READ_VOUT_MIN:
+ case PMBUS_VIRT_READ_TEMP_MIN:
+ ret = -ENXIO;
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP2_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = ltc2978_read_word_data_common(client, page, reg);
+ break;
+ }
+ return ret;
+}
+
+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);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_IIN_MAX:
+ ret = ltc_get_max(data, client, page, LTC3883_MFR_IIN_PEAK,
+ &data->iin_max);
+ break;
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = ltc3880_read_word_data(client, page, phase, reg);
+ break;
+ }
+ return ret;
+}
+
+static int ltc2978_clear_peaks(struct ltc2978_data *data,
+ struct i2c_client *client, int page)
+{
+ int ret;
+
+ if (has_clear_peaks(data))
+ ret = ltc_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
+ else
+ ret = ltc_write_byte(client, page, PMBUS_CLEAR_FAULTS);
+
+ return ret;
+}
+
+static int ltc2978_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 ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ data->iin_max = 0x7c00;
+ data->iin_min = 0x7bff;
+ ret = ltc2978_clear_peaks(data, client, 0);
+ break;
+ case PMBUS_VIRT_RESET_PIN_HISTORY:
+ data->pin_max = 0x7c00;
+ data->pin_min = 0x7bff;
+ ret = ltc2978_clear_peaks(data, client, 0);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ data->iout_max[page] = 0x7c00;
+ data->iout_min[page] = 0xfbff;
+ ret = ltc2978_clear_peaks(data, client, page);
+ break;
+ case PMBUS_VIRT_RESET_TEMP2_HISTORY:
+ data->temp2_max = 0x7c00;
+ ret = ltc2978_clear_peaks(data, client, page);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ data->vout_min[page] = 0xffff;
+ data->vout_max[page] = 0;
+ ret = ltc2978_clear_peaks(data, client, page);
+ break;
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ data->vin_min = 0x7bff;
+ data->vin_max = 0x7c00;
+ ret = ltc2978_clear_peaks(data, client, page);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ data->temp_min[page] = 0x7bff;
+ data->temp_max[page] = 0x7c00;
+ ret = ltc2978_clear_peaks(data, client, page);
+ break;
+ default:
+ ret = ltc_wait_ready(client);
+ if (ret < 0)
+ return ret;
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+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),
+ PMBUS_REGULATOR("vout", 3),
+ PMBUS_REGULATOR("vout", 4),
+ PMBUS_REGULATOR("vout", 5),
+ PMBUS_REGULATOR("vout", 6),
+ PMBUS_REGULATOR("vout", 7),
+};
+#endif /* CONFIG_SENSORS_LTC2978_REGULATOR */
+
+static int ltc2978_get_id(struct i2c_client *client)
+{
+ int chip_id;
+
+ chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID);
+ if (chip_id < 0) {
+ const struct i2c_device_id *id;
+ u8 buf[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
+ if (ret < 0)
+ return ret;
+ if (ret < 3 || strncmp(buf, "LTC", 3))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+ if (ret < 0)
+ return ret;
+ for (id = &ltc2978_id[0]; strlen(id->name); id++) {
+ if (!strncasecmp(id->name, buf, strlen(id->name)))
+ return (int)id->driver_data;
+ }
+ return -ENODEV;
+ }
+
+ chip_id &= LTC2978_ID_MASK;
+
+ if (chip_id == LTC2972_ID)
+ return ltc2972;
+ else if (chip_id == LTC2974_ID)
+ return ltc2974;
+ else if (chip_id == LTC2975_ID)
+ return ltc2975;
+ else if (chip_id == LTC2977_ID)
+ 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)
+ return ltc3880;
+ else if (chip_id == LTC3882_ID || chip_id == LTC3882_ID_D1)
+ 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)
+{
+ 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))
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct ltc2978_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ chip_id = ltc2978_get_id(client);
+ if (chip_id < 0)
+ 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 (%d), detected %d\n",
+ 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;
+
+ data->vin_min = 0x7bff;
+ data->vin_max = 0x7c00;
+ for (i = 0; i < ARRAY_SIZE(data->vout_min); i++)
+ data->vout_min[i] = 0xffff;
+ for (i = 0; i < ARRAY_SIZE(data->iout_min); i++)
+ data->iout_min[i] = 0xfbff;
+ for (i = 0; i < ARRAY_SIZE(data->iout_max); i++)
+ data->iout_max[i] = 0x7c00;
+ for (i = 0; i < ARRAY_SIZE(data->temp_min); i++)
+ data->temp_min[i] = 0x7bff;
+ for (i = 0; i < ARRAY_SIZE(data->temp_max); i++)
+ data->temp_max[i] = 0x7c00;
+ 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;
+ info->func[0] = 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 ltc2975:
+ info->read_word_data = ltc2975_read_word_data;
+ info->pages = LTC2974_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 ltc2977:
+ case ltc2978:
+ case ltc2979:
+ case ltc2980:
+ case ltm2987:
+ info->read_word_data = ltc2978_read_word_data;
+ info->pages = LTC2978_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ for (i = 1; i < LTC2978_NUM_PAGES; i++) {
+ info->func[i] = PMBUS_HAVE_VOUT
+ | PMBUS_HAVE_STATUS_VOUT;
+ }
+ break;
+ case ltc3880:
+ 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;
+ info->pages = LTC3880_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+ info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ break;
+ case ltc3882:
+ data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
+ info->read_word_data = ltc3880_read_word_data;
+ info->pages = LTC3880_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN
+ | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+ info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ break;
+ case ltc3883:
+ data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
+ info->read_word_data = ltc3883_read_word_data;
+ info->pages = LTC3883_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | 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;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+ info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
+ info->num_regulators = info->pages;
+ 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, 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);
+#endif
+
+static struct i2c_driver ltc2978_driver = {
+ .driver = {
+ .name = "ltc2978",
+ .of_match_table = of_match_ptr(ltc2978_of_match),
+ },
+ .probe_new = ltc2978_probe,
+ .id_table = ltc2978_id,
+};
+
+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
new file mode 100644
index 000000000..8e13a7ddc
--- /dev/null
+++ b/drivers/hwmon/pmbus/ltc3815.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for LTC3815
+ *
+ * Copyright (c) 2015 Linear Technology
+ * Copyright (c) 2015 Guenter Roeck
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define LTC3815_MFR_IOUT_PEAK 0xd7
+#define LTC3815_MFR_VOUT_PEAK 0xdd
+#define LTC3815_MFR_VIN_PEAK 0xde
+#define LTC3815_MFR_TEMP_PEAK 0xdf
+#define LTC3815_MFR_IIN_PEAK 0xe1
+#define LTC3815_MFR_SPECIAL_ID 0xe7
+
+#define LTC3815_ID 0x8000
+#define LTC3815_ID_MASK 0xff00
+
+static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ /*
+ * The chip returns 0x3e, suggesting VID mode with manufacturer
+ * specific VID codes. Since the output voltage is reported
+ * with a LSB of 0.5mV, override and report direct mode with
+ * appropriate coefficients.
+ */
+ ret = 0x40;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_CLEAR_FAULTS:
+ /*
+ * LTC3815 does not support the CLEAR_FAULTS command.
+ * Emulate it by clearing the status register.
+ */
+ ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_WORD);
+ if (ret > 0) {
+ pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD,
+ ret);
+ ret = 0;
+ }
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+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, phase,
+ LTC3815_MFR_VIN_PEAK);
+ break;
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ 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, phase,
+ LTC3815_MFR_TEMP_PEAK);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ 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, phase,
+ LTC3815_MFR_IIN_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int ltc3815_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_IIN_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_IOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_VOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_VIN_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_TEMP_PEAK, 0);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id ltc3815_id[] = {
+ {"ltc3815", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc3815_id);
+
+static struct pmbus_driver_info ltc3815_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_IN] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 250,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .m[PSC_VOLTAGE_OUT] = 2,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_CURRENT_IN] = 1,
+ .b[PSC_CURRENT_IN] = 0,
+ .R[PSC_CURRENT_IN] = 2,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 2,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 0,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
+ .read_byte_data = ltc3815_read_byte_data,
+ .read_word_data = ltc3815_read_word_data,
+ .write_byte = ltc3815_write_byte,
+ .write_word_data = ltc3815_write_word_data,
+};
+
+static int ltc3815_probe(struct i2c_client *client)
+{
+ int chip_id;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+
+ chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID);
+ if (chip_id < 0)
+ return chip_id;
+ if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID)
+ return -ENODEV;
+
+ return pmbus_do_probe(client, &ltc3815_info);
+}
+
+static struct i2c_driver ltc3815_driver = {
+ .driver = {
+ .name = "ltc3815",
+ },
+ .probe_new = ltc3815_probe,
+ .id_table = ltc3815_id,
+};
+
+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 000000000..0b6f88428
--- /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
new file mode 100644
index 000000000..94f869039
--- /dev/null
+++ b/drivers/hwmon/pmbus/max16064.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Maxim MAX16064
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+#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 phase, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ 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, phase,
+ MAX16064_MFR_TEMPERATURE_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max16064_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX16064_MFR_VOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX16064_MFR_TEMPERATURE_PEAK,
+ 0xffff);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static struct pmbus_driver_info max16064_info = {
+ .pages = 4,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 19995,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = -1,
+ .m[PSC_VOLTAGE_OUT] = 19995,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = -1,
+ .m[PSC_TEMPERATURE] = -7612,
+ .b[PSC_TEMPERATURE] = 335,
+ .R[PSC_TEMPERATURE] = -3,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .read_word_data = max16064_read_word_data,
+ .write_word_data = max16064_write_word_data,
+};
+
+static int max16064_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &max16064_info);
+}
+
+static const struct i2c_device_id max16064_id[] = {
+ {"max16064", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max16064_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max16064_driver = {
+ .driver = {
+ .name = "max16064",
+ },
+ .probe_new = max16064_probe,
+ .id_table = max16064_id,
+};
+
+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 000000000..b628405e6
--- /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
new file mode 100644
index 000000000..ba39f03c6
--- /dev/null
+++ b/drivers/hwmon/pmbus/max20730.c
@@ -0,0 +1,788 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/pmbus.h>
+#include <linux/util_macros.h>
+#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
+ * values are constants, so we could do that calculation manually. On the
+ * downside, that would make the driver more difficult to maintain, so lets
+ * use this approach.
+ */
+static u16 val_to_direct(int v, enum pmbus_sensor_classes class,
+ const struct pmbus_driver_info *info)
+{
+ int R = info->R[class] - 3; /* take milli-units into account */
+ int b = info->b[class] * 1000;
+ long d;
+
+ d = v * info->m[class] + b;
+ /*
+ * R < 0 is true for all callers, so we don't need to bother
+ * about the R > 0 case.
+ */
+ while (R < 0) {
+ d = DIV_ROUND_CLOSEST(d, 10);
+ R++;
+ }
+ return (u16)d;
+}
+
+static long direct_to_val(u16 w, enum pmbus_sensor_classes class,
+ const struct pmbus_driver_info *info)
+{
+ int R = info->R[class] - 3;
+ int b = info->b[class] * 1000;
+ int m = info->m[class];
+ long d = (s16)w;
+
+ if (m == 0)
+ return 0;
+
+ while (R < 0) {
+ d *= 10;
+ R++;
+ }
+ d = (d - b) / m;
+ return d;
+}
+
+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 phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct max20730_data *data = to_max20730_data(info);
+ int ret = 0;
+ u32 max_c;
+
+ switch (reg) {
+ case PMBUS_OT_FAULT_LIMIT:
+ switch ((data->mfr_devset1 >> 11) & 0x3) {
+ case 0x0:
+ ret = val_to_direct(150000, PSC_TEMPERATURE, info);
+ break;
+ case 0x1:
+ ret = val_to_direct(130000, PSC_TEMPERATURE, info);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ break;
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ 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;
+ }
+ return ret;
+}
+
+static int max20730_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ struct pmbus_driver_info *info;
+ struct max20730_data *data;
+ u16 devset1;
+ int ret = 0;
+ int idx;
+
+ info = (struct pmbus_driver_info *)pmbus_get_driver_info(client);
+ data = to_max20730_data(info);
+
+ mutex_lock(&data->lock);
+ devset1 = data->mfr_devset1;
+
+ switch (reg) {
+ case PMBUS_OT_FAULT_LIMIT:
+ devset1 &= ~(BIT(11) | BIT(12));
+ if (direct_to_val(word, PSC_TEMPERATURE, info) < 140000)
+ devset1 |= BIT(11);
+ break;
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ devset1 &= ~(BIT(5) | BIT(6));
+
+ idx = find_closest(direct_to_val(word, PSC_CURRENT_OUT, info),
+ max_current[data->id], 4);
+ devset1 |= (idx << 5);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ if (!ret && devset1 != data->mfr_devset1) {
+ ret = i2c_smbus_write_word_data(client, MAX20730_MFR_DEVSET1,
+ devset1);
+ if (!ret) {
+ data->mfr_devset1 = devset1;
+ pmbus_clear_cache(client);
+ }
+ }
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+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,
+ .write_word_data = max20730_write_word_data,
+
+ /* Source : Maxim 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,
+
+ /*
+ * Values in the datasheet are adjusted for temperature and
+ * for the relationship between Vin and Vout.
+ * Unfortunately, the data sheet suggests that Vout measurement
+ * may be scaled with a resistor array. This is indeed the case
+ * at least on the evaulation boards. As a result, any in-driver
+ * adjustments would either be wrong or require elaborate means
+ * to configure the scaling. Instead of doing that, just report
+ * raw values and let userspace handle adjustments.
+ */
+ .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,
+ },
+ [max20734] = {
+ .pages = 1,
+ .read_word_data = max20730_read_word_data,
+ .write_word_data = max20730_write_word_data,
+
+ /* Source : Maxim AN6209 */
+ .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] = 3592,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = -2,
+
+ .format[PSC_CURRENT_OUT] = direct,
+ .m[PSC_CURRENT_OUT] = 111,
+ .b[PSC_CURRENT_OUT] = 3461,
+ .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,
+ },
+ [max20743] = {
+ .pages = 1,
+ .read_word_data = max20730_read_word_data,
+ .write_word_data = max20730_write_word_data,
+
+ /* Source : Maxim 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] = 3597,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = -2,
+
+ .format[PSC_CURRENT_OUT] = direct,
+ .m[PSC_CURRENT_OUT] = 95,
+ .b[PSC_CURRENT_OUT] = 5014,
+ .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,
+ },
+};
+
+static int max20730_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+ struct max20730_data *data;
+ enum chips chip_id;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read Manufacturer ID\n");
+ return ret;
+ }
+ if (ret != 5 || strncmp(buf, "MAXIM", 5)) {
+ buf[ret] = '\0';
+ dev_err(dev, "Unsupported Manufacturer ID '%s'\n", buf);
+ return -ENODEV;
+ }
+
+ /*
+ * The chips support reading PMBUS_MFR_MODEL. On both MAX20730
+ * and MAX20734, reading it returns M20743. Presumably that is
+ * the reason why the command is not documented. Unfortunately,
+ * that means that there is no reliable means to detect the chip.
+ * However, we can at least detect the chip series. Compare
+ * the returned value against 'M20743' and bail out if there is
+ * a mismatch. If that doesn't work for all chips, we may have
+ * to remove this check.
+ */
+ 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 != 6 || strncmp(buf, "M20743", 6)) {
+ 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 != 1 || buf[0] != 'F') {
+ buf[ret] = '\0';
+ dev_err(dev, "Unsupported Manufacturer Revision '%s'\n", buf);
+ return -ENODEV;
+ }
+
+ if (client->dev.of_node)
+ chip_id = (enum chips)of_device_get_match_data(dev);
+ else
+ chip_id = i2c_match_id(max20730_id, client)->driver_data;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ 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;
+
+ 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 },
+ { },
+};
+
+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 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, max20730_of_match);
+
+static struct i2c_driver max20730_driver = {
+ .driver = {
+ .name = "max20730",
+ .of_match_table = max20730_of_match,
+ },
+ .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 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
new file mode 100644
index 000000000..2272dc8c2
--- /dev/null
+++ b/drivers/hwmon/pmbus/max20751.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Maxim MAX20751
+ *
+ * Copyright (c) 2015 Guenter Roeck
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+static struct pmbus_driver_info max20751_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = vid,
+ .vrm_version[0] = vr12,
+ .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 |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT,
+};
+
+static int max20751_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &max20751_info);
+}
+
+static const struct i2c_device_id max20751_id[] = {
+ {"max20751", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max20751_id);
+
+static struct i2c_driver max20751_driver = {
+ .driver = {
+ .name = "max20751",
+ },
+ .probe_new = max20751_probe,
+ .id_table = max20751_id,
+};
+
+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
new file mode 100644
index 000000000..95d79a64b
--- /dev/null
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 IBM Corp.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum max31785_regs {
+ MFR_REVISION = 0x9b,
+ MFR_FAN_CONFIG = 0xf1,
+};
+
+#define MAX31785 0x3030
+#define MAX31785A 0x3040
+#define MAX31785B 0x3061
+
+#define MFR_FAN_CONFIG_DUAL_TACH BIT(12)
+
+#define MAX31785_NR_PAGES 23
+#define MAX31785_NR_FAN_PAGES 6
+
+static int max31785_read_byte_data(struct i2c_client *client, int page,
+ int reg)
+{
+ if (page < MAX31785_NR_PAGES)
+ return -ENODATA;
+
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ return -ENOTSUPP;
+ case PMBUS_FAN_CONFIG_12:
+ return pmbus_read_byte_data(client, page - MAX31785_NR_PAGES,
+ reg);
+ }
+
+ return -ENODATA;
+}
+
+static int max31785_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ if (page < MAX31785_NR_PAGES)
+ return -ENODATA;
+
+ return -ENOTSUPP;
+}
+
+static int max31785_read_long_data(struct i2c_client *client, int page,
+ int reg, u32 *data)
+{
+ unsigned char cmdbuf[1];
+ unsigned char rspbuf[4];
+ int rc;
+
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(cmdbuf),
+ .buf = cmdbuf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(rspbuf),
+ .buf = rspbuf,
+ },
+ };
+
+ cmdbuf[0] = reg;
+
+ rc = pmbus_set_page(client, page, 0xff);
+ if (rc < 0)
+ return rc;
+
+ rc = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (rc < 0)
+ return rc;
+
+ *data = (rspbuf[0] << (0 * 8)) | (rspbuf[1] << (1 * 8)) |
+ (rspbuf[2] << (2 * 8)) | (rspbuf[3] << (3 * 8));
+
+ return rc;
+}
+
+static int max31785_get_pwm(struct i2c_client *client, int page)
+{
+ int rv;
+
+ rv = pmbus_get_fan_rate_device(client, page, 0, percent);
+ if (rv < 0)
+ return rv;
+ else if (rv >= 0x8000)
+ return 0;
+ else if (rv >= 0x2711)
+ return 0x2710;
+
+ return rv;
+}
+
+static int max31785_get_pwm_mode(struct i2c_client *client, int page)
+{
+ int config;
+ int command;
+
+ config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+ if (config < 0)
+ return config;
+
+ command = pmbus_read_word_data(client, page, 0xff, PMBUS_FAN_COMMAND_1);
+ if (command < 0)
+ return command;
+
+ if (config & PB_FAN_1_RPM)
+ return (command >= 0x8000) ? 3 : 2;
+
+ if (command >= 0x8000)
+ return 3;
+ else if (command >= 0x2711)
+ return 0;
+
+ return 1;
+}
+
+static int max31785_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ u32 val;
+ int rv;
+
+ switch (reg) {
+ case PMBUS_READ_FAN_SPEED_1:
+ if (page < MAX31785_NR_PAGES)
+ return -ENODATA;
+
+ rv = max31785_read_long_data(client, page - MAX31785_NR_PAGES,
+ reg, &val);
+ if (rv < 0)
+ return rv;
+
+ rv = (val >> 16) & 0xffff;
+ break;
+ case PMBUS_FAN_COMMAND_1:
+ /*
+ * PMBUS_FAN_COMMAND_x is probed to judge whether or not to
+ * expose fan control registers.
+ *
+ * Don't expose fan_target attribute for virtual pages.
+ */
+ rv = (page >= MAX31785_NR_PAGES) ? -ENOTSUPP : -ENODATA;
+ break;
+ case PMBUS_VIRT_PWM_1:
+ rv = max31785_get_pwm(client, page);
+ break;
+ case PMBUS_VIRT_PWM_ENABLE_1:
+ rv = max31785_get_pwm_mode(client, page);
+ break;
+ default:
+ rv = -ENODATA;
+ break;
+ }
+
+ return rv;
+}
+
+static inline u32 max31785_scale_pwm(u32 sensor_val)
+{
+ /*
+ * The datasheet describes the accepted value range for manual PWM as
+ * [0, 0x2710], while the hwmon pwmX sysfs interface accepts values in
+ * [0, 255]. The MAX31785 uses DIRECT mode to scale the FAN_COMMAND
+ * registers and in PWM mode the coefficients are m=1, b=0, R=2. The
+ * important observation here is that 0x2710 == 10000 == 100 * 100.
+ *
+ * R=2 (== 10^2 == 100) accounts for scaling the value provided at the
+ * sysfs interface into the required hardware resolution, but it does
+ * not yet yield a value that we can write to the device (this initial
+ * scaling is handled by pmbus_data2reg()). Multiplying by 100 below
+ * translates the parameter value into the percentage units required by
+ * PMBus, and then we scale back by 255 as required by the hwmon pwmX
+ * interface to yield the percentage value at the appropriate
+ * resolution for hardware.
+ */
+ return (sensor_val * 100) / 255;
+}
+
+static int max31785_pwm_enable(struct i2c_client *client, int page,
+ u16 word)
+{
+ int config = 0;
+ int rate;
+
+ switch (word) {
+ case 0:
+ rate = 0x7fff;
+ break;
+ case 1:
+ rate = pmbus_get_fan_rate_cached(client, page, 0, percent);
+ if (rate < 0)
+ return rate;
+ rate = max31785_scale_pwm(rate);
+ break;
+ case 2:
+ config = PB_FAN_1_RPM;
+ rate = pmbus_get_fan_rate_cached(client, page, 0, rpm);
+ if (rate < 0)
+ return rate;
+ break;
+ case 3:
+ rate = 0xffff;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate);
+}
+
+static int max31785_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ switch (reg) {
+ case PMBUS_VIRT_PWM_1:
+ return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
+ max31785_scale_pwm(word));
+ case PMBUS_VIRT_PWM_ENABLE_1:
+ return max31785_pwm_enable(client, page, word);
+ default:
+ break;
+ }
+
+ return -ENODATA;
+}
+
+#define MAX31785_FAN_FUNCS \
+ (PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_PWM12)
+
+#define MAX31785_TEMP_FUNCS \
+ (PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
+
+#define MAX31785_VOUT_FUNCS \
+ (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT)
+
+static const struct pmbus_driver_info max31785_info = {
+ .pages = MAX31785_NR_PAGES,
+
+ .write_word_data = max31785_write_word_data,
+ .read_byte_data = max31785_read_byte_data,
+ .read_word_data = max31785_read_word_data,
+ .write_byte = max31785_write_byte,
+
+ /* RPM */
+ .format[PSC_FAN] = direct,
+ .m[PSC_FAN] = 1,
+ .b[PSC_FAN] = 0,
+ .R[PSC_FAN] = 0,
+ /* PWM */
+ .format[PSC_PWM] = direct,
+ .m[PSC_PWM] = 1,
+ .b[PSC_PWM] = 0,
+ .R[PSC_PWM] = 2,
+ .func[0] = MAX31785_FAN_FUNCS,
+ .func[1] = MAX31785_FAN_FUNCS,
+ .func[2] = MAX31785_FAN_FUNCS,
+ .func[3] = MAX31785_FAN_FUNCS,
+ .func[4] = MAX31785_FAN_FUNCS,
+ .func[5] = MAX31785_FAN_FUNCS,
+
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ .func[6] = MAX31785_TEMP_FUNCS,
+ .func[7] = MAX31785_TEMP_FUNCS,
+ .func[8] = MAX31785_TEMP_FUNCS,
+ .func[9] = MAX31785_TEMP_FUNCS,
+ .func[10] = MAX31785_TEMP_FUNCS,
+ .func[11] = MAX31785_TEMP_FUNCS,
+ .func[12] = MAX31785_TEMP_FUNCS,
+ .func[13] = MAX31785_TEMP_FUNCS,
+ .func[14] = MAX31785_TEMP_FUNCS,
+ .func[15] = MAX31785_TEMP_FUNCS,
+ .func[16] = MAX31785_TEMP_FUNCS,
+
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 0,
+ .func[17] = MAX31785_VOUT_FUNCS,
+ .func[18] = MAX31785_VOUT_FUNCS,
+ .func[19] = MAX31785_VOUT_FUNCS,
+ .func[20] = MAX31785_VOUT_FUNCS,
+ .func[21] = MAX31785_VOUT_FUNCS,
+ .func[22] = MAX31785_VOUT_FUNCS,
+};
+
+static int max31785_configure_dual_tach(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < MAX31785_NR_FAN_PAGES; i++) {
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ if (ret & MFR_FAN_CONFIG_DUAL_TACH) {
+ int virtual = MAX31785_NR_PAGES + i;
+
+ info->pages = virtual + 1;
+ info->func[virtual] |= PMBUS_HAVE_FAN12;
+ info->func[virtual] |= PMBUS_PAGE_VIRTUAL;
+ }
+ }
+
+ return 0;
+}
+
+static int max31785_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct pmbus_driver_info *info;
+ bool dual_tach = false;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ *info = max31785_info;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_REVISION);
+ if (ret < 0)
+ return ret;
+
+ if (ret == MAX31785A || ret == MAX31785B) {
+ dual_tach = true;
+ } else if (ret == MAX31785) {
+ 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;
+ }
+
+ if (dual_tach) {
+ ret = max31785_configure_dual_tach(client, info);
+ if (ret < 0)
+ return ret;
+ }
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id max31785_id[] = {
+ { "max31785", 0 },
+ { "max31785a", 0 },
+ { "max31785b", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max31785_id);
+
+static const struct of_device_id max31785_of_match[] = {
+ { .compatible = "maxim,max31785" },
+ { .compatible = "maxim,max31785a" },
+ { .compatible = "maxim,max31785b" },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, max31785_of_match);
+
+static struct i2c_driver max31785_driver = {
+ .driver = {
+ .name = "max31785",
+ .of_match_table = max31785_of_match,
+ },
+ .probe_new = max31785_probe,
+ .id_table = max31785_id,
+};
+
+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
new file mode 100644
index 000000000..ea7609058
--- /dev/null
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Maxim MAX34440/MAX34441
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2012 Guenter Roeck
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum chips { max34440, max34441, max34446, max34451, max34460, max34461 };
+
+#define MAX34440_MFR_VOUT_PEAK 0xd4
+#define MAX34440_MFR_IOUT_PEAK 0xd5
+#define MAX34440_MFR_TEMPERATURE_PEAK 0xd6
+#define MAX34440_MFR_VOUT_MIN 0xd7
+
+#define MAX34446_MFR_POUT_PEAK 0xe0
+#define MAX34446_MFR_POUT_AVG 0xe1
+#define MAX34446_MFR_IOUT_AVG 0xe2
+#define MAX34446_MFR_TEMPERATURE_AVG 0xe3
+
+#define MAX34440_STATUS_OC_WARN BIT(0)
+#define MAX34440_STATUS_OC_FAULT BIT(1)
+#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
+
+struct max34440_data {
+ int id;
+ struct pmbus_driver_info info;
+};
+
+#define to_max34440_data(x) container_of(x, struct max34440_data, info)
+
+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, phase,
+ MAX34440_MFR_VOUT_MIN);
+ break;
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ 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, phase,
+ MAX34446_MFR_IOUT_AVG);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ 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, 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, 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, phase,
+ MAX34446_MFR_TEMPERATURE_AVG);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, page, phase,
+ MAX34440_MFR_TEMPERATURE_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_POUT_HISTORY:
+ if (data->id != max34446)
+ return -ENXIO;
+ ret = 0;
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max34440_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct max34440_data *data = to_max34440_data(info);
+ 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);
+ if (ret)
+ break;
+ ret = pmbus_write_word_data(client, page,
+ MAX34446_MFR_POUT_AVG, 0);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX34440_MFR_VOUT_MIN, 0x7fff);
+ if (ret)
+ break;
+ ret = pmbus_write_word_data(client, page,
+ MAX34440_MFR_VOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX34440_MFR_IOUT_PEAK, 0);
+ if (!ret && (data->id == max34446 || data->id == max34451))
+ ret = pmbus_write_word_data(client, page,
+ MAX34446_MFR_IOUT_AVG, 0);
+
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ MAX34440_MFR_TEMPERATURE_PEAK,
+ 0x8000);
+ if (!ret && data->id == max34446)
+ ret = pmbus_write_word_data(client, page,
+ MAX34446_MFR_TEMPERATURE_AVG, 0);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret = 0;
+ int mfg_status;
+
+ if (page >= 0) {
+ 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, 0xff,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (mfg_status < 0)
+ return mfg_status;
+ if (mfg_status & MAX34440_STATUS_OC_WARN)
+ ret |= PB_IOUT_OC_WARNING;
+ if (mfg_status & MAX34440_STATUS_OC_FAULT)
+ ret |= PB_IOUT_OC_FAULT;
+ break;
+ case PMBUS_STATUS_TEMPERATURE:
+ mfg_status = pmbus_read_word_data(client, 0, 0xff,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (mfg_status < 0)
+ return mfg_status;
+ if (mfg_status & MAX34440_STATUS_OT_WARN)
+ ret |= PB_TEMP_OT_WARNING;
+ if (mfg_status & MAX34440_STATUS_OT_FAULT)
+ ret |= PB_TEMP_OT_FAULT;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max34451_set_supported_funcs(struct i2c_client *client,
+ struct max34440_data *data)
+{
+ /*
+ * Each of the channel 0-15 can be configured to monitor the following
+ * functions based on MFR_CHANNEL_CONFIG[5:0]
+ * 0x10: Sequencing + voltage monitoring (only valid for PAGES 0–11)
+ * 0x20: Voltage monitoring (no sequencing)
+ * 0x21: Voltage read only
+ * 0x22: Current monitoring
+ * 0x23: Current read only
+ * 0x30: General-purpose input active low
+ * 0x34: General-purpose input active high
+ * 0x00: Disabled
+ */
+
+ int page, rv;
+
+ for (page = 0; page < 16; page++) {
+ rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ if (rv < 0)
+ return rv;
+
+ rv = i2c_smbus_read_word_data(client,
+ MAX34451_MFR_CHANNEL_CONFIG);
+ if (rv < 0)
+ return rv;
+
+ switch (rv & MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK) {
+ case 0x10:
+ case 0x20:
+ data->info.func[page] = PMBUS_HAVE_VOUT |
+ PMBUS_HAVE_STATUS_VOUT;
+ break;
+ case 0x21:
+ data->info.func[page] = PMBUS_HAVE_VOUT;
+ break;
+ case 0x22:
+ data->info.func[page] = PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_STATUS_IOUT;
+ break;
+ case 0x23:
+ data->info.func[page] = PMBUS_HAVE_IOUT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static struct pmbus_driver_info max34440_info[] = {
+ [max34440] = {
+ .pages = 14,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .m[PSC_VOLTAGE_IN] = 1,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3, /* R = 0 in datasheet reflects mV */
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 3, /* R = 0 in datasheet reflects mA */
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[13] = 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,
+ },
+ [max34441] = {
+ .pages = 12,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_FAN] = direct,
+ .m[PSC_VOLTAGE_IN] = 1,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 3,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 3,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ .m[PSC_FAN] = 1,
+ .b[PSC_FAN] = 0,
+ .R[PSC_FAN] = 0,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+ .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[11] = 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,
+ },
+ [max34446] = {
+ .pages = 7,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_POWER] = direct,
+ .m[PSC_VOLTAGE_IN] = 1,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 3,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 3,
+ .m[PSC_POWER] = 1,
+ .b[PSC_POWER] = 0,
+ .R[PSC_POWER] = 3,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ .func[0] = 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,
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
+ .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[4] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[5] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[6] = 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,
+ },
+ [max34451] = {
+ .pages = 21,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 2,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ /* func 0-15 is set dynamically before probing */
+ .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .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_word_data = max34440_read_word_data,
+ .write_word_data = max34440_write_word_data,
+ },
+ [max34460] = {
+ .pages = 18,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .func[14] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .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_word_data = max34440_read_word_data,
+ .write_word_data = max34440_write_word_data,
+ },
+ [max34461] = {
+ .pages = 23,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[12] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[13] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[14] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[15] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ /* page 16 is reserved */
+ .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .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,
+ .func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_word_data = max34440_read_word_data,
+ .write_word_data = max34440_write_word_data,
+ },
+};
+
+static int max34440_probe(struct i2c_client *client)
+{
+ struct max34440_data *data;
+ int rv;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct max34440_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ 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);
+ if (rv)
+ return rv;
+ }
+
+ return pmbus_do_probe(client, &data->info);
+}
+
+static const struct i2c_device_id max34440_id[] = {
+ {"max34440", max34440},
+ {"max34441", max34441},
+ {"max34446", max34446},
+ {"max34451", max34451},
+ {"max34460", max34460},
+ {"max34461", max34461},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, max34440_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max34440_driver = {
+ .driver = {
+ .name = "max34440",
+ },
+ .probe_new = max34440_probe,
+ .id_table = max34440_id,
+};
+
+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
new file mode 100644
index 000000000..5e66c28c0
--- /dev/null
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Maxim MAX8688
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+#define MAX8688_MFR_VOUT_PEAK 0xd4
+#define MAX8688_MFR_IOUT_PEAK 0xd5
+#define MAX8688_MFR_TEMPERATURE_PEAK 0xd6
+#define MAX8688_MFG_STATUS 0xd8
+
+#define MAX8688_STATUS_OC_FAULT BIT(4)
+#define MAX8688_STATUS_OV_FAULT BIT(5)
+#define MAX8688_STATUS_OV_WARNING BIT(8)
+#define MAX8688_STATUS_UV_FAULT BIT(9)
+#define MAX8688_STATUS_UV_WARNING BIT(10)
+#define MAX8688_STATUS_UC_FAULT BIT(11)
+#define MAX8688_STATUS_OC_WARNING BIT(12)
+#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 phase, int reg)
+{
+ int ret;
+
+ if (page > 0)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ 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, 0xff,
+ MAX8688_MFR_IOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, 0, 0xff,
+ MAX8688_MFR_TEMPERATURE_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max8688_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK,
+ 0);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK,
+ 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, 0,
+ MAX8688_MFR_TEMPERATURE_PEAK,
+ 0xffff);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret = 0;
+ int mfg_status;
+
+ if (page > 0)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_STATUS_VOUT:
+ mfg_status = pmbus_read_word_data(client, 0, 0xff,
+ MAX8688_MFG_STATUS);
+ if (mfg_status < 0)
+ return mfg_status;
+ if (mfg_status & MAX8688_STATUS_UV_WARNING)
+ ret |= PB_VOLTAGE_UV_WARNING;
+ if (mfg_status & MAX8688_STATUS_UV_FAULT)
+ ret |= PB_VOLTAGE_UV_FAULT;
+ if (mfg_status & MAX8688_STATUS_OV_WARNING)
+ ret |= PB_VOLTAGE_OV_WARNING;
+ if (mfg_status & MAX8688_STATUS_OV_FAULT)
+ ret |= PB_VOLTAGE_OV_FAULT;
+ break;
+ case PMBUS_STATUS_IOUT:
+ mfg_status = pmbus_read_word_data(client, 0, 0xff,
+ MAX8688_MFG_STATUS);
+ if (mfg_status < 0)
+ return mfg_status;
+ if (mfg_status & MAX8688_STATUS_UC_FAULT)
+ ret |= PB_IOUT_UC_FAULT;
+ if (mfg_status & MAX8688_STATUS_OC_WARNING)
+ ret |= PB_IOUT_OC_WARNING;
+ if (mfg_status & MAX8688_STATUS_OC_FAULT)
+ ret |= PB_IOUT_OC_FAULT;
+ break;
+ case PMBUS_STATUS_TEMPERATURE:
+ mfg_status = pmbus_read_word_data(client, 0, 0xff,
+ MAX8688_MFG_STATUS);
+ if (mfg_status < 0)
+ return mfg_status;
+ if (mfg_status & MAX8688_STATUS_OT_WARNING)
+ ret |= PB_TEMP_OT_WARNING;
+ if (mfg_status & MAX8688_STATUS_OT_FAULT)
+ ret |= PB_TEMP_OT_FAULT;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static struct pmbus_driver_info max8688_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .m[PSC_VOLTAGE_IN] = 19995,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = -1,
+ .m[PSC_VOLTAGE_OUT] = 19995,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = -1,
+ .m[PSC_CURRENT_OUT] = 23109,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = -2,
+ .m[PSC_TEMPERATURE] = -7612,
+ .b[PSC_TEMPERATURE] = 335,
+ .R[PSC_TEMPERATURE] = -3,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_STATUS_TEMP,
+ .read_byte_data = max8688_read_byte_data,
+ .read_word_data = max8688_read_word_data,
+ .write_word_data = max8688_write_word_data,
+};
+
+static int max8688_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &max8688_info);
+}
+
+static const struct i2c_device_id max8688_id[] = {
+ {"max8688", 0},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max8688_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max8688_driver = {
+ .driver = {
+ .name = "max8688",
+ },
+ .probe_new = max8688_probe,
+ .id_table = max8688_id,
+};
+
+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 000000000..24e519470
--- /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 000000000..51986adfb
--- /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 000000000..791a06c3c
--- /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 000000000..273ff6e57
--- /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 000000000..05b4ee35b
--- /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 000000000..e0bbc8a30
--- /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
new file mode 100644
index 000000000..d0d386990
--- /dev/null
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for PMBus devices
+ *
+ * Copyright (c) 2010, 2011 Ericsson AB.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+struct pmbus_device_info {
+ int pages;
+ u32 flags;
+};
+
+static const struct i2c_device_id pmbus_id[];
+
+/*
+ * Find sensor groups and status registers on each page.
+ */
+static void pmbus_find_sensor_groups(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ int page;
+
+ /* Sensors detected on page 0 only */
+ if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN))
+ info->func[0] |= PMBUS_HAVE_VIN;
+ if (pmbus_check_word_register(client, 0, PMBUS_READ_VCAP))
+ info->func[0] |= PMBUS_HAVE_VCAP;
+ if (pmbus_check_word_register(client, 0, PMBUS_READ_IIN))
+ info->func[0] |= PMBUS_HAVE_IIN;
+ if (pmbus_check_word_register(client, 0, PMBUS_READ_PIN))
+ info->func[0] |= PMBUS_HAVE_PIN;
+ if (info->func[0]
+ && pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT))
+ info->func[0] |= PMBUS_HAVE_STATUS_INPUT;
+ if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_12) &&
+ pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) {
+ info->func[0] |= PMBUS_HAVE_FAN12;
+ if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12))
+ info->func[0] |= PMBUS_HAVE_STATUS_FAN12;
+ }
+ if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_34) &&
+ pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) {
+ info->func[0] |= PMBUS_HAVE_FAN34;
+ if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34))
+ info->func[0] |= PMBUS_HAVE_STATUS_FAN34;
+ }
+ if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1))
+ info->func[0] |= PMBUS_HAVE_TEMP;
+ if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_2))
+ info->func[0] |= PMBUS_HAVE_TEMP2;
+ if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_3))
+ info->func[0] |= PMBUS_HAVE_TEMP3;
+ if (info->func[0] & (PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
+ | PMBUS_HAVE_TEMP3)
+ && pmbus_check_byte_register(client, 0,
+ PMBUS_STATUS_TEMPERATURE))
+ info->func[0] |= PMBUS_HAVE_STATUS_TEMP;
+
+ /* Sensors detected on all pages */
+ for (page = 0; page < info->pages; page++) {
+ if (pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) {
+ info->func[page] |= PMBUS_HAVE_VOUT;
+ if (pmbus_check_byte_register(client, page,
+ PMBUS_STATUS_VOUT))
+ info->func[page] |= PMBUS_HAVE_STATUS_VOUT;
+ }
+ if (pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) {
+ info->func[page] |= PMBUS_HAVE_IOUT;
+ if (pmbus_check_byte_register(client, 0,
+ PMBUS_STATUS_IOUT))
+ info->func[page] |= PMBUS_HAVE_STATUS_IOUT;
+ }
+ if (pmbus_check_word_register(client, page, PMBUS_READ_POUT))
+ info->func[page] |= PMBUS_HAVE_POUT;
+ }
+}
+
+/*
+ * Identify chip parameters.
+ */
+static int pmbus_identify(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ int ret = 0;
+
+ if (!info->pages) {
+ /*
+ * Check if the PAGE command is supported. If it is,
+ * keep setting the page number until it fails or until the
+ * maximum number of pages has been reached. Assume that
+ * this is the number of pages supported by the chip.
+ */
+ if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) {
+ int page;
+
+ for (page = 1; page < PMBUS_PAGES; page++) {
+ if (pmbus_set_page(client, page, 0xff) < 0)
+ break;
+ }
+ pmbus_set_page(client, 0, 0xff);
+ info->pages = page;
+ } else {
+ info->pages = 1;
+ }
+
+ pmbus_clear_faults(client);
+ }
+
+ if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) {
+ int vout_mode, i;
+
+ vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
+ if (vout_mode >= 0 && vout_mode != 0xff) {
+ switch (vout_mode >> 5) {
+ case 0:
+ break;
+ case 1:
+ info->format[PSC_VOLTAGE_OUT] = vid;
+ for (i = 0; i < info->pages; i++)
+ info->vrm_version[i] = vr11;
+ break;
+ case 2:
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ break;
+ default:
+ ret = -ENODEV;
+ goto abort;
+ }
+ }
+ }
+
+ /*
+ * We should check if the COEFFICIENTS register is supported.
+ * If it is, and the chip is configured for direct mode, we can read
+ * the coefficients from the chip, one set per group of sensor
+ * registers.
+ *
+ * To do this, we will need access to a chip which actually supports the
+ * COEFFICIENTS command, since the command is too complex to implement
+ * without testing it. Until then, abort if a chip configured for direct
+ * mode was detected.
+ */
+ if (info->format[PSC_VOLTAGE_OUT] == direct) {
+ ret = -ENODEV;
+ goto abort;
+ }
+
+ /* Try to find sensor groups */
+ pmbus_find_sensor_groups(client, info);
+abort:
+ return ret;
+}
+
+static int pmbus_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ struct pmbus_platform_data *pdata = NULL;
+ struct device *dev = &client->dev;
+ struct pmbus_device_info *device_info;
+
+ info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ 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 = device_info->flags;
+ }
+
+ info->pages = device_info->pages;
+ info->identify = pmbus_identify;
+ dev->platform_data = pdata;
+
+ 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},
+ {"max20796", (kernel_ulong_t)&pmbus_info_one},
+ {"mdt040", (kernel_ulong_t)&pmbus_info_one},
+ {"ncp4200", (kernel_ulong_t)&pmbus_info_one},
+ {"ncp4208", (kernel_ulong_t)&pmbus_info_one},
+ {"pdt003", (kernel_ulong_t)&pmbus_info_one},
+ {"pdt006", (kernel_ulong_t)&pmbus_info_one},
+ {"pdt012", (kernel_ulong_t)&pmbus_info_one},
+ {"pmbus", (kernel_ulong_t)&pmbus_info_zero},
+ {"sgd009", (kernel_ulong_t)&pmbus_info_one_skip},
+ {"tps40400", (kernel_ulong_t)&pmbus_info_one},
+ {"tps544b20", (kernel_ulong_t)&pmbus_info_one},
+ {"tps544b25", (kernel_ulong_t)&pmbus_info_one},
+ {"tps544c20", (kernel_ulong_t)&pmbus_info_one},
+ {"tps544c25", (kernel_ulong_t)&pmbus_info_one},
+ {"udt020", (kernel_ulong_t)&pmbus_info_one},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, pmbus_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver pmbus_driver = {
+ .driver = {
+ .name = "pmbus",
+ },
+ .probe_new = pmbus_probe,
+ .id_table = pmbus_id,
+};
+
+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
new file mode 100644
index 000000000..10fb17879
--- /dev/null
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -0,0 +1,511 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * pmbus.h - Common defines and structures for PMBus devices
+ *
+ * Copyright (c) 2010, 2011 Ericsson AB.
+ * Copyright (c) 2012 Guenter Roeck
+ */
+
+#ifndef PMBUS_H
+#define PMBUS_H
+
+#include <linux/bitops.h>
+#include <linux/regulator/driver.h>
+
+/*
+ * Registers
+ */
+enum pmbus_regs {
+ PMBUS_PAGE = 0x00,
+ PMBUS_OPERATION = 0x01,
+ PMBUS_ON_OFF_CONFIG = 0x02,
+ PMBUS_CLEAR_FAULTS = 0x03,
+ PMBUS_PHASE = 0x04,
+
+ PMBUS_WRITE_PROTECT = 0x10,
+
+ PMBUS_CAPABILITY = 0x19,
+ PMBUS_QUERY = 0x1A,
+
+ PMBUS_VOUT_MODE = 0x20,
+ PMBUS_VOUT_COMMAND = 0x21,
+ PMBUS_VOUT_TRIM = 0x22,
+ PMBUS_VOUT_CAL_OFFSET = 0x23,
+ PMBUS_VOUT_MAX = 0x24,
+ PMBUS_VOUT_MARGIN_HIGH = 0x25,
+ PMBUS_VOUT_MARGIN_LOW = 0x26,
+ PMBUS_VOUT_TRANSITION_RATE = 0x27,
+ PMBUS_VOUT_DROOP = 0x28,
+ PMBUS_VOUT_SCALE_LOOP = 0x29,
+ PMBUS_VOUT_SCALE_MONITOR = 0x2A,
+
+ PMBUS_COEFFICIENTS = 0x30,
+ PMBUS_POUT_MAX = 0x31,
+
+ PMBUS_FAN_CONFIG_12 = 0x3A,
+ PMBUS_FAN_COMMAND_1 = 0x3B,
+ PMBUS_FAN_COMMAND_2 = 0x3C,
+ PMBUS_FAN_CONFIG_34 = 0x3D,
+ PMBUS_FAN_COMMAND_3 = 0x3E,
+ PMBUS_FAN_COMMAND_4 = 0x3F,
+
+ PMBUS_VOUT_OV_FAULT_LIMIT = 0x40,
+ PMBUS_VOUT_OV_FAULT_RESPONSE = 0x41,
+ PMBUS_VOUT_OV_WARN_LIMIT = 0x42,
+ PMBUS_VOUT_UV_WARN_LIMIT = 0x43,
+ PMBUS_VOUT_UV_FAULT_LIMIT = 0x44,
+ PMBUS_VOUT_UV_FAULT_RESPONSE = 0x45,
+ PMBUS_IOUT_OC_FAULT_LIMIT = 0x46,
+ PMBUS_IOUT_OC_FAULT_RESPONSE = 0x47,
+ PMBUS_IOUT_OC_LV_FAULT_LIMIT = 0x48,
+ PMBUS_IOUT_OC_LV_FAULT_RESPONSE = 0x49,
+ PMBUS_IOUT_OC_WARN_LIMIT = 0x4A,
+ PMBUS_IOUT_UC_FAULT_LIMIT = 0x4B,
+ PMBUS_IOUT_UC_FAULT_RESPONSE = 0x4C,
+
+ PMBUS_OT_FAULT_LIMIT = 0x4F,
+ PMBUS_OT_FAULT_RESPONSE = 0x50,
+ PMBUS_OT_WARN_LIMIT = 0x51,
+ PMBUS_UT_WARN_LIMIT = 0x52,
+ PMBUS_UT_FAULT_LIMIT = 0x53,
+ PMBUS_UT_FAULT_RESPONSE = 0x54,
+ PMBUS_VIN_OV_FAULT_LIMIT = 0x55,
+ PMBUS_VIN_OV_FAULT_RESPONSE = 0x56,
+ PMBUS_VIN_OV_WARN_LIMIT = 0x57,
+ PMBUS_VIN_UV_WARN_LIMIT = 0x58,
+ PMBUS_VIN_UV_FAULT_LIMIT = 0x59,
+
+ PMBUS_IIN_OC_FAULT_LIMIT = 0x5B,
+ PMBUS_IIN_OC_WARN_LIMIT = 0x5D,
+
+ PMBUS_POUT_OP_FAULT_LIMIT = 0x68,
+ PMBUS_POUT_OP_WARN_LIMIT = 0x6A,
+ PMBUS_PIN_OP_WARN_LIMIT = 0x6B,
+
+ PMBUS_STATUS_BYTE = 0x78,
+ PMBUS_STATUS_WORD = 0x79,
+ PMBUS_STATUS_VOUT = 0x7A,
+ PMBUS_STATUS_IOUT = 0x7B,
+ PMBUS_STATUS_INPUT = 0x7C,
+ PMBUS_STATUS_TEMPERATURE = 0x7D,
+ PMBUS_STATUS_CML = 0x7E,
+ PMBUS_STATUS_OTHER = 0x7F,
+ PMBUS_STATUS_MFR_SPECIFIC = 0x80,
+ PMBUS_STATUS_FAN_12 = 0x81,
+ PMBUS_STATUS_FAN_34 = 0x82,
+
+ PMBUS_READ_VIN = 0x88,
+ PMBUS_READ_IIN = 0x89,
+ PMBUS_READ_VCAP = 0x8A,
+ PMBUS_READ_VOUT = 0x8B,
+ PMBUS_READ_IOUT = 0x8C,
+ PMBUS_READ_TEMPERATURE_1 = 0x8D,
+ PMBUS_READ_TEMPERATURE_2 = 0x8E,
+ PMBUS_READ_TEMPERATURE_3 = 0x8F,
+ PMBUS_READ_FAN_SPEED_1 = 0x90,
+ PMBUS_READ_FAN_SPEED_2 = 0x91,
+ PMBUS_READ_FAN_SPEED_3 = 0x92,
+ PMBUS_READ_FAN_SPEED_4 = 0x93,
+ PMBUS_READ_DUTY_CYCLE = 0x94,
+ PMBUS_READ_FREQUENCY = 0x95,
+ PMBUS_READ_POUT = 0x96,
+ PMBUS_READ_PIN = 0x97,
+
+ PMBUS_REVISION = 0x98,
+ PMBUS_MFR_ID = 0x99,
+ PMBUS_MFR_MODEL = 0x9A,
+ PMBUS_MFR_REVISION = 0x9B,
+ PMBUS_MFR_LOCATION = 0x9C,
+ 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
+ * registers but exist as manufacturer specific registers on individual chips.
+ * Must be mapped to real registers in device specific code.
+ *
+ * Semantics:
+ * Virtual registers are all word size.
+ * READ registers are read-only; writes are either ignored or return an error.
+ * RESET registers are read/write. Reading reset registers returns zero
+ * (used for detection), writing any value causes the associated history to be
+ * reset.
+ * Virtual registers have to be handled in device specific driver code. Chip
+ * driver code returns non-negative register values if a virtual register is
+ * supported, or a negative error code if not. The chip driver may return
+ * -ENODATA or any other error code in this case, though an error code other
+ * than -ENODATA is handled more efficiently and thus preferred. Either case,
+ * the calling PMBus core code will abort if the chip driver returns an error
+ * code when reading or writing virtual registers.
+ */
+ PMBUS_VIRT_BASE = 0x100,
+ PMBUS_VIRT_READ_TEMP_AVG,
+ PMBUS_VIRT_READ_TEMP_MIN,
+ PMBUS_VIRT_READ_TEMP_MAX,
+ PMBUS_VIRT_RESET_TEMP_HISTORY,
+ PMBUS_VIRT_READ_VIN_AVG,
+ PMBUS_VIRT_READ_VIN_MIN,
+ PMBUS_VIRT_READ_VIN_MAX,
+ PMBUS_VIRT_RESET_VIN_HISTORY,
+ PMBUS_VIRT_READ_IIN_AVG,
+ PMBUS_VIRT_READ_IIN_MIN,
+ PMBUS_VIRT_READ_IIN_MAX,
+ PMBUS_VIRT_RESET_IIN_HISTORY,
+ PMBUS_VIRT_READ_PIN_AVG,
+ PMBUS_VIRT_READ_PIN_MIN,
+ PMBUS_VIRT_READ_PIN_MAX,
+ PMBUS_VIRT_RESET_PIN_HISTORY,
+ PMBUS_VIRT_READ_POUT_AVG,
+ PMBUS_VIRT_READ_POUT_MIN,
+ PMBUS_VIRT_READ_POUT_MAX,
+ PMBUS_VIRT_RESET_POUT_HISTORY,
+ PMBUS_VIRT_READ_VOUT_AVG,
+ PMBUS_VIRT_READ_VOUT_MIN,
+ PMBUS_VIRT_READ_VOUT_MAX,
+ PMBUS_VIRT_RESET_VOUT_HISTORY,
+ PMBUS_VIRT_READ_IOUT_AVG,
+ PMBUS_VIRT_READ_IOUT_MIN,
+ PMBUS_VIRT_READ_IOUT_MAX,
+ PMBUS_VIRT_RESET_IOUT_HISTORY,
+ PMBUS_VIRT_READ_TEMP2_AVG,
+ PMBUS_VIRT_READ_TEMP2_MIN,
+ PMBUS_VIRT_READ_TEMP2_MAX,
+ PMBUS_VIRT_RESET_TEMP2_HISTORY,
+
+ PMBUS_VIRT_READ_VMON,
+ PMBUS_VIRT_VMON_UV_WARN_LIMIT,
+ PMBUS_VIRT_VMON_OV_WARN_LIMIT,
+ PMBUS_VIRT_VMON_UV_FAULT_LIMIT,
+ PMBUS_VIRT_VMON_OV_FAULT_LIMIT,
+ PMBUS_VIRT_STATUS_VMON,
+
+ /*
+ * RPM and PWM Fan control
+ *
+ * Drivers wanting to expose PWM control must define the behaviour of
+ * PMBUS_VIRT_PWM_[1-4] and PMBUS_VIRT_PWM_ENABLE_[1-4] in the
+ * {read,write}_word_data callback.
+ *
+ * pmbus core provides a default implementation for
+ * PMBUS_VIRT_FAN_TARGET_[1-4].
+ *
+ * TARGET, PWM and PWM_ENABLE members must be defined sequentially;
+ * pmbus core uses the difference between the provided register and
+ * it's _1 counterpart to calculate the FAN/PWM ID.
+ */
+ PMBUS_VIRT_FAN_TARGET_1,
+ PMBUS_VIRT_FAN_TARGET_2,
+ PMBUS_VIRT_FAN_TARGET_3,
+ PMBUS_VIRT_FAN_TARGET_4,
+ PMBUS_VIRT_PWM_1,
+ PMBUS_VIRT_PWM_2,
+ PMBUS_VIRT_PWM_3,
+ PMBUS_VIRT_PWM_4,
+ PMBUS_VIRT_PWM_ENABLE_1,
+ PMBUS_VIRT_PWM_ENABLE_2,
+ PMBUS_VIRT_PWM_ENABLE_3,
+ PMBUS_VIRT_PWM_ENABLE_4,
+
+ /* Samples for average
+ *
+ * Drivers wanting to expose functionality for changing the number of
+ * samples used for average values should implement support in
+ * {read,write}_word_data callback for either PMBUS_VIRT_SAMPLES if it
+ * applies to all types of measurements, or any number of specific
+ * PMBUS_VIRT_*_SAMPLES registers to allow for individual control.
+ */
+ PMBUS_VIRT_SAMPLES,
+ PMBUS_VIRT_IN_SAMPLES,
+ PMBUS_VIRT_CURR_SAMPLES,
+ PMBUS_VIRT_POWER_SAMPLES,
+ PMBUS_VIRT_TEMP_SAMPLES,
+};
+
+/*
+ * OPERATION
+ */
+#define PB_OPERATION_CONTROL_ON BIT(7)
+
+/*
+ * WRITE_PROTECT
+ */
+#define PB_WP_ALL BIT(7) /* all but WRITE_PROTECT */
+#define PB_WP_OP BIT(6) /* all but WP, OPERATION, PAGE */
+#define PB_WP_VOUT BIT(5) /* all but WP, OPERATION, PAGE, VOUT, ON_OFF */
+
+#define PB_WP_ANY (PB_WP_ALL | PB_WP_OP | PB_WP_VOUT)
+
+/*
+ * CAPABILITY
+ */
+#define PB_CAPABILITY_SMBALERT BIT(4)
+#define PB_CAPABILITY_ERROR_CHECK BIT(7)
+
+/*
+ * VOUT_MODE
+ */
+#define PB_VOUT_MODE_MODE_MASK 0xe0
+#define PB_VOUT_MODE_PARAM_MASK 0x1f
+
+#define PB_VOUT_MODE_LINEAR 0x00
+#define PB_VOUT_MODE_VID 0x20
+#define PB_VOUT_MODE_DIRECT 0x40
+
+/*
+ * Fan configuration
+ */
+#define PB_FAN_2_PULSE_MASK (BIT(0) | BIT(1))
+#define PB_FAN_2_RPM BIT(2)
+#define PB_FAN_2_INSTALLED BIT(3)
+#define PB_FAN_1_PULSE_MASK (BIT(4) | BIT(5))
+#define PB_FAN_1_RPM BIT(6)
+#define PB_FAN_1_INSTALLED BIT(7)
+
+enum pmbus_fan_mode { percent = 0, rpm };
+
+/*
+ * STATUS_BYTE, STATUS_WORD (lower)
+ */
+#define PB_STATUS_NONE_ABOVE BIT(0)
+#define PB_STATUS_CML BIT(1)
+#define PB_STATUS_TEMPERATURE BIT(2)
+#define PB_STATUS_VIN_UV BIT(3)
+#define PB_STATUS_IOUT_OC BIT(4)
+#define PB_STATUS_VOUT_OV BIT(5)
+#define PB_STATUS_OFF BIT(6)
+#define PB_STATUS_BUSY BIT(7)
+
+/*
+ * STATUS_WORD (upper)
+ */
+#define PB_STATUS_UNKNOWN BIT(8)
+#define PB_STATUS_OTHER BIT(9)
+#define PB_STATUS_FANS BIT(10)
+#define PB_STATUS_POWER_GOOD_N BIT(11)
+#define PB_STATUS_WORD_MFR BIT(12)
+#define PB_STATUS_INPUT BIT(13)
+#define PB_STATUS_IOUT_POUT BIT(14)
+#define PB_STATUS_VOUT BIT(15)
+
+/*
+ * STATUS_IOUT
+ */
+#define PB_POUT_OP_WARNING BIT(0)
+#define PB_POUT_OP_FAULT BIT(1)
+#define PB_POWER_LIMITING BIT(2)
+#define PB_CURRENT_SHARE_FAULT BIT(3)
+#define PB_IOUT_UC_FAULT BIT(4)
+#define PB_IOUT_OC_WARNING BIT(5)
+#define PB_IOUT_OC_LV_FAULT BIT(6)
+#define PB_IOUT_OC_FAULT BIT(7)
+
+/*
+ * 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)
+#define PB_VOLTAGE_OV_FAULT BIT(7)
+
+/*
+ * STATUS_INPUT
+ */
+#define PB_PIN_OP_WARNING BIT(0)
+#define PB_IIN_OC_WARNING BIT(1)
+#define PB_IIN_OC_FAULT BIT(2)
+
+/*
+ * STATUS_TEMPERATURE
+ */
+#define PB_TEMP_UT_FAULT BIT(4)
+#define PB_TEMP_UT_WARNING BIT(5)
+#define PB_TEMP_OT_WARNING BIT(6)
+#define PB_TEMP_OT_FAULT BIT(7)
+
+/*
+ * STATUS_FAN
+ */
+#define PB_FAN_AIRFLOW_WARNING BIT(0)
+#define PB_FAN_AIRFLOW_FAULT BIT(1)
+#define PB_FAN_FAN2_SPEED_OVERRIDE BIT(2)
+#define PB_FAN_FAN1_SPEED_OVERRIDE BIT(3)
+#define PB_FAN_FAN2_WARNING BIT(4)
+#define PB_FAN_FAN1_WARNING BIT(5)
+#define PB_FAN_FAN2_FAULT BIT(6)
+#define PB_FAN_FAN1_FAULT BIT(7)
+
+/*
+ * CML_FAULT_STATUS
+ */
+#define PB_CML_FAULT_OTHER_MEM_LOGIC BIT(0)
+#define PB_CML_FAULT_OTHER_COMM BIT(1)
+#define PB_CML_FAULT_PROCESSOR BIT(3)
+#define PB_CML_FAULT_MEMORY BIT(4)
+#define PB_CML_FAULT_PACKET_ERROR BIT(5)
+#define PB_CML_FAULT_INVALID_DATA BIT(6)
+#define PB_CML_FAULT_INVALID_COMMAND BIT(7)
+
+enum pmbus_sensor_classes {
+ PSC_VOLTAGE_IN = 0,
+ PSC_VOLTAGE_OUT,
+ PSC_CURRENT_IN,
+ PSC_CURRENT_OUT,
+ PSC_POWER,
+ PSC_TEMPERATURE,
+ PSC_FAN,
+ PSC_PWM,
+ PSC_NUM_CLASSES /* Number of power 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)
+#define PMBUS_HAVE_VCAP BIT(1)
+#define PMBUS_HAVE_VOUT BIT(2)
+#define PMBUS_HAVE_IIN BIT(3)
+#define PMBUS_HAVE_IOUT BIT(4)
+#define PMBUS_HAVE_PIN BIT(5)
+#define PMBUS_HAVE_POUT BIT(6)
+#define PMBUS_HAVE_FAN12 BIT(7)
+#define PMBUS_HAVE_FAN34 BIT(8)
+#define PMBUS_HAVE_TEMP BIT(9)
+#define PMBUS_HAVE_TEMP2 BIT(10)
+#define PMBUS_HAVE_TEMP3 BIT(11)
+#define PMBUS_HAVE_STATUS_VOUT BIT(12)
+#define PMBUS_HAVE_STATUS_IOUT BIT(13)
+#define PMBUS_HAVE_STATUS_INPUT BIT(14)
+#define PMBUS_HAVE_STATUS_TEMP BIT(15)
+#define PMBUS_HAVE_STATUS_FAN12 BIT(16)
+#define PMBUS_HAVE_STATUS_FAN34 BIT(17)
+#define PMBUS_HAVE_VMON BIT(18)
+#define PMBUS_HAVE_STATUS_VMON BIT(19)
+#define PMBUS_HAVE_PWM12 BIT(20)
+#define PMBUS_HAVE_PWM34 BIT(21)
+#define PMBUS_HAVE_SAMPLES BIT(22)
+
+#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, 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 */
+ /*
+ * Support one set of coefficients for each sensor type
+ * Used for chips providing data in direct mode.
+ */
+ int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */
+ int b[PSC_NUM_CLASSES]; /* offset */
+ 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
+ * necessary.
+ * Functions return the register value (read) or zero (write) if
+ * successful. A return value of -ENODATA indicates that there is no
+ * manufacturer specific register, but that a standard PMBus register
+ * may exist. Any other negative return value indicates that the
+ * register does not exist, and that no attempt should be made to read
+ * 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 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);
+ /*
+ * The identify function determines supported PMBus functionality.
+ * This function is only necessary if a chip driver supports multiple
+ * chips, and the chip functionality is not pre-determined.
+ */
+ int (*identify)(struct i2c_client *client,
+ struct pmbus_driver_info *info);
+
+ /* Regulator functionality, if supported by this chip driver. */
+ int num_regulators;
+ const struct regulator_desc *reg_desc;
+
+ /* custom attributes */
+ const struct attribute_group **groups;
+};
+
+/* Regulator ops */
+
+extern const struct regulator_ops pmbus_regulator_ops;
+
+/* Macros for filling in array of struct regulator_desc */
+#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step) \
+ [_id] = { \
+ .name = (_name # _id), \
+ .id = (_id), \
+ .of_match = of_match_ptr(_name # _id), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .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);
+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,
+ u8 value);
+int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+ u8 mask, u8 value);
+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, 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,
+ enum pmbus_fan_mode mode);
+int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
+ enum pmbus_fan_mode mode);
+int pmbus_update_fan(struct i2c_client *client, int page, int id,
+ u8 config, u8 mask, u16 command);
+struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client);
+
+#endif /* PMBUS_H */
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
new file mode 100644
index 000000000..7ec049347
--- /dev/null
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -0,0 +1,3431 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for PMBus devices
+ *
+ * Copyright (c) 2010, 2011 Ericsson AB.
+ * Copyright (c) 2012 Guenter Roeck
+ */
+
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.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"
+
+/*
+ * Number of additional attribute pointers to allocate
+ * with each call to krealloc
+ */
+#define PMBUS_ATTR_ALLOC_SIZE 32
+#define PMBUS_NAME_SIZE 24
+
+struct pmbus_sensor {
+ struct pmbus_sensor *next;
+ 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 */
+ bool convert; /* Whether or not to apply linear/vid/direct */
+ int data; /* Sensor data.
+ Negative if there was a read error */
+};
+#define to_pmbus_sensor(_attr) \
+ container_of(_attr, struct pmbus_sensor, attribute)
+
+struct pmbus_boolean {
+ char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */
+ struct sensor_device_attribute attribute;
+ struct pmbus_sensor *s1;
+ struct pmbus_sensor *s2;
+};
+#define to_pmbus_boolean(_attr) \
+ container_of(_attr, struct pmbus_boolean, attribute)
+
+struct pmbus_label {
+ char name[PMBUS_NAME_SIZE]; /* sysfs label name */
+ struct device_attribute attribute;
+ char label[PMBUS_NAME_SIZE]; /* 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;
+
+ u32 flags; /* from platform data */
+
+ int exponent[PMBUS_PAGES];
+ /* linear mode: exponent for output voltages */
+
+ const struct pmbus_driver_info *info;
+
+ int max_attributes;
+ int num_attributes;
+ struct attribute_group group;
+ const struct attribute_group **groups;
+ struct dentry *debugfs; /* debugfs device directory */
+
+ struct pmbus_sensor *sensors;
+
+ struct mutex update_lock;
+
+ bool has_status_word; /* device uses STATUS_WORD register */
+ int (*read_status)(struct i2c_client *client, int page);
+
+ 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 {
+ struct i2c_client *client;
+ u8 page;
+ u8 reg;
+};
+
+static const int pmbus_fan_rpm_mask[] = {
+ PB_FAN_1_RPM,
+ PB_FAN_2_RPM,
+ PB_FAN_1_RPM,
+ PB_FAN_2_RPM,
+};
+
+static const int pmbus_fan_config_registers[] = {
+ PMBUS_FAN_CONFIG_12,
+ PMBUS_FAN_CONFIG_12,
+ PMBUS_FAN_CONFIG_34,
+ PMBUS_FAN_CONFIG_34
+};
+
+static const int pmbus_fan_command_registers[] = {
+ PMBUS_FAN_COMMAND_1,
+ PMBUS_FAN_COMMAND_2,
+ PMBUS_FAN_COMMAND_3,
+ PMBUS_FAN_COMMAND_4,
+};
+
+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;
+
+ for (sensor = data->sensors; sensor; sensor = sensor->next)
+ if (sensor->reg == reg)
+ sensor->update = update;
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS);
+
+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)
+ return 0;
+
+ 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;
+
+ rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
+ if (rv < 0)
+ return rv;
+
+ 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_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, 0xff);
+ if (rv < 0)
+ return rv;
+
+ return i2c_smbus_write_byte(client, value);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS);
+
+/*
+ * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->write_byte) {
+ status = info->write_byte(client, page, value);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_write_byte(client, page, value);
+}
+
+int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
+ u16 word)
+{
+ int rv;
+
+ rv = pmbus_set_page(client, page, 0xff);
+ if (rv < 0)
+ return rv;
+
+ return i2c_smbus_write_word_data(client, reg, word);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS);
+
+
+static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int bit;
+ int id;
+ int rv;
+
+ switch (reg) {
+ case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
+ id = reg - PMBUS_VIRT_FAN_TARGET_1;
+ bit = pmbus_fan_rpm_mask[id];
+ rv = pmbus_update_fan(client, page, id, bit, bit, word);
+ break;
+ default:
+ rv = -ENXIO;
+ break;
+ }
+
+ return rv;
+}
+
+/*
+ * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->write_word_data) {
+ status = info->write_word_data(client, page, reg, word);
+ if (status != -ENODATA)
+ return status;
+ }
+
+ if (reg >= PMBUS_VIRT_BASE)
+ return pmbus_write_virt_reg(client, page, reg, word);
+
+ 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)
+{
+ int from;
+ int rv;
+ u8 to;
+
+ 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,
+ pmbus_fan_config_registers[id], to);
+ if (rv < 0)
+ return rv;
+ }
+
+ return _pmbus_write_word_data(client, page,
+ pmbus_fan_command_registers[id], command);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS);
+
+int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
+{
+ int rv;
+
+ rv = pmbus_set_page(client, page, phase);
+ if (rv < 0)
+ return rv;
+
+ return i2c_smbus_read_word_data(client, reg);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS);
+
+static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg)
+{
+ int rv;
+ int id;
+
+ switch (reg) {
+ case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
+ id = reg - PMBUS_VIRT_FAN_TARGET_1;
+ rv = pmbus_get_fan_rate_device(client, page, id, rpm);
+ break;
+ default:
+ rv = -ENXIO;
+ break;
+ }
+
+ return rv;
+}
+
+/*
+ * _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 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, phase, reg);
+ if (status != -ENODATA)
+ return status;
+ }
+
+ if (reg >= PMBUS_VIRT_BASE)
+ return pmbus_read_virt_reg(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, 0xff);
+ if (rv < 0)
+ return rv;
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+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, 0xff);
+ if (rv < 0)
+ return rv;
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+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)
+{
+ unsigned int tmp;
+ int rv;
+
+ 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);
+
+ return rv;
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
+
+static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
+ char *data_buf)
+{
+ int rv;
+
+ 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,
+ int reg)
+{
+ struct pmbus_sensor *sensor;
+
+ for (sensor = data->sensors; sensor; sensor = sensor->next) {
+ if (sensor->page == page && sensor->reg == reg)
+ return sensor;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
+ enum pmbus_fan_mode mode,
+ bool from_cache)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ bool want_rpm, have_rpm;
+ struct pmbus_sensor *s;
+ int config;
+ int reg;
+
+ want_rpm = (mode == rpm);
+
+ if (from_cache) {
+ reg = want_rpm ? PMBUS_VIRT_FAN_TARGET_1 : PMBUS_VIRT_PWM_1;
+ s = pmbus_find_sensor(data, page, reg + id);
+ if (IS_ERR(s))
+ return PTR_ERR(s);
+
+ return s->data;
+ }
+
+ 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, 0xff,
+ pmbus_fan_command_registers[id]);
+
+ /* Can't sensibly map between RPM and PWM, just return zero */
+ return 0;
+}
+
+int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
+ enum pmbus_fan_mode mode)
+{
+ return pmbus_get_fan_rate(client, page, id, mode, false);
+}
+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_NS_GPL(pmbus_get_fan_rate_cached, PMBUS);
+
+static void pmbus_clear_fault_page(struct i2c_client *client, int page)
+{
+ _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
+}
+
+void pmbus_clear_faults(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < data->info->pages; i++)
+ pmbus_clear_fault_page(client, i);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS);
+
+static int pmbus_check_status_cml(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ int status, status2;
+
+ status = data->read_status(client, -1);
+ if (status < 0 || (status & PB_STATUS_CML)) {
+ status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
+ if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))
+ return -EIO;
+ }
+ return 0;
+}
+
+static bool pmbus_check_register(struct i2c_client *client,
+ int (*func)(struct i2c_client *client,
+ int page, int reg),
+ int page, int reg)
+{
+ int rv;
+ struct pmbus_data *data = i2c_get_clientdata(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;
+}
+
+static bool pmbus_check_status_register(struct i2c_client *client, int page)
+{
+ int status;
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ status = data->read_status(client, page);
+ if (status >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK) &&
+ (status & PB_STATUS_CML)) {
+ status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
+ if (status < 0 || (status & PB_CML_FAULT_INVALID_COMMAND))
+ status = -EIO;
+ }
+
+ pmbus_clear_fault_page(client, -1);
+ return status >= 0;
+}
+
+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_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);
+}
+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;
+}
+
+const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ return data->info;
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS);
+
+static int pmbus_get_status(struct i2c_client *client, int page, int reg)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ int status;
+
+ 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);
+ 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;
+ }
+
+ /* 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 s64 pmbus_reg2data_linear(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
+{
+ s16 exponent;
+ s32 mantissa;
+ s64 val;
+
+ if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */
+ exponent = data->exponent[sensor->page];
+ mantissa = (u16) sensor->data;
+ } else { /* LINEAR11 */
+ exponent = ((s16)sensor->data) >> 11;
+ mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5;
+ }
+
+ val = mantissa;
+
+ /* scale result to milli-units for all sensors except fans */
+ if (sensor->class != PSC_FAN)
+ val = val * 1000LL;
+
+ /* scale result to micro-units for power sensors */
+ if (sensor->class == PSC_POWER)
+ val = val * 1000LL;
+
+ if (exponent >= 0)
+ val <<= exponent;
+ else
+ val >>= -exponent;
+
+ return val;
+}
+
+/*
+ * Convert direct sensor values to milli- or micro-units
+ * depending on sensor type.
+ */
+static s64 pmbus_reg2data_direct(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
+{
+ s64 b, val = (s16)sensor->data;
+ s32 m, R;
+
+ m = data->info->m[sensor->class];
+ b = data->info->b[sensor->class];
+ R = data->info->R[sensor->class];
+
+ if (m == 0)
+ return 0;
+
+ /* X = 1/m * (Y * 10^-R - b) */
+ R = -R;
+ /* scale result to milli-units for everything but fans */
+ if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
+ R += 3;
+ b *= 1000;
+ }
+
+ /* scale result to micro-units for power sensors */
+ if (sensor->class == PSC_POWER) {
+ R += 3;
+ b *= 1000;
+ }
+
+ while (R > 0) {
+ val *= 10;
+ R--;
+ }
+ while (R < 0) {
+ val = div_s64(val + 5LL, 10L); /* round closest */
+ R++;
+ }
+
+ val = div_s64(val - b, m);
+ return val;
+}
+
+/*
+ * Convert VID sensor values to milli- or micro-units
+ * depending on sensor type.
+ */
+static s64 pmbus_reg2data_vid(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
+{
+ long val = sensor->data;
+ long rv = 0;
+
+ switch (data->info->vrm_version[sensor->page]) {
+ case vr11:
+ if (val >= 0x02 && val <= 0xb2)
+ rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);
+ break;
+ case vr12:
+ if (val >= 0x01)
+ rv = 250 + (val - 1) * 5;
+ break;
+ case vr13:
+ if (val >= 0x01)
+ rv = 500 + (val - 1) * 10;
+ break;
+ case imvp9:
+ if (val >= 0x01)
+ rv = 200 + (val - 1) * 10;
+ break;
+ case amd625mv:
+ if (val >= 0x0 && val <= 0xd8)
+ rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100);
+ break;
+ }
+ return rv;
+}
+
+static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
+{
+ s64 val;
+
+ if (!sensor->convert)
+ return sensor->data;
+
+ switch (data->info->format[sensor->class]) {
+ case direct:
+ val = pmbus_reg2data_direct(data, sensor);
+ break;
+ 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);
+ break;
+ }
+ return val;
+}
+
+#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, s64 val)
+{
+ s16 exponent = 0, mantissa;
+ bool negative = false;
+
+ /* simple case */
+ if (val == 0)
+ return 0;
+
+ if (sensor->class == PSC_VOLTAGE_OUT) {
+ /* LINEAR16 does not support negative voltages */
+ if (val < 0)
+ return 0;
+
+ /*
+ * For a static exponents, we don't have a choice
+ * but to adjust the value to it.
+ */
+ if (data->exponent[sensor->page] < 0)
+ val <<= -data->exponent[sensor->page];
+ else
+ val >>= data->exponent[sensor->page];
+ val = DIV_ROUND_CLOSEST_ULL(val, 1000);
+ return clamp_val(val, 0, 0xffff);
+ }
+
+ if (val < 0) {
+ negative = true;
+ val = -val;
+ }
+
+ /* Power is in uW. Convert to mW before converting. */
+ if (sensor->class == PSC_POWER)
+ 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 * 1000LL;
+
+ /* Reduce large mantissa until it fits into 10 bit */
+ while (val >= MAX_LIN_MANTISSA && exponent < 15) {
+ exponent++;
+ val >>= 1;
+ }
+ /* Increase small mantissa to improve precision */
+ while (val < MIN_LIN_MANTISSA && exponent > -15) {
+ exponent--;
+ val <<= 1;
+ }
+
+ /* Convert mantissa from milli-units to units */
+ mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff);
+
+ /* restore sign */
+ if (negative)
+ mantissa = -mantissa;
+
+ /* Convert to 5 bit exponent, 11 bit mantissa */
+ return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
+}
+
+static u16 pmbus_data2reg_direct(struct pmbus_data *data,
+ struct pmbus_sensor *sensor, s64 val)
+{
+ s64 b;
+ s32 m, R;
+
+ m = data->info->m[sensor->class];
+ b = data->info->b[sensor->class];
+ R = data->info->R[sensor->class];
+
+ /* Power is in uW. Adjust R and b. */
+ if (sensor->class == PSC_POWER) {
+ R -= 3;
+ b *= 1000;
+ }
+
+ /* Calculate Y = (m * X + b) * 10^R */
+ if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
+ R -= 3; /* Adjust R and b for data in milli-units */
+ b *= 1000;
+ }
+ val = val * m + b;
+
+ while (R > 0) {
+ val *= 10;
+ R--;
+ }
+ while (R < 0) {
+ val = div_s64(val + 5LL, 10L); /* round closest */
+ R++;
+ }
+
+ return (u16)clamp_val(val, S16_MIN, S16_MAX);
+}
+
+static u16 pmbus_data2reg_vid(struct pmbus_data *data,
+ struct pmbus_sensor *sensor, s64 val)
+{
+ val = clamp_val(val, 500, 1600);
+
+ return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625);
+}
+
+static u16 pmbus_data2reg(struct pmbus_data *data,
+ struct pmbus_sensor *sensor, s64 val)
+{
+ u16 regval;
+
+ if (!sensor->convert)
+ return val;
+
+ switch (data->info->format[sensor->class]) {
+ case direct:
+ regval = pmbus_data2reg_direct(data, sensor, val);
+ break;
+ 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);
+ break;
+ }
+ return regval;
+}
+
+/*
+ * Return boolean calculated from converted data.
+ * <index> defines a status register index and mask.
+ * The mask is in the lower 8 bits, the register index is in bits 8..23.
+ *
+ * The associated pmbus_boolean structure contains optional pointers to two
+ * sensor attributes. If specified, those attributes are compared against each
+ * other to determine if a limit has been exceeded.
+ *
+ * If the sensor attribute pointers are NULL, the function returns true if
+ * (status[reg] & mask) is true.
+ *
+ * If sensor attribute pointers are provided, a comparison against a specified
+ * limit has to be performed to determine the boolean result.
+ * In this case, the function returns true if v1 >= v2 (where v1 and v2 are
+ * sensor values referenced by sensor attribute pointers s1 and s2).
+ *
+ * To determine if an object exceeds upper limits, specify <s1,s2> = <v,limit>.
+ * To determine if an object exceeds lower limits, specify <s1,s2> = <limit,v>.
+ *
+ * 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 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 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;
+
+ 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 (regval) {
+ ret = _pmbus_write_byte_data(client, page, reg, regval);
+ if (ret)
+ goto unlock;
+ }
+ if (s1 && s2) {
+ s64 v1, v2;
+
+ 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;
+}
+
+static ssize_t pmbus_show_boolean(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct pmbus_boolean *boolean = to_pmbus_boolean(attr);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ int val;
+
+ val = pmbus_get_boolean(client, boolean, attr->index);
+ if (val < 0)
+ return val;
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t pmbus_show_sensor(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ 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)
+ 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,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
+ ssize_t rv = count;
+ s64 val;
+ int ret;
+ u16 regval;
+
+ if (kstrtos64(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ regval = pmbus_data2reg(data, sensor, val);
+ ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
+ if (ret < 0)
+ rv = ret;
+ else
+ sensor->data = -ENODATA;
+ mutex_unlock(&data->update_lock);
+ return rv;
+}
+
+static ssize_t pmbus_show_label(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct pmbus_label *label = to_pmbus_label(da);
+
+ 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 = devm_krealloc(data->dev, data->group.attrs,
+ new_max_attrs * sizeof(void *),
+ GFP_KERNEL);
+ if (!new_attrs)
+ return -ENOMEM;
+ data->group.attrs = new_attrs;
+ data->max_attributes = new_max_attrs;
+ }
+
+ data->group.attrs[data->num_attributes++] = attr;
+ data->group.attrs[data->num_attributes] = NULL;
+ return 0;
+}
+
+static void pmbus_dev_attr_init(struct device_attribute *dev_attr,
+ const char *name,
+ umode_t mode,
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*store)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count))
+{
+ sysfs_attr_init(&dev_attr->attr);
+ dev_attr->attr.name = name;
+ dev_attr->attr.mode = mode;
+ dev_attr->show = show;
+ dev_attr->store = store;
+}
+
+static void pmbus_attr_init(struct sensor_device_attribute *a,
+ const char *name,
+ umode_t mode,
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*store)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count),
+ int idx)
+{
+ pmbus_dev_attr_init(&a->dev_attr, name, mode, show, store);
+ a->index = idx;
+}
+
+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,
+ 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;
+
+ a = &boolean->attribute;
+
+ snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s",
+ name, seq, type);
+ boolean->s1 = s1;
+ boolean->s2 = s2;
+ pmbus_attr_init(a, boolean->name, 0444, pmbus_show_boolean, NULL,
+ 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 phase,
+ int reg,
+ enum pmbus_sensor_classes class,
+ bool update, bool readonly,
+ bool convert)
+{
+ struct pmbus_sensor *sensor;
+ struct device_attribute *a;
+
+ sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return NULL;
+ a = &sensor->attribute;
+
+ if (type)
+ snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s",
+ name, seq, type);
+ else
+ snprintf(sensor->name, sizeof(sensor->name), "%s%d",
+ name, seq);
+
+ if (data->flags & PMBUS_WRITE_PROTECTED)
+ 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);
+
+ if (pmbus_add_attribute(data, &a->attr))
+ return NULL;
+
+ 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, int phase)
+{
+ struct pmbus_label *label;
+ struct device_attribute *a;
+
+ label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL);
+ if (!label)
+ return -ENOMEM;
+
+ a = &label->attribute;
+
+ snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq);
+ 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);
+}
+
+/*
+ * Search for attributes. Allocate sensors, booleans, and labels as needed.
+ */
+
+/*
+ * The pmbus_limit_attr structure describes a single limit attribute
+ * and its associated alarm attribute.
+ */
+struct pmbus_limit_attr {
+ u16 reg; /* Limit register */
+ u16 sbit; /* Alarm attribute status bit */
+ bool update; /* True if register needs updates */
+ bool low; /* True if low limit; for limits with compare
+ functions only */
+ const char *attr; /* Attribute name */
+ const char *alarm; /* Alarm attribute name */
+};
+
+/*
+ * The pmbus_sensor_attr structure describes one sensor attribute. This
+ * description includes a reference to the associated limit attributes.
+ */
+struct pmbus_sensor_attr {
+ u16 reg; /* sensor register */
+ u16 gbit; /* generic status bit */
+ u8 nlimit; /* # of limit registers */
+ enum pmbus_sensor_classes class;/* sensor class */
+ const char *label; /* sensor label */
+ bool paged; /* true if paged sensor */
+ bool update; /* true if update needed */
+ bool compare; /* true if compare function needed */
+ u32 func; /* sensor mask */
+ u32 sfunc; /* sensor status mask */
+ int sreg; /* status register */
+ const struct pmbus_limit_attr *limit;/* limit registers */
+};
+
+/*
+ * Add a set of limit attributes and, if supported, the associated
+ * alarm attributes.
+ * returns 0 if no alarm register found, 1 if an alarm register was found,
+ * < 0 on errors.
+ */
+static int pmbus_add_limit_attrs(struct i2c_client *client,
+ struct pmbus_data *data,
+ const struct pmbus_driver_info *info,
+ const char *name, int index, int page,
+ struct pmbus_sensor *base,
+ const struct pmbus_sensor_attr *attr)
+{
+ const struct pmbus_limit_attr *l = attr->limit;
+ int nlimit = attr->nlimit;
+ int have_alarm = 0;
+ int i, ret;
+ struct pmbus_sensor *curr;
+
+ 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, 0xff, l->reg, attr->class,
+ attr->update || l->update,
+ false, true);
+ if (!curr)
+ return -ENOMEM;
+ if (l->sbit && (info->func[page] & attr->sfunc)) {
+ ret = pmbus_add_boolean(data, name,
+ l->alarm, index,
+ attr->compare ? l->low ? curr : base
+ : NULL,
+ attr->compare ? l->low ? base : curr
+ : NULL,
+ page, attr->sreg, l->sbit);
+ if (ret)
+ return ret;
+ have_alarm = 1;
+ }
+ }
+ l++;
+ }
+ return have_alarm;
+}
+
+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 phase,
+ const struct pmbus_sensor_attr *attr,
+ bool paged)
+{
+ struct pmbus_sensor *base;
+ bool upper = !!(attr->gbit & 0xff00); /* need to check STATUS_WORD */
+ int ret;
+
+ if (attr->label) {
+ ret = pmbus_add_label(data, name, index, attr->label,
+ paged ? page + 1 : 0, phase);
+ if (ret)
+ return ret;
+ }
+ base = pmbus_add_sensor(data, name, "input", index, page, phase,
+ attr->reg, attr->class, true, true, true);
+ if (!base)
+ return -ENOMEM;
+ /* 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)
+ return ret;
+ /*
+ * Add generic alarm attribute only if there are no individual
+ * alarm attributes, if there is a global alarm bit, and if
+ * the generic status register (word or byte, depending on
+ * which global bit is set) for this page is accessible.
+ */
+ if (!ret && attr->gbit &&
+ (!upper || data->has_status_word) &&
+ pmbus_check_status_register(client, page)) {
+ ret = pmbus_add_boolean(data, name, "alarm", index,
+ NULL, NULL,
+ page, PMBUS_STATUS_WORD,
+ attr->gbit);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static bool pmbus_sensor_is_paged(const struct pmbus_driver_info *info,
+ const struct pmbus_sensor_attr *attr)
+{
+ int p;
+
+ if (attr->paged)
+ return true;
+
+ /*
+ * Some attributes may be present on more than one page despite
+ * not being marked with the paged attribute. If that is the case,
+ * then treat the sensor as being paged and add the page suffix to the
+ * attribute name.
+ * We don't just add the paged attribute to all such attributes, in
+ * order to maintain the un-suffixed labels in the case where the
+ * attribute is only on page 0.
+ */
+ for (p = 1; p < info->pages; p++) {
+ if (info->func[p] & attr->func)
+ return true;
+ }
+ return false;
+}
+
+static int pmbus_add_sensor_attrs(struct i2c_client *client,
+ struct pmbus_data *data,
+ const char *name,
+ const struct pmbus_sensor_attr *attrs,
+ int nattrs)
+{
+ const struct pmbus_driver_info *info = data->info;
+ int index, i;
+ int ret;
+
+ index = 1;
+ for (i = 0; i < nattrs; i++) {
+ int page, pages;
+ bool paged = pmbus_sensor_is_paged(info, attrs);
+
+ pages = paged ? info->pages : 1;
+ for (page = 0; page < pages; page++) {
+ 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++;
+ }
+ return 0;
+}
+
+static const struct pmbus_limit_attr vin_limit_attrs[] = {
+ {
+ .reg = PMBUS_VIN_UV_WARN_LIMIT,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_VOLTAGE_UV_WARNING,
+ }, {
+ .reg = PMBUS_VIN_UV_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_VOLTAGE_UV_FAULT | PB_VOLTAGE_VIN_OFF,
+ }, {
+ .reg = PMBUS_VIN_OV_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_VOLTAGE_OV_WARNING,
+ }, {
+ .reg = PMBUS_VIN_OV_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_VOLTAGE_OV_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_VIN_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_VIN_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_VIN_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .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",
+ },
+};
+
+static const struct pmbus_limit_attr vmon_limit_attrs[] = {
+ {
+ .reg = PMBUS_VIRT_VMON_UV_WARN_LIMIT,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_VOLTAGE_UV_WARNING,
+ }, {
+ .reg = PMBUS_VIRT_VMON_UV_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_VOLTAGE_UV_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_VMON_OV_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_VOLTAGE_OV_WARNING,
+ }, {
+ .reg = PMBUS_VIRT_VMON_OV_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_VOLTAGE_OV_FAULT,
+ }
+};
+
+static const struct pmbus_limit_attr vout_limit_attrs[] = {
+ {
+ .reg = PMBUS_VOUT_UV_WARN_LIMIT,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_VOLTAGE_UV_WARNING,
+ }, {
+ .reg = PMBUS_VOUT_UV_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_VOLTAGE_UV_FAULT,
+ }, {
+ .reg = PMBUS_VOUT_OV_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_VOLTAGE_OV_WARNING,
+ }, {
+ .reg = PMBUS_VOUT_OV_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_VOLTAGE_OV_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_VOUT_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_VOUT_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_VOUT_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .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[] = {
+ {
+ .reg = PMBUS_READ_VIN,
+ .class = PSC_VOLTAGE_IN,
+ .label = "vin",
+ .func = PMBUS_HAVE_VIN,
+ .sfunc = PMBUS_HAVE_STATUS_INPUT,
+ .sreg = PMBUS_STATUS_INPUT,
+ .gbit = PB_STATUS_VIN_UV,
+ .limit = vin_limit_attrs,
+ .nlimit = ARRAY_SIZE(vin_limit_attrs),
+ }, {
+ .reg = PMBUS_VIRT_READ_VMON,
+ .class = PSC_VOLTAGE_IN,
+ .label = "vmon",
+ .func = PMBUS_HAVE_VMON,
+ .sfunc = PMBUS_HAVE_STATUS_VMON,
+ .sreg = PMBUS_VIRT_STATUS_VMON,
+ .limit = vmon_limit_attrs,
+ .nlimit = ARRAY_SIZE(vmon_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_VCAP,
+ .class = PSC_VOLTAGE_IN,
+ .label = "vcap",
+ .func = PMBUS_HAVE_VCAP,
+ }, {
+ .reg = PMBUS_READ_VOUT,
+ .class = PSC_VOLTAGE_OUT,
+ .label = "vout",
+ .paged = true,
+ .func = PMBUS_HAVE_VOUT,
+ .sfunc = PMBUS_HAVE_STATUS_VOUT,
+ .sreg = PMBUS_STATUS_VOUT,
+ .gbit = PB_STATUS_VOUT_OV,
+ .limit = vout_limit_attrs,
+ .nlimit = ARRAY_SIZE(vout_limit_attrs),
+ }
+};
+
+/* Current attributes */
+
+static const struct pmbus_limit_attr iin_limit_attrs[] = {
+ {
+ .reg = PMBUS_IIN_OC_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_IIN_OC_WARNING,
+ }, {
+ .reg = PMBUS_IIN_OC_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_IIN_OC_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_IIN_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_IIN_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_IIN_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .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[] = {
+ {
+ .reg = PMBUS_IOUT_OC_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_IOUT_OC_WARNING,
+ }, {
+ .reg = PMBUS_IOUT_UC_FAULT_LIMIT,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_IOUT_UC_FAULT,
+ }, {
+ .reg = PMBUS_IOUT_OC_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_IOUT_OC_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_IOUT_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_IOUT_MIN,
+ .update = true,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_IOUT_MAX,
+ .update = true,
+ .attr = "highest",
+ }, {
+ .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[] = {
+ {
+ .reg = PMBUS_READ_IIN,
+ .class = PSC_CURRENT_IN,
+ .label = "iin",
+ .func = PMBUS_HAVE_IIN,
+ .sfunc = PMBUS_HAVE_STATUS_INPUT,
+ .sreg = PMBUS_STATUS_INPUT,
+ .gbit = PB_STATUS_INPUT,
+ .limit = iin_limit_attrs,
+ .nlimit = ARRAY_SIZE(iin_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_IOUT,
+ .class = PSC_CURRENT_OUT,
+ .label = "iout",
+ .paged = true,
+ .func = PMBUS_HAVE_IOUT,
+ .sfunc = PMBUS_HAVE_STATUS_IOUT,
+ .sreg = PMBUS_STATUS_IOUT,
+ .gbit = PB_STATUS_IOUT_OC,
+ .limit = iout_limit_attrs,
+ .nlimit = ARRAY_SIZE(iout_limit_attrs),
+ }
+};
+
+/* Power attributes */
+
+static const struct pmbus_limit_attr pin_limit_attrs[] = {
+ {
+ .reg = PMBUS_PIN_OP_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "alarm",
+ .sbit = PB_PIN_OP_WARNING,
+ }, {
+ .reg = PMBUS_VIRT_READ_PIN_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_PIN_MIN,
+ .update = true,
+ .attr = "input_lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_PIN_MAX,
+ .update = true,
+ .attr = "input_highest",
+ }, {
+ .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[] = {
+ {
+ .reg = PMBUS_POUT_MAX,
+ .attr = "cap",
+ .alarm = "cap_alarm",
+ .sbit = PB_POWER_LIMITING,
+ }, {
+ .reg = PMBUS_POUT_OP_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_POUT_OP_WARNING,
+ }, {
+ .reg = PMBUS_POUT_OP_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_POUT_OP_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_POUT_AVG,
+ .update = true,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_POUT_MIN,
+ .update = true,
+ .attr = "input_lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_POUT_MAX,
+ .update = true,
+ .attr = "input_highest",
+ }, {
+ .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[] = {
+ {
+ .reg = PMBUS_READ_PIN,
+ .class = PSC_POWER,
+ .label = "pin",
+ .func = PMBUS_HAVE_PIN,
+ .sfunc = PMBUS_HAVE_STATUS_INPUT,
+ .sreg = PMBUS_STATUS_INPUT,
+ .gbit = PB_STATUS_INPUT,
+ .limit = pin_limit_attrs,
+ .nlimit = ARRAY_SIZE(pin_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_POUT,
+ .class = PSC_POWER,
+ .label = "pout",
+ .paged = true,
+ .func = PMBUS_HAVE_POUT,
+ .sfunc = PMBUS_HAVE_STATUS_IOUT,
+ .sreg = PMBUS_STATUS_IOUT,
+ .limit = pout_limit_attrs,
+ .nlimit = ARRAY_SIZE(pout_limit_attrs),
+ }
+};
+
+/* Temperature atributes */
+
+static const struct pmbus_limit_attr temp_limit_attrs[] = {
+ {
+ .reg = PMBUS_UT_WARN_LIMIT,
+ .low = true,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_TEMP_UT_WARNING,
+ }, {
+ .reg = PMBUS_UT_FAULT_LIMIT,
+ .low = true,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_TEMP_UT_FAULT,
+ }, {
+ .reg = PMBUS_OT_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_TEMP_OT_WARNING,
+ }, {
+ .reg = PMBUS_OT_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_TEMP_OT_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP_MIN,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP_AVG,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP_MAX,
+ .attr = "highest",
+ }, {
+ .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[] = {
+ {
+ .reg = PMBUS_UT_WARN_LIMIT,
+ .low = true,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_TEMP_UT_WARNING,
+ }, {
+ .reg = PMBUS_UT_FAULT_LIMIT,
+ .low = true,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_TEMP_UT_FAULT,
+ }, {
+ .reg = PMBUS_OT_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_TEMP_OT_WARNING,
+ }, {
+ .reg = PMBUS_OT_FAULT_LIMIT,
+ .attr = "crit",
+ .alarm = "crit_alarm",
+ .sbit = PB_TEMP_OT_FAULT,
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP2_MIN,
+ .attr = "lowest",
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP2_AVG,
+ .attr = "average",
+ }, {
+ .reg = PMBUS_VIRT_READ_TEMP2_MAX,
+ .attr = "highest",
+ }, {
+ .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[] = {
+ {
+ .reg = PMBUS_UT_WARN_LIMIT,
+ .low = true,
+ .attr = "min",
+ .alarm = "min_alarm",
+ .sbit = PB_TEMP_UT_WARNING,
+ }, {
+ .reg = PMBUS_UT_FAULT_LIMIT,
+ .low = true,
+ .attr = "lcrit",
+ .alarm = "lcrit_alarm",
+ .sbit = PB_TEMP_UT_FAULT,
+ }, {
+ .reg = PMBUS_OT_WARN_LIMIT,
+ .attr = "max",
+ .alarm = "max_alarm",
+ .sbit = PB_TEMP_OT_WARNING,
+ }, {
+ .reg = PMBUS_OT_FAULT_LIMIT,
+ .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[] = {
+ {
+ .reg = PMBUS_READ_TEMPERATURE_1,
+ .class = PSC_TEMPERATURE,
+ .paged = true,
+ .update = true,
+ .compare = true,
+ .func = PMBUS_HAVE_TEMP,
+ .sfunc = PMBUS_HAVE_STATUS_TEMP,
+ .sreg = PMBUS_STATUS_TEMPERATURE,
+ .gbit = PB_STATUS_TEMPERATURE,
+ .limit = temp_limit_attrs,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs),
+ }, {
+ .reg = PMBUS_READ_TEMPERATURE_2,
+ .class = PSC_TEMPERATURE,
+ .paged = true,
+ .update = true,
+ .compare = true,
+ .func = PMBUS_HAVE_TEMP2,
+ .sfunc = PMBUS_HAVE_STATUS_TEMP,
+ .sreg = PMBUS_STATUS_TEMPERATURE,
+ .gbit = PB_STATUS_TEMPERATURE,
+ .limit = temp_limit_attrs2,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs2),
+ }, {
+ .reg = PMBUS_READ_TEMPERATURE_3,
+ .class = PSC_TEMPERATURE,
+ .paged = true,
+ .update = true,
+ .compare = true,
+ .func = PMBUS_HAVE_TEMP3,
+ .sfunc = PMBUS_HAVE_STATUS_TEMP,
+ .sreg = PMBUS_STATUS_TEMPERATURE,
+ .gbit = PB_STATUS_TEMPERATURE,
+ .limit = temp_limit_attrs3,
+ .nlimit = ARRAY_SIZE(temp_limit_attrs3),
+ }
+};
+
+static const int pmbus_fan_registers[] = {
+ PMBUS_READ_FAN_SPEED_1,
+ PMBUS_READ_FAN_SPEED_2,
+ PMBUS_READ_FAN_SPEED_3,
+ PMBUS_READ_FAN_SPEED_4
+};
+
+static const int pmbus_fan_status_registers[] = {
+ PMBUS_STATUS_FAN_12,
+ PMBUS_STATUS_FAN_12,
+ PMBUS_STATUS_FAN_34,
+ PMBUS_STATUS_FAN_34
+};
+
+static const u32 pmbus_fan_flags[] = {
+ PMBUS_HAVE_FAN12,
+ PMBUS_HAVE_FAN12,
+ PMBUS_HAVE_FAN34,
+ PMBUS_HAVE_FAN34
+};
+
+static const u32 pmbus_fan_status_flags[] = {
+ PMBUS_HAVE_STATUS_FAN12,
+ PMBUS_HAVE_STATUS_FAN12,
+ PMBUS_HAVE_STATUS_FAN34,
+ PMBUS_HAVE_STATUS_FAN34
+};
+
+/* Fans */
+
+/* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */
+static int pmbus_add_fan_ctrl(struct i2c_client *client,
+ struct pmbus_data *data, int index, int page, int id,
+ u8 config)
+{
+ struct pmbus_sensor *sensor;
+
+ sensor = pmbus_add_sensor(data, "fan", "target", index, page,
+ 0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
+ false, false, true);
+
+ if (!sensor)
+ return -ENOMEM;
+
+ if (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||
+ (data->info->func[page] & PMBUS_HAVE_PWM34)))
+ return 0;
+
+ sensor = pmbus_add_sensor(data, "pwm", NULL, index, page,
+ 0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM,
+ false, false, true);
+
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor = pmbus_add_sensor(data, "pwm", "enable", index, page,
+ 0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
+ true, false, false);
+
+ if (!sensor)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int pmbus_add_fan_attributes(struct i2c_client *client,
+ struct pmbus_data *data)
+{
+ const struct pmbus_driver_info *info = data->info;
+ int index = 1;
+ int page;
+ int ret;
+
+ for (page = 0; page < info->pages; page++) {
+ int f;
+
+ for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) {
+ int regval;
+
+ if (!(info->func[page] & pmbus_fan_flags[f]))
+ break;
+
+ if (!pmbus_check_word_register(client, page,
+ pmbus_fan_registers[f]))
+ break;
+
+ /*
+ * Skip fan if not installed.
+ * Each fan configuration register covers multiple fans,
+ * so we have to do some magic.
+ */
+ regval = _pmbus_read_byte_data(client, page,
+ pmbus_fan_config_registers[f]);
+ if (regval < 0 ||
+ (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4)))))
+ continue;
+
+ if (pmbus_add_sensor(data, "fan", "input", index,
+ page, 0xff, pmbus_fan_registers[f],
+ PSC_FAN, true, true, true) == NULL)
+ return -ENOMEM;
+
+ /* Fan control */
+ if (pmbus_check_word_register(client, page,
+ pmbus_fan_command_registers[f])) {
+ ret = pmbus_add_fan_ctrl(client, data, index,
+ page, f, regval);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * Each fan status register covers multiple fans,
+ * so we have to do some magic.
+ */
+ if ((info->func[page] & pmbus_fan_status_flags[f]) &&
+ pmbus_check_byte_register(client,
+ page, pmbus_fan_status_registers[f])) {
+ int reg;
+
+ if (f > 1) /* fan 3, 4 */
+ reg = PMBUS_STATUS_FAN_34;
+ else
+ reg = PMBUS_STATUS_FAN_12;
+ ret = pmbus_add_boolean(data, "fan",
+ "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, page, reg,
+ PB_FAN_FAN1_FAULT >> (f & 1));
+ if (ret)
+ return ret;
+ }
+ index++;
+ }
+ }
+ return 0;
+}
+
+struct pmbus_samples_attr {
+ int reg;
+ char *name;
+};
+
+struct pmbus_samples_reg {
+ int page;
+ struct pmbus_samples_attr *attr;
+ struct device_attribute dev_attr;
+};
+
+static struct pmbus_samples_attr pmbus_samples_registers[] = {
+ {
+ .reg = PMBUS_VIRT_SAMPLES,
+ .name = "samples",
+ }, {
+ .reg = PMBUS_VIRT_IN_SAMPLES,
+ .name = "in_samples",
+ }, {
+ .reg = PMBUS_VIRT_CURR_SAMPLES,
+ .name = "curr_samples",
+ }, {
+ .reg = PMBUS_VIRT_POWER_SAMPLES,
+ .name = "power_samples",
+ }, {
+ .reg = PMBUS_VIRT_TEMP_SAMPLES,
+ .name = "temp_samples",
+ }
+};
+
+#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr)
+
+static ssize_t pmbus_show_samples(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ 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);
+
+ 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 sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t pmbus_set_samples(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long 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);
+
+ if (kstrtol(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = _pmbus_write_word_data(client, reg->page, reg->attr->reg, val);
+ mutex_unlock(&data->update_lock);
+
+ return ret ? : count;
+}
+
+static int pmbus_add_samples_attr(struct pmbus_data *data, int page,
+ struct pmbus_samples_attr *attr)
+{
+ struct pmbus_samples_reg *reg;
+
+ reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL);
+ if (!reg)
+ return -ENOMEM;
+
+ reg->attr = attr;
+ reg->page = page;
+
+ pmbus_dev_attr_init(&reg->dev_attr, attr->name, 0644,
+ pmbus_show_samples, pmbus_set_samples);
+
+ return pmbus_add_attribute(data, &reg->dev_attr.attr);
+}
+
+static int pmbus_add_samples_attributes(struct i2c_client *client,
+ struct pmbus_data *data)
+{
+ const struct pmbus_driver_info *info = data->info;
+ int s;
+
+ if (!(info->func[0] & PMBUS_HAVE_SAMPLES))
+ return 0;
+
+ for (s = 0; s < ARRAY_SIZE(pmbus_samples_registers); s++) {
+ struct pmbus_samples_attr *attr;
+ int ret;
+
+ attr = &pmbus_samples_registers[s];
+ if (!pmbus_check_word_register(client, 0, attr->reg))
+ continue;
+
+ ret = pmbus_add_samples_attr(data, 0, attr);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pmbus_find_attributes(struct i2c_client *client,
+ struct pmbus_data *data)
+{
+ int ret;
+
+ /* Voltage sensors */
+ ret = pmbus_add_sensor_attrs(client, data, "in", voltage_attributes,
+ ARRAY_SIZE(voltage_attributes));
+ if (ret)
+ return ret;
+
+ /* Current sensors */
+ ret = pmbus_add_sensor_attrs(client, data, "curr", current_attributes,
+ ARRAY_SIZE(current_attributes));
+ if (ret)
+ return ret;
+
+ /* Power sensors */
+ ret = pmbus_add_sensor_attrs(client, data, "power", power_attributes,
+ ARRAY_SIZE(power_attributes));
+ if (ret)
+ return ret;
+
+ /* Temperature sensors */
+ ret = pmbus_add_sensor_attrs(client, data, "temp", temp_attributes,
+ ARRAY_SIZE(temp_attributes));
+ if (ret)
+ return ret;
+
+ /* Fans */
+ ret = pmbus_add_fan_attributes(client, data);
+ if (ret)
+ return ret;
+
+ ret = pmbus_add_samples_attributes(client, data);
+ return ret;
+}
+
+/*
+ * 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.
+ */
+static int pmbus_identify_common(struct i2c_client *client,
+ struct pmbus_data *data, int page)
+{
+ int vout_mode = -1;
+
+ if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE))
+ vout_mode = _pmbus_read_byte_data(client, page,
+ PMBUS_VOUT_MODE);
+ if (vout_mode >= 0 && vout_mode != 0xff) {
+ /*
+ * Not all chips support the VOUT_MODE command,
+ * so a failure to read it is not an error.
+ */
+ switch (vout_mode >> 5) {
+ case 0: /* linear mode */
+ if (data->info->format[PSC_VOLTAGE_OUT] != linear)
+ return -ENODEV;
+
+ data->exponent[page] = ((s8)(vout_mode << 3)) >> 3;
+ break;
+ case 1: /* VID mode */
+ if (data->info->format[PSC_VOLTAGE_OUT] != vid)
+ return -ENODEV;
+ break;
+ case 2: /* direct mode */
+ 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;
+ }
+ }
+
+ pmbus_clear_fault_page(client, page);
+ return 0;
+}
+
+static int pmbus_read_status_byte(struct i2c_client *client, int page)
+{
+ return _pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
+}
+
+static int pmbus_read_status_word(struct i2c_client *client, int page)
+{
+ 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,
+ struct pmbus_driver_info *info)
+{
+ struct device *dev = &client->dev;
+ 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.
+ */
+ data->read_status = pmbus_read_status_word;
+ ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
+ if (ret < 0 || ret == 0xffff) {
+ data->read_status = pmbus_read_status_byte;
+ ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
+ if (ret < 0 || ret == 0xff) {
+ dev_err(dev, "PMBus status register not found\n");
+ return -ENODEV;
+ }
+ } else {
+ data->has_status_word = true;
+ }
+
+ /*
+ * 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.
+ */
+ 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);
+ else
+ pmbus_clear_fault_page(client, -1);
+
+ if (info->identify) {
+ ret = (*info->identify)(client, info);
+ if (ret < 0) {
+ dev_err(dev, "Chip identification failed\n");
+ return ret;
+ }
+ }
+
+ if (info->pages <= 0 || info->pages > PMBUS_PAGES) {
+ dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages);
+ return -ENODEV;
+ }
+
+ for (page = 0; page < info->pages; page++) {
+ ret = pmbus_identify_common(client, data, page);
+ if (ret < 0) {
+ dev_err(dev, "Failed to identify chip capabilities\n");
+ 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;
+}
+
+#if IS_ENABLED(CONFIG_REGULATOR)
+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;
+
+ mutex_lock(&data->update_lock);
+ ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+ mutex_unlock(&data->update_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & PB_OPERATION_CONTROL_ON);
+}
+
+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;
+
+ 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)
+{
+ return _pmbus_regulator_on_off(rdev, 1);
+}
+
+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_NS_GPL(pmbus_regulator_ops, PMBUS);
+
+static int pmbus_regulator_register(struct pmbus_data *data)
+{
+ struct device *dev = data->dev;
+ const struct pmbus_driver_info *info = data->info;
+ const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
+ struct regulator_dev *rdev;
+ int i;
+
+ for (i = 0; i < info->num_regulators; i++) {
+ struct regulator_config config = { };
+
+ config.dev = dev;
+ config.driver_data = data;
+
+ if (pdata && pdata->reg_init_data)
+ config.init_data = &pdata->reg_init_data[i];
+
+ rdev = devm_regulator_register(dev, &info->reg_desc[i],
+ &config);
+ 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;
+}
+#else
+static int pmbus_regulator_register(struct pmbus_data *data)
+{
+ return 0;
+}
+#endif
+
+static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int pmbus_debugfs_get(void *data, u64 *val)
+{
+ int rc;
+ struct pmbus_debugfs_entry *entry = data;
+
+ rc = _pmbus_read_byte_data(entry->client, entry->page, entry->reg);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL,
+ "0x%02llx\n");
+
+static int pmbus_debugfs_get_status(void *data, u64 *val)
+{
+ int rc;
+ struct pmbus_debugfs_entry *entry = data;
+ struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
+
+ rc = pdata->read_status(entry->client, entry->page);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+
+ return 0;
+}
+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)
+{
+ int i, idx = 0;
+ char name[PMBUS_NAME_SIZE];
+ struct pmbus_debugfs_entry *entries;
+
+ if (!pmbus_debugfs_dir)
+ return -ENODEV;
+
+ /*
+ * Create the debugfs directory for this device. Use the hwmon device
+ * name to avoid conflicts (hwmon numbers are globally unique).
+ */
+ data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev),
+ pmbus_debugfs_dir);
+ if (IS_ERR_OR_NULL(data->debugfs)) {
+ data->debugfs = NULL;
+ return -ENODEV;
+ }
+
+ /*
+ * Allocate the max possible entries we need.
+ * 6 entries device-specific
+ * 10 entries page-specific
+ */
+ entries = devm_kcalloc(data->dev,
+ 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)) {
+ /* No need to set reg as we have special read op. */
+ entries[idx].client = client;
+ entries[idx].page = i;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops_status);
+ }
+
+ if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_VOUT;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_IOUT;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_INPUT;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_TEMPERATURE;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_CML;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_OTHER;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (pmbus_check_byte_register(client, i,
+ PMBUS_STATUS_MFR_SPECIFIC)) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_FAN_12;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
+ if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) {
+ entries[idx].client = client;
+ entries[idx].page = i;
+ entries[idx].reg = PMBUS_STATUS_FAN_34;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+ }
+
+ return devm_add_action_or_reset(data->dev,
+ pmbus_remove_debugfs, data->debugfs);
+}
+#else
+static int pmbus_init_debugfs(struct i2c_client *client,
+ struct pmbus_data *data)
+{
+ return 0;
+}
+#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
+
+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;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE
+ | I2C_FUNC_SMBUS_BYTE_DATA
+ | I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (info->groups)
+ while (info->groups[groups_num])
+ groups_num++;
+
+ data->groups = devm_kcalloc(dev, groups_num + 2, sizeof(void *),
+ GFP_KERNEL);
+ if (!data->groups)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+ data->dev = dev;
+
+ 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)
+ return ret;
+
+ ret = pmbus_find_attributes(client, data);
+ if (ret)
+ return ret;
+
+ /*
+ * If there are no attributes, something is wrong.
+ * Bail out instead of trying to register nothing.
+ */
+ if (!data->num_attributes) {
+ dev_err(dev, "No attributes found\n");
+ 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 = devm_hwmon_device_register_with_groups(dev,
+ name, data, data->groups);
+ if (IS_ERR(data->hwmon_dev)) {
+ dev_err(dev, "Failed to register hwmon device\n");
+ return PTR_ERR(data->hwmon_dev);
+ }
+
+ ret = pmbus_regulator_register(data);
+ if (ret)
+ return ret;
+
+ ret = pmbus_init_debugfs(client, data);
+ if (ret)
+ dev_warn(dev, "Failed to register debugfs\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS);
+
+struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ return data->debugfs;
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS);
+
+static int __init pmbus_core_init(void)
+{
+ pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL);
+ if (IS_ERR(pmbus_debugfs_dir))
+ pmbus_debugfs_dir = NULL;
+
+ return 0;
+}
+
+static void __exit pmbus_core_exit(void)
+{
+ debugfs_remove_recursive(pmbus_debugfs_dir);
+}
+
+module_init(pmbus_core_init);
+module_exit(pmbus_core_exit);
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c
new file mode 100644
index 000000000..52bee5de2
--- /dev/null
+++ b/drivers/hwmon/pmbus/pxe1610.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Infineon PXE1610
+ *
+ * Copyright (c) 2019 Facebook Inc
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define PXE1610_NUM_PAGES 3
+
+/* Identify chip parameters. */
+static int pxe1610_identify(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ int i;
+
+ for (i = 0; i < PXE1610_NUM_PAGES; i++) {
+ if (pmbus_check_byte_register(client, i, PMBUS_VOUT_MODE)) {
+ u8 vout_mode;
+ int ret;
+
+ /* Read the register with VOUT scaling value.*/
+ ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ vout_mode = ret & GENMASK(4, 0);
+
+ switch (vout_mode) {
+ case 1:
+ info->vrm_version[i] = vr12;
+ break;
+ case 2:
+ 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;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct pmbus_driver_info pxe1610_info = {
+ .pages = PXE1610_NUM_PAGES,
+ .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_VOUT | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
+ | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+ .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_VOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+ .func[2] = PMBUS_HAVE_VIN
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
+ | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+ .identify = pxe1610_identify,
+};
+
+static int pxe1610_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ u8 buf[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ if (!i2c_check_functionality(
+ client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA
+ | I2C_FUNC_SMBUS_READ_WORD_DATA
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+ return -ENODEV;
+
+ /*
+ * By default this device doesn't boot to page 0, so set page 0
+ * to access all pmbus registers.
+ */
+ i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+
+ /* Read Manufacturer id */
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
+ return ret;
+ }
+ if (ret != 2 || strncmp(buf, "XP", 2)) {
+ dev_err(&client->dev, "MFR_ID unrecognized\n");
+ return -ENODEV;
+ }
+
+ info = devm_kmemdup(&client->dev, &pxe1610_info,
+ sizeof(struct pmbus_driver_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id pxe1610_id[] = {
+ {"pxe1610", 0},
+ {"pxe1110", 0},
+ {"pxm1310", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, pxe1610_id);
+
+static struct i2c_driver pxe1610_driver = {
+ .driver = {
+ .name = "pxe1610",
+ },
+ .probe_new = pxe1610_probe,
+ .id_table = pxe1610_id,
+};
+
+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 000000000..fa298b426
--- /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 000000000..357b9d9d8
--- /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
new file mode 100644
index 000000000..31bb83c0e
--- /dev/null
+++ b/drivers/hwmon/pmbus/tps40422.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for TI TPS40422
+ *
+ * Copyright (c) 2014 Nokia Solutions and Networks.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+static struct pmbus_driver_info tps40422_info = {
+ .pages = 2,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP2
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP2
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+};
+
+static int tps40422_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &tps40422_info);
+}
+
+static const struct i2c_device_id tps40422_id[] = {
+ {"tps40422", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tps40422_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver tps40422_driver = {
+ .driver = {
+ .name = "tps40422",
+ },
+ .probe_new = tps40422_probe,
+ .id_table = tps40422_id,
+};
+
+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
new file mode 100644
index 000000000..81b9d8136
--- /dev/null
+++ b/drivers/hwmon/pmbus/tps53679.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Texas Instruments TPS53679
+ *
+ * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+ * 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 */
+#define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */
+#define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */
+#define TPS53679_PAGE_NUM 2
+
+#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 < info->pages; i++) {
+ /* Read the register with VOUT scaling value.*/
+ ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ vout_params = ret & GENMASK(4, 0);
+
+ switch (vout_params) {
+ case TPS53679_PROT_VR13_10MV:
+ case TPS53679_PROT_VR12_5_10MV:
+ info->vrm_version[i] = vr13;
+ break;
+ case TPS53679_PROT_VR13_5MV:
+ case TPS53679_PROT_VR12_5MV:
+ case TPS53679_PROT_IMVP8_5MV:
+ info->vrm_version[i] = vr12;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ 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 = {
+ .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_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_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT,
+ .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)
+{
+ struct device *dev = &client->dev;
+ struct pmbus_driver_info *info;
+ enum chips chip_id;
+
+ 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;
+
+ 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[] = {
+ {"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,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);
+
+static struct i2c_driver tps53679_driver = {
+ .driver = {
+ .name = "tps53679",
+ .of_match_table = of_match_ptr(tps53679_of_match),
+ },
+ .probe_new = tps53679_probe,
+ .id_table = tps53679_id,
+};
+
+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 000000000..435f94304
--- /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
new file mode 100644
index 000000000..3daaf2237
--- /dev/null
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -0,0 +1,707 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for UCD90xxx Sequencer and System Health
+ * Controller series
+ *
+ * Copyright (C) 2011 Ericsson AB.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/pmbus.h>
+#include <linux/gpio/driver.h>
+#include <linux/timekeeping.h>
+#include "pmbus.h"
+
+enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd90320, ucd9090,
+ ucd90910 };
+
+#define UCD9000_MONITOR_CONFIG 0xd5
+#define UCD9000_NUM_PAGES 0xd6
+#define UCD9000_FAN_CONFIG_INDEX 0xe7
+#define UCD9000_FAN_CONFIG 0xe8
+#define UCD9000_MFR_STATUS 0xf3
+#define UCD9000_GPIO_SELECT 0xfa
+#define UCD9000_GPIO_CONFIG 0xfb
+#define UCD9000_DEVICE_ID 0xfd
+
+/* GPIO CONFIG bits */
+#define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
+#define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
+#define UCD9000_GPIO_CONFIG_OUT_VALUE BIT(2)
+#define UCD9000_GPIO_CONFIG_STATUS BIT(3)
+#define UCD9000_GPIO_INPUT 0
+#define UCD9000_GPIO_OUTPUT 1
+
+#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07)
+#define UCD9000_MON_PAGE(x) ((x) & 0x1f)
+
+#define UCD9000_MON_VOLTAGE 1
+#define UCD9000_MON_TEMPERATURE 2
+#define UCD9000_MON_CURRENT 3
+#define UCD9000_MON_VOLTAGE_HW 4
+
+#define UCD9000_NUM_FAN 4
+
+#define UCD9000_GPIO_NAME_LEN 16
+#define UCD9090_NUM_GPIOS 23
+#define UCD901XX_NUM_GPIOS 26
+#define UCD90320_NUM_GPIOS 84
+#define UCD90910_NUM_GPIOS 26
+
+#define UCD9000_DEBUGFS_NAME_LEN 24
+#define UCD9000_GPI_COUNT 8
+#define UCD90320_GPI_COUNT 32
+
+struct ucd9000_data {
+ u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
+ struct pmbus_driver_info info;
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio;
+#endif
+ struct dentry *debugfs;
+ ktime_t write_time;
+};
+#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
+
+struct ucd9000_debugfs_entry {
+ struct i2c_client *client;
+ u8 index;
+};
+
+/*
+ * It has been observed that the UCD90320 randomly fails register access when
+ * doing another access right on the back of a register write. To mitigate this
+ * make sure that there is a minimum delay between a write access and the
+ * following access. The 250us is based on experimental data. At a delay of
+ * 200us the issue seems to go away. Add a bit of extra margin to allow for
+ * system to system differences.
+ */
+#define UCD90320_WAIT_DELAY_US 250
+
+static inline void ucd90320_wait(const struct ucd9000_data *data)
+{
+ s64 delta = ktime_us_delta(ktime_get(), data->write_time);
+
+ if (delta < UCD90320_WAIT_DELAY_US)
+ udelay(UCD90320_WAIT_DELAY_US - delta);
+}
+
+static int ucd90320_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 ucd9000_data *data = to_ucd9000_data(info);
+
+ if (reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+
+ ucd90320_wait(data);
+ return pmbus_read_word_data(client, page, phase, reg);
+}
+
+static int ucd90320_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ucd9000_data *data = to_ucd9000_data(info);
+
+ ucd90320_wait(data);
+ return pmbus_read_byte_data(client, page, reg);
+}
+
+static int ucd90320_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 ucd9000_data *data = to_ucd9000_data(info);
+ int ret;
+
+ ucd90320_wait(data);
+ ret = pmbus_write_word_data(client, page, reg, word);
+ data->write_time = ktime_get();
+
+ return ret;
+}
+
+static int ucd90320_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ucd9000_data *data = to_ucd9000_data(info);
+ int ret;
+
+ ucd90320_wait(data);
+ ret = pmbus_write_byte(client, page, value);
+ data->write_time = ktime_get();
+
+ return ret;
+}
+
+static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
+{
+ int fan_config = 0;
+ struct ucd9000_data *data
+ = to_ucd9000_data(pmbus_get_driver_info(client));
+
+ if (data->fan_data[fan][3] & 1)
+ fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */
+
+ /* Pulses/revolution */
+ fan_config |= (data->fan_data[fan][3] & 0x06) >> 1;
+
+ return fan_config;
+}
+
+static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret = 0;
+ int fan_config;
+
+ switch (reg) {
+ case PMBUS_FAN_CONFIG_12:
+ if (page > 0)
+ return -ENXIO;
+
+ ret = ucd9000_get_fan_config(client, 0);
+ if (ret < 0)
+ return ret;
+ fan_config = ret << 4;
+ ret = ucd9000_get_fan_config(client, 1);
+ if (ret < 0)
+ return ret;
+ fan_config |= ret;
+ ret = fan_config;
+ break;
+ case PMBUS_FAN_CONFIG_34:
+ if (page > 0)
+ return -ENXIO;
+
+ ret = ucd9000_get_fan_config(client, 2);
+ if (ret < 0)
+ return ret;
+ fan_config = ret << 4;
+ ret = ucd9000_get_fan_config(client, 3);
+ if (ret < 0)
+ return ret;
+ fan_config |= ret;
+ ret = fan_config;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id ucd9000_id[] = {
+ {"ucd9000", ucd9000},
+ {"ucd90120", ucd90120},
+ {"ucd90124", ucd90124},
+ {"ucd90160", ucd90160},
+ {"ucd90320", ucd90320},
+ {"ucd9090", ucd9090},
+ {"ucd90910", ucd90910},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ucd9000_id);
+
+static const struct of_device_id __maybe_unused ucd9000_of_match[] = {
+ {
+ .compatible = "ti,ucd9000",
+ .data = (void *)ucd9000
+ },
+ {
+ .compatible = "ti,ucd90120",
+ .data = (void *)ucd90120
+ },
+ {
+ .compatible = "ti,ucd90124",
+ .data = (void *)ucd90124
+ },
+ {
+ .compatible = "ti,ucd90160",
+ .data = (void *)ucd90160
+ },
+ {
+ .compatible = "ti,ucd90320",
+ .data = (void *)ucd90320
+ },
+ {
+ .compatible = "ti,ucd9090",
+ .data = (void *)ucd9090
+ },
+ {
+ .compatible = "ti,ucd90910",
+ .data = (void *)ucd90910
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ucd9000_of_match);
+
+#ifdef CONFIG_GPIOLIB
+static int ucd9000_gpio_read_config(struct i2c_client *client,
+ unsigned int offset)
+{
+ int ret;
+
+ /* No page set required */
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_SELECT, offset);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_read_byte_data(client, UCD9000_GPIO_CONFIG);
+}
+
+static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
+}
+
+static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
+ offset, ret);
+ return;
+ }
+
+ if (value) {
+ if (ret & UCD9000_GPIO_CONFIG_STATUS)
+ return;
+
+ ret |= UCD9000_GPIO_CONFIG_STATUS;
+ } else {
+ if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
+ return;
+
+ ret &= ~UCD9000_GPIO_CONFIG_STATUS;
+ }
+
+ ret |= UCD9000_GPIO_CONFIG_ENABLE;
+
+ /* Page set not required */
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+ offset, ret);
+ return;
+ }
+
+ ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, ret);
+ if (ret < 0)
+ dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
+ offset, ret);
+}
+
+static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0)
+ return ret;
+
+ return !(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE);
+}
+
+static int ucd9000_gpio_set_direction(struct gpio_chip *gc,
+ unsigned int offset, bool direction_out,
+ int requested_out)
+{
+ struct i2c_client *client = gpiochip_get_data(gc);
+ int ret, config, out_val;
+
+ ret = ucd9000_gpio_read_config(client, offset);
+ if (ret < 0)
+ return ret;
+
+ if (direction_out) {
+ out_val = requested_out ? UCD9000_GPIO_CONFIG_OUT_VALUE : 0;
+
+ if (ret & UCD9000_GPIO_CONFIG_OUT_ENABLE) {
+ if ((ret & UCD9000_GPIO_CONFIG_OUT_VALUE) == out_val)
+ return 0;
+ } else {
+ ret |= UCD9000_GPIO_CONFIG_OUT_ENABLE;
+ }
+
+ if (out_val)
+ ret |= UCD9000_GPIO_CONFIG_OUT_VALUE;
+ else
+ ret &= ~UCD9000_GPIO_CONFIG_OUT_VALUE;
+
+ } else {
+ if (!(ret & UCD9000_GPIO_CONFIG_OUT_ENABLE))
+ return 0;
+
+ ret &= ~UCD9000_GPIO_CONFIG_OUT_ENABLE;
+ }
+
+ ret |= UCD9000_GPIO_CONFIG_ENABLE;
+ config = ret;
+
+ /* Page set not required */
+ ret = i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, config);
+ if (ret < 0)
+ return ret;
+
+ config &= ~UCD9000_GPIO_CONFIG_ENABLE;
+
+ return i2c_smbus_write_byte_data(client, UCD9000_GPIO_CONFIG, config);
+}
+
+static int ucd9000_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return ucd9000_gpio_set_direction(gc, offset, UCD9000_GPIO_INPUT, 0);
+}
+
+static int ucd9000_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int val)
+{
+ return ucd9000_gpio_set_direction(gc, offset, UCD9000_GPIO_OUTPUT,
+ val);
+}
+
+static void ucd9000_probe_gpio(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+ int rc;
+
+ switch (mid->driver_data) {
+ case ucd9090:
+ data->gpio.ngpio = UCD9090_NUM_GPIOS;
+ break;
+ case ucd90120:
+ case ucd90124:
+ case ucd90160:
+ data->gpio.ngpio = UCD901XX_NUM_GPIOS;
+ break;
+ case ucd90320:
+ data->gpio.ngpio = UCD90320_NUM_GPIOS;
+ break;
+ case ucd90910:
+ data->gpio.ngpio = UCD90910_NUM_GPIOS;
+ break;
+ default:
+ return; /* GPIO support is optional. */
+ }
+
+ /*
+ * Pinmux support has not been added to the new gpio_chip.
+ * This support should be added when possible given the mux
+ * behavior of these IO devices.
+ */
+ data->gpio.label = client->name;
+ data->gpio.get_direction = ucd9000_gpio_get_direction;
+ data->gpio.direction_input = ucd9000_gpio_direction_input;
+ data->gpio.direction_output = ucd9000_gpio_direction_output;
+ data->gpio.get = ucd9000_gpio_get;
+ data->gpio.set = ucd9000_gpio_set;
+ data->gpio.can_sleep = true;
+ data->gpio.base = -1;
+ data->gpio.parent = &client->dev;
+
+ rc = devm_gpiochip_add_data(&client->dev, &data->gpio, client);
+ if (rc)
+ dev_warn(&client->dev, "Could not add gpiochip: %d\n", rc);
+}
+#else
+static void ucd9000_probe_gpio(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+}
+#endif /* CONFIG_GPIOLIB */
+
+#ifdef CONFIG_DEBUG_FS
+static int ucd9000_get_mfr_status(struct i2c_client *client, u8 *buffer)
+{
+ int ret = pmbus_set_page(client, 0, 0xff);
+
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_read_block_data(client, UCD9000_MFR_STATUS, buffer);
+}
+
+static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val)
+{
+ struct ucd9000_debugfs_entry *entry = data;
+ struct i2c_client *client = entry->client;
+ u8 buffer[I2C_SMBUS_BLOCK_MAX];
+ int ret, i;
+
+ ret = ucd9000_get_mfr_status(client, buffer);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * GPI fault bits are in sets of 8, two bytes from end of response.
+ */
+ i = ret - 3 - entry->index / 8;
+ if (i >= 0)
+ *val = !!(buffer[i] & BIT(entry->index % 8));
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucd9000_debugfs_mfr_status_bit,
+ ucd9000_debugfs_show_mfr_status_bit, NULL, "%1lld\n");
+
+static ssize_t ucd9000_debugfs_read_mfr_status(struct file *file,
+ char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct i2c_client *client = file->private_data;
+ u8 buffer[I2C_SMBUS_BLOCK_MAX];
+ char str[(I2C_SMBUS_BLOCK_MAX * 2) + 2];
+ char *res;
+ int rc;
+
+ rc = ucd9000_get_mfr_status(client, buffer);
+ if (rc < 0)
+ return rc;
+
+ res = bin2hex(str, buffer, min(rc, I2C_SMBUS_BLOCK_MAX));
+ *res++ = '\n';
+ *res = 0;
+
+ return simple_read_from_buffer(buf, count, ppos, str, res - str);
+}
+
+static const struct file_operations ucd9000_debugfs_show_mfr_status_fops = {
+ .llseek = noop_llseek,
+ .read = ucd9000_debugfs_read_mfr_status,
+ .open = simple_open,
+};
+
+static int ucd9000_init_debugfs(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+ struct dentry *debugfs;
+ struct ucd9000_debugfs_entry *entries;
+ int i, gpi_count;
+ char name[UCD9000_DEBUGFS_NAME_LEN];
+
+ debugfs = pmbus_get_debugfs_dir(client);
+ if (!debugfs)
+ return -ENOENT;
+
+ data->debugfs = debugfs_create_dir(client->name, debugfs);
+ if (!data->debugfs)
+ return -ENOENT;
+
+ /*
+ * Of the chips this driver supports, only the UCD9090, UCD90160,
+ * UCD90320, and UCD90910 report GPI faults in their MFR_STATUS
+ * register, so only create the GPI fault debugfs attributes for those
+ * chips.
+ */
+ if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
+ mid->driver_data == ucd90320 || mid->driver_data == ucd90910) {
+ gpi_count = mid->driver_data == ucd90320 ? UCD90320_GPI_COUNT
+ : UCD9000_GPI_COUNT;
+ entries = devm_kcalloc(&client->dev,
+ gpi_count, sizeof(*entries),
+ GFP_KERNEL);
+ if (!entries)
+ return -ENOMEM;
+
+ for (i = 0; i < gpi_count; i++) {
+ entries[i].client = client;
+ entries[i].index = i;
+ scnprintf(name, UCD9000_DEBUGFS_NAME_LEN,
+ "gpi%d_alarm", i + 1);
+ debugfs_create_file(name, 0444, data->debugfs,
+ &entries[i],
+ &ucd9000_debugfs_mfr_status_bit);
+ }
+ }
+
+ scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, "mfr_status");
+ debugfs_create_file(name, 0444, data->debugfs, client,
+ &ucd9000_debugfs_show_mfr_status_fops);
+
+ return 0;
+}
+#else
+static int ucd9000_init_debugfs(struct i2c_client *client,
+ const struct i2c_device_id *mid,
+ struct ucd9000_data *data)
+{
+ return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static int ucd9000_probe(struct i2c_client *client)
+{
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+ struct ucd9000_data *data;
+ struct pmbus_driver_info *info;
+ const struct i2c_device_id *mid;
+ enum chips chip;
+ int i, ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, UCD9000_DEVICE_ID,
+ block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read device ID\n");
+ return ret;
+ }
+ block_buffer[ret] = '\0';
+ dev_info(&client->dev, "Device ID %s\n", block_buffer);
+
+ for (mid = ucd9000_id; mid->name[0]; mid++) {
+ if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
+ break;
+ }
+ if (!mid->name[0]) {
+ dev_err(&client->dev, "Unsupported device\n");
+ return -ENODEV;
+ }
+
+ if (client->dev.of_node)
+ chip = (enum chips)of_device_get_match_data(&client->dev);
+ else
+ chip = mid->driver_data;
+
+ if (chip != ucd9000 && strcmp(client->name, mid->name) != 0)
+ dev_notice(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ client->name, mid->name);
+
+ data = devm_kzalloc(&client->dev, sizeof(struct ucd9000_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ info = &data->info;
+
+ ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to read number of active pages\n");
+ return ret;
+ }
+ info->pages = ret;
+ if (!info->pages) {
+ dev_err(&client->dev, "No pages configured\n");
+ return -ENODEV;
+ }
+
+ /* The internal temperature sensor is always active */
+ info->func[0] = PMBUS_HAVE_TEMP;
+
+ /* Everything else is configurable */
+ ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG,
+ block_buffer);
+ if (ret <= 0) {
+ dev_err(&client->dev, "Failed to read configuration data\n");
+ return -ENODEV;
+ }
+ for (i = 0; i < ret; i++) {
+ int page = UCD9000_MON_PAGE(block_buffer[i]);
+
+ if (page >= info->pages)
+ continue;
+
+ switch (UCD9000_MON_TYPE(block_buffer[i])) {
+ case UCD9000_MON_VOLTAGE:
+ case UCD9000_MON_VOLTAGE_HW:
+ info->func[page] |= PMBUS_HAVE_VOUT
+ | PMBUS_HAVE_STATUS_VOUT;
+ break;
+ case UCD9000_MON_TEMPERATURE:
+ info->func[page] |= PMBUS_HAVE_TEMP2
+ | PMBUS_HAVE_STATUS_TEMP;
+ break;
+ case UCD9000_MON_CURRENT:
+ info->func[page] |= PMBUS_HAVE_IOUT
+ | PMBUS_HAVE_STATUS_IOUT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Fan configuration */
+ if (mid->driver_data == ucd90124) {
+ for (i = 0; i < UCD9000_NUM_FAN; i++) {
+ i2c_smbus_write_byte_data(client,
+ UCD9000_FAN_CONFIG_INDEX, i);
+ ret = i2c_smbus_read_block_data(client,
+ UCD9000_FAN_CONFIG,
+ data->fan_data[i]);
+ if (ret < 0)
+ return ret;
+ }
+ i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0);
+
+ info->read_byte_data = ucd9000_read_byte_data;
+ info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12
+ | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
+ } else if (mid->driver_data == ucd90320) {
+ info->read_byte_data = ucd90320_read_byte_data;
+ info->read_word_data = ucd90320_read_word_data;
+ info->write_byte = ucd90320_write_byte;
+ info->write_word_data = ucd90320_write_word_data;
+ }
+
+ ucd9000_probe_gpio(client, mid, data);
+
+ ret = pmbus_do_probe(client, info);
+ if (ret)
+ return ret;
+
+ ret = ucd9000_init_debugfs(client, mid, data);
+ if (ret)
+ dev_warn(&client->dev, "Failed to register debugfs: %d\n",
+ ret);
+
+ return 0;
+}
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ucd9000_driver = {
+ .driver = {
+ .name = "ucd9000",
+ .of_match_table = of_match_ptr(ucd9000_of_match),
+ },
+ .probe_new = ucd9000_probe,
+ .id_table = ucd9000_id,
+};
+
+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
new file mode 100644
index 000000000..3ad375a76
--- /dev/null
+++ b/drivers/hwmon/pmbus/ucd9200.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for ucd9200 series Digital PWM System Controllers
+ *
+ * Copyright (C) 2011 Ericsson AB.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+#define UCD9200_PHASE_INFO 0xd2
+#define UCD9200_DEVICE_ID 0xfd
+
+enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246,
+ ucd9248 };
+
+static const struct i2c_device_id ucd9200_id[] = {
+ {"ucd9200", ucd9200},
+ {"ucd9220", ucd9220},
+ {"ucd9222", ucd9222},
+ {"ucd9224", ucd9224},
+ {"ucd9240", ucd9240},
+ {"ucd9244", ucd9244},
+ {"ucd9246", ucd9246},
+ {"ucd9248", ucd9248},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ucd9200_id);
+
+static const struct of_device_id __maybe_unused ucd9200_of_match[] = {
+ {
+ .compatible = "ti,cd9200",
+ .data = (void *)ucd9200
+ },
+ {
+ .compatible = "ti,cd9220",
+ .data = (void *)ucd9220
+ },
+ {
+ .compatible = "ti,cd9222",
+ .data = (void *)ucd9222
+ },
+ {
+ .compatible = "ti,cd9224",
+ .data = (void *)ucd9224
+ },
+ {
+ .compatible = "ti,cd9240",
+ .data = (void *)ucd9240
+ },
+ {
+ .compatible = "ti,cd9244",
+ .data = (void *)ucd9244
+ },
+ {
+ .compatible = "ti,cd9246",
+ .data = (void *)ucd9246
+ },
+ {
+ .compatible = "ti,cd9248",
+ .data = (void *)ucd9248
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ucd9200_of_match);
+
+static int ucd9200_probe(struct i2c_client *client)
+{
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+ struct pmbus_driver_info *info;
+ const struct i2c_device_id *mid;
+ enum chips chip;
+ int i, j, ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, UCD9200_DEVICE_ID,
+ block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read device ID\n");
+ return ret;
+ }
+ block_buffer[ret] = '\0';
+ dev_info(&client->dev, "Device ID %s\n", block_buffer);
+
+ for (mid = ucd9200_id; mid->name[0]; mid++) {
+ if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
+ break;
+ }
+ if (!mid->name[0]) {
+ dev_err(&client->dev, "Unsupported device\n");
+ return -ENODEV;
+ }
+
+ if (client->dev.of_node)
+ chip = (enum chips)of_device_get_match_data(&client->dev);
+ else
+ chip = mid->driver_data;
+
+ if (chip != ucd9200 && strcmp(client->name, mid->name) != 0)
+ dev_notice(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ client->name, mid->name);
+
+ info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ ret = i2c_smbus_read_block_data(client, UCD9200_PHASE_INFO,
+ block_buffer);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read phase information\n");
+ return ret;
+ }
+
+ /*
+ * Calculate number of configured pages (rails) from PHASE_INFO
+ * register.
+ * Rails have to be sequential, so we can abort after finding
+ * the first unconfigured rail.
+ */
+ info->pages = 0;
+ for (i = 0; i < ret; i++) {
+ if (!block_buffer[i])
+ break;
+ info->pages++;
+ }
+ if (!info->pages) {
+ dev_err(&client->dev, "No rails configured\n");
+ return -ENODEV;
+ }
+ dev_info(&client->dev, "%d rails configured\n", info->pages);
+
+ /*
+ * Set PHASE registers on all pages to 0xff to ensure that phase
+ * specific commands will apply to all phases of a given page (rail).
+ * 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 phases of the rail.
+ */
+ for (i = 0; i < info->pages; i++) {
+ /*
+ * Setting PAGE & PHASE fails once in a while for no obvious
+ * reason, so we need to retry a couple of times.
+ */
+ for (j = 0; j < 3; j++) {
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+ if (ret < 0)
+ continue;
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
+ 0xff);
+ if (ret < 0)
+ continue;
+ break;
+ }
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Failed to initialize PHASE registers\n");
+ return ret;
+ }
+ }
+ if (info->pages > 1)
+ i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP |
+ PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+
+ for (i = 1; i < info->pages; i++)
+ info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT |
+ PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
+
+ /* ucd9240 supports a single fan */
+ if (mid->driver_data == ucd9240)
+ info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12;
+
+ return pmbus_do_probe(client, info);
+}
+
+/* This is the driver that will be inserted */
+static struct i2c_driver ucd9200_driver = {
+ .driver = {
+ .name = "ucd9200",
+ .of_match_table = of_match_ptr(ucd9200_of_match),
+ },
+ .probe_new = ucd9200_probe,
+ .id_table = ucd9200_id,
+};
+
+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
new file mode 100644
index 000000000..32bc7736d
--- /dev/null
+++ b/drivers/hwmon/pmbus/xdpe12284.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers
+ *
+ * Copyright (c) 2020 Mellanox 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 <linux/regulator/driver.h>
+
+#include "pmbus.h"
+
+#define XDPE122_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */
+#define XDPE122_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */
+#define XDPE122_PROT_IMVP9_10MV 0x03 /* IMVP9 mode, 10-mV DAC */
+#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 phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ long val;
+ s16 exponent;
+ s32 mantissa;
+ int ret;
+
+ switch (reg) {
+ 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;
+
+ /* Convert register value to LINEAR11 data. */
+ exponent = ((s16)ret) >> 11;
+ mantissa = ((s16)((ret & GENMASK(10, 0)) << 5)) >> 5;
+ val = mantissa * 1000L;
+ if (exponent >= 0)
+ val <<= exponent;
+ else
+ val >>= -exponent;
+
+ /* Convert data to VID register. */
+ switch (info->vrm_version[page]) {
+ case vr13:
+ if (val >= 500)
+ return 1 + DIV_ROUND_CLOSEST(val - 500, 10);
+ return 0;
+ case vr12:
+ if (val >= 250)
+ return 1 + DIV_ROUND_CLOSEST(val - 250, 5);
+ return 0;
+ case imvp9:
+ if (val >= 200)
+ return 1 + DIV_ROUND_CLOSEST(val - 200, 10);
+ return 0;
+ case amd625mv:
+ if (val >= 200 && val <= 1550)
+ return DIV_ROUND_CLOSEST((1550 - val) * 100,
+ 625);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int xdpe122_identify(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ u8 vout_params;
+ 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.*/
+ ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ vout_params = ret & GENMASK(4, 0);
+
+ switch (vout_params) {
+ case XDPE122_PROT_VR12_5_10MV:
+ info->vrm_version[i] = vr13;
+ break;
+ case XDPE122_PROT_VR12_5MV:
+ info->vrm_version[i] = vr12;
+ break;
+ case XDPE122_PROT_IMVP9_10MV:
+ info->vrm_version[i] = imvp9;
+ break;
+ case XDPE122_AMD_625MV:
+ info->vrm_version[i] = amd625mv;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ 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_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_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_TEMP | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
+ .identify = xdpe122_identify,
+#if IS_ENABLED(CONFIG_SENSORS_XDPE122_REGULATOR)
+ .num_regulators = 2,
+ .reg_desc = xdpe122_reg_desc,
+#endif
+};
+
+static int xdpe122_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+
+ info = devm_kmemdup(&client->dev, &xdpe122_info, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id xdpe122_id[] = {
+ {"xdpe11280", 0},
+ {"xdpe12254", 0},
+ {"xdpe12284", 0},
+ {}
+};
+
+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"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, xdpe122_of_match);
+
+static struct i2c_driver xdpe122_driver = {
+ .driver = {
+ .name = "xdpe12284",
+ .of_match_table = of_match_ptr(xdpe122_of_match),
+ },
+ .probe_new = xdpe122_probe,
+ .id_table = xdpe122_id,
+};
+
+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 000000000..b8a36ef73
--- /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
new file mode 100644
index 000000000..e9df0c56d
--- /dev/null
+++ b/drivers/hwmon/pmbus/zl6100.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for ZL6100 and compatibles
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2012 Guenter Roeck
+ */
+
+#include <linux/bitops.h>
+#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 "pmbus.h"
+
+enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
+ zl8802, zl9101, zl9117, zls1003, zls4009 };
+
+struct zl6100_data {
+ int id;
+ ktime_t access; /* chip access time */
+ int delay; /* Delay between chip accesses in uS */
+ struct pmbus_driver_info info;
+};
+
+#define to_zl6100_data(x) container_of(x, struct zl6100_data, info)
+
+#define ZL6100_MFR_CONFIG 0xd0
+#define ZL6100_DEVICE_ID 0xe4
+
+#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
+
+#define VMON_UV_WARNING BIT(5)
+#define VMON_OV_WARNING BIT(4)
+#define VMON_UV_FAULT BIT(1)
+#define VMON_OV_FAULT BIT(0)
+
+#define ZL6100_WAIT_TIME 1000 /* uS */
+
+static ushort delay = ZL6100_WAIT_TIME;
+module_param(delay, ushort, 0644);
+MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
+
+/* Convert linear sensor value to milli-units */
+static long zl6100_l2d(s16 l)
+{
+ s16 exponent;
+ s32 mantissa;
+ long val;
+
+ exponent = l >> 11;
+ mantissa = ((s16)((l & 0x7ff) << 5)) >> 5;
+
+ val = mantissa;
+
+ /* scale result to milli-units */
+ val = val * 1000L;
+
+ if (exponent >= 0)
+ val <<= exponent;
+ else
+ val >>= -exponent;
+
+ return val;
+}
+
+#define MAX_MANTISSA (1023 * 1000)
+#define MIN_MANTISSA (511 * 1000)
+
+static u16 zl6100_d2l(long val)
+{
+ s16 exponent = 0, mantissa;
+ bool negative = false;
+
+ /* simple case */
+ if (val == 0)
+ return 0;
+
+ if (val < 0) {
+ negative = true;
+ val = -val;
+ }
+
+ /* Reduce large mantissa until it fits into 10 bit */
+ while (val >= MAX_MANTISSA && exponent < 15) {
+ exponent++;
+ val >>= 1;
+ }
+ /* Increase small mantissa to improve precision */
+ while (val < MIN_MANTISSA && exponent > -15) {
+ exponent--;
+ val <<= 1;
+ }
+
+ /* Convert mantissa from milli-units to units */
+ mantissa = DIV_ROUND_CLOSEST(val, 1000);
+
+ /* Ensure that resulting number is within range */
+ if (mantissa > 0x3ff)
+ mantissa = 0x3ff;
+
+ /* restore sign */
+ if (negative)
+ mantissa = -mantissa;
+
+ /* Convert to 5 bit exponent, 11 bit mantissa */
+ return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
+}
+
+/* Some chips need a delay between accesses */
+static inline void zl6100_wait(const struct zl6100_data *data)
+{
+ if (data->delay) {
+ s64 delta = ktime_us_delta(ktime_get(), data->access);
+ if (delta < data->delay)
+ udelay(data->delay - delta);
+ }
+}
+
+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 >= info->pages)
+ return -ENXIO;
+
+ if (data->id == zl2005) {
+ /*
+ * Limit register detection is not reliable on ZL2005.
+ * Make sure registers are not erroneously detected.
+ */
+ switch (reg) {
+ case PMBUS_VOUT_OV_WARN_LIMIT:
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ return -ENXIO;
+ }
+ }
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VMON:
+ vreg = MFR_READ_VMON;
+ break;
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:
+ vreg = MFR_VMON_OV_FAULT_LIMIT;
+ break;
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:
+ vreg = MFR_VMON_UV_FAULT_LIMIT;
+ break;
+ default:
+ if (reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+ vreg = reg;
+ break;
+ }
+
+ zl6100_wait(data);
+ ret = pmbus_read_word_data(client, page, phase, vreg);
+ data->access = ktime_get();
+ if (ret < 0)
+ return ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10));
+ break;
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10));
+ break;
+ }
+
+ return ret;
+}
+
+static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct zl6100_data *data = to_zl6100_data(info);
+ int ret, status;
+
+ if (page >= info->pages)
+ return -ENXIO;
+
+ zl6100_wait(data);
+
+ switch (reg) {
+ case PMBUS_VIRT_STATUS_VMON:
+ ret = pmbus_read_byte_data(client, 0,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (ret < 0)
+ break;
+
+ status = 0;
+ if (ret & VMON_UV_WARNING)
+ status |= PB_VOLTAGE_UV_WARNING;
+ if (ret & VMON_OV_WARNING)
+ status |= PB_VOLTAGE_OV_WARNING;
+ if (ret & VMON_UV_FAULT)
+ status |= PB_VOLTAGE_UV_FAULT;
+ if (ret & VMON_OV_FAULT)
+ status |= PB_VOLTAGE_OV_FAULT;
+ ret = status;
+ break;
+ default:
+ ret = pmbus_read_byte_data(client, page, reg);
+ break;
+ }
+ data->access = ktime_get();
+
+ return ret;
+}
+
+static int zl6100_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 zl6100_data *data = to_zl6100_data(info);
+ int ret, vreg;
+
+ if (page >= info->pages)
+ return -ENXIO;
+
+ switch (reg) {
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9));
+ vreg = MFR_VMON_OV_FAULT_LIMIT;
+ pmbus_clear_cache(client);
+ break;
+ case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:
+ vreg = MFR_VMON_OV_FAULT_LIMIT;
+ pmbus_clear_cache(client);
+ break;
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11));
+ vreg = MFR_VMON_UV_FAULT_LIMIT;
+ pmbus_clear_cache(client);
+ break;
+ case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:
+ vreg = MFR_VMON_UV_FAULT_LIMIT;
+ pmbus_clear_cache(client);
+ break;
+ default:
+ if (reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+ vreg = reg;
+ }
+
+ zl6100_wait(data);
+ ret = pmbus_write_word_data(client, page, vreg, word);
+ data->access = ktime_get();
+
+ return ret;
+}
+
+static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct zl6100_data *data = to_zl6100_data(info);
+ int ret;
+
+ if (page >= info->pages)
+ return -ENXIO;
+
+ zl6100_wait(data);
+ ret = pmbus_write_byte(client, page, value);
+ data->access = ktime_get();
+
+ return ret;
+}
+
+static const struct i2c_device_id zl6100_id[] = {
+ {"bmr450", zl2005},
+ {"bmr451", zl2005},
+ {"bmr462", zl2008},
+ {"bmr463", zl2008},
+ {"bmr464", zl2008},
+ {"bmr465", zls4009},
+ {"bmr466", zls1003},
+ {"bmr467", zls4009},
+ {"bmr469", zl8802},
+ {"zl2004", zl2004},
+ {"zl2005", zl2005},
+ {"zl2006", zl2006},
+ {"zl2008", zl2008},
+ {"zl2105", zl2105},
+ {"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)
+{
+ int ret, i;
+ struct zl6100_data *data;
+ struct pmbus_driver_info *info;
+ u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
+ const struct i2c_device_id *mid;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,
+ device_id);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to read device ID\n");
+ return ret;
+ }
+ device_id[ret] = '\0';
+ dev_info(&client->dev, "Device ID %s\n", device_id);
+
+ mid = NULL;
+ for (mid = zl6100_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) != 0)
+ dev_notice(&client->dev,
+ "Device mismatch: Configured %s, detected %s\n",
+ client->name, mid->name);
+
+ data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->id = mid->driver_data;
+
+ /*
+ * According to information from the chip vendor, all currently
+ * supported chips are known to require a wait time between I2C
+ * accesses.
+ */
+ data->delay = delay;
+
+ /*
+ * Since there was a direct I2C device access above, wait before
+ * accessing the chip again.
+ */
+ data->access = ktime_get();
+ zl6100_wait(data);
+
+ info = &data->info;
+
+ info->pages = 1;
+ info->func[0] = PMBUS_HAVE_VIN | 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;
+
+ /*
+ * 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 == zl8802 || data->id == zl9101 ||
+ data->id == zl9117 || data->id == zls4009)
+ info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
+
+ /*
+ * 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;
+
+ 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);
+
+ info->read_word_data = zl6100_read_word_data;
+ info->read_byte_data = zl6100_read_byte_data;
+ info->write_word_data = zl6100_write_word_data;
+ info->write_byte = zl6100_write_byte;
+
+ return pmbus_do_probe(client, info);
+}
+
+static struct i2c_driver zl6100_driver = {
+ .driver = {
+ .name = "zl6100",
+ },
+ .probe_new = zl6100_probe,
+ .id_table = zl6100_id,
+};
+
+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
new file mode 100644
index 000000000..f77dc6db3
--- /dev/null
+++ b/drivers/hwmon/powr1220.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * powr1220.c - Driver for the Lattice POWR1220 programmable power supply
+ * and monitor. Users can read all ADC inputs along with their labels
+ * using the sysfs nodes.
+ *
+ * Copyright (c) 2014 Echo360 https://www.echo360.com
+ * Scott Kanowitz <skanowitz@echo360.com> <scott.kanowitz@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#define ADC_STEP_MV 2
+#define ADC_MAX_LOW_MEASUREMENT_MV 2000
+
+enum powr1xxx_chips { powr1014, powr1220 };
+
+enum powr1220_regs {
+ VMON_STATUS0,
+ VMON_STATUS1,
+ VMON_STATUS2,
+ OUTPUT_STATUS0,
+ OUTPUT_STATUS1,
+ OUTPUT_STATUS2,
+ INPUT_STATUS,
+ ADC_VALUE_LOW,
+ ADC_VALUE_HIGH,
+ ADC_MUX,
+ UES_BYTE0,
+ UES_BYTE1,
+ UES_BYTE2,
+ UES_BYTE3,
+ GP_OUTPUT1,
+ GP_OUTPUT2,
+ GP_OUTPUT3,
+ INPUT_VALUE,
+ RESET,
+ TRIM1_TRIM,
+ TRIM2_TRIM,
+ TRIM3_TRIM,
+ TRIM4_TRIM,
+ TRIM5_TRIM,
+ TRIM6_TRIM,
+ TRIM7_TRIM,
+ TRIM8_TRIM,
+ MAX_POWR1220_REGS
+};
+
+enum powr1220_adc_values {
+ VMON1,
+ VMON2,
+ VMON3,
+ VMON4,
+ VMON5,
+ VMON6,
+ VMON7,
+ VMON8,
+ VMON9,
+ VMON10,
+ VMON11,
+ VMON12,
+ VCCA,
+ VCCINP,
+ MAX_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];
+
+ /* values */
+ int adc_maxes[MAX_POWR1220_ADC_VALUES];
+ int adc_values[MAX_POWR1220_ADC_VALUES];
+};
+
+static const char * const input_names[] = {
+ [VMON1] = "vmon1",
+ [VMON2] = "vmon2",
+ [VMON3] = "vmon3",
+ [VMON4] = "vmon4",
+ [VMON5] = "vmon5",
+ [VMON6] = "vmon6",
+ [VMON7] = "vmon7",
+ [VMON8] = "vmon8",
+ [VMON9] = "vmon9",
+ [VMON10] = "vmon10",
+ [VMON11] = "vmon11",
+ [VMON12] = "vmon12",
+ [VCCA] = "vcca",
+ [VCCINP] = "vccinp",
+};
+
+/* Reads the specified ADC channel */
+static int powr1220_read_adc(struct device *dev, int ch_num)
+{
+ struct powr1220_data *data = dev_get_drvdata(dev);
+ int reading;
+ int result;
+ int adc_range = 0;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->adc_last_updated[ch_num] + HZ) ||
+ !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
+ * for. We dynamically set the attenuator depending on the
+ * max reading.
+ */
+ if (data->adc_maxes[ch_num] > ADC_MAX_LOW_MEASUREMENT_MV ||
+ 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);
+ if (result)
+ goto exit;
+
+ /*
+ * wait at least Tconvert time (200 us) for the
+ * conversion to complete
+ */
+ udelay(200);
+
+ /* get the ADC reading */
+ result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_LOW);
+ if (result < 0)
+ goto exit;
+
+ reading = result >> 4;
+
+ /* get the upper half of the reading */
+ result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_HIGH);
+ if (result < 0)
+ goto exit;
+
+ reading |= result << 4;
+
+ /* now convert the reading to a voltage */
+ reading *= ADC_STEP_MV;
+ data->adc_values[ch_num] = reading;
+ data->adc_valid[ch_num] = true;
+ data->adc_last_updated[ch_num] = jiffies;
+ result = reading;
+
+ if (reading > data->adc_maxes[ch_num])
+ data->adc_maxes[ch_num] = reading;
+ } else {
+ result = data->adc_values[ch_num];
+ }
+
+exit:
+ mutex_unlock(&data->update_lock);
+
+ return result;
+}
+
+static umode_t
+powr1220_is_visible(const void *data, enum hwmon_sensor_types type, u32
+ attr, int channel)
+{
+ 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 0;
+}
+
+static int
+powr1220_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ 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 -EOPNOTSUPP;
+}
+
+static int
+powr1220_read(struct device *dev, enum hwmon_sensor_types type, u32
+ attr, int channel, long *val)
+{
+ 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 0;
+}
+
+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
+};
+
+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 const struct i2c_device_id powr1220_ids[];
+
+static int powr1220_probe(struct i2c_client *client)
+{
+ struct powr1220_data *data;
+ struct device *hwmon_dev;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ 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_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[] = {
+ { "powr1014", powr1014, },
+ { "powr1220", powr1220, },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, powr1220_ids);
+
+static struct i2c_driver powr1220_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "powr1220",
+ },
+ .probe_new = powr1220_probe,
+ .id_table = powr1220_ids,
+};
+
+module_i2c_driver(powr1220_driver);
+
+MODULE_AUTHOR("Scott Kanowitz");
+MODULE_DESCRIPTION("POWR1220 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
new file mode 100644
index 000000000..83a347ca3
--- /dev/null
+++ b/drivers/hwmon/pwm-fan.c
@@ -0,0 +1,682 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pwm-fan.c - Hwmon driver for fans connected to PWM lines.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Author: Kamil Debski <k.debski@samsung.com>
+ */
+
+#include <linux/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include <linux/timer.h>
+
+#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 tach_count;
+ struct pwm_fan_tach *tachs;
+ ktime_t sample_start;
+ struct timer_list rpm_timer;
+
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ 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_tach *tach = dev_id;
+
+ atomic_inc(&tach->pulses);
+
+ return IRQ_HANDLED;
+}
+
+static void sample_timer(struct timer_list *t)
+{
+ struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer);
+ unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
+ int i;
+
+ 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();
+ }
+
+ 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;
+
+ if (pwm > 0) {
+ if (ctx->enable_mode == pwm_off_reg_off)
+ /* pwm-fan hard disabled */
+ return 0;
+
+ 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;
+
+ 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;
+}
+
+static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+ int i;
+
+ for (i = 0; i < ctx->pwm_fan_max_state; ++i)
+ if (pwm < ctx->pwm_fan_cooling_levels[i + 1])
+ break;
+
+ ctx->pwm_fan_state = i;
+}
+
+static int pwm_fan_update_enable(struct pwm_fan_ctx *ctx, long val)
+{
+ int ret = 0;
+ int old_val;
+
+ mutex_lock(&ctx->lock);
+
+ if (ctx->enable_mode == val)
+ goto out;
+
+ old_val = ctx->enable_mode;
+ ctx->enable_mode = val;
+
+ 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 ret;
+}
+
+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;
+
+ 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);
+
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+
+ 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;
+
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+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;
+
+ 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 */
+static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_max_state;
+
+ return 0;
+}
+
+static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+
+ if (!ctx)
+ return -EINVAL;
+
+ *state = ctx->pwm_fan_state;
+
+ return 0;
+}
+
+static int
+pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+ struct pwm_fan_ctx *ctx = cdev->devdata;
+ int ret;
+
+ if (!ctx || (state > ctx->pwm_fan_max_state))
+ return -EINVAL;
+
+ if (state == ctx->pwm_fan_state)
+ return 0;
+
+ ret = set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+ if (ret) {
+ dev_err(&cdev->device, "Cannot set pwm!\n");
+ return ret;
+ }
+
+ ctx->pwm_fan_state = state;
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
+ .get_max_state = pwm_fan_get_max_state,
+ .get_cur_state = pwm_fan_get_cur_state,
+ .set_cur_state = pwm_fan_set_cur_state,
+};
+
+static int pwm_fan_of_get_cooling_data(struct device *dev,
+ struct pwm_fan_ctx *ctx)
+{
+ struct device_node *np = dev->of_node;
+ int num, i, ret;
+
+ if (!of_find_property(np, "cooling-levels", NULL))
+ return 0;
+
+ ret = of_property_count_u32_elems(np, "cooling-levels");
+ if (ret <= 0) {
+ dev_err(dev, "Wrong data!\n");
+ return ret ? : -EINVAL;
+ }
+
+ num = ret;
+ ctx->pwm_fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32),
+ GFP_KERNEL);
+ if (!ctx->pwm_fan_cooling_levels)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(np, "cooling-levels",
+ ctx->pwm_fan_cooling_levels, num);
+ if (ret) {
+ dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) {
+ dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
+ ctx->pwm_fan_cooling_levels[i], MAX_PWM);
+ return -EINVAL;
+ }
+ }
+
+ ctx->pwm_fan_max_state = num - 1;
+
+ return 0;
+}
+
+static void pwm_fan_cleanup(void *__ctx)
+{
+ struct pwm_fan_ctx *ctx = __ctx;
+
+ 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)
+{
+ struct thermal_cooling_device *cdev;
+ struct device *dev = &pdev->dev;
+ struct pwm_fan_ctx *ctx;
+ struct device *hwmon;
+ int ret;
+ 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)
+ return -ENOMEM;
+
+ mutex_init(&ctx->lock);
+
+ 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->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;
+ }
+
+ 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;
+ }
+
+ ctx->enable_mode = pwm_disable_reg_enable;
+
+ /*
+ * 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_cleanup, ctx);
+ if (ret)
+ return ret;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+
+ ret = pwm_fan_of_get_cooling_data(dev, ctx);
+ if (ret)
+ return ret;
+
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+ cdev = devm_thermal_of_cooling_device_register(dev,
+ dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops);
+ if (IS_ERR(cdev)) {
+ ret = PTR_ERR(cdev);
+ dev_err(dev,
+ "Failed to register pwm-fan as cooling device: %d\n",
+ ret);
+ return ret;
+ }
+ ctx->cdev = cdev;
+ }
+
+ return 0;
+}
+
+static void pwm_fan_shutdown(struct platform_device *pdev)
+{
+ struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
+
+ pwm_fan_cleanup(ctx);
+}
+
+static int pwm_fan_suspend(struct device *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);
+
+ return set_pwm(ctx, ctx->pwm_value);
+}
+
+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", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_pwm_fan_match);
+
+static struct platform_driver pwm_fan_driver = {
+ .probe = pwm_fan_probe,
+ .shutdown = pwm_fan_shutdown,
+ .driver = {
+ .name = "pwm-fan",
+ .pm = pm_sleep_ptr(&pwm_fan_pm),
+ .of_match_table = of_pwm_fan_match,
+ },
+};
+
+module_platform_driver(pwm_fan_driver);
+
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_ALIAS("platform:pwm-fan");
+MODULE_DESCRIPTION("PWM FAN driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c
new file mode 100644
index 000000000..1650d3b4c
--- /dev/null
+++ b/drivers/hwmon/raspberrypi-hwmon.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Raspberry Pi voltage sensor driver
+ *
+ * Based on firmware/raspberrypi.c by Noralf Trønnes
+ *
+ * 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>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define UNDERVOLTAGE_STICKY_BIT BIT(16)
+
+struct rpi_hwmon_data {
+ struct device *hwmon_dev;
+ struct rpi_firmware *fw;
+ u32 last_throttled;
+ struct delayed_work get_values_poll_work;
+};
+
+static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
+{
+ u32 new_uv, old_uv, value;
+ int ret;
+
+ /* Request firmware to clear sticky bits */
+ value = 0xffff;
+
+ ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
+ &value, sizeof(value));
+ if (ret) {
+ dev_err_once(data->hwmon_dev, "Failed to get throttled (%d)\n",
+ ret);
+ return;
+ }
+
+ new_uv = value & UNDERVOLTAGE_STICKY_BIT;
+ old_uv = data->last_throttled & UNDERVOLTAGE_STICKY_BIT;
+ data->last_throttled = value;
+
+ if (new_uv == old_uv)
+ return;
+
+ if (new_uv)
+ dev_crit(data->hwmon_dev, "Undervoltage detected!\n");
+ else
+ dev_info(data->hwmon_dev, "Voltage normalised\n");
+
+ hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0);
+}
+
+static void get_values_poll(struct work_struct *work)
+{
+ struct rpi_hwmon_data *data;
+
+ data = container_of(work, struct rpi_hwmon_data,
+ get_values_poll_work.work);
+
+ rpi_firmware_get_throttled(data);
+
+ /*
+ * We can't run faster than the sticky shift (100ms) since we get
+ * flipping in the sticky bits that are cleared.
+ */
+ schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
+}
+
+static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct rpi_hwmon_data *data = dev_get_drvdata(dev);
+
+ *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
+ return 0;
+}
+
+static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ return 0444;
+}
+
+static const struct hwmon_channel_info *rpi_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_LCRIT_ALARM),
+ NULL
+};
+
+static const struct hwmon_ops rpi_hwmon_ops = {
+ .is_visible = rpi_is_visible,
+ .read = rpi_read,
+};
+
+static const struct hwmon_chip_info rpi_chip_info = {
+ .ops = &rpi_hwmon_ops,
+ .info = rpi_info,
+};
+
+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)
+ return -ENOMEM;
+
+ /* Parent driver assure that firmware is correct */
+ data->fw = dev_get_drvdata(dev->parent);
+
+ data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
+ data,
+ &rpi_chip_info,
+ NULL);
+ if (IS_ERR(data->hwmon_dev))
+ return PTR_ERR(data->hwmon_dev);
+
+ ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work,
+ get_values_poll);
+ if (ret)
+ return ret;
+ platform_set_drvdata(pdev, data);
+
+ schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
+
+ return 0;
+}
+
+static struct platform_driver rpi_hwmon_driver = {
+ .probe = rpi_hwmon_probe,
+ .driver = {
+ .name = "raspberrypi-hwmon",
+ },
+};
+module_platform_driver(rpi_hwmon_driver);
+
+MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
+MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:raspberrypi-hwmon");
diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c
new file mode 100644
index 000000000..70ae665db
--- /dev/null
+++ b/drivers/hwmon/s3c-hwmon.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* linux/drivers/hwmon/s3c-hwmon.c
+ *
+ * Copyright (C) 2005, 2008, 2009 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX/S3C64XX ADC hwmon support
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/soc/samsung/s3c-adc.h>
+#include <linux/platform_data/hwmon-s3c.h>
+
+struct s3c_hwmon_attr {
+ struct sensor_device_attribute in;
+ struct sensor_device_attribute label;
+ char in_name[12];
+ char label_name[12];
+};
+
+/**
+ * struct s3c_hwmon - ADC hwmon client information
+ * @lock: Access lock to serialise the conversions.
+ * @client: The client we registered with the S3C ADC core.
+ * @hwmon_dev: The hwmon device we created.
+ * @attr: The holders for the channel attributes.
+*/
+struct s3c_hwmon {
+ struct mutex lock;
+ struct s3c_adc_client *client;
+ struct device *hwmon_dev;
+
+ struct s3c_hwmon_attr attrs[8];
+};
+
+/**
+ * s3c_hwmon_read_ch - read a value from a given adc channel.
+ * @dev: The device.
+ * @hwmon: Our state.
+ * @channel: The channel we're reading from.
+ *
+ * Read a value from the @channel with the proper locking and sleep until
+ * either the read completes or we timeout awaiting the ADC core to get
+ * back to us.
+ */
+static int s3c_hwmon_read_ch(struct device *dev,
+ struct s3c_hwmon *hwmon, int channel)
+{
+ int ret;
+
+ ret = mutex_lock_interruptible(&hwmon->lock);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "reading channel %d\n", channel);
+
+ ret = s3c_adc_read(hwmon->client, channel);
+ mutex_unlock(&hwmon->lock);
+
+ return ret;
+}
+
+#ifdef CONFIG_SENSORS_S3C_RAW
+/**
+ * s3c_hwmon_show_raw - show a conversion from the raw channel number.
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * This show deals with the raw attribute, registered for each possible
+ * ADC channel. This does a conversion and returns the raw (un-scaled)
+ * value returned from the hardware.
+ */
+static ssize_t s3c_hwmon_show_raw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct s3c_hwmon *adc = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sa = to_sensor_dev_attr(attr);
+ int ret;
+
+ ret = s3c_hwmon_read_ch(dev, adc, sa->index);
+
+ return (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static SENSOR_DEVICE_ATTR(adc0_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 0);
+static SENSOR_DEVICE_ATTR(adc1_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 1);
+static SENSOR_DEVICE_ATTR(adc2_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 2);
+static SENSOR_DEVICE_ATTR(adc3_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 3);
+static SENSOR_DEVICE_ATTR(adc4_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 4);
+static SENSOR_DEVICE_ATTR(adc5_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 5);
+static SENSOR_DEVICE_ATTR(adc6_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 6);
+static SENSOR_DEVICE_ATTR(adc7_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 7);
+
+static struct attribute *s3c_hwmon_attrs[9] = {
+ &sensor_dev_attr_adc0_raw.dev_attr.attr,
+ &sensor_dev_attr_adc1_raw.dev_attr.attr,
+ &sensor_dev_attr_adc2_raw.dev_attr.attr,
+ &sensor_dev_attr_adc3_raw.dev_attr.attr,
+ &sensor_dev_attr_adc4_raw.dev_attr.attr,
+ &sensor_dev_attr_adc5_raw.dev_attr.attr,
+ &sensor_dev_attr_adc6_raw.dev_attr.attr,
+ &sensor_dev_attr_adc7_raw.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group s3c_hwmon_attrgroup = {
+ .attrs = s3c_hwmon_attrs,
+};
+
+static inline int s3c_hwmon_add_raw(struct device *dev)
+{
+ return sysfs_create_group(&dev->kobj, &s3c_hwmon_attrgroup);
+}
+
+static inline void s3c_hwmon_remove_raw(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &s3c_hwmon_attrgroup);
+}
+
+#else
+
+static inline int s3c_hwmon_add_raw(struct device *dev) { return 0; }
+static inline void s3c_hwmon_remove_raw(struct device *dev) { }
+
+#endif /* CONFIG_SENSORS_S3C_RAW */
+
+/**
+ * s3c_hwmon_ch_show - show value of a given channel
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * Read a value from the ADC and scale it before returning it to the
+ * caller. The scale factor is gained from the channel configuration
+ * passed via the platform data when the device was registered.
+ */
+static ssize_t s3c_hwmon_ch_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
+ struct s3c_hwmon *hwmon = dev_get_drvdata(dev);
+ struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev);
+ struct s3c_hwmon_chcfg *cfg;
+ int ret;
+
+ cfg = pdata->in[sen_attr->index];
+
+ ret = s3c_hwmon_read_ch(dev, hwmon, sen_attr->index);
+ if (ret < 0)
+ return ret;
+
+ ret *= cfg->mult;
+ ret = DIV_ROUND_CLOSEST(ret, cfg->div);
+
+ return sysfs_emit(buf, "%d\n", ret);
+}
+
+/**
+ * s3c_hwmon_label_show - show label name of the given channel.
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * Return the label name of a given channel
+ */
+static ssize_t s3c_hwmon_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
+ struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev);
+ struct s3c_hwmon_chcfg *cfg;
+
+ cfg = pdata->in[sen_attr->index];
+
+ return sysfs_emit(buf, "%s\n", cfg->name);
+}
+
+/**
+ * s3c_hwmon_create_attr - create hwmon attribute for given channel.
+ * @dev: The device to create the attribute on.
+ * @cfg: The channel configuration passed from the platform data.
+ * @channel: The ADC channel number to process.
+ *
+ * Create the scaled attribute for use with hwmon from the specified
+ * platform data in @pdata. The sysfs entry is handled by the routine
+ * s3c_hwmon_ch_show().
+ *
+ * The attribute name is taken from the configuration data if present
+ * otherwise the name is taken by concatenating in_ with the channel
+ * number.
+ */
+static int s3c_hwmon_create_attr(struct device *dev,
+ struct s3c_hwmon_chcfg *cfg,
+ struct s3c_hwmon_attr *attrs,
+ int channel)
+{
+ struct sensor_device_attribute *attr;
+ int ret;
+
+ snprintf(attrs->in_name, sizeof(attrs->in_name), "in%d_input", channel);
+
+ attr = &attrs->in;
+ attr->index = channel;
+ sysfs_attr_init(&attr->dev_attr.attr);
+ attr->dev_attr.attr.name = attrs->in_name;
+ attr->dev_attr.attr.mode = S_IRUGO;
+ attr->dev_attr.show = s3c_hwmon_ch_show;
+
+ ret = device_create_file(dev, &attr->dev_attr);
+ if (ret < 0) {
+ dev_err(dev, "failed to create input attribute\n");
+ return ret;
+ }
+
+ /* if this has a name, add a label */
+ if (cfg->name) {
+ snprintf(attrs->label_name, sizeof(attrs->label_name),
+ "in%d_label", channel);
+
+ attr = &attrs->label;
+ attr->index = channel;
+ sysfs_attr_init(&attr->dev_attr.attr);
+ attr->dev_attr.attr.name = attrs->label_name;
+ attr->dev_attr.attr.mode = S_IRUGO;
+ attr->dev_attr.show = s3c_hwmon_label_show;
+
+ ret = device_create_file(dev, &attr->dev_attr);
+ if (ret < 0) {
+ device_remove_file(dev, &attrs->in.dev_attr);
+ dev_err(dev, "failed to create label attribute\n");
+ }
+ }
+
+ return ret;
+}
+
+static void s3c_hwmon_remove_attr(struct device *dev,
+ struct s3c_hwmon_attr *attrs)
+{
+ device_remove_file(dev, &attrs->in.dev_attr);
+ device_remove_file(dev, &attrs->label.dev_attr);
+}
+
+/**
+ * s3c_hwmon_probe - device probe entry.
+ * @dev: The device being probed.
+*/
+static int s3c_hwmon_probe(struct platform_device *dev)
+{
+ struct s3c_hwmon_pdata *pdata = dev_get_platdata(&dev->dev);
+ struct s3c_hwmon *hwmon;
+ int ret = 0;
+ int i;
+
+ if (!pdata) {
+ dev_err(&dev->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ hwmon = devm_kzalloc(&dev->dev, sizeof(struct s3c_hwmon), GFP_KERNEL);
+ if (hwmon == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(dev, hwmon);
+
+ mutex_init(&hwmon->lock);
+
+ /* Register with the core ADC driver. */
+
+ hwmon->client = s3c_adc_register(dev, NULL, NULL, 0);
+ if (IS_ERR(hwmon->client)) {
+ dev_err(&dev->dev, "cannot register adc\n");
+ return PTR_ERR(hwmon->client);
+ }
+
+ /* add attributes for our adc devices. */
+
+ ret = s3c_hwmon_add_raw(&dev->dev);
+ if (ret)
+ goto err_registered;
+
+ /* register with the hwmon core */
+
+ hwmon->hwmon_dev = hwmon_device_register(&dev->dev);
+ if (IS_ERR(hwmon->hwmon_dev)) {
+ dev_err(&dev->dev, "error registering with hwmon\n");
+ ret = PTR_ERR(hwmon->hwmon_dev);
+ goto err_raw_attribute;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->in); i++) {
+ struct s3c_hwmon_chcfg *cfg = pdata->in[i];
+
+ if (!cfg)
+ continue;
+
+ if (cfg->mult >= 0x10000)
+ dev_warn(&dev->dev,
+ "channel %d multiplier too large\n",
+ i);
+
+ if (cfg->div == 0) {
+ dev_err(&dev->dev, "channel %d divider zero\n", i);
+ continue;
+ }
+
+ ret = s3c_hwmon_create_attr(&dev->dev, pdata->in[i],
+ &hwmon->attrs[i], i);
+ if (ret) {
+ dev_err(&dev->dev,
+ "error creating channel %d\n", i);
+
+ for (i--; i >= 0; i--)
+ s3c_hwmon_remove_attr(&dev->dev,
+ &hwmon->attrs[i]);
+
+ goto err_hwmon_register;
+ }
+ }
+
+ return 0;
+
+ err_hwmon_register:
+ hwmon_device_unregister(hwmon->hwmon_dev);
+
+ err_raw_attribute:
+ s3c_hwmon_remove_raw(&dev->dev);
+
+ err_registered:
+ s3c_adc_release(hwmon->client);
+
+ return ret;
+}
+
+static int s3c_hwmon_remove(struct platform_device *dev)
+{
+ struct s3c_hwmon *hwmon = platform_get_drvdata(dev);
+ int i;
+
+ s3c_hwmon_remove_raw(&dev->dev);
+
+ for (i = 0; i < ARRAY_SIZE(hwmon->attrs); i++)
+ s3c_hwmon_remove_attr(&dev->dev, &hwmon->attrs[i]);
+
+ hwmon_device_unregister(hwmon->hwmon_dev);
+ s3c_adc_release(hwmon->client);
+
+ return 0;
+}
+
+static struct platform_driver s3c_hwmon_driver = {
+ .driver = {
+ .name = "s3c-hwmon",
+ },
+ .probe = s3c_hwmon_probe,
+ .remove = s3c_hwmon_remove,
+};
+
+module_platform_driver(s3c_hwmon_driver);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C ADC HWMon driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c-hwmon");
diff --git a/drivers/hwmon/sbrmi.c b/drivers/hwmon/sbrmi.c
new file mode 100644
index 000000000..7bf0c3fba
--- /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 000000000..e35357c48
--- /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
new file mode 100644
index 000000000..886386272
--- /dev/null
+++ b/drivers/hwmon/sch5627.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bits.h>
+#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/err.h>
+#include <linux/mutex.h>
+#include "sch56xx-common.h"
+
+#define DRVNAME "sch5627"
+#define DEVNAME DRVNAME /* We only support one model */
+
+#define SCH5627_HWMON_ID 0xa5
+#define SCH5627_COMPANY_ID 0x5c
+#define SCH5627_PRIMARY_ID 0xa0
+
+#define SCH5627_REG_BUILD_CODE 0x39
+#define SCH5627_REG_BUILD_ID 0x3a
+#define SCH5627_REG_HWMON_ID 0x3c
+#define SCH5627_REG_HWMON_REV 0x3d
+#define SCH5627_REG_COMPANY_ID 0x3e
+#define SCH5627_REG_PRIMARY_ID 0x3f
+#define SCH5627_REG_CTRL 0x40
+
+#define SCH5627_CTRL_START BIT(0)
+#define SCH5627_CTRL_LOCK BIT(1)
+#define SCH5627_CTRL_VBAT BIT(4)
+
+#define SCH5627_NO_TEMPS 8
+#define SCH5627_NO_FANS 4
+#define SCH5627_NO_IN 5
+
+static const u16 SCH5627_REG_TEMP_MSB[SCH5627_NO_TEMPS] = {
+ 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181 };
+static const u16 SCH5627_REG_TEMP_LSN[SCH5627_NO_TEMPS] = {
+ 0xE2, 0xE1, 0xE1, 0xE5, 0xE5, 0xE6, 0x182, 0x182 };
+static const u16 SCH5627_REG_TEMP_HIGH_NIBBLE[SCH5627_NO_TEMPS] = {
+ 0, 0, 1, 1, 0, 0, 0, 1 };
+static const u16 SCH5627_REG_TEMP_HIGH[SCH5627_NO_TEMPS] = {
+ 0x61, 0x57, 0x59, 0x5B, 0x5D, 0x5F, 0x184, 0x186 };
+static const u16 SCH5627_REG_TEMP_ABS[SCH5627_NO_TEMPS] = {
+ 0x9B, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x1A8, 0x1A9 };
+
+static const u16 SCH5627_REG_FAN[SCH5627_NO_FANS] = {
+ 0x2C, 0x2E, 0x30, 0x32 };
+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] = {
+ 0xE4, 0xE4, 0xE3, 0xE3, 0x18A };
+static const u16 SCH5627_REG_IN_HIGH_NIBBLE[SCH5627_NO_IN] = {
+ 1, 0, 1, 0, 1 };
+static const u16 SCH5627_REG_IN_FACTOR[SCH5627_NO_IN] = {
+ 10745, 3660, 9765, 10745, 3660 };
+static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = {
+ "VCC", "VTT", "VBAT", "VTR", "V_IN" };
+
+struct sch5627_data {
+ unsigned short addr;
+ u8 control;
+ u8 temp_max[SCH5627_NO_TEMPS];
+ u8 temp_crit[SCH5627_NO_TEMPS];
+ u16 fan_min[SCH5627_NO_FANS];
+
+ struct mutex update_lock;
+ unsigned long last_battery; /* 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 int sch5627_update_temp(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->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]);
+ if (unlikely(val < 0)) {
+ 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]);
+ if (unlikely(val < 0)) {
+ 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 | SCH5627_CTRL_VBAT);
+ 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]);
+ if (unlikely(val < 0)) {
+ ret = val;
+ goto abort;
+ }
+ data->in[i] = val;
+ }
+ data->in_last_updated = jiffies;
+ data->in_valid = 1;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int sch5627_read_limits(struct sch5627_data *data)
+{
+ int i, val;
+
+ for (i = 0; i < SCH5627_NO_TEMPS; i++) {
+ /*
+ * Note what SMSC calls ABS, is what lm_sensors calls max
+ * (aka high), and HIGH is what lm_sensors calls crit.
+ */
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_TEMP_ABS[i]);
+ if (val < 0)
+ return val;
+ data->temp_max[i] = val;
+
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_TEMP_HIGH[i]);
+ if (val < 0)
+ return val;
+ data->temp_crit[i] = val;
+ }
+ for (i = 0; i < SCH5627_NO_FANS; i++) {
+ val = sch56xx_read_virtual_reg16(data->addr,
+ SCH5627_REG_FAN_MIN[i]);
+ if (val < 0)
+ return val;
+ data->fan_min[i] = val;
+ }
+
+ return 0;
+}
+
+static int reg_to_temp(u16 reg)
+{
+ return (reg * 625) / 10 - 64000;
+}
+
+static int reg_to_temp_limit(u8 reg)
+{
+ return (reg - 64) * 1000;
+}
+
+static int reg_to_rpm(u16 reg)
+{
+ if (reg == 0)
+ return -EIO;
+ if (reg == 0xffff)
+ return 0;
+
+ return 5400540 / reg;
+}
+
+static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ const struct sch5627_data *data = drvdata;
+
+ /* Once the lock bit is set, the virtual registers become read-only
+ * until the next power cycle.
+ */
+ if (data->control & SCH5627_CTRL_LOCK)
+ return 0444;
+
+ if (type == hwmon_pwm && attr == hwmon_pwm_auto_channels_temp)
+ return 0644;
+
+ return 0444;
+}
+
+static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long *val)
+{
+ struct sch5627_data *data = dev_get_drvdata(dev);
+ 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 -EOPNOTSUPP;
+}
+
+static int sch5627_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ switch (type) {
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_label:
+ *str = SCH5627_IN_LABELS[channel];
+ return 0;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int sch5627_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long val)
+{
+ struct sch5627_data *data = dev_get_drvdata(dev);
+ 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 -EOPNOTSUPP;
+}
+
+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 hwmon_chip_info sch5627_chip_info = {
+ .ops = &sch5627_ops,
+ .info = sch5627_info,
+};
+
+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),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID);
+ 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);
+ return -ENODEV;
+ }
+
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID);
+ 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);
+ return -ENODEV;
+ }
+
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID);
+ 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);
+ return -ENODEV;
+ }
+
+ build_code = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_BUILD_CODE);
+ if (build_code < 0)
+ return build_code;
+
+ build_id = sch56xx_read_virtual_reg16(data->addr,
+ SCH5627_REG_BUILD_ID);
+ if (build_id < 0)
+ return build_id;
+
+ hwmon_rev = sch56xx_read_virtual_reg(data->addr,
+ SCH5627_REG_HWMON_REV);
+ if (hwmon_rev < 0)
+ return hwmon_rev;
+
+ val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL);
+ if (val < 0)
+ return val;
+
+ data->control = val;
+ if (!(data->control & SCH5627_CTRL_START)) {
+ pr_err("hardware monitoring not enabled\n");
+ return -ENODEV;
+ }
+ /* Trigger a Vbat voltage measurement, so that we get a valid reading
+ the first time we read Vbat */
+ sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | SCH5627_CTRL_VBAT);
+ data->last_battery = jiffies;
+
+ /*
+ * Read limits, we do this only once as reading a register on
+ * the sch5627 is quite expensive (and they don't change).
+ */
+ err = sch5627_read_limits(data);
+ if (err)
+ 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);
+
+ 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 */
+ sch56xx_watchdog_register(&pdev->dev, data->addr,
+ (build_code << 24) | (build_id << 8) | hwmon_rev,
+ &data->update_lock, 1);
+
+ return 0;
+}
+
+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,
+ .id_table = sch5627_device_id,
+};
+
+module_platform_driver(sch5627_driver);
+
+MODULE_DESCRIPTION("SMSC SCH5627 Hardware Monitoring Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c
new file mode 100644
index 000000000..269757bc3
--- /dev/null
+++ b/drivers/hwmon/sch5636.c
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * Copyright (C) 2011-2012 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ ***************************************************************************/
+
+#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"
+
+#define DRVNAME "sch5636"
+#define DEVNAME "theseus" /* We only support one model for now */
+
+#define SCH5636_REG_FUJITSU_ID 0x780
+#define SCH5636_REG_FUJITSU_REV 0x783
+
+#define SCH5636_NO_INS 5
+#define SCH5636_NO_TEMPS 16
+#define SCH5636_NO_FANS 8
+
+static const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = {
+ 0x22, 0x23, 0x24, 0x25, 0x189 };
+static const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = {
+ 4400, 1500, 4000, 4400, 16000 };
+static const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = {
+ "3.3V", "VREF", "VBAT", "3.3AUX", "12V" };
+
+static const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = {
+ 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C };
+#define SCH5636_REG_TEMP_CTRL(i) (0x790 + (i))
+#define SCH5636_TEMP_WORKING 0x01
+#define SCH5636_TEMP_ALARM 0x02
+#define SCH5636_TEMP_DEACTIVATED 0x80
+
+static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = {
+ 0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 };
+#define SCH5636_REG_FAN_CTRL(i) (0x880 + (i))
+/* FAULT in datasheet, but acts as an alarm */
+#define SCH5636_FAN_ALARM 0x04
+#define SCH5636_FAN_NOT_PRESENT 0x08
+#define SCH5636_FAN_DEACTIVATED 0x80
+
+
+struct sch5636_data {
+ unsigned short addr;
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ 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];
+ u8 temp_ctrl[SCH5636_NO_TEMPS];
+ u16 fan_val[SCH5636_NO_FANS];
+ u8 fan_ctrl[SCH5636_NO_FANS];
+};
+
+static struct sch5636_data *sch5636_update_device(struct device *dev)
+{
+ struct sch5636_data *data = dev_get_drvdata(dev);
+ struct sch5636_data *ret = data;
+ int i, val;
+
+ mutex_lock(&data->update_lock);
+
+ /* Cache the values for 1 second */
+ if (data->valid && !time_after(jiffies, data->last_updated + HZ))
+ goto abort;
+
+ for (i = 0; i < SCH5636_NO_INS; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_IN_VAL[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->in[i] = val;
+ }
+
+ for (i = 0; i < SCH5636_NO_TEMPS; i++) {
+ if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED)
+ continue;
+
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_VAL[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_val[i] = val;
+
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_CTRL(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_ctrl[i] = val;
+ /* Alarms need to be explicitly write-cleared */
+ if (val & SCH5636_TEMP_ALARM) {
+ sch56xx_write_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_CTRL(i), val);
+ }
+ }
+
+ for (i = 0; i < SCH5636_NO_FANS; i++) {
+ if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED)
+ continue;
+
+ val = sch56xx_read_virtual_reg16(data->addr,
+ SCH5636_REG_FAN_VAL[i]);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->fan_val[i] = val;
+
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FAN_CTRL(i));
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->fan_ctrl[i] = val;
+ /* Alarms need to be explicitly write-cleared */
+ if (val & SCH5636_FAN_ALARM) {
+ sch56xx_write_virtual_reg(data->addr,
+ SCH5636_REG_FAN_CTRL(i), val);
+ }
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int reg_to_rpm(u16 reg)
+{
+ if (reg == 0)
+ return -EIO;
+ if (reg == 0xffff)
+ return 0;
+
+ return 5400540 / reg;
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%s\n", DEVNAME);
+}
+
+static ssize_t in_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = DIV_ROUND_CLOSEST(
+ data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index],
+ 255);
+ return sysfs_emit(buf, "%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);
+
+ return sysfs_emit(buf, "%s\n",
+ SCH5636_IN_LABELS[attr->index]);
+}
+
+static ssize_t temp_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->temp_val[attr->index] - 64) * 1000;
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t temp_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1;
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t temp_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0;
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t fan_value_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = reg_to_rpm(data->fan_val[attr->index]);
+ if (val < 0)
+ return val;
+
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t fan_fault_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0;
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t fan_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct sch5636_data *data = sch5636_update_device(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0;
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static struct sensor_device_attribute sch5636_attr[] = {
+ SENSOR_ATTR_RO(name, name, 0),
+ SENSOR_ATTR_RO(in0_input, in_value, 0),
+ SENSOR_ATTR_RO(in0_label, in_label, 0),
+ SENSOR_ATTR_RO(in1_input, in_value, 1),
+ SENSOR_ATTR_RO(in1_label, in_label, 1),
+ SENSOR_ATTR_RO(in2_input, in_value, 2),
+ SENSOR_ATTR_RO(in2_label, in_label, 2),
+ SENSOR_ATTR_RO(in3_input, in_value, 3),
+ SENSOR_ATTR_RO(in3_label, in_label, 3),
+ SENSOR_ATTR_RO(in4_input, in_value, 4),
+ SENSOR_ATTR_RO(in4_label, in_label, 4),
+};
+
+static struct sensor_device_attribute sch5636_temp_attr[] = {
+ SENSOR_ATTR_RO(temp1_input, temp_value, 0),
+ SENSOR_ATTR_RO(temp1_fault, temp_fault, 0),
+ SENSOR_ATTR_RO(temp1_alarm, temp_alarm, 0),
+ SENSOR_ATTR_RO(temp2_input, temp_value, 1),
+ SENSOR_ATTR_RO(temp2_fault, temp_fault, 1),
+ SENSOR_ATTR_RO(temp2_alarm, temp_alarm, 1),
+ SENSOR_ATTR_RO(temp3_input, temp_value, 2),
+ SENSOR_ATTR_RO(temp3_fault, temp_fault, 2),
+ SENSOR_ATTR_RO(temp3_alarm, temp_alarm, 2),
+ SENSOR_ATTR_RO(temp4_input, temp_value, 3),
+ SENSOR_ATTR_RO(temp4_fault, temp_fault, 3),
+ SENSOR_ATTR_RO(temp4_alarm, temp_alarm, 3),
+ SENSOR_ATTR_RO(temp5_input, temp_value, 4),
+ SENSOR_ATTR_RO(temp5_fault, temp_fault, 4),
+ SENSOR_ATTR_RO(temp5_alarm, temp_alarm, 4),
+ SENSOR_ATTR_RO(temp6_input, temp_value, 5),
+ SENSOR_ATTR_RO(temp6_fault, temp_fault, 5),
+ SENSOR_ATTR_RO(temp6_alarm, temp_alarm, 5),
+ SENSOR_ATTR_RO(temp7_input, temp_value, 6),
+ SENSOR_ATTR_RO(temp7_fault, temp_fault, 6),
+ SENSOR_ATTR_RO(temp7_alarm, temp_alarm, 6),
+ SENSOR_ATTR_RO(temp8_input, temp_value, 7),
+ SENSOR_ATTR_RO(temp8_fault, temp_fault, 7),
+ SENSOR_ATTR_RO(temp8_alarm, temp_alarm, 7),
+ SENSOR_ATTR_RO(temp9_input, temp_value, 8),
+ SENSOR_ATTR_RO(temp9_fault, temp_fault, 8),
+ SENSOR_ATTR_RO(temp9_alarm, temp_alarm, 8),
+ SENSOR_ATTR_RO(temp10_input, temp_value, 9),
+ SENSOR_ATTR_RO(temp10_fault, temp_fault, 9),
+ SENSOR_ATTR_RO(temp10_alarm, temp_alarm, 9),
+ SENSOR_ATTR_RO(temp11_input, temp_value, 10),
+ SENSOR_ATTR_RO(temp11_fault, temp_fault, 10),
+ SENSOR_ATTR_RO(temp11_alarm, temp_alarm, 10),
+ SENSOR_ATTR_RO(temp12_input, temp_value, 11),
+ SENSOR_ATTR_RO(temp12_fault, temp_fault, 11),
+ SENSOR_ATTR_RO(temp12_alarm, temp_alarm, 11),
+ SENSOR_ATTR_RO(temp13_input, temp_value, 12),
+ SENSOR_ATTR_RO(temp13_fault, temp_fault, 12),
+ SENSOR_ATTR_RO(temp13_alarm, temp_alarm, 12),
+ SENSOR_ATTR_RO(temp14_input, temp_value, 13),
+ SENSOR_ATTR_RO(temp14_fault, temp_fault, 13),
+ SENSOR_ATTR_RO(temp14_alarm, temp_alarm, 13),
+ SENSOR_ATTR_RO(temp15_input, temp_value, 14),
+ SENSOR_ATTR_RO(temp15_fault, temp_fault, 14),
+ SENSOR_ATTR_RO(temp15_alarm, temp_alarm, 14),
+ SENSOR_ATTR_RO(temp16_input, temp_value, 15),
+ SENSOR_ATTR_RO(temp16_fault, temp_fault, 15),
+ SENSOR_ATTR_RO(temp16_alarm, temp_alarm, 15),
+};
+
+static struct sensor_device_attribute sch5636_fan_attr[] = {
+ SENSOR_ATTR_RO(fan1_input, fan_value, 0),
+ SENSOR_ATTR_RO(fan1_fault, fan_fault, 0),
+ SENSOR_ATTR_RO(fan1_alarm, fan_alarm, 0),
+ SENSOR_ATTR_RO(fan2_input, fan_value, 1),
+ SENSOR_ATTR_RO(fan2_fault, fan_fault, 1),
+ SENSOR_ATTR_RO(fan2_alarm, fan_alarm, 1),
+ SENSOR_ATTR_RO(fan3_input, fan_value, 2),
+ SENSOR_ATTR_RO(fan3_fault, fan_fault, 2),
+ SENSOR_ATTR_RO(fan3_alarm, fan_alarm, 2),
+ SENSOR_ATTR_RO(fan4_input, fan_value, 3),
+ SENSOR_ATTR_RO(fan4_fault, fan_fault, 3),
+ SENSOR_ATTR_RO(fan4_alarm, fan_alarm, 3),
+ SENSOR_ATTR_RO(fan5_input, fan_value, 4),
+ SENSOR_ATTR_RO(fan5_fault, fan_fault, 4),
+ SENSOR_ATTR_RO(fan5_alarm, fan_alarm, 4),
+ SENSOR_ATTR_RO(fan6_input, fan_value, 5),
+ SENSOR_ATTR_RO(fan6_fault, fan_fault, 5),
+ SENSOR_ATTR_RO(fan6_alarm, fan_alarm, 5),
+ SENSOR_ATTR_RO(fan7_input, fan_value, 6),
+ SENSOR_ATTR_RO(fan7_fault, fan_fault, 6),
+ SENSOR_ATTR_RO(fan7_alarm, fan_alarm, 6),
+ SENSOR_ATTR_RO(fan8_input, fan_value, 7),
+ SENSOR_ATTR_RO(fan8_fault, fan_fault, 7),
+ SENSOR_ATTR_RO(fan8_alarm, fan_alarm, 7),
+};
+
+static int sch5636_remove(struct platform_device *pdev)
+{
+ struct sch5636_data *data = platform_get_drvdata(pdev);
+ int i;
+
+ if (data->hwmon_dev)
+ hwmon_device_unregister(data->hwmon_dev);
+
+ for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++)
+ device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr);
+
+ for (i = 0; i < SCH5636_NO_TEMPS * 3; i++)
+ device_remove_file(&pdev->dev,
+ &sch5636_temp_attr[i].dev_attr);
+
+ for (i = 0; i < SCH5636_NO_FANS * 3; i++)
+ device_remove_file(&pdev->dev,
+ &sch5636_fan_attr[i].dev_attr);
+
+ return 0;
+}
+
+static int sch5636_probe(struct platform_device *pdev)
+{
+ struct sch5636_data *data;
+ int i, err, val, revision[2];
+ char id[4];
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct sch5636_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ for (i = 0; i < 3; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FUJITSU_ID + i);
+ if (val < 0) {
+ pr_err("Could not read Fujitsu id byte at %#x\n",
+ SCH5636_REG_FUJITSU_ID + i);
+ err = val;
+ goto error;
+ }
+ id[i] = val;
+ }
+ id[i] = '\0';
+
+ if (strcmp(id, "THS")) {
+ pr_err("Unknown Fujitsu id: %02x%02x%02x\n",
+ id[0], id[1], id[2]);
+ err = -ENODEV;
+ goto error;
+ }
+
+ for (i = 0; i < 2; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FUJITSU_REV + i);
+ if (val < 0) {
+ err = val;
+ goto error;
+ }
+ revision[i] = val;
+ }
+ pr_info("Found %s chip at %#hx, revision: %d.%02d\n", DEVNAME,
+ data->addr, revision[0], revision[1]);
+
+ /* Read all temp + fan ctrl registers to determine which are active */
+ for (i = 0; i < SCH5636_NO_TEMPS; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_TEMP_CTRL(i));
+ if (unlikely(val < 0)) {
+ err = val;
+ goto error;
+ }
+ data->temp_ctrl[i] = val;
+ }
+
+ for (i = 0; i < SCH5636_NO_FANS; i++) {
+ val = sch56xx_read_virtual_reg(data->addr,
+ SCH5636_REG_FAN_CTRL(i));
+ if (unlikely(val < 0)) {
+ err = val;
+ goto error;
+ }
+ data->fan_ctrl[i] = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) {
+ err = device_create_file(&pdev->dev,
+ &sch5636_attr[i].dev_attr);
+ if (err)
+ goto error;
+ }
+
+ for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) {
+ if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED)
+ continue;
+
+ err = device_create_file(&pdev->dev,
+ &sch5636_temp_attr[i].dev_attr);
+ if (err)
+ goto error;
+ }
+
+ for (i = 0; i < (SCH5636_NO_FANS * 3); i++) {
+ if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED)
+ continue;
+
+ err = device_create_file(&pdev->dev,
+ &sch5636_fan_attr[i].dev_attr);
+ 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;
+ }
+
+ /* Note failing to register the watchdog is not a fatal error */
+ sch56xx_watchdog_register(&pdev->dev, data->addr, (revision[0] << 8) | revision[1],
+ &data->update_lock, 0);
+
+ return 0;
+
+error:
+ sch5636_remove(pdev);
+ 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);
+
+MODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c
new file mode 100644
index 000000000..ac1f72580
--- /dev/null
+++ b/drivers/hwmon/sch56xx-common.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include "sch56xx-common.h"
+
+/* Insmod parameters */
+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) ")");
+
+#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */
+#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */
+
+#define SIO_SCH5627_ID 0xC6 /* Chipset ID */
+#define SIO_SCH5636_ID 0xC7 /* Chipset ID */
+
+#define REGION_LENGTH 10
+
+#define SCH56XX_CMD_READ 0x02
+#define SCH56XX_CMD_WRITE 0x03
+
+/* Watchdog registers */
+#define SCH56XX_REG_WDOG_PRESET 0x58B
+#define SCH56XX_REG_WDOG_CONTROL 0x58C
+#define SCH56XX_WDOG_TIME_BASE_SEC 0x01
+#define SCH56XX_REG_WDOG_OUTPUT_ENABLE 0x58E
+#define SCH56XX_WDOG_OUTPUT_ENABLE 0x02
+
+struct sch56xx_watchdog_data {
+ u16 addr;
+ struct mutex *io_lock;
+ struct watchdog_info wdinfo;
+ struct watchdog_device wddev;
+ u8 watchdog_preset;
+ u8 watchdog_control;
+ u8 watchdog_output_enable;
+};
+
+static struct platform_device *sch56xx_pdev;
+
+/* Super I/O functions */
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident */
+ if (!request_muxed_region(base, 2, "sch56xx")) {
+ pr_err("I/O address 0x%04x already in use\n", base);
+ return -EBUSY;
+ }
+
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_REG_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v)
+{
+ u8 val;
+ int i;
+ /*
+ * According to SMSC for the commands we use the maximum time for
+ * the EM to respond is 15 ms, but testing shows in practice it
+ * responds within 15-32 reads, so we first busy poll, and if
+ * that fails sleep a bit and try again until we are way past
+ * the 15 ms maximum response time.
+ */
+ const int max_busy_polls = 64;
+ const int max_lazy_polls = 32;
+
+ /* (Optional) Write-Clear the EC to Host Mailbox Register */
+ val = inb(addr + 1);
+ outb(val, addr + 1);
+
+ /* Set Mailbox Address Pointer to first location in Region 1 */
+ outb(0x00, addr + 2);
+ outb(0x80, addr + 3);
+
+ /* Write Request Packet Header */
+ outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */
+ outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */
+ outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */
+
+ /* Write Value field */
+ if (cmd == SCH56XX_CMD_WRITE)
+ outb(v, addr + 4);
+
+ /* Write Address field */
+ outb(reg & 0xff, addr + 6);
+ outb(reg >> 8, addr + 7);
+
+ /* Execute the Random Access Command */
+ outb(0x01, addr); /* Write 01h to the Host-to-EC register */
+
+ /* EM Interface Polling "Algorithm" */
+ for (i = 0; i < max_busy_polls + max_lazy_polls; i++) {
+ if (i >= max_busy_polls)
+ usleep_range(1000, 2000);
+ /* Read Interrupt source Register */
+ val = inb(addr + 8);
+ /* Write Clear the interrupt source bits */
+ if (val)
+ outb(val, addr + 8);
+ /* Command Completed ? */
+ if (val & 0x01)
+ break;
+ }
+ if (i == max_busy_polls + max_lazy_polls) {
+ pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n",
+ reg, 1);
+ return -EIO;
+ }
+
+ /*
+ * According to SMSC we may need to retry this, but sofar I've always
+ * seen this succeed in 1 try.
+ */
+ for (i = 0; i < max_busy_polls; i++) {
+ /* Read EC-to-Host Register */
+ val = inb(addr + 1);
+ /* Command Completed ? */
+ if (val == 0x01)
+ break;
+
+ if (i == 0)
+ pr_warn("EC reports: 0x%02x reading virtual register 0x%04hx\n",
+ (unsigned int)val, reg);
+ }
+ if (i == max_busy_polls) {
+ pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n",
+ reg, 2);
+ return -EIO;
+ }
+
+ /*
+ * According to the SMSC app note we should now do:
+ *
+ * Set Mailbox Address Pointer to first location in Region 1 *
+ * outb(0x00, addr + 2);
+ * outb(0x80, addr + 3);
+ *
+ * But if we do that things don't work, so let's not.
+ */
+
+ /* Read Value field */
+ if (cmd == SCH56XX_CMD_READ)
+ return inb(addr + 4);
+
+ return 0;
+}
+
+int sch56xx_read_virtual_reg(u16 addr, u16 reg)
+{
+ return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0);
+}
+EXPORT_SYMBOL(sch56xx_read_virtual_reg);
+
+int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val)
+{
+ return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val);
+}
+EXPORT_SYMBOL(sch56xx_write_virtual_reg);
+
+int sch56xx_read_virtual_reg16(u16 addr, u16 reg)
+{
+ int lsb, msb;
+
+ /* Read LSB first, this will cause the matching MSB to be latched */
+ lsb = sch56xx_read_virtual_reg(addr, reg);
+ if (lsb < 0)
+ return lsb;
+
+ msb = sch56xx_read_virtual_reg(addr, reg + 1);
+ if (msb < 0)
+ return msb;
+
+ return lsb | (msb << 8);
+}
+EXPORT_SYMBOL(sch56xx_read_virtual_reg16);
+
+int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
+ int high_nibble)
+{
+ int msb, lsn;
+
+ /* Read MSB first, this will cause the matching LSN to be latched */
+ msb = sch56xx_read_virtual_reg(addr, msb_reg);
+ if (msb < 0)
+ return msb;
+
+ lsn = sch56xx_read_virtual_reg(addr, lsn_reg);
+ if (lsn < 0)
+ return lsn;
+
+ if (high_nibble)
+ return (msb << 4) | (lsn >> 4);
+ else
+ return (msb << 4) | (lsn & 0x0f);
+}
+EXPORT_SYMBOL(sch56xx_read_virtual_reg12);
+
+/*
+ * Watchdog routines
+ */
+
+static int watchdog_set_timeout(struct watchdog_device *wddev,
+ unsigned int timeout)
+{
+ struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
+ unsigned int resolution;
+ u8 control;
+ int ret;
+
+ /* 1 second or 60 second resolution? */
+ if (timeout <= 255)
+ resolution = 1;
+ else
+ resolution = 60;
+
+ if (timeout < resolution || timeout > (resolution * 255))
+ return -EINVAL;
+
+ if (resolution == 1)
+ control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC;
+ else
+ control = data->watchdog_control & ~SCH56XX_WDOG_TIME_BASE_SEC;
+
+ if (data->watchdog_control != control) {
+ mutex_lock(data->io_lock);
+ ret = sch56xx_write_virtual_reg(data->addr,
+ SCH56XX_REG_WDOG_CONTROL,
+ control);
+ mutex_unlock(data->io_lock);
+ if (ret)
+ return ret;
+
+ data->watchdog_control = control;
+ }
+
+ /*
+ * Remember new timeout value, but do not write as that (re)starts
+ * the watchdog countdown.
+ */
+ data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
+ wddev->timeout = data->watchdog_preset * resolution;
+
+ return 0;
+}
+
+static int watchdog_start(struct watchdog_device *wddev)
+{
+ struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
+ int ret;
+ u8 val;
+
+ /*
+ * The sch56xx's watchdog cannot really be started / stopped
+ * it is always running, but we can avoid the timer expiring
+ * from causing a system reset by clearing the output enable bit.
+ *
+ * The sch56xx's watchdog will set the watchdog event bit, bit 0
+ * of the second interrupt source register (at base-address + 9),
+ * when the timer expires.
+ *
+ * This will only cause a system reset if the 0-1 flank happens when
+ * output enable is true. Setting output enable after the flank will
+ * not cause a reset, nor will the timer expiring a second time.
+ * This means we must clear the watchdog event bit in case it is set.
+ *
+ * The timer may still be running (after a recent watchdog_stop) and
+ * mere milliseconds away from expiring, so the timer must be reset
+ * first!
+ */
+
+ mutex_lock(data->io_lock);
+
+ /* 1. Reset the watchdog countdown counter */
+ ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET,
+ data->watchdog_preset);
+ if (ret)
+ goto leave;
+
+ /* 2. Enable output */
+ val = data->watchdog_output_enable | SCH56XX_WDOG_OUTPUT_ENABLE;
+ ret = sch56xx_write_virtual_reg(data->addr,
+ SCH56XX_REG_WDOG_OUTPUT_ENABLE, val);
+ if (ret)
+ goto leave;
+
+ data->watchdog_output_enable = val;
+
+ /* 3. Clear the watchdog event bit if set */
+ val = inb(data->addr + 9);
+ if (val & 0x01)
+ outb(0x01, data->addr + 9);
+
+leave:
+ mutex_unlock(data->io_lock);
+ return ret;
+}
+
+static int watchdog_trigger(struct watchdog_device *wddev)
+{
+ struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
+ int ret;
+
+ /* Reset the watchdog countdown counter */
+ mutex_lock(data->io_lock);
+ ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET,
+ data->watchdog_preset);
+ mutex_unlock(data->io_lock);
+
+ return ret;
+}
+
+static int watchdog_stop(struct watchdog_device *wddev)
+{
+ struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
+ int ret = 0;
+ u8 val;
+
+ val = data->watchdog_output_enable & ~SCH56XX_WDOG_OUTPUT_ENABLE;
+ mutex_lock(data->io_lock);
+ ret = sch56xx_write_virtual_reg(data->addr,
+ SCH56XX_REG_WDOG_OUTPUT_ENABLE, val);
+ mutex_unlock(data->io_lock);
+ if (ret)
+ return ret;
+
+ data->watchdog_output_enable = val;
+ return 0;
+}
+
+static const struct watchdog_ops watchdog_ops = {
+ .owner = THIS_MODULE,
+ .start = watchdog_start,
+ .stop = watchdog_stop,
+ .ping = watchdog_trigger,
+ .set_timeout = watchdog_set_timeout,
+};
+
+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;
+
+ /* Cache the watchdog registers */
+ mutex_lock(io_lock);
+ control =
+ sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_CONTROL);
+ output_enable =
+ sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_OUTPUT_ENABLE);
+ mutex_unlock(io_lock);
+
+ if (control < 0)
+ return;
+ if (output_enable < 0)
+ return;
+ if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) {
+ pr_warn("Watchdog not enabled by BIOS, not registering\n");
+ return;
+ }
+
+ data = devm_kzalloc(parent, sizeof(struct sch56xx_watchdog_data), GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->addr = addr;
+ data->io_lock = io_lock;
+
+ strscpy(data->wdinfo.identity, "sch56xx watchdog", sizeof(data->wdinfo.identity));
+ data->wdinfo.firmware_version = revision;
+ data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT;
+ if (!nowayout)
+ data->wdinfo.options |= WDIOF_MAGICCLOSE;
+
+ data->wddev.info = &data->wdinfo;
+ data->wddev.ops = &watchdog_ops;
+ data->wddev.parent = parent;
+ data->wddev.timeout = 60;
+ data->wddev.min_timeout = 1;
+ data->wddev.max_timeout = 255 * 60;
+ watchdog_set_nowayout(&data->wddev, nowayout);
+ if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)
+ 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) ->
+ Choose a preset which will give us a 1 minute timeout */
+ if (control & SCH56XX_WDOG_TIME_BASE_SEC)
+ data->watchdog_preset = 60; /* seconds */
+ else
+ data->watchdog_preset = 1; /* minute */
+
+ data->watchdog_control = control;
+ data->watchdog_output_enable = output_enable;
+
+ watchdog_set_drvdata(&data->wddev, data);
+ err = devm_watchdog_register_device(parent, &data->wddev);
+ if (err) {
+ pr_err("Registering watchdog chardev: %d\n", err);
+ devm_kfree(parent, data);
+ }
+}
+EXPORT_SYMBOL(sch56xx_watchdog_register);
+
+/*
+ * platform dev find, add and remove functions
+ */
+
+static int __init sch56xx_find(int sioaddr, const char **name)
+{
+ u8 devid;
+ unsigned short address;
+ int err;
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ devid = superio_inb(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_SCH5627_ID:
+ *name = "sch5627";
+ break;
+ case SIO_SCH5636_ID:
+ *name = "sch5636";
+ break;
+ default:
+ pr_debug("Unsupported device id: 0x%02x\n",
+ (unsigned int)devid);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ superio_select(sioaddr, SIO_SCH56XX_LD_EM);
+
+ if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
+ pr_warn("Device not activated\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ /*
+ * Warning the order of the low / high byte is the other way around
+ * as on most other superio devices!!
+ */
+ address = superio_inb(sioaddr, SIO_REG_ADDR) |
+ superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8;
+ if (address == 0) {
+ pr_warn("Base address not set\n");
+ err = -ENODEV;
+ goto exit;
+ }
+ err = address;
+
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+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;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ return err;
+
+ sch56xx_pdev = platform_device_register_simple(name, -1, &res, 1);
+
+ return PTR_ERR_OR_ZERO(sch56xx_pdev);
+}
+
+static int __init sch56xx_init(void)
+{
+ int address;
+ const char *name = NULL;
+
+ address = sch56xx_find(0x4e, &name);
+ if (address < 0)
+ address = sch56xx_find(0x2e, &name);
+ if (address < 0)
+ return address;
+
+ return sch56xx_device_add(address, name);
+}
+
+static void __exit sch56xx_exit(void)
+{
+ platform_device_unregister(sch56xx_pdev);
+}
+
+MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_init(sch56xx_init);
+module_exit(sch56xx_exit);
diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h
new file mode 100644
index 000000000..e907d9da0
--- /dev/null
+++ b/drivers/hwmon/sch56xx-common.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/***************************************************************************
+ * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com> *
+ * *
+ ***************************************************************************/
+
+#include <linux/mutex.h>
+
+struct sch56xx_watchdog_data;
+
+int sch56xx_read_virtual_reg(u16 addr, u16 reg);
+int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val);
+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);
+
+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
new file mode 100644
index 000000000..e192f0c67
--- /dev/null
+++ b/drivers/hwmon/scmi-hwmon.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface(SCMI) based hwmon sensor driver
+ *
+ * Copyright (C) 2018-2021 ARM Ltd.
+ * Sudeep Holla <sudeep.holla@arm.com>
+ */
+
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+static const struct scmi_sensor_proto_ops *sensor_ops;
+
+struct scmi_sensors {
+ 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;
+
+ while (x--)
+ r *= 10;
+
+ return r;
+}
+
+static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value)
+{
+ int scale = sensor->scale;
+ u64 f;
+
+ switch (sensor->type) {
+ case TEMPERATURE_C:
+ case VOLTAGE:
+ case CURRENT:
+ scale += 3;
+ break;
+ case POWER:
+ case ENERGY:
+ scale += 6;
+ break;
+ default:
+ break;
+ }
+
+ if (scale == 0)
+ return 0;
+
+ if (abs(scale) > 19)
+ return -E2BIG;
+
+ f = __pow10(abs(scale));
+ if (scale > 0)
+ *value *= f;
+ else
+ *value = div64_u64(*value, f);
+
+ return 0;
+}
+
+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;
+
+ ret = sensor_ops->reading_get(ph, sensor->id, &value);
+ if (ret)
+ return ret;
+
+ ret = scmi_hwmon_scale(sensor, &value);
+ if (!ret)
+ *val = value;
+
+ 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)
+{
+ const struct scmi_sensor_info *sensor;
+ struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
+
+ sensor = *(scmi_sensors->info[type] + channel);
+ *str = sensor->name;
+
+ return 0;
+}
+
+static umode_t
+scmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct scmi_sensor_info *sensor;
+ const struct scmi_sensors *scmi_sensors = drvdata;
+
+ sensor = *(scmi_sensors->info[type] + channel);
+ if (sensor)
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops scmi_hwmon_ops = {
+ .is_visible = scmi_hwmon_is_visible,
+ .read = scmi_hwmon_read,
+ .read_string = scmi_hwmon_read_string,
+};
+
+static struct hwmon_chip_info scmi_chip_info = {
+ .ops = &scmi_hwmon_ops,
+ .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)
+{
+ int i;
+ u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
+
+ if (!cfg)
+ return -ENOMEM;
+
+ scmi_hwmon_chan->type = type;
+ scmi_hwmon_chan->config = cfg;
+ for (i = 0; i < num; i++, cfg++)
+ *cfg = config;
+
+ return 0;
+}
+
+static enum hwmon_sensor_types scmi_types[] = {
+ [TEMPERATURE_C] = hwmon_temp,
+ [VOLTAGE] = hwmon_in,
+ [CURRENT] = hwmon_curr,
+ [POWER] = hwmon_power,
+ [ENERGY] = hwmon_energy,
+};
+
+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,
+ [hwmon_power] = HWMON_P_INPUT | HWMON_P_LABEL,
+ [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;
+ u16 nr_sensors;
+ 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, 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)
+ return -ENODEV;
+
+ 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;
+
+ scmi_sensors = devm_kzalloc(dev, sizeof(*scmi_sensors), GFP_KERNEL);
+ if (!scmi_sensors)
+ return -ENOMEM;
+
+ scmi_sensors->ph = ph;
+
+ for (i = 0; i < nr_sensors; i++) {
+ sensor = sensor_ops->info_get(ph, i);
+ if (!sensor)
+ return -EINVAL;
+
+ switch (sensor->type) {
+ case TEMPERATURE_C:
+ case VOLTAGE:
+ case CURRENT:
+ case POWER:
+ case ENERGY:
+ type = scmi_types[sensor->type];
+ if (!nr_count[type])
+ nr_types++;
+ nr_count[type]++;
+ break;
+ }
+ }
+
+ if (nr_count[hwmon_temp])
+ nr_count_temp = nr_count[hwmon_temp];
+
+ scmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*scmi_hwmon_chan),
+ GFP_KERNEL);
+ if (!scmi_hwmon_chan)
+ return -ENOMEM;
+
+ ptr_scmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*ptr_scmi_ci),
+ GFP_KERNEL);
+ if (!ptr_scmi_ci)
+ return -ENOMEM;
+
+ scmi_chip_info.info = ptr_scmi_ci;
+ chip_info = &scmi_chip_info;
+
+ for (type = 0; type < hwmon_max; type++) {
+ if (!nr_count[type])
+ continue;
+
+ scmi_hwmon_add_chan_info(scmi_hwmon_chan, dev, nr_count[type],
+ type, hwmon_attributes[type]);
+ *ptr_scmi_ci++ = scmi_hwmon_chan++;
+
+ scmi_sensors->info[type] =
+ devm_kcalloc(dev, nr_count[type],
+ sizeof(*scmi_sensors->info), GFP_KERNEL);
+ if (!scmi_sensors->info[type])
+ return -ENOMEM;
+ }
+
+ for (i = nr_sensors - 1; i >= 0 ; i--) {
+ sensor = sensor_ops->info_get(ph, i);
+ if (!sensor)
+ continue;
+
+ switch (sensor->type) {
+ case TEMPERATURE_C:
+ case VOLTAGE:
+ case CURRENT:
+ case POWER:
+ case ENERGY:
+ type = scmi_types[sensor->type];
+ idx = --nr_count[type];
+ *(scmi_sensors->info[type] + idx) = sensor;
+ break;
+ }
+ }
+
+ hwdev = devm_hwmon_device_register_with_info(dev, "scmi_sensors",
+ scmi_sensors, chip_info,
+ NULL);
+ if (IS_ERR(hwdev))
+ return PTR_ERR(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[] = {
+ { SCMI_PROTOCOL_SENSOR, "hwmon" },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_hwmon_drv = {
+ .name = "scmi-hwmon",
+ .probe = scmi_hwmon_probe,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_hwmon_drv);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI HWMON interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c
new file mode 100644
index 000000000..4d75385f7
--- /dev/null
+++ b/drivers/hwmon/scpi-hwmon.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Power Interface(SCPI) based hwmon sensor driver
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ * Punit Agrawal <punit.agrawal@arm.com>
+ */
+
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/scpi_protocol.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+struct sensor_data {
+ unsigned int scale;
+ struct scpi_sensor_info info;
+ struct device_attribute dev_attr_input;
+ struct device_attribute dev_attr_label;
+ char input[20];
+ char label[20];
+};
+
+struct scpi_thermal_zone {
+ int sensor_id;
+ struct scpi_sensors *scpi_sensors;
+};
+
+struct scpi_sensors {
+ struct scpi_ops *scpi_ops;
+ struct sensor_data *data;
+ struct list_head thermal_zones;
+ struct attribute **attrs;
+ struct attribute_group group;
+ const struct attribute_group *groups[2];
+};
+
+static const u32 gxbb_scpi_scale[] = {
+ [TEMPERATURE] = 1, /* (celsius) */
+ [VOLTAGE] = 1000, /* (millivolts) */
+ [CURRENT] = 1000, /* (milliamperes) */
+ [POWER] = 1000000, /* (microwatts) */
+ [ENERGY] = 1000000, /* (microjoules) */
+};
+
+static const u32 scpi_scale[] = {
+ [TEMPERATURE] = 1000, /* (millicelsius) */
+ [VOLTAGE] = 1000, /* (millivolts) */
+ [CURRENT] = 1000, /* (milliamperes) */
+ [POWER] = 1000000, /* (microwatts) */
+ [ENERGY] = 1000000, /* (microjoules) */
+};
+
+static void scpi_scale_reading(u64 *value, struct sensor_data *sensor)
+{
+ if (scpi_scale[sensor->info.class] != sensor->scale) {
+ *value *= scpi_scale[sensor->info.class];
+ do_div(*value, sensor->scale);
+ }
+}
+
+static int scpi_read_temp(struct thermal_zone_device *tz, int *temp)
+{
+ 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];
+ u64 value;
+ int ret;
+
+ ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
+ if (ret)
+ return ret;
+
+ scpi_scale_reading(&value, sensor);
+
+ *temp = value;
+ return 0;
+}
+
+/* hwmon callback functions */
+static ssize_t
+scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct scpi_sensors *scpi_sensors = dev_get_drvdata(dev);
+ struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops;
+ struct sensor_data *sensor;
+ u64 value;
+ int ret;
+
+ sensor = container_of(attr, struct sensor_data, dev_attr_input);
+
+ ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value);
+ if (ret)
+ return ret;
+
+ 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);
+}
+
+static ssize_t
+scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_data *sensor;
+
+ sensor = container_of(attr, struct sensor_data, dev_attr_label);
+
+ return sprintf(buf, "%s\n", sensor->info.name);
+}
+
+static const struct thermal_zone_device_ops scpi_sensor_ops = {
+ .get_temp = scpi_read_temp,
+};
+
+static const struct of_device_id scpi_of_match[] = {
+ {.compatible = "arm,scpi-sensors", .data = &scpi_scale},
+ {.compatible = "amlogic,meson-gxbb-scpi-sensors", .data = &gxbb_scpi_scale},
+ {},
+};
+MODULE_DEVICE_TABLE(of, scpi_of_match);
+
+static int scpi_hwmon_probe(struct platform_device *pdev)
+{
+ u16 nr_sensors, i;
+ const u32 *scale;
+ int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0;
+ int num_energy = 0;
+ struct scpi_ops *scpi_ops;
+ struct device *hwdev, *dev = &pdev->dev;
+ struct scpi_sensors *scpi_sensors;
+ int idx, ret;
+
+ scpi_ops = get_scpi_ops();
+ if (!scpi_ops)
+ return -EPROBE_DEFER;
+
+ ret = scpi_ops->sensor_get_capability(&nr_sensors);
+ if (ret)
+ return ret;
+
+ if (!nr_sensors)
+ return -ENODEV;
+
+ scpi_sensors = devm_kzalloc(dev, sizeof(*scpi_sensors), GFP_KERNEL);
+ if (!scpi_sensors)
+ return -ENOMEM;
+
+ scpi_sensors->data = devm_kcalloc(dev, nr_sensors,
+ sizeof(*scpi_sensors->data), GFP_KERNEL);
+ if (!scpi_sensors->data)
+ return -ENOMEM;
+
+ scpi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1,
+ sizeof(*scpi_sensors->attrs), GFP_KERNEL);
+ if (!scpi_sensors->attrs)
+ return -ENOMEM;
+
+ scpi_sensors->scpi_ops = scpi_ops;
+
+ scale = of_device_get_match_data(&pdev->dev);
+ if (!scale) {
+ dev_err(&pdev->dev, "Unable to initialize scpi-hwmon data\n");
+ return -ENODEV;
+ }
+
+ for (i = 0, idx = 0; i < nr_sensors; i++) {
+ struct sensor_data *sensor = &scpi_sensors->data[idx];
+
+ ret = scpi_ops->sensor_get_info(i, &sensor->info);
+ if (ret)
+ return ret;
+
+ switch (sensor->info.class) {
+ case TEMPERATURE:
+ snprintf(sensor->input, sizeof(sensor->input),
+ "temp%d_input", num_temp + 1);
+ snprintf(sensor->label, sizeof(sensor->input),
+ "temp%d_label", num_temp + 1);
+ num_temp++;
+ break;
+ case VOLTAGE:
+ snprintf(sensor->input, sizeof(sensor->input),
+ "in%d_input", num_volt);
+ snprintf(sensor->label, sizeof(sensor->input),
+ "in%d_label", num_volt);
+ num_volt++;
+ break;
+ case CURRENT:
+ snprintf(sensor->input, sizeof(sensor->input),
+ "curr%d_input", num_current + 1);
+ snprintf(sensor->label, sizeof(sensor->input),
+ "curr%d_label", num_current + 1);
+ num_current++;
+ break;
+ case POWER:
+ snprintf(sensor->input, sizeof(sensor->input),
+ "power%d_input", num_power + 1);
+ snprintf(sensor->label, sizeof(sensor->input),
+ "power%d_label", num_power + 1);
+ num_power++;
+ break;
+ case ENERGY:
+ snprintf(sensor->input, sizeof(sensor->input),
+ "energy%d_input", num_energy + 1);
+ snprintf(sensor->label, sizeof(sensor->input),
+ "energy%d_label", num_energy + 1);
+ num_energy++;
+ break;
+ default:
+ continue;
+ }
+
+ sensor->scale = scale[sensor->info.class];
+
+ sensor->dev_attr_input.attr.mode = 0444;
+ sensor->dev_attr_input.show = scpi_show_sensor;
+ sensor->dev_attr_input.attr.name = sensor->input;
+
+ sensor->dev_attr_label.attr.mode = 0444;
+ sensor->dev_attr_label.show = scpi_show_label;
+ sensor->dev_attr_label.attr.name = sensor->label;
+
+ scpi_sensors->attrs[idx << 1] = &sensor->dev_attr_input.attr;
+ scpi_sensors->attrs[(idx << 1) + 1] = &sensor->dev_attr_label.attr;
+
+ sysfs_attr_init(scpi_sensors->attrs[idx << 1]);
+ sysfs_attr_init(scpi_sensors->attrs[(idx << 1) + 1]);
+ idx++;
+ }
+
+ scpi_sensors->group.attrs = scpi_sensors->attrs;
+ scpi_sensors->groups[0] = &scpi_sensors->group;
+
+ platform_set_drvdata(pdev, scpi_sensors);
+
+ hwdev = devm_hwmon_device_register_with_groups(dev,
+ "scpi_sensors", scpi_sensors, scpi_sensors->groups);
+
+ if (IS_ERR(hwdev))
+ return PTR_ERR(hwdev);
+
+ /*
+ * Register the temperature sensors with the thermal framework
+ * to allow their usage in setting up the thermal zones from
+ * device tree.
+ *
+ * NOTE: Not all temperature sensors maybe used for thermal
+ * control
+ */
+ INIT_LIST_HEAD(&scpi_sensors->thermal_zones);
+ for (i = 0; i < nr_sensors; i++) {
+ struct sensor_data *sensor = &scpi_sensors->data[i];
+ struct thermal_zone_device *z;
+ struct scpi_thermal_zone *zone;
+
+ if (sensor->info.class != TEMPERATURE)
+ continue;
+
+ zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
+ if (!zone)
+ return -ENOMEM;
+
+ zone->sensor_id = i;
+ zone->scpi_sensors = scpi_sensors;
+ 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
+ * any thermal zones or if the thermal subsystem is
+ * not configured.
+ */
+ if (IS_ERR(z))
+ devm_kfree(dev, zone);
+ }
+
+ return 0;
+}
+
+static struct platform_driver scpi_hwmon_platdrv = {
+ .driver = {
+ .name = "scpi-hwmon",
+ .of_match_table = scpi_of_match,
+ },
+ .probe = scpi_hwmon_probe,
+};
+module_platform_driver(scpi_hwmon_platdrv);
+
+MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>");
+MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
new file mode 100644
index 000000000..ae4d14257
--- /dev/null
+++ b/drivers/hwmon/sht15.c
@@ -0,0 +1,1062 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sht15.c - support for the SHT15 Temperature and Humidity Sensor
+ *
+ * Portions Copyright (c) 2010-2012 Savoir-faire Linux Inc.
+ * Jerome Oufella <jerome.oufella@savoirfairelinux.com>
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
+ * Copyright (c) 2009 Jonathan Cameron
+ *
+ * Copyright (c) 2007 Wouter Horre
+ *
+ * For further information, see the Documentation/hwmon/sht15.rst file.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/bitrev.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+
+/* Commands */
+#define SHT15_MEASURE_TEMP 0x03
+#define SHT15_MEASURE_RH 0x05
+#define SHT15_WRITE_STATUS 0x06
+#define SHT15_READ_STATUS 0x07
+#define SHT15_SOFT_RESET 0x1E
+
+/* Min timings */
+#define SHT15_TSCKL 100 /* (nsecs) clock low */
+#define SHT15_TSCKH 100 /* (nsecs) clock high */
+#define SHT15_TSU 150 /* (nsecs) data setup time */
+#define SHT15_TSRST 11 /* (msecs) soft reset time */
+
+/* Status Register Bits */
+#define SHT15_STATUS_LOW_RESOLUTION 0x01
+#define SHT15_STATUS_NO_OTP_RELOAD 0x02
+#define SHT15_STATUS_HEATER 0x04
+#define SHT15_STATUS_LOW_BATTERY 0x40
+
+/* List of supported chips */
+enum sht15_chips { sht10, sht11, sht15, sht71, sht75 };
+
+/* Actions the driver may be doing */
+enum sht15_state {
+ SHT15_READING_NOTHING,
+ SHT15_READING_TEMP,
+ SHT15_READING_HUMID
+};
+
+/**
+ * struct sht15_temppair - elements of voltage dependent temp calc
+ * @vdd: supply voltage in microvolts
+ * @d1: see data sheet
+ */
+struct sht15_temppair {
+ int vdd; /* microvolts */
+ int d1;
+};
+
+/* Table 9 from datasheet - relates temperature calculation to supply voltage */
+static const struct sht15_temppair temppoints[] = {
+ { 2500000, -39400 },
+ { 3000000, -39600 },
+ { 3500000, -39700 },
+ { 4000000, -39800 },
+ { 5000000, -40100 },
+};
+
+/* Table from CRC datasheet, section 2.4 */
+static const u8 sht15_crc8_table[] = {
+ 0, 49, 98, 83, 196, 245, 166, 151,
+ 185, 136, 219, 234, 125, 76, 31, 46,
+ 67, 114, 33, 16, 135, 182, 229, 212,
+ 250, 203, 152, 169, 62, 15, 92, 109,
+ 134, 183, 228, 213, 66, 115, 32, 17,
+ 63, 14, 93, 108, 251, 202, 153, 168,
+ 197, 244, 167, 150, 1, 48, 99, 82,
+ 124, 77, 30, 47, 184, 137, 218, 235,
+ 61, 12, 95, 110, 249, 200, 155, 170,
+ 132, 181, 230, 215, 64, 113, 34, 19,
+ 126, 79, 28, 45, 186, 139, 216, 233,
+ 199, 246, 165, 148, 3, 50, 97, 80,
+ 187, 138, 217, 232, 127, 78, 29, 44,
+ 2, 51, 96, 81, 198, 247, 164, 149,
+ 248, 201, 154, 171, 60, 13, 94, 111,
+ 65, 112, 35, 18, 133, 180, 231, 214,
+ 122, 75, 24, 41, 190, 143, 220, 237,
+ 195, 242, 161, 144, 7, 54, 101, 84,
+ 57, 8, 91, 106, 253, 204, 159, 174,
+ 128, 177, 226, 211, 68, 117, 38, 23,
+ 252, 205, 158, 175, 56, 9, 90, 107,
+ 69, 116, 39, 22, 129, 176, 227, 210,
+ 191, 142, 221, 236, 123, 74, 25, 40,
+ 6, 55, 100, 85, 194, 243, 160, 145,
+ 71, 118, 37, 20, 131, 178, 225, 208,
+ 254, 207, 156, 173, 58, 11, 88, 105,
+ 4, 53, 102, 87, 192, 241, 162, 147,
+ 189, 140, 223, 238, 121, 72, 27, 42,
+ 193, 240, 163, 146, 5, 52, 103, 86,
+ 120, 73, 26, 43, 188, 141, 222, 239,
+ 130, 179, 224, 209, 70, 119, 36, 21,
+ 59, 10, 89, 104, 255, 206, 157, 172
+};
+
+/**
+ * struct sht15_data - device instance specific data
+ * @sck: clock GPIO line
+ * @data: data GPIO line
+ * @read_work: bh of interrupt handler.
+ * @wait_queue: wait queue for getting values from device.
+ * @val_temp: last temperature value read from device.
+ * @val_humid: last humidity value read from device.
+ * @val_status: last status register value read from device.
+ * @checksum_ok: last value read from the device passed CRC validation.
+ * @checksumming: flag used to enable the data validation with CRC.
+ * @state: state identifying the action the driver is doing.
+ * @measurements_valid: are the current stored measures valid (start condition).
+ * @status_valid: is the current stored status valid (start condition).
+ * @last_measurement: time of last measure.
+ * @last_status: time of last status reading.
+ * @read_lock: mutex to ensure only one read in progress at a time.
+ * @dev: associate device structure.
+ * @hwmon_dev: device associated with hwmon subsystem.
+ * @reg: associated regulator (if specified).
+ * @nb: notifier block to handle notifications of voltage
+ * changes.
+ * @supply_uv: local copy of supply voltage used to allow use of
+ * regulator consumer if available.
+ * @supply_uv_valid: indicates that an updated value has not yet been
+ * obtained from the regulator and so any calculations
+ * based upon it will be invalid.
+ * @update_supply_work: work struct that is used to update the supply_uv.
+ * @interrupt_handled: flag used to indicate a handler has been scheduled.
+ */
+struct sht15_data {
+ struct gpio_desc *sck;
+ struct gpio_desc *data;
+ struct work_struct read_work;
+ wait_queue_head_t wait_queue;
+ uint16_t val_temp;
+ uint16_t val_humid;
+ u8 val_status;
+ bool checksum_ok;
+ bool checksumming;
+ enum sht15_state state;
+ bool measurements_valid;
+ bool status_valid;
+ unsigned long last_measurement;
+ unsigned long last_status;
+ struct mutex read_lock;
+ struct device *dev;
+ struct device *hwmon_dev;
+ struct regulator *reg;
+ struct notifier_block nb;
+ int supply_uv;
+ bool supply_uv_valid;
+ struct work_struct update_supply_work;
+ atomic_t interrupt_handled;
+};
+
+/**
+ * sht15_crc8() - compute crc8
+ * @data: sht15 specific data.
+ * @value: sht15 retrieved data.
+ * @len: Length of retrieved data
+ *
+ * This implements section 2 of the CRC datasheet.
+ */
+static u8 sht15_crc8(struct sht15_data *data,
+ const u8 *value,
+ int len)
+{
+ u8 crc = bitrev8(data->val_status & 0x0F);
+
+ while (len--) {
+ crc = sht15_crc8_table[*value ^ crc];
+ value++;
+ }
+
+ return crc;
+}
+
+/**
+ * sht15_connection_reset() - reset the comms interface
+ * @data: sht15 specific data
+ *
+ * This implements section 3.4 of the data sheet
+ */
+static int sht15_connection_reset(struct sht15_data *data)
+{
+ int i, err;
+
+ err = gpiod_direction_output(data->data, 1);
+ if (err)
+ return err;
+ ndelay(SHT15_TSCKL);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ for (i = 0; i < 9; ++i) {
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSCKH);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ }
+ return 0;
+}
+
+/**
+ * sht15_send_bit() - send an individual bit to the device
+ * @data: device state data
+ * @val: value of bit to be sent
+ */
+static inline void sht15_send_bit(struct sht15_data *data, int val)
+{
+ gpiod_set_value(data->data, val);
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSCKH);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL); /* clock low time */
+}
+
+/**
+ * sht15_transmission_start() - specific sequence for new transmission
+ * @data: device state data
+ *
+ * Timings for this are not documented on the data sheet, so very
+ * conservative ones used in implementation. This implements
+ * figure 12 on the data sheet.
+ */
+static int sht15_transmission_start(struct sht15_data *data)
+{
+ int err;
+
+ /* ensure data is high and output */
+ err = gpiod_direction_output(data->data, 1);
+ if (err)
+ return err;
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSCKH);
+ gpiod_set_value(data->data, 0);
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSCKH);
+ gpiod_set_value(data->data, 1);
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ return 0;
+}
+
+/**
+ * sht15_send_byte() - send a single byte to the device
+ * @data: device state
+ * @byte: value to be sent
+ */
+static void sht15_send_byte(struct sht15_data *data, u8 byte)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ sht15_send_bit(data, !!(byte & 0x80));
+ byte <<= 1;
+ }
+}
+
+/**
+ * sht15_wait_for_response() - checks for ack from device
+ * @data: device state
+ */
+static int sht15_wait_for_response(struct sht15_data *data)
+{
+ int err;
+
+ err = gpiod_direction_input(data->data);
+ if (err)
+ return err;
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSCKH);
+ if (gpiod_get_value(data->data)) {
+ gpiod_set_value(data->sck, 0);
+ dev_err(data->dev, "Command not acknowledged\n");
+ err = sht15_connection_reset(data);
+ if (err)
+ return err;
+ return -EIO;
+ }
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ return 0;
+}
+
+/**
+ * sht15_send_cmd() - Sends a command to the device.
+ * @data: device state
+ * @cmd: command byte to be sent
+ *
+ * On entry, sck is output low, data is output pull high
+ * and the interrupt disabled.
+ */
+static int sht15_send_cmd(struct sht15_data *data, u8 cmd)
+{
+ int err;
+
+ err = sht15_transmission_start(data);
+ if (err)
+ return err;
+ sht15_send_byte(data, cmd);
+ return sht15_wait_for_response(data);
+}
+
+/**
+ * sht15_soft_reset() - send a soft reset command
+ * @data: sht15 specific data.
+ *
+ * As described in section 3.2 of the datasheet.
+ */
+static int sht15_soft_reset(struct sht15_data *data)
+{
+ int ret;
+
+ ret = sht15_send_cmd(data, SHT15_SOFT_RESET);
+ if (ret)
+ return ret;
+ msleep(SHT15_TSRST);
+ /* device resets default hardware status register value */
+ data->val_status = 0;
+
+ return ret;
+}
+
+/**
+ * sht15_ack() - send a ack
+ * @data: sht15 specific data.
+ *
+ * Each byte of data is acknowledged by pulling the data line
+ * low for one clock pulse.
+ */
+static int sht15_ack(struct sht15_data *data)
+{
+ int err;
+
+ err = gpiod_direction_output(data->data, 0);
+ if (err)
+ return err;
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->data, 1);
+
+ return gpiod_direction_input(data->data);
+}
+
+/**
+ * sht15_end_transmission() - notify device of end of transmission
+ * @data: device state.
+ *
+ * This is basically a NAK (single clock pulse, data high).
+ */
+static int sht15_end_transmission(struct sht15_data *data)
+{
+ int err;
+
+ err = gpiod_direction_output(data->data, 1);
+ if (err)
+ return err;
+ ndelay(SHT15_TSU);
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSCKH);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ return 0;
+}
+
+/**
+ * sht15_read_byte() - Read a byte back from the device
+ * @data: device state.
+ */
+static u8 sht15_read_byte(struct sht15_data *data)
+{
+ int i;
+ u8 byte = 0;
+
+ for (i = 0; i < 8; ++i) {
+ byte <<= 1;
+ gpiod_set_value(data->sck, 1);
+ ndelay(SHT15_TSCKH);
+ byte |= !!gpiod_get_value(data->data);
+ gpiod_set_value(data->sck, 0);
+ ndelay(SHT15_TSCKL);
+ }
+ return byte;
+}
+
+/**
+ * sht15_send_status() - write the status register byte
+ * @data: sht15 specific data.
+ * @status: the byte to set the status register with.
+ *
+ * As described in figure 14 and table 5 of the datasheet.
+ */
+static int sht15_send_status(struct sht15_data *data, u8 status)
+{
+ int err;
+
+ err = sht15_send_cmd(data, SHT15_WRITE_STATUS);
+ if (err)
+ return err;
+ err = gpiod_direction_output(data->data, 1);
+ if (err)
+ return err;
+ ndelay(SHT15_TSU);
+ sht15_send_byte(data, status);
+ err = sht15_wait_for_response(data);
+ if (err)
+ return err;
+
+ data->val_status = status;
+ return 0;
+}
+
+/**
+ * sht15_update_status() - get updated status register from device if too old
+ * @data: device instance specific data.
+ *
+ * As described in figure 15 and table 5 of the datasheet.
+ */
+static int sht15_update_status(struct sht15_data *data)
+{
+ int ret = 0;
+ u8 status;
+ u8 previous_config;
+ u8 dev_checksum = 0;
+ u8 checksum_vals[2];
+ int timeout = HZ;
+
+ mutex_lock(&data->read_lock);
+ if (time_after(jiffies, data->last_status + timeout)
+ || !data->status_valid) {
+ ret = sht15_send_cmd(data, SHT15_READ_STATUS);
+ if (ret)
+ goto unlock;
+ status = sht15_read_byte(data);
+
+ if (data->checksumming) {
+ sht15_ack(data);
+ dev_checksum = bitrev8(sht15_read_byte(data));
+ checksum_vals[0] = SHT15_READ_STATUS;
+ checksum_vals[1] = status;
+ data->checksum_ok = (sht15_crc8(data, checksum_vals, 2)
+ == dev_checksum);
+ }
+
+ ret = sht15_end_transmission(data);
+ if (ret)
+ goto unlock;
+
+ /*
+ * Perform checksum validation on the received data.
+ * Specification mentions that in case a checksum verification
+ * fails, a soft reset command must be sent to the device.
+ */
+ if (data->checksumming && !data->checksum_ok) {
+ previous_config = data->val_status & 0x07;
+ ret = sht15_soft_reset(data);
+ if (ret)
+ goto unlock;
+ if (previous_config) {
+ ret = sht15_send_status(data, previous_config);
+ if (ret) {
+ dev_err(data->dev,
+ "CRC validation failed, unable "
+ "to restore device settings\n");
+ goto unlock;
+ }
+ }
+ ret = -EAGAIN;
+ goto unlock;
+ }
+
+ data->val_status = status;
+ data->status_valid = true;
+ data->last_status = jiffies;
+ }
+
+unlock:
+ mutex_unlock(&data->read_lock);
+ return ret;
+}
+
+/**
+ * sht15_measurement() - get a new value from device
+ * @data: device instance specific data
+ * @command: command sent to request value
+ * @timeout_msecs: timeout after which comms are assumed
+ * to have failed are reset.
+ */
+static int sht15_measurement(struct sht15_data *data,
+ int command,
+ int timeout_msecs)
+{
+ int ret;
+ u8 previous_config;
+
+ ret = sht15_send_cmd(data, command);
+ if (ret)
+ return ret;
+
+ ret = gpiod_direction_input(data->data);
+ if (ret)
+ return ret;
+ atomic_set(&data->interrupt_handled, 0);
+
+ enable_irq(gpiod_to_irq(data->data));
+ if (gpiod_get_value(data->data) == 0) {
+ disable_irq_nosync(gpiod_to_irq(data->data));
+ /* Only relevant if the interrupt hasn't occurred. */
+ if (!atomic_read(&data->interrupt_handled))
+ schedule_work(&data->read_work);
+ }
+ ret = wait_event_timeout(data->wait_queue,
+ (data->state == SHT15_READING_NOTHING),
+ msecs_to_jiffies(timeout_msecs));
+ if (data->state != SHT15_READING_NOTHING) { /* I/O error occurred */
+ data->state = SHT15_READING_NOTHING;
+ return -EIO;
+ } else if (ret == 0) { /* timeout occurred */
+ disable_irq_nosync(gpiod_to_irq(data->data));
+ ret = sht15_connection_reset(data);
+ if (ret)
+ return ret;
+ return -ETIME;
+ }
+
+ /*
+ * Perform checksum validation on the received data.
+ * Specification mentions that in case a checksum verification fails,
+ * a soft reset command must be sent to the device.
+ */
+ if (data->checksumming && !data->checksum_ok) {
+ previous_config = data->val_status & 0x07;
+ ret = sht15_soft_reset(data);
+ if (ret)
+ return ret;
+ if (previous_config) {
+ ret = sht15_send_status(data, previous_config);
+ if (ret) {
+ dev_err(data->dev,
+ "CRC validation failed, unable "
+ "to restore device settings\n");
+ return ret;
+ }
+ }
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/**
+ * sht15_update_measurements() - get updated measures from device if too old
+ * @data: device state
+ */
+static int sht15_update_measurements(struct sht15_data *data)
+{
+ int ret = 0;
+ int timeout = HZ;
+
+ mutex_lock(&data->read_lock);
+ if (time_after(jiffies, data->last_measurement + timeout)
+ || !data->measurements_valid) {
+ data->state = SHT15_READING_HUMID;
+ ret = sht15_measurement(data, SHT15_MEASURE_RH, 160);
+ if (ret)
+ goto unlock;
+ data->state = SHT15_READING_TEMP;
+ ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400);
+ if (ret)
+ goto unlock;
+ data->measurements_valid = true;
+ data->last_measurement = jiffies;
+ }
+
+unlock:
+ mutex_unlock(&data->read_lock);
+ return ret;
+}
+
+/**
+ * sht15_calc_temp() - convert the raw reading to a temperature
+ * @data: device state
+ *
+ * As per section 4.3 of the data sheet.
+ */
+static inline int sht15_calc_temp(struct sht15_data *data)
+{
+ int d1 = temppoints[0].d1;
+ int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10;
+ int i;
+
+ for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--)
+ /* Find pointer to interpolate */
+ if (data->supply_uv > temppoints[i - 1].vdd) {
+ d1 = (data->supply_uv - temppoints[i - 1].vdd)
+ * (temppoints[i].d1 - temppoints[i - 1].d1)
+ / (temppoints[i].vdd - temppoints[i - 1].vdd)
+ + temppoints[i - 1].d1;
+ break;
+ }
+
+ return data->val_temp * d2 + d1;
+}
+
+/**
+ * sht15_calc_humid() - using last temperature convert raw to humid
+ * @data: device state
+ *
+ * This is the temperature compensated version as per section 4.2 of
+ * the data sheet.
+ *
+ * The sensor is assumed to be V3, which is compatible with V4.
+ * Humidity conversion coefficients are shown in table 7 of the datasheet.
+ */
+static inline int sht15_calc_humid(struct sht15_data *data)
+{
+ int rh_linear; /* milli percent */
+ int temp = sht15_calc_temp(data);
+ int c2, c3;
+ int t2;
+ const int c1 = -4;
+
+ if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) {
+ c2 = 648000; /* x 10 ^ -6 */
+ c3 = -7200; /* x 10 ^ -7 */
+ t2 = 1280;
+ } else {
+ c2 = 40500; /* x 10 ^ -6 */
+ c3 = -28; /* x 10 ^ -7 */
+ t2 = 80;
+ }
+
+ rh_linear = c1 * 1000
+ + c2 * data->val_humid / 1000
+ + (data->val_humid * data->val_humid * c3) / 10000;
+ return (temp - 25000) * (10000 + t2 * data->val_humid)
+ / 1000000 + rh_linear;
+}
+
+/**
+ * sht15_show_status() - show status information in sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer where information is written to.
+ *
+ * Will be called on read access to temp1_fault, humidity1_fault
+ * and heater_enable sysfs attributes.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t sht15_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct sht15_data *data = dev_get_drvdata(dev);
+ u8 bit = to_sensor_dev_attr(attr)->index;
+
+ ret = sht15_update_status(data);
+
+ return ret ? ret : sprintf(buf, "%d\n", !!(data->val_status & bit));
+}
+
+/**
+ * sht15_store_heater() - change heater state via sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer to read the new heater state from.
+ * @count: length of the data.
+ *
+ * Will be called on write access to heater_enable sysfs attribute.
+ * Returns number of bytes actually decoded, negative errno on error.
+ */
+static ssize_t sht15_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct sht15_data *data = dev_get_drvdata(dev);
+ long value;
+ u8 status;
+
+ if (kstrtol(buf, 10, &value))
+ return -EINVAL;
+
+ mutex_lock(&data->read_lock);
+ status = data->val_status & 0x07;
+ if (!!value)
+ status |= SHT15_STATUS_HEATER;
+ else
+ status &= ~SHT15_STATUS_HEATER;
+
+ ret = sht15_send_status(data, status);
+ mutex_unlock(&data->read_lock);
+
+ return ret ? ret : count;
+}
+
+/**
+ * sht15_show_temp() - show temperature measurement value in sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer where measurement values are written to.
+ *
+ * Will be called on read access to temp1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t sht15_temp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct sht15_data *data = dev_get_drvdata(dev);
+
+ /* Technically no need to read humidity as well */
+ ret = sht15_update_measurements(data);
+
+ return ret ? ret : sprintf(buf, "%d\n",
+ sht15_calc_temp(data));
+}
+
+/**
+ * sht15_show_humidity() - show humidity measurement value in sysfs
+ * @dev: device.
+ * @attr: device attribute.
+ * @buf: sysfs buffer where measurement values are written to.
+ *
+ * Will be called on read access to humidity1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t sht15_humidity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct sht15_data *data = dev_get_drvdata(dev);
+
+ ret = sht15_update_measurements(data);
+
+ return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data));
+}
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ return sprintf(buf, "%s\n", pdev->name);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, sht15_temp, 0);
+static SENSOR_DEVICE_ATTR_RO(humidity1_input, sht15_humidity, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_fault, sht15_status,
+ SHT15_STATUS_LOW_BATTERY);
+static SENSOR_DEVICE_ATTR_RO(humidity1_fault, sht15_status,
+ SHT15_STATUS_LOW_BATTERY);
+static SENSOR_DEVICE_ATTR_RW(heater_enable, sht15_status, SHT15_STATUS_HEATER);
+static DEVICE_ATTR_RO(name);
+static struct attribute *sht15_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_humidity1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_humidity1_fault.dev_attr.attr,
+ &sensor_dev_attr_heater_enable.dev_attr.attr,
+ &dev_attr_name.attr,
+ NULL,
+};
+
+static const struct attribute_group sht15_attr_group = {
+ .attrs = sht15_attrs,
+};
+
+static irqreturn_t sht15_interrupt_fired(int irq, void *d)
+{
+ struct sht15_data *data = d;
+
+ /* First disable the interrupt */
+ disable_irq_nosync(irq);
+ atomic_inc(&data->interrupt_handled);
+ /* Then schedule a reading work struct */
+ if (data->state != SHT15_READING_NOTHING)
+ schedule_work(&data->read_work);
+ return IRQ_HANDLED;
+}
+
+static void sht15_bh_read_data(struct work_struct *work_s)
+{
+ uint16_t val = 0;
+ u8 dev_checksum = 0;
+ u8 checksum_vals[3];
+ struct sht15_data *data
+ = container_of(work_s, struct sht15_data,
+ read_work);
+
+ /* Firstly, verify the line is low */
+ if (gpiod_get_value(data->data)) {
+ /*
+ * If not, then start the interrupt again - care here as could
+ * have gone low in meantime so verify it hasn't!
+ */
+ atomic_set(&data->interrupt_handled, 0);
+ enable_irq(gpiod_to_irq(data->data));
+ /* If still not occurred or another handler was scheduled */
+ if (gpiod_get_value(data->data)
+ || atomic_read(&data->interrupt_handled))
+ return;
+ }
+
+ /* Read the data back from the device */
+ val = sht15_read_byte(data);
+ val <<= 8;
+ if (sht15_ack(data))
+ goto wakeup;
+ val |= sht15_read_byte(data);
+
+ if (data->checksumming) {
+ /*
+ * Ask the device for a checksum and read it back.
+ * Note: the device sends the checksum byte reversed.
+ */
+ if (sht15_ack(data))
+ goto wakeup;
+ dev_checksum = bitrev8(sht15_read_byte(data));
+ checksum_vals[0] = (data->state == SHT15_READING_TEMP) ?
+ SHT15_MEASURE_TEMP : SHT15_MEASURE_RH;
+ checksum_vals[1] = (u8) (val >> 8);
+ checksum_vals[2] = (u8) val;
+ data->checksum_ok
+ = (sht15_crc8(data, checksum_vals, 3) == dev_checksum);
+ }
+
+ /* Tell the device we are done */
+ if (sht15_end_transmission(data))
+ goto wakeup;
+
+ switch (data->state) {
+ case SHT15_READING_TEMP:
+ data->val_temp = val;
+ break;
+ case SHT15_READING_HUMID:
+ data->val_humid = val;
+ break;
+ default:
+ break;
+ }
+
+ data->state = SHT15_READING_NOTHING;
+wakeup:
+ wake_up(&data->wait_queue);
+}
+
+static void sht15_update_voltage(struct work_struct *work_s)
+{
+ struct sht15_data *data
+ = container_of(work_s, struct sht15_data,
+ update_supply_work);
+ data->supply_uv = regulator_get_voltage(data->reg);
+}
+
+/**
+ * sht15_invalidate_voltage() - mark supply voltage invalid when notified by reg
+ * @nb: associated notification structure
+ * @event: voltage regulator state change event code
+ * @ignored: function parameter - ignored here
+ *
+ * Note that as the notification code holds the regulator lock, we have
+ * to schedule an update of the supply voltage rather than getting it directly.
+ */
+static int sht15_invalidate_voltage(struct notifier_block *nb,
+ unsigned long event,
+ void *ignored)
+{
+ struct sht15_data *data = container_of(nb, struct sht15_data, nb);
+
+ if (event == REGULATOR_EVENT_VOLTAGE_CHANGE)
+ data->supply_uv_valid = false;
+ schedule_work(&data->update_supply_work);
+
+ return NOTIFY_OK;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sht15_dt_match[] = {
+ { .compatible = "sensirion,sht15" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sht15_dt_match);
+#endif
+
+static int sht15_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct sht15_data *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ INIT_WORK(&data->read_work, sht15_bh_read_data);
+ INIT_WORK(&data->update_supply_work, sht15_update_voltage);
+ platform_set_drvdata(pdev, data);
+ mutex_init(&data->read_lock);
+ data->dev = &pdev->dev;
+ init_waitqueue_head(&data->wait_queue);
+
+ /*
+ * If a regulator is available,
+ * query what the supply voltage actually is!
+ */
+ data->reg = devm_regulator_get_optional(data->dev, "vcc");
+ if (!IS_ERR(data->reg)) {
+ int voltage;
+
+ voltage = regulator_get_voltage(data->reg);
+ if (voltage)
+ data->supply_uv = voltage;
+
+ ret = regulator_enable(data->reg);
+ if (ret != 0) {
+ dev_err(&pdev->dev,
+ "failed to enable regulator: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Setup a notifier block to update this if another device
+ * causes the voltage to change
+ */
+ data->nb.notifier_call = &sht15_invalidate_voltage;
+ ret = regulator_register_notifier(data->reg, &data->nb);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "regulator notifier request failed\n");
+ regulator_disable(data->reg);
+ return ret;
+ }
+ }
+
+ /* Try requesting the GPIOs */
+ data->sck = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_LOW);
+ if (IS_ERR(data->sck)) {
+ ret = PTR_ERR(data->sck);
+ dev_err(&pdev->dev, "clock line GPIO request failed\n");
+ goto err_release_reg;
+ }
+ data->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN);
+ if (IS_ERR(data->data)) {
+ ret = PTR_ERR(data->data);
+ dev_err(&pdev->dev, "data line GPIO request failed\n");
+ goto err_release_reg;
+ }
+
+ ret = devm_request_irq(&pdev->dev, gpiod_to_irq(data->data),
+ sht15_interrupt_fired,
+ IRQF_TRIGGER_FALLING,
+ "sht15 data",
+ data);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get irq for data line\n");
+ goto err_release_reg;
+ }
+ disable_irq_nosync(gpiod_to_irq(data->data));
+ ret = sht15_connection_reset(data);
+ if (ret)
+ goto err_release_reg;
+ ret = sht15_soft_reset(data);
+ if (ret)
+ goto err_release_reg;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "sysfs create failed\n");
+ goto err_release_reg;
+ }
+
+ data->hwmon_dev = hwmon_device_register(data->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ goto err_release_sysfs_group;
+ }
+
+ return 0;
+
+err_release_sysfs_group:
+ sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
+err_release_reg:
+ if (!IS_ERR(data->reg)) {
+ regulator_unregister_notifier(data->reg, &data->nb);
+ regulator_disable(data->reg);
+ }
+ return ret;
+}
+
+static int sht15_remove(struct platform_device *pdev)
+{
+ struct sht15_data *data = platform_get_drvdata(pdev);
+ int ret;
+
+ 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);
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id sht15_device_ids[] = {
+ { "sht10", sht10 },
+ { "sht11", sht11 },
+ { "sht15", sht15 },
+ { "sht71", sht71 },
+ { "sht75", sht75 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, sht15_device_ids);
+
+static struct platform_driver sht15_driver = {
+ .driver = {
+ .name = "sht15",
+ .of_match_table = of_match_ptr(sht15_dt_match),
+ },
+ .probe = sht15_probe,
+ .remove = sht15_remove,
+ .id_table = sht15_device_ids,
+};
+module_platform_driver(sht15_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Sensirion SHT15 temperature and humidity sensor driver");
diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c
new file mode 100644
index 000000000..e23dbf287
--- /dev/null
+++ b/drivers/hwmon/sht21.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Sensirion SHT21 humidity and temperature sensor driver
+ *
+ * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.com>
+ *
+ * Data sheet available at https://www.sensirion.com/file/datasheet_sht21
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+
+/* I2C command bytes */
+#define SHT21_TRIG_T_MEASUREMENT_HM 0xe3
+#define SHT21_TRIG_RH_MEASUREMENT_HM 0xe5
+#define SHT21_READ_SNB_CMD1 0xFA
+#define SHT21_READ_SNB_CMD2 0x0F
+#define SHT21_READ_SNAC_CMD1 0xFC
+#define SHT21_READ_SNAC_CMD2 0xC9
+
+/**
+ * struct sht21 - SHT21 device specific data
+ * @client: I2C client device
+ * @lock: mutex to protect measurement values
+ * @last_update: time of last update (jiffies)
+ * @temperature: cached temperature measurement value
+ * @humidity: cached humidity measurement value
+ * @valid: only 0 before first measurement is taken
+ * @eic: cached electronic identification code text
+ */
+struct sht21 {
+ struct i2c_client *client;
+ struct mutex lock;
+ unsigned long last_update;
+ int temperature;
+ int humidity;
+ bool valid;
+ char eic[18];
+};
+
+/**
+ * sht21_temp_ticks_to_millicelsius() - convert raw temperature ticks to
+ * milli celsius
+ * @ticks: temperature ticks value received from sensor
+ */
+static inline int sht21_temp_ticks_to_millicelsius(int ticks)
+{
+ ticks &= ~0x0003; /* clear status bits */
+ /*
+ * Formula T = -46.85 + 175.72 * ST / 2^16 from data sheet 6.2,
+ * optimized for integer fixed point (3 digits) arithmetic
+ */
+ return ((21965 * ticks) >> 13) - 46850;
+}
+
+/**
+ * sht21_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to
+ * one-thousandths of a percent relative humidity
+ * @ticks: humidity ticks value received from sensor
+ */
+static inline int sht21_rh_ticks_to_per_cent_mille(int ticks)
+{
+ ticks &= ~0x0003; /* clear status bits */
+ /*
+ * Formula RH = -6 + 125 * SRH / 2^16 from data sheet 6.1,
+ * optimized for integer fixed point (3 digits) arithmetic
+ */
+ return ((15625 * ticks) >> 13) - 6000;
+}
+
+/**
+ * sht21_update_measurements() - get updated measurements from device
+ * @dev: device
+ *
+ * Returns 0 on success, else negative errno.
+ */
+static int sht21_update_measurements(struct device *dev)
+{
+ int ret = 0;
+ struct sht21 *sht21 = dev_get_drvdata(dev);
+ struct i2c_client *client = sht21->client;
+
+ mutex_lock(&sht21->lock);
+ /*
+ * Data sheet 2.4:
+ * SHT2x should not be active for more than 10% of the time - e.g.
+ * maximum two measurements per second at 12bit accuracy shall be made.
+ */
+ if (time_after(jiffies, sht21->last_update + HZ / 2) || !sht21->valid) {
+ ret = i2c_smbus_read_word_swapped(client,
+ SHT21_TRIG_T_MEASUREMENT_HM);
+ if (ret < 0)
+ goto out;
+ sht21->temperature = sht21_temp_ticks_to_millicelsius(ret);
+ ret = i2c_smbus_read_word_swapped(client,
+ SHT21_TRIG_RH_MEASUREMENT_HM);
+ if (ret < 0)
+ goto out;
+ sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret);
+ sht21->last_update = jiffies;
+ sht21->valid = true;
+ }
+out:
+ mutex_unlock(&sht21->lock);
+
+ return ret >= 0 ? 0 : ret;
+}
+
+/**
+ * sht21_show_temperature() - show temperature measurement value in sysfs
+ * @dev: device
+ * @attr: device attribute
+ * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
+ *
+ * Will be called on read access to temp1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t sht21_temperature_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sht21 *sht21 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sht21_update_measurements(dev);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", sht21->temperature);
+}
+
+/**
+ * sht21_show_humidity() - show humidity measurement value in sysfs
+ * @dev: device
+ * @attr: device attribute
+ * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
+ *
+ * Will be called on read access to humidity1_input sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t sht21_humidity_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sht21 *sht21 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sht21_update_measurements(dev);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", sht21->humidity);
+}
+
+static ssize_t eic_read(struct sht21 *sht21)
+{
+ struct i2c_client *client = sht21->client;
+ u8 tx[2];
+ u8 rx[8];
+ u8 eic[8];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = tx,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 8,
+ .buf = rx,
+ },
+ };
+ int ret;
+
+ tx[0] = SHT21_READ_SNB_CMD1;
+ tx[1] = SHT21_READ_SNB_CMD2;
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ goto out;
+ eic[2] = rx[0];
+ eic[3] = rx[2];
+ eic[4] = rx[4];
+ eic[5] = rx[6];
+
+ tx[0] = SHT21_READ_SNAC_CMD1;
+ tx[1] = SHT21_READ_SNAC_CMD2;
+ msgs[1].len = 6;
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0)
+ goto out;
+ eic[0] = rx[3];
+ eic[1] = rx[4];
+ eic[6] = rx[0];
+ eic[7] = rx[1];
+
+ ret = snprintf(sht21->eic, sizeof(sht21->eic),
+ "%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ eic[0], eic[1], eic[2], eic[3],
+ eic[4], eic[5], eic[6], eic[7]);
+out:
+ if (ret < 0)
+ sht21->eic[0] = 0;
+
+ return ret;
+}
+
+/**
+ * eic_show() - show Electronic Identification Code in sysfs
+ * @dev: device
+ * @attr: device attribute
+ * @buf: sysfs buffer (PAGE_SIZE) where EIC is written
+ *
+ * Will be called on read access to eic sysfs attribute.
+ * Returns number of bytes written into buffer, negative errno on error.
+ */
+static ssize_t eic_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sht21 *sht21 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sizeof(sht21->eic) - 1;
+ mutex_lock(&sht21->lock);
+ if (!sht21->eic[0])
+ ret = eic_read(sht21);
+ if (ret > 0)
+ memcpy(buf, sht21->eic, ret);
+ mutex_unlock(&sht21->lock);
+ return ret;
+}
+
+/* sysfs attributes */
+static SENSOR_DEVICE_ATTR_RO(temp1_input, sht21_temperature, 0);
+static SENSOR_DEVICE_ATTR_RO(humidity1_input, sht21_humidity, 0);
+static DEVICE_ATTR_RO(eic);
+
+static struct attribute *sht21_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_humidity1_input.dev_attr.attr,
+ &dev_attr_eic.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(sht21);
+
+static int sht21_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct sht21 *sht21;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev,
+ "adapter does not support SMBus word transactions\n");
+ return -ENODEV;
+ }
+
+ sht21 = devm_kzalloc(dev, sizeof(*sht21), GFP_KERNEL);
+ if (!sht21)
+ return -ENOMEM;
+
+ sht21->client = client;
+
+ mutex_init(&sht21->lock);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ sht21, sht21_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* Device ID table */
+static const struct i2c_device_id sht21_id[] = {
+ { "sht21", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sht21_id);
+
+static struct i2c_driver sht21_driver = {
+ .driver.name = "sht21",
+ .probe_new = sht21_probe,
+ .id_table = sht21_id,
+};
+
+module_i2c_driver(sht21_driver);
+
+MODULE_AUTHOR("Urs Fleisch <urs.fleisch@sensirion.com>");
+MODULE_DESCRIPTION("Sensirion SHT21 humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
new file mode 100644
index 000000000..3f279aa1c
--- /dev/null
+++ b/drivers/hwmon/sht3x.c
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Sensirion SHT3x-DIS humidity and temperature sensor driver.
+ * The SHT3x comes in many different versions, this driver is for the
+ * I2C version only.
+ *
+ * Copyright (C) 2016 Sensirion AG, Switzerland
+ * Author: David Frey <david.frey@sensirion.com>
+ * Author: Pascal Sachs <pascal.sachs@sensirion.com>
+ */
+
+#include <asm/page.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_data/sht3x.h>
+
+/* commands (high precision mode) */
+static const unsigned char sht3x_cmd_measure_blocking_hpm[] = { 0x2c, 0x06 };
+static const unsigned char sht3x_cmd_measure_nonblocking_hpm[] = { 0x24, 0x00 };
+
+/* commands (low power mode) */
+static const unsigned char sht3x_cmd_measure_blocking_lpm[] = { 0x2c, 0x10 };
+static const unsigned char sht3x_cmd_measure_nonblocking_lpm[] = { 0x24, 0x16 };
+
+/* commands for periodic mode */
+static const unsigned char sht3x_cmd_measure_periodic_mode[] = { 0xe0, 0x00 };
+static const unsigned char sht3x_cmd_break[] = { 0x30, 0x93 };
+
+/* commands for heater control */
+static const unsigned char sht3x_cmd_heater_on[] = { 0x30, 0x6d };
+static const unsigned char sht3x_cmd_heater_off[] = { 0x30, 0x66 };
+
+/* other commands */
+static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d };
+static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 };
+
+/* delays for non-blocking i2c commands, both in us */
+#define SHT3X_NONBLOCKING_WAIT_TIME_HPM 15000
+#define SHT3X_NONBLOCKING_WAIT_TIME_LPM 4000
+
+#define SHT3X_WORD_LEN 2
+#define SHT3X_CMD_LENGTH 2
+#define SHT3X_CRC8_LEN 1
+#define SHT3X_RESPONSE_LENGTH 6
+#define SHT3X_CRC8_POLYNOMIAL 0x31
+#define SHT3X_CRC8_INIT 0xFF
+#define SHT3X_MIN_TEMPERATURE -45000
+#define SHT3X_MAX_TEMPERATURE 130000
+#define SHT3X_MIN_HUMIDITY 0
+#define SHT3X_MAX_HUMIDITY 100000
+
+enum sht3x_chips {
+ sht3x,
+ sts3x,
+};
+
+enum sht3x_limits {
+ limit_max = 0,
+ limit_max_hyst,
+ limit_min,
+ limit_min_hyst,
+};
+
+DECLARE_CRC8_TABLE(sht3x_crc8_table);
+
+/* periodic measure commands (high precision mode) */
+static const char periodic_measure_commands_hpm[][SHT3X_CMD_LENGTH] = {
+ /* 0.5 measurements per second */
+ {0x20, 0x32},
+ /* 1 measurements per second */
+ {0x21, 0x30},
+ /* 2 measurements per second */
+ {0x22, 0x36},
+ /* 4 measurements per second */
+ {0x23, 0x34},
+ /* 10 measurements per second */
+ {0x27, 0x37},
+};
+
+/* periodic measure commands (low power mode) */
+static const char periodic_measure_commands_lpm[][SHT3X_CMD_LENGTH] = {
+ /* 0.5 measurements per second */
+ {0x20, 0x2f},
+ /* 1 measurements per second */
+ {0x21, 0x2d},
+ /* 2 measurements per second */
+ {0x22, 0x2b},
+ /* 4 measurements per second */
+ {0x23, 0x29},
+ /* 10 measurements per second */
+ {0x27, 0x2a},
+};
+
+struct sht3x_limit_commands {
+ const char read_command[SHT3X_CMD_LENGTH];
+ const char write_command[SHT3X_CMD_LENGTH];
+};
+
+static const struct sht3x_limit_commands limit_commands[] = {
+ /* temp1_max, humidity1_max */
+ [limit_max] = { {0xe1, 0x1f}, {0x61, 0x1d} },
+ /* temp_1_max_hyst, humidity1_max_hyst */
+ [limit_max_hyst] = { {0xe1, 0x14}, {0x61, 0x16} },
+ /* temp1_min, humidity1_min */
+ [limit_min] = { {0xe1, 0x02}, {0x61, 0x00} },
+ /* temp_1_min_hyst, humidity1_min_hyst */
+ [limit_min_hyst] = { {0xe1, 0x09}, {0x61, 0x0B} },
+};
+
+#define SHT3X_NUM_LIMIT_CMD ARRAY_SIZE(limit_commands)
+
+static const u16 mode_to_update_interval[] = {
+ 0,
+ 2000,
+ 1000,
+ 500,
+ 250,
+ 100,
+};
+
+struct sht3x_data {
+ struct i2c_client *client;
+ struct mutex i2c_lock; /* lock for sending i2c commands */
+ struct mutex data_lock; /* lock for updating driver data */
+
+ u8 mode;
+ const unsigned char *command;
+ u32 wait_time; /* in us*/
+ unsigned long last_update; /* last update in periodic mode*/
+
+ struct sht3x_platform_data setup;
+
+ /*
+ * cached values for temperature and humidity and limits
+ * the limits arrays have the following order:
+ * max, max_hyst, min, min_hyst
+ */
+ int temperature;
+ int temperature_limits[SHT3X_NUM_LIMIT_CMD];
+ u32 humidity;
+ u32 humidity_limits[SHT3X_NUM_LIMIT_CMD];
+};
+
+static u8 get_mode_from_update_interval(u16 value)
+{
+ size_t index;
+ u8 number_of_modes = ARRAY_SIZE(mode_to_update_interval);
+
+ if (value == 0)
+ return 0;
+
+ /* find next faster update interval */
+ for (index = 1; index < number_of_modes; index++) {
+ if (mode_to_update_interval[index] <= value)
+ return index;
+ }
+
+ return number_of_modes - 1;
+}
+
+static int sht3x_read_from_command(struct i2c_client *client,
+ struct sht3x_data *data,
+ const char *command,
+ char *buf, int length, u32 wait_time)
+{
+ int ret;
+
+ mutex_lock(&data->i2c_lock);
+ ret = i2c_master_send(client, command, SHT3X_CMD_LENGTH);
+
+ if (ret != SHT3X_CMD_LENGTH) {
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ }
+
+ if (wait_time)
+ usleep_range(wait_time, wait_time + 1000);
+
+ ret = i2c_master_recv(client, buf, length);
+ if (ret != length) {
+ ret = ret < 0 ? ret : -EIO;
+ goto out;
+ }
+
+ ret = 0;
+out:
+ mutex_unlock(&data->i2c_lock);
+ return ret;
+}
+
+static int sht3x_extract_temperature(u16 raw)
+{
+ /*
+ * From datasheet:
+ * T = -45 + 175 * ST / 2^16
+ * Adapted for integer fixed point (3 digit) arithmetic.
+ */
+ return ((21875 * (int)raw) >> 13) - 45000;
+}
+
+static u32 sht3x_extract_humidity(u16 raw)
+{
+ /*
+ * From datasheet:
+ * RH = 100 * SRH / 2^16
+ * Adapted for integer fixed point (3 digit) arithmetic.
+ */
+ return (12500 * (u32)raw) >> 13;
+}
+
+static struct sht3x_data *sht3x_update_client(struct device *dev)
+{
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u16 interval_ms = mode_to_update_interval[data->mode];
+ unsigned long interval_jiffies = msecs_to_jiffies(interval_ms);
+ unsigned char buf[SHT3X_RESPONSE_LENGTH];
+ u16 val;
+ int ret = 0;
+
+ mutex_lock(&data->data_lock);
+ /*
+ * Only update cached readings once per update interval in periodic
+ * mode. In single shot mode the sensor measures values on demand, so
+ * every time the sysfs interface is called, a measurement is triggered.
+ * In periodic mode however, the measurement process is handled
+ * internally by the sensor and reading out sensor values only makes
+ * sense if a new reading is available.
+ */
+ if (time_after(jiffies, data->last_update + interval_jiffies)) {
+ ret = sht3x_read_from_command(client, data, data->command, buf,
+ sizeof(buf), data->wait_time);
+ if (ret)
+ goto out;
+
+ val = be16_to_cpup((__be16 *)buf);
+ data->temperature = sht3x_extract_temperature(val);
+ val = be16_to_cpup((__be16 *)(buf + 3));
+ data->humidity = sht3x_extract_humidity(val);
+ data->last_update = jiffies;
+ }
+
+out:
+ mutex_unlock(&data->data_lock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return data;
+}
+
+/* sysfs attributes */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sht3x_data *data = sht3x_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temperature);
+}
+
+static ssize_t humidity1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sht3x_data *data = sht3x_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%u\n", data->humidity);
+}
+
+/*
+ * limits_update must only be called from probe or with data_lock held
+ */
+static int limits_update(struct sht3x_data *data)
+{
+ int ret;
+ u8 index;
+ int temperature;
+ u32 humidity;
+ u16 raw;
+ char buffer[SHT3X_RESPONSE_LENGTH];
+ const struct sht3x_limit_commands *commands;
+ struct i2c_client *client = data->client;
+
+ for (index = 0; index < SHT3X_NUM_LIMIT_CMD; index++) {
+ commands = &limit_commands[index];
+ ret = sht3x_read_from_command(client, data,
+ commands->read_command, buffer,
+ SHT3X_RESPONSE_LENGTH, 0);
+
+ if (ret)
+ return ret;
+
+ raw = be16_to_cpup((__be16 *)buffer);
+ temperature = sht3x_extract_temperature((raw & 0x01ff) << 7);
+ humidity = sht3x_extract_humidity(raw & 0xfe00);
+ data->temperature_limits[index] = temperature;
+ data->humidity_limits[index] = humidity;
+ }
+
+ return ret;
+}
+
+static ssize_t temp1_limit_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ u8 index = to_sensor_dev_attr(attr)->index;
+ int temperature_limit = data->temperature_limits[index];
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", temperature_limit);
+}
+
+static ssize_t humidity1_limit_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ u8 index = to_sensor_dev_attr(attr)->index;
+ u32 humidity_limit = data->humidity_limits[index];
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", humidity_limit);
+}
+
+/*
+ * limit_store must only be called with data_lock held
+ */
+static size_t limit_store(struct device *dev,
+ size_t count,
+ u8 index,
+ int temperature,
+ u32 humidity)
+{
+ char buffer[SHT3X_CMD_LENGTH + SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
+ char *position = buffer;
+ int ret;
+ u16 raw;
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ const struct sht3x_limit_commands *commands;
+
+ commands = &limit_commands[index];
+
+ memcpy(position, commands->write_command, SHT3X_CMD_LENGTH);
+ position += SHT3X_CMD_LENGTH;
+ /*
+ * ST = (T + 45) / 175 * 2^16
+ * SRH = RH / 100 * 2^16
+ * adapted for fixed point arithmetic and packed the same as
+ * in limit_show()
+ */
+ raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7);
+ raw |= ((humidity * 42950) >> 16) & 0xfe00;
+
+ *((__be16 *)position) = cpu_to_be16(raw);
+ position += SHT3X_WORD_LEN;
+ *position = crc8(sht3x_crc8_table,
+ position - SHT3X_WORD_LEN,
+ SHT3X_WORD_LEN,
+ SHT3X_CRC8_INIT);
+
+ mutex_lock(&data->i2c_lock);
+ ret = i2c_master_send(client, buffer, sizeof(buffer));
+ mutex_unlock(&data->i2c_lock);
+
+ if (ret != sizeof(buffer))
+ return ret < 0 ? ret : -EIO;
+
+ data->temperature_limits[index] = temperature;
+ data->humidity_limits[index] = humidity;
+ return count;
+}
+
+static ssize_t temp1_limit_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int temperature;
+ int ret;
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ u8 index = to_sensor_dev_attr(attr)->index;
+
+ ret = kstrtoint(buf, 0, &temperature);
+ if (ret)
+ return ret;
+
+ temperature = clamp_val(temperature, SHT3X_MIN_TEMPERATURE,
+ SHT3X_MAX_TEMPERATURE);
+ mutex_lock(&data->data_lock);
+ ret = limit_store(dev, count, index, temperature,
+ data->humidity_limits[index]);
+ mutex_unlock(&data->data_lock);
+
+ return ret;
+}
+
+static ssize_t humidity1_limit_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ u32 humidity;
+ int ret;
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ u8 index = to_sensor_dev_attr(attr)->index;
+
+ ret = kstrtou32(buf, 0, &humidity);
+ if (ret)
+ return ret;
+
+ humidity = clamp_val(humidity, SHT3X_MIN_HUMIDITY, SHT3X_MAX_HUMIDITY);
+ mutex_lock(&data->data_lock);
+ ret = limit_store(dev, count, index, data->temperature_limits[index],
+ humidity);
+ mutex_unlock(&data->data_lock);
+
+ return ret;
+}
+
+static void sht3x_select_command(struct sht3x_data *data)
+{
+ /*
+ * In blocking mode (clock stretching mode) the I2C bus
+ * is blocked for other traffic, thus the call to i2c_master_recv()
+ * will wait until the data is ready. For non blocking mode, we
+ * have to wait ourselves.
+ */
+ if (data->mode > 0) {
+ data->command = sht3x_cmd_measure_periodic_mode;
+ data->wait_time = 0;
+ } else if (data->setup.blocking_io) {
+ data->command = data->setup.high_precision ?
+ sht3x_cmd_measure_blocking_hpm :
+ sht3x_cmd_measure_blocking_lpm;
+ data->wait_time = 0;
+ } else {
+ if (data->setup.high_precision) {
+ data->command = sht3x_cmd_measure_nonblocking_hpm;
+ data->wait_time = SHT3X_NONBLOCKING_WAIT_TIME_HPM;
+ } else {
+ data->command = sht3x_cmd_measure_nonblocking_lpm;
+ data->wait_time = SHT3X_NONBLOCKING_WAIT_TIME_LPM;
+ }
+ }
+}
+
+static int status_register_read(struct device *dev,
+ struct device_attribute *attr,
+ char *buffer, int length)
+{
+ int ret;
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ ret = sht3x_read_from_command(client, data, sht3x_cmd_read_status_reg,
+ buffer, length, 0);
+
+ return ret;
+}
+
+static ssize_t temp1_alarm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
+ int ret;
+
+ ret = status_register_read(dev, attr, buffer,
+ SHT3X_WORD_LEN + SHT3X_CRC8_LEN);
+ if (ret)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x04));
+}
+
+static ssize_t humidity1_alarm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
+ int ret;
+
+ ret = status_register_read(dev, attr, buffer,
+ SHT3X_WORD_LEN + SHT3X_CRC8_LEN);
+ if (ret)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x08));
+}
+
+static ssize_t heater_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
+ int ret;
+
+ ret = status_register_read(dev, attr, buffer,
+ SHT3X_WORD_LEN + SHT3X_CRC8_LEN);
+ if (ret)
+ return ret;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x20));
+}
+
+static ssize_t heater_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+ bool status;
+
+ ret = kstrtobool(buf, &status);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->i2c_lock);
+
+ if (status)
+ ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_on,
+ SHT3X_CMD_LENGTH);
+ else
+ ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_off,
+ SHT3X_CMD_LENGTH);
+
+ mutex_unlock(&data->i2c_lock);
+
+ return ret;
+}
+
+static ssize_t update_interval_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sht3x_data *data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ mode_to_update_interval[data->mode]);
+}
+
+static ssize_t update_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ u16 update_interval;
+ u8 mode;
+ int ret;
+ const char *command;
+ struct sht3x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ ret = kstrtou16(buf, 0, &update_interval);
+ if (ret)
+ return ret;
+
+ mode = get_mode_from_update_interval(update_interval);
+
+ mutex_lock(&data->data_lock);
+ /* mode did not change */
+ if (mode == data->mode) {
+ mutex_unlock(&data->data_lock);
+ return count;
+ }
+
+ mutex_lock(&data->i2c_lock);
+ /*
+ * Abort periodic measure mode.
+ * To do any changes to the configuration while in periodic mode, we
+ * have to send a break command to the sensor, which then falls back
+ * to single shot (mode = 0).
+ */
+ if (data->mode > 0) {
+ ret = i2c_master_send(client, sht3x_cmd_break,
+ SHT3X_CMD_LENGTH);
+ if (ret != SHT3X_CMD_LENGTH)
+ goto out;
+ data->mode = 0;
+ }
+
+ if (mode > 0) {
+ if (data->setup.high_precision)
+ command = periodic_measure_commands_hpm[mode - 1];
+ else
+ command = periodic_measure_commands_lpm[mode - 1];
+
+ /* select mode */
+ ret = i2c_master_send(client, command, SHT3X_CMD_LENGTH);
+ if (ret != SHT3X_CMD_LENGTH)
+ goto out;
+ }
+
+ /* select mode and command */
+ data->mode = mode;
+ sht3x_select_command(data);
+
+out:
+ mutex_unlock(&data->i2c_lock);
+ mutex_unlock(&data->data_lock);
+ if (ret != SHT3X_CMD_LENGTH)
+ return ret < 0 ? ret : -EIO;
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp1_input, 0);
+static SENSOR_DEVICE_ATTR_RO(humidity1_input, humidity1_input, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp1_limit, limit_max);
+static SENSOR_DEVICE_ATTR_RW(humidity1_max, humidity1_limit, limit_max);
+static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp1_limit, limit_max_hyst);
+static SENSOR_DEVICE_ATTR_RW(humidity1_max_hyst, humidity1_limit,
+ limit_max_hyst);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp1_limit, limit_min);
+static SENSOR_DEVICE_ATTR_RW(humidity1_min, humidity1_limit, limit_min);
+static SENSOR_DEVICE_ATTR_RW(temp1_min_hyst, temp1_limit, limit_min_hyst);
+static SENSOR_DEVICE_ATTR_RW(humidity1_min_hyst, humidity1_limit,
+ limit_min_hyst);
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, temp1_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(humidity1_alarm, humidity1_alarm, 0);
+static SENSOR_DEVICE_ATTR_RW(heater_enable, heater_enable, 0);
+static SENSOR_DEVICE_ATTR_RW(update_interval, update_interval, 0);
+
+static struct attribute *sht3x_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_humidity1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_humidity1_max.dev_attr.attr,
+ &sensor_dev_attr_humidity1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_humidity1_min.dev_attr.attr,
+ &sensor_dev_attr_humidity1_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_humidity1_alarm.dev_attr.attr,
+ &sensor_dev_attr_heater_enable.dev_attr.attr,
+ &sensor_dev_attr_update_interval.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *sts3x_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(sht3x);
+ATTRIBUTE_GROUPS(sts3x);
+
+static const struct i2c_device_id sht3x_ids[];
+
+static int sht3x_probe(struct i2c_client *client)
+{
+ int ret;
+ struct sht3x_data *data;
+ struct device *hwmon_dev;
+ struct i2c_adapter *adap = client->adapter;
+ struct device *dev = &client->dev;
+ const struct attribute_group **attribute_groups;
+
+ /*
+ * we require full i2c support since the sht3x uses multi-byte read and
+ * writes as well as multi-byte commands which are not supported by
+ * the smbus protocol
+ */
+ if (!i2c_check_functionality(adap, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ ret = i2c_master_send(client, sht3x_cmd_clear_status_reg,
+ SHT3X_CMD_LENGTH);
+ if (ret != SHT3X_CMD_LENGTH)
+ return ret < 0 ? ret : -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->setup.blocking_io = false;
+ data->setup.high_precision = true;
+ data->mode = 0;
+ data->last_update = jiffies - msecs_to_jiffies(3000);
+ data->client = client;
+ crc8_populate_msb(sht3x_crc8_table, SHT3X_CRC8_POLYNOMIAL);
+
+ if (client->dev.platform_data)
+ data->setup = *(struct sht3x_platform_data *)dev->platform_data;
+
+ sht3x_select_command(data);
+
+ mutex_init(&data->i2c_lock);
+ mutex_init(&data->data_lock);
+
+ /*
+ * An attempt to read limits register too early
+ * causes a NACK response from the chip.
+ * Waiting for an empirical delay of 500 us solves the issue.
+ */
+ usleep_range(500, 600);
+
+ ret = limits_update(data);
+ if (ret)
+ return ret;
+
+ if (i2c_match_id(sht3x_ids, client)->driver_data == sts3x)
+ attribute_groups = sts3x_groups;
+ else
+ attribute_groups = sht3x_groups;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name,
+ data,
+ attribute_groups);
+
+ if (IS_ERR(hwmon_dev))
+ dev_dbg(dev, "unable to register hwmon device\n");
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* device ID table */
+static const struct i2c_device_id sht3x_ids[] = {
+ {"sht3x", sht3x},
+ {"sts3x", sts3x},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, sht3x_ids);
+
+static struct i2c_driver sht3x_i2c_driver = {
+ .driver.name = "sht3x",
+ .probe_new = sht3x_probe,
+ .id_table = sht3x_ids,
+};
+
+module_i2c_driver(sht3x_i2c_driver);
+
+MODULE_AUTHOR("David Frey <david.frey@sensirion.com>");
+MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");
+MODULE_DESCRIPTION("Sensirion SHT3x humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c
new file mode 100644
index 000000000..13ac2d8f2
--- /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
new file mode 100644
index 000000000..18546ebc8
--- /dev/null
+++ b/drivers/hwmon/shtc1.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Sensirion SHTC1 humidity and temperature sensor driver
+ *
+ * Copyright (C) 2014 Sensirion AG, Switzerland
+ * Author: Johannes Winkelmann <johannes.winkelmann@sensirion.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#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 };
+static const unsigned char shtc1_cmd_measure_nonblocking_hpm[] = { 0x78, 0x66 };
+
+/* commands (low precision mode) */
+static const unsigned char shtc1_cmd_measure_blocking_lpm[] = { 0x64, 0x58 };
+static const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c };
+
+/* command for reading the ID register */
+static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 };
+
+/*
+ * constants for reading the ID register
+ * SHTC1: 0x0007 with mask 0x003f
+ * SHTW1: 0x0007 with mask 0x003f
+ * SHTC3: 0x0807 with mask 0x083f
+ */
+#define SHTC3_ID 0x0807
+#define SHTC3_ID_MASK 0x083f
+#define SHTC1_ID 0x0007
+#define SHTC1_ID_MASK 0x003f
+
+/* delays for non-blocking i2c commands, both in us */
+#define SHTC1_NONBLOCKING_WAIT_TIME_HPM 14400
+#define SHTC1_NONBLOCKING_WAIT_TIME_LPM 1000
+#define SHTC3_NONBLOCKING_WAIT_TIME_HPM 12100
+#define SHTC3_NONBLOCKING_WAIT_TIME_LPM 800
+
+#define SHTC1_CMD_LENGTH 2
+#define SHTC1_RESPONSE_LENGTH 6
+
+enum shtcx_chips {
+ shtc1,
+ shtc3,
+};
+
+struct shtc1_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ const unsigned char *command;
+ unsigned int nonblocking_wait_time; /* in us */
+
+ struct shtc1_platform_data setup;
+ enum shtcx_chips chip;
+
+ int temperature; /* 1000 * temperature in dgr C */
+ int humidity; /* 1000 * relative humidity in %RH */
+};
+
+static int shtc1_update_values(struct i2c_client *client,
+ struct shtc1_data *data,
+ char *buf, int bufsize)
+{
+ int ret = i2c_master_send(client, data->command, SHTC1_CMD_LENGTH);
+ if (ret != SHTC1_CMD_LENGTH) {
+ dev_err(&client->dev, "failed to send command: %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ /*
+ * In blocking mode (clock stretching mode) the I2C bus
+ * is blocked for other traffic, thus the call to i2c_master_recv()
+ * will wait until the data is ready. For non blocking mode, we
+ * have to wait ourselves.
+ */
+ if (!data->setup.blocking_io)
+ usleep_range(data->nonblocking_wait_time,
+ data->nonblocking_wait_time + 1000);
+
+ ret = i2c_master_recv(client, buf, bufsize);
+ if (ret != bufsize) {
+ dev_err(&client->dev, "failed to read values: %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+/* sysfs attributes */
+static struct shtc1_data *shtc1_update_client(struct device *dev)
+{
+ struct shtc1_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ unsigned char buf[SHTC1_RESPONSE_LENGTH];
+ int val;
+ int ret = 0;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
+ ret = shtc1_update_values(client, data, buf, sizeof(buf));
+ if (ret)
+ goto out;
+
+ /*
+ * From datasheet:
+ * T = -45 + 175 * ST / 2^16
+ * RH = 100 * SRH / 2^16
+ *
+ * Adapted for integer fixed point (3 digit) arithmetic.
+ */
+ val = be16_to_cpup((__be16 *)buf);
+ data->temperature = ((21875 * val) >> 13) - 45000;
+ val = be16_to_cpup((__be16 *)(buf + 3));
+ data->humidity = ((12500 * val) >> 13);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+out:
+ mutex_unlock(&data->update_lock);
+
+ return ret == 0 ? data : ERR_PTR(ret);
+}
+
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct shtc1_data *data = shtc1_update_client(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->temperature);
+}
+
+static ssize_t humidity1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct shtc1_data *data = shtc1_update_client(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", data->humidity);
+}
+
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RO(humidity1_input);
+
+static struct attribute *shtc1_attrs[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_humidity1_input.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(shtc1);
+
+static void shtc1_select_command(struct shtc1_data *data)
+{
+ if (data->setup.high_precision) {
+ data->command = data->setup.blocking_io ?
+ shtc1_cmd_measure_blocking_hpm :
+ shtc1_cmd_measure_nonblocking_hpm;
+ data->nonblocking_wait_time = (data->chip == shtc1) ?
+ SHTC1_NONBLOCKING_WAIT_TIME_HPM :
+ SHTC3_NONBLOCKING_WAIT_TIME_HPM;
+ } else {
+ data->command = data->setup.blocking_io ?
+ shtc1_cmd_measure_blocking_lpm :
+ shtc1_cmd_measure_nonblocking_lpm;
+ data->nonblocking_wait_time = (data->chip == shtc1) ?
+ SHTC1_NONBLOCKING_WAIT_TIME_LPM :
+ SHTC3_NONBLOCKING_WAIT_TIME_LPM;
+ }
+}
+
+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 = 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");
+ return -ENODEV;
+ }
+
+ ret = i2c_master_send(client, shtc1_cmd_read_id_reg, SHTC1_CMD_LENGTH);
+ if (ret != SHTC1_CMD_LENGTH) {
+ dev_err(dev, "could not send read_id_reg command: %d\n", ret);
+ return ret < 0 ? ret : -ENODEV;
+ }
+ ret = i2c_master_recv(client, id_reg_buf, sizeof(id_reg_buf));
+ if (ret != sizeof(id_reg_buf)) {
+ dev_err(dev, "could not read ID register: %d\n", ret);
+ return -ENODEV;
+ }
+
+ id_reg = be16_to_cpup((__be16 *)id_reg_buf);
+ if (chip == shtc3) {
+ if ((id_reg & SHTC3_ID_MASK) != SHTC3_ID) {
+ dev_err(dev, "SHTC3 ID register does not match\n");
+ return -ENODEV;
+ }
+ } else if ((id_reg & SHTC1_ID_MASK) != SHTC1_ID) {
+ dev_err(dev, "SHTC1 ID register does not match\n");
+ return -ENODEV;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->setup.blocking_io = false;
+ data->setup.high_precision = true;
+ data->client = client;
+ data->chip = chip;
+
+ 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);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name,
+ data,
+ shtc1_groups);
+ if (IS_ERR(hwmon_dev))
+ dev_dbg(dev, "unable to register hwmon device\n");
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+/* device ID table */
+static const struct i2c_device_id shtc1_id[] = {
+ { "shtc1", shtc1 },
+ { "shtw1", shtc1 },
+ { "shtc3", shtc3 },
+ { }
+};
+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",
+ .of_match_table = shtc1_of_match,
+ },
+ .probe_new = shtc1_probe,
+ .id_table = shtc1_id,
+};
+
+module_i2c_driver(shtc1_i2c_driver);
+
+MODULE_AUTHOR("Johannes Winkelmann <johannes.winkelmann@sensirion.com>");
+MODULE_DESCRIPTION("Sensirion SHTC1 humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c
new file mode 100644
index 000000000..b0b05fd12
--- /dev/null
+++ b/drivers/hwmon/sis5595.c
@@ -0,0 +1,908 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * sis5595.c - Part of lm_sensors, Linux kernel modules
+ * for hardware monitoring
+ *
+ * Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
+ * Kyösti Mälkki <kmalkki@cc.hut.fi>, and
+ * Mark D. Studebaker <mdsxyz123@yahoo.com>
+ * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
+ * the help of Jean Delvare <jdelvare@suse.de>
+ */
+
+/*
+ * SiS southbridge has a LM78-like chip integrated on the same IC.
+ * This driver is a customized copy of lm78.c
+ *
+ * Supports following revisions:
+ * Version PCI ID PCI Revision
+ * 1 1039/0008 AF or less
+ * 2 1039/0008 B0 or greater
+ *
+ * Note: these chips contain a 0008 device which is incompatible with the
+ * 5595. We recognize these by the presence of the listed
+ * "blacklist" PCI ID and refuse to load.
+ *
+ * NOT SUPPORTED PCI ID BLACKLIST PCI ID
+ * 540 0008 0540
+ * 550 0008 0550
+ * 5513 0008 5511
+ * 5581 0008 5597
+ * 5582 0008 5597
+ * 5597 0008 5597
+ * 5598 0008 5597/5598
+ * 630 0008 0630
+ * 645 0008 0645
+ * 730 0008 0730
+ * 735 0008 0735
+ */
+
+#define DRIVER_NAME "sis5595"
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+/*
+ * If force_addr is set to anything different from 0, we forcibly enable
+ * the device at the given address.
+ */
+static u16 force_addr;
+module_param(force_addr, ushort, 0);
+MODULE_PARM_DESC(force_addr,
+ "Initialize the base address of the sensors");
+
+static struct platform_device *pdev;
+
+/* Many SIS5595 constants specified below */
+
+/* Length of ISA address segment */
+#define SIS5595_EXTENT 8
+/* PCI Config Registers */
+#define SIS5595_BASE_REG 0x68
+#define SIS5595_PIN_REG 0x7A
+#define SIS5595_ENABLE_REG 0x7B
+
+/* Where are the ISA address/data registers relative to the base address */
+#define SIS5595_ADDR_REG_OFFSET 5
+#define SIS5595_DATA_REG_OFFSET 6
+
+/* The SIS5595 registers */
+#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define SIS5595_REG_IN(nr) (0x20 + (nr))
+
+#define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr))
+#define SIS5595_REG_FAN(nr) (0x28 + (nr))
+
+/*
+ * On the first version of the chip, the temp registers are separate.
+ * On the second version,
+ * TEMP pin is shared with IN4, configured in PCI register 0x7A.
+ * The registers are the same as well.
+ * OVER and HYST are really MAX and MIN.
+ */
+
+#define REV2MIN 0xb0
+#define SIS5595_REG_TEMP (((data->revision) >= REV2MIN) ? \
+ SIS5595_REG_IN(4) : 0x27)
+#define SIS5595_REG_TEMP_OVER (((data->revision) >= REV2MIN) ? \
+ SIS5595_REG_IN_MAX(4) : 0x39)
+#define SIS5595_REG_TEMP_HYST (((data->revision) >= REV2MIN) ? \
+ SIS5595_REG_IN_MIN(4) : 0x3a)
+
+#define SIS5595_REG_CONFIG 0x40
+#define SIS5595_REG_ALARM1 0x41
+#define SIS5595_REG_ALARM2 0x42
+#define SIS5595_REG_FANDIV 0x47
+
+/*
+ * Conversions. Limit checking is only done on the TO_REG
+ * variants.
+ */
+
+/*
+ * IN: mV, (0V to 4.08V)
+ * REG: 16mV/bit
+ */
+static inline u8 IN_TO_REG(unsigned long val)
+{
+ unsigned long nval = clamp_val(val, 0, 4080);
+ return (nval + 8) / 16;
+}
+#define IN_FROM_REG(val) ((val) * 16)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm <= 0)
+ return 255;
+ if (rpm > 1350000)
+ return 1;
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+static inline int FAN_FROM_REG(u8 val, int div)
+{
+ return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div);
+}
+
+/*
+ * TEMP: mC (-54.12C to +157.53C)
+ * REG: 0.83C/bit + 52.12, two's complement
+ */
+static inline int TEMP_FROM_REG(s8 val)
+{
+ return val * 830 + 52120;
+}
+static inline s8 TEMP_TO_REG(long val)
+{
+ int nval = clamp_val(val, -54120, 157530) ;
+ return nval < 0 ? (nval - 5212 - 415) / 830 : (nval - 5212 + 415) / 830;
+}
+
+/*
+ * FAN DIV: 1, 2, 4, or 8 (defaults to 2)
+ * REG: 0, 1, 2, or 3 (respectively) (defaults to 1)
+ */
+static inline u8 DIV_TO_REG(int val)
+{
+ return val == 8 ? 3 : val == 4 ? 2 : val == 1 ? 0 : 1;
+}
+#define DIV_FROM_REG(val) (1 << (val))
+
+/*
+ * For each registered chip, we need to keep some data in memory.
+ * The structure is dynamically allocated.
+ */
+struct sis5595_data {
+ unsigned short addr;
+ const char *name;
+ struct device *hwmon_dev;
+ struct mutex lock;
+
+ struct mutex update_lock;
+ 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 */
+
+ u8 in[5]; /* Register value */
+ u8 in_max[5]; /* Register value */
+ u8 in_min[5]; /* Register value */
+ u8 fan[2]; /* Register value */
+ u8 fan_min[2]; /* Register value */
+ s8 temp; /* Register value */
+ s8 temp_over; /* Register value */
+ s8 temp_hyst; /* Register value */
+ u8 fan_div[2]; /* Register encoding, shifted right */
+ u16 alarms; /* Register encoding, combined */
+};
+
+static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */
+
+/* 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);
+}
+
+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,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
+}
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sis5595_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[nr] = IN_TO_REG(val);
+ sis5595_write_value(data, SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sis5595_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_max[nr] = IN_TO_REG(val);
+ sis5595_write_value(data, SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
+ 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);
+
+/* Temperature */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
+}
+
+static ssize_t temp1_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
+}
+
+static ssize_t temp1_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sis5595_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->temp_over = TEMP_TO_REG(val);
+ sis5595_write_value(data, SIS5595_REG_TEMP_OVER, data->temp_over);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp1_max_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
+}
+
+static ssize_t temp1_max_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sis5595_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->temp_hyst = TEMP_TO_REG(val);
+ sis5595_write_value(data, SIS5595_REG_TEMP_HYST, data->temp_hyst);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RW(temp1_max);
+static DEVICE_ATTR_RW(temp1_max_hyst);
+
+/* 2 Fans */
+static ssize_t fan_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_min_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sis5595_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ 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]));
+ sis5595_write_value(data, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t fan_div_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sis5595_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ unsigned long min;
+ int reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ min = FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+ reg = sis5595_read_value(data, SIS5595_REG_FANDIV);
+
+ switch (val) {
+ case 1:
+ data->fan_div[nr] = 0;
+ break;
+ case 2:
+ data->fan_div[nr] = 1;
+ break;
+ case 4:
+ data->fan_div[nr] = 2;
+ break;
+ case 8:
+ data->fan_div[nr] = 3;
+ break;
+ default:
+ dev_err(dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ switch (nr) {
+ case 0:
+ reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
+ break;
+ case 1:
+ reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
+ break;
+ }
+ sis5595_write_value(data, SIS5595_REG_FANDIV, reg);
+ data->fan_min[nr] =
+ FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ sis5595_write_value(data, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
+ 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_RW(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_RW(fan2_div, fan_div, 1);
+
+/* Alarms */
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sis5595_data *data = sis5595_update_device(dev);
+ int nr = to_sensor_dev_attr(da)->index;
+ return sprintf(buf, "%u\n", (data->alarms >> nr) & 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, 15);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 15);
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sis5595_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", data->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *sis5595_attributes[] = {
+ &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_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_name.attr,
+ NULL
+};
+
+static const struct attribute_group sis5595_group = {
+ .attrs = sis5595_attributes,
+};
+
+static struct attribute *sis5595_attributes_in4[] = {
+ &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,
+ NULL
+};
+
+static const struct attribute_group sis5595_group_in4 = {
+ .attrs = sis5595_attributes_in4,
+};
+
+static struct attribute *sis5595_attributes_temp1[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ NULL
+};
+
+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)
+{
+ int err = 0;
+ int i;
+ struct sis5595_data *data;
+ struct resource *res;
+ char val;
+
+ /* Reserve the ISA region */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start, SIS5595_EXTENT,
+ DRIVER_NAME))
+ return -EBUSY;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct sis5595_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
+ data->addr = res->start;
+ data->name = DRIVER_NAME;
+ platform_set_drvdata(pdev, data);
+
+ /*
+ * Check revision and pin registers to determine whether 4 or 5 voltages
+ */
+ data->revision = s_bridge->revision;
+ /* 4 voltages, 1 temp */
+ data->maxins = 3;
+ if (data->revision >= REV2MIN) {
+ pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val);
+ if (!(val & 0x80))
+ /* 5 voltages, no temps */
+ data->maxins = 4;
+ }
+
+ /* Initialize the SIS5595 chip */
+ sis5595_init_device(data);
+
+ /* A few vars need to be filled upon startup */
+ for (i = 0; i < 2; i++) {
+ data->fan_min[i] = sis5595_read_value(data,
+ SIS5595_REG_FAN_MIN(i));
+ }
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group);
+ if (err)
+ return err;
+ if (data->maxins == 4) {
+ err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group_in4);
+ if (err)
+ goto exit_remove_files;
+ } else {
+ err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group_temp1);
+ if (err)
+ goto exit_remove_files;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ sysfs_remove_group(&pdev->dev.kobj, &sis5595_group);
+ sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4);
+ sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1);
+ return err;
+}
+
+static int sis5595_remove(struct platform_device *pdev)
+{
+ struct sis5595_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &sis5595_group);
+ sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4);
+ sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1);
+
+ return 0;
+}
+
+static const struct pci_device_id sis5595_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, sis5595_pci_ids);
+
+static int blacklist[] = {
+ PCI_DEVICE_ID_SI_540,
+ PCI_DEVICE_ID_SI_550,
+ PCI_DEVICE_ID_SI_630,
+ PCI_DEVICE_ID_SI_645,
+ PCI_DEVICE_ID_SI_730,
+ PCI_DEVICE_ID_SI_735,
+ PCI_DEVICE_ID_SI_5511, /*
+ * 5513 chip has the 0008 device but
+ * that ID shows up in other chips so we
+ * use the 5511 ID for recognition
+ */
+ PCI_DEVICE_ID_SI_5597,
+ PCI_DEVICE_ID_SI_5598,
+ 0 };
+
+static int sis5595_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + SIS5595_EXTENT - 1,
+ .name = DRIVER_NAME,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc(DRIVER_NAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+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)
+{
+ u16 address;
+ u8 enable;
+ int *i;
+
+ for (i = blacklist; *i != 0; i++) {
+ struct pci_dev *d;
+ d = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
+ if (d) {
+ dev_err(&d->dev,
+ "Looked for SIS5595 but found unsupported device %.4x\n",
+ *i);
+ pci_dev_put(d);
+ return -ENODEV;
+ }
+ }
+
+ force_addr &= ~(SIS5595_EXTENT - 1);
+ if (force_addr) {
+ dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", force_addr);
+ pci_write_config_word(dev, SIS5595_BASE_REG, force_addr);
+ }
+
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_read_config_word(dev, SIS5595_BASE_REG, &address)) {
+ dev_err(&dev->dev, "Failed to read ISA address\n");
+ return -ENODEV;
+ }
+
+ address &= ~(SIS5595_EXTENT - 1);
+ if (!address) {
+ dev_err(&dev->dev,
+ "Base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+ return -ENODEV;
+ }
+ if (force_addr && address != force_addr) {
+ /* doesn't work for some chips? */
+ dev_err(&dev->dev, "Failed to force ISA address\n");
+ return -ENODEV;
+ }
+
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_read_config_byte(dev, SIS5595_ENABLE_REG, &enable)) {
+ dev_err(&dev->dev, "Failed to read enable register\n");
+ return -ENODEV;
+ }
+ if (!(enable & 0x80)) {
+ if ((PCIBIOS_SUCCESSFUL !=
+ pci_write_config_byte(dev, SIS5595_ENABLE_REG,
+ enable | 0x80))
+ || (PCIBIOS_SUCCESSFUL !=
+ pci_read_config_byte(dev, SIS5595_ENABLE_REG, &enable))
+ || (!(enable & 0x80))) {
+ /* doesn't work for some chips! */
+ dev_err(&dev->dev, "Failed to enable HWM device\n");
+ return -ENODEV;
+ }
+ }
+
+ if (platform_driver_register(&sis5595_driver)) {
+ dev_dbg(&dev->dev, "Failed to register sis5595 driver\n");
+ goto exit;
+ }
+
+ s_bridge = pci_dev_get(dev);
+ /* Sets global pdev as a side effect */
+ if (sis5595_device_add(address))
+ goto exit_unregister;
+
+ /*
+ * Always return failure here. This is to allow other drivers to bind
+ * to this pci device. We don't really want to have control over the
+ * pci device, we only wanted to read as few register values from it.
+ */
+ return -ENODEV;
+
+exit_unregister:
+ pci_dev_put(dev);
+ platform_driver_unregister(&sis5595_driver);
+exit:
+ return -ENODEV;
+}
+
+static struct pci_driver sis5595_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = sis5595_pci_ids,
+ .probe = sis5595_pci_probe,
+};
+
+static int __init sm_sis5595_init(void)
+{
+ return pci_register_driver(&sis5595_pci_driver);
+}
+
+static void __exit sm_sis5595_exit(void)
+{
+ pci_unregister_driver(&sis5595_pci_driver);
+ if (s_bridge != NULL) {
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&sis5595_driver);
+ pci_dev_put(s_bridge);
+ s_bridge = NULL;
+ }
+}
+
+MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_DESCRIPTION("SiS 5595 Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(sm_sis5595_init);
+module_exit(sm_sis5595_exit);
diff --git a/drivers/hwmon/sl28cpld-hwmon.c b/drivers/hwmon/sl28cpld-hwmon.c
new file mode 100644
index 000000000..9ce4899a8
--- /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
new file mode 100644
index 000000000..c36bdbe42
--- /dev/null
+++ b/drivers/hwmon/smm665.c
@@ -0,0 +1,706 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for SMM665 Power Controller / Monitor
+ *
+ * Copyright (C) 2010 Ericsson AB.
+ *
+ * This driver should also work for SMM465, SMM764, and SMM766, but is untested
+ * for those chips. Only monitoring functionality is implemented.
+ *
+ * Datasheets:
+ * http://www.summitmicro.com/prod_select/summary/SMM665/SMM665B_2089_20.pdf
+ * http://www.summitmicro.com/prod_select/summary/SMM766B/SMM766B_2122.pdf
+ */
+
+#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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+
+/* Internal reference voltage (VREF, x 1000 */
+#define SMM665_VREF_ADC_X1000 1250
+
+/* module parameters */
+static int vref = SMM665_VREF_ADC_X1000;
+module_param(vref, int, 0);
+MODULE_PARM_DESC(vref, "Reference voltage in mV");
+
+enum chips { smm465, smm665, smm665c, smm764, smm766 };
+
+/*
+ * ADC channel addresses
+ */
+#define SMM665_MISC16_ADC_DATA_A 0x00
+#define SMM665_MISC16_ADC_DATA_B 0x01
+#define SMM665_MISC16_ADC_DATA_C 0x02
+#define SMM665_MISC16_ADC_DATA_D 0x03
+#define SMM665_MISC16_ADC_DATA_E 0x04
+#define SMM665_MISC16_ADC_DATA_F 0x05
+#define SMM665_MISC16_ADC_DATA_VDD 0x06
+#define SMM665_MISC16_ADC_DATA_12V 0x07
+#define SMM665_MISC16_ADC_DATA_INT_TEMP 0x08
+#define SMM665_MISC16_ADC_DATA_AIN1 0x09
+#define SMM665_MISC16_ADC_DATA_AIN2 0x0a
+
+/*
+ * Command registers
+ */
+#define SMM665_MISC8_CMD_STS 0x80
+#define SMM665_MISC8_STATUS1 0x81
+#define SMM665_MISC8_STATUSS2 0x82
+#define SMM665_MISC8_IO_POLARITY 0x83
+#define SMM665_MISC8_PUP_POLARITY 0x84
+#define SMM665_MISC8_ADOC_STATUS1 0x85
+#define SMM665_MISC8_ADOC_STATUS2 0x86
+#define SMM665_MISC8_WRITE_PROT 0x87
+#define SMM665_MISC8_STS_TRACK 0x88
+
+/*
+ * Configuration registers and register groups
+ */
+#define SMM665_ADOC_ENABLE 0x0d
+#define SMM665_LIMIT_BASE 0x80 /* First limit register */
+
+/*
+ * Limit register bit masks
+ */
+#define SMM665_TRIGGER_RST 0x8000
+#define SMM665_TRIGGER_HEALTHY 0x4000
+#define SMM665_TRIGGER_POWEROFF 0x2000
+#define SMM665_TRIGGER_SHUTDOWN 0x1000
+#define SMM665_ADC_MASK 0x03ff
+
+#define smm665_is_critical(lim) ((lim) & (SMM665_TRIGGER_RST \
+ | SMM665_TRIGGER_POWEROFF \
+ | SMM665_TRIGGER_SHUTDOWN))
+/*
+ * Fault register bit definitions
+ * Values are merged from status registers 1/2,
+ * with status register 1 providing the upper 8 bits.
+ */
+#define SMM665_FAULT_A 0x0001
+#define SMM665_FAULT_B 0x0002
+#define SMM665_FAULT_C 0x0004
+#define SMM665_FAULT_D 0x0008
+#define SMM665_FAULT_E 0x0010
+#define SMM665_FAULT_F 0x0020
+#define SMM665_FAULT_VDD 0x0040
+#define SMM665_FAULT_12V 0x0080
+#define SMM665_FAULT_TEMP 0x0100
+#define SMM665_FAULT_AIN1 0x0200
+#define SMM665_FAULT_AIN2 0x0400
+
+/*
+ * I2C Register addresses
+ *
+ * The configuration register needs to be the configured base register.
+ * The command/status register address is derived from it.
+ */
+#define SMM665_REGMASK 0x78
+#define SMM665_CMDREG_BASE 0x48
+#define SMM665_CONFREG_BASE 0x50
+
+/*
+ * Equations given by chip manufacturer to calculate voltage/temperature values
+ * vref = Reference voltage on VREF_ADC pin (module parameter)
+ * adc = 10bit ADC value read back from registers
+ */
+
+/* Voltage A-F and VDD */
+#define SMM665_VMON_ADC_TO_VOLTS(adc) ((adc) * vref / 256)
+
+/* Voltage 12VIN */
+#define SMM665_12VIN_ADC_TO_VOLTS(adc) ((adc) * vref * 3 / 256)
+
+/* Voltage AIN1, AIN2 */
+#define SMM665_AIN_ADC_TO_VOLTS(adc) ((adc) * vref / 512)
+
+/* Temp Sensor */
+#define SMM665_TEMP_ADC_TO_CELSIUS(adc) (((adc) <= 511) ? \
+ ((int)(adc) * 1000 / 4) : \
+ (((int)(adc) - 0x400) * 1000 / 4))
+
+#define SMM665_NUM_ADC 11
+
+/*
+ * Chip dependent ADC conversion time, in uS
+ */
+#define SMM665_ADC_WAIT_SMM665 70
+#define SMM665_ADC_WAIT_SMM766 185
+
+struct smm665_data {
+ enum chips type;
+ int conversion_time; /* ADC conversion time */
+ struct i2c_client *client;
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+ u16 adc[SMM665_NUM_ADC]; /* adc values (raw) */
+ u16 faults; /* fault status */
+ /* The following values are in mV */
+ int critical_min_limit[SMM665_NUM_ADC];
+ int alarm_min_limit[SMM665_NUM_ADC];
+ int critical_max_limit[SMM665_NUM_ADC];
+ int alarm_max_limit[SMM665_NUM_ADC];
+ struct i2c_client *cmdreg;
+};
+
+/*
+ * smm665_read16()
+ *
+ * Read 16 bit value from <reg>, <reg+1>. Upper 8 bits are in <reg>.
+ */
+static int smm665_read16(struct i2c_client *client, int reg)
+{
+ int rv, val;
+
+ rv = i2c_smbus_read_byte_data(client, reg);
+ if (rv < 0)
+ return rv;
+ val = rv << 8;
+ rv = i2c_smbus_read_byte_data(client, reg + 1);
+ if (rv < 0)
+ return rv;
+ val |= rv;
+ return val;
+}
+
+/*
+ * Read adc value.
+ */
+static int smm665_read_adc(struct smm665_data *data, int adc)
+{
+ struct i2c_client *client = data->cmdreg;
+ int rv;
+ int radc;
+
+ /*
+ * Algorithm for reading ADC, per SMM665 datasheet
+ *
+ * {[S][addr][W][Ack]} {[offset][Ack]} {[S][addr][R][Nack]}
+ * [wait conversion time]
+ * {[S][addr][R][Ack]} {[datahi][Ack]} {[datalo][Ack][P]}
+ *
+ * To implement the first part of this exchange,
+ * do a full read transaction and expect a failure/Nack.
+ * This sets up the address pointer on the SMM665
+ * and starts the ADC conversion.
+ * Then do a two-byte read transaction.
+ */
+ rv = i2c_smbus_read_byte_data(client, adc << 3);
+ if (rv != -ENXIO) {
+ /*
+ * We expect ENXIO to reflect NACK
+ * (per Documentation/i2c/fault-codes.rst).
+ * Everything else is an error.
+ */
+ dev_dbg(&client->dev,
+ "Unexpected return code %d when setting ADC index", rv);
+ return (rv < 0) ? rv : -EIO;
+ }
+
+ udelay(data->conversion_time);
+
+ /*
+ * Now read two bytes.
+ *
+ * Neither i2c_smbus_read_byte() nor
+ * i2c_smbus_read_block_data() worked here,
+ * so use i2c_smbus_read_word_swapped() instead.
+ * We could also try to use i2c_master_recv(),
+ * but that is not always supported.
+ */
+ rv = i2c_smbus_read_word_swapped(client, 0);
+ if (rv < 0) {
+ dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv);
+ return rv;
+ }
+ /*
+ * Validate/verify readback adc channel (in bit 11..14).
+ */
+ radc = (rv >> 11) & 0x0f;
+ if (radc != adc) {
+ dev_dbg(&client->dev, "Unexpected RADC: Expected %d got %d",
+ adc, radc);
+ return -EIO;
+ }
+
+ return rv & SMM665_ADC_MASK;
+}
+
+static struct smm665_data *smm665_update_device(struct device *dev)
+{
+ struct smm665_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ struct smm665_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ int i, val;
+
+ /*
+ * read status registers
+ */
+ val = smm665_read16(client, SMM665_MISC8_STATUS1);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->faults = val;
+
+ /* Read adc registers */
+ for (i = 0; i < SMM665_NUM_ADC; i++) {
+ val = smm665_read_adc(data, i);
+ if (unlikely(val < 0)) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->adc[i] = val;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+/* Return converted value from given adc */
+static int smm665_convert(u16 adcval, int index)
+{
+ int val = 0;
+
+ switch (index) {
+ case SMM665_MISC16_ADC_DATA_12V:
+ val = SMM665_12VIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK);
+ break;
+
+ case SMM665_MISC16_ADC_DATA_VDD:
+ case SMM665_MISC16_ADC_DATA_A:
+ case SMM665_MISC16_ADC_DATA_B:
+ case SMM665_MISC16_ADC_DATA_C:
+ case SMM665_MISC16_ADC_DATA_D:
+ case SMM665_MISC16_ADC_DATA_E:
+ case SMM665_MISC16_ADC_DATA_F:
+ val = SMM665_VMON_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK);
+ break;
+
+ case SMM665_MISC16_ADC_DATA_AIN1:
+ case SMM665_MISC16_ADC_DATA_AIN2:
+ val = SMM665_AIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK);
+ break;
+
+ case SMM665_MISC16_ADC_DATA_INT_TEMP:
+ val = SMM665_TEMP_ADC_TO_CELSIUS(adcval & SMM665_ADC_MASK);
+ break;
+
+ default:
+ /* If we get here, the developer messed up */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ return val;
+}
+
+static int smm665_get_min(struct device *dev, int index)
+{
+ struct smm665_data *data = dev_get_drvdata(dev);
+
+ return data->alarm_min_limit[index];
+}
+
+static int smm665_get_max(struct device *dev, int index)
+{
+ struct smm665_data *data = dev_get_drvdata(dev);
+
+ return data->alarm_max_limit[index];
+}
+
+static int smm665_get_lcrit(struct device *dev, int index)
+{
+ struct smm665_data *data = dev_get_drvdata(dev);
+
+ return data->critical_min_limit[index];
+}
+
+static int smm665_get_crit(struct device *dev, int index)
+{
+ struct smm665_data *data = dev_get_drvdata(dev);
+
+ return data->critical_max_limit[index];
+}
+
+static ssize_t smm665_show_crit_alarm(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct smm665_data *data = smm665_update_device(dev);
+ int val = 0;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (data->faults & (1 << attr->index))
+ val = 1;
+
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t smm665_show_input(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct smm665_data *data = smm665_update_device(dev);
+ int adc = attr->index;
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = smm665_convert(data->adc[adc], adc);
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+#define SMM665_SHOW(what) \
+static ssize_t smm665_show_##what(struct device *dev, \
+ struct device_attribute *da, char *buf) \
+{ \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \
+ const int val = smm665_get_##what(dev, attr->index); \
+ return snprintf(buf, PAGE_SIZE, "%d\n", val); \
+}
+
+SMM665_SHOW(min);
+SMM665_SHOW(max);
+SMM665_SHOW(lcrit);
+SMM665_SHOW(crit);
+
+/*
+ * These macros are used below in constructing device attribute objects
+ * for use with sysfs_create_group() to make a sysfs device file
+ * for each register.
+ */
+
+#define SMM665_ATTR(name, type, cmd_idx) \
+ static SENSOR_DEVICE_ATTR(name##_##type, S_IRUGO, \
+ smm665_show_##type, NULL, cmd_idx)
+
+/* Construct a sensor_device_attribute structure for each register */
+
+/* Input voltages */
+SMM665_ATTR(in1, input, SMM665_MISC16_ADC_DATA_12V);
+SMM665_ATTR(in2, input, SMM665_MISC16_ADC_DATA_VDD);
+SMM665_ATTR(in3, input, SMM665_MISC16_ADC_DATA_A);
+SMM665_ATTR(in4, input, SMM665_MISC16_ADC_DATA_B);
+SMM665_ATTR(in5, input, SMM665_MISC16_ADC_DATA_C);
+SMM665_ATTR(in6, input, SMM665_MISC16_ADC_DATA_D);
+SMM665_ATTR(in7, input, SMM665_MISC16_ADC_DATA_E);
+SMM665_ATTR(in8, input, SMM665_MISC16_ADC_DATA_F);
+SMM665_ATTR(in9, input, SMM665_MISC16_ADC_DATA_AIN1);
+SMM665_ATTR(in10, input, SMM665_MISC16_ADC_DATA_AIN2);
+
+/* Input voltages min */
+SMM665_ATTR(in1, min, SMM665_MISC16_ADC_DATA_12V);
+SMM665_ATTR(in2, min, SMM665_MISC16_ADC_DATA_VDD);
+SMM665_ATTR(in3, min, SMM665_MISC16_ADC_DATA_A);
+SMM665_ATTR(in4, min, SMM665_MISC16_ADC_DATA_B);
+SMM665_ATTR(in5, min, SMM665_MISC16_ADC_DATA_C);
+SMM665_ATTR(in6, min, SMM665_MISC16_ADC_DATA_D);
+SMM665_ATTR(in7, min, SMM665_MISC16_ADC_DATA_E);
+SMM665_ATTR(in8, min, SMM665_MISC16_ADC_DATA_F);
+SMM665_ATTR(in9, min, SMM665_MISC16_ADC_DATA_AIN1);
+SMM665_ATTR(in10, min, SMM665_MISC16_ADC_DATA_AIN2);
+
+/* Input voltages max */
+SMM665_ATTR(in1, max, SMM665_MISC16_ADC_DATA_12V);
+SMM665_ATTR(in2, max, SMM665_MISC16_ADC_DATA_VDD);
+SMM665_ATTR(in3, max, SMM665_MISC16_ADC_DATA_A);
+SMM665_ATTR(in4, max, SMM665_MISC16_ADC_DATA_B);
+SMM665_ATTR(in5, max, SMM665_MISC16_ADC_DATA_C);
+SMM665_ATTR(in6, max, SMM665_MISC16_ADC_DATA_D);
+SMM665_ATTR(in7, max, SMM665_MISC16_ADC_DATA_E);
+SMM665_ATTR(in8, max, SMM665_MISC16_ADC_DATA_F);
+SMM665_ATTR(in9, max, SMM665_MISC16_ADC_DATA_AIN1);
+SMM665_ATTR(in10, max, SMM665_MISC16_ADC_DATA_AIN2);
+
+/* Input voltages lcrit */
+SMM665_ATTR(in1, lcrit, SMM665_MISC16_ADC_DATA_12V);
+SMM665_ATTR(in2, lcrit, SMM665_MISC16_ADC_DATA_VDD);
+SMM665_ATTR(in3, lcrit, SMM665_MISC16_ADC_DATA_A);
+SMM665_ATTR(in4, lcrit, SMM665_MISC16_ADC_DATA_B);
+SMM665_ATTR(in5, lcrit, SMM665_MISC16_ADC_DATA_C);
+SMM665_ATTR(in6, lcrit, SMM665_MISC16_ADC_DATA_D);
+SMM665_ATTR(in7, lcrit, SMM665_MISC16_ADC_DATA_E);
+SMM665_ATTR(in8, lcrit, SMM665_MISC16_ADC_DATA_F);
+SMM665_ATTR(in9, lcrit, SMM665_MISC16_ADC_DATA_AIN1);
+SMM665_ATTR(in10, lcrit, SMM665_MISC16_ADC_DATA_AIN2);
+
+/* Input voltages crit */
+SMM665_ATTR(in1, crit, SMM665_MISC16_ADC_DATA_12V);
+SMM665_ATTR(in2, crit, SMM665_MISC16_ADC_DATA_VDD);
+SMM665_ATTR(in3, crit, SMM665_MISC16_ADC_DATA_A);
+SMM665_ATTR(in4, crit, SMM665_MISC16_ADC_DATA_B);
+SMM665_ATTR(in5, crit, SMM665_MISC16_ADC_DATA_C);
+SMM665_ATTR(in6, crit, SMM665_MISC16_ADC_DATA_D);
+SMM665_ATTR(in7, crit, SMM665_MISC16_ADC_DATA_E);
+SMM665_ATTR(in8, crit, SMM665_MISC16_ADC_DATA_F);
+SMM665_ATTR(in9, crit, SMM665_MISC16_ADC_DATA_AIN1);
+SMM665_ATTR(in10, crit, SMM665_MISC16_ADC_DATA_AIN2);
+
+/* critical alarms */
+SMM665_ATTR(in1, crit_alarm, SMM665_FAULT_12V);
+SMM665_ATTR(in2, crit_alarm, SMM665_FAULT_VDD);
+SMM665_ATTR(in3, crit_alarm, SMM665_FAULT_A);
+SMM665_ATTR(in4, crit_alarm, SMM665_FAULT_B);
+SMM665_ATTR(in5, crit_alarm, SMM665_FAULT_C);
+SMM665_ATTR(in6, crit_alarm, SMM665_FAULT_D);
+SMM665_ATTR(in7, crit_alarm, SMM665_FAULT_E);
+SMM665_ATTR(in8, crit_alarm, SMM665_FAULT_F);
+SMM665_ATTR(in9, crit_alarm, SMM665_FAULT_AIN1);
+SMM665_ATTR(in10, crit_alarm, SMM665_FAULT_AIN2);
+
+/* Temperature */
+SMM665_ATTR(temp1, input, SMM665_MISC16_ADC_DATA_INT_TEMP);
+SMM665_ATTR(temp1, min, SMM665_MISC16_ADC_DATA_INT_TEMP);
+SMM665_ATTR(temp1, max, SMM665_MISC16_ADC_DATA_INT_TEMP);
+SMM665_ATTR(temp1, lcrit, SMM665_MISC16_ADC_DATA_INT_TEMP);
+SMM665_ATTR(temp1, crit, SMM665_MISC16_ADC_DATA_INT_TEMP);
+SMM665_ATTR(temp1, crit_alarm, SMM665_FAULT_TEMP);
+
+/*
+ * Finally, construct an array of pointers to members of the above objects,
+ * as required for sysfs_create_group()
+ */
+static struct attribute *smm665_attrs[] = {
+ &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_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in1_crit.dev_attr.attr,
+ &sensor_dev_attr_in1_crit_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_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in2_crit.dev_attr.attr,
+ &sensor_dev_attr_in2_crit_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_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in3_crit.dev_attr.attr,
+ &sensor_dev_attr_in3_crit_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_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in4_crit.dev_attr.attr,
+ &sensor_dev_attr_in4_crit_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_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in5_crit.dev_attr.attr,
+ &sensor_dev_attr_in5_crit_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in6_crit.dev_attr.attr,
+ &sensor_dev_attr_in6_crit_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in7_crit.dev_attr.attr,
+ &sensor_dev_attr_in7_crit_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in8_crit.dev_attr.attr,
+ &sensor_dev_attr_in8_crit_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in9_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in9_crit.dev_attr.attr,
+ &sensor_dev_attr_in9_crit_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in10_lcrit.dev_attr.attr,
+ &sensor_dev_attr_in10_crit.dev_attr.attr,
+ &sensor_dev_attr_in10_crit_alarm.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_lcrit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(smm665);
+
+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;
+ struct device *hwmon_dev;
+ int i, ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
+ | I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ if (i2c_smbus_read_byte_data(client, SMM665_ADOC_ENABLE) < 0)
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ data->client = client;
+ 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))
+ return PTR_ERR(data->cmdreg);
+
+ switch (data->type) {
+ case smm465:
+ case smm665:
+ data->conversion_time = SMM665_ADC_WAIT_SMM665;
+ break;
+ case smm665c:
+ case smm764:
+ case smm766:
+ data->conversion_time = SMM665_ADC_WAIT_SMM766;
+ break;
+ }
+
+ ret = -ENODEV;
+ if (i2c_smbus_read_byte_data(data->cmdreg, SMM665_MISC8_CMD_STS) < 0)
+ goto out_unregister;
+
+ /*
+ * Read limits.
+ *
+ * Limit registers start with register SMM665_LIMIT_BASE.
+ * Each channel uses 8 registers, providing four limit values
+ * per channel. Each limit value requires two registers, with the
+ * high byte in the first register and the low byte in the second
+ * register. The first two limits are under limit values, followed
+ * by two over limit values.
+ *
+ * Limit register order matches the ADC register order, so we use
+ * ADC register defines throughout the code to index limit registers.
+ *
+ * We save the first retrieved value both as "critical" and "alarm"
+ * value. The second value overwrites either the critical or the
+ * alarm value, depending on its configuration. This ensures that both
+ * critical and alarm values are initialized, even if both registers are
+ * configured as critical or non-critical.
+ */
+ for (i = 0; i < SMM665_NUM_ADC; i++) {
+ int val;
+
+ val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8);
+ if (unlikely(val < 0))
+ goto out_unregister;
+ data->critical_min_limit[i] = data->alarm_min_limit[i]
+ = smm665_convert(val, i);
+ val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 2);
+ if (unlikely(val < 0))
+ goto out_unregister;
+ if (smm665_is_critical(val))
+ data->critical_min_limit[i] = smm665_convert(val, i);
+ else
+ data->alarm_min_limit[i] = smm665_convert(val, i);
+ val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 4);
+ if (unlikely(val < 0))
+ goto out_unregister;
+ data->critical_max_limit[i] = data->alarm_max_limit[i]
+ = smm665_convert(val, i);
+ val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 6);
+ if (unlikely(val < 0))
+ goto out_unregister;
+ if (smm665_is_critical(val))
+ data->critical_max_limit[i] = smm665_convert(val, i);
+ else
+ data->alarm_max_limit[i] = smm665_convert(val, i);
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ smm665_groups);
+ if (IS_ERR(hwmon_dev)) {
+ ret = PTR_ERR(hwmon_dev);
+ goto out_unregister;
+ }
+
+ return 0;
+
+out_unregister:
+ i2c_unregister_device(data->cmdreg);
+ return ret;
+}
+
+static void smm665_remove(struct i2c_client *client)
+{
+ struct smm665_data *data = i2c_get_clientdata(client);
+
+ i2c_unregister_device(data->cmdreg);
+}
+
+static const struct i2c_device_id smm665_id[] = {
+ {"smm465", smm465},
+ {"smm665", smm665},
+ {"smm665c", smm665c},
+ {"smm764", smm764},
+ {"smm766", smm766},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, smm665_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver smm665_driver = {
+ .driver = {
+ .name = "smm665",
+ },
+ .probe_new = smm665_probe,
+ .remove = smm665_remove,
+ .id_table = smm665_id,
+};
+
+module_i2c_driver(smm665_driver);
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("SMM665 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c
new file mode 100644
index 000000000..c26d6eae0
--- /dev/null
+++ b/drivers/hwmon/smsc47b397.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * smsc47b397.c - Part of lm_sensors, Linux kernel modules
+ * for hardware monitoring
+ *
+ * Supports the SMSC LPC47B397-NC Super-I/O chip.
+ *
+ * Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com>
+ * Copyright (C) 2004 Utilitek Systems, Inc.
+ *
+ * derived in part from smsc47m1.c:
+ * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ * Copyright (C) 2004 Jean Delvare <jdelvare@suse.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.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/init.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static struct platform_device *pdev;
+
+#define DRVNAME "smsc47b397"
+
+/* Super-I/0 registers and commands */
+
+#define REG 0x2e /* The register to read/write */
+#define VAL 0x2f /* The value to read/write */
+
+static inline void superio_outb(int reg, int val)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+/* select superio logical device */
+static inline void superio_select(int ld)
+{
+ superio_outb(0x07, ld);
+}
+
+static inline int superio_enter(void)
+{
+ if (!request_muxed_region(REG, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x55, REG);
+ return 0;
+}
+
+static inline void superio_exit(void)
+{
+ outb(0xAA, REG);
+ release_region(REG, 2);
+}
+
+#define SUPERIO_REG_DEVID 0x20
+#define SUPERIO_REG_DEVREV 0x21
+#define SUPERIO_REG_BASE_MSB 0x60
+#define SUPERIO_REG_BASE_LSB 0x61
+#define SUPERIO_REG_LD8 0x08
+
+#define SMSC_EXTENT 0x02
+
+/* 0 <= nr <= 3 */
+static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
+#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)])
+
+/* 0 <= nr <= 3 */
+#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr))
+#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
+
+struct smsc47b397_data {
+ unsigned short addr;
+ struct mutex lock;
+
+ struct mutex update_lock;
+ unsigned long last_updated; /* in jiffies */
+ bool valid;
+
+ /* register values */
+ u16 fan[4];
+ u8 temp[4];
+};
+
+static int smsc47b397_read_value(struct smsc47b397_data *data, u8 reg)
+{
+ int res;
+
+ mutex_lock(&data->lock);
+ outb(reg, data->addr);
+ res = inb_p(data->addr + 1);
+ mutex_unlock(&data->lock);
+ return res;
+}
+
+static struct smsc47b397_data *smsc47b397_update_device(struct device *dev)
+{
+ struct smsc47b397_data *data = dev_get_drvdata(dev);
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ dev_dbg(dev, "starting device update...\n");
+
+ /* 4 temperature inputs, 4 fan inputs */
+ for (i = 0; i < 4; i++) {
+ data->temp[i] = smsc47b397_read_value(data,
+ SMSC47B397_REG_TEMP(i));
+
+ /* must read LSB first */
+ data->fan[i] = smsc47b397_read_value(data,
+ SMSC47B397_REG_FAN_LSB(i));
+ data->fan[i] |= smsc47b397_read_value(data,
+ SMSC47B397_REG_FAN_MSB(i)) << 8;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+
+ dev_dbg(dev, "... device update complete\n");
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * TEMP: 0.001C/bit (-128C to +127C)
+ * REG: 1C/bit, two's complement
+ */
+static int temp_from_reg(u8 reg)
+{
+ return (s8)reg * 1000;
+}
+
+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 smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index]));
+}
+
+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);
+
+/*
+ * FAN: 1 RPM/bit
+ * REG: count of 90kHz pulses / revolution
+ */
+static int fan_from_reg(u16 reg)
+{
+ if (reg == 0 || reg == 0xffff)
+ return 0;
+ return 90000 * 60 / reg;
+}
+
+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 smsc47b397_data *data = smsc47b397_update_device(dev);
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan[attr->index]));
+}
+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 struct attribute *smsc47b397_attrs[] = {
+ &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_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,
+
+ NULL
+};
+
+ATTRIBUTE_GROUPS(smsc47b397);
+
+static int smsc47b397_probe(struct platform_device *pdev);
+
+static struct platform_driver smsc47b397_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = smsc47b397_probe,
+};
+
+static int smsc47b397_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct smsc47b397_data *data;
+ struct device *hwmon_dev;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(dev, res->start, SMSC_EXTENT,
+ smsc47b397_driver.driver.name)) {
+ dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+ (unsigned long)res->start,
+ (unsigned long)res->start + SMSC_EXTENT - 1);
+ return -EBUSY;
+ }
+
+ data = devm_kzalloc(dev, sizeof(struct smsc47b397_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = res->start;
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, "smsc47b397",
+ data,
+ smsc47b397_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static int __init smsc47b397_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + SMSC_EXTENT - 1,
+ .name = DRVNAME,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int __init smsc47b397_find(void)
+{
+ u8 id, rev;
+ char *name;
+ unsigned short addr;
+ int err;
+
+ err = superio_enter();
+ if (err)
+ return err;
+
+ id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
+
+ switch (id) {
+ case 0x81:
+ name = "SCH5307-NS";
+ break;
+ case 0x6f:
+ name = "LPC47B397-NC";
+ break;
+ case 0x85:
+ case 0x8c:
+ name = "SCH5317";
+ break;
+ default:
+ superio_exit();
+ return -ENODEV;
+ }
+
+ rev = superio_inb(SUPERIO_REG_DEVREV);
+
+ superio_select(SUPERIO_REG_LD8);
+ addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
+ | superio_inb(SUPERIO_REG_BASE_LSB);
+
+ pr_info("found SMSC %s (base address 0x%04x, revision %u)\n",
+ name, addr, rev);
+
+ superio_exit();
+ return addr;
+}
+
+static int __init smsc47b397_init(void)
+{
+ unsigned short address;
+ int ret;
+
+ ret = smsc47b397_find();
+ if (ret < 0)
+ return ret;
+ address = ret;
+
+ ret = platform_driver_register(&smsc47b397_driver);
+ if (ret)
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ ret = smsc47b397_device_add(address);
+ if (ret)
+ goto exit_driver;
+
+ return 0;
+
+exit_driver:
+ platform_driver_unregister(&smsc47b397_driver);
+exit:
+ return ret;
+}
+
+static void __exit smsc47b397_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&smsc47b397_driver);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
+MODULE_DESCRIPTION("SMSC LPC47B397 driver");
+MODULE_LICENSE("GPL");
+
+module_init(smsc47b397_init);
+module_exit(smsc47b397_exit);
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c
new file mode 100644
index 000000000..37531b5c8
--- /dev/null
+++ b/drivers/hwmon/smsc47m1.c
@@ -0,0 +1,949 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * smsc47m1.c - Part of lm_sensors, Linux kernel modules
+ * for hardware monitoring
+ *
+ * Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
+ * LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
+ * Super-I/O chips.
+ *
+ * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ * Copyright (C) 2004-2007 Jean Delvare <jdelvare@suse.de>
+ * Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
+ * and Jean Delvare
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.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/init.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static struct platform_device *pdev;
+
+#define DRVNAME "smsc47m1"
+enum chips { smsc47m1, smsc47m2 };
+
+/* Super-I/0 registers and commands */
+
+#define REG 0x2e /* The register to read/write */
+#define VAL 0x2f /* The value to read/write */
+
+static inline void
+superio_outb(int reg, int val)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+/* logical device for fans is 0x0A */
+#define superio_select() superio_outb(0x07, 0x0A)
+
+static inline int
+superio_enter(void)
+{
+ if (!request_muxed_region(REG, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x55, REG);
+ return 0;
+}
+
+static inline void
+superio_exit(void)
+{
+ outb(0xAA, REG);
+ release_region(REG, 2);
+}
+
+#define SUPERIO_REG_ACT 0x30
+#define SUPERIO_REG_BASE 0x60
+#define SUPERIO_REG_DEVID 0x20
+#define SUPERIO_REG_DEVREV 0x21
+
+/* Logical device registers */
+
+#define SMSC_EXTENT 0x80
+
+/* nr is 0 or 1 in the macros below */
+#define SMSC47M1_REG_ALARM 0x04
+#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
+#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
+#define SMSC47M1_REG_FANDIV 0x58
+
+static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b };
+static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c };
+static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 };
+
+#define SMSC47M2_REG_ALARM6 0x09
+#define SMSC47M2_REG_TPIN1 0x38
+#define SMSC47M2_REG_TPIN2 0x37
+#define SMSC47M2_REG_TPIN3 0x2d
+#define SMSC47M2_REG_PPIN3 0x2c
+#define SMSC47M2_REG_FANDIV3 0x6a
+
+#define MIN_FROM_REG(reg, div) ((reg) >= 192 ? 0 : \
+ 983040 / ((192 - (reg)) * (div)))
+#define FAN_FROM_REG(reg, div, preload) ((reg) <= (preload) || (reg) == 255 ? \
+ 0 : \
+ 983040 / (((reg) - (preload)) * (div)))
+#define DIV_FROM_REG(reg) (1 << (reg))
+#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
+#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
+#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
+
+struct smsc47m1_data {
+ unsigned short addr;
+ const char *name;
+ enum chips type;
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ unsigned long last_updated; /* In jiffies */
+
+ u8 fan[3]; /* Register value */
+ u8 fan_preload[3]; /* Register value */
+ u8 fan_div[3]; /* Register encoding, shifted right */
+ u8 alarms; /* Register encoding */
+ u8 pwm[3]; /* Register value (bit 0 is disable) */
+};
+
+struct smsc47m1_sio_data {
+ enum chips type;
+ u8 activate; /* Remember initial device state */
+};
+
+static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
+{
+ return inb_p(data->addr + reg);
+}
+
+static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
+ u8 value)
+{
+ outb_p(value, data->addr + reg);
+}
+
+static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
+ int init)
+{
+ struct smsc47m1_data *data = dev_get_drvdata(dev);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
+ int i, fan_nr;
+ fan_nr = data->type == smsc47m2 ? 3 : 2;
+
+ for (i = 0; i < fan_nr; i++) {
+ data->fan[i] = smsc47m1_read_value(data,
+ SMSC47M1_REG_FAN[i]);
+ data->fan_preload[i] = smsc47m1_read_value(data,
+ SMSC47M1_REG_FAN_PRELOAD[i]);
+ data->pwm[i] = smsc47m1_read_value(data,
+ SMSC47M1_REG_PWM[i]);
+ }
+
+ i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = i >> 6;
+
+ data->alarms = smsc47m1_read_value(data,
+ SMSC47M1_REG_ALARM) >> 6;
+ /* Clear alarms if needed */
+ if (data->alarms)
+ smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
+
+ if (fan_nr >= 3) {
+ data->fan_div[2] = (smsc47m1_read_value(data,
+ SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
+ data->alarms |= (smsc47m1_read_value(data,
+ SMSC47M2_REG_ALARM6) & 0x40) >> 4;
+ /* Clear alarm if needed */
+ if (data->alarms & 0x04)
+ smsc47m1_write_value(data,
+ SMSC47M2_REG_ALARM6,
+ 0x40);
+ }
+
+ data->last_updated = jiffies;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+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 smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ int nr = attr->index;
+ /*
+ * This chip (stupidly) stops monitoring fan speed if PWM is
+ * enabled and duty cycle is 0%. This is fine if the monitoring
+ * and control concern the same fan, but troublesome if they are
+ * not (which could as well happen).
+ */
+ int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
+ FAN_FROM_REG(data->fan[nr],
+ DIV_FROM_REG(data->fan_div[nr]),
+ data->fan_preload[nr]);
+ return sprintf(buf, "%d\n", rpm);
+}
+
+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 smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ int nr = attr->index;
+ int rpm = MIN_FROM_REG(data->fan_preload[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+ return sprintf(buf, "%d\n", rpm);
+}
+
+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 smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
+}
+
+static ssize_t fan_alarm_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int bitnr = to_sensor_dev_attr(devattr)->index;
+ struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
+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 smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
+}
+
+static ssize_t pwm_en_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
+}
+
+static ssize_t alarms_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+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 smsc47m1_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ long rpmdiv;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
+
+ if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
+ smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
+ data->fan_preload[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * Note: we save and restore the fan minimum here, because its value is
+ * determined in part by the fan clock divider. This follows the principle
+ * of least surprise; the user doesn't expect the fan minimum to change just
+ * because the divider changed.
+ */
+static ssize_t fan_div_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 smsc47m1_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ long new_div;
+ int err;
+ long tmp;
+ u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
+
+ err = kstrtol(buf, 10, &new_div);
+ if (err)
+ return err;
+
+ if (new_div == old_div) /* No change */
+ return count;
+
+ mutex_lock(&data->update_lock);
+ switch (new_div) {
+ case 1:
+ data->fan_div[nr] = 0;
+ break;
+ case 2:
+ data->fan_div[nr] = 1;
+ break;
+ case 4:
+ data->fan_div[nr] = 2;
+ break;
+ case 8:
+ data->fan_div[nr] = 3;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ switch (nr) {
+ case 0:
+ case 1:
+ tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
+ & ~(0x03 << (4 + 2 * nr));
+ tmp |= data->fan_div[nr] << (4 + 2 * nr);
+ smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
+ break;
+ case 2:
+ tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
+ tmp |= data->fan_div[2] << 4;
+ smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
+ break;
+ default:
+ BUG();
+ }
+
+ /* Preserve fan min */
+ tmp = 192 - (old_div * (192 - data->fan_preload[nr])
+ + new_div / 2) / new_div;
+ data->fan_preload[nr] = clamp_val(tmp, 0, 191);
+ smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
+ data->fan_preload[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+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 smsc47m1_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] &= 0x81; /* Preserve additional bits */
+ data->pwm[nr] |= PWM_TO_REG(val);
+ smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
+ data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t pwm_en_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 smsc47m1_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] &= 0xFE; /* preserve the other bits */
+ data->pwm[nr] |= !val;
+ smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
+ data->pwm[nr]);
+ 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_RW(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, fan_alarm, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_en, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
+static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, fan_alarm, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_en, 1);
+static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
+static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2);
+static SENSOR_DEVICE_ATTR_RW(fan3_div, fan_div, 2);
+static SENSOR_DEVICE_ATTR_RO(fan3_alarm, fan_alarm, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_en, 2);
+
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t name_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct smsc47m1_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *smsc47m1_attributes_fan1[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group_fan1 = {
+ .attrs = smsc47m1_attributes_fan1,
+};
+
+static struct attribute *smsc47m1_attributes_fan2[] = {
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group_fan2 = {
+ .attrs = smsc47m1_attributes_fan2,
+};
+
+static struct attribute *smsc47m1_attributes_fan3[] = {
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group_fan3 = {
+ .attrs = smsc47m1_attributes_fan3,
+};
+
+static struct attribute *smsc47m1_attributes_pwm1[] = {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group_pwm1 = {
+ .attrs = smsc47m1_attributes_pwm1,
+};
+
+static struct attribute *smsc47m1_attributes_pwm2[] = {
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group_pwm2 = {
+ .attrs = smsc47m1_attributes_pwm2,
+};
+
+static struct attribute *smsc47m1_attributes_pwm3[] = {
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group_pwm3 = {
+ .attrs = smsc47m1_attributes_pwm3,
+};
+
+static struct attribute *smsc47m1_attributes[] = {
+ &dev_attr_alarms.attr,
+ &dev_attr_name.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group = {
+ .attrs = smsc47m1_attributes,
+};
+
+static int __init smsc47m1_find(struct smsc47m1_sio_data *sio_data)
+{
+ u8 val;
+ unsigned short addr;
+ int err;
+
+ err = superio_enter();
+ if (err)
+ return err;
+
+ val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
+
+ /*
+ * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
+ * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
+ * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
+ * can do much more besides (device id 0x60).
+ * The LPC47M997 is undocumented, but seems to be compatible with
+ * the LPC47M192, and has the same device id.
+ * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
+ * supports a 3rd fan, and the pin configuration registers are
+ * unfortunately different.
+ * The LPC47M233 has the same device id (0x6B) but is not compatible.
+ * We check the high bit of the device revision register to
+ * differentiate them.
+ */
+ switch (val) {
+ case 0x51:
+ pr_info("Found SMSC LPC47B27x\n");
+ sio_data->type = smsc47m1;
+ break;
+ case 0x59:
+ pr_info("Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
+ sio_data->type = smsc47m1;
+ break;
+ case 0x5F:
+ pr_info("Found SMSC LPC47M14x\n");
+ sio_data->type = smsc47m1;
+ break;
+ case 0x60:
+ pr_info("Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
+ sio_data->type = smsc47m1;
+ break;
+ case 0x6B:
+ if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
+ pr_debug("Found SMSC LPC47M233, unsupported\n");
+ superio_exit();
+ return -ENODEV;
+ }
+
+ pr_info("Found SMSC LPC47M292\n");
+ sio_data->type = smsc47m2;
+ break;
+ default:
+ superio_exit();
+ return -ENODEV;
+ }
+
+ superio_select();
+ addr = (superio_inb(SUPERIO_REG_BASE) << 8)
+ | superio_inb(SUPERIO_REG_BASE + 1);
+ if (addr == 0) {
+ pr_info("Device address not set, will not use\n");
+ superio_exit();
+ return -ENODEV;
+ }
+
+ /*
+ * Enable only if address is set (needed at least on the
+ * Compaq Presario S4000NX)
+ */
+ sio_data->activate = superio_inb(SUPERIO_REG_ACT);
+ if ((sio_data->activate & 0x01) == 0) {
+ pr_info("Enabling device\n");
+ superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
+ }
+
+ superio_exit();
+ return addr;
+}
+
+/* Restore device to its initial state */
+static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
+{
+ if ((sio_data->activate & 0x01) == 0) {
+ if (!superio_enter()) {
+ superio_select();
+ pr_info("Disabling device\n");
+ superio_outb(SUPERIO_REG_ACT, sio_data->activate);
+ superio_exit();
+ } else {
+ pr_warn("Failed to disable device\n");
+ }
+ }
+}
+
+#define CHECK 1
+#define REQUEST 2
+
+/*
+ * This function can be used to:
+ * - test for resource conflicts with ACPI
+ * - request the resources
+ * We only allocate the I/O ports we really need, to minimize the risk of
+ * conflicts with ACPI or with other drivers.
+ */
+static int __init smsc47m1_handle_resources(unsigned short address,
+ enum chips type, int action,
+ struct device *dev)
+{
+ static const u8 ports_m1[] = {
+ /* register, region length */
+ 0x04, 1,
+ 0x33, 4,
+ 0x56, 7,
+ };
+
+ static const u8 ports_m2[] = {
+ /* register, region length */
+ 0x04, 1,
+ 0x09, 1,
+ 0x2c, 2,
+ 0x35, 4,
+ 0x56, 7,
+ 0x69, 4,
+ };
+
+ int i, ports_size, err;
+ const u8 *ports;
+
+ switch (type) {
+ case smsc47m1:
+ default:
+ ports = ports_m1;
+ ports_size = ARRAY_SIZE(ports_m1);
+ break;
+ case smsc47m2:
+ ports = ports_m2;
+ ports_size = ARRAY_SIZE(ports_m2);
+ break;
+ }
+
+ for (i = 0; i + 1 < ports_size; i += 2) {
+ unsigned short start = address + ports[i];
+ unsigned short len = ports[i + 1];
+
+ switch (action) {
+ case CHECK:
+ /* Only check for conflicts */
+ err = acpi_check_region(start, len, DRVNAME);
+ if (err)
+ return err;
+ break;
+ case REQUEST:
+ /* Request the resources */
+ if (!devm_request_region(dev, start, len, DRVNAME)) {
+ dev_err(dev,
+ "Region 0x%x-0x%x already in use!\n",
+ start, start + len);
+ return -EBUSY;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void smsc47m1_remove_files(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &smsc47m1_group);
+ sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan1);
+ sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan2);
+ sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan3);
+ sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm1);
+ sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm2);
+ sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm3);
+}
+
+static int __init smsc47m1_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct smsc47m1_sio_data *sio_data = dev_get_platdata(dev);
+ struct smsc47m1_data *data;
+ struct resource *res;
+ int err;
+ int fan1, fan2, fan3, pwm1, pwm2, pwm3;
+
+ static const char * const names[] = {
+ "smsc47m1",
+ "smsc47m2",
+ };
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ err = smsc47m1_handle_resources(res->start, sio_data->type,
+ REQUEST, dev);
+ if (err < 0)
+ return err;
+
+ data = devm_kzalloc(dev, sizeof(struct smsc47m1_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = res->start;
+ data->type = sio_data->type;
+ data->name = names[sio_data->type];
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ /*
+ * If no function is properly configured, there's no point in
+ * actually registering the chip.
+ */
+ pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
+ == 0x04;
+ pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
+ == 0x04;
+ if (data->type == smsc47m2) {
+ fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
+ & 0x0d) == 0x09;
+ fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
+ & 0x0d) == 0x09;
+ fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
+ & 0x0d) == 0x0d;
+ pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
+ & 0x0d) == 0x08;
+ } else {
+ fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
+ & 0x05) == 0x05;
+ fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
+ & 0x05) == 0x05;
+ fan3 = 0;
+ pwm3 = 0;
+ }
+ if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
+ dev_warn(dev, "Device not configured, will not use\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Some values (fan min, clock dividers, pwm registers) may be
+ * needed before any update is triggered, so we better read them
+ * at least once here. We don't usually do it that way, but in
+ * this particular case, manually reading 5 registers out of 8
+ * doesn't make much sense and we're better using the existing
+ * function.
+ */
+ smsc47m1_update_device(dev, 1);
+
+ /* Register sysfs hooks */
+ if (fan1) {
+ err = sysfs_create_group(&dev->kobj,
+ &smsc47m1_group_fan1);
+ if (err)
+ goto error_remove_files;
+ } else
+ dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
+
+ if (fan2) {
+ err = sysfs_create_group(&dev->kobj,
+ &smsc47m1_group_fan2);
+ if (err)
+ goto error_remove_files;
+ } else
+ dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
+
+ if (fan3) {
+ err = sysfs_create_group(&dev->kobj,
+ &smsc47m1_group_fan3);
+ if (err)
+ goto error_remove_files;
+ } else if (data->type == smsc47m2)
+ dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
+
+ if (pwm1) {
+ err = sysfs_create_group(&dev->kobj,
+ &smsc47m1_group_pwm1);
+ if (err)
+ goto error_remove_files;
+ } else
+ dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
+
+ if (pwm2) {
+ err = sysfs_create_group(&dev->kobj,
+ &smsc47m1_group_pwm2);
+ if (err)
+ goto error_remove_files;
+ } else
+ dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
+
+ if (pwm3) {
+ err = sysfs_create_group(&dev->kobj,
+ &smsc47m1_group_pwm3);
+ if (err)
+ goto error_remove_files;
+ } else if (data->type == smsc47m2)
+ dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
+
+ err = sysfs_create_group(&dev->kobj, &smsc47m1_group);
+ if (err)
+ goto error_remove_files;
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto error_remove_files;
+ }
+
+ return 0;
+
+error_remove_files:
+ smsc47m1_remove_files(dev);
+ return err;
+}
+
+static int __exit smsc47m1_remove(struct platform_device *pdev)
+{
+ struct smsc47m1_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ smsc47m1_remove_files(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver smsc47m1_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .remove = __exit_p(smsc47m1_remove),
+};
+
+static int __init smsc47m1_device_add(unsigned short address,
+ const struct smsc47m1_sio_data *sio_data)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + SMSC_EXTENT - 1,
+ .name = DRVNAME,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add_data(pdev, sio_data,
+ sizeof(struct smsc47m1_sio_data));
+ if (err) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int __init sm_smsc47m1_init(void)
+{
+ int err;
+ unsigned short address;
+ struct smsc47m1_sio_data sio_data;
+
+ err = smsc47m1_find(&sio_data);
+ if (err < 0)
+ return err;
+ address = err;
+
+ /* Sets global pdev as a side effect */
+ err = smsc47m1_device_add(address, &sio_data);
+ if (err)
+ return err;
+
+ err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
+ if (err)
+ goto exit_device;
+
+ return 0;
+
+exit_device:
+ platform_device_unregister(pdev);
+ smsc47m1_restore(&sio_data);
+ return err;
+}
+
+static void __exit sm_smsc47m1_exit(void)
+{
+ platform_driver_unregister(&smsc47m1_driver);
+ smsc47m1_restore(dev_get_platdata(&pdev->dev));
+ platform_device_unregister(pdev);
+}
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_smsc47m1_init);
+module_exit(sm_smsc47m1_exit);
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
new file mode 100644
index 000000000..70d215223
--- /dev/null
+++ b/drivers/hwmon/smsc47m192.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * smsc47m192.c - Support for hardware monitoring block of
+ * SMSC LPC47M192 and compatible Super I/O chips
+ *
+ * Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de>
+ *
+ * Derived from lm78.c and other chip drivers.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
+
+/* SMSC47M192 registers */
+#define SMSC47M192_REG_IN(nr) ((nr) < 6 ? (0x20 + (nr)) : \
+ (0x50 + (nr) - 6))
+#define SMSC47M192_REG_IN_MAX(nr) ((nr) < 6 ? (0x2b + (nr) * 2) : \
+ (0x54 + (((nr) - 6) * 2)))
+#define SMSC47M192_REG_IN_MIN(nr) ((nr) < 6 ? (0x2c + (nr) * 2) : \
+ (0x55 + (((nr) - 6) * 2)))
+static u8 SMSC47M192_REG_TEMP[3] = { 0x27, 0x26, 0x52 };
+static u8 SMSC47M192_REG_TEMP_MAX[3] = { 0x39, 0x37, 0x58 };
+static u8 SMSC47M192_REG_TEMP_MIN[3] = { 0x3A, 0x38, 0x59 };
+#define SMSC47M192_REG_TEMP_OFFSET(nr) ((nr) == 2 ? 0x1e : 0x1f)
+#define SMSC47M192_REG_ALARM1 0x41
+#define SMSC47M192_REG_ALARM2 0x42
+#define SMSC47M192_REG_VID 0x47
+#define SMSC47M192_REG_VID4 0x49
+#define SMSC47M192_REG_CONFIG 0x40
+#define SMSC47M192_REG_SFR 0x4f
+#define SMSC47M192_REG_COMPANY_ID 0x3e
+#define SMSC47M192_REG_VERSION 0x3f
+
+/* generalised scaling with integer rounding */
+static inline int SCALE(long val, int mul, int div)
+{
+ if (val < 0)
+ return (val * mul - div / 2) / div;
+ else
+ return (val * mul + div / 2) / div;
+}
+
+/* Conversions */
+
+/* smsc47m192 internally scales voltage measurements */
+static const u16 nom_mv[] = { 2500, 2250, 3300, 5000, 12000, 3300, 1500, 1800 };
+
+static inline unsigned int IN_FROM_REG(u8 reg, int n)
+{
+ return SCALE(reg, nom_mv[n], 192);
+}
+
+static inline u8 IN_TO_REG(unsigned long val, int n)
+{
+ val = clamp_val(val, 0, nom_mv[n] * 255 / 192);
+ return SCALE(val, 192, nom_mv[n]);
+}
+
+/*
+ * TEMP: 0.001 degC units (-128C to +127C)
+ * REG: 1C/bit, two's complement
+ */
+static inline s8 TEMP_TO_REG(long val)
+{
+ return SCALE(clamp_val(val, -128000, 127000), 1, 1000);
+}
+
+static inline int TEMP_FROM_REG(s8 val)
+{
+ return val * 1000;
+}
+
+struct smsc47m192_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 in[8]; /* Register value */
+ u8 in_max[8]; /* Register value */
+ u8 in_min[8]; /* Register value */
+ s8 temp[3]; /* Register value */
+ s8 temp_max[3]; /* Register value */
+ s8 temp_min[3]; /* Register value */
+ s8 temp_offset[3]; /* Register value */
+ u16 alarms; /* Register encoding, combined */
+ u8 vid; /* Register encoding, combined */
+ u8 vrm;
+};
+
+static struct smsc47m192_data *smsc47m192_update_device(struct device *dev)
+{
+ struct smsc47m192_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i, config;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR);
+
+ dev_dbg(&client->dev, "Starting smsc47m192 update\n");
+
+ for (i = 0; i <= 7; i++) {
+ data->in[i] = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_IN(i));
+ data->in_min[i] = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_IN_MIN(i));
+ data->in_max[i] = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_IN_MAX(i));
+ }
+ for (i = 0; i < 3; i++) {
+ data->temp[i] = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_TEMP[i]);
+ data->temp_max[i] = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_TEMP_MAX[i]);
+ data->temp_min[i] = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_TEMP_MIN[i]);
+ }
+ for (i = 1; i < 3; i++)
+ data->temp_offset[i] = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_TEMP_OFFSET(i));
+ /*
+ * first offset is temp_offset[0] if SFR bit 4 is set,
+ * temp_offset[1] otherwise
+ */
+ if (sfr & 0x10) {
+ data->temp_offset[0] = data->temp_offset[1];
+ data->temp_offset[1] = 0;
+ } else
+ data->temp_offset[0] = 0;
+
+ data->vid = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID)
+ & 0x0f;
+ config = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_CONFIG);
+ if (config & 0x20)
+ data->vid |= (i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_VID4) & 0x01) << 4;
+ data->alarms = i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_ALARM1) |
+ (i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_ALARM2) << 8);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* Voltages */
+static ssize_t in_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr));
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr));
+}
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_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[nr] = IN_TO_REG(val, nr);
+ i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MIN(nr),
+ data->in_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_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[nr] = IN_TO_REG(val, nr);
+ i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MAX(nr),
+ data->in_max[nr]);
+ 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);
+static SENSOR_DEVICE_ATTR_RO(in6_input, in, 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, 7);
+static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 7);
+static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 7);
+
+/* Temperatures */
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
+}
+
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
+}
+
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = TEMP_TO_REG(val);
+ i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MIN[nr],
+ data->temp_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = TEMP_TO_REG(val);
+ i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MAX[nr],
+ data->temp_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_offset[nr]));
+}
+
+static ssize_t temp_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_offset[nr] = TEMP_TO_REG(val);
+ if (nr > 1)
+ i2c_smbus_write_byte_data(client,
+ SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]);
+ else if (data->temp_offset[nr] != 0) {
+ /*
+ * offset[0] and offset[1] share the same register,
+ * SFR bit 4 activates offset[0]
+ */
+ i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR,
+ (sfr & 0xef) | (nr == 0 ? 0x10 : 0));
+ data->temp_offset[1-nr] = 0;
+ i2c_smbus_write_byte_data(client,
+ SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]);
+ } else if ((sfr & 0x10) == (nr == 0 ? 0x10 : 0))
+ i2c_smbus_write_byte_data(client,
+ SMSC47M192_REG_TEMP_OFFSET(nr), 0);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_offset, temp_offset, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_offset, temp_offset, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_offset, temp_offset, 2);
+
+/* VID */
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%d\n", 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 smsc47m192_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct smsc47m192_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);
+
+/* Alarms */
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct smsc47m192_data *data = smsc47m192_update_device(dev);
+ return sprintf(buf, "%u\n", (data->alarms & nr) ? 1 : 0);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 0x0010);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 0x0020);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 0x0040);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 0x4000);
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 0x8000);
+static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0x0001);
+static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 0x0002);
+static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 0x0004);
+static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 0x0008);
+static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 0x0100);
+static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 0x0200);
+static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 0x0400);
+static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 0x0800);
+
+static struct attribute *smsc47m192_attributes[] = {
+ &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_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,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+
+ &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_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m192_group = {
+ .attrs = smsc47m192_attributes,
+};
+
+static struct attribute *smsc47m192_attributes_in4[] = {
+ &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,
+ NULL
+};
+
+static const struct attribute_group smsc47m192_group_in4 = {
+ .attrs = smsc47m192_attributes_in4,
+};
+
+static void smsc47m192_init_client(struct i2c_client *client)
+{
+ int i;
+ u8 config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG);
+ u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR);
+
+ /* select cycle mode (pause 1 sec between updates) */
+ i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR,
+ (sfr & 0xfd) | 0x02);
+ if (!(config & 0x01)) {
+ /* initialize alarm limits */
+ for (i = 0; i < 8; i++) {
+ i2c_smbus_write_byte_data(client,
+ SMSC47M192_REG_IN_MIN(i), 0);
+ i2c_smbus_write_byte_data(client,
+ SMSC47M192_REG_IN_MAX(i), 0xff);
+ }
+ for (i = 0; i < 3; i++) {
+ i2c_smbus_write_byte_data(client,
+ SMSC47M192_REG_TEMP_MIN[i], 0x80);
+ i2c_smbus_write_byte_data(client,
+ SMSC47M192_REG_TEMP_MAX[i], 0x7f);
+ }
+
+ /* start monitoring */
+ i2c_smbus_write_byte_data(client, SMSC47M192_REG_CONFIG,
+ (config & 0xf7) | 0x01);
+ }
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int smsc47m192_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int version;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Detection criteria from sensors_detect script */
+ version = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VERSION);
+ if (i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_COMPANY_ID) == 0x55
+ && (version & 0xf0) == 0x20
+ && (i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_VID) & 0x70) == 0x00
+ && (i2c_smbus_read_byte_data(client,
+ SMSC47M192_REG_VID4) & 0xfe) == 0x80) {
+ dev_info(&adapter->dev,
+ "found SMSC47M192 or compatible, "
+ "version 2, stepping A%d\n", version & 0x0f);
+ } else {
+ dev_dbg(&adapter->dev,
+ "SMSC47M192 detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "smsc47m192", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int smsc47m192_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct smsc47m192_data *data;
+ int config;
+
+ data = devm_kzalloc(dev, sizeof(struct smsc47m192_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->vrm = vid_which_vrm();
+ mutex_init(&data->update_lock);
+
+ /* Initialize the SMSC47M192 chip */
+ smsc47m192_init_client(client);
+
+ /* sysfs hooks */
+ data->groups[0] = &smsc47m192_group;
+ /* Pin 110 is either in4 (+12V) or VID4 */
+ config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG);
+ if (!(config & 0x20))
+ data->groups[1] = &smsc47m192_group_in4;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id smsc47m192_id[] = {
+ { "smsc47m192", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, smsc47m192_id);
+
+static struct i2c_driver smsc47m192_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "smsc47m192",
+ },
+ .probe_new = smsc47m192_probe,
+ .id_table = smsc47m192_id,
+ .detect = smsc47m192_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(smsc47m192_driver);
+
+MODULE_AUTHOR("Hartmut Rick <linux@rick.claranet.de>");
+MODULE_DESCRIPTION("SMSC47M192 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sparx5-temp.c b/drivers/hwmon/sparx5-temp.c
new file mode 100644
index 000000000..04fd8505e
--- /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
new file mode 100644
index 000000000..2f67c6747
--- /dev/null
+++ b/drivers/hwmon/stts751.c
@@ -0,0 +1,835 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STTS751 sensor driver
+ *
+ * Copyright (C) 2016-2017 Istituto Italiano di Tecnologia - RBCS - EDL
+ * Robotics, Brain and Cognitive Sciences department
+ * Electronic Design Laboratory
+ *
+ * Written by Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Based on LM95241 driver and LM90 driver
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/util_macros.h>
+
+#define DEVNAME "stts751"
+
+static const unsigned short normal_i2c[] = {
+ 0x48, 0x49, 0x38, 0x39, /* STTS751-0 */
+ 0x4A, 0x4B, 0x3A, 0x3B, /* STTS751-1 */
+ I2C_CLIENT_END };
+
+#define STTS751_REG_TEMP_H 0x00
+#define STTS751_REG_STATUS 0x01
+#define STTS751_STATUS_TRIPT BIT(0)
+#define STTS751_STATUS_TRIPL BIT(5)
+#define STTS751_STATUS_TRIPH BIT(6)
+#define STTS751_REG_TEMP_L 0x02
+#define STTS751_REG_CONF 0x03
+#define STTS751_CONF_RES_MASK 0x0C
+#define STTS751_CONF_RES_SHIFT 2
+#define STTS751_CONF_EVENT_DIS BIT(7)
+#define STTS751_CONF_STOP BIT(6)
+#define STTS751_REG_RATE 0x04
+#define STTS751_REG_HLIM_H 0x05
+#define STTS751_REG_HLIM_L 0x06
+#define STTS751_REG_LLIM_H 0x07
+#define STTS751_REG_LLIM_L 0x08
+#define STTS751_REG_TLIM 0x20
+#define STTS751_REG_HYST 0x21
+#define STTS751_REG_SMBUS_TO 0x22
+
+#define STTS751_REG_PROD_ID 0xFD
+#define STTS751_REG_MAN_ID 0xFE
+#define STTS751_REG_REV_ID 0xFF
+
+#define STTS751_0_PROD_ID 0x00
+#define STTS751_1_PROD_ID 0x01
+#define ST_MAN_ID 0x53
+
+/*
+ * Possible update intervals are (in mS):
+ * 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 62.5, 31.25
+ * However we are not going to complicate things too much and we stick to the
+ * approx value in mS.
+ */
+static const int stts751_intervals[] = {
+ 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 63, 31
+};
+
+static const struct i2c_device_id stts751_id[] = {
+ { "stts751", 0 },
+ { }
+};
+
+static const struct of_device_id __maybe_unused stts751_of_match[] = {
+ { .compatible = "stts751" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stts751_of_match);
+
+struct stts751_priv {
+ struct device *dev;
+ struct i2c_client *client;
+ struct mutex access_lock;
+ u8 interval;
+ int res;
+ int event_max, event_min;
+ int therm;
+ int hyst;
+ bool smbus_timeout;
+ int temp;
+ unsigned long last_update, last_alert_update;
+ u8 config;
+ bool min_alert, max_alert, therm_trip;
+ bool data_valid, alert_valid;
+ bool notify_max, notify_min;
+};
+
+/*
+ * These functions converts temperature from HW format to integer format and
+ * vice-vers. They are (mostly) taken from lm90 driver. Unit is in mC.
+ */
+static int stts751_to_deg(s16 hw_val)
+{
+ return hw_val * 125 / 32;
+}
+
+static s32 stts751_to_hw(int val)
+{
+ return DIV_ROUND_CLOSEST(val, 125) * 32;
+}
+
+static int stts751_adjust_resolution(struct stts751_priv *priv)
+{
+ u8 res;
+
+ switch (priv->interval) {
+ case 9:
+ /* 10 bits */
+ res = 0;
+ break;
+ case 8:
+ /* 11 bits */
+ res = 1;
+ break;
+ default:
+ /* 12 bits */
+ res = 3;
+ break;
+ }
+
+ if (priv->res == res)
+ return 0;
+
+ priv->config &= ~STTS751_CONF_RES_MASK;
+ priv->config |= res << STTS751_CONF_RES_SHIFT;
+ dev_dbg(&priv->client->dev, "setting res %d. config %x",
+ res, priv->config);
+ priv->res = res;
+
+ return i2c_smbus_write_byte_data(priv->client,
+ STTS751_REG_CONF, priv->config);
+}
+
+static int stts751_update_temp(struct stts751_priv *priv)
+{
+ s32 integer1, integer2, frac;
+
+ /*
+ * There is a trick here, like in the lm90 driver. We have to read two
+ * registers to get the sensor temperature, but we have to beware a
+ * conversion could occur between the readings. We could use the
+ * one-shot conversion register, but we don't want to do this (disables
+ * hardware monitoring). 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.
+ */
+ integer1 = i2c_smbus_read_byte_data(priv->client, STTS751_REG_TEMP_H);
+ if (integer1 < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C read failed (temp H). ret: %x\n", integer1);
+ return integer1;
+ }
+
+ frac = i2c_smbus_read_byte_data(priv->client, STTS751_REG_TEMP_L);
+ if (frac < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C read failed (temp L). ret: %x\n", frac);
+ return frac;
+ }
+
+ integer2 = i2c_smbus_read_byte_data(priv->client, STTS751_REG_TEMP_H);
+ if (integer2 < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C 2nd read failed (temp H). ret: %x\n", integer2);
+ return integer2;
+ }
+
+ if (integer1 != integer2) {
+ frac = i2c_smbus_read_byte_data(priv->client,
+ STTS751_REG_TEMP_L);
+ if (frac < 0) {
+ dev_dbg(&priv->client->dev,
+ "I2C 2nd read failed (temp L). ret: %x\n",
+ frac);
+ return frac;
+ }
+ }
+
+ priv->temp = stts751_to_deg((integer1 << 8) | frac);
+ return 0;
+}
+
+static int stts751_set_temp_reg16(struct stts751_priv *priv, int temp,
+ u8 hreg, u8 lreg)
+{
+ s32 hwval;
+ int ret;
+
+ hwval = stts751_to_hw(temp);
+
+ ret = i2c_smbus_write_byte_data(priv->client, hreg, hwval >> 8);
+ if (ret)
+ return ret;
+
+ return i2c_smbus_write_byte_data(priv->client, lreg, hwval & 0xff);
+}
+
+static int stts751_set_temp_reg8(struct stts751_priv *priv, int temp, u8 reg)
+{
+ s32 hwval;
+
+ hwval = stts751_to_hw(temp);
+ return i2c_smbus_write_byte_data(priv->client, reg, hwval >> 8);
+}
+
+static int stts751_read_reg16(struct stts751_priv *priv, int *temp,
+ u8 hreg, u8 lreg)
+{
+ int integer, frac;
+
+ integer = i2c_smbus_read_byte_data(priv->client, hreg);
+ if (integer < 0)
+ return integer;
+
+ frac = i2c_smbus_read_byte_data(priv->client, lreg);
+ if (frac < 0)
+ return frac;
+
+ *temp = stts751_to_deg((integer << 8) | frac);
+
+ return 0;
+}
+
+static int stts751_read_reg8(struct stts751_priv *priv, int *temp, u8 reg)
+{
+ int integer;
+
+ integer = i2c_smbus_read_byte_data(priv->client, reg);
+ if (integer < 0)
+ return integer;
+
+ *temp = stts751_to_deg(integer << 8);
+
+ return 0;
+}
+
+/*
+ * Update alert flags without waiting for cache to expire. We detects alerts
+ * immediately for the sake of the alert handler; we still need to deal with
+ * caching to workaround the fact that alarm flags int the status register,
+ * despite what the datasheet claims, gets always cleared on read.
+ */
+static int stts751_update_alert(struct stts751_priv *priv)
+{
+ int ret;
+ bool conv_done;
+ int cache_time = msecs_to_jiffies(stts751_intervals[priv->interval]);
+
+ /*
+ * Add another 10% because if we run faster than the HW conversion
+ * rate we will end up in reporting incorrectly alarms.
+ */
+ cache_time += cache_time / 10;
+
+ ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_STATUS);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&priv->client->dev, "status reg %x\n", ret);
+ conv_done = ret & (STTS751_STATUS_TRIPH | STTS751_STATUS_TRIPL);
+ /*
+ * Reset the cache if the cache time expired, or if we are sure
+ * we have valid data from a device conversion, or if we know
+ * our cache has been never written.
+ *
+ * Note that when the cache has been never written the point is
+ * to correctly initialize the timestamp, rather than clearing
+ * the cache values.
+ *
+ * Note that updating the cache timestamp when we get an alarm flag
+ * is required, otherwise we could incorrectly report alarms to be zero.
+ */
+ if (time_after(jiffies, priv->last_alert_update + cache_time) ||
+ conv_done || !priv->alert_valid) {
+ priv->max_alert = false;
+ priv->min_alert = false;
+ priv->alert_valid = true;
+ priv->last_alert_update = jiffies;
+ dev_dbg(&priv->client->dev, "invalidating alert cache\n");
+ }
+
+ priv->max_alert |= !!(ret & STTS751_STATUS_TRIPH);
+ priv->min_alert |= !!(ret & STTS751_STATUS_TRIPL);
+ priv->therm_trip = !!(ret & STTS751_STATUS_TRIPT);
+
+ dev_dbg(&priv->client->dev, "max_alert: %d, min_alert: %d, therm_trip: %d\n",
+ priv->max_alert, priv->min_alert, priv->therm_trip);
+
+ return 0;
+}
+
+static void stts751_alert(struct i2c_client *client,
+ enum i2c_alert_protocol type, unsigned int data)
+{
+ int ret;
+ struct stts751_priv *priv = i2c_get_clientdata(client);
+
+ if (type != I2C_PROTOCOL_SMBUS_ALERT)
+ return;
+
+ dev_dbg(&client->dev, "alert!");
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update_alert(priv);
+ if (ret < 0) {
+ /* default to worst case */
+ priv->max_alert = true;
+ priv->min_alert = true;
+
+ dev_warn(priv->dev,
+ "Alert received, but can't communicate to the device. Triggering all alarms!");
+ }
+
+ if (priv->max_alert) {
+ if (priv->notify_max)
+ dev_notice(priv->dev, "got alert for HIGH temperature");
+ priv->notify_max = false;
+
+ /* unblock alert poll */
+ sysfs_notify(&priv->dev->kobj, NULL, "temp1_max_alarm");
+ }
+
+ if (priv->min_alert) {
+ if (priv->notify_min)
+ dev_notice(priv->dev, "got alert for LOW temperature");
+ priv->notify_min = false;
+
+ /* unblock alert poll */
+ sysfs_notify(&priv->dev->kobj, NULL, "temp1_min_alarm");
+ }
+
+ if (priv->min_alert || priv->max_alert)
+ kobject_uevent(&priv->dev->kobj, KOBJ_CHANGE);
+
+ mutex_unlock(&priv->access_lock);
+}
+
+static int stts751_update(struct stts751_priv *priv)
+{
+ int ret;
+ int cache_time = msecs_to_jiffies(stts751_intervals[priv->interval]);
+
+ if (time_after(jiffies, priv->last_update + cache_time) ||
+ !priv->data_valid) {
+ ret = stts751_update_temp(priv);
+ if (ret)
+ return ret;
+
+ ret = stts751_update_alert(priv);
+ if (ret)
+ return ret;
+ priv->data_valid = true;
+ priv->last_update = jiffies;
+ }
+
+ return 0;
+}
+
+static ssize_t max_alarm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ if (!ret)
+ priv->notify_max = true;
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", priv->max_alert);
+}
+
+static ssize_t min_alarm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ if (!ret)
+ priv->notify_min = true;
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", priv->min_alert);
+}
+
+static ssize_t input_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", priv->temp);
+}
+
+static ssize_t therm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", priv->therm);
+}
+
+static ssize_t therm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, -64000, 127937);
+ mutex_lock(&priv->access_lock);
+ ret = stts751_set_temp_reg8(priv, temp, STTS751_REG_TLIM);
+ if (ret)
+ goto exit;
+
+ dev_dbg(&priv->client->dev, "setting therm %ld", temp);
+
+ /*
+ * hysteresis reg is relative to therm, so the HW does not need to be
+ * adjusted, we need to update our local copy only.
+ */
+ priv->hyst = temp - (priv->therm - priv->hyst);
+ priv->therm = temp;
+
+exit:
+ mutex_unlock(&priv->access_lock);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t hyst_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", priv->hyst);
+}
+
+static ssize_t hyst_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->access_lock);
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, -64000, priv->therm);
+ priv->hyst = temp;
+ dev_dbg(&priv->client->dev, "setting hyst %ld", temp);
+ temp = priv->therm - temp;
+ ret = stts751_set_temp_reg8(priv, temp, STTS751_REG_HYST);
+ mutex_unlock(&priv->access_lock);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t therm_trip_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ mutex_lock(&priv->access_lock);
+ ret = stts751_update(priv);
+ mutex_unlock(&priv->access_lock);
+ if (ret < 0)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", priv->therm_trip);
+}
+
+static ssize_t max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", priv->event_max);
+}
+
+static ssize_t max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->access_lock);
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, priv->event_min, 127937);
+ ret = stts751_set_temp_reg16(priv, temp,
+ STTS751_REG_HLIM_H, STTS751_REG_HLIM_L);
+ if (ret)
+ goto exit;
+
+ dev_dbg(&priv->client->dev, "setting event max %ld", temp);
+ priv->event_max = temp;
+ ret = count;
+exit:
+ mutex_unlock(&priv->access_lock);
+ return ret;
+}
+
+static ssize_t min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", priv->event_min);
+}
+
+static ssize_t min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long temp;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtol(buf, 10, &temp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->access_lock);
+ /* HW works in range -64C to +127.937C */
+ temp = clamp_val(temp, -64000, priv->event_max);
+ ret = stts751_set_temp_reg16(priv, temp,
+ STTS751_REG_LLIM_H, STTS751_REG_LLIM_L);
+ if (ret)
+ goto exit;
+
+ dev_dbg(&priv->client->dev, "setting event min %ld", temp);
+ priv->event_min = temp;
+ ret = count;
+exit:
+ mutex_unlock(&priv->access_lock);
+ return ret;
+}
+
+static ssize_t interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n",
+ stts751_intervals[priv->interval]);
+}
+
+static ssize_t interval_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ int idx;
+ int ret = count;
+ struct stts751_priv *priv = dev_get_drvdata(dev);
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ idx = find_closest_descending(val, stts751_intervals,
+ ARRAY_SIZE(stts751_intervals));
+
+ dev_dbg(&priv->client->dev, "setting interval. req:%lu, idx: %d, val: %d",
+ val, idx, stts751_intervals[idx]);
+
+ mutex_lock(&priv->access_lock);
+ if (priv->interval == idx)
+ goto exit;
+
+ /*
+ * In early development stages I've become suspicious about the chip
+ * starting to misbehave if I ever set, even briefly, an invalid
+ * configuration. While I'm not sure this is really needed, be
+ * conservative and set rate/resolution in such an order that avoids
+ * passing through an invalid configuration.
+ */
+
+ /* speed up: lower the resolution, then modify convrate */
+ if (priv->interval < idx) {
+ dev_dbg(&priv->client->dev, "lower resolution, then modify convrate");
+ priv->interval = idx;
+ ret = stts751_adjust_resolution(priv);
+ if (ret)
+ goto exit;
+ }
+
+ ret = i2c_smbus_write_byte_data(priv->client, STTS751_REG_RATE, idx);
+ if (ret)
+ goto exit;
+ /* slow down: modify convrate, then raise resolution */
+ if (priv->interval != idx) {
+ dev_dbg(&priv->client->dev, "modify convrate, then raise resolution");
+ priv->interval = idx;
+ ret = stts751_adjust_resolution(priv);
+ if (ret)
+ goto exit;
+ }
+ ret = count;
+exit:
+ mutex_unlock(&priv->access_lock);
+
+ return ret;
+}
+
+static int stts751_detect(struct i2c_client *new_client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ const char *name;
+ int tmp;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_MAN_ID);
+ if (tmp != ST_MAN_ID)
+ return -ENODEV;
+
+ /* lower temperaure registers always have bits 0-3 set to zero */
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_TEMP_L);
+ if (tmp & 0xf)
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_HLIM_L);
+ if (tmp & 0xf)
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_LLIM_L);
+ if (tmp & 0xf)
+ return -ENODEV;
+
+ /* smbus timeout register always have bits 0-7 set to zero */
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_SMBUS_TO);
+ if (tmp & 0x7f)
+ return -ENODEV;
+
+ tmp = i2c_smbus_read_byte_data(new_client, STTS751_REG_PROD_ID);
+
+ switch (tmp) {
+ case STTS751_0_PROD_ID:
+ name = "STTS751-0";
+ break;
+ case STTS751_1_PROD_ID:
+ name = "STTS751-1";
+ break;
+ default:
+ return -ENODEV;
+ }
+ dev_dbg(&new_client->dev, "Chip %s detected", name);
+
+ strscpy(info->type, stts751_id[0].name, I2C_NAME_SIZE);
+ return 0;
+}
+
+static int stts751_read_chip_config(struct stts751_priv *priv)
+{
+ int ret;
+ int tmp;
+
+ ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_CONF);
+ if (ret < 0)
+ return ret;
+ priv->config = ret;
+ priv->res = (ret & STTS751_CONF_RES_MASK) >> STTS751_CONF_RES_SHIFT;
+
+ ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_RATE);
+ if (ret < 0)
+ return ret;
+ if (ret >= ARRAY_SIZE(stts751_intervals)) {
+ dev_err(priv->dev, "Unrecognized conversion rate 0x%x\n", ret);
+ return -ENODEV;
+ }
+ priv->interval = ret;
+
+ ret = stts751_read_reg16(priv, &priv->event_max,
+ STTS751_REG_HLIM_H, STTS751_REG_HLIM_L);
+ if (ret)
+ return ret;
+
+ ret = stts751_read_reg16(priv, &priv->event_min,
+ STTS751_REG_LLIM_H, STTS751_REG_LLIM_L);
+ if (ret)
+ return ret;
+
+ ret = stts751_read_reg8(priv, &priv->therm, STTS751_REG_TLIM);
+ if (ret)
+ return ret;
+
+ ret = stts751_read_reg8(priv, &tmp, STTS751_REG_HYST);
+ if (ret)
+ return ret;
+ priv->hyst = priv->therm - tmp;
+
+ return 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_RO(temp1_min_alarm, min_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, max_alarm, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, therm, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, hyst, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, therm_trip, 0);
+static SENSOR_DEVICE_ATTR_RW(update_interval, interval, 0);
+
+static struct attribute *stts751_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,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_update_interval.dev_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(stts751);
+
+static int stts751_probe(struct i2c_client *client)
+{
+ struct stts751_priv *priv;
+ int ret;
+ bool smbus_nto;
+ int rev_id;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client = client;
+ priv->notify_max = true;
+ priv->notify_min = true;
+ i2c_set_clientdata(client, priv);
+ mutex_init(&priv->access_lock);
+
+ if (device_property_present(&client->dev,
+ "smbus-timeout-disable")) {
+ smbus_nto = device_property_read_bool(&client->dev,
+ "smbus-timeout-disable");
+
+ ret = i2c_smbus_write_byte_data(client, STTS751_REG_SMBUS_TO,
+ smbus_nto ? 0 : 0x80);
+ if (ret)
+ return ret;
+ }
+
+ rev_id = i2c_smbus_read_byte_data(client, STTS751_REG_REV_ID);
+ if (rev_id < 0)
+ return -ENODEV;
+ if (rev_id != 0x1) {
+ dev_dbg(&client->dev, "Chip revision 0x%x is untested\n",
+ rev_id);
+ }
+
+ ret = stts751_read_chip_config(priv);
+ if (ret)
+ return ret;
+
+ priv->config &= ~(STTS751_CONF_STOP | STTS751_CONF_EVENT_DIS);
+ ret = i2c_smbus_write_byte_data(client, STTS751_REG_CONF, priv->config);
+ if (ret)
+ return ret;
+
+ priv->dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, priv,
+ stts751_groups);
+ return PTR_ERR_OR_ZERO(priv->dev);
+}
+
+MODULE_DEVICE_TABLE(i2c, stts751_id);
+
+static struct i2c_driver stts751_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DEVNAME,
+ .of_match_table = of_match_ptr(stts751_of_match),
+ },
+ .probe_new = stts751_probe,
+ .id_table = stts751_id,
+ .detect = stts751_detect,
+ .alert = stts751_alert,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(stts751_driver);
+
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
+MODULE_DESCRIPTION("STTS751 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sy7636a-hwmon.c b/drivers/hwmon/sy7636a-hwmon.c
new file mode 100644
index 000000000..6dd9c2a0f
--- /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, &reg_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
new file mode 100644
index 000000000..54cd33d09
--- /dev/null
+++ b/drivers/hwmon/tc654.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * tc654.c - Linux kernel modules for fan speed controller
+ *
+ * Copyright (C) 2016 Allied Telesis Labs NZ
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/util_macros.h>
+
+enum tc654_regs {
+ TC654_REG_RPM1 = 0x00, /* RPM Output 1 */
+ TC654_REG_RPM2 = 0x01, /* RPM Output 2 */
+ TC654_REG_FAN_FAULT1 = 0x02, /* Fan Fault 1 Threshold */
+ TC654_REG_FAN_FAULT2 = 0x03, /* Fan Fault 2 Threshold */
+ TC654_REG_CONFIG = 0x04, /* Configuration */
+ TC654_REG_STATUS = 0x05, /* Status */
+ TC654_REG_DUTY_CYCLE = 0x06, /* Fan Speed Duty Cycle */
+ TC654_REG_MFR_ID = 0x07, /* Manufacturer Identification */
+ TC654_REG_VER_ID = 0x08, /* Version Identification */
+};
+
+/* Macros to easily index the registers */
+#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx))
+#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx))
+
+/* Config register bits */
+#define TC654_REG_CONFIG_RES BIT(6) /* Resolution Selection */
+#define TC654_REG_CONFIG_DUTYC BIT(5) /* Duty Cycle Control */
+#define TC654_REG_CONFIG_SDM BIT(0) /* Shutdown Mode */
+
+/* Status register bits */
+#define TC654_REG_STATUS_F2F BIT(1) /* Fan 2 Fault */
+#define TC654_REG_STATUS_F1F BIT(0) /* Fan 1 Fault */
+
+/* RPM resolution for RPM Output registers */
+#define TC654_HIGH_RPM_RESOLUTION 25 /* 25 RPM resolution */
+#define TC654_LOW_RPM_RESOLUTION 50 /* 50 RPM resolution */
+
+/* Convert to the fan fault RPM threshold from register value */
+#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50) /* 50 RPM resolution */
+
+/* Convert to register value from the fan fault RPM threshold */
+#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff)
+
+/* Register data is read (and cached) at most once per second. */
+#define TC654_UPDATE_INTERVAL HZ
+
+struct tc654_data {
+ struct i2c_client *client;
+
+ /* update mutex */
+ struct mutex update_lock;
+
+ /* tc654 register cache */
+ bool valid;
+ unsigned long last_updated; /* in jiffies */
+
+ u8 rpm_output[2]; /* The fan RPM data for fans 1 and 2 is then
+ * written to registers RPM1 and RPM2
+ */
+ u8 fan_fault[2]; /* The Fan Fault Threshold Registers are used to
+ * set the fan fault threshold levels for fan 1
+ * and fan 2
+ */
+ u8 config; /* The Configuration Register is an 8-bit read/
+ * writable multi-function control register
+ * 7: Fan Fault Clear
+ * 1 = Clear Fan Fault
+ * 0 = Normal Operation (default)
+ * 6: Resolution Selection for RPM Output Registers
+ * RPM Output Registers (RPM1 and RPM2) will be
+ * set for
+ * 1 = 25 RPM (9-bit) resolution
+ * 0 = 50 RPM (8-bit) resolution (default)
+ * 5: Duty Cycle Control Method
+ * The V OUT duty cycle will be controlled via
+ * 1 = the SMBus interface.
+ * 0 = via the V IN analog input pin. (default)
+ * 4,3: Fan 2 Pulses Per Rotation
+ * 00 = 1
+ * 01 = 2 (default)
+ * 10 = 4
+ * 11 = 8
+ * 2,1: Fan 1 Pulses Per Rotation
+ * 00 = 1
+ * 01 = 2 (default)
+ * 10 = 4
+ * 11 = 8
+ * 0: Shutdown Mode
+ * 1 = Shutdown mode.
+ * 0 = Normal operation. (default)
+ */
+ u8 status; /* The Status register provides all the information
+ * about what is going on within the TC654/TC655
+ * devices.
+ * 7,6: Unimplemented, Read as '0'
+ * 5: Over-Temperature Fault Condition
+ * 1 = Over-Temperature condition has occurred
+ * 0 = Normal operation. V IN is less than 2.6V
+ * 4: RPM2 Counter Overflow
+ * 1 = Fault condition
+ * 0 = Normal operation
+ * 3: RPM1 Counter Overflow
+ * 1 = Fault condition
+ * 0 = Normal operation
+ * 2: V IN Input Status
+ * 1 = V IN is open
+ * 0 = Normal operation. voltage present at V IN
+ * 1: Fan 2 Fault
+ * 1 = Fault condition
+ * 0 = Normal operation
+ * 0: Fan 1 Fault
+ * 1 = Fault condition
+ * 0 = Normal operation
+ */
+ u8 duty_cycle; /* The DUTY_CYCLE register is a 4-bit read/
+ * writable register used to control the duty
+ * cycle of the V OUT output.
+ */
+};
+
+/* helper to grab and cache data, at most one time per second */
+static struct tc654_data *tc654_update_client(struct device *dev)
+{
+ struct tc654_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret = 0;
+
+ mutex_lock(&data->update_lock);
+ if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) &&
+ likely(data->valid))
+ goto out;
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0));
+ if (ret < 0)
+ goto out;
+ data->rpm_output[0] = ret;
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1));
+ if (ret < 0)
+ goto out;
+ data->rpm_output[1] = ret;
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0));
+ if (ret < 0)
+ goto out;
+ data->fan_fault[0] = ret;
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1));
+ if (ret < 0)
+ goto out;
+ data->fan_fault[1] = ret;
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
+ if (ret < 0)
+ goto out;
+ data->config = ret;
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS);
+ if (ret < 0)
+ goto out;
+ data->status = ret;
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE);
+ if (ret < 0)
+ goto out;
+ data->duty_cycle = ret & 0x0f;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+out:
+ mutex_unlock(&data->update_lock);
+
+ if (ret < 0) /* upon error, encode it in return value */
+ data = ERR_PTR(ret);
+
+ return data;
+}
+
+/*
+ * sysfs attributes
+ */
+
+static ssize_t fan_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct tc654_data *data = tc654_update_client(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (data->config & TC654_REG_CONFIG_RES)
+ val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION;
+ else
+ val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct tc654_data *data = tc654_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n",
+ TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr]));
+}
+
+static ssize_t fan_min_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ 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;
+
+ val = clamp_val(val, 0, 12750);
+
+ mutex_lock(&data->update_lock);
+
+ data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val);
+ ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr),
+ data->fan_fault[nr]);
+
+ mutex_unlock(&data->update_lock);
+ return ret < 0 ? ret : count;
+}
+
+static ssize_t fan_alarm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct tc654_data *data = tc654_update_client(dev);
+ int val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (nr == 0)
+ val = !!(data->status & TC654_REG_STATUS_F1F);
+ else
+ val = !!(data->status & TC654_REG_STATUS_F2F);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 };
+
+static ssize_t fan_pulses_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct tc654_data *data = tc654_update_client(dev);
+ u8 val;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t fan_pulses_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(da)->index;
+ struct tc654_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 config;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ switch (val) {
+ case 1:
+ config = 0;
+ break;
+ case 2:
+ config = 1;
+ break;
+ case 4:
+ config = 2;
+ break;
+ case 8:
+ config = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+
+ data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]);
+ data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]);
+ ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
+
+ mutex_unlock(&data->update_lock);
+ return ret < 0 ? ret : count;
+}
+
+static ssize_t pwm_mode_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct tc654_data *data = tc654_update_client(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", !!(data->config & TC654_REG_CONFIG_DUTYC));
+}
+
+static ssize_t pwm_mode_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ 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 != 0 && val != 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ if (val)
+ data->config |= TC654_REG_CONFIG_DUTYC;
+ else
+ data->config &= ~TC654_REG_CONFIG_DUTYC;
+
+ ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
+
+ mutex_unlock(&data->update_lock);
+ return ret < 0 ? ret : count;
+}
+
+static const int tc654_pwm_map[16] = { 77, 88, 102, 112, 124, 136, 148, 160,
+ 172, 184, 196, 207, 219, 231, 243, 255};
+
+static ssize_t pwm_show(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct tc654_data *data = tc654_update_client(dev);
+ int pwm;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (data->config & TC654_REG_CONFIG_SDM)
+ pwm = 0;
+ else
+ pwm = tc654_pwm_map[data->duty_cycle];
+
+ return sprintf(buf, "%d\n", pwm);
+}
+
+static int _set_pwm(struct tc654_data *data, unsigned long val)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ mutex_lock(&data->update_lock);
+
+ if (val == 0) {
+ data->config |= TC654_REG_CONFIG_SDM;
+ data->duty_cycle = 0;
+ } else {
+ data->config &= ~TC654_REG_CONFIG_SDM;
+ data->duty_cycle = val - 1;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE,
+ data->duty_cycle);
+
+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;
+}
+
+static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
+static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, fan_alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, fan_alarm, 1);
+static SENSOR_DEVICE_ATTR_RW(fan1_pulses, fan_pulses, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_pulses, fan_pulses, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm1_mode, pwm_mode, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
+
+/* Driver data */
+static struct attribute *tc654_attrs[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_pulses.dev_attr.attr,
+ &sensor_dev_attr_fan2_pulses.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ NULL
+};
+
+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)
+{
+ struct device *dev = &client->dev;
+ struct tc654_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ data->config = ret;
+
+ hwmon_dev =
+ devm_hwmon_device_register_with_groups(dev, client->name, data,
+ tc654_groups);
+ 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[] = {
+ {"tc654", 0},
+ {"tc655", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tc654_id);
+
+static struct i2c_driver tc654_driver = {
+ .driver = {
+ .name = "tc654",
+ },
+ .probe_new = tc654_probe,
+ .id_table = tc654_id,
+};
+
+module_i2c_driver(tc654_driver);
+
+MODULE_AUTHOR("Allied Telesis Labs");
+MODULE_DESCRIPTION("Microchip TC654/TC655 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c
new file mode 100644
index 000000000..ace55da97
--- /dev/null
+++ b/drivers/hwmon/tc74.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * An hwmon driver for the Microchip TC74
+ *
+ * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name>
+ *
+ * Based on ad7414.c:
+ * Copyright 2006 Stefan Roese, DENX Software Engineering
+ * Copyright 2008 Sean MacLennan, PIKA Technologies
+ * Copyright 2008 Frank Edelhaeuser, Spansion Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+/* TC74 registers */
+#define TC74_REG_TEMP 0x00
+#define TC74_REG_CONFIG 0x01
+
+struct tc74_data {
+ struct i2c_client *client;
+ struct mutex lock; /* atomic read data updates */
+ bool valid; /* validity of fields below */
+ unsigned long next_update; /* In jiffies */
+ s8 temp_input; /* Temp value in dC */
+};
+
+static int tc74_update_device(struct device *dev)
+{
+ struct tc74_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = mutex_lock_interruptible(&data->lock);
+ if (ret)
+ return ret;
+
+ if (time_after(jiffies, data->next_update) || !data->valid) {
+ s32 value;
+
+ value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+ if (value < 0) {
+ dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n",
+ (int)value);
+
+ ret = value;
+ goto ret_unlock;
+ }
+
+ if (!(value & BIT(6))) {
+ /* not ready yet */
+
+ ret = -EAGAIN;
+ goto ret_unlock;
+ }
+
+ value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP);
+ if (value < 0) {
+ dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n",
+ (int)value);
+
+ ret = value;
+ goto ret_unlock;
+ }
+
+ data->temp_input = value;
+ data->next_update = jiffies + HZ / 4;
+ data->valid = true;
+ }
+
+ret_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static ssize_t temp_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tc74_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = tc74_update_device(dev);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", data->temp_input * 1000);
+}
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
+
+static struct attribute *tc74_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(tc74);
+
+static int tc74_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tc74_data *data;
+ struct device *hwmon_dev;
+ s32 conf;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EOPNOTSUPP;
+
+ data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+
+ /* Make sure the chip is powered up. */
+ conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+ if (conf < 0) {
+ dev_err(dev, "unable to read config register\n");
+
+ return conf;
+ }
+
+ if (conf & 0x3f) {
+ dev_err(dev, "invalid config register value\n");
+
+ return -ENODEV;
+ }
+
+ if (conf & BIT(7)) {
+ s32 ret;
+
+ conf &= ~BIT(7);
+
+ ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf);
+ if (ret)
+ dev_warn(dev, "unable to disable STANDBY\n");
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name,
+ data, tc74_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id tc74_id[] = {
+ { "tc74", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tc74_id);
+
+static struct i2c_driver tc74_driver = {
+ .driver = {
+ .name = "tc74",
+ },
+ .probe_new = tc74_probe,
+ .id_table = tc74_id,
+};
+
+module_i2c_driver(tc74_driver);
+
+MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>");
+
+MODULE_DESCRIPTION("TC74 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c
new file mode 100644
index 000000000..81cdb0129
--- /dev/null
+++ b/drivers/hwmon/thmc50.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * thmc50.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl>
+ * Based on 2.4 driver by Frodo Looijaard <frodol@dds.nl> and
+ * Philip Edelbrock <phil@netroedge.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+
+MODULE_LICENSE("GPL");
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+/* Insmod parameters */
+enum chips { thmc50, adm1022 };
+
+static unsigned short adm1022_temp3[16];
+static unsigned int adm1022_temp3_num;
+module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0);
+MODULE_PARM_DESC(adm1022_temp3,
+ "List of adapter,address pairs to enable 3rd temperature (ADM1022 only)");
+
+/* Many THMC50 constants specified below */
+
+/* The THMC50 registers */
+#define THMC50_REG_CONF 0x40
+#define THMC50_REG_COMPANY_ID 0x3E
+#define THMC50_REG_DIE_CODE 0x3F
+#define THMC50_REG_ANALOG_OUT 0x19
+/*
+ * The mirror status register cannot be used as
+ * reading it does not clear alarms.
+ */
+#define THMC50_REG_INTR 0x41
+
+static const u8 THMC50_REG_TEMP[] = { 0x27, 0x26, 0x20 };
+static const u8 THMC50_REG_TEMP_MIN[] = { 0x3A, 0x38, 0x2C };
+static const u8 THMC50_REG_TEMP_MAX[] = { 0x39, 0x37, 0x2B };
+static const u8 THMC50_REG_TEMP_CRITICAL[] = { 0x13, 0x14, 0x14 };
+static const u8 THMC50_REG_TEMP_DEFAULT[] = { 0x17, 0x18, 0x18 };
+
+#define THMC50_REG_CONF_nFANOFF 0x20
+#define THMC50_REG_CONF_PROGRAMMED 0x08
+
+/* Each client has this additional data */
+struct thmc50_data {
+ struct i2c_client *client;
+ const struct attribute_group *groups[3];
+
+ struct mutex update_lock;
+ enum chips type;
+ unsigned long last_updated; /* In jiffies */
+ char has_temp3; /* !=0 if it is ADM1022 in temp3 mode */
+ bool valid; /* true if following fields are valid */
+
+ /* Register values */
+ s8 temp_input[3];
+ s8 temp_max[3];
+ s8 temp_min[3];
+ s8 temp_critical[3];
+ u8 analog_out;
+ u8 alarms;
+};
+
+static struct thmc50_data *thmc50_update_device(struct device *dev)
+{
+ struct thmc50_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int timeout = HZ / 5 + (data->type == thmc50 ? HZ : 0);
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + timeout)
+ || !data->valid) {
+
+ int temps = data->has_temp3 ? 3 : 2;
+ int i;
+ int prog = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
+
+ prog &= THMC50_REG_CONF_PROGRAMMED;
+
+ for (i = 0; i < temps; i++) {
+ data->temp_input[i] = i2c_smbus_read_byte_data(client,
+ THMC50_REG_TEMP[i]);
+ data->temp_max[i] = i2c_smbus_read_byte_data(client,
+ THMC50_REG_TEMP_MAX[i]);
+ data->temp_min[i] = i2c_smbus_read_byte_data(client,
+ THMC50_REG_TEMP_MIN[i]);
+ data->temp_critical[i] =
+ i2c_smbus_read_byte_data(client,
+ prog ? THMC50_REG_TEMP_CRITICAL[i]
+ : THMC50_REG_TEMP_DEFAULT[i]);
+ }
+ data->analog_out =
+ i2c_smbus_read_byte_data(client, THMC50_REG_ANALOG_OUT);
+ data->alarms =
+ i2c_smbus_read_byte_data(client, THMC50_REG_INTR);
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static ssize_t analog_out_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct thmc50_data *data = thmc50_update_device(dev);
+ return sprintf(buf, "%d\n", data->analog_out);
+}
+
+static ssize_t analog_out_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thmc50_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int config;
+ unsigned long tmp;
+ int err;
+
+ err = kstrtoul(buf, 10, &tmp);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->analog_out = clamp_val(tmp, 0, 255);
+ i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT,
+ data->analog_out);
+
+ config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
+ if (data->analog_out == 0)
+ config &= ~THMC50_REG_CONF_nFANOFF;
+ else
+ config |= THMC50_REG_CONF_nFANOFF;
+ i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* There is only one PWM mode = DC */
+static ssize_t pwm_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
+
+/* Temperatures */
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct thmc50_data *data = thmc50_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_input[nr] * 1000);
+}
+
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct thmc50_data *data = thmc50_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_min[nr] * 1000);
+}
+
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct thmc50_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_min[nr] = clamp_val(val / 1000, -128, 127);
+ i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MIN[nr],
+ data->temp_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct thmc50_data *data = thmc50_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_max[nr] * 1000);
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct thmc50_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_max[nr] = clamp_val(val / 1000, -128, 127);
+ i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MAX[nr],
+ data->temp_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_critical_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct thmc50_data *data = thmc50_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_critical[nr] * 1000);
+}
+
+static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct thmc50_data *data = thmc50_update_device(dev);
+
+ return sprintf(buf, "%u\n", (data->alarms >> index) & 1);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
+static SENSOR_DEVICE_ATTR_RO(temp1_crit, temp_critical, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_crit, temp_critical, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
+static SENSOR_DEVICE_ATTR_RO(temp3_crit, temp_critical, 2);
+
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2);
+
+static SENSOR_DEVICE_ATTR_RW(pwm1, analog_out, 0);
+static SENSOR_DEVICE_ATTR_RO(pwm1_mode, pwm_mode, 0);
+
+static struct attribute *thmc50_attributes[] = {
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group thmc50_group = {
+ .attrs = thmc50_attributes,
+};
+
+/* for ADM1022 3rd temperature mode */
+static struct attribute *temp3_attributes[] = {
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group temp3_group = {
+ .attrs = temp3_attributes,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int thmc50_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ unsigned company;
+ unsigned revision;
+ unsigned config;
+ struct i2c_adapter *adapter = client->adapter;
+ const char *type_name;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_debug("thmc50: detect failed, smbus byte data not supported!\n");
+ return -ENODEV;
+ }
+
+ pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n",
+ client->addr, i2c_adapter_id(client->adapter));
+
+ company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID);
+ revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE);
+ config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
+ if (revision < 0xc0 || (config & 0x10))
+ return -ENODEV;
+
+ if (company == 0x41) {
+ int id = i2c_adapter_id(client->adapter);
+ int i;
+
+ type_name = "adm1022";
+ for (i = 0; i + 1 < adm1022_temp3_num; i += 2)
+ if (adm1022_temp3[i] == id &&
+ adm1022_temp3[i + 1] == client->addr) {
+ /* enable 2nd remote temp */
+ config |= (1 << 7);
+ i2c_smbus_write_byte_data(client,
+ THMC50_REG_CONF,
+ config);
+ break;
+ }
+ } else if (company == 0x49) {
+ type_name = "thmc50";
+ } else {
+ pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n");
+ return -ENODEV;
+ }
+
+ pr_debug("thmc50: Detected %s (version %x, revision %x)\n",
+ type_name, (revision >> 4) - 0xc, revision & 0xf);
+
+ strscpy(info->type, type_name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void thmc50_init_client(struct thmc50_data *data)
+{
+ struct i2c_client *client = data->client;
+ int config;
+
+ data->analog_out = i2c_smbus_read_byte_data(client,
+ THMC50_REG_ANALOG_OUT);
+ /* set up to at least 1 */
+ if (data->analog_out == 0) {
+ data->analog_out = 1;
+ i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT,
+ data->analog_out);
+ }
+ config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
+ config |= 0x1; /* start the chip if it is in standby mode */
+ if (data->type == adm1022 && (config & (1 << 7)))
+ data->has_temp3 = 1;
+ i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config);
+}
+
+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;
+ struct device *hwmon_dev;
+ int idx = 0;
+
+ data = devm_kzalloc(dev, sizeof(struct thmc50_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->type = i2c_match_id(thmc50_id, client)->driver_data;
+ mutex_init(&data->update_lock);
+
+ thmc50_init_client(data);
+
+ /* sysfs hooks */
+ data->groups[idx++] = &thmc50_group;
+
+ /* Register additional ADM1022 sysfs hooks */
+ if (data->has_temp3)
+ data->groups[idx++] = &temp3_group;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data, data->groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id thmc50_id[] = {
+ { "adm1022", adm1022 },
+ { "thmc50", thmc50 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, thmc50_id);
+
+static struct i2c_driver thmc50_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "thmc50",
+ },
+ .probe_new = thmc50_probe,
+ .id_table = thmc50_id,
+ .detect = thmc50_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(thmc50_driver);
+
+MODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>");
+MODULE_DESCRIPTION("THMC50 driver");
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
new file mode 100644
index 000000000..2bf496a62
--- /dev/null
+++ b/drivers/hwmon/tmp102.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Texas Instruments TMP102 SMBus temperature sensor driver
+ *
+ * Copyright (C) 2010 Steven King <sfking@fdwdc.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+#define DRIVER_NAME "tmp102"
+
+#define TMP102_TEMP_REG 0x00
+#define TMP102_CONF_REG 0x01
+/* note: these bit definitions are byte swapped */
+#define TMP102_CONF_SD 0x0100
+#define TMP102_CONF_TM 0x0200
+#define TMP102_CONF_POL 0x0400
+#define TMP102_CONF_F0 0x0800
+#define TMP102_CONF_F1 0x1000
+#define TMP102_CONF_R0 0x2000
+#define TMP102_CONF_R1 0x4000
+#define TMP102_CONF_OS 0x8000
+#define TMP102_CONF_EM 0x0010
+#define TMP102_CONF_AL 0x0020
+#define TMP102_CONF_CR0 0x0040
+#define TMP102_CONF_CR1 0x0080
+#define TMP102_TLOW_REG 0x02
+#define TMP102_THIGH_REG 0x03
+
+#define TMP102_CONFREG_MASK (TMP102_CONF_SD | TMP102_CONF_TM | \
+ TMP102_CONF_POL | TMP102_CONF_F0 | \
+ TMP102_CONF_F1 | TMP102_CONF_OS | \
+ TMP102_CONF_EM | TMP102_CONF_AL | \
+ TMP102_CONF_CR0 | TMP102_CONF_CR1)
+
+#define TMP102_CONFIG_CLEAR (TMP102_CONF_SD | TMP102_CONF_OS | \
+ TMP102_CONF_CR0)
+#define TMP102_CONFIG_SET (TMP102_CONF_TM | TMP102_CONF_EM | \
+ TMP102_CONF_CR1)
+
+#define CONVERSION_TIME_MS 35 /* in milli-seconds */
+
+struct tmp102 {
+ struct regmap *regmap;
+ u16 config_orig;
+ unsigned long ready_time;
+};
+
+/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
+static inline int tmp102_reg_to_mC(s16 val)
+{
+ return ((val & ~0x01) * 1000) / 128;
+}
+
+/* convert milliCelsius to left adjusted 13-bit TMP102 register value */
+static inline u16 tmp102_mC_to_reg(int val)
+{
+ return (val * 128) / 1000;
+}
+
+static int tmp102_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ struct tmp102 *tmp102 = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err, reg;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ /* Is it too early to return a conversion ? */
+ if (time_before(jiffies, tmp102->ready_time)) {
+ dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
+ return -EAGAIN;
+ }
+ reg = TMP102_TEMP_REG;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = TMP102_TLOW_REG;
+ break;
+ case hwmon_temp_max:
+ reg = TMP102_THIGH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ err = regmap_read(tmp102->regmap, reg, &regval);
+ if (err < 0)
+ return err;
+ *temp = tmp102_reg_to_mC(regval);
+
+ return 0;
+}
+
+static int tmp102_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long temp)
+{
+ struct tmp102 *tmp102 = dev_get_drvdata(dev);
+ int reg;
+
+ switch (attr) {
+ case hwmon_temp_max_hyst:
+ reg = TMP102_TLOW_REG;
+ break;
+ case hwmon_temp_max:
+ reg = TMP102_THIGH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ temp = clamp_val(temp, -256000, 255000);
+ return regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(temp));
+}
+
+static umode_t tmp102_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_max_hyst:
+ case hwmon_temp_max:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info *tmp102_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST),
+ NULL
+};
+
+static const struct hwmon_ops tmp102_hwmon_ops = {
+ .is_visible = tmp102_is_visible,
+ .read = tmp102_read,
+ .write = tmp102_write,
+};
+
+static const struct hwmon_chip_info tmp102_chip_info = {
+ .ops = &tmp102_hwmon_ops,
+ .info = tmp102_info,
+};
+
+static void tmp102_restore_config(void *data)
+{
+ struct tmp102 *tmp102 = data;
+
+ regmap_write(tmp102->regmap, TMP102_CONF_REG, tmp102->config_orig);
+}
+
+static bool tmp102_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg != TMP102_TEMP_REG;
+}
+
+static bool tmp102_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg == TMP102_TEMP_REG;
+}
+
+static const struct regmap_config tmp102_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = TMP102_THIGH_REG,
+ .writeable_reg = tmp102_is_writeable_reg,
+ .volatile_reg = tmp102_is_volatile_reg,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int tmp102_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct tmp102 *tmp102;
+ unsigned int regval;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(dev,
+ "adapter doesn't support SMBus word transactions\n");
+ return -ENODEV;
+ }
+
+ tmp102 = devm_kzalloc(dev, sizeof(*tmp102), GFP_KERNEL);
+ if (!tmp102)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, tmp102);
+
+ tmp102->regmap = devm_regmap_init_i2c(client, &tmp102_regmap_config);
+ if (IS_ERR(tmp102->regmap))
+ return PTR_ERR(tmp102->regmap);
+
+ err = regmap_read(tmp102->regmap, TMP102_CONF_REG, &regval);
+ if (err < 0) {
+ dev_err(dev, "error reading config register\n");
+ return err;
+ }
+
+ if ((regval & ~TMP102_CONFREG_MASK) !=
+ (TMP102_CONF_R0 | TMP102_CONF_R1)) {
+ dev_err(dev, "unexpected config register value\n");
+ return -ENODEV;
+ }
+
+ tmp102->config_orig = regval;
+
+ err = devm_add_action_or_reset(dev, tmp102_restore_config, tmp102);
+ if (err)
+ return err;
+
+ regval &= ~TMP102_CONFIG_CLEAR;
+ regval |= TMP102_CONFIG_SET;
+
+ err = regmap_write(tmp102->regmap, TMP102_CONF_REG, regval);
+ if (err < 0) {
+ dev_err(dev, "error writing config register\n");
+ return err;
+ }
+
+ /*
+ * Mark that we are not ready with data until the first
+ * conversion is complete
+ */
+ tmp102->ready_time = jiffies + msecs_to_jiffies(CONVERSION_TIME_MS);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ tmp102,
+ &tmp102_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev)) {
+ dev_dbg(dev, "unable to register hwmon device\n");
+ return PTR_ERR(hwmon_dev);
+ }
+ dev_info(dev, "initialized\n");
+
+ return 0;
+}
+
+static int tmp102_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tmp102 *tmp102 = i2c_get_clientdata(client);
+
+ return regmap_update_bits(tmp102->regmap, TMP102_CONF_REG,
+ TMP102_CONF_SD, TMP102_CONF_SD);
+}
+
+static int tmp102_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tmp102 *tmp102 = i2c_get_clientdata(client);
+ int err;
+
+ err = regmap_update_bits(tmp102->regmap, TMP102_CONF_REG,
+ TMP102_CONF_SD, 0);
+
+ tmp102->ready_time = jiffies + msecs_to_jiffies(CONVERSION_TIME_MS);
+
+ return err;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume);
+
+static const struct i2c_device_id tmp102_id[] = {
+ { "tmp102", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp102_id);
+
+static const struct of_device_id __maybe_unused tmp102_of_match[] = {
+ { .compatible = "ti,tmp102" },
+ { },
+};
+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 = pm_sleep_ptr(&tmp102_dev_pm_ops),
+ .probe_new = tmp102_probe,
+ .id_table = tmp102_id,
+};
+
+module_i2c_driver(tmp102_driver);
+
+MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
+MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c
new file mode 100644
index 000000000..56d5cbf36
--- /dev/null
+++ b/drivers/hwmon/tmp103.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments TMP103 SMBus temperature sensor driver
+ * Copyright (C) 2014 Heiko Schocher <hs@denx.de>
+ *
+ * Based on:
+ * Texas Instruments TMP102 SMBus temperature sensor driver
+ *
+ * Copyright (C) 2010 Steven King <sfking@fdwdc.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/regmap.h>
+
+#define TMP103_TEMP_REG 0x00
+#define TMP103_CONF_REG 0x01
+#define TMP103_TLOW_REG 0x02
+#define TMP103_THIGH_REG 0x03
+
+#define TMP103_CONF_M0 0x01
+#define TMP103_CONF_M1 0x02
+#define TMP103_CONF_LC 0x04
+#define TMP103_CONF_FL 0x08
+#define TMP103_CONF_FH 0x10
+#define TMP103_CONF_CR0 0x20
+#define TMP103_CONF_CR1 0x40
+#define TMP103_CONF_ID 0x80
+#define TMP103_CONF_SD (TMP103_CONF_M1)
+#define TMP103_CONF_SD_MASK (TMP103_CONF_M0 | TMP103_CONF_M1)
+
+#define TMP103_CONFIG (TMP103_CONF_CR1 | TMP103_CONF_M1)
+#define TMP103_CONFIG_MASK (TMP103_CONF_CR0 | TMP103_CONF_CR1 | \
+ TMP103_CONF_M0 | TMP103_CONF_M1)
+
+static inline int tmp103_reg_to_mc(s8 val)
+{
+ return val * 1000;
+}
+
+static inline u8 tmp103_mc_to_reg(int val)
+{
+ return DIV_ROUND_CLOSEST(val, 1000);
+}
+
+static int tmp103_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+ unsigned int regval;
+ 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;
+ }
+
+ err = regmap_read(regmap, reg, &regval);
+ if (err < 0)
+ return err;
+
+ *temp = tmp103_reg_to_mc(regval);
+
+ return 0;
+}
+
+static int tmp103_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long temp)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+ 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;
+ }
+
+ temp = clamp_val(temp, -55000, 127000);
+ return regmap_write(regmap, reg, tmp103_mc_to_reg(temp));
+}
+
+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 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 const struct hwmon_ops tmp103_hwmon_ops = {
+ .is_visible = tmp103_is_visible,
+ .read = tmp103_read,
+ .write = tmp103_write,
+};
+
+static const struct hwmon_chip_info tmp103_chip_info = {
+ .ops = &tmp103_hwmon_ops,
+ .info = tmp103_info,
+};
+
+static bool tmp103_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ return reg == TMP103_TEMP_REG;
+}
+
+static const struct regmap_config tmp103_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TMP103_THIGH_REG,
+ .volatile_reg = tmp103_regmap_is_volatile,
+};
+
+static int tmp103_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &tmp103_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ ret = regmap_update_bits(regmap, TMP103_CONF_REG, TMP103_CONFIG_MASK,
+ TMP103_CONFIG);
+ if (ret < 0) {
+ dev_err(&client->dev, "error writing config register\n");
+ return ret;
+ }
+
+ i2c_set_clientdata(client, regmap);
+ 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 tmp103_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ return regmap_update_bits(regmap, TMP103_CONF_REG,
+ TMP103_CONF_SD_MASK, 0);
+}
+
+static int tmp103_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ return regmap_update_bits(regmap, TMP103_CONF_REG,
+ TMP103_CONF_SD_MASK, TMP103_CONF_SD);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume);
+
+static const struct i2c_device_id tmp103_id[] = {
+ { "tmp103", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp103_id);
+
+static const struct of_device_id __maybe_unused tmp103_of_match[] = {
+ { .compatible = "ti,tmp103" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tmp103_of_match);
+
+static struct i2c_driver tmp103_driver = {
+ .driver = {
+ .name = "tmp103",
+ .of_match_table = of_match_ptr(tmp103_of_match),
+ .pm = pm_sleep_ptr(&tmp103_dev_pm_ops),
+ },
+ .probe_new = tmp103_probe,
+ .id_table = tmp103_id,
+};
+
+module_i2c_driver(tmp103_driver);
+
+MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
+MODULE_DESCRIPTION("Texas Instruments TMP103 temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c
new file mode 100644
index 000000000..acb4ba750
--- /dev/null
+++ b/drivers/hwmon/tmp108.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Texas Instruments TMP108 SMBus temperature sensor driver
+ *
+ * Copyright (C) 2016 John Muir <john@jmuir.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRIVER_NAME "tmp108"
+
+#define TMP108_REG_TEMP 0x00
+#define TMP108_REG_CONF 0x01
+#define TMP108_REG_TLOW 0x02
+#define TMP108_REG_THIGH 0x03
+
+#define TMP108_TEMP_MIN_MC -50000 /* Minimum millicelcius. */
+#define TMP108_TEMP_MAX_MC 127937 /* Maximum millicelcius. */
+
+/* Configuration register bits.
+ * Note: these bit definitions are byte swapped.
+ */
+#define TMP108_CONF_M0 0x0100 /* Sensor mode. */
+#define TMP108_CONF_M1 0x0200
+#define TMP108_CONF_TM 0x0400 /* Thermostat mode. */
+#define TMP108_CONF_FL 0x0800 /* Watchdog flag - TLOW */
+#define TMP108_CONF_FH 0x1000 /* Watchdog flag - THIGH */
+#define TMP108_CONF_CR0 0x2000 /* Conversion rate. */
+#define TMP108_CONF_CR1 0x4000
+#define TMP108_CONF_ID 0x8000
+#define TMP108_CONF_HYS0 0x0010 /* Hysteresis. */
+#define TMP108_CONF_HYS1 0x0020
+#define TMP108_CONF_POL 0x0080 /* Polarity of alert. */
+
+/* Defaults set by the hardware upon reset. */
+#define TMP108_CONF_DEFAULTS (TMP108_CONF_CR0 | TMP108_CONF_TM |\
+ TMP108_CONF_HYS0 | TMP108_CONF_M1)
+/* These bits are read-only. */
+#define TMP108_CONF_READ_ONLY (TMP108_CONF_FL | TMP108_CONF_FH |\
+ TMP108_CONF_ID)
+
+#define TMP108_CONF_MODE_MASK (TMP108_CONF_M0|TMP108_CONF_M1)
+#define TMP108_MODE_SHUTDOWN 0x0000
+#define TMP108_MODE_ONE_SHOT TMP108_CONF_M0
+#define TMP108_MODE_CONTINUOUS TMP108_CONF_M1 /* Default */
+ /* When M1 is set, M0 is ignored. */
+
+#define TMP108_CONF_CONVRATE_MASK (TMP108_CONF_CR0|TMP108_CONF_CR1)
+#define TMP108_CONVRATE_0P25HZ 0x0000
+#define TMP108_CONVRATE_1HZ TMP108_CONF_CR0 /* Default */
+#define TMP108_CONVRATE_4HZ TMP108_CONF_CR1
+#define TMP108_CONVRATE_16HZ (TMP108_CONF_CR0|TMP108_CONF_CR1)
+
+#define TMP108_CONF_HYSTERESIS_MASK (TMP108_CONF_HYS0|TMP108_CONF_HYS1)
+#define TMP108_HYSTERESIS_0C 0x0000
+#define TMP108_HYSTERESIS_1C TMP108_CONF_HYS0 /* Default */
+#define TMP108_HYSTERESIS_2C TMP108_CONF_HYS1
+#define TMP108_HYSTERESIS_4C (TMP108_CONF_HYS0|TMP108_CONF_HYS1)
+
+#define TMP108_CONVERSION_TIME_MS 30 /* in milli-seconds */
+
+struct tmp108 {
+ struct regmap *regmap;
+ u16 orig_config;
+ unsigned long ready_time;
+};
+
+/* convert 12-bit TMP108 register value to milliCelsius */
+static inline int tmp108_temp_reg_to_mC(s16 val)
+{
+ return (val & ~0x0f) * 1000 / 256;
+}
+
+/* convert milliCelsius to left adjusted 12-bit TMP108 register value */
+static inline u16 tmp108_mC_to_temp_reg(int val)
+{
+ return (val * 256) / 1000;
+}
+
+static int tmp108_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ struct tmp108 *tmp108 = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err, hyst;
+
+ if (type == hwmon_chip) {
+ if (attr == hwmon_chip_update_interval) {
+ err = regmap_read(tmp108->regmap, TMP108_REG_CONF,
+ &regval);
+ if (err < 0)
+ return err;
+ switch (regval & TMP108_CONF_CONVRATE_MASK) {
+ case TMP108_CONVRATE_0P25HZ:
+ default:
+ *temp = 4000;
+ break;
+ case TMP108_CONVRATE_1HZ:
+ *temp = 1000;
+ break;
+ case TMP108_CONVRATE_4HZ:
+ *temp = 250;
+ break;
+ case TMP108_CONVRATE_16HZ:
+ *temp = 63;
+ break;
+ }
+ return 0;
+ }
+ return -EOPNOTSUPP;
+ }
+
+ switch (attr) {
+ case hwmon_temp_input:
+ /* Is it too early to return a conversion ? */
+ if (time_before(jiffies, tmp108->ready_time)) {
+ dev_dbg(dev, "%s: Conversion not ready yet..\n",
+ __func__);
+ return -EAGAIN;
+ }
+ err = regmap_read(tmp108->regmap, TMP108_REG_TEMP, &regval);
+ if (err < 0)
+ return err;
+ *temp = tmp108_temp_reg_to_mC(regval);
+ break;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ err = regmap_read(tmp108->regmap, attr == hwmon_temp_min ?
+ TMP108_REG_TLOW : TMP108_REG_THIGH, &regval);
+ if (err < 0)
+ return err;
+ *temp = tmp108_temp_reg_to_mC(regval);
+ break;
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &regval);
+ if (err < 0)
+ return err;
+ *temp = !!(regval & (attr == hwmon_temp_min_alarm ?
+ TMP108_CONF_FL : TMP108_CONF_FH));
+ break;
+ case hwmon_temp_min_hyst:
+ case hwmon_temp_max_hyst:
+ err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &regval);
+ if (err < 0)
+ return err;
+ switch (regval & TMP108_CONF_HYSTERESIS_MASK) {
+ case TMP108_HYSTERESIS_0C:
+ default:
+ hyst = 0;
+ break;
+ case TMP108_HYSTERESIS_1C:
+ hyst = 1000;
+ break;
+ case TMP108_HYSTERESIS_2C:
+ hyst = 2000;
+ break;
+ case TMP108_HYSTERESIS_4C:
+ hyst = 4000;
+ break;
+ }
+ err = regmap_read(tmp108->regmap, attr == hwmon_temp_min_hyst ?
+ TMP108_REG_TLOW : TMP108_REG_THIGH, &regval);
+ if (err < 0)
+ return err;
+ *temp = tmp108_temp_reg_to_mC(regval);
+ if (attr == hwmon_temp_min_hyst)
+ *temp += hyst;
+ else
+ *temp -= hyst;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int tmp108_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long temp)
+{
+ struct tmp108 *tmp108 = dev_get_drvdata(dev);
+ u32 regval, mask;
+ int err;
+
+ if (type == hwmon_chip) {
+ if (attr == hwmon_chip_update_interval) {
+ if (temp < 156)
+ mask = TMP108_CONVRATE_16HZ;
+ else if (temp < 625)
+ mask = TMP108_CONVRATE_4HZ;
+ else if (temp < 2500)
+ mask = TMP108_CONVRATE_1HZ;
+ else
+ mask = TMP108_CONVRATE_0P25HZ;
+ return regmap_update_bits(tmp108->regmap,
+ TMP108_REG_CONF,
+ TMP108_CONF_CONVRATE_MASK,
+ mask);
+ }
+ return -EOPNOTSUPP;
+ }
+
+ switch (attr) {
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC);
+ return regmap_write(tmp108->regmap,
+ attr == hwmon_temp_min ?
+ TMP108_REG_TLOW : TMP108_REG_THIGH,
+ tmp108_mC_to_temp_reg(temp));
+ case hwmon_temp_min_hyst:
+ case hwmon_temp_max_hyst:
+ temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC);
+ err = regmap_read(tmp108->regmap,
+ attr == hwmon_temp_min_hyst ?
+ TMP108_REG_TLOW : TMP108_REG_THIGH,
+ &regval);
+ if (err < 0)
+ return err;
+ if (attr == hwmon_temp_min_hyst)
+ temp -= tmp108_temp_reg_to_mC(regval);
+ else
+ temp = tmp108_temp_reg_to_mC(regval) - temp;
+ if (temp < 500)
+ mask = TMP108_HYSTERESIS_0C;
+ else if (temp < 1500)
+ mask = TMP108_HYSTERESIS_1C;
+ else if (temp < 3000)
+ mask = TMP108_HYSTERESIS_2C;
+ else
+ mask = TMP108_HYSTERESIS_4C;
+ return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
+ TMP108_CONF_HYSTERESIS_MASK, mask);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t tmp108_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_chip && attr == hwmon_chip_update_interval)
+ return 0644;
+
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ return 0444;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_min_hyst:
+ case hwmon_temp_max_hyst:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
+static const struct hwmon_channel_info *tmp108_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
+ HWMON_T_MIN_HYST | HWMON_T_MAX_HYST |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM),
+ NULL
+};
+
+static const struct hwmon_ops tmp108_hwmon_ops = {
+ .is_visible = tmp108_is_visible,
+ .read = tmp108_read,
+ .write = tmp108_write,
+};
+
+static const struct hwmon_chip_info tmp108_chip_info = {
+ .ops = &tmp108_hwmon_ops,
+ .info = tmp108_info,
+};
+
+static void tmp108_restore_config(void *data)
+{
+ struct tmp108 *tmp108 = data;
+
+ regmap_write(tmp108->regmap, TMP108_REG_CONF, tmp108->orig_config);
+}
+
+static bool tmp108_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg != TMP108_REG_TEMP;
+}
+
+static bool tmp108_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* Configuration register must be volatile to enable FL and FH. */
+ return reg == TMP108_REG_TEMP || reg == TMP108_REG_CONF;
+}
+
+static const struct regmap_config tmp108_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = TMP108_REG_THIGH,
+ .writeable_reg = tmp108_is_writeable_reg,
+ .volatile_reg = tmp108_is_volatile_reg,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int tmp108_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct tmp108 *tmp108;
+ int err;
+ u32 config;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(dev,
+ "adapter doesn't support SMBus word transactions\n");
+ return -ENODEV;
+ }
+
+ tmp108 = devm_kzalloc(dev, sizeof(*tmp108), GFP_KERNEL);
+ if (!tmp108)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, tmp108);
+
+ tmp108->regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config);
+ if (IS_ERR(tmp108->regmap)) {
+ err = PTR_ERR(tmp108->regmap);
+ dev_err(dev, "regmap init failed: %d", err);
+ return err;
+ }
+
+ err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &config);
+ if (err < 0) {
+ dev_err(dev, "error reading config register: %d", err);
+ return err;
+ }
+ tmp108->orig_config = config;
+
+ /* Only continuous mode is supported. */
+ config &= ~TMP108_CONF_MODE_MASK;
+ config |= TMP108_MODE_CONTINUOUS;
+
+ /* Only comparator mode is supported. */
+ config &= ~TMP108_CONF_TM;
+
+ err = regmap_write(tmp108->regmap, TMP108_REG_CONF, config);
+ if (err < 0) {
+ dev_err(dev, "error writing config register: %d", err);
+ return err;
+ }
+
+ tmp108->ready_time = jiffies;
+ if ((tmp108->orig_config & TMP108_CONF_MODE_MASK) ==
+ TMP108_MODE_SHUTDOWN)
+ tmp108->ready_time +=
+ msecs_to_jiffies(TMP108_CONVERSION_TIME_MS);
+
+ err = devm_add_action_or_reset(dev, tmp108_restore_config, tmp108);
+ if (err) {
+ dev_err(dev, "add action or reset failed: %d", err);
+ return err;
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ tmp108,
+ &tmp108_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static int tmp108_suspend(struct device *dev)
+{
+ struct tmp108 *tmp108 = dev_get_drvdata(dev);
+
+ return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
+ TMP108_CONF_MODE_MASK, TMP108_MODE_SHUTDOWN);
+}
+
+static int tmp108_resume(struct device *dev)
+{
+ struct tmp108 *tmp108 = dev_get_drvdata(dev);
+ int err;
+
+ err = regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
+ TMP108_CONF_MODE_MASK, TMP108_MODE_CONTINUOUS);
+ tmp108->ready_time = jiffies +
+ msecs_to_jiffies(TMP108_CONVERSION_TIME_MS);
+ return err;
+}
+
+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 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tmp108_of_ids[] = {
+ { .compatible = "ti,tmp108", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, tmp108_of_ids);
+#endif
+
+static struct i2c_driver tmp108_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = pm_sleep_ptr(&tmp108_dev_pm_ops),
+ .of_match_table = of_match_ptr(tmp108_of_ids),
+ },
+ .probe_new = tmp108_probe,
+ .id_table = tmp108_i2c_ids,
+};
+
+module_i2c_driver(tmp108_driver);
+
+MODULE_AUTHOR("John Muir <john@jmuir.com>");
+MODULE_DESCRIPTION("Texas Instruments TMP108 temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
new file mode 100644
index 000000000..f358ba679
--- /dev/null
+++ b/drivers/hwmon/tmp401.c
@@ -0,0 +1,779 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* tmp401.c
+ *
+ * Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com>
+ * Preliminary tmp411 support by:
+ * Gabriel Konat, Sander Leget, Wouter Willems
+ * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
+ *
+ * Cleanup and support for TMP431 and TMP432 by Guenter Roeck
+ * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
+ */
+
+/*
+ * Driver for the Texas Instruments TMP401 SMBUS temperature sensor IC.
+ *
+ * Note this IC is in some aspect similar to the LM90, but it has quite a
+ * few differences too, for example the local temp has a higher resolution
+ * and thus has 16 bits registers for its value and limit instead of 8 bits.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.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 };
+
+/*
+ * The TMP401 registers, note some registers have different addresses for
+ * reading and writing
+ */
+#define TMP401_STATUS 0x02
+#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[7][3] = {
+ { 0x00, 0x01, 0x23 }, /* temp */
+ { 0x06, 0x08, 0x16 }, /* low limit */
+ { 0x05, 0x07, 0x15 }, /* high limit */
+ { 0x20, 0x19, 0x1a }, /* therm (crit) limit */
+ { 0x30, 0x34, 0x00 }, /* lowest */
+ { 0x32, 0xf6, 0x00 }, /* highest */
+};
+
+/* [0] = fault, [1] = low, [2] = high, [3] = therm/crit */
+static const u8 TMP432_STATUS_REG[] = {
+ 0x1b, 0x36, 0x35, 0x37 };
+
+/* Flags */
+#define TMP401_CONFIG_RANGE BIT(2)
+#define TMP401_CONFIG_SHUTDOWN BIT(6)
+#define TMP401_STATUS_LOCAL_CRIT BIT(0)
+#define TMP401_STATUS_REMOTE_CRIT BIT(1)
+#define TMP401_STATUS_REMOTE_OPEN BIT(2)
+#define TMP401_STATUS_REMOTE_LOW BIT(3)
+#define TMP401_STATUS_REMOTE_HIGH BIT(4)
+#define TMP401_STATUS_LOCAL_LOW BIT(5)
+#define TMP401_STATUS_LOCAL_HIGH BIT(6)
+
+/* On TMP432, each status has its own register */
+#define TMP432_STATUS_LOCAL BIT(0)
+#define TMP432_STATUS_REMOTE1 BIT(1)
+#define TMP432_STATUS_REMOTE2 BIT(2)
+
+/* Manufacturer / Device ID's */
+#define TMP401_MANUFACTURER_ID 0x55
+#define TMP401_DEVICE_ID 0x11
+#define TMP411A_DEVICE_ID 0x12
+#define TMP411B_DEVICE_ID 0x13
+#define TMP411C_DEVICE_ID 0x10
+#define TMP431_DEVICE_ID 0x31
+#define TMP432_DEVICE_ID 0x32
+#define TMP435_DEVICE_ID 0x35
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id tmp401_id[] = {
+ { "tmp401", tmp401 },
+ { "tmp411", tmp411 },
+ { "tmp431", tmp431 },
+ { "tmp432", tmp432 },
+ { "tmp435", tmp435 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp401_id);
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct tmp401_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct mutex update_lock;
+ enum chips kind;
+
+ bool extended_range;
+
+ /* 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;
+};
+
+/* regmap */
+
+static bool tmp401_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ 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;
+ }
+}
+
+static int tmp401_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct tmp401_data *data = context;
+ struct i2c_client *client = data->client;
+ int regval;
+
+ 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;
+ }
+ /* 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;
+ }
+ break;
+ default:
+ regval = i2c_smbus_read_byte_data(client, reg);
+ if (regval < 0)
+ return regval;
+ *val = regval;
+ break;
+ }
+ return 0;
+}
+
+static int tmp401_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct tmp401_data *data = context;
+ struct i2c_client *client = data->client;
+
+ 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 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,
+};
+
+/* temperature conversion */
+
+static int tmp401_register_to_temp(u16 reg, bool extended)
+{
+ int temp = reg;
+
+ if (extended)
+ temp -= 64 * 256;
+
+ return DIV_ROUND_CLOSEST(temp * 125, 32);
+}
+
+static u16 tmp401_temp_to_register(long temp, bool extended, int zbits)
+{
+ if (extended) {
+ 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;
+}
+
+/* hwmon API functions */
+
+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,
+};
+
+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,
+};
+
+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, &regval);
+ 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, &regval);
+ if (ret < 0)
+ goto unlock;
+ *val = tmp401_register_to_temp(regval, data->extended_range);
+ ret = regmap_read(regmap, TMP401_TEMP_CRIT_HYST, &regval);
+ 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, &regval);
+ if (ret < 0)
+ return ret;
+ *val = !!(regval & BIT(channel));
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int tmp401_temp_write(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, temp;
+
+ mutex_lock(&data->update_lock);
+ 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, &regval);
+ 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 ret;
+}
+
+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, &regval);
+ if (ret < 0)
+ return ret;
+ *val = (1 << (7 - regval)) * 125;
+ break;
+ case hwmon_chip_temp_reset_history:
+ *val = 0;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int tmp401_set_convrate(struct regmap *regmap, long val)
+{
+ int rate;
+
+ /*
+ * For valid rates, interval can be calculated as
+ * interval = (1 << (7 - rate)) * 125;
+ * Rounded rate is therefore
+ * rate = 7 - __fls(interval * 4 / (125 * 3));
+ * Use clamp_val() to avoid overflows, and to ensure valid input
+ * for __fls.
+ */
+ val = clamp_val(val, 125, 16000);
+ rate = 7 - __fls(val * 4 / (125 * 3));
+ return regmap_write(regmap, TMP401_CONVERSION_RATE, rate);
+}
+
+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;
+
+ 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);
+
+ return err;
+}
+
+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;
+ }
+}
+
+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 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 hwmon_ops tmp401_ops = {
+ .is_visible = tmp401_is_visible,
+ .read = tmp401_read,
+ .write = tmp401_write,
+};
+
+/* chip initialization, detect, probe */
+
+static int tmp401_init_client(struct tmp401_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ u32 config, config_orig;
+ int ret;
+ u32 val = 0;
+ s32 nfactor = 0;
+
+ /* 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) */
+ ret = regmap_read(regmap, TMP401_CONFIG, &config);
+ if (ret < 0)
+ return ret;
+
+ config_orig = config;
+ config &= ~TMP401_CONFIG_SHUTDOWN;
+
+ 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 0;
+}
+
+static int tmp401_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ enum chips kind;
+ struct i2c_adapter *adapter = client->adapter;
+ u8 reg;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Detect and identify the chip */
+ reg = i2c_smbus_read_byte_data(client, TMP401_MANUFACTURER_ID_REG);
+ if (reg != TMP401_MANUFACTURER_ID)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
+
+ switch (reg) {
+ case TMP401_DEVICE_ID:
+ if (client->addr != 0x4c)
+ return -ENODEV;
+ kind = tmp401;
+ break;
+ case TMP411A_DEVICE_ID:
+ if (client->addr != 0x4c)
+ return -ENODEV;
+ kind = tmp411;
+ break;
+ case TMP411B_DEVICE_ID:
+ if (client->addr != 0x4d)
+ return -ENODEV;
+ kind = tmp411;
+ break;
+ case TMP411C_DEVICE_ID:
+ if (client->addr != 0x4e)
+ return -ENODEV;
+ kind = tmp411;
+ break;
+ case TMP431_DEVICE_ID:
+ if (client->addr != 0x4c && client->addr != 0x4d)
+ return -ENODEV;
+ kind = tmp431;
+ break;
+ case TMP432_DEVICE_ID:
+ if (client->addr != 0x4c && client->addr != 0x4d)
+ return -ENODEV;
+ kind = tmp432;
+ break;
+ case TMP435_DEVICE_ID:
+ kind = tmp435;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG);
+ if (reg & 0x1b)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP401_CONVERSION_RATE);
+ /* Datasheet says: 0x1-0x6 */
+ if (reg > 15)
+ return -ENODEV;
+
+ strscpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int tmp401_probe(struct i2c_client *client)
+{
+ static const char * const names[] = {
+ "TMP401", "TMP411", "TMP431", "TMP432", "TMP435"
+ };
+ struct device *dev = &client->dev;
+ struct hwmon_channel_info *info;
+ struct device *hwmon_dev;
+ struct tmp401_data *data;
+ int status;
+
+ data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+ data->kind = i2c_match_id(tmp401_id, client)->driver_data;
+
+ data->regmap = devm_regmap_init(dev, NULL, data, &tmp401_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ /* initialize configuration data */
+ data->chip.ops = &tmp401_ops;
+ data->chip.info = data->info;
+
+ data->info[0] = &data->chip_info;
+ data->info[1] = &data->temp_info;
+
+ info = &data->chip_info;
+ info->type = hwmon_chip;
+ info->config = data->chip_channel_config;
+
+ data->chip_channel_config[0] = HWMON_C_UPDATE_INTERVAL;
+
+ 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);
+
+ dev_info(dev, "Detected TI %s chip\n", names[data->kind]);
+
+ 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_new = tmp401_probe,
+ .id_table = tmp401_id,
+ .detect = tmp401_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(tmp401_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Texas Instruments TMP401 temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
new file mode 100644
index 000000000..45fd7fb5e
--- /dev/null
+++ b/drivers/hwmon/tmp421.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* tmp421.c
+ *
+ * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
+ * Preliminary support by:
+ * Melvin Rook, Raymond Ng
+ */
+
+/*
+ * Driver for the Texas Instruments TMP421 SMBus temperature sensor IC.
+ * Supported models: TMP421, TMP422, TMP423, TMP441, TMP442
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/sysfs.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f,
+ I2C_CLIENT_END };
+
+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[MAX_CHANNELS] = { 0x00, 0x01, 0x02, 0x03 };
+static const u8 TMP421_TEMP_LSB[MAX_CHANNELS] = { 0x10, 0x11, 0x12, 0x13 };
+
+/* Flags */
+#define TMP421_CONFIG_SHUTDOWN 0x40
+#define TMP421_CONFIG_RANGE 0x04
+
+/* Manufacturer / Device ID's */
+#define TMP421_MANUFACTURER_ID 0x55
+#define TMP421_DEVICE_ID 0x21
+#define TMP422_DEVICE_ID 0x22
+#define TMP423_DEVICE_ID 0x23
+#define TMP441_DEVICE_ID 0x41
+#define TMP442_DEVICE_ID 0x42
+
+static const struct i2c_device_id tmp421_id[] = {
+ { "tmp421", 2 },
+ { "tmp422", 3 },
+ { "tmp423", 4 },
+ { "tmp441", 2 },
+ { "tmp442", 3 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp421_id);
+
+static const struct of_device_id __maybe_unused tmp421_of_match[] = {
+ {
+ .compatible = "ti,tmp421",
+ .data = (void *)2
+ },
+ {
+ .compatible = "ti,tmp422",
+ .data = (void *)3
+ },
+ {
+ .compatible = "ti,tmp423",
+ .data = (void *)4
+ },
+ {
+ .compatible = "ti,tmp441",
+ .data = (void *)2
+ },
+ {
+ .compatible = "ti,tmp442",
+ .data = (void *)3
+ },
+ { },
+};
+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[MAX_CHANNELS + 1];
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_chip_info chip;
+ bool valid;
+ unsigned long last_updated;
+ unsigned long channels;
+ u8 config;
+ struct tmp421_channel channel[MAX_CHANNELS];
+};
+
+static int temp_from_raw(u16 reg, bool extended)
+{
+ /* Mask out status bits */
+ int temp = reg & ~0xf;
+
+ if (extended)
+ temp = temp - 64 * 256;
+ else
+ temp = (s16)temp;
+
+ return DIV_ROUND_CLOSEST(temp * 1000, 256);
+}
+
+static int tmp421_update_device(struct tmp421_data *data)
+{
+ 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) {
+ 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++) {
+ 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 = true;
+ }
+
+exit:
+ mutex_unlock(&data->update_lock);
+
+ 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 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = tmp421_update_device(tmp421);
+ if (ret)
+ return ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ 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;
+ /*
+ * Any of OPEN or /PVLD bits indicate a hardware mulfunction
+ * and the conversion result may be incorrect
+ */
+ *val = !!(tmp421->channel[channel].temp & 0x03);
+ return 0;
+ case hwmon_temp_enable:
+ *val = tmp421->channel[channel].enabled;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+}
+
+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:
+ 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 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);
+
+ /* Start conversions (disable shutdown if necessary) */
+ config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1);
+ if (config < 0) {
+ dev_err(&client->dev,
+ "Could not read configuration register (%d)\n", config);
+ return config;
+ }
+
+ config_orig = config;
+ config &= ~TMP421_CONFIG_SHUTDOWN;
+
+ if (config != config_orig) {
+ dev_info(&client->dev, "Enable monitoring chip\n");
+ i2c_smbus_write_byte_data(client, TMP421_CONFIG_REG_1, config);
+ }
+
+ return tmp421_enable_channels(data);
+}
+
+static int tmp421_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ enum chips kind;
+ struct i2c_adapter *adapter = client->adapter;
+ static const char * const names[] = {
+ "TMP421", "TMP422", "TMP423",
+ "TMP441", "TMP442"
+ };
+ int addr = client->addr;
+ u8 reg;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP421_MANUFACTURER_ID_REG);
+ if (reg != TMP421_MANUFACTURER_ID)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP421_CONVERSION_RATE_REG);
+ if (reg & 0xf8)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP421_STATUS_REG);
+ if (reg & 0x7f)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP421_DEVICE_ID_REG);
+ switch (reg) {
+ case TMP421_DEVICE_ID:
+ kind = tmp421;
+ break;
+ case TMP422_DEVICE_ID:
+ if (addr == 0x2a)
+ return -ENODEV;
+ kind = tmp422;
+ break;
+ case TMP423_DEVICE_ID:
+ if (addr != 0x4c && addr != 0x4d)
+ return -ENODEV;
+ kind = tmp423;
+ break;
+ case TMP441_DEVICE_ID:
+ kind = tmp441;
+ break;
+ case TMP442_DEVICE_ID:
+ if (addr != 0x4c && addr != 0x4d)
+ return -ENODEV;
+ kind = tmp442;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ 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)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct tmp421_data *data;
+ int i, err;
+
+ data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->update_lock);
+ if (client->dev.of_node)
+ data->channels = (unsigned long)
+ of_device_get_match_data(&client->dev);
+ else
+ data->channels = i2c_match_id(tmp421_id, client)->driver_data;
+ data->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;
+
+ err = tmp421_init_client(data);
+ if (err)
+ return err;
+
+ data->chip.ops = &tmp421_ops;
+ data->chip.info = data->info;
+
+ data->info[0] = &data->temp_info;
+
+ data->temp_info.type = hwmon_temp;
+ data->temp_info.config = data->temp_config;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &data->chip,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct i2c_driver tmp421_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "tmp421",
+ .of_match_table = of_match_ptr(tmp421_of_match),
+ },
+ .probe_new = tmp421_probe,
+ .id_table = tmp421_id,
+ .detect = tmp421_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(tmp421_driver);
+
+MODULE_AUTHOR("Andre Prendel <andre.prendel@gmx.de>");
+MODULE_DESCRIPTION("Texas Instruments TMP421/422/423/441/442 temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c
new file mode 100644
index 000000000..7814f39bd
--- /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, &regval);
+ if (err < 0)
+ break;
+ *val = !!(regval & BIT(channel + 7));
+ break;
+ case hwmon_temp_crit_alarm:
+ err = regmap_read(regmap, TMP464_THERM2_STATUS_REG, &regval);
+ 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, &regval);
+ 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], &regval);
+ if (err < 0)
+ break;
+ err = regmap_read(regmap, TMP464_TEMP_HYST_REG, &regval2);
+ if (err < 0)
+ break;
+ regval -= regval2;
+ *val = temp_from_reg(regval);
+ break;
+ case hwmon_temp_max:
+ err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], &regval);
+ if (err < 0)
+ break;
+ *val = temp_from_reg(regval);
+ break;
+ case hwmon_temp_crit_hyst:
+ err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], &regval);
+ if (err < 0)
+ break;
+ err = regmap_read(regmap, TMP464_TEMP_HYST_REG, &regval2);
+ if (err < 0)
+ break;
+ regval -= regval2;
+ *val = temp_from_reg(regval);
+ break;
+ case hwmon_temp_crit:
+ err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], &regval);
+ if (err < 0)
+ break;
+ *val = temp_from_reg(regval);
+ break;
+ case hwmon_temp_offset:
+ err = regmap_read(regmap, TMP464_TEMP_OFFSET_REG(channel), &regval);
+ 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), &regval);
+ 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], &regval);
+ 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, &regval);
+ 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, &regval);
+ 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
new file mode 100644
index 000000000..b9a93ee9c
--- /dev/null
+++ b/drivers/hwmon/tmp513.c
@@ -0,0 +1,771 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Texas Instruments TMP512, TMP513 power monitor chips
+ *
+ * TMP513:
+ * Thermal/Power Management with Triple Remote and
+ * Local Temperature Sensor and Current Shunt Monitor
+ * Datasheet: https://www.ti.com/lit/gpn/tmp513
+ *
+ * TMP512:
+ * Thermal/Power Management with Dual Remote
+ * and Local Temperature Sensor and Current Shunt Monitor
+ * Datasheet: https://www.ti.com/lit/gpn/tmp512
+ *
+ * Copyright (C) 2019 Eric Tremblay <etremblay@distech-controls.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#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/regmap.h>
+#include <linux/slab.h>
+#include <linux/util_macros.h>
+
+// Common register definition
+#define TMP51X_SHUNT_CONFIG 0x00
+#define TMP51X_TEMP_CONFIG 0x01
+#define TMP51X_STATUS 0x02
+#define TMP51X_SMBUS_ALERT 0x03
+#define TMP51X_SHUNT_CURRENT_RESULT 0x04
+#define TMP51X_BUS_VOLTAGE_RESULT 0x05
+#define TMP51X_POWER_RESULT 0x06
+#define TMP51X_BUS_CURRENT_RESULT 0x07
+#define TMP51X_LOCAL_TEMP_RESULT 0x08
+#define TMP51X_REMOTE_TEMP_RESULT_1 0x09
+#define TMP51X_REMOTE_TEMP_RESULT_2 0x0A
+#define TMP51X_SHUNT_CURRENT_H_LIMIT 0x0C
+#define TMP51X_SHUNT_CURRENT_L_LIMIT 0x0D
+#define TMP51X_BUS_VOLTAGE_H_LIMIT 0x0E
+#define TMP51X_BUS_VOLTAGE_L_LIMIT 0x0F
+#define TMP51X_POWER_LIMIT 0x10
+#define TMP51X_LOCAL_TEMP_LIMIT 0x11
+#define TMP51X_REMOTE_TEMP_LIMIT_1 0x12
+#define TMP51X_REMOTE_TEMP_LIMIT_2 0x13
+#define TMP51X_SHUNT_CALIBRATION 0x15
+#define TMP51X_N_FACTOR_AND_HYST_1 0x16
+#define TMP51X_N_FACTOR_2 0x17
+#define TMP51X_MAN_ID_REG 0xFE
+#define TMP51X_DEVICE_ID_REG 0xFF
+
+// TMP513 specific register definition
+#define TMP513_REMOTE_TEMP_RESULT_3 0x0B
+#define TMP513_REMOTE_TEMP_LIMIT_3 0x14
+#define TMP513_N_FACTOR_3 0x18
+
+// Common attrs, and NULL
+#define TMP51X_MANUFACTURER_ID 0x55FF
+
+#define TMP512_DEVICE_ID 0x22FF
+#define TMP513_DEVICE_ID 0x23FF
+
+// Default config
+#define TMP51X_SHUNT_CONFIG_DEFAULT 0x399F
+#define TMP51X_SHUNT_VALUE_DEFAULT 1000
+#define TMP51X_VBUS_RANGE_DEFAULT TMP51X_VBUS_RANGE_32V
+#define TMP51X_PGA_DEFAULT 8
+#define TMP51X_MAX_REGISTER_ADDR 0xFF
+
+#define TMP512_TEMP_CONFIG_DEFAULT 0xBF80
+#define TMP513_TEMP_CONFIG_DEFAULT 0xFF80
+
+// Mask and shift
+#define CURRENT_SENSE_VOLTAGE_320_MASK 0x1800
+#define CURRENT_SENSE_VOLTAGE_160_MASK 0x1000
+#define CURRENT_SENSE_VOLTAGE_80_MASK 0x0800
+#define CURRENT_SENSE_VOLTAGE_40_MASK 0
+
+#define TMP51X_BUS_VOLTAGE_MASK 0x2000
+#define TMP51X_NFACTOR_MASK 0xFF00
+#define TMP51X_HYST_MASK 0x00FF
+
+#define TMP51X_BUS_VOLTAGE_SHIFT 3
+#define TMP51X_TEMP_SHIFT 3
+
+// Alarms
+#define TMP51X_SHUNT_CURRENT_H_LIMIT_POS 15
+#define TMP51X_SHUNT_CURRENT_L_LIMIT_POS 14
+#define TMP51X_BUS_VOLTAGE_H_LIMIT_POS 13
+#define TMP51X_BUS_VOLTAGE_L_LIMIT_POS 12
+#define TMP51X_POWER_LIMIT_POS 11
+#define TMP51X_LOCAL_TEMP_LIMIT_POS 10
+#define TMP51X_REMOTE_TEMP_LIMIT_1_POS 9
+#define TMP51X_REMOTE_TEMP_LIMIT_2_POS 8
+#define TMP513_REMOTE_TEMP_LIMIT_3_POS 7
+
+#define TMP51X_VBUS_RANGE_32V 32000000
+#define TMP51X_VBUS_RANGE_16V 16000000
+
+// Max and Min value
+#define MAX_BUS_VOLTAGE_32_LIMIT 32764
+#define MAX_BUS_VOLTAGE_16_LIMIT 16382
+
+// Max possible value is -256 to +256 but datasheet indicated -40 to 125.
+#define MAX_TEMP_LIMIT 125000
+#define MIN_TEMP_LIMIT -40000
+
+#define MAX_TEMP_HYST 127500
+
+static const u8 TMP51X_TEMP_INPUT[4] = {
+ TMP51X_LOCAL_TEMP_RESULT,
+ TMP51X_REMOTE_TEMP_RESULT_1,
+ TMP51X_REMOTE_TEMP_RESULT_2,
+ TMP513_REMOTE_TEMP_RESULT_3
+};
+
+static const u8 TMP51X_TEMP_CRIT[4] = {
+ TMP51X_LOCAL_TEMP_LIMIT,
+ TMP51X_REMOTE_TEMP_LIMIT_1,
+ TMP51X_REMOTE_TEMP_LIMIT_2,
+ TMP513_REMOTE_TEMP_LIMIT_3
+};
+
+static const u8 TMP51X_TEMP_CRIT_ALARM[4] = {
+ TMP51X_LOCAL_TEMP_LIMIT_POS,
+ TMP51X_REMOTE_TEMP_LIMIT_1_POS,
+ TMP51X_REMOTE_TEMP_LIMIT_2_POS,
+ TMP513_REMOTE_TEMP_LIMIT_3_POS
+};
+
+static const u8 TMP51X_TEMP_CRIT_HYST[4] = {
+ TMP51X_N_FACTOR_AND_HYST_1,
+ TMP51X_N_FACTOR_AND_HYST_1,
+ TMP51X_N_FACTOR_AND_HYST_1,
+ TMP51X_N_FACTOR_AND_HYST_1
+};
+
+static const u8 TMP51X_CURR_INPUT[2] = {
+ TMP51X_SHUNT_CURRENT_RESULT,
+ TMP51X_BUS_CURRENT_RESULT
+};
+
+static struct regmap_config tmp51x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = TMP51X_MAX_REGISTER_ADDR,
+};
+
+enum tmp51x_ids {
+ tmp512, tmp513
+};
+
+struct tmp51x_data {
+ u16 shunt_config;
+ u16 pga_gain;
+ u32 vbus_range_uvolt;
+
+ u16 temp_config;
+ u32 nfactor[3];
+
+ u32 shunt_uohms;
+
+ u32 curr_lsb_ua;
+ u32 pwr_lsb_uw;
+
+ enum tmp51x_ids id;
+ struct regmap *regmap;
+};
+
+// Set the shift based on the gain 8=4, 4=3, 2=2, 1=1
+static inline u8 tmp51x_get_pga_shift(struct tmp51x_data *data)
+{
+ return 5 - ffs(data->pga_gain);
+}
+
+static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
+ unsigned int regval, long *val)
+{
+ switch (reg) {
+ case TMP51X_STATUS:
+ *val = (regval >> pos) & 1;
+ break;
+ case TMP51X_SHUNT_CURRENT_RESULT:
+ case TMP51X_SHUNT_CURRENT_H_LIMIT:
+ case TMP51X_SHUNT_CURRENT_L_LIMIT:
+ /*
+ * The valus is read in voltage in the chip but reported as
+ * current to the user.
+ * 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));
+ *val = DIV_ROUND_CLOSEST(*val * 10000, data->shunt_uohms);
+ break;
+ case TMP51X_BUS_VOLTAGE_RESULT:
+ case TMP51X_BUS_VOLTAGE_H_LIMIT:
+ case TMP51X_BUS_VOLTAGE_L_LIMIT:
+ // 1lsb = 4mV
+ *val = (regval >> TMP51X_BUS_VOLTAGE_SHIFT) * 4;
+ break;
+ case TMP51X_POWER_RESULT:
+ case TMP51X_POWER_LIMIT:
+ // Power = (current * BusVoltage) / 5000
+ *val = regval * data->pwr_lsb_uw;
+ break;
+ case TMP51X_BUS_CURRENT_RESULT:
+ // Current = (ShuntVoltage * CalibrationRegister) / 4096
+ *val = sign_extend32(regval, 16) * data->curr_lsb_ua;
+ *val = DIV_ROUND_CLOSEST(*val, 1000);
+ break;
+ case TMP51X_LOCAL_TEMP_RESULT:
+ case TMP51X_REMOTE_TEMP_RESULT_1:
+ case TMP51X_REMOTE_TEMP_RESULT_2:
+ case TMP513_REMOTE_TEMP_RESULT_3:
+ case TMP51X_LOCAL_TEMP_LIMIT:
+ case TMP51X_REMOTE_TEMP_LIMIT_1:
+ case TMP51X_REMOTE_TEMP_LIMIT_2:
+ case TMP513_REMOTE_TEMP_LIMIT_3:
+ // 1lsb = 0.0625 degrees centigrade
+ *val = sign_extend32(regval, 16) >> TMP51X_TEMP_SHIFT;
+ *val = DIV_ROUND_CLOSEST(*val * 625, 10);
+ break;
+ case TMP51X_N_FACTOR_AND_HYST_1:
+ // 1lsb = 0.5 degrees centigrade
+ *val = (regval & TMP51X_HYST_MASK) * 500;
+ break;
+ default:
+ // Programmer goofed
+ WARN_ON_ONCE(1);
+ *val = 0;
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int tmp51x_set_value(struct tmp51x_data *data, u8 reg, long val)
+{
+ int regval, max_val;
+ u32 mask = 0;
+
+ switch (reg) {
+ case TMP51X_SHUNT_CURRENT_H_LIMIT:
+ case TMP51X_SHUNT_CURRENT_L_LIMIT:
+ /*
+ * The user enter current value and we convert it to
+ * voltage. 1lsb = 10uV
+ */
+ val = DIV_ROUND_CLOSEST(val * data->shunt_uohms, 10000);
+ max_val = U16_MAX >> tmp51x_get_pga_shift(data);
+ regval = clamp_val(val, -max_val, max_val);
+ break;
+ case TMP51X_BUS_VOLTAGE_H_LIMIT:
+ case TMP51X_BUS_VOLTAGE_L_LIMIT:
+ // 1lsb = 4mV
+ max_val = (data->vbus_range_uvolt == TMP51X_VBUS_RANGE_32V) ?
+ MAX_BUS_VOLTAGE_32_LIMIT : MAX_BUS_VOLTAGE_16_LIMIT;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 4), 0, max_val);
+ regval = val << TMP51X_BUS_VOLTAGE_SHIFT;
+ break;
+ case TMP51X_POWER_LIMIT:
+ regval = clamp_val(DIV_ROUND_CLOSEST(val, data->pwr_lsb_uw), 0,
+ U16_MAX);
+ break;
+ case TMP51X_LOCAL_TEMP_LIMIT:
+ case TMP51X_REMOTE_TEMP_LIMIT_1:
+ case TMP51X_REMOTE_TEMP_LIMIT_2:
+ case TMP513_REMOTE_TEMP_LIMIT_3:
+ // 1lsb = 0.0625 degrees centigrade
+ val = clamp_val(val, MIN_TEMP_LIMIT, MAX_TEMP_LIMIT);
+ regval = DIV_ROUND_CLOSEST(val * 10, 625) << TMP51X_TEMP_SHIFT;
+ break;
+ case TMP51X_N_FACTOR_AND_HYST_1:
+ // 1lsb = 0.5 degrees centigrade
+ val = clamp_val(val, 0, MAX_TEMP_HYST);
+ regval = DIV_ROUND_CLOSEST(val, 500);
+ mask = TMP51X_HYST_MASK;
+ break;
+ default:
+ // Programmer goofed
+ WARN_ON_ONCE(1);
+ return -EOPNOTSUPP;
+ }
+
+ if (mask == 0)
+ return regmap_write(data->regmap, reg, regval);
+ else
+ return regmap_update_bits(data->regmap, reg, mask, regval);
+}
+
+static u8 tmp51x_get_reg(enum hwmon_sensor_types type, u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return TMP51X_TEMP_INPUT[channel];
+ case hwmon_temp_crit_alarm:
+ return TMP51X_STATUS;
+ case hwmon_temp_crit:
+ return TMP51X_TEMP_CRIT[channel];
+ case hwmon_temp_crit_hyst:
+ return TMP51X_TEMP_CRIT_HYST[channel];
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ return TMP51X_BUS_VOLTAGE_RESULT;
+ case hwmon_in_lcrit_alarm:
+ case hwmon_in_crit_alarm:
+ return TMP51X_STATUS;
+ case hwmon_in_lcrit:
+ return TMP51X_BUS_VOLTAGE_L_LIMIT;
+ case hwmon_in_crit:
+ return TMP51X_BUS_VOLTAGE_H_LIMIT;
+ }
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ return TMP51X_CURR_INPUT[channel];
+ case hwmon_curr_lcrit_alarm:
+ case hwmon_curr_crit_alarm:
+ return TMP51X_STATUS;
+ case hwmon_curr_lcrit:
+ return TMP51X_SHUNT_CURRENT_L_LIMIT;
+ case hwmon_curr_crit:
+ return TMP51X_SHUNT_CURRENT_H_LIMIT;
+ }
+ break;
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ return TMP51X_POWER_RESULT;
+ case hwmon_power_crit_alarm:
+ return TMP51X_STATUS;
+ case hwmon_power_crit:
+ return TMP51X_POWER_LIMIT;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static u8 tmp51x_get_status_pos(enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_crit_alarm:
+ return TMP51X_TEMP_CRIT_ALARM[channel];
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_lcrit_alarm:
+ return TMP51X_BUS_VOLTAGE_L_LIMIT_POS;
+ case hwmon_in_crit_alarm:
+ return TMP51X_BUS_VOLTAGE_H_LIMIT_POS;
+ }
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_lcrit_alarm:
+ return TMP51X_SHUNT_CURRENT_L_LIMIT_POS;
+ case hwmon_curr_crit_alarm:
+ return TMP51X_SHUNT_CURRENT_H_LIMIT_POS;
+ }
+ break;
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_crit_alarm:
+ return TMP51X_POWER_LIMIT_POS;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tmp51x_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct tmp51x_data *data = dev_get_drvdata(dev);
+ int ret;
+ u32 regval;
+ u8 pos = 0, reg = 0;
+
+ reg = tmp51x_get_reg(type, attr, channel);
+ if (reg == 0)
+ return -EOPNOTSUPP;
+
+ if (reg == TMP51X_STATUS)
+ pos = tmp51x_get_status_pos(type, attr, channel);
+
+ ret = regmap_read(data->regmap, reg, &regval);
+ if (ret < 0)
+ return ret;
+
+ return tmp51x_get_value(data, reg, pos, regval, val);
+}
+
+static int tmp51x_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ u8 reg = 0;
+
+ reg = tmp51x_get_reg(type, attr, channel);
+ if (reg == 0)
+ return -EOPNOTSUPP;
+
+ return tmp51x_set_value(dev_get_drvdata(dev), reg, val);
+}
+
+static umode_t tmp51x_is_visible(const void *_data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ const struct tmp51x_data *data = _data;
+
+ switch (type) {
+ case hwmon_temp:
+ if (data->id == tmp512 && channel == 3)
+ return 0;
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_crit_alarm:
+ return 0444;
+ case hwmon_temp_crit:
+ return 0644;
+ case hwmon_temp_crit_hyst:
+ if (channel == 0)
+ return 0644;
+ return 0444;
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_lcrit_alarm:
+ case hwmon_in_crit_alarm:
+ return 0444;
+ case hwmon_in_lcrit:
+ case hwmon_in_crit:
+ return 0644;
+ }
+ break;
+ case hwmon_curr:
+ if (!data->shunt_uohms)
+ return 0;
+
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_lcrit_alarm:
+ case hwmon_curr_crit_alarm:
+ return 0444;
+ case hwmon_curr_lcrit:
+ case hwmon_curr_crit:
+ return 0644;
+ }
+ break;
+ case hwmon_power:
+ if (!data->shunt_uohms)
+ return 0;
+
+ switch (attr) {
+ case hwmon_power_input:
+ case hwmon_power_crit_alarm:
+ return 0444;
+ case hwmon_power_crit:
+ return 0644;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *tmp51x_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
+ HWMON_T_CRIT_HYST,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
+ HWMON_T_CRIT_HYST,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
+ HWMON_T_CRIT_HYST,
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
+ HWMON_T_CRIT_HYST),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_LCRIT | HWMON_I_LCRIT_ALARM |
+ HWMON_I_CRIT | HWMON_I_CRIT_ALARM),
+ HWMON_CHANNEL_INFO(curr,
+ HWMON_C_INPUT | HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM |
+ HWMON_C_CRIT | HWMON_C_CRIT_ALARM,
+ HWMON_C_INPUT),
+ HWMON_CHANNEL_INFO(power,
+ HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM),
+ NULL
+};
+
+static const struct hwmon_ops tmp51x_hwmon_ops = {
+ .is_visible = tmp51x_is_visible,
+ .read = tmp51x_read,
+ .write = tmp51x_write,
+};
+
+static const struct hwmon_chip_info tmp51x_chip_info = {
+ .ops = &tmp51x_hwmon_ops,
+ .info = tmp51x_info,
+};
+
+/*
+ * Calibrate the tmp51x following the datasheet method
+ */
+static int tmp51x_calibrate(struct tmp51x_data *data)
+{
+ int vshunt_max = data->pga_gain * 40;
+ u64 max_curr_ma;
+ u32 div;
+
+ /*
+ * If shunt_uohms is equal to 0, the calibration should be set to 0.
+ * The consequence will be that the current and power measurement engine
+ * of the sensor will not work. Temperature and voltage sensing will
+ * continue to work.
+ */
+ if (data->shunt_uohms == 0)
+ return regmap_write(data->regmap, TMP51X_SHUNT_CALIBRATION, 0);
+
+ max_curr_ma = DIV_ROUND_CLOSEST_ULL(vshunt_max * 1000 * 1000,
+ data->shunt_uohms);
+
+ /*
+ * Calculate the minimal bit resolution for the current and the power.
+ * Those values will be used during register interpretation.
+ */
+ data->curr_lsb_ua = DIV_ROUND_CLOSEST_ULL(max_curr_ma * 1000, 32767);
+ data->pwr_lsb_uw = 20 * data->curr_lsb_ua;
+
+ div = DIV_ROUND_CLOSEST_ULL(data->curr_lsb_ua * data->shunt_uohms,
+ 1000 * 1000);
+
+ return regmap_write(data->regmap, TMP51X_SHUNT_CALIBRATION,
+ DIV_ROUND_CLOSEST(40960, div));
+}
+
+/*
+ * Initialize the configuration and calibration registers.
+ */
+static int tmp51x_init(struct tmp51x_data *data)
+{
+ unsigned int regval;
+ int ret = regmap_write(data->regmap, TMP51X_SHUNT_CONFIG,
+ data->shunt_config);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, TMP51X_TEMP_CONFIG, data->temp_config);
+ if (ret < 0)
+ return ret;
+
+ // nFactor configuration
+ ret = regmap_update_bits(data->regmap, TMP51X_N_FACTOR_AND_HYST_1,
+ TMP51X_NFACTOR_MASK, data->nfactor[0] << 8);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, TMP51X_N_FACTOR_2,
+ data->nfactor[1] << 8);
+ if (ret < 0)
+ return ret;
+
+ if (data->id == tmp513) {
+ ret = regmap_write(data->regmap, TMP513_N_FACTOR_3,
+ data->nfactor[2] << 8);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = tmp51x_calibrate(data);
+ if (ret < 0)
+ return ret;
+
+ // Read the status register before using as the datasheet propose
+ return regmap_read(data->regmap, TMP51X_STATUS, &regval);
+}
+
+static const struct i2c_device_id tmp51x_id[] = {
+ { "tmp512", tmp512 },
+ { "tmp513", tmp513 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp51x_id);
+
+static const struct of_device_id tmp51x_of_match[] = {
+ {
+ .compatible = "ti,tmp512",
+ .data = (void *)tmp512
+ },
+ {
+ .compatible = "ti,tmp513",
+ .data = (void *)tmp513
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tmp51x_of_match);
+
+static int tmp51x_vbus_range_to_reg(struct device *dev,
+ struct tmp51x_data *data)
+{
+ if (data->vbus_range_uvolt == TMP51X_VBUS_RANGE_32V) {
+ data->shunt_config |= TMP51X_BUS_VOLTAGE_MASK;
+ } else if (data->vbus_range_uvolt == TMP51X_VBUS_RANGE_16V) {
+ data->shunt_config &= ~TMP51X_BUS_VOLTAGE_MASK;
+ } else {
+ dev_err(dev, "ti,bus-range-microvolt is invalid: %u\n",
+ data->vbus_range_uvolt);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tmp51x_pga_gain_to_reg(struct device *dev, struct tmp51x_data *data)
+{
+ if (data->pga_gain == 8) {
+ data->shunt_config |= CURRENT_SENSE_VOLTAGE_320_MASK;
+ } else if (data->pga_gain == 4) {
+ data->shunt_config |= CURRENT_SENSE_VOLTAGE_160_MASK;
+ } else if (data->pga_gain == 2) {
+ data->shunt_config |= CURRENT_SENSE_VOLTAGE_80_MASK;
+ } else if (data->pga_gain == 1) {
+ data->shunt_config |= CURRENT_SENSE_VOLTAGE_40_MASK;
+ } else {
+ dev_err(dev, "ti,pga-gain is invalid: %u\n", data->pga_gain);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tmp51x_read_properties(struct device *dev, struct tmp51x_data *data)
+{
+ int ret;
+ u32 nfactor[3];
+ u32 val;
+
+ ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val);
+ data->shunt_uohms = (ret >= 0) ? val : TMP51X_SHUNT_VALUE_DEFAULT;
+
+ ret = device_property_read_u32(dev, "ti,bus-range-microvolt", &val);
+ data->vbus_range_uvolt = (ret >= 0) ? val : TMP51X_VBUS_RANGE_DEFAULT;
+ ret = tmp51x_vbus_range_to_reg(dev, data);
+ if (ret < 0)
+ return ret;
+
+ ret = device_property_read_u32(dev, "ti,pga-gain", &val);
+ data->pga_gain = (ret >= 0) ? val : TMP51X_PGA_DEFAULT;
+ ret = tmp51x_pga_gain_to_reg(dev, data);
+ if (ret < 0)
+ return ret;
+
+ ret = device_property_read_u32_array(dev, "ti,nfactor", nfactor,
+ (data->id == tmp513) ? 3 : 2);
+ if (ret >= 0)
+ memcpy(data->nfactor, nfactor, (data->id == tmp513) ? 3 : 2);
+
+ // Check if shunt value is compatible with pga-gain
+ if (data->shunt_uohms > data->pga_gain * 40 * 1000 * 1000) {
+ dev_err(dev, "shunt-resistor: %u too big for pga_gain: %u\n",
+ data->shunt_uohms, data->pga_gain);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void tmp51x_use_default(struct tmp51x_data *data)
+{
+ data->vbus_range_uvolt = TMP51X_VBUS_RANGE_DEFAULT;
+ data->pga_gain = TMP51X_PGA_DEFAULT;
+ data->shunt_uohms = TMP51X_SHUNT_VALUE_DEFAULT;
+}
+
+static int tmp51x_configure(struct device *dev, struct tmp51x_data *data)
+{
+ data->shunt_config = TMP51X_SHUNT_CONFIG_DEFAULT;
+ data->temp_config = (data->id == tmp513) ?
+ TMP513_TEMP_CONFIG_DEFAULT : TMP512_TEMP_CONFIG_DEFAULT;
+
+ if (dev->of_node)
+ return tmp51x_read_properties(dev, data);
+
+ tmp51x_use_default(data);
+
+ return 0;
+}
+
+static int tmp51x_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tmp51x_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (client->dev.of_node)
+ data->id = (enum tmp51x_ids)device_get_match_data(&client->dev);
+ else
+ data->id = i2c_match_id(tmp51x_id, client)->driver_data;
+
+ ret = tmp51x_configure(dev, data);
+ if (ret < 0) {
+ dev_err(dev, "error configuring the device: %d\n", ret);
+ return ret;
+ }
+
+ data->regmap = devm_regmap_init_i2c(client, &tmp51x_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = tmp51x_init(data);
+ 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,
+ &tmp51x_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_dbg(dev, "power monitor %s\n", client->name);
+
+ return 0;
+}
+
+static struct i2c_driver tmp51x_driver = {
+ .driver = {
+ .name = "tmp51x",
+ .of_match_table = tmp51x_of_match,
+ },
+ .probe_new = tmp51x_probe,
+ .id_table = tmp51x_id,
+};
+
+module_i2c_driver(tmp51x_driver);
+
+MODULE_AUTHOR("Eric Tremblay <etremblay@distechcontrols.com>");
+MODULE_DESCRIPTION("tmp51x driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c
new file mode 100644
index 000000000..68c77c493
--- /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, &regval);
+ 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,
+ &regval, 2);
+ } else {
+ err = regmap_bulk_read(data->regmap,
+ INPUT_VOLTAGE_LSB,
+ &regval, 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,
+ &regval, 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,
+ &regval,
+ 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/ultra45_env.c b/drivers/hwmon/ultra45_env.c
new file mode 100644
index 000000000..b23cff2e3
--- /dev/null
+++ b/drivers/hwmon/ultra45_env.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ultra45_env.c: Driver for Ultra45 PIC16F747 environmental monitor.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+
+#define DRV_MODULE_VERSION "0.1"
+
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_DESCRIPTION("Ultra45 environmental monitor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+/* PIC device registers */
+#define REG_CMD 0x00UL
+#define REG_CMD_RESET 0x80
+#define REG_CMD_ESTAR 0x01
+#define REG_STAT 0x01UL
+#define REG_STAT_FWVER 0xf0
+#define REG_STAT_TGOOD 0x08
+#define REG_STAT_STALE 0x04
+#define REG_STAT_BUSY 0x02
+#define REG_STAT_FAULT 0x01
+#define REG_DATA 0x40UL
+#define REG_ADDR 0x41UL
+#define REG_SIZE 0x42UL
+
+/* Registers accessed indirectly via REG_DATA/REG_ADDR */
+#define IREG_FAN0 0x00
+#define IREG_FAN1 0x01
+#define IREG_FAN2 0x02
+#define IREG_FAN3 0x03
+#define IREG_FAN4 0x04
+#define IREG_FAN5 0x05
+#define IREG_LCL_TEMP 0x06
+#define IREG_RMT1_TEMP 0x07
+#define IREG_RMT2_TEMP 0x08
+#define IREG_RMT3_TEMP 0x09
+#define IREG_LM95221_TEMP 0x0a
+#define IREG_FIRE_TEMP 0x0b
+#define IREG_LSI1064_TEMP 0x0c
+#define IREG_FRONT_TEMP 0x0d
+#define IREG_FAN_STAT 0x0e
+#define IREG_VCORE0 0x0f
+#define IREG_VCORE1 0x10
+#define IREG_VMEM0 0x11
+#define IREG_VMEM1 0x12
+#define IREG_PSU_TEMP 0x13
+
+struct env {
+ void __iomem *regs;
+ spinlock_t lock;
+
+ struct device *hwmon_dev;
+};
+
+static u8 env_read(struct env *p, u8 ireg)
+{
+ u8 ret;
+
+ spin_lock(&p->lock);
+ writeb(ireg, p->regs + REG_ADDR);
+ ret = readb(p->regs + REG_DATA);
+ spin_unlock(&p->lock);
+
+ return ret;
+}
+
+static void env_write(struct env *p, u8 ireg, u8 val)
+{
+ spin_lock(&p->lock);
+ writeb(ireg, p->regs + REG_ADDR);
+ writeb(val, p->regs + REG_DATA);
+ spin_unlock(&p->lock);
+}
+
+/*
+ * There seems to be a adr7462 providing these values, thus a lot
+ * of these calculations are borrowed from the adt7470 driver.
+ */
+#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x))
+#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM
+#define FAN_PERIOD_INVALID (0xff << 8)
+#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+
+static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int fan_nr = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ int rpm, period;
+ u8 val;
+
+ val = env_read(p, IREG_FAN0 + fan_nr);
+ period = (int) val << 8;
+ if (FAN_DATA_VALID(period))
+ rpm = FAN_PERIOD_TO_RPM(period);
+ else
+ rpm = 0;
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_fan_speed(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int fan_nr = to_sensor_dev_attr(attr)->index;
+ unsigned long rpm;
+ struct env *p = dev_get_drvdata(dev);
+ int period;
+ u8 val;
+ int err;
+
+ err = kstrtoul(buf, 10, &rpm);
+ if (err)
+ return err;
+
+ if (!rpm)
+ return -EINVAL;
+
+ period = FAN_RPM_TO_PERIOD(rpm);
+ val = period >> 8;
+ env_write(p, IREG_FAN0 + fan_nr, val);
+
+ return count;
+}
+
+static ssize_t show_fan_fault(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int fan_nr = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ u8 val = env_read(p, IREG_FAN_STAT);
+ return sprintf(buf, "%d\n", (val & (1 << fan_nr)) ? 1 : 0);
+}
+
+#define fan(index) \
+static SENSOR_DEVICE_ATTR(fan##index##_speed, S_IRUGO | S_IWUSR, \
+ show_fan_speed, set_fan_speed, index); \
+static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, \
+ show_fan_fault, NULL, index)
+
+fan(0);
+fan(1);
+fan(2);
+fan(3);
+fan(4);
+
+static SENSOR_DEVICE_ATTR(psu_fan_fault, S_IRUGO, show_fan_fault, NULL, 6);
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int temp_nr = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ s8 val;
+
+ val = env_read(p, IREG_LCL_TEMP + temp_nr);
+ return sprintf(buf, "%d\n", ((int) val) - 64);
+}
+
+static SENSOR_DEVICE_ATTR(adt7462_local_temp, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(cpu0_temp, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(cpu1_temp, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(motherboard_temp, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(lm95221_local_temp, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(fire_temp, S_IRUGO, show_temp, NULL, 5);
+static SENSOR_DEVICE_ATTR(lsi1064_local_temp, S_IRUGO, show_temp, NULL, 6);
+static SENSOR_DEVICE_ATTR(front_panel_temp, S_IRUGO, show_temp, NULL, 7);
+static SENSOR_DEVICE_ATTR(psu_temp, S_IRUGO, show_temp, NULL, 13);
+
+static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ u8 val;
+
+ val = readb(p->regs + REG_STAT);
+ return sprintf(buf, "%d\n", (val & (1 << index)) ? 1 : 0);
+}
+
+static SENSOR_DEVICE_ATTR(fan_failure, S_IRUGO, show_stat_bit, NULL, 0);
+static SENSOR_DEVICE_ATTR(env_bus_busy, S_IRUGO, show_stat_bit, NULL, 1);
+static SENSOR_DEVICE_ATTR(env_data_stale, S_IRUGO, show_stat_bit, NULL, 2);
+static SENSOR_DEVICE_ATTR(tpm_self_test_passed, S_IRUGO, show_stat_bit, NULL,
+ 3);
+
+static ssize_t show_fwver(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct env *p = dev_get_drvdata(dev);
+ u8 val;
+
+ val = readb(p->regs + REG_STAT);
+ return sprintf(buf, "%d\n", val >> 4);
+}
+
+static SENSOR_DEVICE_ATTR(firmware_version, S_IRUGO, show_fwver, NULL, 0);
+
+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "ultra45\n");
+}
+
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static struct attribute *env_attributes[] = {
+ &sensor_dev_attr_fan0_speed.dev_attr.attr,
+ &sensor_dev_attr_fan0_fault.dev_attr.attr,
+ &sensor_dev_attr_fan1_speed.dev_attr.attr,
+ &sensor_dev_attr_fan1_fault.dev_attr.attr,
+ &sensor_dev_attr_fan2_speed.dev_attr.attr,
+ &sensor_dev_attr_fan2_fault.dev_attr.attr,
+ &sensor_dev_attr_fan3_speed.dev_attr.attr,
+ &sensor_dev_attr_fan3_fault.dev_attr.attr,
+ &sensor_dev_attr_fan4_speed.dev_attr.attr,
+ &sensor_dev_attr_fan4_fault.dev_attr.attr,
+ &sensor_dev_attr_psu_fan_fault.dev_attr.attr,
+ &sensor_dev_attr_adt7462_local_temp.dev_attr.attr,
+ &sensor_dev_attr_cpu0_temp.dev_attr.attr,
+ &sensor_dev_attr_cpu1_temp.dev_attr.attr,
+ &sensor_dev_attr_motherboard_temp.dev_attr.attr,
+ &sensor_dev_attr_lm95221_local_temp.dev_attr.attr,
+ &sensor_dev_attr_fire_temp.dev_attr.attr,
+ &sensor_dev_attr_lsi1064_local_temp.dev_attr.attr,
+ &sensor_dev_attr_front_panel_temp.dev_attr.attr,
+ &sensor_dev_attr_psu_temp.dev_attr.attr,
+ &sensor_dev_attr_fan_failure.dev_attr.attr,
+ &sensor_dev_attr_env_bus_busy.dev_attr.attr,
+ &sensor_dev_attr_env_data_stale.dev_attr.attr,
+ &sensor_dev_attr_tpm_self_test_passed.dev_attr.attr,
+ &sensor_dev_attr_firmware_version.dev_attr.attr,
+ &sensor_dev_attr_name.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group env_group = {
+ .attrs = env_attributes,
+};
+
+static int env_probe(struct platform_device *op)
+{
+ struct env *p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
+ int err = -ENOMEM;
+
+ if (!p)
+ goto out;
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0, REG_SIZE, "pic16f747");
+ if (!p->regs)
+ goto out;
+
+ err = sysfs_create_group(&op->dev.kobj, &env_group);
+ if (err)
+ goto out_iounmap;
+
+ p->hwmon_dev = hwmon_device_register(&op->dev);
+ if (IS_ERR(p->hwmon_dev)) {
+ err = PTR_ERR(p->hwmon_dev);
+ goto out_sysfs_remove_group;
+ }
+
+ platform_set_drvdata(op, p);
+ err = 0;
+
+out:
+ return err;
+
+out_sysfs_remove_group:
+ sysfs_remove_group(&op->dev.kobj, &env_group);
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, REG_SIZE);
+
+ goto out;
+}
+
+static int env_remove(struct platform_device *op)
+{
+ struct env *p = platform_get_drvdata(op);
+
+ if (p) {
+ sysfs_remove_group(&op->dev.kobj, &env_group);
+ hwmon_device_unregister(p->hwmon_dev);
+ of_iounmap(&op->resource[0], p->regs, REG_SIZE);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id env_match[] = {
+ {
+ .name = "env-monitor",
+ .compatible = "SUNW,ebus-pic16f747-env",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, env_match);
+
+static struct platform_driver env_driver = {
+ .driver = {
+ .name = "ultra45_env",
+ .of_match_table = env_match,
+ },
+ .probe = env_probe,
+ .remove = env_remove,
+};
+
+module_platform_driver(env_driver);
diff --git a/drivers/hwmon/vexpress-hwmon.c b/drivers/hwmon/vexpress-hwmon.c
new file mode 100644
index 000000000..2ac5fb96b
--- /dev/null
+++ b/drivers/hwmon/vexpress-hwmon.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#define DRVNAME "vexpress-hwmon"
+#define pr_fmt(fmt) DRVNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/vexpress.h>
+
+struct vexpress_hwmon_data {
+ struct device *hwmon_dev;
+ struct regmap *reg;
+};
+
+static ssize_t vexpress_hwmon_label_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buffer)
+{
+ const char *label = of_get_property(dev->of_node, "label", NULL);
+
+ return sysfs_emit(buffer, "%s\n", label);
+}
+
+static ssize_t vexpress_hwmon_u32_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buffer)
+{
+ struct vexpress_hwmon_data *data = dev_get_drvdata(dev);
+ int err;
+ u32 value;
+
+ err = regmap_read(data->reg, 0, &value);
+ if (err)
+ return err;
+
+ return sysfs_emit(buffer, "%u\n", value /
+ to_sensor_dev_attr(dev_attr)->index);
+}
+
+static ssize_t vexpress_hwmon_u64_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buffer)
+{
+ struct vexpress_hwmon_data *data = dev_get_drvdata(dev);
+ int err;
+ u32 value_hi, value_lo;
+
+ err = regmap_read(data->reg, 0, &value_lo);
+ if (err)
+ return err;
+
+ err = regmap_read(data->reg, 1, &value_hi);
+ if (err)
+ return err;
+
+ 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,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct device_attribute *dev_attr = container_of(attr,
+ struct device_attribute, attr);
+
+ if (dev_attr->show == vexpress_hwmon_label_show &&
+ !of_get_property(dev->of_node, "label", NULL))
+ return 0;
+
+ return attr->mode;
+}
+
+struct vexpress_hwmon_type {
+ const char *name;
+ const struct attribute_group **attr_groups;
+};
+
+#if !defined(CONFIG_REGULATOR_VEXPRESS)
+static DEVICE_ATTR(in1_label, 0444, vexpress_hwmon_label_show, NULL);
+static SENSOR_DEVICE_ATTR_RO(in1_input, vexpress_hwmon_u32, 1000);
+static struct attribute *vexpress_hwmon_attrs_volt[] = {
+ &dev_attr_in1_label.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ NULL
+};
+static struct attribute_group vexpress_hwmon_group_volt = {
+ .is_visible = vexpress_hwmon_attr_is_visible,
+ .attrs = vexpress_hwmon_attrs_volt,
+};
+static struct vexpress_hwmon_type vexpress_hwmon_volt = {
+ .name = "vexpress_volt",
+ .attr_groups = (const struct attribute_group *[]) {
+ &vexpress_hwmon_group_volt,
+ NULL,
+ },
+};
+#endif
+
+static DEVICE_ATTR(curr1_label, 0444, vexpress_hwmon_label_show, NULL);
+static SENSOR_DEVICE_ATTR_RO(curr1_input, vexpress_hwmon_u32, 1000);
+static struct attribute *vexpress_hwmon_attrs_amp[] = {
+ &dev_attr_curr1_label.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ NULL
+};
+static struct attribute_group vexpress_hwmon_group_amp = {
+ .is_visible = vexpress_hwmon_attr_is_visible,
+ .attrs = vexpress_hwmon_attrs_amp,
+};
+static struct vexpress_hwmon_type vexpress_hwmon_amp = {
+ .name = "vexpress_amp",
+ .attr_groups = (const struct attribute_group *[]) {
+ &vexpress_hwmon_group_amp,
+ NULL
+ },
+};
+
+static DEVICE_ATTR(temp1_label, 0444, vexpress_hwmon_label_show, NULL);
+static SENSOR_DEVICE_ATTR_RO(temp1_input, vexpress_hwmon_u32, 1000);
+static struct attribute *vexpress_hwmon_attrs_temp[] = {
+ &dev_attr_temp1_label.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+static struct attribute_group vexpress_hwmon_group_temp = {
+ .is_visible = vexpress_hwmon_attr_is_visible,
+ .attrs = vexpress_hwmon_attrs_temp,
+};
+static struct vexpress_hwmon_type vexpress_hwmon_temp = {
+ .name = "vexpress_temp",
+ .attr_groups = (const struct attribute_group *[]) {
+ &vexpress_hwmon_group_temp,
+ NULL
+ },
+};
+
+static DEVICE_ATTR(power1_label, 0444, vexpress_hwmon_label_show, NULL);
+static SENSOR_DEVICE_ATTR_RO(power1_input, vexpress_hwmon_u32, 1);
+static struct attribute *vexpress_hwmon_attrs_power[] = {
+ &dev_attr_power1_label.attr,
+ &sensor_dev_attr_power1_input.dev_attr.attr,
+ NULL
+};
+static struct attribute_group vexpress_hwmon_group_power = {
+ .is_visible = vexpress_hwmon_attr_is_visible,
+ .attrs = vexpress_hwmon_attrs_power,
+};
+static struct vexpress_hwmon_type vexpress_hwmon_power = {
+ .name = "vexpress_power",
+ .attr_groups = (const struct attribute_group *[]) {
+ &vexpress_hwmon_group_power,
+ NULL
+ },
+};
+
+static DEVICE_ATTR(energy1_label, 0444, vexpress_hwmon_label_show, NULL);
+static SENSOR_DEVICE_ATTR_RO(energy1_input, vexpress_hwmon_u64, 1);
+static struct attribute *vexpress_hwmon_attrs_energy[] = {
+ &dev_attr_energy1_label.attr,
+ &sensor_dev_attr_energy1_input.dev_attr.attr,
+ NULL
+};
+static struct attribute_group vexpress_hwmon_group_energy = {
+ .is_visible = vexpress_hwmon_attr_is_visible,
+ .attrs = vexpress_hwmon_attrs_energy,
+};
+static struct vexpress_hwmon_type vexpress_hwmon_energy = {
+ .name = "vexpress_energy",
+ .attr_groups = (const struct attribute_group *[]) {
+ &vexpress_hwmon_group_energy,
+ NULL
+ },
+};
+
+static const struct of_device_id vexpress_hwmon_of_match[] = {
+#if !defined(CONFIG_REGULATOR_VEXPRESS)
+ {
+ .compatible = "arm,vexpress-volt",
+ .data = &vexpress_hwmon_volt,
+ },
+#endif
+ {
+ .compatible = "arm,vexpress-amp",
+ .data = &vexpress_hwmon_amp,
+ }, {
+ .compatible = "arm,vexpress-temp",
+ .data = &vexpress_hwmon_temp,
+ }, {
+ .compatible = "arm,vexpress-power",
+ .data = &vexpress_hwmon_power,
+ }, {
+ .compatible = "arm,vexpress-energy",
+ .data = &vexpress_hwmon_energy,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match);
+
+static int vexpress_hwmon_probe(struct platform_device *pdev)
+{
+ struct vexpress_hwmon_data *data;
+ const struct vexpress_hwmon_type *type;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, data);
+
+ type = of_device_get_match_data(&pdev->dev);
+ if (!type)
+ return -ENODEV;
+
+ data->reg = devm_regmap_init_vexpress_config(&pdev->dev);
+ if (IS_ERR(data->reg))
+ return PTR_ERR(data->reg);
+
+ data->hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+ type->name, data, type->attr_groups);
+
+ return PTR_ERR_OR_ZERO(data->hwmon_dev);
+}
+
+static struct platform_driver vexpress_hwmon_driver = {
+ .probe = vexpress_hwmon_probe,
+ .driver = {
+ .name = DRVNAME,
+ .of_match_table = vexpress_hwmon_of_match,
+ },
+};
+
+module_platform_driver(vexpress_hwmon_driver);
+
+MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
+MODULE_DESCRIPTION("Versatile Express hwmon sensors driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vexpress-hwmon");
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
new file mode 100644
index 000000000..e5d18dac8
--- /dev/null
+++ b/drivers/hwmon/via-cputemp.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * via-cputemp.c - Driver for VIA CPU core temperature monitoring
+ * Copyright (C) 2009 VIA Technologies, Inc.
+ *
+ * based on existing coretemp.c, which is
+ *
+ * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/cpu.h>
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/cpu_device_id.h>
+
+#define DRVNAME "via_cputemp"
+
+enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
+
+/*
+ * Functions declaration
+ */
+
+struct via_cputemp_data {
+ struct device *hwmon_dev;
+ const char *name;
+ u8 vrm;
+ u32 id;
+ u32 msr_temp;
+ u32 msr_vid;
+};
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ int ret;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct via_cputemp_data *data = dev_get_drvdata(dev);
+
+ if (attr->index == SHOW_NAME)
+ ret = sprintf(buf, "%s\n", data->name);
+ else /* show label */
+ ret = sprintf(buf, "Core %d\n", data->id);
+ return ret;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct via_cputemp_data *data = dev_get_drvdata(dev);
+ u32 eax, edx;
+ int err;
+
+ err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
+ if (err)
+ return -EAGAIN;
+
+ return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000);
+}
+
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct via_cputemp_data *data = dev_get_drvdata(dev);
+ u32 eax, edx;
+ int err;
+
+ err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx);
+ if (err)
+ return -EAGAIN;
+
+ return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm));
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, SHOW_TEMP);
+static SENSOR_DEVICE_ATTR_RO(temp1_label, name, SHOW_LABEL);
+static SENSOR_DEVICE_ATTR_RO(name, name, SHOW_NAME);
+
+static struct attribute *via_cputemp_attributes[] = {
+ &sensor_dev_attr_name.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group via_cputemp_group = {
+ .attrs = via_cputemp_attributes,
+};
+
+/* Optional attributes */
+static DEVICE_ATTR_RO(cpu0_vid);
+
+static int via_cputemp_probe(struct platform_device *pdev)
+{
+ struct via_cputemp_data *data;
+ struct cpuinfo_x86 *c = &cpu_data(pdev->id);
+ int err;
+ u32 eax, edx;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct via_cputemp_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->id = pdev->id;
+ data->name = "via_cputemp";
+
+ if (c->x86 == 7) {
+ data->msr_temp = 0x1423;
+ } else {
+ switch (c->x86_model) {
+ case 0xA:
+ /* C7 A */
+ case 0xD:
+ /* C7 D */
+ data->msr_temp = 0x1169;
+ data->msr_vid = 0x198;
+ break;
+ case 0xF:
+ /* Nano */
+ data->msr_temp = 0x1423;
+ break;
+ default:
+ return -ENODEV;
+ }
+ }
+
+ /* test if we can access the TEMPERATURE MSR */
+ err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Unable to access TEMPERATURE MSR, giving up\n");
+ return err;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group);
+ if (err)
+ return err;
+
+ if (data->msr_vid)
+ data->vrm = vid_which_vrm();
+
+ if (data->vrm) {
+ err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid);
+ if (err)
+ goto exit_remove;
+ }
+
+ 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_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ if (data->vrm)
+ device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
+ sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
+ return err;
+}
+
+static int via_cputemp_remove(struct platform_device *pdev)
+{
+ struct via_cputemp_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ if (data->vrm)
+ device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
+ sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
+ return 0;
+}
+
+static struct platform_driver via_cputemp_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = via_cputemp_probe,
+ .remove = via_cputemp_remove,
+};
+
+struct pdev_entry {
+ struct list_head list;
+ struct platform_device *pdev;
+ unsigned int cpu;
+};
+
+static LIST_HEAD(pdev_list);
+static DEFINE_MUTEX(pdev_list_mutex);
+
+static int via_cputemp_online(unsigned int cpu)
+{
+ int err;
+ struct platform_device *pdev;
+ struct pdev_entry *pdev_entry;
+
+ pdev = platform_device_alloc(DRVNAME, cpu);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
+ if (!pdev_entry) {
+ err = -ENOMEM;
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_free;
+ }
+
+ pdev_entry->pdev = pdev;
+ pdev_entry->cpu = cpu;
+ mutex_lock(&pdev_list_mutex);
+ list_add_tail(&pdev_entry->list, &pdev_list);
+ mutex_unlock(&pdev_list_mutex);
+
+ return 0;
+
+exit_device_free:
+ kfree(pdev_entry);
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int via_cputemp_down_prep(unsigned int cpu)
+{
+ struct pdev_entry *p;
+
+ mutex_lock(&pdev_list_mutex);
+ list_for_each_entry(p, &pdev_list, list) {
+ if (p->cpu == cpu) {
+ platform_device_unregister(p->pdev);
+ list_del(&p->list);
+ mutex_unlock(&pdev_list_mutex);
+ kfree(p);
+ return 0;
+ }
+ }
+ mutex_unlock(&pdev_list_mutex);
+ return 0;
+}
+
+static const struct x86_cpu_id __initconst cputemp_ids[] = {
+ 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);
+
+static enum cpuhp_state via_temp_online;
+
+static int __init via_cputemp_init(void)
+{
+ int err;
+
+ if (!x86_match_cpu(cputemp_ids))
+ return -ENODEV;
+
+ err = platform_driver_register(&via_cputemp_driver);
+ if (err)
+ goto exit;
+
+ err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online",
+ via_cputemp_online, via_cputemp_down_prep);
+ if (err < 0)
+ goto exit_driver_unreg;
+ via_temp_online = err;
+
+#ifndef CONFIG_HOTPLUG_CPU
+ if (list_empty(&pdev_list)) {
+ err = -ENODEV;
+ goto exit_hp_unreg;
+ }
+#endif
+ return 0;
+
+#ifndef CONFIG_HOTPLUG_CPU
+exit_hp_unreg:
+ cpuhp_remove_state_nocalls(via_temp_online);
+#endif
+exit_driver_unreg:
+ platform_driver_unregister(&via_cputemp_driver);
+exit:
+ return err;
+}
+
+static void __exit via_cputemp_exit(void)
+{
+ cpuhp_remove_state(via_temp_online);
+ platform_driver_unregister(&via_cputemp_driver);
+}
+
+MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
+MODULE_DESCRIPTION("VIA CPU temperature monitor");
+MODULE_LICENSE("GPL");
+
+module_init(via_cputemp_init)
+module_exit(via_cputemp_exit)
diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c
new file mode 100644
index 000000000..37d737489
--- /dev/null
+++ b/drivers/hwmon/via686a.c
@@ -0,0 +1,945 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * via686a.c - Part of lm_sensors, Linux kernel modules
+ * for hardware monitoring
+ *
+ * Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
+ * Kyösti Mälkki <kmalkki@cc.hut.fi>,
+ * Mark Studebaker <mdsxyz123@yahoo.com>,
+ * and Bob Dougherty <bobd@stanford.edu>
+ *
+ * (Some conversion-factor data were contributed by Jonathan Teh Soon Yew
+ * <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
+ */
+
+/*
+ * Supports the Via VT82C686A, VT82C686B south bridges.
+ * Reports all as a 686A.
+ * Warning - only supports a single device.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.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/init.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#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.
+ */
+static unsigned short force_addr;
+module_param(force_addr, ushort, 0);
+MODULE_PARM_DESC(force_addr,
+ "Initialize the base address of the sensors");
+
+static struct platform_device *pdev;
+
+/*
+ * The Via 686a southbridge has a LM78-like chip integrated on the same IC.
+ * This driver is a customized copy of lm78.c
+ */
+
+/* Many VIA686A constants specified below */
+
+/* Length of ISA address segment */
+#define VIA686A_EXTENT 0x80
+#define VIA686A_BASE_REG 0x70
+#define VIA686A_ENABLE_REG 0x74
+
+/* The VIA686A registers */
+/* ins numbered 0-4 */
+#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
+#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
+#define VIA686A_REG_IN(nr) (0x22 + (nr))
+
+/* fans numbered 1-2 */
+#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VIA686A_REG_FAN(nr) (0x28 + (nr))
+
+/* temps numbered 1-3 */
+static const u8 VIA686A_REG_TEMP[] = { 0x20, 0x21, 0x1f };
+static const u8 VIA686A_REG_TEMP_OVER[] = { 0x39, 0x3d, 0x1d };
+static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e };
+/* bits 7-6 */
+#define VIA686A_REG_TEMP_LOW1 0x4b
+/* 2 = bits 5-4, 3 = bits 7-6 */
+#define VIA686A_REG_TEMP_LOW23 0x49
+
+#define VIA686A_REG_ALARM1 0x41
+#define VIA686A_REG_ALARM2 0x42
+#define VIA686A_REG_FANDIV 0x47
+#define VIA686A_REG_CONFIG 0x40
+/*
+ * The following register sets temp interrupt mode (bits 1-0 for temp1,
+ * 3-2 for temp2, 5-4 for temp3). Modes are:
+ * 00 interrupt stays as long as value is out-of-range
+ * 01 interrupt is cleared once register is read (default)
+ * 10 comparator mode- like 00, but ignores hysteresis
+ * 11 same as 00
+ */
+#define VIA686A_REG_TEMP_MODE 0x4b
+/* We'll just assume that you want to set all 3 simultaneously: */
+#define VIA686A_TEMP_MODE_MASK 0x3F
+#define VIA686A_TEMP_MODE_CONTINUOUS 0x00
+
+/*
+ * Conversions. Limit checking is only done on the TO_REG
+ * variants.
+ *
+ ******** VOLTAGE CONVERSIONS (Bob Dougherty) ********
+ * From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
+ * voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp
+ * voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V
+ * voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V
+ * voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V
+ * voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V
+ * in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
+ * That is:
+ * volts = (25*regVal+133)*factor
+ * regVal = (volts/factor-133)/25
+ * (These conversions were contributed by Jonathan Teh Soon Yew
+ * <j.teh@iname.com>)
+ */
+static inline u8 IN_TO_REG(long val, int in_num)
+{
+ /*
+ * To avoid floating point, we multiply constants by 10 (100 for +12V).
+ * Rounding is done (120500 is actually 133000 - 12500).
+ * Remember that val is expressed in 0.001V/bit, which is why we divide
+ * by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
+ * for the constants.
+ */
+ if (in_num <= 1)
+ return (u8) clamp_val((val * 21024 - 1205000) / 250000, 0, 255);
+ else if (in_num == 2)
+ return (u8) clamp_val((val * 15737 - 1205000) / 250000, 0, 255);
+ else if (in_num == 3)
+ return (u8) clamp_val((val * 10108 - 1205000) / 250000, 0, 255);
+ else
+ return (u8) clamp_val((val * 41714 - 12050000) / 2500000, 0,
+ 255);
+}
+
+static inline long IN_FROM_REG(u8 val, int in_num)
+{
+ /*
+ * To avoid floating point, we multiply constants by 10 (100 for +12V).
+ * We also multiply them by 1000 because we want 0.001V/bit for the
+ * output value. Rounding is done.
+ */
+ if (in_num <= 1)
+ return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
+ else if (in_num == 2)
+ return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
+ else if (in_num == 3)
+ return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
+ else
+ return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
+}
+
+/********* FAN RPM CONVERSIONS ********/
+/*
+ * Higher register values = slower fans (the fan's strobe gates a counter).
+ * But this chip saturates back at 0, not at 255 like all the other chips.
+ * So, 0 means 0 RPM
+ */
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 0;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (val) == 255 ? 0 : 1350000 / \
+ ((val) * (div)))
+
+/******** TEMP CONVERSIONS (Bob Dougherty) *********/
+/*
+ * linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
+ * if(temp<169)
+ * return double(temp)*0.427-32.08;
+ * else if(temp>=169 && temp<=202)
+ * return double(temp)*0.582-58.16;
+ * else
+ * return double(temp)*0.924-127.33;
+ *
+ * A fifth-order polynomial fits the unofficial data (provided by Alex van
+ * Kaam <darkside@chello.nl>) a bit better. It also give more reasonable
+ * numbers on my machine (ie. they agree with what my BIOS tells me).
+ * Here's the fifth-order fit to the 8-bit data:
+ * temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 -
+ * 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
+ *
+ * (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for
+ * finding my typos in this formula!)
+ *
+ * Alas, none of the elegant function-fit solutions will work because we
+ * aren't allowed to use floating point in the kernel and doing it with
+ * integers doesn't provide enough precision. So we'll do boring old
+ * look-up table stuff. The unofficial data (see below) have effectively
+ * 7-bit resolution (they are rounded to the nearest degree). I'm assuming
+ * that the transfer function of the device is monotonic and smooth, so a
+ * smooth function fit to the data will allow us to get better precision.
+ * I used the 5th-order poly fit described above and solved for
+ * VIA register values 0-255. I *10 before rounding, so we get tenth-degree
+ * precision. (I could have done all 1024 values for our 10-bit readings,
+ * but the function is very linear in the useful range (0-80 deg C), so
+ * we'll just use linear interpolation for 10-bit readings.) So, temp_lut
+ * is the temp at via register values 0-255:
+ */
+static const s16 temp_lut[] = {
+ -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
+ -503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
+ -362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
+ -255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
+ -173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
+ -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
+ -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
+ 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
+ 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
+ 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
+ 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
+ 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
+ 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
+ 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
+ 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
+ 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
+ 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
+ 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
+ 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
+ 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
+ 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
+ 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
+};
+
+/*
+ * the original LUT values from Alex van Kaam <darkside@chello.nl>
+ * (for via register values 12-240):
+ * {-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
+ * -30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
+ * -15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
+ * -3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12,
+ * 12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
+ * 22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
+ * 33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
+ * 45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
+ * 61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
+ * 85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
+ *
+ *
+ * Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed
+ * an extra term for a good fit to these inverse data!) and then
+ * solving for each temp value from -50 to 110 (the useable range for
+ * this chip). Here's the fit:
+ * viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4
+ * - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
+ * Note that n=161:
+ */
+static const u8 via_lut[] = {
+ 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
+ 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
+ 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
+ 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
+ 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
+ 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
+ 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
+ 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+ 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
+ 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
+ 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+ 239, 240
+};
+
+/*
+ * Converting temps to (8-bit) hyst and over registers
+ * No interpolation here.
+ * The +50 is because the temps start at -50
+ */
+static inline u8 TEMP_TO_REG(long val)
+{
+ return via_lut[val <= -50000 ? 0 : val >= 110000 ? 160 :
+ (val < 0 ? val - 500 : val + 500) / 1000 + 50];
+}
+
+/* for 8-bit temperature hyst and over registers */
+#define TEMP_FROM_REG(val) ((long)temp_lut[val] * 100)
+
+/* for 10-bit temperature readings */
+static inline long TEMP_FROM_REG10(u16 val)
+{
+ u16 eight_bits = val >> 2;
+ u16 two_bits = val & 3;
+
+ /* no interpolation for these */
+ if (two_bits == 0 || eight_bits == 255)
+ return TEMP_FROM_REG(eight_bits);
+
+ /* do some linear interpolation */
+ return (temp_lut[eight_bits] * (4 - two_bits) +
+ temp_lut[eight_bits + 1] * two_bits) * 25;
+}
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val) == 8 ? 3 : (val) == 4 ? 2 : (val) == 1 ? 0 : 1)
+
+/*
+ * For each registered chip, we need to keep some data in memory.
+ * The structure is dynamically allocated.
+ */
+struct via686a_data {
+ unsigned short addr;
+ const char *name;
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 in[5]; /* Register value */
+ u8 in_max[5]; /* Register value */
+ u8 in_min[5]; /* Register value */
+ u8 fan[2]; /* Register value */
+ u8 fan_min[2]; /* Register value */
+ u16 temp[3]; /* Register value 10 bit */
+ u8 temp_over[3]; /* Register value */
+ u8 temp_hyst[3]; /* Register value */
+ u8 fan_div[2]; /* Register encoding, shifted right */
+ u16 alarms; /* Register encoding, combined */
+};
+
+static struct pci_dev *s_bridge; /* pointer to the (only) via686a */
+
+static inline int via686a_read_value(struct via686a_data *data, u8 reg)
+{
+ return inb_p(data->addr + reg);
+}
+
+static inline void via686a_write_value(struct via686a_data *data, u8 reg,
+ u8 value)
+{
+ outb_p(value, data->addr + reg);
+}
+
+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 */
+
+/* 7 voltage sensors */
+static ssize_t in_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
+}
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count) {
+ struct via686a_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_min[nr] = IN_TO_REG(val, nr);
+ via686a_write_value(data, VIA686A_REG_IN_MIN(nr),
+ data->in_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t in_max_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count) {
+ struct via686a_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->in_max[nr] = IN_TO_REG(val, nr);
+ via686a_write_value(data, VIA686A_REG_IN_MAX(nr),
+ data->in_max[nr]);
+ 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);
+
+/* 3 temperatures */
+static ssize_t temp_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
+}
+static ssize_t temp_over_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
+}
+static ssize_t temp_hyst_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
+}
+static ssize_t temp_over_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count) {
+ struct via686a_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_over[nr] = TEMP_TO_REG(val);
+ via686a_write_value(data, VIA686A_REG_TEMP_OVER[nr],
+ data->temp_over[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t temp_hyst_store(struct device *dev,
+ struct device_attribute *da, const char *buf,
+ size_t count) {
+ struct via686a_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_hyst[nr] = TEMP_TO_REG(val);
+ via686a_write_value(data, VIA686A_REG_TEMP_HYST[nr],
+ data->temp_hyst[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_over, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp_hyst, 0);
+static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_over, 1);
+static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, temp_hyst, 1);
+static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_over, 2);
+static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, temp_hyst, 2);
+
+/* 2 Fans */
+static ssize_t fan_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n",
+ FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *da,
+ char *buf) {
+ struct via686a_data *data = via686a_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
+}
+static ssize_t fan_min_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count) {
+ struct via686a_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ 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]));
+ via686a_write_value(data, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t fan_div_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count) {
+ struct via686a_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int nr = attr->index;
+ int old;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ old = via686a_read_value(data, VIA686A_REG_FANDIV);
+ data->fan_div[nr] = DIV_TO_REG(val);
+ old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
+ via686a_write_value(data, VIA686A_REG_FANDIV, old);
+ 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_RW(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_RW(fan2_div, fan_div, 1);
+
+/* Alarms */
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct via686a_data *data = via686a_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+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 via686a_data *data = via686a_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(temp1_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 15);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
+
+static ssize_t name_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct via686a_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", data->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *via686a_attributes[] = {
+ &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_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.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_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.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_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_name.attr,
+ NULL
+};
+
+static const struct attribute_group via686a_group = {
+ .attrs = via686a_attributes,
+};
+
+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)
+{
+ struct via686a_data *data;
+ struct resource *res;
+ int err;
+
+ /* Reserve the ISA region */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start, VIA686A_EXTENT,
+ 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;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct via686a_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+ data->addr = res->start;
+ data->name = DRIVER_NAME;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the VIA686A chip */
+ via686a_init_device(data);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&pdev->dev.kobj, &via686a_group);
+ if (err)
+ return err;
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
+ return err;
+}
+
+static int via686a_remove(struct platform_device *pdev)
+{
+ struct via686a_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &via686a_group);
+
+ return 0;
+}
+
+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) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
+
+static int via686a_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + VIA686A_EXTENT - 1,
+ .name = DRIVER_NAME,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc(DRIVER_NAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int via686a_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ u16 address, val;
+
+ if (force_addr) {
+ address = force_addr & ~(VIA686A_EXTENT - 1);
+ dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", address);
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_write_config_word(dev, VIA686A_BASE_REG, address | 1))
+ return -ENODEV;
+ }
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_read_config_word(dev, VIA686A_BASE_REG, &val))
+ return -ENODEV;
+
+ address = val & ~(VIA686A_EXTENT - 1);
+ if (address == 0) {
+ dev_err(&dev->dev,
+ "base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+ return -ENODEV;
+ }
+
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_read_config_word(dev, VIA686A_ENABLE_REG, &val))
+ return -ENODEV;
+ if (!(val & 0x0001)) {
+ if (!force_addr) {
+ dev_warn(&dev->dev,
+ "Sensors disabled, enable with force_addr=0x%x\n",
+ address);
+ return -ENODEV;
+ }
+
+ dev_warn(&dev->dev, "Enabling sensors\n");
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_write_config_word(dev, VIA686A_ENABLE_REG,
+ val | 0x0001))
+ return -ENODEV;
+ }
+
+ if (platform_driver_register(&via686a_driver))
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ if (via686a_device_add(address))
+ goto exit_unregister;
+
+ /*
+ * Always return failure here. This is to allow other drivers to bind
+ * to this pci device. We don't really want to have control over the
+ * pci device, we only wanted to read as few register values from it.
+ */
+ s_bridge = pci_dev_get(dev);
+ return -ENODEV;
+
+exit_unregister:
+ platform_driver_unregister(&via686a_driver);
+exit:
+ return -ENODEV;
+}
+
+static struct pci_driver via686a_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = via686a_pci_ids,
+ .probe = via686a_pci_probe,
+};
+
+static int __init sm_via686a_init(void)
+{
+ return pci_register_driver(&via686a_pci_driver);
+}
+
+static void __exit sm_via686a_exit(void)
+{
+ pci_unregister_driver(&via686a_pci_driver);
+ if (s_bridge != NULL) {
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&via686a_driver);
+ pci_dev_put(s_bridge);
+ s_bridge = NULL;
+ }
+}
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>, "
+ "Mark Studebaker <mdsxyz123@yahoo.com> "
+ "and Bob Dougherty <bobd@stanford.edu>");
+MODULE_DESCRIPTION("VIA 686A Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(sm_via686a_init);
+module_exit(sm_via686a_exit);
diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c
new file mode 100644
index 000000000..4a5e911d2
--- /dev/null
+++ b/drivers/hwmon/vt1211.c
@@ -0,0 +1,1367 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * vt1211.c - driver for the VIA VT1211 Super-I/O chip integrated hardware
+ * monitoring features
+ * Copyright (C) 2006 Juerg Haefliger <juergh@gmail.com>
+ *
+ * This driver is based on the driver for kernel 2.4 by Mark D. Studebaker
+ * and its port to kernel 2.6 by Lars Ekman.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.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/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/ioport.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+static int uch_config = -1;
+module_param(uch_config, int, 0);
+MODULE_PARM_DESC(uch_config, "Initialize the universal channel configuration");
+
+static int int_mode = -1;
+module_param(int_mode, int, 0);
+MODULE_PARM_DESC(int_mode, "Force the temperature interrupt mode");
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static struct platform_device *pdev;
+
+#define DRVNAME "vt1211"
+
+/* ---------------------------------------------------------------------
+ * Registers
+ *
+ * The sensors are defined as follows.
+ *
+ * Sensor Voltage Mode Temp Mode Notes (from the datasheet)
+ * -------- ------------ --------- --------------------------
+ * Reading 1 temp1 Intel thermal diode
+ * Reading 3 temp2 Internal thermal diode
+ * UCH1/Reading2 in0 temp3 NTC type thermistor
+ * UCH2 in1 temp4 +2.5V
+ * UCH3 in2 temp5 VccP
+ * UCH4 in3 temp6 +5V
+ * UCH5 in4 temp7 +12V
+ * 3.3V in5 Internal VDD (+3.3V)
+ *
+ * --------------------------------------------------------------------- */
+
+/* Voltages (in) numbered 0-5 (ix) */
+#define VT1211_REG_IN(ix) (0x21 + (ix))
+#define VT1211_REG_IN_MIN(ix) ((ix) == 0 ? 0x3e : 0x2a + 2 * (ix))
+#define VT1211_REG_IN_MAX(ix) ((ix) == 0 ? 0x3d : 0x29 + 2 * (ix))
+
+/* Temperatures (temp) numbered 0-6 (ix) */
+static u8 regtemp[] = {0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25};
+static u8 regtempmax[] = {0x39, 0x1d, 0x3d, 0x2b, 0x2d, 0x2f, 0x31};
+static u8 regtemphyst[] = {0x3a, 0x1e, 0x3e, 0x2c, 0x2e, 0x30, 0x32};
+
+/* Fans numbered 0-1 (ix) */
+#define VT1211_REG_FAN(ix) (0x29 + (ix))
+#define VT1211_REG_FAN_MIN(ix) (0x3b + (ix))
+#define VT1211_REG_FAN_DIV 0x47
+
+/* PWMs numbered 0-1 (ix) */
+/* Auto points numbered 0-3 (ap) */
+#define VT1211_REG_PWM(ix) (0x60 + (ix))
+#define VT1211_REG_PWM_CLK 0x50
+#define VT1211_REG_PWM_CTL 0x51
+#define VT1211_REG_PWM_AUTO_TEMP(ap) (0x55 - (ap))
+#define VT1211_REG_PWM_AUTO_PWM(ix, ap) (0x58 + 2 * (ix) - (ap))
+
+/* Miscellaneous registers */
+#define VT1211_REG_CONFIG 0x40
+#define VT1211_REG_ALARM1 0x41
+#define VT1211_REG_ALARM2 0x42
+#define VT1211_REG_VID 0x45
+#define VT1211_REG_UCH_CONFIG 0x4a
+#define VT1211_REG_TEMP1_CONFIG 0x4b
+#define VT1211_REG_TEMP2_CONFIG 0x4c
+
+/* In, temp & fan alarm bits */
+static const u8 bitalarmin[] = {11, 0, 1, 3, 8, 2, 9};
+static const u8 bitalarmtemp[] = {4, 15, 11, 0, 1, 3, 8};
+static const u8 bitalarmfan[] = {6, 7};
+
+/* ---------------------------------------------------------------------
+ * Data structures and manipulation thereof
+ * --------------------------------------------------------------------- */
+
+struct vt1211_data {
+ unsigned short addr;
+ const char *name;
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Register values */
+ u8 in[6];
+ u8 in_max[6];
+ u8 in_min[6];
+ u8 temp[7];
+ u8 temp_max[7];
+ u8 temp_hyst[7];
+ u8 fan[2];
+ u8 fan_min[2];
+ u8 fan_div[2];
+ u8 fan_ctl;
+ u8 pwm[2];
+ u8 pwm_ctl[2];
+ u8 pwm_clk;
+ u8 pwm_auto_temp[4];
+ u8 pwm_auto_pwm[2][4];
+ u8 vid; /* Read once at init time */
+ u8 vrm;
+ u8 uch_config; /* Read once at init time */
+ u16 alarms;
+};
+
+/* ix = [0-5] */
+#define ISVOLT(ix, uch_config) ((ix) > 4 ? 1 : \
+ !(((uch_config) >> ((ix) + 2)) & 1))
+
+/* ix = [0-6] */
+#define ISTEMP(ix, uch_config) ((ix) < 2 ? 1 : \
+ ((uch_config) >> (ix)) & 1)
+
+/*
+ * in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the
+ * driver according to the VT1211 BIOS porting guide
+ */
+#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \
+ (((reg) - 3) * 15882 + 479) / 958 : \
+ (((reg) - 3) * 10000 + 479) / 958)
+#define IN_TO_REG(ix, val) (clamp_val((ix) == 5 ? \
+ ((val) * 958 + 7941) / 15882 + 3 : \
+ ((val) * 958 + 5000) / 10000 + 3, 0, 255))
+
+/*
+ * temp1 (ix = 0) is an intel thermal diode which is scaled in user space.
+ * temp2 (ix = 1) is the internal temp diode so it's scaled in the driver
+ * according to some measurements that I took on an EPIA M10000.
+ * temp3-7 are thermistor based so the driver returns the voltage measured at
+ * the pin (range 0V - 2.2V).
+ */
+#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \
+ (ix) == 1 ? (reg) < 51 ? 0 : \
+ ((reg) - 51) * 1000 : \
+ ((253 - (reg)) * 2200 + 105) / 210)
+#define TEMP_TO_REG(ix, val) clamp_val( \
+ ((ix) == 0 ? ((val) + 500) / 1000 : \
+ (ix) == 1 ? ((val) + 500) / 1000 + 51 : \
+ 253 - ((val) * 210 + 1100) / 2200), 0, 255)
+
+#define DIV_FROM_REG(reg) (1 << (reg))
+
+#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \
+ 1310720 / (reg) / DIV_FROM_REG(div))
+#define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \
+ clamp_val((1310720 / (val) / \
+ DIV_FROM_REG(div)), 1, 254))
+
+/* ---------------------------------------------------------------------
+ * Super-I/O constants and functions
+ * --------------------------------------------------------------------- */
+
+/*
+ * Configuration index port registers
+ * The vt1211 can live at 2 different addresses so we need to probe both
+ */
+#define SIO_REG_CIP1 0x2e
+#define SIO_REG_CIP2 0x4e
+
+/* Configuration registers */
+#define SIO_VT1211_LDN 0x07 /* logical device number */
+#define SIO_VT1211_DEVID 0x20 /* device ID */
+#define SIO_VT1211_DEVREV 0x21 /* device revision */
+#define SIO_VT1211_ACTIVE 0x30 /* HW monitor active */
+#define SIO_VT1211_BADDR 0x60 /* base I/O address */
+#define SIO_VT1211_ID 0x3c /* VT1211 device ID */
+
+/* VT1211 logical device numbers */
+#define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */
+
+static inline void superio_outb(int sio_cip, int reg, int val)
+{
+ outb(reg, sio_cip);
+ outb(val, sio_cip + 1);
+}
+
+static inline int superio_inb(int sio_cip, int reg)
+{
+ outb(reg, sio_cip);
+ return inb(sio_cip + 1);
+}
+
+static inline void superio_select(int sio_cip, int ldn)
+{
+ outb(SIO_VT1211_LDN, sio_cip);
+ outb(ldn, sio_cip + 1);
+}
+
+static inline int superio_enter(int sio_cip)
+{
+ if (!request_muxed_region(sio_cip, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x87, sio_cip);
+ outb(0x87, sio_cip);
+
+ return 0;
+}
+
+static inline void superio_exit(int sio_cip)
+{
+ outb(0xaa, sio_cip);
+ release_region(sio_cip, 2);
+}
+
+/* ---------------------------------------------------------------------
+ * Device I/O access
+ * --------------------------------------------------------------------- */
+
+static inline u8 vt1211_read8(struct vt1211_data *data, u8 reg)
+{
+ return inb(data->addr + reg);
+}
+
+static inline void vt1211_write8(struct vt1211_data *data, u8 reg, u8 val)
+{
+ outb(val, data->addr + reg);
+}
+
+static struct vt1211_data *vt1211_update_device(struct device *dev)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ int ix, val;
+
+ mutex_lock(&data->update_lock);
+
+ /* registers cache is refreshed after 1 second */
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ /* read VID */
+ data->vid = vt1211_read8(data, VT1211_REG_VID) & 0x1f;
+
+ /* voltage (in) registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
+ if (ISVOLT(ix, data->uch_config)) {
+ data->in[ix] = vt1211_read8(data,
+ VT1211_REG_IN(ix));
+ data->in_min[ix] = vt1211_read8(data,
+ VT1211_REG_IN_MIN(ix));
+ data->in_max[ix] = vt1211_read8(data,
+ VT1211_REG_IN_MAX(ix));
+ }
+ }
+
+ /* temp registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) {
+ if (ISTEMP(ix, data->uch_config)) {
+ data->temp[ix] = vt1211_read8(data,
+ regtemp[ix]);
+ data->temp_max[ix] = vt1211_read8(data,
+ regtempmax[ix]);
+ data->temp_hyst[ix] = vt1211_read8(data,
+ regtemphyst[ix]);
+ }
+ }
+
+ /* fan & pwm registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
+ data->fan[ix] = vt1211_read8(data,
+ VT1211_REG_FAN(ix));
+ data->fan_min[ix] = vt1211_read8(data,
+ VT1211_REG_FAN_MIN(ix));
+ data->pwm[ix] = vt1211_read8(data,
+ VT1211_REG_PWM(ix));
+ }
+ val = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (val >> 4) & 3;
+ data->fan_div[1] = (val >> 6) & 3;
+ data->fan_ctl = val & 0xf;
+
+ val = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = val & 0xf;
+ data->pwm_ctl[1] = (val >> 4) & 0xf;
+
+ data->pwm_clk = vt1211_read8(data, VT1211_REG_PWM_CLK);
+
+ /* pwm & temp auto point registers */
+ data->pwm_auto_pwm[0][1] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(0, 1));
+ data->pwm_auto_pwm[0][2] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(0, 2));
+ data->pwm_auto_pwm[1][1] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(1, 1));
+ data->pwm_auto_pwm[1][2] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(1, 2));
+ for (ix = 0; ix < ARRAY_SIZE(data->pwm_auto_temp); ix++) {
+ data->pwm_auto_temp[ix] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_TEMP(ix));
+ }
+
+ /* alarm registers */
+ data->alarms = (vt1211_read8(data, VT1211_REG_ALARM2) << 8) |
+ vt1211_read8(data, VT1211_REG_ALARM1);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* ---------------------------------------------------------------------
+ * Voltage sysfs interfaces
+ * ix = [0-5]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_IN_INPUT 0
+#define SHOW_SET_IN_MIN 1
+#define SHOW_SET_IN_MAX 2
+#define SHOW_IN_ALARM 3
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_IN_INPUT:
+ res = IN_FROM_REG(ix, data->in[ix]);
+ break;
+ case SHOW_SET_IN_MIN:
+ res = IN_FROM_REG(ix, data->in_min[ix]);
+ break;
+ case SHOW_SET_IN_MAX:
+ res = IN_FROM_REG(ix, data->in_max[ix]);
+ break;
+ case SHOW_IN_ALARM:
+ res = (data->alarms >> bitalarmin[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SHOW_SET_IN_MIN:
+ data->in_min[ix] = IN_TO_REG(ix, val);
+ vt1211_write8(data, VT1211_REG_IN_MIN(ix), data->in_min[ix]);
+ break;
+ case SHOW_SET_IN_MAX:
+ data->in_max[ix] = IN_TO_REG(ix, val);
+ vt1211_write8(data, VT1211_REG_IN_MAX(ix), data->in_max[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Temperature sysfs interfaces
+ * ix = [0-6]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_TEMP_INPUT 0
+#define SHOW_SET_TEMP_MAX 1
+#define SHOW_SET_TEMP_MAX_HYST 2
+#define SHOW_TEMP_ALARM 3
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_TEMP_INPUT:
+ res = TEMP_FROM_REG(ix, data->temp[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX:
+ res = TEMP_FROM_REG(ix, data->temp_max[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX_HYST:
+ res = TEMP_FROM_REG(ix, data->temp_hyst[ix]);
+ break;
+ case SHOW_TEMP_ALARM:
+ res = (data->alarms >> bitalarmtemp[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SHOW_SET_TEMP_MAX:
+ data->temp_max[ix] = TEMP_TO_REG(ix, val);
+ vt1211_write8(data, regtempmax[ix],
+ data->temp_max[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX_HYST:
+ data->temp_hyst[ix] = TEMP_TO_REG(ix, val);
+ vt1211_write8(data, regtemphyst[ix],
+ data->temp_hyst[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Fan sysfs interfaces
+ * ix = [0-1]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_FAN_INPUT 0
+#define SHOW_SET_FAN_MIN 1
+#define SHOW_SET_FAN_DIV 2
+#define SHOW_FAN_ALARM 3
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_FAN_INPUT:
+ res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]);
+ break;
+ case SHOW_SET_FAN_MIN:
+ res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]);
+ break;
+ case SHOW_SET_FAN_DIV:
+ res = DIV_FROM_REG(data->fan_div[ix]);
+ break;
+ case SHOW_FAN_ALARM:
+ res = (data->alarms >> bitalarmfan[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (reg >> 4) & 3;
+ data->fan_div[1] = (reg >> 6) & 3;
+ data->fan_ctl = reg & 0xf;
+
+ switch (fn) {
+ case SHOW_SET_FAN_MIN:
+ data->fan_min[ix] = RPM_TO_REG(val, data->fan_div[ix]);
+ vt1211_write8(data, VT1211_REG_FAN_MIN(ix),
+ data->fan_min[ix]);
+ break;
+ case SHOW_SET_FAN_DIV:
+ switch (val) {
+ case 1:
+ data->fan_div[ix] = 0;
+ break;
+ case 2:
+ data->fan_div[ix] = 1;
+ break;
+ case 4:
+ data->fan_div[ix] = 2;
+ break;
+ case 8:
+ data->fan_div[ix] = 3;
+ break;
+ default:
+ count = -EINVAL;
+ dev_warn(dev,
+ "fan div value %ld not supported. Choose one of 1, 2, 4, or 8.\n",
+ val);
+ goto EXIT;
+ }
+ vt1211_write8(data, VT1211_REG_FAN_DIV,
+ ((data->fan_div[1] << 6) |
+ (data->fan_div[0] << 4) |
+ data->fan_ctl));
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * PWM sysfs interfaces
+ * ix = [0-1]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_PWM 0
+#define SHOW_SET_PWM_ENABLE 1
+#define SHOW_SET_PWM_FREQ 2
+#define SHOW_SET_PWM_AUTO_CHANNELS_TEMP 3
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_PWM:
+ res = data->pwm[ix];
+ break;
+ case SHOW_SET_PWM_ENABLE:
+ res = ((data->pwm_ctl[ix] >> 3) & 1) ? 2 : 0;
+ break;
+ case SHOW_SET_PWM_FREQ:
+ res = 90000 >> (data->pwm_clk & 7);
+ break;
+ case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
+ res = (data->pwm_ctl[ix] & 7) + 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int tmp, reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ switch (fn) {
+ case SHOW_SET_PWM_ENABLE:
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (reg >> 4) & 3;
+ data->fan_div[1] = (reg >> 6) & 3;
+ data->fan_ctl = reg & 0xf;
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+ switch (val) {
+ case 0:
+ data->pwm_ctl[ix] &= 7;
+ /*
+ * disable SmartGuardian if both PWM outputs are
+ * disabled
+ */
+ if ((data->pwm_ctl[ix ^ 1] & 1) == 0)
+ data->fan_ctl &= 0xe;
+ break;
+ case 2:
+ data->pwm_ctl[ix] |= 8;
+ data->fan_ctl |= 1;
+ break;
+ default:
+ count = -EINVAL;
+ dev_warn(dev,
+ "pwm mode %ld not supported. Choose one of 0 or 2.\n",
+ val);
+ goto EXIT;
+ }
+ vt1211_write8(data, VT1211_REG_PWM_CTL,
+ ((data->pwm_ctl[1] << 4) |
+ data->pwm_ctl[0]));
+ vt1211_write8(data, VT1211_REG_FAN_DIV,
+ ((data->fan_div[1] << 6) |
+ (data->fan_div[0] << 4) |
+ data->fan_ctl));
+ break;
+ case SHOW_SET_PWM_FREQ:
+ val = 135000 / clamp_val(val, 135000 >> 7, 135000);
+ /* calculate tmp = log2(val) */
+ tmp = 0;
+ for (val >>= 1; val > 0; val >>= 1)
+ tmp++;
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CLK);
+ data->pwm_clk = (reg & 0xf8) | tmp;
+ vt1211_write8(data, VT1211_REG_PWM_CLK, data->pwm_clk);
+ break;
+ case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
+ if (val < 1 || val > 7) {
+ count = -EINVAL;
+ dev_warn(dev,
+ "temp channel %ld not supported. Choose a value between 1 and 7.\n",
+ val);
+ goto EXIT;
+ }
+ if (!ISTEMP(val - 1, data->uch_config)) {
+ count = -EINVAL;
+ dev_warn(dev, "temp channel %ld is not available.\n",
+ val);
+ goto EXIT;
+ }
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+ data->pwm_ctl[ix] = (data->pwm_ctl[ix] & 8) | (val - 1);
+ vt1211_write8(data, VT1211_REG_PWM_CTL,
+ ((data->pwm_ctl[1] << 4) | data->pwm_ctl[0]));
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * PWM auto point definitions
+ * ix = [0-1]
+ * ap = [0-3]
+ * --------------------------------------------------------------------- */
+
+/*
+ * pwm[ix+1]_auto_point[ap+1]_temp mapping table:
+ * Note that there is only a single set of temp auto points that controls both
+ * PWM controllers. We still create 2 sets of sysfs files to make it look
+ * more consistent even though they map to the same registers.
+ *
+ * ix ap : description
+ * -------------------
+ * 0 0 : pwm1/2 off temperature (pwm_auto_temp[0])
+ * 0 1 : pwm1/2 low speed temperature (pwm_auto_temp[1])
+ * 0 2 : pwm1/2 high speed temperature (pwm_auto_temp[2])
+ * 0 3 : pwm1/2 full speed temperature (pwm_auto_temp[3])
+ * 1 0 : pwm1/2 off temperature (pwm_auto_temp[0])
+ * 1 1 : pwm1/2 low speed temperature (pwm_auto_temp[1])
+ * 1 2 : pwm1/2 high speed temperature (pwm_auto_temp[2])
+ * 1 3 : pwm1/2 full speed temperature (pwm_auto_temp[3])
+ */
+
+static ssize_t show_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7,
+ data->pwm_auto_temp[ap]));
+}
+
+static ssize_t set_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+ int reg;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+
+ mutex_lock(&data->update_lock);
+
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+
+ data->pwm_auto_temp[ap] = TEMP_TO_REG(data->pwm_ctl[ix] & 7, val);
+ vt1211_write8(data, VT1211_REG_PWM_AUTO_TEMP(ap),
+ data->pwm_auto_temp[ap]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * pwm[ix+1]_auto_point[ap+1]_pwm mapping table:
+ * Note that the PWM auto points 0 & 3 are hard-wired in the VT1211 and can't
+ * be changed.
+ *
+ * ix ap : description
+ * -------------------
+ * 0 0 : pwm1 off (pwm_auto_pwm[0][0], hard-wired to 0)
+ * 0 1 : pwm1 low speed duty cycle (pwm_auto_pwm[0][1])
+ * 0 2 : pwm1 high speed duty cycle (pwm_auto_pwm[0][2])
+ * 0 3 : pwm1 full speed (pwm_auto_pwm[0][3], hard-wired to 255)
+ * 1 0 : pwm2 off (pwm_auto_pwm[1][0], hard-wired to 0)
+ * 1 1 : pwm2 low speed duty cycle (pwm_auto_pwm[1][1])
+ * 1 2 : pwm2 high speed duty cycle (pwm_auto_pwm[1][2])
+ * 1 3 : pwm2 full speed (pwm_auto_pwm[1][3], hard-wired to 255)
+ */
+
+static ssize_t show_pwm_auto_point_pwm(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+
+ return sprintf(buf, "%d\n", data->pwm_auto_pwm[ix][ap]);
+}
+
+static ssize_t set_pwm_auto_point_pwm(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->pwm_auto_pwm[ix][ap] = clamp_val(val, 0, 255);
+ vt1211_write8(data, VT1211_REG_PWM_AUTO_PWM(ix, ap),
+ data->pwm_auto_pwm[ix][ap]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Miscellaneous sysfs interfaces (VRM, VID, name, and (legacy) alarms)
+ * --------------------------------------------------------------------- */
+
+static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_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 ssize_t show_vid(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t show_alarms(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+/* ---------------------------------------------------------------------
+ * Device attribute structs
+ * --------------------------------------------------------------------- */
+
+#define SENSOR_ATTR_IN(ix) \
+{ SENSOR_ATTR_2(in##ix##_input, S_IRUGO, \
+ show_in, NULL, SHOW_IN_INPUT, ix), \
+ SENSOR_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SHOW_SET_IN_MIN, ix), \
+ SENSOR_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SHOW_SET_IN_MAX, ix), \
+ SENSOR_ATTR_2(in##ix##_alarm, S_IRUGO, \
+ show_in, NULL, SHOW_IN_ALARM, ix) \
+}
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in[][4] = {
+ SENSOR_ATTR_IN(0),
+ SENSOR_ATTR_IN(1),
+ SENSOR_ATTR_IN(2),
+ SENSOR_ATTR_IN(3),
+ SENSOR_ATTR_IN(4),
+ SENSOR_ATTR_IN(5)
+};
+
+#define IN_UNIT_ATTRS(X) \
+{ &vt1211_sysfs_in[X][0].dev_attr.attr, \
+ &vt1211_sysfs_in[X][1].dev_attr.attr, \
+ &vt1211_sysfs_in[X][2].dev_attr.attr, \
+ &vt1211_sysfs_in[X][3].dev_attr.attr, \
+ NULL \
+}
+
+static struct attribute *vt1211_in_attr[][5] = {
+ IN_UNIT_ATTRS(0),
+ IN_UNIT_ATTRS(1),
+ IN_UNIT_ATTRS(2),
+ IN_UNIT_ATTRS(3),
+ IN_UNIT_ATTRS(4),
+ IN_UNIT_ATTRS(5)
+};
+
+static const struct attribute_group vt1211_in_attr_group[] = {
+ { .attrs = vt1211_in_attr[0] },
+ { .attrs = vt1211_in_attr[1] },
+ { .attrs = vt1211_in_attr[2] },
+ { .attrs = vt1211_in_attr[3] },
+ { .attrs = vt1211_in_attr[4] },
+ { .attrs = vt1211_in_attr[5] }
+};
+
+#define SENSOR_ATTR_TEMP(ix) \
+{ SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \
+ show_temp, NULL, SHOW_TEMP_INPUT, ix-1), \
+ SENSOR_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1), \
+ SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1), \
+ SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \
+ show_temp, NULL, SHOW_TEMP_ALARM, ix-1) \
+}
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp[][4] = {
+ SENSOR_ATTR_TEMP(1),
+ SENSOR_ATTR_TEMP(2),
+ SENSOR_ATTR_TEMP(3),
+ SENSOR_ATTR_TEMP(4),
+ SENSOR_ATTR_TEMP(5),
+ SENSOR_ATTR_TEMP(6),
+ SENSOR_ATTR_TEMP(7),
+};
+
+#define TEMP_UNIT_ATTRS(X) \
+{ &vt1211_sysfs_temp[X][0].dev_attr.attr, \
+ &vt1211_sysfs_temp[X][1].dev_attr.attr, \
+ &vt1211_sysfs_temp[X][2].dev_attr.attr, \
+ &vt1211_sysfs_temp[X][3].dev_attr.attr, \
+ NULL \
+}
+
+static struct attribute *vt1211_temp_attr[][5] = {
+ TEMP_UNIT_ATTRS(0),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+ TEMP_UNIT_ATTRS(3),
+ TEMP_UNIT_ATTRS(4),
+ TEMP_UNIT_ATTRS(5),
+ TEMP_UNIT_ATTRS(6)
+};
+
+static const struct attribute_group vt1211_temp_attr_group[] = {
+ { .attrs = vt1211_temp_attr[0] },
+ { .attrs = vt1211_temp_attr[1] },
+ { .attrs = vt1211_temp_attr[2] },
+ { .attrs = vt1211_temp_attr[3] },
+ { .attrs = vt1211_temp_attr[4] },
+ { .attrs = vt1211_temp_attr[5] },
+ { .attrs = vt1211_temp_attr[6] }
+};
+
+#define SENSOR_ATTR_FAN(ix) \
+ SENSOR_ATTR_2(fan##ix##_input, S_IRUGO, \
+ show_fan, NULL, SHOW_FAN_INPUT, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SHOW_SET_FAN_MIN, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_div, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SHOW_SET_FAN_DIV, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_alarm, S_IRUGO, \
+ show_fan, NULL, SHOW_FAN_ALARM, ix-1)
+
+#define SENSOR_ATTR_PWM(ix) \
+ SENSOR_ATTR_2(pwm##ix, S_IRUGO, \
+ show_pwm, NULL, SHOW_PWM, ix-1), \
+ SENSOR_ATTR_2(pwm##ix##_enable, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_ENABLE, ix-1), \
+ SENSOR_ATTR_2(pwm##ix##_auto_channels_temp, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_AUTO_CHANNELS_TEMP, ix-1)
+
+#define SENSOR_ATTR_PWM_FREQ(ix) \
+ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_FREQ, ix-1)
+
+#define SENSOR_ATTR_PWM_FREQ_RO(ix) \
+ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO, \
+ show_pwm, NULL, SHOW_SET_PWM_FREQ, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO | S_IWUSR, \
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO, \
+ show_pwm_auto_point_temp, NULL, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_PWM(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO | S_IWUSR, \
+ show_pwm_auto_point_pwm, set_pwm_auto_point_pwm, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO, \
+ show_pwm_auto_point_pwm, NULL, \
+ ap-1, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_fan_pwm[] = {
+ SENSOR_ATTR_FAN(1),
+ SENSOR_ATTR_FAN(2),
+ SENSOR_ATTR_PWM(1),
+ SENSOR_ATTR_PWM(2),
+ SENSOR_ATTR_PWM_FREQ(1),
+ SENSOR_ATTR_PWM_FREQ_RO(2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 4),
+};
+
+static struct device_attribute vt1211_sysfs_misc[] = {
+ __ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm),
+ __ATTR(cpu0_vid, S_IRUGO, show_vid, NULL),
+ __ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(alarms, S_IRUGO, show_alarms, NULL),
+};
+
+/* ---------------------------------------------------------------------
+ * Device registration and initialization
+ * --------------------------------------------------------------------- */
+
+static void vt1211_init_device(struct vt1211_data *data)
+{
+ /* set VRM */
+ data->vrm = vid_which_vrm();
+
+ /* Read (and initialize) UCH config */
+ data->uch_config = vt1211_read8(data, VT1211_REG_UCH_CONFIG);
+ if (uch_config > -1) {
+ data->uch_config = (data->uch_config & 0x83) |
+ (uch_config << 2);
+ vt1211_write8(data, VT1211_REG_UCH_CONFIG, data->uch_config);
+ }
+
+ /*
+ * Initialize the interrupt mode (if request at module load time).
+ * The VT1211 implements 3 different modes for clearing interrupts:
+ * 0: Clear INT when status register is read. Regenerate INT as long
+ * as temp stays above hysteresis limit.
+ * 1: Clear INT when status register is read. DON'T regenerate INT
+ * until temp falls below hysteresis limit and exceeds hot limit
+ * again.
+ * 2: Clear INT when temp falls below max limit.
+ *
+ * The driver only allows to force mode 0 since that's the only one
+ * that makes sense for 'sensors'
+ */
+ if (int_mode == 0) {
+ vt1211_write8(data, VT1211_REG_TEMP1_CONFIG, 0);
+ vt1211_write8(data, VT1211_REG_TEMP2_CONFIG, 0);
+ }
+
+ /* Fill in some hard wired values into our data struct */
+ data->pwm_auto_pwm[0][3] = 255;
+ data->pwm_auto_pwm[1][3] = 255;
+}
+
+static void vt1211_remove_sysfs(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++)
+ sysfs_remove_group(&dev->kobj, &vt1211_in_attr_group[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++)
+ sysfs_remove_group(&dev->kobj, &vt1211_temp_attr_group[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
+ device_remove_file(dev,
+ &vt1211_sysfs_fan_pwm[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++)
+ device_remove_file(dev, &vt1211_sysfs_misc[i]);
+}
+
+static int vt1211_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vt1211_data *data;
+ struct resource *res;
+ int i, err;
+
+ data = devm_kzalloc(dev, sizeof(struct vt1211_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(dev, res->start, resource_size(res),
+ DRVNAME)) {
+ dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
+ (unsigned long)res->start, (unsigned long)res->end);
+ return -EBUSY;
+ }
+ data->addr = res->start;
+ data->name = DRVNAME;
+ mutex_init(&data->update_lock);
+
+ platform_set_drvdata(pdev, data);
+
+ /* Initialize the VT1211 chip */
+ vt1211_init_device(data);
+
+ /* Create sysfs interface files */
+ for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++) {
+ if (ISVOLT(i, data->uch_config)) {
+ err = sysfs_create_group(&dev->kobj,
+ &vt1211_in_attr_group[i]);
+ if (err)
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++) {
+ if (ISTEMP(i, data->uch_config)) {
+ err = sysfs_create_group(&dev->kobj,
+ &vt1211_temp_attr_group[i]);
+ if (err)
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
+ err = device_create_file(dev,
+ &vt1211_sysfs_fan_pwm[i].dev_attr);
+ if (err)
+ goto EXIT_DEV_REMOVE;
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) {
+ err = device_create_file(dev,
+ &vt1211_sysfs_misc[i]);
+ if (err)
+ goto EXIT_DEV_REMOVE;
+ }
+
+ /* Register device */
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(dev, "Class registration failed (%d)\n", err);
+ goto EXIT_DEV_REMOVE_SILENT;
+ }
+
+ return 0;
+
+EXIT_DEV_REMOVE:
+ dev_err(dev, "Sysfs interface creation failed (%d)\n", err);
+EXIT_DEV_REMOVE_SILENT:
+ vt1211_remove_sysfs(pdev);
+ return err;
+}
+
+static int vt1211_remove(struct platform_device *pdev)
+{
+ struct vt1211_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ vt1211_remove_sysfs(pdev);
+
+ return 0;
+}
+
+static struct platform_driver vt1211_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = vt1211_probe,
+ .remove = vt1211_remove,
+};
+
+static int __init vt1211_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + 0x7f,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed (%d)\n", err);
+ goto EXIT;
+ }
+
+ res.name = pdev->name;
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto EXIT_DEV_PUT;
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto EXIT_DEV_PUT;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto EXIT_DEV_PUT;
+ }
+
+ return 0;
+
+EXIT_DEV_PUT:
+ platform_device_put(pdev);
+EXIT:
+ return err;
+}
+
+static int __init vt1211_find(int sio_cip, unsigned short *address)
+{
+ int err;
+ int devid;
+
+ err = superio_enter(sio_cip);
+ if (err)
+ return err;
+
+ err = -ENODEV;
+ devid = force_id ? force_id : superio_inb(sio_cip, SIO_VT1211_DEVID);
+ if (devid != SIO_VT1211_ID)
+ goto EXIT;
+
+ superio_select(sio_cip, SIO_VT1211_LDN_HWMON);
+
+ if ((superio_inb(sio_cip, SIO_VT1211_ACTIVE) & 1) == 0) {
+ pr_warn("HW monitor is disabled, skipping\n");
+ goto EXIT;
+ }
+
+ *address = ((superio_inb(sio_cip, SIO_VT1211_BADDR) << 8) |
+ (superio_inb(sio_cip, SIO_VT1211_BADDR + 1))) & 0xff00;
+ if (*address == 0) {
+ pr_warn("Base address is not set, skipping\n");
+ goto EXIT;
+ }
+
+ err = 0;
+ pr_info("Found VT1211 chip at 0x%04x, revision %u\n",
+ *address, superio_inb(sio_cip, SIO_VT1211_DEVREV));
+
+EXIT:
+ superio_exit(sio_cip);
+ return err;
+}
+
+static int __init vt1211_init(void)
+{
+ int err;
+ unsigned short address = 0;
+
+ err = vt1211_find(SIO_REG_CIP1, &address);
+ if (err) {
+ err = vt1211_find(SIO_REG_CIP2, &address);
+ if (err)
+ goto EXIT;
+ }
+
+ if ((uch_config < -1) || (uch_config > 31)) {
+ err = -EINVAL;
+ pr_warn("Invalid UCH configuration %d. Choose a value between 0 and 31.\n",
+ uch_config);
+ goto EXIT;
+ }
+
+ if ((int_mode < -1) || (int_mode > 0)) {
+ err = -EINVAL;
+ pr_warn("Invalid interrupt mode %d. Only mode 0 is supported.\n",
+ int_mode);
+ goto EXIT;
+ }
+
+ err = platform_driver_register(&vt1211_driver);
+ if (err)
+ goto EXIT;
+
+ /* Sets global pdev as a side effect */
+ err = vt1211_device_add(address);
+ if (err)
+ goto EXIT_DRV_UNREGISTER;
+
+ return 0;
+
+EXIT_DRV_UNREGISTER:
+ platform_driver_unregister(&vt1211_driver);
+EXIT:
+ return err;
+}
+
+static void __exit vt1211_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&vt1211_driver);
+}
+
+MODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>");
+MODULE_DESCRIPTION("VT1211 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(vt1211_init);
+module_exit(vt1211_exit);
diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c
new file mode 100644
index 000000000..3b7f8922b
--- /dev/null
+++ b/drivers/hwmon/vt8231.c
@@ -0,0 +1,1060 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * vt8231.c - Part of lm_sensors, Linux kernel modules
+ * for hardware monitoring
+ *
+ * Copyright (c) 2005 Roger Lucas <vt8231@hiddenengine.co.uk>
+ * Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ * Aaron M. Marsh <amarsh@sdf.lonestar.org>
+ */
+
+/*
+ * Supports VIA VT8231 South Bridge embedded sensors
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.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/io.h>
+
+static int force_addr;
+module_param(force_addr, int, 0);
+MODULE_PARM_DESC(force_addr, "Initialize the base address of the sensors");
+
+static struct platform_device *pdev;
+
+#define VT8231_EXTENT 0x80
+#define VT8231_BASE_REG 0x70
+#define VT8231_ENABLE_REG 0x74
+
+#define DRIVER_NAME "vt8231"
+
+/*
+ * The VT8231 registers
+ *
+ * The reset value for the input channel configuration is used (Reg 0x4A=0x07)
+ * which sets the selected inputs marked with '*' below if multiple options are
+ * possible:
+ *
+ * Voltage Mode Temperature Mode
+ * Sensor Linux Id Linux Id VIA Id
+ * -------- -------- -------- ------
+ * CPU Diode N/A temp1 0
+ * UIC1 in0 temp2 * 1
+ * UIC2 in1 * temp3 2
+ * UIC3 in2 * temp4 3
+ * UIC4 in3 * temp5 4
+ * UIC5 in4 * temp6 5
+ * 3.3V in5 N/A
+ *
+ * Note that the BIOS may set the configuration register to a different value
+ * to match the motherboard configuration.
+ */
+
+/* fans numbered 0-1 */
+#define VT8231_REG_FAN_MIN(nr) (0x3b + (nr))
+#define VT8231_REG_FAN(nr) (0x29 + (nr))
+
+/* Voltage inputs numbered 0-5 */
+
+static const u8 regvolt[] = { 0x21, 0x22, 0x23, 0x24, 0x25, 0x26 };
+static const u8 regvoltmax[] = { 0x3d, 0x2b, 0x2d, 0x2f, 0x31, 0x33 };
+static const u8 regvoltmin[] = { 0x3e, 0x2c, 0x2e, 0x30, 0x32, 0x34 };
+
+/*
+ * Temperatures are numbered 1-6 according to the Linux kernel specification.
+ *
+ * In the VIA datasheet, however, the temperatures are numbered from zero.
+ * Since it is important that this driver can easily be compared to the VIA
+ * datasheet, we will use the VIA numbering within this driver and map the
+ * kernel sysfs device name to the VIA number in the sysfs callback.
+ */
+
+#define VT8231_REG_TEMP_LOW01 0x49
+#define VT8231_REG_TEMP_LOW25 0x4d
+
+static const u8 regtemp[] = { 0x1f, 0x21, 0x22, 0x23, 0x24, 0x25 };
+static const u8 regtempmax[] = { 0x39, 0x3d, 0x2b, 0x2d, 0x2f, 0x31 };
+static const u8 regtempmin[] = { 0x3a, 0x3e, 0x2c, 0x2e, 0x30, 0x32 };
+
+#define TEMP_FROM_REG(reg) (((253 * 4 - (reg)) * 550 + 105) / 210)
+#define TEMP_MAXMIN_FROM_REG(reg) (((253 - (reg)) * 2200 + 105) / 210)
+#define TEMP_MAXMIN_TO_REG(val) (253 - ((val) * 210 + 1100) / 2200)
+
+#define VT8231_REG_CONFIG 0x40
+#define VT8231_REG_ALARM1 0x41
+#define VT8231_REG_ALARM2 0x42
+#define VT8231_REG_FANDIV 0x47
+#define VT8231_REG_UCH_CONFIG 0x4a
+#define VT8231_REG_TEMP1_CONFIG 0x4b
+#define VT8231_REG_TEMP2_CONFIG 0x4c
+
+/*
+ * temps 0-5 as numbered in VIA datasheet - see later for mapping to Linux
+ * numbering
+ */
+#define ISTEMP(i, ch_config) ((i) == 0 ? 1 : \
+ ((ch_config) >> ((i)+1)) & 0x01)
+/* voltages 0-5 */
+#define ISVOLT(i, ch_config) ((i) == 5 ? 1 : \
+ !(((ch_config) >> ((i)+2)) & 0x01))
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+/*
+ * NB The values returned here are NOT temperatures. The calibration curves
+ * for the thermistor curves are board-specific and must go in the
+ * sensors.conf file. Temperature sensors are actually ten bits, but the
+ * VIA datasheet only considers the 8 MSBs obtained from the regtemp[]
+ * register. The temperature value returned should have a magnitude of 3,
+ * so we use the VIA scaling as the "true" scaling and use the remaining 2
+ * LSBs as fractional precision.
+ *
+ * All the on-chip hardware temperature comparisons for the alarms are only
+ * 8-bits wide, and compare against the 8 MSBs of the temperature. The bits
+ * in the registers VT8231_REG_TEMP_LOW01 and VT8231_REG_TEMP_LOW25 are
+ * ignored.
+ */
+
+/*
+ ****** FAN RPM CONVERSIONS ********
+ * This chip saturates back at 0, not at 255 like many the other chips.
+ * So, 0 means 0 RPM
+ */
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm <= 0 || rpm > 1310720)
+ return 0;
+ return clamp_val(1310720 / (rpm * div), 1, 255);
+}
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : 1310720 / ((val) * (div)))
+
+struct vt8231_data {
+ unsigned short addr;
+ const char *name;
+
+ struct mutex update_lock;
+ struct device *hwmon_dev;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 in[6]; /* Register value */
+ u8 in_max[6]; /* Register value */
+ u8 in_min[6]; /* Register value */
+ u16 temp[6]; /* Register value 10 bit, right aligned */
+ u8 temp_max[6]; /* Register value */
+ u8 temp_min[6]; /* Register value */
+ u8 fan[2]; /* Register value */
+ u8 fan_min[2]; /* Register value */
+ u8 fan_div[2]; /* Register encoding, shifted right */
+ u16 alarms; /* Register encoding */
+ u8 uch_config;
+};
+
+static struct pci_dev *s_bridge;
+
+static inline int vt8231_read_value(struct vt8231_data *data, u8 reg)
+{
+ return inb_p(data->addr + reg);
+}
+
+static inline void vt8231_write_value(struct vt8231_data *data, u8 reg,
+ u8 value)
+{
+ 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)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+
+ return sprintf(buf, "%d\n", ((data->in[nr] - 3) * 10000) / 958);
+}
+
+static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+
+ return sprintf(buf, "%d\n", ((data->in_min[nr] - 3) * 10000) / 958);
+}
+
+static ssize_t in_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+
+ return sprintf(buf, "%d\n", (((data->in_max[nr] - 3) * 10000) / 958));
+}
+
+static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_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->in_min[nr] = clamp_val(((val * 958) / 10000) + 3, 0, 255);
+ vt8231_write_value(data, regvoltmin[nr], data->in_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_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->in_max[nr] = clamp_val(((val * 958) / 10000) + 3, 0, 255);
+ vt8231_write_value(data, regvoltmax[nr], data->in_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* Special case for input 5 as this has 3.3V scaling built into the chip */
+static ssize_t in5_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt8231_data *data = vt8231_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ (((data->in[5] - 3) * 10000 * 54) / (958 * 34)));
+}
+
+static ssize_t in5_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt8231_data *data = vt8231_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ (((data->in_min[5] - 3) * 10000 * 54) / (958 * 34)));
+}
+
+static ssize_t in5_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt8231_data *data = vt8231_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ (((data->in_max[5] - 3) * 10000 * 54) / (958 * 34)));
+}
+
+static ssize_t in5_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct vt8231_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->in_min[5] = clamp_val(((val * 958 * 34) / (10000 * 54)) + 3,
+ 0, 255);
+ vt8231_write_value(data, regvoltmin[5], data->in_min[5]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t in5_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct vt8231_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->in_max[5] = clamp_val(((val * 958 * 34) / (10000 * 54)) + 3,
+ 0, 255);
+ vt8231_write_value(data, regvoltmax[5], data->in_max[5]);
+ 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 DEVICE_ATTR_RO(in5_input);
+static DEVICE_ATTR_RW(in5_min);
+static DEVICE_ATTR_RW(in5_max);
+
+/* Temperatures */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp[0] * 250);
+}
+
+static ssize_t temp1_max_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_max[0] * 1000);
+}
+
+static ssize_t temp1_max_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_min[0] * 1000);
+}
+
+static ssize_t temp1_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct vt8231_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->temp_max[0] = clamp_val((val + 500) / 1000, 0, 255);
+ vt8231_write_value(data, regtempmax[0], data->temp_max[0]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t temp1_max_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt8231_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->temp_min[0] = clamp_val((val + 500) / 1000, 0, 255);
+ vt8231_write_value(data, regtempmin[0], data->temp_min[0]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
+}
+
+static ssize_t temp_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_MAXMIN_FROM_REG(data->temp_max[nr]));
+}
+
+static ssize_t temp_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_MAXMIN_FROM_REG(data->temp_min[nr]));
+}
+
+static ssize_t temp_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_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->temp_max[nr] = clamp_val(TEMP_MAXMIN_TO_REG(val), 0, 255);
+ vt8231_write_value(data, regtempmax[nr], data->temp_max[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+static ssize_t temp_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_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->temp_min[nr] = clamp_val(TEMP_MAXMIN_TO_REG(val), 0, 255);
+ vt8231_write_value(data, regtempmin[nr], data->temp_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Note that these map the Linux temperature sensor numbering (1-6) to the VIA
+ * temperature sensor numbering (0-5)
+ */
+
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RW(temp1_max);
+static DEVICE_ATTR_RW(temp1_max_hyst);
+
+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_min, 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_min, 2);
+static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
+static SENSOR_DEVICE_ATTR_RW(temp4_max, temp_max, 3);
+static SENSOR_DEVICE_ATTR_RW(temp4_max_hyst, temp_min, 3);
+static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4);
+static SENSOR_DEVICE_ATTR_RW(temp5_max, temp_max, 4);
+static SENSOR_DEVICE_ATTR_RW(temp5_max_hyst, temp_min, 4);
+static SENSOR_DEVICE_ATTR_RO(temp6_input, temp, 5);
+static SENSOR_DEVICE_ATTR_RW(temp6_max, temp_max, 5);
+static SENSOR_DEVICE_ATTR_RW(temp6_max_hyst, temp_min, 5);
+
+/* Fans */
+static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr])));
+}
+
+static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
+}
+
+static ssize_t fan_min_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct vt8231_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]));
+ vt8231_write_value(data, VT8231_REG_FAN_MIN(nr), data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t fan_div_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct vt8231_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ unsigned long val;
+ int nr = sensor_attr->index;
+ int old = vt8231_read_value(data, VT8231_REG_FANDIV);
+ long min = FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case 1:
+ data->fan_div[nr] = 0;
+ break;
+ case 2:
+ data->fan_div[nr] = 1;
+ break;
+ case 4:
+ data->fan_div[nr] = 2;
+ break;
+ case 8:
+ data->fan_div[nr] = 3;
+ break;
+ default:
+ dev_err(dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ /* Correct the fan minimum speed */
+ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ vt8231_write_value(data, VT8231_REG_FAN_MIN(nr), data->fan_min[nr]);
+
+ old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
+ vt8231_write_value(data, VT8231_REG_FANDIV, old);
+ 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_RW(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_RW(fan2_div, fan_div, 1);
+
+/* Alarms */
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+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 vt8231_data *data = vt8231_update_device(dev);
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(temp4_alarm, alarm, 1);
+static SENSOR_DEVICE_ATTR_RO(temp5_alarm, alarm, 3);
+static SENSOR_DEVICE_ATTR_RO(temp6_alarm, alarm, 8);
+static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 0);
+static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 1);
+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, 2);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
+
+static ssize_t name_show(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct vt8231_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", data->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static struct attribute *vt8231_attributes_temps[6][5] = {
+ {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp1_max.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+ &sensor_dev_attr_temp6_alarm.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group vt8231_group_temps[6] = {
+ { .attrs = vt8231_attributes_temps[0] },
+ { .attrs = vt8231_attributes_temps[1] },
+ { .attrs = vt8231_attributes_temps[2] },
+ { .attrs = vt8231_attributes_temps[3] },
+ { .attrs = vt8231_attributes_temps[4] },
+ { .attrs = vt8231_attributes_temps[5] },
+};
+
+static struct attribute *vt8231_attributes_volts[6][5] = {
+ {
+ &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,
+ NULL
+ }, {
+ &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,
+ NULL
+ }, {
+ &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,
+ NULL
+ }, {
+ &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,
+ NULL
+ }, {
+ &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,
+ NULL
+ }, {
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group vt8231_group_volts[6] = {
+ { .attrs = vt8231_attributes_volts[0] },
+ { .attrs = vt8231_attributes_volts[1] },
+ { .attrs = vt8231_attributes_volts[2] },
+ { .attrs = vt8231_attributes_volts[3] },
+ { .attrs = vt8231_attributes_volts[4] },
+ { .attrs = vt8231_attributes_volts[5] },
+};
+
+static struct attribute *vt8231_attributes[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_name.attr,
+ NULL
+};
+
+static const struct attribute_group vt8231_group = {
+ .attrs = vt8231_attributes,
+};
+
+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)
+{
+ struct resource *res;
+ struct vt8231_data *data;
+ int err = 0, i;
+
+ /* Reserve the ISA region */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start, VT8231_EXTENT,
+ 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;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct vt8231_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+ data->addr = res->start;
+ data->name = DRIVER_NAME;
+
+ mutex_init(&data->update_lock);
+ vt8231_init_device(data);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&pdev->dev.kobj, &vt8231_group);
+ if (err)
+ return err;
+
+ /* Must update device information to find out the config field */
+ data->uch_config = vt8231_read_value(data, VT8231_REG_UCH_CONFIG);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) {
+ if (ISTEMP(i, data->uch_config)) {
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &vt8231_group_temps[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) {
+ if (ISVOLT(i, data->uch_config)) {
+ err = sysfs_create_group(&pdev->dev.kobj,
+ &vt8231_group_volts[i]);
+ if (err)
+ goto exit_remove_files;
+ }
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+ return 0;
+
+exit_remove_files:
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
+ sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_volts[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
+ sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);
+
+ sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);
+ return err;
+}
+
+static int vt8231_remove(struct platform_device *pdev)
+{
+ struct vt8231_data *data = platform_get_drvdata(pdev);
+ int i;
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
+ sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_volts[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
+ sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);
+
+ sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);
+
+ return 0;
+}
+
+
+static struct platform_driver vt8231_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .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_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + VT8231_EXTENT - 1,
+ .name = DRIVER_NAME,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc(DRIVER_NAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int vt8231_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ u16 address, val;
+ if (force_addr) {
+ address = force_addr & 0xff00;
+ dev_warn(&dev->dev, "Forcing ISA address 0x%x\n",
+ address);
+
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_write_config_word(dev, VT8231_BASE_REG, address | 1))
+ return -ENODEV;
+ }
+
+ pci_read_config_word(dev, VT8231_BASE_REG, &val);
+ if (val == (u16)~0)
+ return -ENODEV;
+
+ address = val & ~(VT8231_EXTENT - 1);
+ if (address == 0) {
+ dev_err(&dev->dev, "base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+ return -ENODEV;
+ }
+
+ pci_read_config_word(dev, VT8231_ENABLE_REG, &val);
+ if (val == (u16)~0)
+ return -ENODEV;
+
+ if (!(val & 0x0001)) {
+ dev_warn(&dev->dev, "enabling sensors\n");
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_write_config_word(dev, VT8231_ENABLE_REG,
+ val | 0x0001))
+ return -ENODEV;
+ }
+
+ if (platform_driver_register(&vt8231_driver))
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ if (vt8231_device_add(address))
+ goto exit_unregister;
+
+ /*
+ * Always return failure here. This is to allow other drivers to bind
+ * to this pci device. We don't really want to have control over the
+ * pci device, we only wanted to read as few register values from it.
+ */
+
+ /*
+ * We do, however, mark ourselves as using the PCI device to stop it
+ * getting unloaded.
+ */
+ s_bridge = pci_dev_get(dev);
+ return -ENODEV;
+
+exit_unregister:
+ platform_driver_unregister(&vt8231_driver);
+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);
+}
+
+static void __exit sm_vt8231_exit(void)
+{
+ pci_unregister_driver(&vt8231_pci_driver);
+ if (s_bridge != NULL) {
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&vt8231_driver);
+ pci_dev_put(s_bridge);
+ s_bridge = NULL;
+ }
+}
+
+MODULE_AUTHOR("Roger Lucas <vt8231@hiddenengine.co.uk>");
+MODULE_DESCRIPTION("VT8231 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(sm_vt8231_init);
+module_exit(sm_vt8231_exit);
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
new file mode 100644
index 000000000..939d4c35e
--- /dev/null
+++ b/drivers/hwmon/w83627ehf.c
@@ -0,0 +1,2160 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w83627ehf - Driver for the hardware monitoring functionality of
+ * the Winbond W83627EHF Super-I/O chip
+ * Copyright (C) 2005-2012 Jean Delvare <jdelvare@suse.de>
+ * Copyright (C) 2006 Yuan Mu (Winbond),
+ * Rudolf Marek <r.marek@assembler.cz>
+ * David Hubbard <david.c.hubbard@gmail.com>
+ * Daniel J Blueman <daniel.blueman@gmail.com>
+ * Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00)
+ *
+ * Shamelessly ripped from the w83627hf driver
+ * Copyright (C) 2003 Mark Studebaker
+ *
+ * Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help
+ * in testing and debugging this driver.
+ *
+ * This driver also supports the W83627EHG, which is the lead-free
+ * version of the W83627EHF.
+ *
+ * Supports the following chips:
+ *
+ * Chip #vin #fan #pwm #temp chip IDs man ID
+ * w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
+ * 0x8860 0xa1
+ * w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
+ * w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
+ * w83627uhg 8 2 2 3 0xa230 0xc1 0x5ca3
+ * w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
+ * w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.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/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include "lm75.h"
+
+enum kinds {
+ w83627ehf, w83627dhg, w83627dhg_p, w83627uhg,
+ w83667hg, w83667hg_b,
+};
+
+/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
+static const char * const w83627ehf_device_names[] = {
+ "w83627ehf",
+ "w83627dhg",
+ "w83627dhg",
+ "w83627uhg",
+ "w83667hg",
+ "w83667hg",
+};
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+#define DRVNAME "w83627ehf"
+
+/*
+ * Super-I/O constants and functions
+ */
+
+#define W83627EHF_LD_HWM 0x0b
+#define W83667HG_LD_VID 0x0d
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_EN_VRM10 0x2C /* GPIO3, GPIO4 selection */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+#define SIO_REG_VID_CTRL 0xF0 /* VID control */
+#define SIO_REG_VID_DATA 0xF1 /* VID data */
+
+#define SIO_W83627EHF_ID 0x8850
+#define SIO_W83627EHG_ID 0x8860
+#define SIO_W83627DHG_ID 0xa020
+#define SIO_W83627DHG_P_ID 0xb070
+#define SIO_W83627UHG_ID 0xa230
+#define SIO_W83667HG_ID 0xa510
+#define SIO_W83667HG_B_ID 0xb350
+#define SIO_ID_MASK 0xFFF0
+
+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)
+{
+ 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 W83627EHF_REG_BANK 0x4E
+#define W83627EHF_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
+ */
+
+static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
+static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
+
+/* The W83627EHF registers for nr=7,8,9 are in bank 5 */
+#define W83627EHF_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+ (0x554 + (((nr) - 7) * 2)))
+#define W83627EHF_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+ (0x555 + (((nr) - 7) * 2)))
+#define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
+ (0x550 + (nr) - 7))
+
+static const u16 W83627EHF_REG_TEMP[] = { 0x27, 0x150, 0x250, 0x7e };
+static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x3a, 0x153, 0x253, 0 };
+static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x39, 0x155, 0x255, 0 };
+static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 };
+
+/* Fan clock dividers are spread over the following five registers */
+#define W83627EHF_REG_FANDIV1 0x47
+#define W83627EHF_REG_FANDIV2 0x4B
+#define W83627EHF_REG_VBAT 0x5D
+#define W83627EHF_REG_DIODE 0x59
+#define W83627EHF_REG_SMI_OVT 0x4C
+
+#define W83627EHF_REG_ALARM1 0x459
+#define W83627EHF_REG_ALARM2 0x45A
+#define W83627EHF_REG_ALARM3 0x45B
+
+#define W83627EHF_REG_CASEOPEN_DET 0x42 /* SMI STATUS #2 */
+#define W83627EHF_REG_CASEOPEN_CLR 0x46 /* SMI MASK #3 */
+
+/* SmartFan registers */
+#define W83627EHF_REG_FAN_STEPUP_TIME 0x0f
+#define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e
+
+/* DC or PWM output fan configuration */
+static const u8 W83627EHF_REG_PWM_ENABLE[] = {
+ 0x04, /* SYS FAN0 output mode and PWM mode */
+ 0x04, /* CPU FAN0 output mode and PWM mode */
+ 0x12, /* AUX FAN mode */
+ 0x62, /* CPU FAN1 mode */
+};
+
+static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
+static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };
+
+/* FAN Duty Cycle, be used to control */
+static const u16 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
+static const u16 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
+static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
+
+/* Advanced Fan control, some values are common for all fans */
+static const u16 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 };
+static const u16 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
+static const u16 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 };
+
+static const u16 W83627EHF_REG_FAN_MAX_OUTPUT_COMMON[]
+ = { 0xff, 0x67, 0xff, 0x69 };
+static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_COMMON[]
+ = { 0xff, 0x68, 0xff, 0x6a };
+
+static const u16 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b };
+static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[]
+ = { 0x68, 0x6a, 0x6c };
+
+static const u16 W83627EHF_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 };
+
+static const char *const w83667hg_b_temp_label[] = {
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN",
+ "AMDTSI",
+ "PECI Agent 1",
+ "PECI Agent 2",
+ "PECI Agent 3",
+ "PECI Agent 4"
+};
+
+#define NUM_REG_TEMP ARRAY_SIZE(W83627EHF_REG_TEMP)
+
+static int is_word_sized(u16 reg)
+{
+ return ((((reg & 0xff00) == 0x100
+ || (reg & 0xff00) == 0x200)
+ && ((reg & 0x00ff) == 0x50
+ || (reg & 0x00ff) == 0x53
+ || (reg & 0x00ff) == 0x55))
+ || (reg & 0xfff0) == 0x630
+ || reg == 0x640 || reg == 0x642
+ || ((reg & 0xfff0) == 0x650
+ && (reg & 0x000f) >= 0x06)
+ || reg == 0x73 || reg == 0x75 || reg == 0x77
+ );
+}
+
+/*
+ * Conversions
+ */
+
+/* 1 is PWM mode, output in ms */
+static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
+{
+ return mode ? 100 * reg : 400 * reg;
+}
+
+static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
+{
+ return clamp_val((mode ? (msec + 50) / 100 : (msec + 200) / 400),
+ 1, 255);
+}
+
+static unsigned int fan_from_reg8(u16 reg, unsigned int divreg)
+{
+ if (reg == 0 || reg == 255)
+ return 0;
+ return 1350000U / (reg << divreg);
+}
+
+static inline unsigned int
+div_from_reg(u8 reg)
+{
+ return 1 << reg;
+}
+
+/*
+ * Some of the voltage inputs have internal scaling, the tables below
+ * contain 8 (the ADC LSB in mV) * scaling factor * 100
+ */
+static const u16 scale_in_common[10] = {
+ 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800
+};
+static const u16 scale_in_w83627uhg[9] = {
+ 800, 800, 3328, 3424, 800, 800, 0, 3328, 3400
+};
+
+static inline long in_from_reg(u8 reg, u8 nr, const u16 *scale_in)
+{
+ return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
+}
+
+static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scale_in)
+{
+ return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255);
+}
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct w83627ehf_data {
+ int addr; /* IO base of hw monitor block */
+ const char *name;
+
+ struct mutex lock;
+
+ u16 reg_temp[NUM_REG_TEMP];
+ u16 reg_temp_over[NUM_REG_TEMP];
+ u16 reg_temp_hyst[NUM_REG_TEMP];
+ u16 reg_temp_config[NUM_REG_TEMP];
+ u8 temp_src[NUM_REG_TEMP];
+ const char * const *temp_label;
+
+ const u16 *REG_FAN_MAX_OUTPUT;
+ const u16 *REG_FAN_STEP_OUTPUT;
+ const u16 *scale_in;
+
+ 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[10]; /* Register value */
+ u8 in_max[10]; /* Register value */
+ u8 in_min[10]; /* Register value */
+ unsigned int rpm[5];
+ u16 fan_min[5];
+ u8 fan_div[5];
+ u8 has_fan; /* some fan inputs can be disabled */
+ u8 has_fan_min; /* some fans don't have min register */
+ u8 temp_type[3];
+ s8 temp_offset[3];
+ s16 temp[9];
+ s16 temp_max[9];
+ s16 temp_max_hyst[9];
+ u32 alarms;
+ u8 caseopen;
+
+ u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
+ u8 pwm_enable[4]; /* 1->manual
+ * 2->thermal cruise mode (also called SmartFan I)
+ * 3->fan speed cruise mode
+ * 4->variable thermal cruise (also called
+ * SmartFan III)
+ * 5->enhanced variable thermal cruise (also called
+ * SmartFan IV)
+ */
+ u8 pwm_enable_orig[4]; /* original value of pwm_enable */
+ u8 pwm_num; /* number of pwm */
+ u8 pwm[4];
+ u8 target_temp[4];
+ u8 tolerance[4];
+
+ u8 fan_start_output[4]; /* minimum fan speed when spinning up */
+ u8 fan_stop_output[4]; /* minimum fan speed when spinning down */
+ u8 fan_stop_time[4]; /* time at minimum before disabling fan */
+ u8 fan_max_output[4]; /* maximum fan speed */
+ u8 fan_step_output[4]; /* rate of change output value */
+
+ u8 vid;
+ u8 vrm;
+
+ u16 have_temp;
+ u16 have_temp_offset;
+ u8 in6_skip:1;
+ u8 temp3_val_only:1;
+ u8 have_vid:1;
+
+ /* Remember extra register values over suspend/resume */
+ u8 vbat;
+ u8 fandiv1;
+ u8 fandiv2;
+};
+
+struct w83627ehf_sio_data {
+ int sioreg;
+ enum kinds kind;
+};
+
+/*
+ * 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 w83627ehf_set_bank(struct w83627ehf_data *data, u16 reg)
+{
+ u8 bank = reg >> 8;
+ if (data->bank != bank) {
+ outb_p(W83627EHF_REG_BANK, data->addr + ADDR_REG_OFFSET);
+ outb_p(bank, data->addr + DATA_REG_OFFSET);
+ data->bank = bank;
+ }
+}
+
+static u16 w83627ehf_read_value(struct w83627ehf_data *data, u16 reg)
+{
+ int res, word_sized = is_word_sized(reg);
+
+ mutex_lock(&data->lock);
+
+ w83627ehf_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);
+ }
+
+ mutex_unlock(&data->lock);
+ return res;
+}
+
+static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg,
+ u16 value)
+{
+ int word_sized = is_word_sized(reg);
+
+ mutex_lock(&data->lock);
+
+ w83627ehf_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);
+
+ mutex_unlock(&data->lock);
+ return 0;
+}
+
+/* We left-align 8-bit temperature values to make the code simpler */
+static u16 w83627ehf_read_temp(struct w83627ehf_data *data, u16 reg)
+{
+ u16 res;
+
+ res = w83627ehf_read_value(data, reg);
+ if (!is_word_sized(reg))
+ res <<= 8;
+
+ return res;
+}
+
+static int w83627ehf_write_temp(struct w83627ehf_data *data, u16 reg,
+ u16 value)
+{
+ if (!is_word_sized(reg))
+ value >>= 8;
+ return w83627ehf_write_value(data, reg, value);
+}
+
+/* This function assumes that the caller holds data->update_lock */
+static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr)
+{
+ u8 reg;
+
+ switch (nr) {
+ case 0:
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV1) & 0xcf)
+ | ((data->fan_div[0] & 0x03) << 4);
+ /* fan5 input control bit is write only, compute the value */
+ reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
+ w83627ehf_write_value(data, W83627EHF_REG_FANDIV1, reg);
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0xdf)
+ | ((data->fan_div[0] & 0x04) << 3);
+ w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);
+ break;
+ case 1:
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV1) & 0x3f)
+ | ((data->fan_div[1] & 0x03) << 6);
+ /* fan5 input control bit is write only, compute the value */
+ reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
+ w83627ehf_write_value(data, W83627EHF_REG_FANDIV1, reg);
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0xbf)
+ | ((data->fan_div[1] & 0x04) << 4);
+ w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);
+ break;
+ case 2:
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV2) & 0x3f)
+ | ((data->fan_div[2] & 0x03) << 6);
+ w83627ehf_write_value(data, W83627EHF_REG_FANDIV2, reg);
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0x7f)
+ | ((data->fan_div[2] & 0x04) << 5);
+ w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);
+ break;
+ case 3:
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_DIODE) & 0xfc)
+ | (data->fan_div[3] & 0x03);
+ w83627ehf_write_value(data, W83627EHF_REG_DIODE, reg);
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_SMI_OVT) & 0x7f)
+ | ((data->fan_div[3] & 0x04) << 5);
+ w83627ehf_write_value(data, W83627EHF_REG_SMI_OVT, reg);
+ break;
+ case 4:
+ reg = (w83627ehf_read_value(data, W83627EHF_REG_DIODE) & 0x73)
+ | ((data->fan_div[4] & 0x03) << 2)
+ | ((data->fan_div[4] & 0x04) << 5);
+ w83627ehf_write_value(data, W83627EHF_REG_DIODE, reg);
+ break;
+ }
+}
+
+static void w83627ehf_update_fan_div(struct w83627ehf_data *data)
+{
+ int i;
+
+ i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = (i >> 6) & 0x03;
+ i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV2);
+ data->fan_div[2] = (i >> 6) & 0x03;
+ i = w83627ehf_read_value(data, W83627EHF_REG_VBAT);
+ data->fan_div[0] |= (i >> 3) & 0x04;
+ data->fan_div[1] |= (i >> 4) & 0x04;
+ data->fan_div[2] |= (i >> 5) & 0x04;
+ if (data->has_fan & ((1 << 3) | (1 << 4))) {
+ i = w83627ehf_read_value(data, W83627EHF_REG_DIODE);
+ data->fan_div[3] = i & 0x03;
+ data->fan_div[4] = ((i >> 2) & 0x03)
+ | ((i >> 5) & 0x04);
+ }
+ if (data->has_fan & (1 << 3)) {
+ i = w83627ehf_read_value(data, W83627EHF_REG_SMI_OVT);
+ data->fan_div[3] |= (i >> 5) & 0x04;
+ }
+}
+
+static void w83627ehf_update_pwm(struct w83627ehf_data *data)
+{
+ int i;
+ int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
+
+ for (i = 0; i < data->pwm_num; i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
+ /* pwmcfg, tolerance mapped for i=0, i=1 to same reg */
+ if (i != 1) {
+ pwmcfg = w83627ehf_read_value(data,
+ W83627EHF_REG_PWM_ENABLE[i]);
+ tolerance = w83627ehf_read_value(data,
+ W83627EHF_REG_TOLERANCE[i]);
+ }
+ data->pwm_mode[i] =
+ ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1;
+ data->pwm_enable[i] = ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
+ & 3) + 1;
+ data->pwm[i] = w83627ehf_read_value(data, W83627EHF_REG_PWM[i]);
+
+ data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) & 0x0f;
+ }
+}
+
+static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
+{
+ struct w83627ehf_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) {
+ /* Fan clock dividers */
+ w83627ehf_update_fan_div(data);
+
+ /* Measured voltages and limits */
+ for (i = 0; i < data->in_num; i++) {
+ if ((i == 6) && data->in6_skip)
+ continue;
+
+ data->in[i] = w83627ehf_read_value(data,
+ W83627EHF_REG_IN(i));
+ data->in_min[i] = w83627ehf_read_value(data,
+ W83627EHF_REG_IN_MIN(i));
+ data->in_max[i] = w83627ehf_read_value(data,
+ W83627EHF_REG_IN_MAX(i));
+ }
+
+ /* Measured fan speeds and limits */
+ for (i = 0; i < 5; i++) {
+ u16 reg;
+
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
+ reg = w83627ehf_read_value(data, W83627EHF_REG_FAN[i]);
+ data->rpm[i] = fan_from_reg8(reg, data->fan_div[i]);
+
+ if (data->has_fan_min & (1 << i))
+ data->fan_min[i] = w83627ehf_read_value(data,
+ W83627EHF_REG_FAN_MIN[i]);
+
+ /*
+ * If we failed to measure the fan speed and clock
+ * divider can be increased, let's try that for next
+ * time
+ */
+ if (reg >= 0xff && data->fan_div[i] < 0x07) {
+ dev_dbg(dev,
+ "Increasing fan%d clock divider from %u to %u\n",
+ i + 1, div_from_reg(data->fan_div[i]),
+ div_from_reg(data->fan_div[i] + 1));
+ data->fan_div[i]++;
+ w83627ehf_write_fan_div(data, i);
+ /* Preserve min limit if possible */
+ if ((data->has_fan_min & (1 << i))
+ && data->fan_min[i] >= 2
+ && data->fan_min[i] != 255)
+ w83627ehf_write_value(data,
+ W83627EHF_REG_FAN_MIN[i],
+ (data->fan_min[i] /= 2));
+ }
+ }
+
+ w83627ehf_update_pwm(data);
+
+ for (i = 0; i < data->pwm_num; i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
+ data->fan_start_output[i] =
+ w83627ehf_read_value(data,
+ W83627EHF_REG_FAN_START_OUTPUT[i]);
+ data->fan_stop_output[i] =
+ w83627ehf_read_value(data,
+ W83627EHF_REG_FAN_STOP_OUTPUT[i]);
+ data->fan_stop_time[i] =
+ w83627ehf_read_value(data,
+ W83627EHF_REG_FAN_STOP_TIME[i]);
+
+ if (data->REG_FAN_MAX_OUTPUT &&
+ data->REG_FAN_MAX_OUTPUT[i] != 0xff)
+ data->fan_max_output[i] =
+ w83627ehf_read_value(data,
+ data->REG_FAN_MAX_OUTPUT[i]);
+
+ if (data->REG_FAN_STEP_OUTPUT &&
+ data->REG_FAN_STEP_OUTPUT[i] != 0xff)
+ data->fan_step_output[i] =
+ w83627ehf_read_value(data,
+ data->REG_FAN_STEP_OUTPUT[i]);
+
+ data->target_temp[i] =
+ w83627ehf_read_value(data,
+ W83627EHF_REG_TARGET[i]) &
+ (data->pwm_mode[i] == 1 ? 0x7f : 0xff);
+ }
+
+ /* Measured temperatures and limits */
+ for (i = 0; i < NUM_REG_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+ data->temp[i] = w83627ehf_read_temp(data,
+ data->reg_temp[i]);
+ if (data->reg_temp_over[i])
+ data->temp_max[i]
+ = w83627ehf_read_temp(data,
+ data->reg_temp_over[i]);
+ if (data->reg_temp_hyst[i])
+ data->temp_max_hyst[i]
+ = w83627ehf_read_temp(data,
+ data->reg_temp_hyst[i]);
+ if (i > 2)
+ continue;
+ if (data->have_temp_offset & (1 << i))
+ data->temp_offset[i]
+ = w83627ehf_read_value(data,
+ W83627EHF_REG_TEMP_OFFSET[i]);
+ }
+
+ data->alarms = w83627ehf_read_value(data,
+ W83627EHF_REG_ALARM1) |
+ (w83627ehf_read_value(data,
+ W83627EHF_REG_ALARM2) << 8) |
+ (w83627ehf_read_value(data,
+ W83627EHF_REG_ALARM3) << 16);
+
+ data->caseopen = w83627ehf_read_value(data,
+ W83627EHF_REG_CASEOPEN_DET);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+#define store_in_reg(REG, reg) \
+static int \
+store_in_##reg(struct device *dev, struct w83627ehf_data *data, int channel, \
+ long val) \
+{ \
+ if (val < 0) \
+ return -EINVAL; \
+ mutex_lock(&data->update_lock); \
+ data->in_##reg[channel] = in_to_reg(val, channel, data->scale_in); \
+ w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(channel), \
+ data->in_##reg[channel]); \
+ mutex_unlock(&data->update_lock); \
+ return 0; \
+}
+
+store_in_reg(MIN, min)
+store_in_reg(MAX, max)
+
+static int
+store_fan_min(struct device *dev, struct w83627ehf_data *data, int channel,
+ long val)
+{
+ unsigned int reg;
+ u8 new_div;
+
+ if (val < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (!val) {
+ /* No min limit, alarm disabled */
+ data->fan_min[channel] = 255;
+ new_div = data->fan_div[channel]; /* No change */
+ dev_info(dev, "fan%u low limit and alarm disabled\n",
+ channel + 1);
+ } else if ((reg = 1350000U / val) >= 128 * 255) {
+ /*
+ * Speed below this value cannot possibly be represented,
+ * even with the highest divider (128)
+ */
+ data->fan_min[channel] = 254;
+ new_div = 7; /* 128 == (1 << 7) */
+ dev_warn(dev,
+ "fan%u low limit %lu below minimum %u, set to minimum\n",
+ channel + 1, val, fan_from_reg8(254, 7));
+ } else if (!reg) {
+ /*
+ * Speed above this value cannot possibly be represented,
+ * even with the lowest divider (1)
+ */
+ data->fan_min[channel] = 1;
+ new_div = 0; /* 1 == (1 << 0) */
+ dev_warn(dev,
+ "fan%u low limit %lu above maximum %u, set to maximum\n",
+ channel + 1, val, fan_from_reg8(1, 0));
+ } else {
+ /*
+ * Automatically pick the best divider, i.e. the one such
+ * that the min limit will correspond to a register value
+ * in the 96..192 range
+ */
+ new_div = 0;
+ while (reg > 192 && new_div < 7) {
+ reg >>= 1;
+ new_div++;
+ }
+ data->fan_min[channel] = reg;
+ }
+
+ /*
+ * Write both the fan clock divider (if it changed) and the new
+ * fan min (unconditionally)
+ */
+ if (new_div != data->fan_div[channel]) {
+ dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
+ channel + 1, div_from_reg(data->fan_div[channel]),
+ div_from_reg(new_div));
+ data->fan_div[channel] = new_div;
+ w83627ehf_write_fan_div(data, channel);
+ /* Give the chip time to sample a new speed value */
+ data->last_updated = jiffies;
+ }
+
+ w83627ehf_write_value(data, W83627EHF_REG_FAN_MIN[channel],
+ data->fan_min[channel]);
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+#define store_temp_reg(addr, reg) \
+static int \
+store_##reg(struct device *dev, struct w83627ehf_data *data, int channel, \
+ long val) \
+{ \
+ mutex_lock(&data->update_lock); \
+ data->reg[channel] = LM75_TEMP_TO_REG(val); \
+ w83627ehf_write_temp(data, data->addr[channel], data->reg[channel]); \
+ mutex_unlock(&data->update_lock); \
+ return 0; \
+}
+store_temp_reg(reg_temp_over, temp_max);
+store_temp_reg(reg_temp_hyst, temp_max_hyst);
+
+static int
+store_temp_offset(struct device *dev, struct w83627ehf_data *data, int channel,
+ long val)
+{
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->temp_offset[channel] = val;
+ w83627ehf_write_value(data, W83627EHF_REG_TEMP_OFFSET[channel], val);
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+static int
+store_pwm_mode(struct device *dev, struct w83627ehf_data *data, int channel,
+ long val)
+{
+ u16 reg;
+
+ if (val < 0 || val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[channel]);
+ data->pwm_mode[channel] = val;
+ reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[channel]);
+ if (!val)
+ reg |= 1 << W83627EHF_PWM_MODE_SHIFT[channel];
+ w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[channel], reg);
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+static int
+store_pwm(struct device *dev, struct w83627ehf_data *data, int channel,
+ long val)
+{
+ val = clamp_val(val, 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->pwm[channel] = val;
+ w83627ehf_write_value(data, W83627EHF_REG_PWM[channel], val);
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+static int
+store_pwm_enable(struct device *dev, struct w83627ehf_data *data, int channel,
+ long val)
+{
+ u16 reg;
+
+ if (!val || val < 0 ||
+ (val > 4 && val != data->pwm_enable_orig[channel]))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm_enable[channel] = val;
+ reg = w83627ehf_read_value(data,
+ W83627EHF_REG_PWM_ENABLE[channel]);
+ reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[channel]);
+ reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[channel];
+ w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[channel],
+ reg);
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+#define show_tol_temp(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev->parent); \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", data->reg[nr] * 1000); \
+}
+
+show_tol_temp(tolerance)
+show_tol_temp(target_temp)
+
+static ssize_t
+store_target_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w83627ehf_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 127);
+
+ mutex_lock(&data->update_lock);
+ data->target_temp[nr] = val;
+ w83627ehf_write_value(data, W83627EHF_REG_TARGET[nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w83627ehf_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u16 reg;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ /* Limit the temp to 0C - 15C */
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 15);
+
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]);
+ if (nr == 1)
+ reg = (reg & 0x0f) | (val << 4);
+ else
+ reg = (reg & 0xf0) | val;
+ w83627ehf_write_value(data, W83627EHF_REG_TOLERANCE[nr], reg);
+ data->tolerance[nr] = val;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(pwm1_target, 0644, show_target_temp,
+ store_target_temp, 0);
+static SENSOR_DEVICE_ATTR(pwm2_target, 0644, show_target_temp,
+ store_target_temp, 1);
+static SENSOR_DEVICE_ATTR(pwm3_target, 0644, show_target_temp,
+ store_target_temp, 2);
+static SENSOR_DEVICE_ATTR(pwm4_target, 0644, show_target_temp,
+ store_target_temp, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1_tolerance, 0644, show_tolerance,
+ store_tolerance, 0);
+static SENSOR_DEVICE_ATTR(pwm2_tolerance, 0644, show_tolerance,
+ store_tolerance, 1);
+static SENSOR_DEVICE_ATTR(pwm3_tolerance, 0644, show_tolerance,
+ store_tolerance, 2);
+static SENSOR_DEVICE_ATTR(pwm4_tolerance, 0644, show_tolerance,
+ store_tolerance, 3);
+
+/* Smart Fan registers */
+
+#define fan_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev->parent); \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", data->reg[nr]); \
+} \
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct w83627ehf_data *data = dev_get_drvdata(dev); \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ unsigned long val; \
+ int err; \
+ err = kstrtoul(buf, 10, &val); \
+ if (err < 0) \
+ return err; \
+ val = clamp_val(val, 1, 255); \
+ mutex_lock(&data->update_lock); \
+ data->reg[nr] = val; \
+ w83627ehf_write_value(data, REG[nr], val); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+fan_functions(fan_start_output, W83627EHF_REG_FAN_START_OUTPUT)
+fan_functions(fan_stop_output, W83627EHF_REG_FAN_STOP_OUTPUT)
+fan_functions(fan_max_output, data->REG_FAN_MAX_OUTPUT)
+fan_functions(fan_step_output, data->REG_FAN_STEP_OUTPUT)
+
+#define fan_time_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev->parent); \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", \
+ step_time_from_reg(data->reg[nr], \
+ data->pwm_mode[nr])); \
+} \
+\
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct w83627ehf_data *data = dev_get_drvdata(dev); \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ unsigned long val; \
+ int err; \
+ err = kstrtoul(buf, 10, &val); \
+ if (err < 0) \
+ return err; \
+ val = step_time_to_reg(val, data->pwm_mode[nr]); \
+ mutex_lock(&data->update_lock); \
+ data->reg[nr] = val; \
+ w83627ehf_write_value(data, REG[nr], val); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+} \
+
+fan_time_functions(fan_stop_time, W83627EHF_REG_FAN_STOP_TIME)
+
+static SENSOR_DEVICE_ATTR(pwm4_stop_time, 0644, show_fan_stop_time,
+ store_fan_stop_time, 3);
+static SENSOR_DEVICE_ATTR(pwm4_start_output, 0644, show_fan_start_output,
+ store_fan_start_output, 3);
+static SENSOR_DEVICE_ATTR(pwm4_stop_output, 0644, show_fan_stop_output,
+ store_fan_stop_output, 3);
+static SENSOR_DEVICE_ATTR(pwm4_max_output, 0644, show_fan_max_output,
+ store_fan_max_output, 3);
+static SENSOR_DEVICE_ATTR(pwm4_step_output, 0644, show_fan_step_output,
+ store_fan_step_output, 3);
+
+static SENSOR_DEVICE_ATTR(pwm3_stop_time, 0644, show_fan_stop_time,
+ store_fan_stop_time, 2);
+static SENSOR_DEVICE_ATTR(pwm3_start_output, 0644, show_fan_start_output,
+ store_fan_start_output, 2);
+static SENSOR_DEVICE_ATTR(pwm3_stop_output, 0644, show_fan_stop_output,
+ store_fan_stop_output, 2);
+
+static SENSOR_DEVICE_ATTR(pwm1_stop_time, 0644, show_fan_stop_time,
+ store_fan_stop_time, 0);
+static SENSOR_DEVICE_ATTR(pwm2_stop_time, 0644, show_fan_stop_time,
+ store_fan_stop_time, 1);
+static SENSOR_DEVICE_ATTR(pwm1_start_output, 0644, show_fan_start_output,
+ store_fan_start_output, 0);
+static SENSOR_DEVICE_ATTR(pwm2_start_output, 0644, show_fan_start_output,
+ store_fan_start_output, 1);
+static SENSOR_DEVICE_ATTR(pwm1_stop_output, 0644, show_fan_stop_output,
+ store_fan_stop_output, 0);
+static SENSOR_DEVICE_ATTR(pwm2_stop_output, 0644, show_fan_stop_output,
+ store_fan_stop_output, 1);
+
+
+/*
+ * pwm1 and pwm3 don't support max and step settings on all chips.
+ * Need to check support while generating/removing attribute files.
+ */
+static SENSOR_DEVICE_ATTR(pwm1_max_output, 0644, show_fan_max_output,
+ store_fan_max_output, 0);
+static SENSOR_DEVICE_ATTR(pwm1_step_output, 0644, show_fan_step_output,
+ store_fan_step_output, 0);
+static SENSOR_DEVICE_ATTR(pwm2_max_output, 0644, show_fan_max_output,
+ store_fan_max_output, 1);
+static SENSOR_DEVICE_ATTR(pwm2_step_output, 0644, show_fan_step_output,
+ store_fan_step_output, 1);
+static SENSOR_DEVICE_ATTR(pwm3_max_output, 0644, show_fan_max_output,
+ store_fan_max_output, 2);
+static SENSOR_DEVICE_ATTR(pwm3_step_output, 0644, show_fan_step_output,
+ store_fan_step_output, 2);
+
+static ssize_t
+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));
+}
+static DEVICE_ATTR_RO(cpu0_vid);
+
+
+/* Case open detection */
+static int
+clear_caseopen(struct device *dev, struct w83627ehf_data *data, int channel,
+ long val)
+{
+ const u16 mask = 0x80;
+ u16 reg;
+
+ if (val != 0 || channel != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ 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 = false; /* Force cache refresh */
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static umode_t w83627ehf_attrs_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct w83627ehf_data *data = dev_get_drvdata(dev);
+ struct device_attribute *devattr;
+ struct sensor_device_attribute *sda;
+
+ devattr = container_of(a, struct device_attribute, attr);
+
+ /* Not sensor */
+ if (devattr->show == cpu0_vid_show && data->have_vid)
+ return a->mode;
+
+ sda = (struct sensor_device_attribute *)devattr;
+
+ if (sda->index < 2 &&
+ (devattr->show == show_fan_stop_time ||
+ devattr->show == show_fan_start_output ||
+ devattr->show == show_fan_stop_output))
+ return a->mode;
+
+ if (sda->index < 3 &&
+ (devattr->show == show_fan_max_output ||
+ devattr->show == show_fan_step_output) &&
+ data->REG_FAN_STEP_OUTPUT &&
+ data->REG_FAN_STEP_OUTPUT[sda->index] != 0xff)
+ return a->mode;
+
+ /* if fan3 and fan4 are enabled create the files for them */
+ if (sda->index == 2 &&
+ (data->has_fan & (1 << 2)) && data->pwm_num >= 3 &&
+ (devattr->show == show_fan_stop_time ||
+ devattr->show == show_fan_start_output ||
+ devattr->show == show_fan_stop_output))
+ return a->mode;
+
+ if (sda->index == 3 &&
+ (data->has_fan & (1 << 3)) && data->pwm_num >= 4 &&
+ (devattr->show == show_fan_stop_time ||
+ devattr->show == show_fan_start_output ||
+ devattr->show == show_fan_stop_output ||
+ devattr->show == show_fan_max_output ||
+ devattr->show == show_fan_step_output))
+ return a->mode;
+
+ if ((devattr->show == show_target_temp ||
+ devattr->show == show_tolerance) &&
+ (data->has_fan & (1 << sda->index)) &&
+ sda->index < data->pwm_num)
+ return a->mode;
+
+ return 0;
+}
+
+/* These groups handle non-standard attributes used in this device */
+static struct attribute *w83627ehf_attrs[] = {
+
+ &sensor_dev_attr_pwm1_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm1_start_output.dev_attr.attr,
+ &sensor_dev_attr_pwm1_stop_output.dev_attr.attr,
+ &sensor_dev_attr_pwm1_max_output.dev_attr.attr,
+ &sensor_dev_attr_pwm1_step_output.dev_attr.attr,
+ &sensor_dev_attr_pwm1_target.dev_attr.attr,
+ &sensor_dev_attr_pwm1_tolerance.dev_attr.attr,
+
+ &sensor_dev_attr_pwm2_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm2_start_output.dev_attr.attr,
+ &sensor_dev_attr_pwm2_stop_output.dev_attr.attr,
+ &sensor_dev_attr_pwm2_max_output.dev_attr.attr,
+ &sensor_dev_attr_pwm2_step_output.dev_attr.attr,
+ &sensor_dev_attr_pwm2_target.dev_attr.attr,
+ &sensor_dev_attr_pwm2_tolerance.dev_attr.attr,
+
+ &sensor_dev_attr_pwm3_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm3_start_output.dev_attr.attr,
+ &sensor_dev_attr_pwm3_stop_output.dev_attr.attr,
+ &sensor_dev_attr_pwm3_max_output.dev_attr.attr,
+ &sensor_dev_attr_pwm3_step_output.dev_attr.attr,
+ &sensor_dev_attr_pwm3_target.dev_attr.attr,
+ &sensor_dev_attr_pwm3_tolerance.dev_attr.attr,
+
+ &sensor_dev_attr_pwm4_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm4_start_output.dev_attr.attr,
+ &sensor_dev_attr_pwm4_stop_output.dev_attr.attr,
+ &sensor_dev_attr_pwm4_max_output.dev_attr.attr,
+ &sensor_dev_attr_pwm4_step_output.dev_attr.attr,
+ &sensor_dev_attr_pwm4_target.dev_attr.attr,
+ &sensor_dev_attr_pwm4_tolerance.dev_attr.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group w83627ehf_group = {
+ .attrs = w83627ehf_attrs,
+ .is_visible = w83627ehf_attrs_visible,
+};
+
+static const struct attribute_group *w83627ehf_groups[] = {
+ &w83627ehf_group,
+ NULL
+};
+
+/*
+ * Driver and device management
+ */
+
+/* Get the monitoring functions started */
+static inline void w83627ehf_init_device(struct w83627ehf_data *data,
+ enum kinds kind)
+{
+ int i;
+ u8 tmp, diode;
+
+ /* Start monitoring is needed */
+ tmp = w83627ehf_read_value(data, W83627EHF_REG_CONFIG);
+ if (!(tmp & 0x01))
+ w83627ehf_write_value(data, W83627EHF_REG_CONFIG,
+ tmp | 0x01);
+
+ /* Enable temperature sensors if needed */
+ for (i = 0; i < NUM_REG_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+ if (!data->reg_temp_config[i])
+ continue;
+ tmp = w83627ehf_read_value(data,
+ data->reg_temp_config[i]);
+ if (tmp & 0x01)
+ w83627ehf_write_value(data,
+ data->reg_temp_config[i],
+ tmp & 0xfe);
+ }
+
+ /* Enable VBAT monitoring if needed */
+ tmp = w83627ehf_read_value(data, W83627EHF_REG_VBAT);
+ if (!(tmp & 0x01))
+ w83627ehf_write_value(data, W83627EHF_REG_VBAT, tmp | 0x01);
+
+ /* Get thermal sensor types */
+ switch (kind) {
+ case w83627ehf:
+ diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE);
+ break;
+ case w83627uhg:
+ diode = 0x00;
+ break;
+ default:
+ diode = 0x70;
+ }
+ for (i = 0; i < 3; i++) {
+ const char *label = NULL;
+
+ if (data->temp_label)
+ label = data->temp_label[data->temp_src[i]];
+
+ /* Digital source overrides analog type */
+ if (label && strncmp(label, "PECI", 4) == 0)
+ data->temp_type[i] = 6;
+ else if (label && strncmp(label, "AMD", 3) == 0)
+ data->temp_type[i] = 5;
+ else if ((tmp & (0x02 << i)))
+ data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 3;
+ else
+ data->temp_type[i] = 4; /* thermistor */
+ }
+}
+
+static void
+w83627ehf_set_temp_reg_ehf(struct w83627ehf_data *data, int n_temp)
+{
+ int i;
+
+ for (i = 0; i < n_temp; i++) {
+ data->reg_temp[i] = W83627EHF_REG_TEMP[i];
+ data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
+ data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
+ data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
+ }
+}
+
+static void
+w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data,
+ struct w83627ehf_data *data)
+{
+ int fan3pin, fan4pin, fan5pin, regval;
+
+ /* The W83627UHG is simple, only two fan inputs, no config */
+ if (sio_data->kind == w83627uhg) {
+ data->has_fan = 0x03; /* fan1 and fan2 */
+ data->has_fan_min = 0x03;
+ return;
+ }
+
+ /* fan4 and fan5 share some pins with the GPIO and serial flash */
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
+ fan3pin = 1;
+ fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
+ fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
+ } else {
+ fan3pin = 1;
+ fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06);
+ fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02);
+ }
+
+ data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
+ data->has_fan |= (fan3pin << 2);
+ data->has_fan_min |= (fan3pin << 2);
+
+ /*
+ * It looks like fan4 and fan5 pins can be alternatively used
+ * as fan on/off switches, but fan5 control is write only :/
+ * We assume that if the serial interface is disabled, designers
+ * connected fan5 as input unless they are emitting log 1, which
+ * is not the default.
+ */
+ regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
+ if ((regval & (1 << 2)) && fan4pin) {
+ data->has_fan |= (1 << 3);
+ data->has_fan_min |= (1 << 3);
+ }
+ if (!(regval & (1 << 1)) && fan5pin) {
+ data->has_fan |= (1 << 4);
+ data->has_fan_min |= (1 << 4);
+ }
+}
+
+static umode_t
+w83627ehf_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct w83627ehf_data *data = drvdata;
+
+ switch (type) {
+ case hwmon_temp:
+ /* channel 0.., name 1.. */
+ if (!(data->have_temp & (1 << channel)))
+ return 0;
+ if (attr == hwmon_temp_input)
+ return 0444;
+ if (attr == hwmon_temp_label) {
+ if (data->temp_label)
+ return 0444;
+ return 0;
+ }
+ if (channel == 2 && data->temp3_val_only)
+ return 0;
+ if (attr == hwmon_temp_max) {
+ if (data->reg_temp_over[channel])
+ return 0644;
+ else
+ return 0;
+ }
+ if (attr == hwmon_temp_max_hyst) {
+ if (data->reg_temp_hyst[channel])
+ return 0644;
+ else
+ return 0;
+ }
+ if (channel > 2)
+ return 0;
+ if (attr == hwmon_temp_alarm || attr == hwmon_temp_type)
+ return 0444;
+ if (attr == hwmon_temp_offset) {
+ if (data->have_temp_offset & (1 << channel))
+ return 0644;
+ else
+ return 0;
+ }
+ break;
+
+ case hwmon_fan:
+ /* channel 0.., name 1.. */
+ if (!(data->has_fan & (1 << channel)))
+ return 0;
+ if (attr == hwmon_fan_input || attr == hwmon_fan_alarm)
+ return 0444;
+ if (attr == hwmon_fan_div) {
+ return 0444;
+ }
+ if (attr == hwmon_fan_min) {
+ if (data->has_fan_min & (1 << channel))
+ return 0644;
+ else
+ return 0;
+ }
+ break;
+
+ case hwmon_in:
+ /* channel 0.., name 0.. */
+ if (channel >= data->in_num)
+ return 0;
+ if (channel == 6 && data->in6_skip)
+ return 0;
+ if (attr == hwmon_in_alarm || attr == hwmon_in_input)
+ return 0444;
+ if (attr == hwmon_in_min || attr == hwmon_in_max)
+ return 0644;
+ break;
+
+ case hwmon_pwm:
+ /* channel 0.., name 1.. */
+ if (!(data->has_fan & (1 << channel)) ||
+ channel >= data->pwm_num)
+ return 0;
+ if (attr == hwmon_pwm_mode || attr == hwmon_pwm_enable ||
+ attr == hwmon_pwm_input)
+ return 0644;
+ break;
+
+ case hwmon_intrusion:
+ return 0644;
+
+ default: /* Shouldn't happen */
+ return 0;
+ }
+
+ return 0; /* Shouldn't happen */
+}
+
+static int
+w83627ehf_do_read_temp(struct w83627ehf_data *data, u32 attr,
+ int channel, long *val)
+{
+ switch (attr) {
+ case hwmon_temp_input:
+ *val = LM75_TEMP_FROM_REG(data->temp[channel]);
+ return 0;
+ case hwmon_temp_max:
+ *val = LM75_TEMP_FROM_REG(data->temp_max[channel]);
+ return 0;
+ case hwmon_temp_max_hyst:
+ *val = LM75_TEMP_FROM_REG(data->temp_max_hyst[channel]);
+ return 0;
+ case hwmon_temp_offset:
+ *val = data->temp_offset[channel] * 1000;
+ return 0;
+ case hwmon_temp_type:
+ *val = (int)data->temp_type[channel];
+ return 0;
+ case hwmon_temp_alarm:
+ if (channel < 3) {
+ int bit[] = { 4, 5, 13 };
+ *val = (data->alarms >> bit[channel]) & 1;
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int
+w83627ehf_do_read_in(struct w83627ehf_data *data, u32 attr,
+ int channel, long *val)
+{
+ switch (attr) {
+ case hwmon_in_input:
+ *val = in_from_reg(data->in[channel], channel, data->scale_in);
+ return 0;
+ case hwmon_in_min:
+ *val = in_from_reg(data->in_min[channel], channel,
+ data->scale_in);
+ return 0;
+ case hwmon_in_max:
+ *val = in_from_reg(data->in_max[channel], channel,
+ data->scale_in);
+ return 0;
+ case hwmon_in_alarm:
+ if (channel < 10) {
+ int bit[] = { 0, 1, 2, 3, 8, 21, 20, 16, 17, 19 };
+ *val = (data->alarms >> bit[channel]) & 1;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return -EOPNOTSUPP;
+}
+
+static int
+w83627ehf_do_read_fan(struct w83627ehf_data *data, u32 attr,
+ int channel, long *val)
+{
+ switch (attr) {
+ case hwmon_fan_input:
+ *val = data->rpm[channel];
+ return 0;
+ case hwmon_fan_min:
+ *val = fan_from_reg8(data->fan_min[channel],
+ data->fan_div[channel]);
+ return 0;
+ case hwmon_fan_div:
+ *val = div_from_reg(data->fan_div[channel]);
+ return 0;
+ case hwmon_fan_alarm:
+ if (channel < 5) {
+ int bit[] = { 6, 7, 11, 10, 23 };
+ *val = (data->alarms >> bit[channel]) & 1;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return -EOPNOTSUPP;
+}
+
+static int
+w83627ehf_do_read_pwm(struct w83627ehf_data *data, u32 attr,
+ int channel, long *val)
+{
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = data->pwm[channel];
+ return 0;
+ case hwmon_pwm_enable:
+ *val = data->pwm_enable[channel];
+ return 0;
+ case hwmon_pwm_mode:
+ *val = data->pwm_enable[channel];
+ return 0;
+ default:
+ break;
+ }
+ return -EOPNOTSUPP;
+}
+
+static int
+w83627ehf_do_read_intrusion(struct w83627ehf_data *data, u32 attr,
+ int channel, long *val)
+{
+ if (attr != hwmon_intrusion_alarm || channel != 0)
+ return -EOPNOTSUPP; /* shouldn't happen */
+
+ *val = !!(data->caseopen & 0x10);
+ return 0;
+}
+
+static int
+w83627ehf_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct w83627ehf_data *data = w83627ehf_update_device(dev->parent);
+
+ switch (type) {
+ case hwmon_fan:
+ return w83627ehf_do_read_fan(data, attr, channel, val);
+
+ case hwmon_in:
+ return w83627ehf_do_read_in(data, attr, channel, val);
+
+ case hwmon_pwm:
+ return w83627ehf_do_read_pwm(data, attr, channel, val);
+
+ case hwmon_temp:
+ return w83627ehf_do_read_temp(data, attr, channel, val);
+
+ case hwmon_intrusion:
+ return w83627ehf_do_read_intrusion(data, attr, channel, val);
+
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int
+w83627ehf_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct w83627ehf_data *data = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ if (attr == hwmon_temp_label) {
+ *str = data->temp_label[data->temp_src[channel]];
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ /* Nothing else should be read as a string */
+ return -EOPNOTSUPP;
+}
+
+static int
+w83627ehf_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct w83627ehf_data *data = dev_get_drvdata(dev);
+
+ if (type == hwmon_in && attr == hwmon_in_min)
+ return store_in_min(dev, data, channel, val);
+ if (type == hwmon_in && attr == hwmon_in_max)
+ return store_in_max(dev, data, channel, val);
+
+ if (type == hwmon_fan && attr == hwmon_fan_min)
+ return store_fan_min(dev, data, channel, val);
+
+ if (type == hwmon_temp && attr == hwmon_temp_max)
+ return store_temp_max(dev, data, channel, val);
+ if (type == hwmon_temp && attr == hwmon_temp_max_hyst)
+ return store_temp_max_hyst(dev, data, channel, val);
+ if (type == hwmon_temp && attr == hwmon_temp_offset)
+ return store_temp_offset(dev, data, channel, val);
+
+ if (type == hwmon_pwm && attr == hwmon_pwm_mode)
+ return store_pwm_mode(dev, data, channel, val);
+ if (type == hwmon_pwm && attr == hwmon_pwm_enable)
+ return store_pwm_enable(dev, data, channel, val);
+ if (type == hwmon_pwm && attr == hwmon_pwm_input)
+ return store_pwm(dev, data, channel, val);
+
+ if (type == hwmon_intrusion && attr == hwmon_intrusion_alarm)
+ return clear_caseopen(dev, data, channel, val);
+
+ return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops w83627ehf_ops = {
+ .is_visible = w83627ehf_is_visible,
+ .read = w83627ehf_read,
+ .read_string = w83627ehf_read_string,
+ .write = w83627ehf_write,
+};
+
+static const struct hwmon_channel_info *w83627ehf_info[] = {
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN,
+ HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN,
+ HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN,
+ HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN,
+ HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN,
+ HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN),
+ HWMON_CHANNEL_INFO(pwm,
+ HWMON_PWM_ENABLE | 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_PWM_INPUT | HWMON_PWM_MODE),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE,
+ HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX |
+ HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE),
+ HWMON_CHANNEL_INFO(intrusion,
+ HWMON_INTRUSION_ALARM),
+ NULL
+};
+
+static const struct hwmon_chip_info w83627ehf_chip_info = {
+ .ops = &w83627ehf_ops,
+ .info = w83627ehf_info,
+};
+
+static int __init w83627ehf_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
+ struct w83627ehf_data *data;
+ struct resource *res;
+ u8 en_vrm10;
+ int i, err = 0;
+ struct device *hwmon_dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME))
+ return -EBUSY;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = res->start;
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
+ data->name = w83627ehf_device_names[sio_data->kind];
+ data->bank = 0xff; /* Force initial bank selection */
+ platform_set_drvdata(pdev, data);
+
+ /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
+ data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
+ /* 667HG has 3 pwms, and 627UHG has only 2 */
+ switch (sio_data->kind) {
+ default:
+ data->pwm_num = 4;
+ break;
+ case w83667hg:
+ case w83667hg_b:
+ data->pwm_num = 3;
+ break;
+ case w83627uhg:
+ data->pwm_num = 2;
+ break;
+ }
+
+ /* Default to 3 temperature inputs, code below will adjust as needed */
+ data->have_temp = 0x07;
+
+ /* Deal with temperature register setup first. */
+ if (sio_data->kind == w83667hg_b) {
+ u8 reg;
+
+ w83627ehf_set_temp_reg_ehf(data, 4);
+
+ /*
+ * Temperature sources are selected with bank 0, registers 0x49
+ * and 0x4a.
+ */
+ reg = w83627ehf_read_value(data, 0x4a);
+ data->temp_src[0] = reg >> 5;
+ reg = w83627ehf_read_value(data, 0x49);
+ data->temp_src[1] = reg & 0x07;
+ data->temp_src[2] = (reg >> 4) & 0x07;
+
+ /*
+ * W83667HG-B has another temperature register at 0x7e.
+ * The temperature source is selected with register 0x7d.
+ * Support it if the source differs from already reported
+ * sources.
+ */
+ reg = w83627ehf_read_value(data, 0x7d);
+ reg &= 0x07;
+ if (reg != data->temp_src[0] && reg != data->temp_src[1]
+ && reg != data->temp_src[2]) {
+ data->temp_src[3] = reg;
+ data->have_temp |= 1 << 3;
+ }
+
+ /*
+ * Chip supports either AUXTIN or VIN3. Try to find out which
+ * one.
+ */
+ reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]);
+ if (data->temp_src[2] == 2 && (reg & 0x01))
+ data->have_temp &= ~(1 << 2);
+
+ if ((data->temp_src[2] == 2 && (data->have_temp & (1 << 2)))
+ || (data->temp_src[3] == 2 && (data->have_temp & (1 << 3))))
+ data->in6_skip = 1;
+
+ data->temp_label = w83667hg_b_temp_label;
+ data->have_temp_offset = data->have_temp & 0x07;
+ for (i = 0; i < 3; i++) {
+ if (data->temp_src[i] > 2)
+ data->have_temp_offset &= ~(1 << i);
+ }
+ } else if (sio_data->kind == w83627uhg) {
+ u8 reg;
+
+ w83627ehf_set_temp_reg_ehf(data, 3);
+
+ /*
+ * Temperature sources for temp2 and temp3 are selected with
+ * bank 0, registers 0x49 and 0x4a.
+ */
+ data->temp_src[0] = 0; /* SYSTIN */
+ reg = w83627ehf_read_value(data, 0x49) & 0x07;
+ /* Adjust to have the same mapping as other source registers */
+ if (reg == 0)
+ data->temp_src[1] = 1;
+ else if (reg >= 2 && reg <= 5)
+ data->temp_src[1] = reg + 2;
+ else /* should never happen */
+ data->have_temp &= ~(1 << 1);
+ reg = w83627ehf_read_value(data, 0x4a);
+ data->temp_src[2] = reg >> 5;
+
+ /*
+ * Skip temp3 if source is invalid or the same as temp1
+ * or temp2.
+ */
+ if (data->temp_src[2] == 2 || data->temp_src[2] == 3 ||
+ data->temp_src[2] == data->temp_src[0] ||
+ ((data->have_temp & (1 << 1)) &&
+ data->temp_src[2] == data->temp_src[1]))
+ data->have_temp &= ~(1 << 2);
+ else
+ data->temp3_val_only = 1; /* No limit regs */
+
+ data->in6_skip = 1; /* No VIN3 */
+
+ data->temp_label = w83667hg_b_temp_label;
+ data->have_temp_offset = data->have_temp & 0x03;
+ for (i = 0; i < 3; i++) {
+ if (data->temp_src[i] > 1)
+ data->have_temp_offset &= ~(1 << i);
+ }
+ } else {
+ w83627ehf_set_temp_reg_ehf(data, 3);
+
+ /* Temperature sources are fixed */
+
+ if (sio_data->kind == w83667hg) {
+ u8 reg;
+
+ /*
+ * Chip supports either AUXTIN or VIN3. Try to find
+ * out which one.
+ */
+ reg = w83627ehf_read_value(data,
+ W83627EHF_REG_TEMP_CONFIG[2]);
+ if (reg & 0x01)
+ data->have_temp &= ~(1 << 2);
+ else
+ data->in6_skip = 1;
+ }
+ data->have_temp_offset = data->have_temp & 0x07;
+ }
+
+ if (sio_data->kind == w83667hg_b) {
+ data->REG_FAN_MAX_OUTPUT =
+ W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B;
+ data->REG_FAN_STEP_OUTPUT =
+ W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B;
+ } else {
+ data->REG_FAN_MAX_OUTPUT =
+ W83627EHF_REG_FAN_MAX_OUTPUT_COMMON;
+ data->REG_FAN_STEP_OUTPUT =
+ W83627EHF_REG_FAN_STEP_OUTPUT_COMMON;
+ }
+
+ /* Setup input voltage scaling factors */
+ if (sio_data->kind == w83627uhg)
+ data->scale_in = scale_in_w83627uhg;
+ else
+ data->scale_in = scale_in_common;
+
+ /* Initialize the chip */
+ w83627ehf_init_device(data, sio_data->kind);
+
+ data->vrm = vid_which_vrm();
+
+ err = superio_enter(sio_data->sioreg);
+ if (err)
+ return err;
+
+ /* Read VID value */
+ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
+ /*
+ * W83667HG has different pins for VID input and output, so
+ * we can get the VID input values directly at logical device D
+ * 0xe3.
+ */
+ superio_select(sio_data->sioreg, W83667HG_LD_VID);
+ data->vid = superio_inb(sio_data->sioreg, 0xe3);
+ data->have_vid = true;
+ } else if (sio_data->kind != w83627uhg) {
+ superio_select(sio_data->sioreg, W83627EHF_LD_HWM);
+ if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) {
+ /*
+ * Set VID input sensibility if needed. In theory the
+ * BIOS should have set it, but in practice it's not
+ * always the case. We only do it for the W83627EHF/EHG
+ * because the W83627DHG is more complex in this
+ * respect.
+ */
+ if (sio_data->kind == w83627ehf) {
+ en_vrm10 = superio_inb(sio_data->sioreg,
+ SIO_REG_EN_VRM10);
+ if ((en_vrm10 & 0x08) && data->vrm == 90) {
+ dev_warn(dev,
+ "Setting VID input voltage to TTL\n");
+ superio_outb(sio_data->sioreg,
+ SIO_REG_EN_VRM10,
+ en_vrm10 & ~0x08);
+ } else if (!(en_vrm10 & 0x08)
+ && data->vrm == 100) {
+ dev_warn(dev,
+ "Setting VID input voltage to VRM10\n");
+ superio_outb(sio_data->sioreg,
+ SIO_REG_EN_VRM10,
+ en_vrm10 | 0x08);
+ }
+ }
+
+ data->vid = superio_inb(sio_data->sioreg,
+ SIO_REG_VID_DATA);
+ if (sio_data->kind == w83627ehf) /* 6 VID pins only */
+ data->vid &= 0x3f;
+ data->have_vid = true;
+ } else {
+ dev_info(dev,
+ "VID pins in output mode, CPU VID not available\n");
+ }
+ }
+
+ w83627ehf_check_fan_inputs(sio_data, data);
+
+ superio_exit(sio_data->sioreg);
+
+ /* Read fan clock dividers immediately */
+ w83627ehf_update_fan_div(data);
+
+ /* Read pwm data to save original values */
+ w83627ehf_update_pwm(data);
+ for (i = 0; i < data->pwm_num; i++)
+ data->pwm_enable_orig[i] = data->pwm_enable[i];
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+ data->name,
+ data,
+ &w83627ehf_chip_info,
+ w83627ehf_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static int w83627ehf_suspend(struct device *dev)
+{
+ struct w83627ehf_data *data = w83627ehf_update_device(dev);
+
+ mutex_lock(&data->update_lock);
+ data->vbat = w83627ehf_read_value(data, W83627EHF_REG_VBAT);
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static int w83627ehf_resume(struct device *dev)
+{
+ struct w83627ehf_data *data = dev_get_drvdata(dev);
+ int i;
+
+ mutex_lock(&data->update_lock);
+ data->bank = 0xff; /* Force initial bank selection */
+
+ /* Restore limits */
+ for (i = 0; i < data->in_num; i++) {
+ if ((i == 6) && data->in6_skip)
+ continue;
+
+ w83627ehf_write_value(data, W83627EHF_REG_IN_MIN(i),
+ data->in_min[i]);
+ w83627ehf_write_value(data, W83627EHF_REG_IN_MAX(i),
+ data->in_max[i]);
+ }
+
+ for (i = 0; i < 5; i++) {
+ if (!(data->has_fan_min & (1 << i)))
+ continue;
+
+ w83627ehf_write_value(data, W83627EHF_REG_FAN_MIN[i],
+ data->fan_min[i]);
+ }
+
+ for (i = 0; i < NUM_REG_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+
+ if (data->reg_temp_over[i])
+ w83627ehf_write_temp(data, data->reg_temp_over[i],
+ data->temp_max[i]);
+ if (data->reg_temp_hyst[i])
+ w83627ehf_write_temp(data, data->reg_temp_hyst[i],
+ data->temp_max_hyst[i]);
+ if (i > 2)
+ continue;
+ if (data->have_temp_offset & (1 << i))
+ w83627ehf_write_value(data,
+ W83627EHF_REG_TEMP_OFFSET[i],
+ data->temp_offset[i]);
+ }
+
+ /* Restore other settings */
+ w83627ehf_write_value(data, W83627EHF_REG_VBAT, data->vbat);
+
+ /* Force re-reading all values */
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(w83627ehf_dev_pm_ops, w83627ehf_suspend, w83627ehf_resume);
+
+static struct platform_driver w83627ehf_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .pm = pm_sleep_ptr(&w83627ehf_dev_pm_ops),
+ },
+};
+
+/* w83627ehf_find() looks for a '627 in the Super-I/O config space */
+static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
+ struct w83627ehf_sio_data *sio_data)
+{
+ static const char sio_name_W83627EHF[] __initconst = "W83627EHF";
+ static const char sio_name_W83627EHG[] __initconst = "W83627EHG";
+ static const char sio_name_W83627DHG[] __initconst = "W83627DHG";
+ static const char sio_name_W83627DHG_P[] __initconst = "W83627DHG-P";
+ static const char sio_name_W83627UHG[] __initconst = "W83627UHG";
+ static const char sio_name_W83667HG[] __initconst = "W83667HG";
+ static const char sio_name_W83667HG_B[] __initconst = "W83667HG-B";
+
+ u16 val;
+ const char *sio_name;
+ int err;
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ if (force_id)
+ val = force_id;
+ else
+ val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
+ | superio_inb(sioaddr, SIO_REG_DEVID + 1);
+ switch (val & SIO_ID_MASK) {
+ case SIO_W83627EHF_ID:
+ sio_data->kind = w83627ehf;
+ sio_name = sio_name_W83627EHF;
+ break;
+ case SIO_W83627EHG_ID:
+ sio_data->kind = w83627ehf;
+ sio_name = sio_name_W83627EHG;
+ break;
+ case SIO_W83627DHG_ID:
+ sio_data->kind = w83627dhg;
+ sio_name = sio_name_W83627DHG;
+ break;
+ case SIO_W83627DHG_P_ID:
+ sio_data->kind = w83627dhg_p;
+ sio_name = sio_name_W83627DHG_P;
+ break;
+ case SIO_W83627UHG_ID:
+ sio_data->kind = w83627uhg;
+ sio_name = sio_name_W83627UHG;
+ break;
+ case SIO_W83667HG_ID:
+ sio_data->kind = w83667hg;
+ sio_name = sio_name_W83667HG;
+ break;
+ case SIO_W83667HG_B_ID:
+ sio_data->kind = w83667hg_b;
+ sio_name = sio_name_W83667HG_B;
+ 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, W83627EHF_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);
+ }
+
+ superio_exit(sioaddr);
+ pr_info("Found %s chip at %#x\n", sio_name, *addr);
+ sio_data->sioreg = sioaddr;
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+static struct platform_device *pdev;
+
+static int __init sensors_w83627ehf_init(void)
+{
+ int err;
+ unsigned short address;
+ struct resource res = {
+ .name = DRVNAME,
+ .flags = IORESOURCE_IO,
+ };
+ struct w83627ehf_sio_data sio_data;
+
+ /*
+ * 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
+ * w83627ehf hardware monitor, and call probe()
+ */
+ if (w83627ehf_find(0x2e, &address, &sio_data) &&
+ w83627ehf_find(0x4e, &address, &sio_data))
+ return -ENODEV;
+
+ res.start = address + IOREGION_OFFSET;
+ res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ return err;
+
+ pdev = platform_create_bundle(&w83627ehf_driver, w83627ehf_probe, &res, 1, &sio_data,
+ sizeof(struct w83627ehf_sio_data));
+
+ return PTR_ERR_OR_ZERO(pdev);
+}
+
+static void __exit sensors_w83627ehf_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&w83627ehf_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("W83627EHF driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_w83627ehf_init);
+module_exit(sensors_w83627ehf_exit);
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
new file mode 100644
index 000000000..b638d672a
--- /dev/null
+++ b/drivers/hwmon/w83627hf.c
@@ -0,0 +1,2009 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (c) 1998 - 2003 Frodo Looijaard <frodol@dds.nl>,
+ * Philip Edelbrock <phil@netroedge.com>,
+ * and Mark Studebaker <mdsxyz123@yahoo.com>
+ * Ported to 2.6 by Bernhard C. Schrenk <clemy@clemy.org>
+ * Copyright (c) 2007 - 1012 Jean Delvare <jdelvare@suse.de>
+ */
+
+/*
+ * Supports following chips:
+ *
+ * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ * w83627hf 9 3 2 3 0x20 0x5ca3 no yes(LPC)
+ * w83627thf 7 3 3 3 0x90 0x5ca3 no yes(LPC)
+ * w83637hf 7 3 3 3 0x80 0x5ca3 no yes(LPC)
+ * w83687thf 7 3 3 3 0x90 0x5ca3 no yes(LPC)
+ * w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC)
+ *
+ * For other winbond chips, and for i2c support in the above chips,
+ * use w83781d.c.
+ *
+ * Note: automatic ("cruise") fan control for 697, 637 & 627thf not
+ * supported yet.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.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/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/ioport.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include "lm75.h"
+
+static struct platform_device *pdev;
+
+#define DRVNAME "w83627hf"
+enum chips { w83627hf, w83627thf, w83697hf, w83637hf, w83687thf };
+
+struct w83627hf_sio_data {
+ enum chips type;
+ int sioaddr;
+};
+
+static u8 force_i2c = 0x1f;
+module_param(force_i2c, byte, 0);
+MODULE_PARM_DESC(force_i2c,
+ "Initialize the i2c address of the sensors");
+
+static bool init = 1;
+module_param(init, bool, 0);
+MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+/* modified from kernel/include/traps.c */
+#define DEV 0x07 /* Register: Logical device select */
+
+/* logical device numbers for superio_select (below) */
+#define W83627HF_LD_FDC 0x00
+#define W83627HF_LD_PRT 0x01
+#define W83627HF_LD_UART1 0x02
+#define W83627HF_LD_UART2 0x03
+#define W83627HF_LD_KBC 0x05
+#define W83627HF_LD_CIR 0x06 /* w83627hf only */
+#define W83627HF_LD_GAME 0x07
+#define W83627HF_LD_MIDI 0x07
+#define W83627HF_LD_GPIO1 0x07
+#define W83627HF_LD_GPIO5 0x07 /* w83627thf only */
+#define W83627HF_LD_GPIO2 0x08
+#define W83627HF_LD_GPIO3 0x09
+#define W83627HF_LD_GPIO4 0x09 /* w83627thf only */
+#define W83627HF_LD_ACPI 0x0a
+#define W83627HF_LD_HWM 0x0b
+
+#define DEVID 0x20 /* Register: Device ID */
+
+#define W83627THF_GPIO5_EN 0x30 /* w83627thf only */
+#define W83627THF_GPIO5_IOSR 0xf3 /* w83627thf only */
+#define W83627THF_GPIO5_DR 0xf4 /* w83627thf only */
+
+#define W83687THF_VID_EN 0x29 /* w83687thf only */
+#define W83687THF_VID_CFG 0xF0 /* w83687thf only */
+#define W83687THF_VID_DATA 0xF1 /* w83687thf only */
+
+static inline void
+superio_outb(struct w83627hf_sio_data *sio, int reg, int val)
+{
+ outb(reg, sio->sioaddr);
+ outb(val, sio->sioaddr + 1);
+}
+
+static inline int
+superio_inb(struct w83627hf_sio_data *sio, int reg)
+{
+ outb(reg, sio->sioaddr);
+ return inb(sio->sioaddr + 1);
+}
+
+static inline void
+superio_select(struct w83627hf_sio_data *sio, int ld)
+{
+ outb(DEV, sio->sioaddr);
+ outb(ld, sio->sioaddr + 1);
+}
+
+static inline int
+superio_enter(struct w83627hf_sio_data *sio)
+{
+ if (!request_muxed_region(sio->sioaddr, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x87, sio->sioaddr);
+ outb(0x87, sio->sioaddr);
+
+ return 0;
+}
+
+static inline void
+superio_exit(struct w83627hf_sio_data *sio)
+{
+ outb(0xAA, sio->sioaddr);
+ release_region(sio->sioaddr, 2);
+}
+
+#define W627_DEVID 0x52
+#define W627THF_DEVID 0x82
+#define W697_DEVID 0x60
+#define W637_DEVID 0x70
+#define W687THF_DEVID 0x85
+#define WINB_ACT_REG 0x30
+#define WINB_BASE_REG 0x60
+/* Constants specified below */
+
+/* Alignment of the base address */
+#define WINB_ALIGNMENT ~7
+
+/* Offset & size of I/O region we are interested in */
+#define WINB_REGION_OFFSET 5
+#define WINB_REGION_SIZE 2
+
+/* Where are the sensors address/data registers relative to the region offset */
+#define W83781D_ADDR_REG_OFFSET 0
+#define W83781D_DATA_REG_OFFSET 1
+
+/* The W83781D registers */
+/* The W83782D registers for nr=7,8 are in bank 5 */
+#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+ (0x554 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+ (0x555 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
+ (0x550 + (nr) - 7))
+
+/* nr:0-2 for fans:1-3 */
+#define W83627HF_REG_FAN_MIN(nr) (0x3b + (nr))
+#define W83627HF_REG_FAN(nr) (0x28 + (nr))
+
+#define W83627HF_REG_TEMP2_CONFIG 0x152
+#define W83627HF_REG_TEMP3_CONFIG 0x252
+/* these are zero-based, unlike config constants above */
+static const u16 w83627hf_reg_temp[] = { 0x27, 0x150, 0x250 };
+static const u16 w83627hf_reg_temp_hyst[] = { 0x3A, 0x153, 0x253 };
+static const u16 w83627hf_reg_temp_over[] = { 0x39, 0x155, 0x255 };
+
+#define W83781D_REG_BANK 0x4E
+
+#define W83781D_REG_CONFIG 0x40
+#define W83781D_REG_ALARM1 0x459
+#define W83781D_REG_ALARM2 0x45A
+#define W83781D_REG_ALARM3 0x45B
+
+#define W83781D_REG_BEEP_CONFIG 0x4D
+#define W83781D_REG_BEEP_INTS1 0x56
+#define W83781D_REG_BEEP_INTS2 0x57
+#define W83781D_REG_BEEP_INTS3 0x453
+
+#define W83781D_REG_VID_FANDIV 0x47
+
+#define W83781D_REG_CHIPID 0x49
+#define W83781D_REG_WCHIPID 0x58
+#define W83781D_REG_CHIPMAN 0x4F
+#define W83781D_REG_PIN 0x4B
+
+#define W83781D_REG_VBAT 0x5D
+
+#define W83627HF_REG_PWM1 0x5A
+#define W83627HF_REG_PWM2 0x5B
+
+static const u8 W83627THF_REG_PWM_ENABLE[] = {
+ 0x04, /* FAN 1 mode */
+ 0x04, /* FAN 2 mode */
+ 0x12, /* FAN AUX mode */
+};
+static const u8 W83627THF_PWM_ENABLE_SHIFT[] = { 2, 4, 1 };
+
+#define W83627THF_REG_PWM1 0x01 /* 697HF/637HF/687THF too */
+#define W83627THF_REG_PWM2 0x03 /* 697HF/637HF/687THF too */
+#define W83627THF_REG_PWM3 0x11 /* 637HF/687THF too */
+
+#define W83627THF_REG_VRM_OVT_CFG 0x18 /* 637HF/687THF too */
+
+static const u8 regpwm_627hf[] = { W83627HF_REG_PWM1, W83627HF_REG_PWM2 };
+static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2,
+ W83627THF_REG_PWM3 };
+#define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \
+ regpwm_627hf[nr] : regpwm[nr])
+
+#define W83627HF_REG_PWM_FREQ 0x5C /* Only for the 627HF */
+
+#define W83637HF_REG_PWM_FREQ1 0x00 /* 697HF/687THF too */
+#define W83637HF_REG_PWM_FREQ2 0x02 /* 697HF/687THF too */
+#define W83637HF_REG_PWM_FREQ3 0x10 /* 687THF too */
+
+static const u8 W83637HF_REG_PWM_FREQ[] = { W83637HF_REG_PWM_FREQ1,
+ W83637HF_REG_PWM_FREQ2,
+ W83637HF_REG_PWM_FREQ3 };
+
+#define W83627HF_BASE_PWM_FREQ 46870
+
+#define W83781D_REG_I2C_ADDR 0x48
+#define W83781D_REG_I2C_SUBADDR 0x4A
+
+/* Sensor selection */
+#define W83781D_REG_SCFG1 0x5D
+static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };
+#define W83781D_REG_SCFG2 0x59
+static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };
+#define W83781D_DEFAULT_BETA 3435
+
+/*
+ * Conversions. Limit checking is only done on the TO_REG
+ * variants. Note that you should be a bit careful with which arguments
+ * these macros are called: arguments may be evaluated more than once.
+ * Fixing this is just not worth it.
+ */
+#define IN_TO_REG(val) (clamp_val((((val) + 8) / 16), 0, 255))
+#define IN_FROM_REG(val) ((val) * 16)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+#define TEMP_MIN (-128000)
+#define TEMP_MAX ( 127000)
+
+/*
+ * TEMP: 0.001C/bit (-128C to +127C)
+ * REG: 1C/bit, two's complement
+ */
+static u8 TEMP_TO_REG(long temp)
+{
+ int ntemp = clamp_val(temp, TEMP_MIN, TEMP_MAX);
+ ntemp += (ntemp < 0 ? -500 : 500);
+ return (u8)(ntemp / 1000);
+}
+
+static int TEMP_FROM_REG(u8 reg)
+{
+ return (s8)reg * 1000;
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+
+#define PWM_TO_REG(val) (clamp_val((val), 0, 255))
+
+static inline unsigned long pwm_freq_from_reg_627hf(u8 reg)
+{
+ unsigned long freq;
+ freq = W83627HF_BASE_PWM_FREQ >> reg;
+ return freq;
+}
+static inline u8 pwm_freq_to_reg_627hf(unsigned long val)
+{
+ u8 i;
+ /*
+ * Only 5 dividers (1 2 4 8 16)
+ * Search for the nearest available frequency
+ */
+ for (i = 0; i < 4; i++) {
+ if (val > (((W83627HF_BASE_PWM_FREQ >> i) +
+ (W83627HF_BASE_PWM_FREQ >> (i+1))) / 2))
+ break;
+ }
+ return i;
+}
+
+static inline unsigned long pwm_freq_from_reg(u8 reg)
+{
+ /* Clock bit 8 -> 180 kHz or 24 MHz */
+ unsigned long clock = (reg & 0x80) ? 180000UL : 24000000UL;
+
+ reg &= 0x7f;
+ /* This should not happen but anyway... */
+ if (reg == 0)
+ reg++;
+ return clock / (reg << 8);
+}
+static inline u8 pwm_freq_to_reg(unsigned long val)
+{
+ /* Minimum divider value is 0x01 and maximum is 0x7F */
+ if (val >= 93750) /* The highest we can do */
+ return 0x01;
+ if (val >= 720) /* Use 24 MHz clock */
+ return 24000000UL / (val << 8);
+ if (val < 6) /* The lowest we can do */
+ return 0xFF;
+ else /* Use 180 kHz clock */
+ return 0x80 | (180000UL / (val << 8));
+}
+
+#define BEEP_MASK_FROM_REG(val) ((val) & 0xff7fff)
+#define BEEP_MASK_TO_REG(val) ((val) & 0xff7fff)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static inline u8 DIV_TO_REG(long val)
+{
+ int i;
+ val = clamp_val(val, 1, 128) >> 1;
+ for (i = 0; i < 7; i++) {
+ if (val == 0)
+ break;
+ val >>= 1;
+ }
+ return (u8)i;
+}
+
+/*
+ * For each registered chip, we need to keep some data in memory.
+ * The structure is dynamically allocated.
+ */
+struct w83627hf_data {
+ unsigned short addr;
+ const char *name;
+ struct device *hwmon_dev;
+ struct mutex lock;
+ enum chips type;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 in[9]; /* Register value */
+ u8 in_max[9]; /* Register value */
+ u8 in_min[9]; /* Register value */
+ u8 fan[3]; /* Register value */
+ u8 fan_min[3]; /* Register value */
+ u16 temp[3]; /* Register value */
+ u16 temp_max[3]; /* Register value */
+ u16 temp_max_hyst[3]; /* Register value */
+ u8 fan_div[3]; /* Register encoding, shifted right */
+ u8 vid; /* Register encoding, combined */
+ u32 alarms; /* Register encoding, combined */
+ u32 beep_mask; /* Register encoding, combined */
+ u8 pwm[3]; /* Register value */
+ u8 pwm_enable[3]; /* 1 = manual
+ * 2 = thermal cruise (also called SmartFan I)
+ * 3 = fan speed cruise
+ */
+ u8 pwm_freq[3]; /* Register value */
+ u16 sens[3]; /* 1 = pentium diode; 2 = 3904 diode;
+ * 4 = thermistor
+ */
+ u8 vrm;
+ u8 vrm_ovt; /* Register value, 627THF/637HF/687THF only */
+
+#ifdef CONFIG_PM
+ /* Remember extra register values over suspend/resume */
+ u8 scfg1;
+ u8 scfg2;
+#endif
+};
+
+/* 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 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)
+{
+ struct w83627hf_data *data = w83627hf_update_device(dev);
+
+ mutex_lock(&data->update_lock);
+ data->scfg1 = w83627hf_read_value(data, W83781D_REG_SCFG1);
+ data->scfg2 = w83627hf_read_value(data, W83781D_REG_SCFG2);
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static int w83627hf_resume(struct device *dev)
+{
+ struct w83627hf_data *data = dev_get_drvdata(dev);
+ int i, num_temps = (data->type == w83697hf) ? 2 : 3;
+
+ /* Restore limits */
+ mutex_lock(&data->update_lock);
+ 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;
+ w83627hf_write_value(data, W83781D_REG_IN_MAX(i),
+ data->in_max[i]);
+ w83627hf_write_value(data, W83781D_REG_IN_MIN(i),
+ data->in_min[i]);
+ }
+ for (i = 0; i <= 2; i++)
+ w83627hf_write_value(data, W83627HF_REG_FAN_MIN(i),
+ data->fan_min[i]);
+ for (i = 0; i < num_temps; i++) {
+ w83627hf_write_value(data, w83627hf_reg_temp_over[i],
+ data->temp_max[i]);
+ w83627hf_write_value(data, w83627hf_reg_temp_hyst[i],
+ data->temp_max_hyst[i]);
+ }
+
+ /* Fixup BIOS bugs */
+ if (data->type == w83627thf || data->type == w83637hf ||
+ data->type == w83687thf)
+ w83627hf_write_value(data, W83627THF_REG_VRM_OVT_CFG,
+ data->vrm_ovt);
+ w83627hf_write_value(data, W83781D_REG_SCFG1, data->scfg1);
+ w83627hf_write_value(data, W83781D_REG_SCFG2, data->scfg2);
+
+ /* Force re-reading all values */
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops w83627hf_dev_pm_ops = {
+ .suspend = w83627hf_suspend,
+ .resume = w83627hf_resume,
+};
+
+#define W83627HF_DEV_PM_OPS (&w83627hf_dev_pm_ops)
+#else
+#define W83627HF_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static int w83627thf_read_gpio5(struct platform_device *pdev)
+{
+ 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 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");
+ 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 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);
+}
+
+/* use a different set of functions for in0 */
+static ssize_t show_in_0(struct w83627hf_data *data, char *buf, u8 reg)
+{
+ long in0;
+
+ if ((data->vrm_ovt & 0x01) &&
+ (w83627thf == data->type || w83637hf == data->type
+ || w83687thf == data->type))
+
+ /* use VRM9 calculation */
+ in0 = (long)((reg * 488 + 70000 + 50) / 100);
+ else
+ /* use VRM8 (standard) calculation */
+ in0 = (long)IN_FROM_REG(reg);
+
+ return sprintf(buf,"%ld\n", in0);
+}
+
+static ssize_t in0_input_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[0]);
+}
+static DEVICE_ATTR_RO(in0_input);
+
+static ssize_t in0_min_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_min[0]);
+}
+
+static ssize_t in0_min_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);
+
+ if ((data->vrm_ovt & 0x01) &&
+ (w83627thf == data->type || w83637hf == data->type
+ || w83687thf == data->type))
+
+ /* use VRM9 calculation */
+ data->in_min[0] =
+ clamp_val(((val * 100) - 70000 + 244) / 488, 0, 255);
+ else
+ /* use VRM8 (standard) calculation */
+ data->in_min[0] = IN_TO_REG(val);
+
+ w83627hf_write_value(data, W83781D_REG_IN_MIN(0), data->in_min[0]);
+ mutex_unlock(&data->update_lock);
+ 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)
+{
+ 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->vrm_ovt & 0x01) &&
+ (w83627thf == data->type || w83637hf == data->type
+ || w83687thf == data->type))
+
+ /* use VRM9 calculation */
+ data->in_max[0] =
+ clamp_val(((val * 100) - 70000 + 244) / 488, 0, 255);
+ else
+ /* use VRM8 (standard) calculation */
+ data->in_max[0] = IN_TO_REG(val);
+
+ w83627hf_write_value(data, W83781D_REG_IN_MAX(0), data->in_max[0]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static DEVICE_ATTR_RW(in0_max);
+
+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);
+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(in6_alarm, alarm, 10);
+static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 16);
+static SENSOR_DEVICE_ATTR_RO(in8_alarm, alarm, 17);
+static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
+static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
+static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, 11);
+static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
+static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5);
+static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 13);
+
+static ssize_t
+beep_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->beep_mask >> bitnr) & 1);
+}
+
+static ssize_t
+beep_store(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct w83627hf_data *data = dev_get_drvdata(dev);
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ u8 reg;
+ unsigned long bit;
+ int err;
+
+ err = kstrtoul(buf, 10, &bit);
+ if (err)
+ return err;
+
+ if (bit & ~1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (bit)
+ data->beep_mask |= (1 << bitnr);
+ else
+ data->beep_mask &= ~(1 << bitnr);
+
+ if (bitnr < 8) {
+ reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS1);
+ if (bit)
+ reg |= (1 << bitnr);
+ else
+ reg &= ~(1 << bitnr);
+ w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, reg);
+ } else if (bitnr < 16) {
+ reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2);
+ if (bit)
+ reg |= (1 << (bitnr - 8));
+ else
+ reg &= ~(1 << (bitnr - 8));
+ w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, reg);
+ } else {
+ reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS3);
+ if (bit)
+ reg |= (1 << (bitnr - 16));
+ else
+ reg &= ~(1 << (bitnr - 16));
+ w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, reg);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(in0_beep, beep, 0);
+static SENSOR_DEVICE_ATTR_RW(in1_beep, beep, 1);
+static SENSOR_DEVICE_ATTR_RW(in2_beep, beep, 2);
+static SENSOR_DEVICE_ATTR_RW(in3_beep, beep, 3);
+static SENSOR_DEVICE_ATTR_RW(in4_beep, beep, 8);
+static SENSOR_DEVICE_ATTR_RW(in5_beep, beep, 9);
+static SENSOR_DEVICE_ATTR_RW(in6_beep, beep, 10);
+static SENSOR_DEVICE_ATTR_RW(in7_beep, beep, 16);
+static SENSOR_DEVICE_ATTR_RW(in8_beep, beep, 17);
+static SENSOR_DEVICE_ATTR_RW(fan1_beep, beep, 6);
+static SENSOR_DEVICE_ATTR_RW(fan2_beep, beep, 7);
+static SENSOR_DEVICE_ATTR_RW(fan3_beep, beep, 11);
+static SENSOR_DEVICE_ATTR_RW(temp1_beep, beep, 4);
+static SENSOR_DEVICE_ATTR_RW(temp2_beep, beep, 5);
+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;
+ struct w83627hf_data *data = w83627hf_update_device(dev);
+ 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t
+fan_div_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 min;
+ u8 reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ /* Save fan_min */
+ min = FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+
+ data->fan_div[nr] = DIV_TO_REG(val);
+
+ reg = (w83627hf_read_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV)
+ & (nr==0 ? 0xcf : 0x3f))
+ | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6));
+ w83627hf_write_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg);
+
+ reg = (w83627hf_read_value(data, W83781D_REG_VBAT)
+ & ~(1 << (5 + nr)))
+ | ((data->fan_div[nr] & 0x04) << (3 + nr));
+ w83627hf_write_value(data, W83781D_REG_VBAT, reg);
+
+ /* Restore fan_min */
+ data->fan_min[nr] = FAN_TO_REG(min, 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_RW(fan1_div, fan_div, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);
+static SENSOR_DEVICE_ATTR_RW(fan3_div, fan_div, 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_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_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_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
+temp_type_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->sens[nr]);
+}
+
+static ssize_t
+temp_type_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;
+ u32 tmp;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ switch (val) {
+ case 1: /* PII/Celeron diode */
+ tmp = w83627hf_read_value(data, W83781D_REG_SCFG1);
+ w83627hf_write_value(data, W83781D_REG_SCFG1,
+ tmp | BIT_SCFG1[nr]);
+ tmp = w83627hf_read_value(data, W83781D_REG_SCFG2);
+ w83627hf_write_value(data, W83781D_REG_SCFG2,
+ tmp | BIT_SCFG2[nr]);
+ data->sens[nr] = val;
+ break;
+ case 2: /* 3904 */
+ tmp = w83627hf_read_value(data, W83781D_REG_SCFG1);
+ w83627hf_write_value(data, W83781D_REG_SCFG1,
+ tmp | BIT_SCFG1[nr]);
+ tmp = w83627hf_read_value(data, W83781D_REG_SCFG2);
+ w83627hf_write_value(data, W83781D_REG_SCFG2,
+ tmp & ~BIT_SCFG2[nr]);
+ data->sens[nr] = val;
+ break;
+ case W83781D_DEFAULT_BETA:
+ dev_warn(dev, "Sensor type %d is deprecated, please use 4 "
+ "instead\n", W83781D_DEFAULT_BETA);
+ fallthrough;
+ case 4: /* thermistor */
+ tmp = w83627hf_read_value(data, W83781D_REG_SCFG1);
+ w83627hf_write_value(data, W83781D_REG_SCFG1,
+ tmp & ~BIT_SCFG1[nr]);
+ data->sens[nr] = val;
+ break;
+ default:
+ dev_err(dev,
+ "Invalid sensor type %ld; must be 1, 2, or 4\n",
+ (long) val);
+ break;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(temp1_type, temp_type, 0);
+static SENSOR_DEVICE_ATTR_RW(temp2_type, temp_type, 1);
+static SENSOR_DEVICE_ATTR_RW(temp3_type, temp_type, 2);
+
+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);
+
+#define VIN_UNIT_ATTRS(_X_) \
+ &sensor_dev_attr_in##_X_##_input.dev_attr.attr, \
+ &sensor_dev_attr_in##_X_##_min.dev_attr.attr, \
+ &sensor_dev_attr_in##_X_##_max.dev_attr.attr, \
+ &sensor_dev_attr_in##_X_##_alarm.dev_attr.attr, \
+ &sensor_dev_attr_in##_X_##_beep.dev_attr.attr
+
+#define FAN_UNIT_ATTRS(_X_) \
+ &sensor_dev_attr_fan##_X_##_input.dev_attr.attr, \
+ &sensor_dev_attr_fan##_X_##_min.dev_attr.attr, \
+ &sensor_dev_attr_fan##_X_##_div.dev_attr.attr, \
+ &sensor_dev_attr_fan##_X_##_alarm.dev_attr.attr, \
+ &sensor_dev_attr_fan##_X_##_beep.dev_attr.attr
+
+#define TEMP_UNIT_ATTRS(_X_) \
+ &sensor_dev_attr_temp##_X_##_input.dev_attr.attr, \
+ &sensor_dev_attr_temp##_X_##_max.dev_attr.attr, \
+ &sensor_dev_attr_temp##_X_##_max_hyst.dev_attr.attr, \
+ &sensor_dev_attr_temp##_X_##_type.dev_attr.attr, \
+ &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,
+ &dev_attr_in0_max.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_beep.dev_attr.attr,
+ VIN_UNIT_ATTRS(2),
+ VIN_UNIT_ATTRS(3),
+ VIN_UNIT_ATTRS(4),
+ VIN_UNIT_ATTRS(7),
+ VIN_UNIT_ATTRS(8),
+
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2),
+
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+
+ &dev_attr_alarms.attr,
+ &sensor_dev_attr_beep_enable.dev_attr.attr,
+ &dev_attr_beep_mask.attr,
+
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &dev_attr_name.attr,
+ NULL
+};
+
+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),
+ VIN_UNIT_ATTRS(6),
+
+ FAN_UNIT_ATTRS(3),
+ TEMP_UNIT_ATTRS(3),
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.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,
+
+ NULL
+};
+
+static const struct attribute_group w83627hf_group_opt = {
+ .attrs = w83627hf_attributes_opt,
+};
+
+static int w83627hf_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct w83627hf_sio_data *sio_data = dev_get_platdata(dev);
+ struct w83627hf_data *data;
+ struct resource *res;
+ int err, i;
+
+ static const char *names[] = {
+ "w83627hf",
+ "w83627thf",
+ "w83697hf",
+ "w83637hf",
+ "w83687thf",
+ };
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(dev, res->start, WINB_REGION_SIZE, DRVNAME)) {
+ dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
+ (unsigned long)res->start,
+ (unsigned long)(res->start + WINB_REGION_SIZE - 1));
+ return -EBUSY;
+ }
+
+ data = devm_kzalloc(dev, sizeof(struct w83627hf_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->addr = res->start;
+ data->type = sio_data->type;
+ data->name = names[sio_data->type];
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
+ platform_set_drvdata(pdev, data);
+
+ /* Initialize the chip */
+ w83627hf_init_device(pdev);
+
+ /* A few vars need to be filled upon startup */
+ for (i = 0; i <= 2; i++)
+ data->fan_min[i] = w83627hf_read_value(
+ data, W83627HF_REG_FAN_MIN(i));
+ w83627hf_update_fan_div(data);
+
+ /* Register common device attributes */
+ err = sysfs_create_group(&dev->kobj, &w83627hf_group);
+ if (err)
+ return err;
+
+ /* Register chip-specific device attributes */
+ if (data->type == w83627hf || data->type == w83697hf)
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_in5_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in5_min.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in5_max.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in5_alarm.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in5_beep.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in6_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in6_min.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in6_max.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in6_alarm.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in6_beep.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm1_freq.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm2_freq.dev_attr)))
+ goto error;
+
+ if (data->type != w83697hf)
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_in1_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in1_min.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in1_max.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in1_alarm.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_in1_beep.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_min.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_div.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_alarm.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_beep.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_temp3_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_temp3_max.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_temp3_alarm.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_temp3_beep.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_temp3_type.dev_attr)))
+ goto error;
+
+ if (data->type != w83697hf && data->vid != 0xff) {
+ /* Convert VID to voltage based on VRM */
+ data->vrm = vid_which_vrm();
+
+ if ((err = device_create_file(dev, &dev_attr_cpu0_vid))
+ || (err = device_create_file(dev, &dev_attr_vrm)))
+ goto error;
+ }
+
+ if (data->type == w83627thf || data->type == w83637hf
+ || data->type == w83687thf) {
+ err = device_create_file(dev, &sensor_dev_attr_pwm3.dev_attr);
+ if (err)
+ goto error;
+ }
+
+ if (data->type == w83637hf || data->type == w83687thf)
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_pwm1_freq.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm2_freq.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm3_freq.dev_attr)))
+ goto error;
+
+ if (data->type != w83627hf)
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_pwm1_enable.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm2_enable.dev_attr)))
+ goto error;
+
+ if (data->type == w83627thf || data->type == w83637hf
+ || data->type == w83687thf) {
+ err = device_create_file(dev,
+ &sensor_dev_attr_pwm3_enable.dev_attr);
+ if (err)
+ goto error;
+ }
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ sysfs_remove_group(&dev->kobj, &w83627hf_group);
+ sysfs_remove_group(&dev->kobj, &w83627hf_group_opt);
+ return err;
+}
+
+static int w83627hf_remove(struct platform_device *pdev)
+{
+ struct w83627hf_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group);
+ sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt);
+
+ return 0;
+}
+
+static struct platform_driver w83627hf_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .pm = W83627HF_DEV_PM_OPS,
+ },
+ .probe = w83627hf_probe,
+ .remove = w83627hf_remove,
+};
+
+static int __init w83627hf_find(int sioaddr, unsigned short *addr,
+ struct w83627hf_sio_data *sio_data)
+{
+ 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;
+}
+
+static int __init w83627hf_device_add(unsigned short address,
+ const struct w83627hf_sio_data *sio_data)
+{
+ struct resource res = {
+ .start = address + WINB_REGION_OFFSET,
+ .end = address + WINB_REGION_OFFSET + WINB_REGION_SIZE - 1,
+ .name = DRVNAME,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add_data(pdev, sio_data,
+ sizeof(struct w83627hf_sio_data));
+ if (err) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit:
+ return err;
+}
+
+static int __init sensors_w83627hf_init(void)
+{
+ int err;
+ unsigned short address;
+ struct w83627hf_sio_data sio_data;
+
+ if (w83627hf_find(0x2e, &address, &sio_data)
+ && w83627hf_find(0x4e, &address, &sio_data))
+ return -ENODEV;
+
+ err = platform_driver_register(&w83627hf_driver);
+ if (err)
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ err = w83627hf_device_add(address, &sio_data);
+ if (err)
+ goto exit_driver;
+
+ return 0;
+
+exit_driver:
+ platform_driver_unregister(&w83627hf_driver);
+exit:
+ return err;
+}
+
+static void __exit sensors_w83627hf_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&w83627hf_driver);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+ "Philip Edelbrock <phil@netroedge.com>, "
+ "and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("W83627HF driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_w83627hf_init);
+module_exit(sensors_w83627hf_exit);
diff --git a/drivers/hwmon/w83773g.c b/drivers/hwmon/w83773g.c
new file mode 100644
index 000000000..88d11dc5f
--- /dev/null
+++ b/drivers/hwmon/w83773g.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 IBM Corp.
+ *
+ * Driver for the Nuvoton W83773G SMBus temperature sensor IC.
+ * Supported models: W83773G
+ */
+
+#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>
+
+/* W83773 has 3 channels */
+#define W83773_CHANNELS 3
+
+/* The W83773 registers */
+#define W83773_CONVERSION_RATE_REG_READ 0x04
+#define W83773_CONVERSION_RATE_REG_WRITE 0x0A
+#define W83773_MANUFACTURER_ID_REG 0xFE
+#define W83773_LOCAL_TEMP 0x00
+
+static const u8 W83773_STATUS[2] = { 0x02, 0x17 };
+
+static const u8 W83773_TEMP_LSB[2] = { 0x10, 0x25 };
+static const u8 W83773_TEMP_MSB[2] = { 0x01, 0x24 };
+
+static const u8 W83773_OFFSET_LSB[2] = { 0x12, 0x16 };
+static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 };
+
+/* this is the number of sensors in the device */
+static const struct i2c_device_id w83773_id[] = {
+ { "w83773g" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, w83773_id);
+
+static const struct of_device_id __maybe_unused w83773_of_match[] = {
+ {
+ .compatible = "nuvoton,w83773g"
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, w83773_of_match);
+
+static inline long temp_of_local(s8 reg)
+{
+ return reg * 1000;
+}
+
+static inline long temp_of_remote(s8 hb, u8 lb)
+{
+ return (hb << 3 | lb >> 5) * 125;
+}
+
+static int get_local_temp(struct regmap *regmap, long *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(regmap, W83773_LOCAL_TEMP, &regval);
+ if (ret < 0)
+ return ret;
+
+ *val = temp_of_local(regval);
+ return 0;
+}
+
+static int get_remote_temp(struct regmap *regmap, int index, long *val)
+{
+ unsigned int regval_high;
+ unsigned int regval_low;
+ int ret;
+
+ ret = regmap_read(regmap, W83773_TEMP_MSB[index], &regval_high);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(regmap, W83773_TEMP_LSB[index], &regval_low);
+ if (ret < 0)
+ return ret;
+
+ *val = temp_of_remote(regval_high, regval_low);
+ return 0;
+}
+
+static int get_fault(struct regmap *regmap, int index, long *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(regmap, W83773_STATUS[index], &regval);
+ if (ret < 0)
+ return ret;
+
+ *val = (regval & 0x04) >> 2;
+ return 0;
+}
+
+static int get_offset(struct regmap *regmap, int index, long *val)
+{
+ unsigned int regval_high;
+ unsigned int regval_low;
+ int ret;
+
+ ret = regmap_read(regmap, W83773_OFFSET_MSB[index], &regval_high);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(regmap, W83773_OFFSET_LSB[index], &regval_low);
+ if (ret < 0)
+ return ret;
+
+ *val = temp_of_remote(regval_high, regval_low);
+ return 0;
+}
+
+static int set_offset(struct regmap *regmap, int index, long val)
+{
+ int ret;
+ u8 high_byte;
+ u8 low_byte;
+
+ val = clamp_val(val, -127825, 127825);
+ /* offset value equals to (high_byte << 3 | low_byte >> 5) * 125 */
+ val /= 125;
+ high_byte = val >> 3;
+ low_byte = (val & 0x07) << 5;
+
+ ret = regmap_write(regmap, W83773_OFFSET_MSB[index], high_byte);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(regmap, W83773_OFFSET_LSB[index], low_byte);
+}
+
+static int get_update_interval(struct regmap *regmap, long *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(regmap, W83773_CONVERSION_RATE_REG_READ, &regval);
+ if (ret < 0)
+ return ret;
+
+ *val = 16000 >> regval;
+ return 0;
+}
+
+static int set_update_interval(struct regmap *regmap, long val)
+{
+ int rate;
+
+ /*
+ * For valid rates, interval can be calculated as
+ * interval = (1 << (8 - rate)) * 62.5;
+ * Rounded rate is therefore
+ * rate = 8 - __fls(interval * 8 / (62.5 * 7));
+ * Use clamp_val() to avoid overflows, and to ensure valid input
+ * for __fls.
+ */
+ val = clamp_val(val, 62, 16000) * 10;
+ rate = 8 - __fls((val * 8 / (625 * 7)));
+ return regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, rate);
+}
+
+static int w83773_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ if (type == hwmon_chip) {
+ if (attr == hwmon_chip_update_interval)
+ return get_update_interval(regmap, val);
+ return -EOPNOTSUPP;
+ }
+
+ switch (attr) {
+ case hwmon_temp_input:
+ if (channel == 0)
+ return get_local_temp(regmap, val);
+ return get_remote_temp(regmap, channel - 1, val);
+ case hwmon_temp_fault:
+ return get_fault(regmap, channel - 1, val);
+ case hwmon_temp_offset:
+ return get_offset(regmap, channel - 1, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int w83773_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ if (type == hwmon_chip && attr == hwmon_chip_update_interval)
+ return set_update_interval(regmap, val);
+
+ if (type == hwmon_temp && attr == hwmon_temp_offset)
+ return set_offset(regmap, channel - 1, val);
+
+ return -EOPNOTSUPP;
+}
+
+static umode_t w83773_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:
+ return 0644;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_fault:
+ return 0444;
+ case hwmon_temp_offset:
+ return 0644;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *w83773_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET,
+ HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET),
+ NULL
+};
+
+static const struct hwmon_ops w83773_ops = {
+ .is_visible = w83773_is_visible,
+ .read = w83773_read,
+ .write = w83773_write,
+};
+
+static const struct hwmon_chip_info w83773_chip_info = {
+ .ops = &w83773_ops,
+ .info = w83773_info,
+};
+
+static const struct regmap_config w83773_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int w83773_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &w83773_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ /* Set the conversion rate to 2 Hz */
+ ret = regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, 0x05);
+ if (ret < 0) {
+ dev_err(&client->dev, "error writing config rate register\n");
+ return ret;
+ }
+
+ i2c_set_clientdata(client, regmap);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev,
+ client->name,
+ regmap,
+ &w83773_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct i2c_driver w83773_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "w83773g",
+ .of_match_table = of_match_ptr(w83773_of_match),
+ },
+ .probe_new = w83773_probe,
+ .id_table = w83773_id,
+};
+
+module_i2c_driver(w83773_driver);
+
+MODULE_AUTHOR("Lei YU <mine260309@gmail.com>");
+MODULE_DESCRIPTION("W83773G temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
new file mode 100644
index 000000000..dacabf25e
--- /dev/null
+++ b/drivers/hwmon/w83781d.c
@@ -0,0 +1,2095 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w83781d.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
+ * Philip Edelbrock <phil@netroedge.com>,
+ * and Mark Studebaker <mdsxyz123@yahoo.com>
+ * Copyright (c) 2007 - 2008 Jean Delvare <jdelvare@suse.de>
+ */
+
+/*
+ * Supports following chips:
+ *
+ * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ * as99127f 7 3 0 3 0x31 0x12c3 yes no
+ * as99127f rev.2 (type_name = as99127f) 0x31 0x5ca3 yes no
+ * w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes
+ * w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes
+ * w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+#ifdef CONFIG_ISA
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#endif
+
+#include "lm75.h"
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, I2C_CLIENT_END };
+
+enum chips { w83781d, w83782d, w83783s, as99127f };
+
+/* Insmod parameters */
+static unsigned short force_subclients[4];
+module_param_array(force_subclients, short, NULL, 0);
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+static bool reset;
+module_param(reset, bool, 0);
+MODULE_PARM_DESC(reset, "Set to one to reset chip on load");
+
+static bool init = 1;
+module_param(init, bool, 0);
+MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
+
+/* Constants specified below */
+
+/* Length of ISA address segment */
+#define W83781D_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define W83781D_ADDR_REG_OFFSET 5
+#define W83781D_DATA_REG_OFFSET 6
+
+/* The device registers */
+/* in nr from 0 to 8 */
+#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+ (0x554 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+ (0x555 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
+ (0x550 + (nr) - 7))
+
+/* fan nr from 0 to 2 */
+#define W83781D_REG_FAN_MIN(nr) (0x3b + (nr))
+#define W83781D_REG_FAN(nr) (0x28 + (nr))
+
+#define W83781D_REG_BANK 0x4E
+#define W83781D_REG_TEMP2_CONFIG 0x152
+#define W83781D_REG_TEMP3_CONFIG 0x252
+/* temp nr from 1 to 3 */
+#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \
+ ((nr == 2) ? (0x0150) : \
+ (0x27)))
+#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \
+ ((nr == 2) ? (0x153) : \
+ (0x3A)))
+#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \
+ ((nr == 2) ? (0x155) : \
+ (0x39)))
+
+#define W83781D_REG_CONFIG 0x40
+
+/* Interrupt status (W83781D, AS99127F) */
+#define W83781D_REG_ALARM1 0x41
+#define W83781D_REG_ALARM2 0x42
+
+/* Real-time status (W83782D, W83783S) */
+#define W83782D_REG_ALARM1 0x459
+#define W83782D_REG_ALARM2 0x45A
+#define W83782D_REG_ALARM3 0x45B
+
+#define W83781D_REG_BEEP_CONFIG 0x4D
+#define W83781D_REG_BEEP_INTS1 0x56
+#define W83781D_REG_BEEP_INTS2 0x57
+#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */
+
+#define W83781D_REG_VID_FANDIV 0x47
+
+#define W83781D_REG_CHIPID 0x49
+#define W83781D_REG_WCHIPID 0x58
+#define W83781D_REG_CHIPMAN 0x4F
+#define W83781D_REG_PIN 0x4B
+
+/* 782D/783S only */
+#define W83781D_REG_VBAT 0x5D
+
+/* PWM 782D (1-4) and 783S (1-2) only */
+static const u8 W83781D_REG_PWM[] = { 0x5B, 0x5A, 0x5E, 0x5F };
+#define W83781D_REG_PWMCLK12 0x5C
+#define W83781D_REG_PWMCLK34 0x45C
+
+#define W83781D_REG_I2C_ADDR 0x48
+#define W83781D_REG_I2C_SUBADDR 0x4A
+
+/*
+ * The following are undocumented in the data sheets however we
+ * received the information in an email from Winbond tech support
+ */
+/* Sensor selection - not on 781d */
+#define W83781D_REG_SCFG1 0x5D
+static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };
+
+#define W83781D_REG_SCFG2 0x59
+static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };
+
+#define W83781D_DEFAULT_BETA 3435
+
+/* Conversions */
+#define IN_TO_REG(val) clamp_val(((val) + 8) / 16, 0, 255)
+#define IN_FROM_REG(val) ((val) * 16)
+
+static inline u8
+FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+static inline long
+FAN_FROM_REG(u8 val, int div)
+{
+ if (val == 0)
+ return -1;
+ if (val == 255)
+ return 0;
+ return 1350000 / (val * div);
+}
+
+#define TEMP_TO_REG(val) clamp_val((val) / 1000, -127, 128)
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+#define BEEP_MASK_FROM_REG(val, type) ((type) == as99127f ? \
+ (~(val)) & 0x7fff : (val) & 0xff7fff)
+#define BEEP_MASK_TO_REG(val, type) ((type) == as99127f ? \
+ (~(val)) & 0x7fff : (val) & 0xff7fff)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static inline u8
+DIV_TO_REG(long val, enum chips type)
+{
+ int i;
+ val = clamp_val(val, 1,
+ ((type == w83781d || type == as99127f) ? 8 : 128)) >> 1;
+ for (i = 0; i < 7; i++) {
+ if (val == 0)
+ break;
+ val >>= 1;
+ }
+ return i;
+}
+
+struct w83781d_data {
+ struct i2c_client *client;
+ struct device *hwmon_dev;
+ struct mutex lock;
+ enum chips type;
+
+ /* For ISA device only */
+ const char *name;
+ int isa_addr;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ struct i2c_client *lm75[2]; /* for secondary I2C addresses */
+ /* array of 2 pointers to subclients */
+
+ u8 in[9]; /* Register value - 8 & 9 for 782D only */
+ u8 in_max[9]; /* Register value - 8 & 9 for 782D only */
+ u8 in_min[9]; /* Register value - 8 & 9 for 782D only */
+ u8 fan[3]; /* Register value */
+ u8 fan_min[3]; /* Register value */
+ s8 temp; /* Register value */
+ s8 temp_max; /* Register value */
+ s8 temp_max_hyst; /* Register value */
+ u16 temp_add[2]; /* Register value */
+ u16 temp_max_add[2]; /* Register value */
+ u16 temp_max_hyst_add[2]; /* Register value */
+ u8 fan_div[3]; /* Register encoding, shifted right */
+ u8 vid; /* Register encoding, combined */
+ u32 alarms; /* Register encoding, combined */
+ u32 beep_mask; /* Register encoding, combined */
+ u8 pwm[4]; /* Register value */
+ u8 pwm2_enable; /* Boolean */
+ u16 sens[3]; /*
+ * 782D/783S only.
+ * 1 = pentium diode; 2 = 3904 diode;
+ * 4 = thermistor
+ */
+ u8 vrm;
+};
+
+static struct w83781d_data *w83781d_data_if_isa(void);
+static int w83781d_alias_detect(struct i2c_client *client, u8 chipid);
+
+static int w83781d_read_value(struct w83781d_data *data, u16 reg);
+static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value);
+static struct w83781d_data *w83781d_update_device(struct device *dev);
+static void w83781d_init_device(struct device *dev);
+
+/* following are the sysfs callback functions */
+#define show_in_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *da, \
+ char *buf) \
+{ \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \
+ struct w83781d_data *data = w83781d_update_device(dev); \
+ return sprintf(buf, "%ld\n", \
+ (long)IN_FROM_REG(data->reg[attr->index])); \
+}
+show_in_reg(in);
+show_in_reg(in_min);
+show_in_reg(in_max);
+
+#define store_in_reg(REG, reg) \
+static ssize_t store_in_##reg(struct device *dev, struct device_attribute \
+ *da, const char *buf, size_t count) \
+{ \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \
+ struct w83781d_data *data = dev_get_drvdata(dev); \
+ int nr = attr->index; \
+ unsigned long val; \
+ int err = kstrtoul(buf, 10, &val); \
+ if (err) \
+ return err; \
+ mutex_lock(&data->update_lock); \
+ data->in_##reg[nr] = IN_TO_REG(val); \
+ w83781d_write_value(data, W83781D_REG_IN_##REG(nr), \
+ data->in_##reg[nr]); \
+ \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+store_in_reg(MIN, min);
+store_in_reg(MAX, max);
+
+#define sysfs_in_offsets(offset) \
+static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \
+ show_in, NULL, offset); \
+static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
+ show_in_min, store_in_min, offset); \
+static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
+ show_in_max, store_in_max, offset)
+
+sysfs_in_offsets(0);
+sysfs_in_offsets(1);
+sysfs_in_offsets(2);
+sysfs_in_offsets(3);
+sysfs_in_offsets(4);
+sysfs_in_offsets(5);
+sysfs_in_offsets(6);
+sysfs_in_offsets(7);
+sysfs_in_offsets(8);
+
+#define show_fan_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *da, \
+ char *buf) \
+{ \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \
+ struct w83781d_data *data = w83781d_update_device(dev); \
+ return sprintf(buf, "%ld\n", \
+ FAN_FROM_REG(data->reg[attr->index], \
+ DIV_FROM_REG(data->fan_div[attr->index]))); \
+}
+show_fan_reg(fan);
+show_fan_reg(fan_min);
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ 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]));
+ w83781d_write_value(data, W83781D_REG_FAN_MIN(nr),
+ data->fan_min[nr]);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,
+ show_fan_min, store_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR,
+ show_fan_min, store_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR,
+ show_fan_min, store_fan_min, 2);
+
+#define show_temp_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *da, \
+ char *buf) \
+{ \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \
+ struct w83781d_data *data = w83781d_update_device(dev); \
+ int nr = attr->index; \
+ if (nr >= 2) { /* TEMP2 and TEMP3 */ \
+ return sprintf(buf, "%d\n", \
+ LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \
+ } else { /* TEMP1 */ \
+ return sprintf(buf, "%ld\n", (long)TEMP_FROM_REG(data->reg)); \
+ } \
+}
+show_temp_reg(temp);
+show_temp_reg(temp_max);
+show_temp_reg(temp_max_hyst);
+
+#define store_temp_reg(REG, reg) \
+static ssize_t store_temp_##reg(struct device *dev, \
+ struct device_attribute *da, const char *buf, size_t count) \
+{ \
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \
+ struct w83781d_data *data = dev_get_drvdata(dev); \
+ int nr = attr->index; \
+ long val; \
+ int err = kstrtol(buf, 10, &val); \
+ if (err) \
+ return err; \
+ mutex_lock(&data->update_lock); \
+ \
+ if (nr >= 2) { /* TEMP2 and TEMP3 */ \
+ data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \
+ w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \
+ data->temp_##reg##_add[nr-2]); \
+ } else { /* TEMP1 */ \
+ data->temp_##reg = TEMP_TO_REG(val); \
+ w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \
+ data->temp_##reg); \
+ } \
+ \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+store_temp_reg(OVER, max);
+store_temp_reg(HYST, max_hyst);
+
+#define sysfs_temp_offsets(offset) \
+static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
+ show_temp, NULL, offset); \
+static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
+ show_temp_max, store_temp_max, offset); \
+static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_temp_max_hyst, store_temp_max_hyst, offset);
+
+sysfs_temp_offsets(1);
+sysfs_temp_offsets(2);
+sysfs_temp_offsets(3);
+
+static ssize_t
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83781d_data *data = w83781d_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 w83781d_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 w83781d_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ data->vrm = clamp_val(val, 0, 255);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(vrm);
+
+static ssize_t
+alarms_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83781d_data *data = w83781d_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+static DEVICE_ATTR_RO(alarms);
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct w83781d_data *data = w83781d_update_device(dev);
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
+/* The W83781D has a single alarm bit for temp2 and temp3 */
+static ssize_t show_temp3_alarm(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w83781d_data *data = w83781d_update_device(dev);
+ int bitnr = (data->type == w83781d) ? 5 : 13;
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16);
+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_temp3_alarm, NULL, 0);
+
+static ssize_t beep_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w83781d_data *data = w83781d_update_device(dev);
+ return sprintf(buf, "%ld\n",
+ (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type));
+}
+
+static ssize_t
+beep_mask_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w83781d_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->beep_mask &= 0x8000; /* preserve beep enable */
+ data->beep_mask |= BEEP_MASK_TO_REG(val, data->type);
+ w83781d_write_value(data, W83781D_REG_BEEP_INTS1,
+ data->beep_mask & 0xff);
+ w83781d_write_value(data, W83781D_REG_BEEP_INTS2,
+ (data->beep_mask >> 8) & 0xff);
+ if (data->type != w83781d && data->type != as99127f) {
+ w83781d_write_value(data, W83781D_REG_BEEP_INTS3,
+ ((data->beep_mask) >> 16) & 0xff);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(beep_mask);
+
+static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct w83781d_data *data = w83781d_update_device(dev);
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1);
+}
+
+static ssize_t
+store_beep(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ int bitnr = to_sensor_dev_attr(attr)->index;
+ u8 reg;
+ unsigned long bit;
+ int err;
+
+ err = kstrtoul(buf, 10, &bit);
+ if (err)
+ return err;
+
+ if (bit & ~1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (bit)
+ data->beep_mask |= (1 << bitnr);
+ else
+ data->beep_mask &= ~(1 << bitnr);
+
+ if (bitnr < 8) {
+ reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS1);
+ if (bit)
+ reg |= (1 << bitnr);
+ else
+ reg &= ~(1 << bitnr);
+ w83781d_write_value(data, W83781D_REG_BEEP_INTS1, reg);
+ } else if (bitnr < 16) {
+ reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS2);
+ if (bit)
+ reg |= (1 << (bitnr - 8));
+ else
+ reg &= ~(1 << (bitnr - 8));
+ w83781d_write_value(data, W83781D_REG_BEEP_INTS2, reg);
+ } else {
+ reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS3);
+ if (bit)
+ reg |= (1 << (bitnr - 16));
+ else
+ reg &= ~(1 << (bitnr - 16));
+ w83781d_write_value(data, W83781D_REG_BEEP_INTS3, reg);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* The W83781D has a single beep bit for temp2 and temp3 */
+static ssize_t show_temp3_beep(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w83781d_data *data = w83781d_update_device(dev);
+ int bitnr = (data->type == w83781d) ? 5 : 13;
+ return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1);
+}
+
+static SENSOR_DEVICE_ATTR(in0_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 0);
+static SENSOR_DEVICE_ATTR(in1_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 1);
+static SENSOR_DEVICE_ATTR(in2_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 2);
+static SENSOR_DEVICE_ATTR(in3_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 3);
+static SENSOR_DEVICE_ATTR(in4_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 8);
+static SENSOR_DEVICE_ATTR(in5_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 9);
+static SENSOR_DEVICE_ATTR(in6_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 10);
+static SENSOR_DEVICE_ATTR(in7_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 16);
+static SENSOR_DEVICE_ATTR(in8_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 17);
+static SENSOR_DEVICE_ATTR(fan1_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 6);
+static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 7);
+static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 11);
+static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 4);
+static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 5);
+static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO,
+ show_temp3_beep, store_beep, 13);
+static SENSOR_DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR,
+ show_beep, store_beep, 15);
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct w83781d_data *data = w83781d_update_device(dev);
+ return sprintf(buf, "%ld\n",
+ (long) DIV_FROM_REG(data->fan_div[attr->index]));
+}
+
+/*
+ * 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t
+store_fan_div(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ unsigned long min;
+ int nr = attr->index;
+ u8 reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ /* Save fan_min */
+ min = FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+
+ data->fan_div[nr] = DIV_TO_REG(val, data->type);
+
+ reg = (w83781d_read_value(data, nr == 2 ?
+ W83781D_REG_PIN : W83781D_REG_VID_FANDIV)
+ & (nr == 0 ? 0xcf : 0x3f))
+ | ((data->fan_div[nr] & 0x03) << (nr == 0 ? 4 : 6));
+ w83781d_write_value(data, nr == 2 ?
+ W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg);
+
+ /* w83781d and as99127f don't have extended divisor bits */
+ if (data->type != w83781d && data->type != as99127f) {
+ reg = (w83781d_read_value(data, W83781D_REG_VBAT)
+ & ~(1 << (5 + nr)))
+ | ((data->fan_div[nr] & 0x04) << (3 + nr));
+ w83781d_write_value(data, W83781D_REG_VBAT, reg);
+ }
+
+ /* Restore fan_min */
+ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ w83781d_write_value(data, W83781D_REG_FAN_MIN(nr), data->fan_min[nr]);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
+ show_fan_div, store_fan_div, 0);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
+ show_fan_div, store_fan_div, 1);
+static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO | S_IWUSR,
+ show_fan_div, store_fan_div, 2);
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct w83781d_data *data = w83781d_update_device(dev);
+ return sprintf(buf, "%d\n", (int)data->pwm[attr->index]);
+}
+
+static ssize_t
+pwm2_enable_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct w83781d_data *data = w83781d_update_device(dev);
+ return sprintf(buf, "%d\n", (int)data->pwm2_enable);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *da, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = clamp_val(val, 0, 255);
+ w83781d_write_value(data, W83781D_REG_PWM[nr], data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+pwm2_enable_store(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ u32 reg;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ switch (val) {
+ case 0:
+ case 1:
+ reg = w83781d_read_value(data, W83781D_REG_PWMCLK12);
+ w83781d_write_value(data, W83781D_REG_PWMCLK12,
+ (reg & 0xf7) | (val << 3));
+
+ reg = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG);
+ w83781d_write_value(data, W83781D_REG_BEEP_CONFIG,
+ (reg & 0xef) | (!val << 4));
+
+ data->pwm2_enable = val;
+ break;
+
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 3);
+/* only PWM2 can be enabled/disabled */
+static DEVICE_ATTR_RW(pwm2_enable);
+
+static ssize_t
+show_sensor(struct device *dev, struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct w83781d_data *data = w83781d_update_device(dev);
+ return sprintf(buf, "%d\n", (int)data->sens[attr->index]);
+}
+
+static ssize_t
+store_sensor(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ unsigned long val;
+ u32 tmp;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ switch (val) {
+ case 1: /* PII/Celeron diode */
+ tmp = w83781d_read_value(data, W83781D_REG_SCFG1);
+ w83781d_write_value(data, W83781D_REG_SCFG1,
+ tmp | BIT_SCFG1[nr]);
+ tmp = w83781d_read_value(data, W83781D_REG_SCFG2);
+ w83781d_write_value(data, W83781D_REG_SCFG2,
+ tmp | BIT_SCFG2[nr]);
+ data->sens[nr] = val;
+ break;
+ case 2: /* 3904 */
+ tmp = w83781d_read_value(data, W83781D_REG_SCFG1);
+ w83781d_write_value(data, W83781D_REG_SCFG1,
+ tmp | BIT_SCFG1[nr]);
+ tmp = w83781d_read_value(data, W83781D_REG_SCFG2);
+ w83781d_write_value(data, W83781D_REG_SCFG2,
+ tmp & ~BIT_SCFG2[nr]);
+ data->sens[nr] = val;
+ break;
+ case W83781D_DEFAULT_BETA:
+ dev_warn(dev,
+ "Sensor type %d is deprecated, please use 4 instead\n",
+ W83781D_DEFAULT_BETA);
+ fallthrough;
+ case 4: /* thermistor */
+ tmp = w83781d_read_value(data, W83781D_REG_SCFG1);
+ w83781d_write_value(data, W83781D_REG_SCFG1,
+ tmp & ~BIT_SCFG1[nr]);
+ data->sens[nr] = val;
+ break;
+ default:
+ dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or 4\n",
+ (long) val);
+ break;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO | S_IWUSR,
+ show_sensor, store_sensor, 0);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR,
+ show_sensor, store_sensor, 1);
+static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR,
+ show_sensor, store_sensor, 2);
+
+/*
+ * Assumes that adapter is of I2C, not ISA variety.
+ * OTHERWISE DON'T CALL THIS
+ */
+static int
+w83781d_detect_subclients(struct i2c_client *new_client)
+{
+ int i, val1 = 0, id;
+ int err;
+ int address = new_client->addr;
+ unsigned short sc_addr[2];
+ struct i2c_adapter *adapter = new_client->adapter;
+ struct w83781d_data *data = i2c_get_clientdata(new_client);
+ enum chips kind = data->type;
+ int num_sc = 1;
+
+ id = i2c_adapter_id(adapter);
+
+ if (force_subclients[0] == id && force_subclients[1] == address) {
+ for (i = 2; i <= 3; i++) {
+ if (force_subclients[i] < 0x48 ||
+ force_subclients[i] > 0x4f) {
+ dev_err(&new_client->dev,
+ "Invalid subclient address %d; must be 0x48-0x4f\n",
+ force_subclients[i]);
+ err = -EINVAL;
+ goto ERROR_SC_1;
+ }
+ }
+ w83781d_write_value(data, W83781D_REG_I2C_SUBADDR,
+ (force_subclients[2] & 0x07) |
+ ((force_subclients[3] & 0x07) << 4));
+ sc_addr[0] = force_subclients[2];
+ } else {
+ val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR);
+ sc_addr[0] = 0x48 + (val1 & 0x07);
+ }
+
+ if (kind != w83783s) {
+ num_sc = 2;
+ if (force_subclients[0] == id &&
+ force_subclients[1] == address) {
+ sc_addr[1] = force_subclients[3];
+ } else {
+ sc_addr[1] = 0x48 + ((val1 >> 4) & 0x07);
+ }
+ if (sc_addr[0] == sc_addr[1]) {
+ dev_err(&new_client->dev,
+ "Duplicate addresses 0x%x for subclients.\n",
+ sc_addr[0]);
+ err = -EBUSY;
+ goto ERROR_SC_2;
+ }
+ }
+
+ for (i = 0; i < num_sc; i++) {
+ data->lm75[i] = i2c_new_dummy_device(adapter, sc_addr[i]);
+ if (IS_ERR(data->lm75[i])) {
+ dev_err(&new_client->dev,
+ "Subclient %d registration at address 0x%x failed.\n",
+ i, sc_addr[i]);
+ err = PTR_ERR(data->lm75[i]);
+ if (i == 1)
+ goto ERROR_SC_3;
+ goto ERROR_SC_2;
+ }
+ }
+
+ return 0;
+
+/* Undo inits in case of errors */
+ERROR_SC_3:
+ i2c_unregister_device(data->lm75[0]);
+ERROR_SC_2:
+ERROR_SC_1:
+ return err;
+}
+
+#define IN_UNIT_ATTRS(X) \
+ &sensor_dev_attr_in##X##_input.dev_attr.attr, \
+ &sensor_dev_attr_in##X##_min.dev_attr.attr, \
+ &sensor_dev_attr_in##X##_max.dev_attr.attr, \
+ &sensor_dev_attr_in##X##_alarm.dev_attr.attr, \
+ &sensor_dev_attr_in##X##_beep.dev_attr.attr
+
+#define FAN_UNIT_ATTRS(X) \
+ &sensor_dev_attr_fan##X##_input.dev_attr.attr, \
+ &sensor_dev_attr_fan##X##_min.dev_attr.attr, \
+ &sensor_dev_attr_fan##X##_div.dev_attr.attr, \
+ &sensor_dev_attr_fan##X##_alarm.dev_attr.attr, \
+ &sensor_dev_attr_fan##X##_beep.dev_attr.attr
+
+#define TEMP_UNIT_ATTRS(X) \
+ &sensor_dev_attr_temp##X##_input.dev_attr.attr, \
+ &sensor_dev_attr_temp##X##_max.dev_attr.attr, \
+ &sensor_dev_attr_temp##X##_max_hyst.dev_attr.attr, \
+ &sensor_dev_attr_temp##X##_alarm.dev_attr.attr, \
+ &sensor_dev_attr_temp##X##_beep.dev_attr.attr
+
+static struct attribute *w83781d_attributes[] = {
+ IN_UNIT_ATTRS(0),
+ IN_UNIT_ATTRS(2),
+ IN_UNIT_ATTRS(3),
+ IN_UNIT_ATTRS(4),
+ IN_UNIT_ATTRS(5),
+ IN_UNIT_ATTRS(6),
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2),
+ FAN_UNIT_ATTRS(3),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_mask.attr,
+ &sensor_dev_attr_beep_enable.dev_attr.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group = {
+ .attrs = w83781d_attributes,
+};
+
+static struct attribute *w83781d_attributes_in1[] = {
+ IN_UNIT_ATTRS(1),
+ NULL
+};
+static const struct attribute_group w83781d_group_in1 = {
+ .attrs = w83781d_attributes_in1,
+};
+
+static struct attribute *w83781d_attributes_in78[] = {
+ IN_UNIT_ATTRS(7),
+ IN_UNIT_ATTRS(8),
+ NULL
+};
+static const struct attribute_group w83781d_group_in78 = {
+ .attrs = w83781d_attributes_in78,
+};
+
+static struct attribute *w83781d_attributes_temp3[] = {
+ TEMP_UNIT_ATTRS(3),
+ NULL
+};
+static const struct attribute_group w83781d_group_temp3 = {
+ .attrs = w83781d_attributes_temp3,
+};
+
+static struct attribute *w83781d_attributes_pwm12[] = {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &dev_attr_pwm2_enable.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group_pwm12 = {
+ .attrs = w83781d_attributes_pwm12,
+};
+
+static struct attribute *w83781d_attributes_pwm34[] = {
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group_pwm34 = {
+ .attrs = w83781d_attributes_pwm34,
+};
+
+static struct attribute *w83781d_attributes_other[] = {
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group_other = {
+ .attrs = w83781d_attributes_other,
+};
+
+/* No clean up is done on error, it's up to the caller */
+static int
+w83781d_create_files(struct device *dev, int kind, int is_isa)
+{
+ int err;
+
+ err = sysfs_create_group(&dev->kobj, &w83781d_group);
+ if (err)
+ return err;
+
+ if (kind != w83783s) {
+ err = sysfs_create_group(&dev->kobj, &w83781d_group_in1);
+ if (err)
+ return err;
+ }
+ if (kind != as99127f && kind != w83781d && kind != w83783s) {
+ err = sysfs_create_group(&dev->kobj, &w83781d_group_in78);
+ if (err)
+ return err;
+ }
+ if (kind != w83783s) {
+ err = sysfs_create_group(&dev->kobj, &w83781d_group_temp3);
+ if (err)
+ return err;
+
+ if (kind != w83781d) {
+ err = sysfs_chmod_file(&dev->kobj,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ S_IRUGO | S_IWUSR);
+ if (err)
+ return err;
+ }
+ }
+
+ if (kind != w83781d && kind != as99127f) {
+ err = sysfs_create_group(&dev->kobj, &w83781d_group_pwm12);
+ if (err)
+ return err;
+ }
+ if (kind == w83782d && !is_isa) {
+ err = sysfs_create_group(&dev->kobj, &w83781d_group_pwm34);
+ if (err)
+ return err;
+ }
+
+ if (kind != as99127f && kind != w83781d) {
+ err = device_create_file(dev,
+ &sensor_dev_attr_temp1_type.dev_attr);
+ if (err)
+ return err;
+ err = device_create_file(dev,
+ &sensor_dev_attr_temp2_type.dev_attr);
+ if (err)
+ return err;
+ if (kind != w83783s) {
+ err = device_create_file(dev,
+ &sensor_dev_attr_temp3_type.dev_attr);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int
+w83781d_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ int val1, val2;
+ struct w83781d_data *isa = w83781d_data_if_isa();
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ const char *client_name;
+ enum vendor { winbond, asus } vendid;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /*
+ * We block updates of the ISA device to minimize the risk of
+ * concurrent access to the same W83781D chip through different
+ * interfaces.
+ */
+ if (isa)
+ mutex_lock(&isa->update_lock);
+
+ if (i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG) & 0x80) {
+ dev_dbg(&adapter->dev,
+ "Detection of w83781d chip failed at step 3\n");
+ goto err_nodev;
+ }
+
+ val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK);
+ val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
+ /* Check for Winbond or Asus ID if in bank 0 */
+ if (!(val1 & 0x07) &&
+ ((!(val1 & 0x80) && val2 != 0xa3 && val2 != 0xc3) ||
+ ((val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) {
+ dev_dbg(&adapter->dev,
+ "Detection of w83781d chip failed at step 4\n");
+ goto err_nodev;
+ }
+ /*
+ * If Winbond SMBus, check address at 0x48.
+ * Asus doesn't support, except for as99127f rev.2
+ */
+ if ((!(val1 & 0x80) && val2 == 0xa3) ||
+ ((val1 & 0x80) && val2 == 0x5c)) {
+ if (i2c_smbus_read_byte_data(client, W83781D_REG_I2C_ADDR)
+ != address) {
+ dev_dbg(&adapter->dev,
+ "Detection of w83781d chip failed at step 5\n");
+ goto err_nodev;
+ }
+ }
+
+ /* Put it now into bank 0 and Vendor ID High Byte */
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ (i2c_smbus_read_byte_data(client, W83781D_REG_BANK)
+ & 0x78) | 0x80);
+
+ /* Get the vendor ID */
+ val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
+ if (val2 == 0x5c)
+ vendid = winbond;
+ else if (val2 == 0x12)
+ vendid = asus;
+ else {
+ dev_dbg(&adapter->dev,
+ "w83781d chip vendor is neither Winbond nor Asus\n");
+ goto err_nodev;
+ }
+
+ /* Determine the chip type. */
+ val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID);
+ if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
+ client_name = "w83781d";
+ else if (val1 == 0x30 && vendid == winbond)
+ client_name = "w83782d";
+ else if (val1 == 0x40 && vendid == winbond && address == 0x2d)
+ client_name = "w83783s";
+ else if (val1 == 0x31)
+ client_name = "as99127f";
+ else
+ goto err_nodev;
+
+ if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) {
+ dev_dbg(&adapter->dev,
+ "Device at 0x%02x appears to be the same as ISA device\n",
+ address);
+ goto err_nodev;
+ }
+
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+
+ strscpy(info->type, client_name, I2C_NAME_SIZE);
+
+ return 0;
+
+ err_nodev:
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+ return -ENODEV;
+}
+
+static void w83781d_remove_files(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &w83781d_group);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_in1);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_in78);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_temp3);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_pwm12);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_pwm34);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_other);
+}
+
+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;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct w83781d_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->lock);
+ mutex_init(&data->update_lock);
+
+ data->type = i2c_match_id(w83781d_ids, client)->driver_data;
+ data->client = client;
+
+ /* attach secondary i2c lm75-like clients */
+ err = w83781d_detect_subclients(client);
+ if (err)
+ return err;
+
+ /* Initialize the chip */
+ w83781d_init_device(dev);
+
+ /* Register sysfs hooks */
+ err = w83781d_create_files(dev, data->type, 0);
+ if (err)
+ goto exit_remove_files;
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+ exit_remove_files:
+ w83781d_remove_files(dev);
+ i2c_unregister_device(data->lm75[0]);
+ i2c_unregister_device(data->lm75[1]);
+ return err;
+}
+
+static void
+w83781d_remove(struct i2c_client *client)
+{
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+
+ hwmon_device_unregister(data->hwmon_dev);
+ w83781d_remove_files(dev);
+
+ i2c_unregister_device(data->lm75[0]);
+ i2c_unregister_device(data->lm75[1]);
+}
+
+static int
+w83781d_read_value_i2c(struct w83781d_data *data, u16 reg)
+{
+ struct i2c_client *client = data->client;
+ int res, bank;
+ struct i2c_client *cl;
+
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ bank);
+ if (bank == 0 || bank > 2) {
+ res = i2c_smbus_read_byte_data(client, reg & 0xff);
+ } else {
+ /* switch to subclient */
+ cl = data->lm75[bank - 1];
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x50: /* TEMP */
+ res = i2c_smbus_read_word_swapped(cl, 0);
+ break;
+ case 0x52: /* CONFIG */
+ res = i2c_smbus_read_byte_data(cl, 1);
+ break;
+ case 0x53: /* HYST */
+ res = i2c_smbus_read_word_swapped(cl, 2);
+ break;
+ case 0x55: /* OVER */
+ default:
+ res = i2c_smbus_read_word_swapped(cl, 3);
+ break;
+ }
+ }
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
+
+ return res;
+}
+
+static int
+w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value)
+{
+ struct i2c_client *client = data->client;
+ int bank;
+ struct i2c_client *cl;
+
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ bank);
+ if (bank == 0 || bank > 2) {
+ i2c_smbus_write_byte_data(client, reg & 0xff,
+ value & 0xff);
+ } else {
+ /* switch to subclient */
+ cl = data->lm75[bank - 1];
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x52: /* CONFIG */
+ i2c_smbus_write_byte_data(cl, 1, value & 0xff);
+ break;
+ case 0x53: /* HYST */
+ i2c_smbus_write_word_swapped(cl, 2, value);
+ break;
+ case 0x55: /* OVER */
+ i2c_smbus_write_word_swapped(cl, 3, value);
+ break;
+ }
+ }
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
+
+ return 0;
+}
+
+static void
+w83781d_init_device(struct device *dev)
+{
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ int i, p;
+ int type = data->type;
+ u8 tmp;
+
+ if (reset && type != as99127f) { /*
+ * this resets registers we don't have
+ * documentation for on the as99127f
+ */
+ /*
+ * Resetting the chip has been the default for a long time,
+ * but it causes the BIOS initializations (fan clock dividers,
+ * thermal sensor types...) to be lost, so it is now optional.
+ * It might even go away if nobody reports it as being useful,
+ * as I see very little reason why this would be needed at
+ * all.
+ */
+ dev_info(dev,
+ "If reset=1 solved a problem you were having, please report!\n");
+
+ /* save these registers */
+ i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG);
+ p = w83781d_read_value(data, W83781D_REG_PWMCLK12);
+ /*
+ * Reset all except Watchdog values and last conversion values
+ * This sets fan-divs to 2, among others
+ */
+ w83781d_write_value(data, W83781D_REG_CONFIG, 0x80);
+ /*
+ * Restore the registers and disable power-on abnormal beep.
+ * This saves FAN 1/2/3 input/output values set by BIOS.
+ */
+ w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80);
+ w83781d_write_value(data, W83781D_REG_PWMCLK12, p);
+ /*
+ * Disable master beep-enable (reset turns it on).
+ * Individual beep_mask should be reset to off but for some
+ * reason disabling this bit helps some people not get beeped
+ */
+ w83781d_write_value(data, W83781D_REG_BEEP_INTS2, 0);
+ }
+
+ /*
+ * Disable power-on abnormal beep, as advised by the datasheet.
+ * Already done if reset=1.
+ */
+ if (init && !reset && type != as99127f) {
+ i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG);
+ w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80);
+ }
+
+ data->vrm = vid_which_vrm();
+
+ if ((type != w83781d) && (type != as99127f)) {
+ tmp = w83781d_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 (w83781d_read_value
+ (data,
+ W83781D_REG_SCFG2) & BIT_SCFG2[i - 1])
+ data->sens[i - 1] = 1;
+ else
+ data->sens[i - 1] = 2;
+ }
+ if (type == w83783s && i == 2)
+ break;
+ }
+ }
+
+ if (init && type != as99127f) {
+ /* Enable temp2 */
+ tmp = w83781d_read_value(data, W83781D_REG_TEMP2_CONFIG);
+ if (tmp & 0x01) {
+ dev_warn(dev,
+ "Enabling temp2, readings might not make sense\n");
+ w83781d_write_value(data, W83781D_REG_TEMP2_CONFIG,
+ tmp & 0xfe);
+ }
+
+ /* Enable temp3 */
+ if (type != w83783s) {
+ tmp = w83781d_read_value(data,
+ W83781D_REG_TEMP3_CONFIG);
+ if (tmp & 0x01) {
+ dev_warn(dev,
+ "Enabling temp3, readings might not make sense\n");
+ w83781d_write_value(data,
+ W83781D_REG_TEMP3_CONFIG, tmp & 0xfe);
+ }
+ }
+ }
+
+ /* Start monitoring */
+ w83781d_write_value(data, W83781D_REG_CONFIG,
+ (w83781d_read_value(data,
+ W83781D_REG_CONFIG) & 0xf7)
+ | 0x01);
+
+ /* A few vars need to be filled upon startup */
+ for (i = 0; i < 3; i++) {
+ data->fan_min[i] = w83781d_read_value(data,
+ W83781D_REG_FAN_MIN(i));
+ }
+
+ mutex_init(&data->update_lock);
+}
+
+static struct w83781d_data *w83781d_update_device(struct device *dev)
+{
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ dev_dbg(dev, "Starting device update\n");
+
+ for (i = 0; i <= 8; i++) {
+ if (data->type == w83783s && i == 1)
+ continue; /* 783S has no in1 */
+ data->in[i] =
+ w83781d_read_value(data, W83781D_REG_IN(i));
+ data->in_min[i] =
+ w83781d_read_value(data, W83781D_REG_IN_MIN(i));
+ data->in_max[i] =
+ w83781d_read_value(data, W83781D_REG_IN_MAX(i));
+ if ((data->type != w83782d) && (i == 6))
+ break;
+ }
+ for (i = 0; i < 3; i++) {
+ data->fan[i] =
+ w83781d_read_value(data, W83781D_REG_FAN(i));
+ data->fan_min[i] =
+ w83781d_read_value(data, W83781D_REG_FAN_MIN(i));
+ }
+ if (data->type != w83781d && data->type != as99127f) {
+ for (i = 0; i < 4; i++) {
+ data->pwm[i] =
+ w83781d_read_value(data,
+ W83781D_REG_PWM[i]);
+ /* Only W83782D on SMBus has PWM3 and PWM4 */
+ if ((data->type != w83782d || !client)
+ && i == 1)
+ break;
+ }
+ /* Only PWM2 can be disabled */
+ data->pwm2_enable = (w83781d_read_value(data,
+ W83781D_REG_PWMCLK12) & 0x08) >> 3;
+ }
+
+ data->temp = w83781d_read_value(data, W83781D_REG_TEMP(1));
+ data->temp_max =
+ w83781d_read_value(data, W83781D_REG_TEMP_OVER(1));
+ data->temp_max_hyst =
+ w83781d_read_value(data, W83781D_REG_TEMP_HYST(1));
+ data->temp_add[0] =
+ w83781d_read_value(data, W83781D_REG_TEMP(2));
+ data->temp_max_add[0] =
+ w83781d_read_value(data, W83781D_REG_TEMP_OVER(2));
+ data->temp_max_hyst_add[0] =
+ w83781d_read_value(data, W83781D_REG_TEMP_HYST(2));
+ if (data->type != w83783s) {
+ data->temp_add[1] =
+ w83781d_read_value(data, W83781D_REG_TEMP(3));
+ data->temp_max_add[1] =
+ w83781d_read_value(data,
+ W83781D_REG_TEMP_OVER(3));
+ data->temp_max_hyst_add[1] =
+ w83781d_read_value(data,
+ W83781D_REG_TEMP_HYST(3));
+ }
+ i = w83781d_read_value(data, W83781D_REG_VID_FANDIV);
+ data->vid = i & 0x0f;
+ data->vid |= (w83781d_read_value(data,
+ W83781D_REG_CHIPID) & 0x01) << 4;
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = (i >> 6) & 0x03;
+ data->fan_div[2] = (w83781d_read_value(data,
+ W83781D_REG_PIN) >> 6) & 0x03;
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ i = w83781d_read_value(data, W83781D_REG_VBAT);
+ data->fan_div[0] |= (i >> 3) & 0x04;
+ data->fan_div[1] |= (i >> 4) & 0x04;
+ data->fan_div[2] |= (i >> 5) & 0x04;
+ }
+ if (data->type == w83782d) {
+ data->alarms = w83781d_read_value(data,
+ W83782D_REG_ALARM1)
+ | (w83781d_read_value(data,
+ W83782D_REG_ALARM2) << 8)
+ | (w83781d_read_value(data,
+ W83782D_REG_ALARM3) << 16);
+ } else if (data->type == w83783s) {
+ data->alarms = w83781d_read_value(data,
+ W83782D_REG_ALARM1)
+ | (w83781d_read_value(data,
+ W83782D_REG_ALARM2) << 8);
+ } else {
+ /*
+ * No real-time status registers, fall back to
+ * interrupt status registers
+ */
+ data->alarms = w83781d_read_value(data,
+ W83781D_REG_ALARM1)
+ | (w83781d_read_value(data,
+ W83781D_REG_ALARM2) << 8);
+ }
+ i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2);
+ data->beep_mask = (i << 8) +
+ w83781d_read_value(data, W83781D_REG_BEEP_INTS1);
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ data->beep_mask |=
+ w83781d_read_value(data,
+ W83781D_REG_BEEP_INTS3) << 16;
+ }
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+static const struct i2c_device_id w83781d_ids[] = {
+ { "w83781d", w83781d, },
+ { "w83782d", w83782d, },
+ { "w83783s", w83783s, },
+ { "as99127f", as99127f },
+ { /* LIST END */ }
+};
+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_new = w83781d_probe,
+ .remove = w83781d_remove,
+ .id_table = w83781d_ids,
+ .detect = w83781d_detect,
+ .address_list = normal_i2c,
+};
+
+/*
+ * ISA related code
+ */
+#ifdef CONFIG_ISA
+
+/* ISA device, if found */
+static struct platform_device *pdev;
+
+static unsigned short isa_address = 0x290;
+
+/*
+ * I2C devices get this name attribute automatically, but for ISA devices
+ * we must create it by ourselves.
+ */
+static ssize_t
+name_show(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ struct w83781d_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", data->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static struct w83781d_data *w83781d_data_if_isa(void)
+{
+ return pdev ? platform_get_drvdata(pdev) : NULL;
+}
+
+/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
+static int w83781d_alias_detect(struct i2c_client *client, u8 chipid)
+{
+ struct w83781d_data *isa;
+ int i;
+
+ if (!pdev) /* No ISA chip */
+ return 0;
+
+ isa = platform_get_drvdata(pdev);
+
+ if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr)
+ return 0; /* Address doesn't match */
+ if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid)
+ return 0; /* Chip type doesn't match */
+
+ /*
+ * We compare all the limit registers, the config register and the
+ * interrupt mask registers
+ */
+ for (i = 0x2b; i <= 0x3d; i++) {
+ if (w83781d_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
+ }
+ if (w83781d_read_value(isa, W83781D_REG_CONFIG) !=
+ i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG))
+ return 0;
+ for (i = 0x43; i <= 0x46; i++) {
+ if (w83781d_read_value(isa, i) !=
+ i2c_smbus_read_byte_data(client, i))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+w83781d_read_value_isa(struct w83781d_data *data, u16 reg)
+{
+ int word_sized, res;
+
+ word_sized = (((reg & 0xff00) == 0x100)
+ || ((reg & 0xff00) == 0x200))
+ && (((reg & 0x00ff) == 0x50)
+ || ((reg & 0x00ff) == 0x53)
+ || ((reg & 0x00ff) == 0x55));
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(reg >> 8,
+ data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+ outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ res = inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET);
+ if (word_sized) {
+ outb_p((reg & 0xff) + 1,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ res =
+ (res << 8) + inb_p(data->isa_addr +
+ W83781D_DATA_REG_OFFSET);
+ }
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+ return res;
+}
+
+static void
+w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value)
+{
+ int word_sized;
+
+ word_sized = (((reg & 0xff00) == 0x100)
+ || ((reg & 0xff00) == 0x200))
+ && (((reg & 0x00ff) == 0x53)
+ || ((reg & 0x00ff) == 0x55));
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(reg >> 8,
+ data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+ outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ if (word_sized) {
+ outb_p(value >> 8,
+ data->isa_addr + W83781D_DATA_REG_OFFSET);
+ outb_p((reg & 0xff) + 1,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ }
+ outb_p(value & 0xff, data->isa_addr + W83781D_DATA_REG_OFFSET);
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ data->isa_addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET);
+ }
+}
+
+/*
+ * The SMBus locks itself, usually, but nothing may access the Winbond between
+ * bank switches. ISA access must always be locked explicitly!
+ * We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
+ * would slow down the W83781D access and should not be necessary.
+ * There are some ugly typecasts here, but the good news is - they should
+ * nowhere else be necessary!
+ */
+static int
+w83781d_read_value(struct w83781d_data *data, u16 reg)
+{
+ struct i2c_client *client = data->client;
+ int res;
+
+ mutex_lock(&data->lock);
+ if (client)
+ res = w83781d_read_value_i2c(data, reg);
+ else
+ res = w83781d_read_value_isa(data, reg);
+ mutex_unlock(&data->lock);
+ return res;
+}
+
+static int
+w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value)
+{
+ struct i2c_client *client = data->client;
+
+ mutex_lock(&data->lock);
+ if (client)
+ w83781d_write_value_i2c(data, reg, value);
+ else
+ w83781d_write_value_isa(data, reg, value);
+ mutex_unlock(&data->lock);
+ return 0;
+}
+
+static int
+w83781d_isa_probe(struct platform_device *pdev)
+{
+ int err, reg;
+ struct w83781d_data *data;
+ struct resource *res;
+
+ /* Reserve the ISA region */
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev,
+ res->start + W83781D_ADDR_REG_OFFSET, 2,
+ "w83781d"))
+ return -EBUSY;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct w83781d_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->lock);
+ data->isa_addr = res->start;
+ platform_set_drvdata(pdev, data);
+
+ reg = w83781d_read_value(data, W83781D_REG_WCHIPID);
+ switch (reg) {
+ case 0x30:
+ data->type = w83782d;
+ data->name = "w83782d";
+ break;
+ default:
+ data->type = w83781d;
+ data->name = "w83781d";
+ }
+
+ /* Initialize the W83781D chip */
+ w83781d_init_device(&pdev->dev);
+
+ /* Register sysfs hooks */
+ err = w83781d_create_files(&pdev->dev, data->type, 1);
+ if (err)
+ goto exit_remove_files;
+
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
+ goto exit_remove_files;
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+ exit_remove_files:
+ w83781d_remove_files(&pdev->dev);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ return err;
+}
+
+static int
+w83781d_isa_remove(struct platform_device *pdev)
+{
+ struct w83781d_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ w83781d_remove_files(&pdev->dev);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+
+ return 0;
+}
+
+static struct platform_driver w83781d_isa_driver = {
+ .driver = {
+ .name = "w83781d",
+ },
+ .probe = w83781d_isa_probe,
+ .remove = w83781d_isa_remove,
+};
+
+/* return 1 if a supported chip is found, 0 otherwise */
+static int __init
+w83781d_isa_found(unsigned short address)
+{
+ int val, save, found = 0;
+ int port;
+
+ /*
+ * Some boards declare base+0 to base+7 as a PNP device, some base+4
+ * to base+7 and some base+5 to base+6. So we better request each port
+ * individually for the probing phase.
+ */
+ for (port = address; port < address + W83781D_EXTENT; port++) {
+ if (!request_region(port, 1, "w83781d")) {
+ pr_debug("Failed to request port 0x%x\n", port);
+ goto release;
+ }
+ }
+
+#define REALLY_SLOW_IO
+ /*
+ * We need the timeouts for at least some W83781D-like
+ * chips. But only if we read 'undefined' registers.
+ */
+ val = inb_p(address + 1);
+ if (inb_p(address + 2) != val
+ || inb_p(address + 3) != val
+ || inb_p(address + 7) != val) {
+ pr_debug("Detection failed at step %d\n", 1);
+ goto release;
+ }
+#undef REALLY_SLOW_IO
+
+ /*
+ * We should be able to change the 7 LSB of the address port. The
+ * MSB (busy flag) should be clear initially, set after the write.
+ */
+ save = inb_p(address + W83781D_ADDR_REG_OFFSET);
+ if (save & 0x80) {
+ pr_debug("Detection failed at step %d\n", 2);
+ goto release;
+ }
+ val = ~save & 0x7f;
+ outb_p(val, address + W83781D_ADDR_REG_OFFSET);
+ if (inb_p(address + W83781D_ADDR_REG_OFFSET) != (val | 0x80)) {
+ outb_p(save, address + W83781D_ADDR_REG_OFFSET);
+ pr_debug("Detection failed at step %d\n", 3);
+ goto release;
+ }
+
+ /* We found a device, now see if it could be a W83781D */
+ outb_p(W83781D_REG_CONFIG, address + W83781D_ADDR_REG_OFFSET);
+ val = inb_p(address + W83781D_DATA_REG_OFFSET);
+ if (val & 0x80) {
+ pr_debug("Detection failed at step %d\n", 4);
+ goto release;
+ }
+ outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET);
+ save = inb_p(address + W83781D_DATA_REG_OFFSET);
+ outb_p(W83781D_REG_CHIPMAN, address + W83781D_ADDR_REG_OFFSET);
+ val = inb_p(address + W83781D_DATA_REG_OFFSET);
+ if ((!(save & 0x80) && (val != 0xa3))
+ || ((save & 0x80) && (val != 0x5c))) {
+ pr_debug("Detection failed at step %d\n", 5);
+ goto release;
+ }
+ outb_p(W83781D_REG_I2C_ADDR, address + W83781D_ADDR_REG_OFFSET);
+ val = inb_p(address + W83781D_DATA_REG_OFFSET);
+ if (val < 0x03 || val > 0x77) { /* Not a valid I2C address */
+ pr_debug("Detection failed at step %d\n", 6);
+ goto release;
+ }
+
+ /* The busy flag should be clear again */
+ if (inb_p(address + W83781D_ADDR_REG_OFFSET) & 0x80) {
+ pr_debug("Detection failed at step %d\n", 7);
+ goto release;
+ }
+
+ /* Determine the chip type */
+ outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET);
+ save = inb_p(address + W83781D_DATA_REG_OFFSET);
+ outb_p(save & 0xf8, address + W83781D_DATA_REG_OFFSET);
+ outb_p(W83781D_REG_WCHIPID, address + W83781D_ADDR_REG_OFFSET);
+ val = inb_p(address + W83781D_DATA_REG_OFFSET);
+ if ((val & 0xfe) == 0x10 /* W83781D */
+ || val == 0x30) /* W83782D */
+ found = 1;
+
+ if (found)
+ pr_info("Found a %s chip at %#x\n",
+ val == 0x30 ? "W83782D" : "W83781D", (int)address);
+
+ release:
+ for (port--; port >= address; port--)
+ release_region(port, 1);
+ return found;
+}
+
+static int __init
+w83781d_isa_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + W83781D_EXTENT - 1,
+ .name = "w83781d",
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ pdev = platform_device_alloc("w83781d", address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit;
+ }
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+ exit_device_put:
+ platform_device_put(pdev);
+ exit:
+ pdev = NULL;
+ return err;
+}
+
+static int __init
+w83781d_isa_register(void)
+{
+ int res;
+
+ if (w83781d_isa_found(isa_address)) {
+ res = platform_driver_register(&w83781d_isa_driver);
+ if (res)
+ goto exit;
+
+ /* Sets global pdev as a side effect */
+ res = w83781d_isa_device_add(isa_address);
+ if (res)
+ goto exit_unreg_isa_driver;
+ }
+
+ return 0;
+
+exit_unreg_isa_driver:
+ platform_driver_unregister(&w83781d_isa_driver);
+exit:
+ return res;
+}
+
+static void
+w83781d_isa_unregister(void)
+{
+ if (pdev) {
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&w83781d_isa_driver);
+ }
+}
+#else /* !CONFIG_ISA */
+
+static struct w83781d_data *w83781d_data_if_isa(void)
+{
+ return NULL;
+}
+
+static int
+w83781d_alias_detect(struct i2c_client *client, u8 chipid)
+{
+ return 0;
+}
+
+static int
+w83781d_read_value(struct w83781d_data *data, u16 reg)
+{
+ int res;
+
+ mutex_lock(&data->lock);
+ res = w83781d_read_value_i2c(data, reg);
+ mutex_unlock(&data->lock);
+
+ return res;
+}
+
+static int
+w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value)
+{
+ mutex_lock(&data->lock);
+ w83781d_write_value_i2c(data, reg, value);
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static int __init
+w83781d_isa_register(void)
+{
+ return 0;
+}
+
+static void
+w83781d_isa_unregister(void)
+{
+}
+#endif /* CONFIG_ISA */
+
+static int __init
+sensors_w83781d_init(void)
+{
+ int res;
+
+ /*
+ * We register the ISA device first, so that we can skip the
+ * registration of an I2C interface to the same device.
+ */
+ res = w83781d_isa_register();
+ if (res)
+ goto exit;
+
+ res = i2c_add_driver(&w83781d_driver);
+ if (res)
+ goto exit_unreg_isa;
+
+ return 0;
+
+ exit_unreg_isa:
+ w83781d_isa_unregister();
+ exit:
+ return res;
+}
+
+static void __exit
+sensors_w83781d_exit(void)
+{
+ w83781d_isa_unregister();
+ i2c_del_driver(&w83781d_driver);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+ "Philip Edelbrock <phil@netroedge.com>, "
+ "and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("W83781D driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_w83781d_init);
+module_exit(sensors_w83781d_exit);
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
new file mode 100644
index 000000000..eaf691365
--- /dev/null
+++ b/drivers/hwmon/w83791d.c
@@ -0,0 +1,1657 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w83791d.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ *
+ * Copyright (C) 2006-2007 Charles Spirakis <bezaur@gmail.com>
+ */
+
+/*
+ * Supports following chips:
+ *
+ * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ * w83791d 10 5 5 3 0x71 0x5ca3 yes no
+ *
+ * The w83791d chip appears to be part way between the 83781d and the
+ * 83792d. Thus, this file is derived from both the w83792d.c and
+ * w83781d.c files.
+ *
+ * The w83791g chip is the same as the w83791d but lead-free.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+
+#define NUMBER_OF_VIN 10
+#define NUMBER_OF_FANIN 5
+#define NUMBER_OF_TEMPIN 3
+#define NUMBER_OF_PWM 5
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
+ I2C_CLIENT_END };
+
+/* Insmod parameters */
+
+static unsigned short force_subclients[4];
+module_param_array(force_subclients, short, NULL, 0);
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+static bool reset;
+module_param(reset, bool, 0);
+MODULE_PARM_DESC(reset, "Set to one to force a hardware chip reset");
+
+static bool init;
+module_param(init, bool, 0);
+MODULE_PARM_DESC(init, "Set to one to force extra software initialization");
+
+/* The W83791D registers */
+static const u8 W83791D_REG_IN[NUMBER_OF_VIN] = {
+ 0x20, /* VCOREA in DataSheet */
+ 0x21, /* VINR0 in DataSheet */
+ 0x22, /* +3.3VIN in DataSheet */
+ 0x23, /* VDD5V in DataSheet */
+ 0x24, /* +12VIN in DataSheet */
+ 0x25, /* -12VIN in DataSheet */
+ 0x26, /* -5VIN in DataSheet */
+ 0xB0, /* 5VSB in DataSheet */
+ 0xB1, /* VBAT in DataSheet */
+ 0xB2 /* VINR1 in DataSheet */
+};
+
+static const u8 W83791D_REG_IN_MAX[NUMBER_OF_VIN] = {
+ 0x2B, /* VCOREA High Limit in DataSheet */
+ 0x2D, /* VINR0 High Limit in DataSheet */
+ 0x2F, /* +3.3VIN High Limit in DataSheet */
+ 0x31, /* VDD5V High Limit in DataSheet */
+ 0x33, /* +12VIN High Limit in DataSheet */
+ 0x35, /* -12VIN High Limit in DataSheet */
+ 0x37, /* -5VIN High Limit in DataSheet */
+ 0xB4, /* 5VSB High Limit in DataSheet */
+ 0xB6, /* VBAT High Limit in DataSheet */
+ 0xB8 /* VINR1 High Limit in DataSheet */
+};
+static const u8 W83791D_REG_IN_MIN[NUMBER_OF_VIN] = {
+ 0x2C, /* VCOREA Low Limit in DataSheet */
+ 0x2E, /* VINR0 Low Limit in DataSheet */
+ 0x30, /* +3.3VIN Low Limit in DataSheet */
+ 0x32, /* VDD5V Low Limit in DataSheet */
+ 0x34, /* +12VIN Low Limit in DataSheet */
+ 0x36, /* -12VIN Low Limit in DataSheet */
+ 0x38, /* -5VIN Low Limit in DataSheet */
+ 0xB5, /* 5VSB Low Limit in DataSheet */
+ 0xB7, /* VBAT Low Limit in DataSheet */
+ 0xB9 /* VINR1 Low Limit in DataSheet */
+};
+static const u8 W83791D_REG_FAN[NUMBER_OF_FANIN] = {
+ 0x28, /* FAN 1 Count in DataSheet */
+ 0x29, /* FAN 2 Count in DataSheet */
+ 0x2A, /* FAN 3 Count in DataSheet */
+ 0xBA, /* FAN 4 Count in DataSheet */
+ 0xBB, /* FAN 5 Count in DataSheet */
+};
+static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = {
+ 0x3B, /* FAN 1 Count Low Limit in DataSheet */
+ 0x3C, /* FAN 2 Count Low Limit in DataSheet */
+ 0x3D, /* FAN 3 Count Low Limit in DataSheet */
+ 0xBC, /* FAN 4 Count Low Limit in DataSheet */
+ 0xBD, /* FAN 5 Count Low Limit in DataSheet */
+};
+
+static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = {
+ 0x81, /* PWM 1 duty cycle register in DataSheet */
+ 0x83, /* PWM 2 duty cycle register in DataSheet */
+ 0x94, /* PWM 3 duty cycle register in DataSheet */
+ 0xA0, /* PWM 4 duty cycle register in DataSheet */
+ 0xA1, /* PWM 5 duty cycle register in DataSheet */
+};
+
+static const u8 W83791D_REG_TEMP_TARGET[3] = {
+ 0x85, /* PWM 1 target temperature for temp 1 */
+ 0x86, /* PWM 2 target temperature for temp 2 */
+ 0x96, /* PWM 3 target temperature for temp 3 */
+};
+
+static const u8 W83791D_REG_TEMP_TOL[2] = {
+ 0x87, /* PWM 1/2 temperature tolerance */
+ 0x97, /* PWM 3 temperature tolerance */
+};
+
+static const u8 W83791D_REG_FAN_CFG[2] = {
+ 0x84, /* FAN 1/2 configuration */
+ 0x95, /* FAN 3 configuration */
+};
+
+static const u8 W83791D_REG_FAN_DIV[3] = {
+ 0x47, /* contains FAN1 and FAN2 Divisor */
+ 0x4b, /* contains FAN3 Divisor */
+ 0x5C, /* contains FAN4 and FAN5 Divisor */
+};
+
+#define W83791D_REG_BANK 0x4E
+#define W83791D_REG_TEMP2_CONFIG 0xC2
+#define W83791D_REG_TEMP3_CONFIG 0xCA
+
+static const u8 W83791D_REG_TEMP1[3] = {
+ 0x27, /* TEMP 1 in DataSheet */
+ 0x39, /* TEMP 1 Over in DataSheet */
+ 0x3A, /* TEMP 1 Hyst in DataSheet */
+};
+
+static const u8 W83791D_REG_TEMP_ADD[2][6] = {
+ {0xC0, /* TEMP 2 in DataSheet */
+ 0xC1, /* TEMP 2(0.5 deg) in DataSheet */
+ 0xC5, /* TEMP 2 Over High part in DataSheet */
+ 0xC6, /* TEMP 2 Over Low part in DataSheet */
+ 0xC3, /* TEMP 2 Thyst High part in DataSheet */
+ 0xC4}, /* TEMP 2 Thyst Low part in DataSheet */
+ {0xC8, /* TEMP 3 in DataSheet */
+ 0xC9, /* TEMP 3(0.5 deg) in DataSheet */
+ 0xCD, /* TEMP 3 Over High part in DataSheet */
+ 0xCE, /* TEMP 3 Over Low part in DataSheet */
+ 0xCB, /* TEMP 3 Thyst High part in DataSheet */
+ 0xCC} /* TEMP 3 Thyst Low part in DataSheet */
+};
+
+#define W83791D_REG_BEEP_CONFIG 0x4D
+
+static const u8 W83791D_REG_BEEP_CTRL[3] = {
+ 0x56, /* BEEP Control Register 1 */
+ 0x57, /* BEEP Control Register 2 */
+ 0xA3, /* BEEP Control Register 3 */
+};
+
+#define W83791D_REG_GPIO 0x15
+#define W83791D_REG_CONFIG 0x40
+#define W83791D_REG_VID_FANDIV 0x47
+#define W83791D_REG_DID_VID4 0x49
+#define W83791D_REG_WCHIPID 0x58
+#define W83791D_REG_CHIPMAN 0x4F
+#define W83791D_REG_PIN 0x4B
+#define W83791D_REG_I2C_SUBADDR 0x4A
+
+#define W83791D_REG_ALARM1 0xA9 /* realtime status register1 */
+#define W83791D_REG_ALARM2 0xAA /* realtime status register2 */
+#define W83791D_REG_ALARM3 0xAB /* realtime status register3 */
+
+#define W83791D_REG_VBAT 0x5D
+#define W83791D_REG_I2C_ADDR 0x48
+
+/*
+ * The SMBus locks itself. The Winbond W83791D has a bank select register
+ * (index 0x4e), but the driver only accesses registers in bank 0. Since
+ * we don't switch banks, we don't need any special code to handle
+ * locking access between bank switches
+ */
+static inline int w83791d_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int w83791d_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/*
+ * The analog voltage inputs have 16mV LSB. Since the sysfs output is
+ * in mV as would be measured on the chip input pin, need to just
+ * multiply/divide by 16 to translate from/to register values.
+ */
+#define IN_TO_REG(val) (clamp_val((((val) + 8) / 16), 0, 255))
+#define IN_FROM_REG(val) ((val) * 16)
+
+static u8 fan_to_reg(long rpm, int div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
+ ((val) == 255 ? 0 : \
+ 1350000 / ((val) * (div))))
+
+/* for temp1 which is 8-bit resolution, LSB = 1 degree Celsius */
+#define TEMP1_FROM_REG(val) ((val) * 1000)
+#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
+ (val) >= 127000 ? 127 : \
+ (val) < 0 ? ((val) - 500) / 1000 : \
+ ((val) + 500) / 1000)
+
+/*
+ * for temp2 and temp3 which are 9-bit resolution, LSB = 0.5 degree Celsius
+ * Assumes the top 8 bits are the integral amount and the bottom 8 bits
+ * are the fractional amount. Since we only have 0.5 degree resolution,
+ * the bottom 7 bits will always be zero
+ */
+#define TEMP23_FROM_REG(val) ((val) / 128 * 500)
+#define TEMP23_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
+ 127500), 500) * 128)
+
+/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */
+#define TARGET_TEMP_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), 0, 127000), \
+ 1000)
+
+/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */
+#define TOL_TEMP_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), 0, 15000), \
+ 1000)
+
+#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
+#define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static u8 div_to_reg(int nr, long val)
+{
+ int i;
+
+ /* fan divisors max out at 128 */
+ val = clamp_val(val, 1, 128) >> 1;
+ for (i = 0; i < 7; i++) {
+ if (val == 0)
+ break;
+ val >>= 1;
+ }
+ return (u8) i;
+}
+
+struct w83791d_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* volts */
+ u8 in[NUMBER_OF_VIN]; /* Register value */
+ u8 in_max[NUMBER_OF_VIN]; /* Register value */
+ u8 in_min[NUMBER_OF_VIN]; /* Register value */
+
+ /* fans */
+ u8 fan[NUMBER_OF_FANIN]; /* Register value */
+ u8 fan_min[NUMBER_OF_FANIN]; /* Register value */
+ u8 fan_div[NUMBER_OF_FANIN]; /* Register encoding, shifted right */
+
+ /* Temperature sensors */
+
+ s8 temp1[3]; /* current, over, thyst */
+ s16 temp_add[2][3]; /* fixed point value. Top 8 bits are the
+ * integral part, bottom 8 bits are the
+ * fractional part. We only use the top
+ * 9 bits as the resolution is only
+ * to the 0.5 degree C...
+ * two sensors with three values
+ * (cur, over, hyst)
+ */
+
+ /* PWMs */
+ u8 pwm[5]; /* pwm duty cycle */
+ u8 pwm_enable[3]; /* pwm enable status for fan 1-3
+ * (fan 4-5 only support manual mode)
+ */
+
+ u8 temp_target[3]; /* pwm 1-3 target temperature */
+ u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */
+
+ /* Misc */
+ u32 alarms; /* realtime status register encoding,combined */
+ u8 beep_enable; /* Global beep enable */
+ u32 beep_mask; /* Mask off specific beeps */
+ u8 vid; /* Register encoding, combined */
+ u8 vrm; /* hwmon-vid */
+};
+
+static int w83791d_probe(struct i2c_client *client);
+static int w83791d_detect(struct i2c_client *client,
+ struct i2c_board_info *info);
+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);
+static struct w83791d_data *w83791d_update_device(struct device *dev);
+
+#ifdef DEBUG
+static void w83791d_print_debug(struct w83791d_data *data, struct device *dev);
+#endif
+
+static void w83791d_init_client(struct i2c_client *client);
+
+static const struct i2c_device_id w83791d_id[] = {
+ { "w83791d", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, w83791d_id);
+
+static struct i2c_driver w83791d_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "w83791d",
+ },
+ .probe_new = w83791d_probe,
+ .remove = w83791d_remove,
+ .id_table = w83791d_id,
+ .detect = w83791d_detect,
+ .address_list = normal_i2c,
+};
+
+/* following are the sysfs callback functions */
+#define show_in_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ struct w83791d_data *data = w83791d_update_device(dev); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \
+}
+
+show_in_reg(in);
+show_in_reg(in_min);
+show_in_reg(in_max);
+
+#define store_in_reg(REG, reg) \
+static ssize_t store_in_##reg(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83791d_data *data = i2c_get_clientdata(client); \
+ int nr = sensor_attr->index; \
+ unsigned long val; \
+ int err = kstrtoul(buf, 10, &val); \
+ if (err) \
+ return err; \
+ mutex_lock(&data->update_lock); \
+ data->in_##reg[nr] = IN_TO_REG(val); \
+ w83791d_write(client, W83791D_REG_IN_##REG[nr], data->in_##reg[nr]); \
+ mutex_unlock(&data->update_lock); \
+ \
+ return count; \
+}
+store_in_reg(MIN, min);
+store_in_reg(MAX, max);
+
+static struct sensor_device_attribute sda_in_input[] = {
+ SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
+ SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
+ SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
+ SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
+ SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
+ SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
+ SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
+ SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
+ SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
+ SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9),
+};
+
+static struct sensor_device_attribute sda_in_min[] = {
+ SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
+ SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
+ SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
+ SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
+ SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
+ SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
+ SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
+ SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
+ SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
+ SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9),
+};
+
+static struct sensor_device_attribute sda_in_max[] = {
+ SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
+ SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
+ SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
+ SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
+ SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
+ SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
+ SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
+ SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
+ SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
+ SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
+};
+
+
+static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr =
+ to_sensor_dev_attr(attr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ int bitnr = sensor_attr->index;
+
+ return sprintf(buf, "%d\n", (data->beep_mask >> bitnr) & 1);
+}
+
+static ssize_t store_beep(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr =
+ to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int bitnr = sensor_attr->index;
+ int bytenr = bitnr / 8;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ val = val ? 1 : 0;
+
+ mutex_lock(&data->update_lock);
+
+ data->beep_mask &= ~(0xff << (bytenr * 8));
+ data->beep_mask |= w83791d_read(client, W83791D_REG_BEEP_CTRL[bytenr])
+ << (bytenr * 8);
+
+ data->beep_mask &= ~(1 << bitnr);
+ data->beep_mask |= val << bitnr;
+
+ w83791d_write(client, W83791D_REG_BEEP_CTRL[bytenr],
+ (data->beep_mask >> (bytenr * 8)) & 0xff);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr =
+ to_sensor_dev_attr(attr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ int bitnr = sensor_attr->index;
+
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
+/*
+ * Note: The bitmask for the beep enable/disable is different than
+ * the bitmask for the alarm.
+ */
+static struct sensor_device_attribute sda_in_beep[] = {
+ SENSOR_ATTR(in0_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 0),
+ SENSOR_ATTR(in1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 13),
+ SENSOR_ATTR(in2_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 2),
+ SENSOR_ATTR(in3_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 3),
+ SENSOR_ATTR(in4_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 8),
+ SENSOR_ATTR(in5_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 9),
+ SENSOR_ATTR(in6_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 10),
+ SENSOR_ATTR(in7_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 16),
+ SENSOR_ATTR(in8_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 17),
+ SENSOR_ATTR(in9_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 14),
+};
+
+static struct sensor_device_attribute sda_in_alarm[] = {
+ SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0),
+ SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1),
+ SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2),
+ SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3),
+ SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8),
+ SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9),
+ SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10),
+ SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 19),
+ SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 20),
+ SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 14),
+};
+
+#define show_fan_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct sensor_device_attribute *sensor_attr = \
+ to_sensor_dev_attr(attr); \
+ struct w83791d_data *data = w83791d_update_device(dev); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", \
+ FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
+}
+
+show_fan_reg(fan);
+show_fan_reg(fan_min);
+
+static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ 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]));
+ w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%u\n", 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long min;
+ u8 tmp_fan_div;
+ u8 fan_div_reg;
+ u8 vbat_reg;
+ int indx = 0;
+ u8 keep_mask = 0;
+ u8 new_shift = 0;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ /* Save fan_min */
+ min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
+
+ mutex_lock(&data->update_lock);
+ data->fan_div[nr] = div_to_reg(nr, val);
+
+ switch (nr) {
+ case 0:
+ indx = 0;
+ keep_mask = 0xcf;
+ new_shift = 4;
+ break;
+ case 1:
+ indx = 0;
+ keep_mask = 0x3f;
+ new_shift = 6;
+ break;
+ case 2:
+ indx = 1;
+ keep_mask = 0x3f;
+ new_shift = 6;
+ break;
+ case 3:
+ indx = 2;
+ keep_mask = 0xf8;
+ new_shift = 0;
+ break;
+ case 4:
+ indx = 2;
+ keep_mask = 0x8f;
+ new_shift = 4;
+ break;
+#ifdef DEBUG
+ default:
+ dev_warn(dev, "store_fan_div: Unexpected nr seen: %d\n", nr);
+ count = -EINVAL;
+ goto err_exit;
+#endif
+ }
+
+ fan_div_reg = w83791d_read(client, W83791D_REG_FAN_DIV[indx])
+ & keep_mask;
+ tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask;
+
+ w83791d_write(client, W83791D_REG_FAN_DIV[indx],
+ fan_div_reg | tmp_fan_div);
+
+ /* Bit 2 of fans 0-2 is stored in the vbat register (bits 5-7) */
+ if (nr < 3) {
+ keep_mask = ~(1 << (nr + 5));
+ vbat_reg = w83791d_read(client, W83791D_REG_VBAT)
+ & keep_mask;
+ tmp_fan_div = (data->fan_div[nr] << (3 + nr)) & ~keep_mask;
+ w83791d_write(client, W83791D_REG_VBAT,
+ vbat_reg | tmp_fan_div);
+ }
+
+ /* Restore fan_min */
+ data->fan_min[nr] = fan_to_reg(min, DIV_FROM_REG(data->fan_div[nr]));
+ w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]);
+
+#ifdef DEBUG
+err_exit:
+#endif
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct sensor_device_attribute sda_fan_input[] = {
+ SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
+ SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
+ SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
+ SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
+ SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
+};
+
+static struct sensor_device_attribute sda_fan_min[] = {
+ SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 0),
+ SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 1),
+ SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 2),
+ SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 3),
+ SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 4),
+};
+
+static struct sensor_device_attribute sda_fan_div[] = {
+ SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 0),
+ SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 1),
+ SENSOR_ATTR(fan3_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 2),
+ SENSOR_ATTR(fan4_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 3),
+ SENSOR_ATTR(fan5_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 4),
+};
+
+static struct sensor_device_attribute sda_fan_beep[] = {
+ SENSOR_ATTR(fan1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 6),
+ SENSOR_ATTR(fan2_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 7),
+ SENSOR_ATTR(fan3_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 11),
+ SENSOR_ATTR(fan4_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 21),
+ SENSOR_ATTR(fan5_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 22),
+};
+
+static struct sensor_device_attribute sda_fan_alarm[] = {
+ SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6),
+ SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7),
+ SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11),
+ SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 21),
+ SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22),
+};
+
+/* read/write PWMs */
+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%u\n", data->pwm[nr]);
+}
+
+static ssize_t store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = clamp_val(val, 0, 255);
+ w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_pwm[] = {
+ SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 0),
+ SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 1),
+ SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 2),
+ SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 3),
+ SENSOR_ATTR(pwm5, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 4),
+};
+
+static ssize_t show_pwmenable(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%u\n", data->pwm_enable[nr] + 1);
+}
+
+static ssize_t store_pwmenable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long val;
+ u8 reg_cfg_tmp;
+ u8 reg_idx = 0;
+ u8 val_shift = 0;
+ u8 keep_mask = 0;
+
+ int ret = kstrtoul(buf, 10, &val);
+
+ if (ret || val < 1 || val > 3)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm_enable[nr] = val - 1;
+ switch (nr) {
+ case 0:
+ reg_idx = 0;
+ val_shift = 2;
+ keep_mask = 0xf3;
+ break;
+ case 1:
+ reg_idx = 0;
+ val_shift = 4;
+ keep_mask = 0xcf;
+ break;
+ case 2:
+ reg_idx = 1;
+ val_shift = 2;
+ keep_mask = 0xf3;
+ break;
+ }
+
+ reg_cfg_tmp = w83791d_read(client, W83791D_REG_FAN_CFG[reg_idx]);
+ reg_cfg_tmp = (reg_cfg_tmp & keep_mask) |
+ data->pwm_enable[nr] << val_shift;
+
+ w83791d_write(client, W83791D_REG_FAN_CFG[reg_idx], reg_cfg_tmp);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+static struct sensor_device_attribute sda_pwmenable[] = {
+ SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 0),
+ SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 1),
+ SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 2),
+};
+
+/* For Smart Fan I / Thermal Cruise */
+static ssize_t show_temp_target(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ int nr = sensor_attr->index;
+ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr]));
+}
+
+static ssize_t store_temp_target(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ long val;
+ u8 target_mask;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->temp_target[nr] = TARGET_TEMP_TO_REG(val);
+ target_mask = w83791d_read(client,
+ W83791D_REG_TEMP_TARGET[nr]) & 0x80;
+ w83791d_write(client, W83791D_REG_TEMP_TARGET[nr],
+ data->temp_target[nr] | target_mask);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_temp_target[] = {
+ SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO,
+ show_temp_target, store_temp_target, 0),
+ SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO,
+ show_temp_target, store_temp_target, 1),
+ SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO,
+ show_temp_target, store_temp_target, 2),
+};
+
+static ssize_t show_temp_tolerance(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ int nr = sensor_attr->index;
+ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr]));
+}
+
+static ssize_t store_temp_tolerance(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = sensor_attr->index;
+ unsigned long val;
+ u8 target_mask;
+ u8 reg_idx = 0;
+ u8 val_shift = 0;
+ u8 keep_mask = 0;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ switch (nr) {
+ case 0:
+ reg_idx = 0;
+ val_shift = 0;
+ keep_mask = 0xf0;
+ break;
+ case 1:
+ reg_idx = 0;
+ val_shift = 4;
+ keep_mask = 0x0f;
+ break;
+ case 2:
+ reg_idx = 1;
+ val_shift = 0;
+ keep_mask = 0xf0;
+ break;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val);
+ target_mask = w83791d_read(client,
+ W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask;
+ w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx],
+ (data->temp_tolerance[nr] << val_shift) | target_mask);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_temp_tolerance[] = {
+ SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 0),
+ SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 1),
+ SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 2),
+};
+
+/* read/write the temperature1, includes measured value and limits */
+static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp1[attr->index]));
+}
+
+static ssize_t store_temp1(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int nr = attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp1[nr] = TEMP1_TO_REG(val);
+ w83791d_write(client, W83791D_REG_TEMP1[nr], data->temp1[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* read/write temperature2-3, includes measured value and limits */
+static ssize_t show_temp23(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct w83791d_data *data = w83791d_update_device(dev);
+ int nr = attr->nr;
+ int index = attr->index;
+ return sprintf(buf, "%d\n", TEMP23_FROM_REG(data->temp_add[nr][index]));
+}
+
+static ssize_t store_temp23(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ long val;
+ int err;
+ int nr = attr->nr;
+ int index = attr->index;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_add[nr][index] = TEMP23_TO_REG(val);
+ w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2],
+ data->temp_add[nr][index] >> 8);
+ w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2 + 1],
+ data->temp_add[nr][index] & 0x80);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct sensor_device_attribute_2 sda_temp_input[] = {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0),
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0),
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max[] = {
+ SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 1),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 0, 1),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 1, 1),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 2),
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 0, 2),
+ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 1, 2),
+};
+
+/*
+ * Note: The bitmask for the beep enable/disable is different than
+ * the bitmask for the alarm.
+ */
+static struct sensor_device_attribute sda_temp_beep[] = {
+ SENSOR_ATTR(temp1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 4),
+ SENSOR_ATTR(temp2_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 5),
+ SENSOR_ATTR(temp3_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 1),
+};
+
+static struct sensor_device_attribute sda_temp_alarm[] = {
+ SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4),
+ SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5),
+ SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
+};
+
+/* get realtime status of all sensors items: voltage, temp, fan */
+static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+
+static DEVICE_ATTR_RO(alarms);
+
+/* Beep control */
+
+#define GLOBAL_BEEP_ENABLE_SHIFT 15
+#define GLOBAL_BEEP_ENABLE_MASK (1 << GLOBAL_BEEP_ENABLE_SHIFT)
+
+static ssize_t show_beep_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%d\n", data->beep_enable);
+}
+
+static ssize_t show_beep_mask(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%d\n", BEEP_MASK_FROM_REG(data->beep_mask));
+}
+
+
+static ssize_t store_beep_mask(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int i;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ /*
+ * The beep_enable state overrides any enabling request from
+ * the masks
+ */
+ data->beep_mask = BEEP_MASK_TO_REG(val) & ~GLOBAL_BEEP_ENABLE_MASK;
+ data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT);
+
+ val = data->beep_mask;
+
+ for (i = 0; i < 3; i++) {
+ w83791d_write(client, W83791D_REG_BEEP_CTRL[i], (val & 0xff));
+ val >>= 8;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t store_beep_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ data->beep_enable = val ? 1 : 0;
+
+ /* Keep the full mask value in sync with the current enable */
+ data->beep_mask &= ~GLOBAL_BEEP_ENABLE_MASK;
+ data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT);
+
+ /*
+ * The global control is in the second beep control register
+ * so only need to update that register
+ */
+ val = (data->beep_mask >> 8) & 0xff;
+
+ w83791d_write(client, W83791D_REG_BEEP_CTRL[1], val);
+
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct sensor_device_attribute sda_beep_ctrl[] = {
+ SENSOR_ATTR(beep_enable, S_IRUGO | S_IWUSR,
+ show_beep_enable, store_beep_enable, 0),
+ SENSOR_ATTR(beep_mask, S_IRUGO | S_IWUSR,
+ show_beep_mask, store_beep_mask, 1)
+};
+
+/* cpu voltage regulation information */
+static ssize_t cpu0_vid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct w83791d_data *data = w83791d_update_device(dev);
+ return sprintf(buf, "%d\n", 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 w83791d_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w83791d_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ /*
+ * No lock needed as vrm is internal to the driver
+ * (not read from a chip register) and so is not
+ * updated in w83791d_update_device()
+ */
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val > 255)
+ return -EINVAL;
+
+ data->vrm = val;
+ return count;
+}
+
+static DEVICE_ATTR_RW(vrm);
+
+#define IN_UNIT_ATTRS(X) \
+ &sda_in_input[X].dev_attr.attr, \
+ &sda_in_min[X].dev_attr.attr, \
+ &sda_in_max[X].dev_attr.attr, \
+ &sda_in_beep[X].dev_attr.attr, \
+ &sda_in_alarm[X].dev_attr.attr
+
+#define FAN_UNIT_ATTRS(X) \
+ &sda_fan_input[X].dev_attr.attr, \
+ &sda_fan_min[X].dev_attr.attr, \
+ &sda_fan_div[X].dev_attr.attr, \
+ &sda_fan_beep[X].dev_attr.attr, \
+ &sda_fan_alarm[X].dev_attr.attr
+
+#define TEMP_UNIT_ATTRS(X) \
+ &sda_temp_input[X].dev_attr.attr, \
+ &sda_temp_max[X].dev_attr.attr, \
+ &sda_temp_max_hyst[X].dev_attr.attr, \
+ &sda_temp_beep[X].dev_attr.attr, \
+ &sda_temp_alarm[X].dev_attr.attr
+
+static struct attribute *w83791d_attributes[] = {
+ IN_UNIT_ATTRS(0),
+ IN_UNIT_ATTRS(1),
+ IN_UNIT_ATTRS(2),
+ IN_UNIT_ATTRS(3),
+ IN_UNIT_ATTRS(4),
+ IN_UNIT_ATTRS(5),
+ IN_UNIT_ATTRS(6),
+ IN_UNIT_ATTRS(7),
+ IN_UNIT_ATTRS(8),
+ IN_UNIT_ATTRS(9),
+ FAN_UNIT_ATTRS(0),
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2),
+ TEMP_UNIT_ATTRS(0),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+ &dev_attr_alarms.attr,
+ &sda_beep_ctrl[0].dev_attr.attr,
+ &sda_beep_ctrl[1].dev_attr.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &sda_pwm[0].dev_attr.attr,
+ &sda_pwm[1].dev_attr.attr,
+ &sda_pwm[2].dev_attr.attr,
+ &sda_pwmenable[0].dev_attr.attr,
+ &sda_pwmenable[1].dev_attr.attr,
+ &sda_pwmenable[2].dev_attr.attr,
+ &sda_temp_target[0].dev_attr.attr,
+ &sda_temp_target[1].dev_attr.attr,
+ &sda_temp_target[2].dev_attr.attr,
+ &sda_temp_tolerance[0].dev_attr.attr,
+ &sda_temp_tolerance[1].dev_attr.attr,
+ &sda_temp_tolerance[2].dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group w83791d_group = {
+ .attrs = w83791d_attributes,
+};
+
+/*
+ * Separate group of attributes for fan/pwm 4-5. Their pins can also be
+ * in use for GPIO in which case their sysfs-interface should not be made
+ * available
+ */
+static struct attribute *w83791d_attributes_fanpwm45[] = {
+ FAN_UNIT_ATTRS(3),
+ FAN_UNIT_ATTRS(4),
+ &sda_pwm[3].dev_attr.attr,
+ &sda_pwm[4].dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group w83791d_group_fanpwm45 = {
+ .attrs = w83791d_attributes_fanpwm45,
+};
+
+static int w83791d_detect_subclients(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ int i, id;
+ u8 val;
+
+ id = i2c_adapter_id(adapter);
+ if (force_subclients[0] == id && force_subclients[1] == address) {
+ for (i = 2; i <= 3; i++) {
+ if (force_subclients[i] < 0x48 ||
+ force_subclients[i] > 0x4f) {
+ dev_err(&client->dev,
+ "invalid subclient "
+ "address %d; must be 0x48-0x4f\n",
+ force_subclients[i]);
+ return -ENODEV;
+ }
+ }
+ w83791d_write(client, W83791D_REG_I2C_SUBADDR,
+ (force_subclients[2] & 0x07) |
+ ((force_subclients[3] & 0x07) << 4));
+ }
+
+ val = w83791d_read(client, W83791D_REG_I2C_SUBADDR);
+
+ 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;
+}
+
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int w83791d_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int val1, val2;
+ unsigned short address = client->addr;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80)
+ return -ENODEV;
+
+ val1 = w83791d_read(client, W83791D_REG_BANK);
+ val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
+ /* Check for Winbond ID if in bank 0 */
+ if (!(val1 & 0x07)) {
+ if ((!(val1 & 0x80) && val2 != 0xa3) ||
+ ((val1 & 0x80) && val2 != 0x5c)) {
+ return -ENODEV;
+ }
+ }
+ /*
+ * If Winbond chip, address of chip and W83791D_REG_I2C_ADDR
+ * should match
+ */
+ if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address)
+ return -ENODEV;
+
+ /* We want bank 0 and Vendor ID high byte */
+ val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78;
+ w83791d_write(client, W83791D_REG_BANK, val1 | 0x80);
+
+ /* Verify it is a Winbond w83791d */
+ val1 = w83791d_read(client, W83791D_REG_WCHIPID);
+ val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
+ if (val1 != 0x71 || val2 != 0x5c)
+ return -ENODEV;
+
+ strscpy(info->type, "w83791d", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int w83791d_probe(struct i2c_client *client)
+{
+ struct w83791d_data *data;
+ struct device *dev = &client->dev;
+ int i, err;
+ u8 has_fanpwm45;
+
+#ifdef DEBUG
+ int val1;
+ val1 = w83791d_read(client, W83791D_REG_DID_VID4);
+ dev_dbg(dev, "Device ID version: %d.%d (0x%02x)\n",
+ (val1 >> 5) & 0x07, (val1 >> 1) & 0x0f, val1);
+#endif
+
+ data = devm_kzalloc(&client->dev, sizeof(struct w83791d_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ err = w83791d_detect_subclients(client);
+ if (err)
+ return err;
+
+ /* Initialize the chip */
+ w83791d_init_client(client);
+
+ /*
+ * If the fan_div is changed, make sure there is a rational
+ * fan_min in place
+ */
+ for (i = 0; i < NUMBER_OF_FANIN; i++)
+ data->fan_min[i] = w83791d_read(client, W83791D_REG_FAN_MIN[i]);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &w83791d_group);
+ if (err)
+ return err;
+
+ /* Check if pins of fan/pwm 4-5 are in use as GPIO */
+ has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10;
+ if (has_fanpwm45) {
+ err = sysfs_create_group(&client->dev.kobj,
+ &w83791d_group_fanpwm45);
+ if (err)
+ goto error4;
+ }
+
+ /* Everything is ready, now register the working device */
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto error5;
+ }
+
+ return 0;
+
+error5:
+ if (has_fanpwm45)
+ sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45);
+error4:
+ sysfs_remove_group(&client->dev.kobj, &w83791d_group);
+ return err;
+}
+
+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);
+}
+
+static void w83791d_init_client(struct i2c_client *client)
+{
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ u8 tmp;
+ u8 old_beep;
+
+ /*
+ * The difference between reset and init is that reset
+ * does a hard reset of the chip via index 0x40, bit 7,
+ * but init simply forces certain registers to have "sane"
+ * values. The hope is that the BIOS has done the right
+ * thing (which is why the default is reset=0, init=0),
+ * but if not, reset is the hard hammer and init
+ * is the soft mallet both of which are trying to whack
+ * things into place...
+ * NOTE: The data sheet makes a distinction between
+ * "power on defaults" and "reset by MR". As far as I can tell,
+ * the hard reset puts everything into a power-on state so I'm
+ * not sure what "reset by MR" means or how it can happen.
+ */
+ if (reset || init) {
+ /* keep some BIOS settings when we... */
+ old_beep = w83791d_read(client, W83791D_REG_BEEP_CONFIG);
+
+ if (reset) {
+ /* ... reset the chip and ... */
+ w83791d_write(client, W83791D_REG_CONFIG, 0x80);
+ }
+
+ /* ... disable power-on abnormal beep */
+ w83791d_write(client, W83791D_REG_BEEP_CONFIG, old_beep | 0x80);
+
+ /* disable the global beep (not done by hard reset) */
+ tmp = w83791d_read(client, W83791D_REG_BEEP_CTRL[1]);
+ w83791d_write(client, W83791D_REG_BEEP_CTRL[1], tmp & 0xef);
+
+ if (init) {
+ /* Make sure monitoring is turned on for add-ons */
+ tmp = w83791d_read(client, W83791D_REG_TEMP2_CONFIG);
+ if (tmp & 1) {
+ w83791d_write(client, W83791D_REG_TEMP2_CONFIG,
+ tmp & 0xfe);
+ }
+
+ tmp = w83791d_read(client, W83791D_REG_TEMP3_CONFIG);
+ if (tmp & 1) {
+ w83791d_write(client, W83791D_REG_TEMP3_CONFIG,
+ tmp & 0xfe);
+ }
+
+ /* Start monitoring */
+ tmp = w83791d_read(client, W83791D_REG_CONFIG) & 0xf7;
+ w83791d_write(client, W83791D_REG_CONFIG, tmp | 0x01);
+ }
+ }
+
+ data->vrm = vid_which_vrm();
+}
+
+static struct w83791d_data *w83791d_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83791d_data *data = i2c_get_clientdata(client);
+ int i, j;
+ u8 reg_array_tmp[3];
+ u8 vbat_reg;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + (HZ * 3))
+ || !data->valid) {
+ dev_dbg(dev, "Starting w83791d device update\n");
+
+ /* Update the voltages measured value and limits */
+ for (i = 0; i < NUMBER_OF_VIN; i++) {
+ data->in[i] = w83791d_read(client,
+ W83791D_REG_IN[i]);
+ data->in_max[i] = w83791d_read(client,
+ W83791D_REG_IN_MAX[i]);
+ data->in_min[i] = w83791d_read(client,
+ W83791D_REG_IN_MIN[i]);
+ }
+
+ /* Update the fan counts and limits */
+ for (i = 0; i < NUMBER_OF_FANIN; i++) {
+ /* Update the Fan measured value and limits */
+ data->fan[i] = w83791d_read(client,
+ W83791D_REG_FAN[i]);
+ data->fan_min[i] = w83791d_read(client,
+ W83791D_REG_FAN_MIN[i]);
+ }
+
+ /* Update the fan divisor */
+ for (i = 0; i < 3; i++) {
+ reg_array_tmp[i] = w83791d_read(client,
+ W83791D_REG_FAN_DIV[i]);
+ }
+ data->fan_div[0] = (reg_array_tmp[0] >> 4) & 0x03;
+ data->fan_div[1] = (reg_array_tmp[0] >> 6) & 0x03;
+ data->fan_div[2] = (reg_array_tmp[1] >> 6) & 0x03;
+ data->fan_div[3] = reg_array_tmp[2] & 0x07;
+ data->fan_div[4] = (reg_array_tmp[2] >> 4) & 0x07;
+
+ /*
+ * The fan divisor for fans 0-2 get bit 2 from
+ * bits 5-7 respectively of vbat register
+ */
+ vbat_reg = w83791d_read(client, W83791D_REG_VBAT);
+ for (i = 0; i < 3; i++)
+ data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04;
+
+ /* Update PWM duty cycle */
+ for (i = 0; i < NUMBER_OF_PWM; i++) {
+ data->pwm[i] = w83791d_read(client,
+ W83791D_REG_PWM[i]);
+ }
+
+ /* Update PWM enable status */
+ for (i = 0; i < 2; i++) {
+ reg_array_tmp[i] = w83791d_read(client,
+ W83791D_REG_FAN_CFG[i]);
+ }
+ data->pwm_enable[0] = (reg_array_tmp[0] >> 2) & 0x03;
+ data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03;
+ data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03;
+
+ /* Update PWM target temperature */
+ for (i = 0; i < 3; i++) {
+ data->temp_target[i] = w83791d_read(client,
+ W83791D_REG_TEMP_TARGET[i]) & 0x7f;
+ }
+
+ /* Update PWM temperature tolerance */
+ for (i = 0; i < 2; i++) {
+ reg_array_tmp[i] = w83791d_read(client,
+ W83791D_REG_TEMP_TOL[i]);
+ }
+ data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f;
+ data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f;
+ data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f;
+
+ /* Update the first temperature sensor */
+ for (i = 0; i < 3; i++) {
+ data->temp1[i] = w83791d_read(client,
+ W83791D_REG_TEMP1[i]);
+ }
+
+ /* Update the rest of the temperature sensors */
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 3; j++) {
+ data->temp_add[i][j] =
+ (w83791d_read(client,
+ W83791D_REG_TEMP_ADD[i][j * 2]) << 8) |
+ w83791d_read(client,
+ W83791D_REG_TEMP_ADD[i][j * 2 + 1]);
+ }
+ }
+
+ /* Update the realtime status */
+ data->alarms =
+ w83791d_read(client, W83791D_REG_ALARM1) +
+ (w83791d_read(client, W83791D_REG_ALARM2) << 8) +
+ (w83791d_read(client, W83791D_REG_ALARM3) << 16);
+
+ /* Update the beep configuration information */
+ data->beep_mask =
+ w83791d_read(client, W83791D_REG_BEEP_CTRL[0]) +
+ (w83791d_read(client, W83791D_REG_BEEP_CTRL[1]) << 8) +
+ (w83791d_read(client, W83791D_REG_BEEP_CTRL[2]) << 16);
+
+ /* Extract global beep enable flag */
+ data->beep_enable =
+ (data->beep_mask >> GLOBAL_BEEP_ENABLE_SHIFT) & 0x01;
+
+ /* Update the cpu voltage information */
+ i = w83791d_read(client, W83791D_REG_VID_FANDIV);
+ data->vid = i & 0x0f;
+ data->vid |= (w83791d_read(client, W83791D_REG_DID_VID4) & 0x01)
+ << 4;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+#ifdef DEBUG
+ w83791d_print_debug(data, dev);
+#endif
+
+ return data;
+}
+
+#ifdef DEBUG
+static void w83791d_print_debug(struct w83791d_data *data, struct device *dev)
+{
+ int i = 0, j = 0;
+
+ dev_dbg(dev, "======Start of w83791d debug values======\n");
+ dev_dbg(dev, "%d set of Voltages: ===>\n", NUMBER_OF_VIN);
+ for (i = 0; i < NUMBER_OF_VIN; i++) {
+ dev_dbg(dev, "vin[%d] is: 0x%02x\n", i, data->in[i]);
+ dev_dbg(dev, "vin[%d] min is: 0x%02x\n", i, data->in_min[i]);
+ dev_dbg(dev, "vin[%d] max is: 0x%02x\n", i, data->in_max[i]);
+ }
+ dev_dbg(dev, "%d set of Fan Counts/Divisors: ===>\n", NUMBER_OF_FANIN);
+ for (i = 0; i < NUMBER_OF_FANIN; i++) {
+ dev_dbg(dev, "fan[%d] is: 0x%02x\n", i, data->fan[i]);
+ dev_dbg(dev, "fan[%d] min is: 0x%02x\n", i, data->fan_min[i]);
+ dev_dbg(dev, "fan_div[%d] is: 0x%02x\n", i, data->fan_div[i]);
+ }
+
+ /*
+ * temperature math is signed, but only print out the
+ * bits that matter
+ */
+ dev_dbg(dev, "%d set of Temperatures: ===>\n", NUMBER_OF_TEMPIN);
+ for (i = 0; i < 3; i++)
+ dev_dbg(dev, "temp1[%d] is: 0x%02x\n", i, (u8) data->temp1[i]);
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 3; j++) {
+ dev_dbg(dev, "temp_add[%d][%d] is: 0x%04x\n", i, j,
+ (u16) data->temp_add[i][j]);
+ }
+ }
+
+ dev_dbg(dev, "Misc Information: ===>\n");
+ dev_dbg(dev, "alarm is: 0x%08x\n", data->alarms);
+ dev_dbg(dev, "beep_mask is: 0x%08x\n", data->beep_mask);
+ dev_dbg(dev, "beep_enable is: %d\n", data->beep_enable);
+ dev_dbg(dev, "vid is: 0x%02x\n", data->vid);
+ dev_dbg(dev, "vrm is: 0x%02x\n", data->vrm);
+ dev_dbg(dev, "=======End of w83791d debug values========\n");
+ dev_dbg(dev, "\n");
+}
+#endif
+
+module_i2c_driver(w83791d_driver);
+
+MODULE_AUTHOR("Charles Spirakis <bezaur@gmail.com>");
+MODULE_DESCRIPTION("W83791D driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
new file mode 100644
index 000000000..6d160eee1
--- /dev/null
+++ b/drivers/hwmon/w83792d.c
@@ -0,0 +1,1644 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w83792d.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2004, 2005 Winbond Electronics Corp.
+ * Shane Huang,
+ * Rudolf Marek <r.marek@assembler.cz>
+ *
+ * Note:
+ * 1. This driver is only for 2.6 kernel, 2.4 kernel need a different driver.
+ * 2. This driver is only for Winbond W83792D C version device, there
+ * are also some motherboards with B version W83792D device. The
+ * calculation method to in6-in7(measured value, limits) is a little
+ * different between C and B version. C or B version can be identified
+ * by CR[0x49h].
+ */
+
+/*
+ * Supports following chips:
+ *
+ * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ * w83792d 9 7 7 3 0x7a 0x5ca3 yes no
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
+ I2C_CLIENT_END };
+
+/* Insmod parameters */
+
+static unsigned short force_subclients[4];
+module_param_array(force_subclients, short, NULL, 0);
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+static bool init;
+module_param(init, bool, 0);
+MODULE_PARM_DESC(init, "Set to one to force chip initialization");
+
+/* The W83792D registers */
+static const u8 W83792D_REG_IN[9] = {
+ 0x20, /* Vcore A in DataSheet */
+ 0x21, /* Vcore B in DataSheet */
+ 0x22, /* VIN0 in DataSheet */
+ 0x23, /* VIN1 in DataSheet */
+ 0x24, /* VIN2 in DataSheet */
+ 0x25, /* VIN3 in DataSheet */
+ 0x26, /* 5VCC in DataSheet */
+ 0xB0, /* 5VSB in DataSheet */
+ 0xB1 /* VBAT in DataSheet */
+};
+#define W83792D_REG_LOW_BITS1 0x3E /* Low Bits I in DataSheet */
+#define W83792D_REG_LOW_BITS2 0x3F /* Low Bits II in DataSheet */
+static const u8 W83792D_REG_IN_MAX[9] = {
+ 0x2B, /* Vcore A High Limit in DataSheet */
+ 0x2D, /* Vcore B High Limit in DataSheet */
+ 0x2F, /* VIN0 High Limit in DataSheet */
+ 0x31, /* VIN1 High Limit in DataSheet */
+ 0x33, /* VIN2 High Limit in DataSheet */
+ 0x35, /* VIN3 High Limit in DataSheet */
+ 0x37, /* 5VCC High Limit in DataSheet */
+ 0xB4, /* 5VSB High Limit in DataSheet */
+ 0xB6 /* VBAT High Limit in DataSheet */
+};
+static const u8 W83792D_REG_IN_MIN[9] = {
+ 0x2C, /* Vcore A Low Limit in DataSheet */
+ 0x2E, /* Vcore B Low Limit in DataSheet */
+ 0x30, /* VIN0 Low Limit in DataSheet */
+ 0x32, /* VIN1 Low Limit in DataSheet */
+ 0x34, /* VIN2 Low Limit in DataSheet */
+ 0x36, /* VIN3 Low Limit in DataSheet */
+ 0x38, /* 5VCC Low Limit in DataSheet */
+ 0xB5, /* 5VSB Low Limit in DataSheet */
+ 0xB7 /* VBAT Low Limit in DataSheet */
+};
+static const u8 W83792D_REG_FAN[7] = {
+ 0x28, /* FAN 1 Count in DataSheet */
+ 0x29, /* FAN 2 Count in DataSheet */
+ 0x2A, /* FAN 3 Count in DataSheet */
+ 0xB8, /* FAN 4 Count in DataSheet */
+ 0xB9, /* FAN 5 Count in DataSheet */
+ 0xBA, /* FAN 6 Count in DataSheet */
+ 0xBE /* FAN 7 Count in DataSheet */
+};
+static const u8 W83792D_REG_FAN_MIN[7] = {
+ 0x3B, /* FAN 1 Count Low Limit in DataSheet */
+ 0x3C, /* FAN 2 Count Low Limit in DataSheet */
+ 0x3D, /* FAN 3 Count Low Limit in DataSheet */
+ 0xBB, /* FAN 4 Count Low Limit in DataSheet */
+ 0xBC, /* FAN 5 Count Low Limit in DataSheet */
+ 0xBD, /* FAN 6 Count Low Limit in DataSheet */
+ 0xBF /* FAN 7 Count Low Limit in DataSheet */
+};
+#define W83792D_REG_FAN_CFG 0x84 /* FAN Configuration in DataSheet */
+static const u8 W83792D_REG_FAN_DIV[4] = {
+ 0x47, /* contains FAN2 and FAN1 Divisor */
+ 0x5B, /* contains FAN4 and FAN3 Divisor */
+ 0x5C, /* contains FAN6 and FAN5 Divisor */
+ 0x9E /* contains FAN7 Divisor. */
+};
+static const u8 W83792D_REG_PWM[7] = {
+ 0x81, /* FAN 1 Duty Cycle, be used to control */
+ 0x83, /* FAN 2 Duty Cycle, be used to control */
+ 0x94, /* FAN 3 Duty Cycle, be used to control */
+ 0xA3, /* FAN 4 Duty Cycle, be used to control */
+ 0xA4, /* FAN 5 Duty Cycle, be used to control */
+ 0xA5, /* FAN 6 Duty Cycle, be used to control */
+ 0xA6 /* FAN 7 Duty Cycle, be used to control */
+};
+#define W83792D_REG_BANK 0x4E
+#define W83792D_REG_TEMP2_CONFIG 0xC2
+#define W83792D_REG_TEMP3_CONFIG 0xCA
+
+static const u8 W83792D_REG_TEMP1[3] = {
+ 0x27, /* TEMP 1 in DataSheet */
+ 0x39, /* TEMP 1 Over in DataSheet */
+ 0x3A, /* TEMP 1 Hyst in DataSheet */
+};
+
+static const u8 W83792D_REG_TEMP_ADD[2][6] = {
+ { 0xC0, /* TEMP 2 in DataSheet */
+ 0xC1, /* TEMP 2(0.5 deg) in DataSheet */
+ 0xC5, /* TEMP 2 Over High part in DataSheet */
+ 0xC6, /* TEMP 2 Over Low part in DataSheet */
+ 0xC3, /* TEMP 2 Thyst High part in DataSheet */
+ 0xC4 }, /* TEMP 2 Thyst Low part in DataSheet */
+ { 0xC8, /* TEMP 3 in DataSheet */
+ 0xC9, /* TEMP 3(0.5 deg) in DataSheet */
+ 0xCD, /* TEMP 3 Over High part in DataSheet */
+ 0xCE, /* TEMP 3 Over Low part in DataSheet */
+ 0xCB, /* TEMP 3 Thyst High part in DataSheet */
+ 0xCC } /* TEMP 3 Thyst Low part in DataSheet */
+};
+
+static const u8 W83792D_REG_THERMAL[3] = {
+ 0x85, /* SmartFanI: Fan1 target value */
+ 0x86, /* SmartFanI: Fan2 target value */
+ 0x96 /* SmartFanI: Fan3 target value */
+};
+
+static const u8 W83792D_REG_TOLERANCE[3] = {
+ 0x87, /* (bit3-0)SmartFan Fan1 tolerance */
+ 0x87, /* (bit7-4)SmartFan Fan2 tolerance */
+ 0x97 /* (bit3-0)SmartFan Fan3 tolerance */
+};
+
+static const u8 W83792D_REG_POINTS[3][4] = {
+ { 0x85, /* SmartFanII: Fan1 temp point 1 */
+ 0xE3, /* SmartFanII: Fan1 temp point 2 */
+ 0xE4, /* SmartFanII: Fan1 temp point 3 */
+ 0xE5 }, /* SmartFanII: Fan1 temp point 4 */
+ { 0x86, /* SmartFanII: Fan2 temp point 1 */
+ 0xE6, /* SmartFanII: Fan2 temp point 2 */
+ 0xE7, /* SmartFanII: Fan2 temp point 3 */
+ 0xE8 }, /* SmartFanII: Fan2 temp point 4 */
+ { 0x96, /* SmartFanII: Fan3 temp point 1 */
+ 0xE9, /* SmartFanII: Fan3 temp point 2 */
+ 0xEA, /* SmartFanII: Fan3 temp point 3 */
+ 0xEB } /* SmartFanII: Fan3 temp point 4 */
+};
+
+static const u8 W83792D_REG_LEVELS[3][4] = {
+ { 0x88, /* (bit3-0) SmartFanII: Fan1 Non-Stop */
+ 0x88, /* (bit7-4) SmartFanII: Fan1 Level 1 */
+ 0xE0, /* (bit7-4) SmartFanII: Fan1 Level 2 */
+ 0xE0 }, /* (bit3-0) SmartFanII: Fan1 Level 3 */
+ { 0x89, /* (bit3-0) SmartFanII: Fan2 Non-Stop */
+ 0x89, /* (bit7-4) SmartFanII: Fan2 Level 1 */
+ 0xE1, /* (bit7-4) SmartFanII: Fan2 Level 2 */
+ 0xE1 }, /* (bit3-0) SmartFanII: Fan2 Level 3 */
+ { 0x98, /* (bit3-0) SmartFanII: Fan3 Non-Stop */
+ 0x98, /* (bit7-4) SmartFanII: Fan3 Level 1 */
+ 0xE2, /* (bit7-4) SmartFanII: Fan3 Level 2 */
+ 0xE2 } /* (bit3-0) SmartFanII: Fan3 Level 3 */
+};
+
+#define W83792D_REG_GPIO_EN 0x1A
+#define W83792D_REG_CONFIG 0x40
+#define W83792D_REG_VID_FANDIV 0x47
+#define W83792D_REG_CHIPID 0x49
+#define W83792D_REG_WCHIPID 0x58
+#define W83792D_REG_CHIPMAN 0x4F
+#define W83792D_REG_PIN 0x4B
+#define W83792D_REG_I2C_SUBADDR 0x4A
+
+#define W83792D_REG_ALARM1 0xA9 /* realtime status register1 */
+#define W83792D_REG_ALARM2 0xAA /* realtime status register2 */
+#define W83792D_REG_ALARM3 0xAB /* realtime status register3 */
+#define W83792D_REG_CHASSIS 0x42 /* Bit 5: Case Open status bit */
+#define W83792D_REG_CHASSIS_CLR 0x44 /* Bit 7: Case Open CLR_CHS/Reset bit */
+
+/* control in0/in1 's limit modifiability */
+#define W83792D_REG_VID_IN_B 0x17
+
+#define W83792D_REG_VBAT 0x5D
+#define W83792D_REG_I2C_ADDR 0x48
+
+/*
+ * Conversions. Rounding and limit checking is only done on the TO_REG
+ * variants. Note that you should be a bit careful with which arguments
+ * these macros are called: arguments may be evaluated more than once.
+ * Fixing this is just not worth it.
+ */
+#define IN_FROM_REG(nr, val) (((nr) <= 1) ? ((val) * 2) : \
+ ((((nr) == 6) || ((nr) == 7)) ? ((val) * 6) : ((val) * 4)))
+#define IN_TO_REG(nr, val) (((nr) <= 1) ? ((val) / 2) : \
+ ((((nr) == 6) || ((nr) == 7)) ? ((val) / 6) : ((val) / 4)))
+
+static inline u8
+FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
+ ((val) == 255 ? 0 : \
+ 1350000 / ((val) * (div))))
+
+/* for temp1 */
+#define TEMP1_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \
+ : (val)) / 1000, 0, 0xff))
+#define TEMP1_FROM_REG(val) (((val) & 0x80 ? (val)-0x100 : (val)) * 1000)
+/* for temp2 and temp3, because they need additional resolution */
+#define TEMP_ADD_FROM_REG(val1, val2) \
+ ((((val1) & 0x80 ? (val1)-0x100 \
+ : (val1)) * 1000) + ((val2 & 0x80) ? 500 : 0))
+#define TEMP_ADD_TO_REG_HIGH(val) \
+ (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 : (val)) / 1000, 0, 0xff))
+#define TEMP_ADD_TO_REG_LOW(val) ((val%1000) ? 0x80 : 0x00)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static inline u8
+DIV_TO_REG(long val)
+{
+ int i;
+ val = clamp_val(val, 1, 128) >> 1;
+ for (i = 0; i < 7; i++) {
+ if (val == 0)
+ break;
+ val >>= 1;
+ }
+ return (u8)i;
+}
+
+struct w83792d_data {
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 in[9]; /* Register value */
+ u8 in_max[9]; /* Register value */
+ u8 in_min[9]; /* Register value */
+ u16 low_bits; /* Additional resolution to voltage in6-0 */
+ u8 fan[7]; /* Register value */
+ u8 fan_min[7]; /* Register value */
+ u8 temp1[3]; /* current, over, thyst */
+ u8 temp_add[2][6]; /* Register value */
+ u8 fan_div[7]; /* Register encoding, shifted right */
+ u8 pwm[7]; /* The 7 PWM outputs */
+ u8 pwmenable[3];
+ u32 alarms; /* realtime status register encoding,combined */
+ u8 chassis; /* Chassis status */
+ u8 thermal_cruise[3]; /* Smart FanI: Fan1,2,3 target value */
+ u8 tolerance[3]; /* Fan1,2,3 tolerance(Smart Fan I/II) */
+ u8 sf2_points[3][4]; /* Smart FanII: Fan1,2,3 temperature points */
+ u8 sf2_levels[3][4]; /* Smart FanII: Fan1,2,3 duty cycle levels */
+};
+
+static int w83792d_probe(struct i2c_client *client);
+static int w83792d_detect(struct i2c_client *client,
+ struct i2c_board_info *info);
+static void w83792d_remove(struct i2c_client *client);
+static struct w83792d_data *w83792d_update_device(struct device *dev);
+
+#ifdef DEBUG
+static void w83792d_print_debug(struct w83792d_data *data, struct device *dev);
+#endif
+
+static void w83792d_init_client(struct i2c_client *client);
+
+static const struct i2c_device_id w83792d_id[] = {
+ { "w83792d", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, w83792d_id);
+
+static struct i2c_driver w83792d_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "w83792d",
+ },
+ .probe_new = w83792d_probe,
+ .remove = w83792d_remove,
+ .id_table = w83792d_id,
+ .detect = w83792d_detect,
+ .address_list = normal_i2c,
+};
+
+static inline long in_count_from_reg(int nr, struct w83792d_data *data)
+{
+ /* in7 and in8 do not have low bits, but the formula still works */
+ return (data->in[nr] << 2) | ((data->low_bits >> (2 * nr)) & 0x03);
+}
+
+/*
+ * The SMBus locks itself. The Winbond W83792D chip has a bank register,
+ * but the driver only accesses registers in bank 0, so we don't have
+ * to switch banks and lock access between switches.
+ */
+static inline int w83792d_read_value(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int
+w83792d_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* following are the sysfs callback functions */
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%ld\n",
+ IN_FROM_REG(nr, in_count_from_reg(nr, data)));
+}
+
+#define show_in_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct sensor_device_attribute *sensor_attr \
+ = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ struct w83792d_data *data = w83792d_update_device(dev); \
+ return sprintf(buf, "%ld\n", \
+ (long)(IN_FROM_REG(nr, data->reg[nr]) * 4)); \
+}
+
+show_in_reg(in_min);
+show_in_reg(in_max);
+
+#define store_in_reg(REG, reg) \
+static ssize_t store_in_##reg(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct sensor_device_attribute *sensor_attr \
+ = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83792d_data *data = i2c_get_clientdata(client); \
+ unsigned long val; \
+ int err = kstrtoul(buf, 10, &val); \
+ if (err) \
+ return err; \
+ mutex_lock(&data->update_lock); \
+ data->in_##reg[nr] = clamp_val(IN_TO_REG(nr, val) / 4, 0, 255); \
+ w83792d_write_value(client, W83792D_REG_IN_##REG[nr], \
+ data->in_##reg[nr]); \
+ mutex_unlock(&data->update_lock); \
+ \
+ return count; \
+}
+store_in_reg(MIN, min);
+store_in_reg(MAX, max);
+
+#define show_fan_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct sensor_device_attribute *sensor_attr \
+ = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index - 1; \
+ struct w83792d_data *data = w83792d_update_device(dev); \
+ return sprintf(buf, "%d\n", \
+ FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
+}
+
+show_fan_reg(fan);
+show_fan_reg(fan_min);
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index - 1;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ 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]));
+ w83792d_write_value(client, W83792D_REG_FAN_MIN[nr],
+ data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr - 1]));
+}
+
+/*
+ * 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t
+store_fan_div(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index - 1;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ unsigned long min;
+ /*u8 reg;*/
+ u8 fan_div_reg = 0;
+ u8 tmp_fan_div;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ /* Save fan_min */
+ mutex_lock(&data->update_lock);
+ min = FAN_FROM_REG(data->fan_min[nr],
+ DIV_FROM_REG(data->fan_div[nr]));
+
+ data->fan_div[nr] = DIV_TO_REG(val);
+
+ fan_div_reg = w83792d_read_value(client, W83792D_REG_FAN_DIV[nr >> 1]);
+ fan_div_reg &= (nr & 0x01) ? 0x8f : 0xf8;
+ tmp_fan_div = (nr & 0x01) ? (((data->fan_div[nr]) << 4) & 0x70)
+ : ((data->fan_div[nr]) & 0x07);
+ w83792d_write_value(client, W83792D_REG_FAN_DIV[nr >> 1],
+ fan_div_reg | tmp_fan_div);
+
+ /* Restore fan_min */
+ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* read/write the temperature1, includes measured value and limits */
+
+static ssize_t show_temp1(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp1[nr]));
+}
+
+static ssize_t store_temp1(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp1[nr] = TEMP1_TO_REG(val);
+ w83792d_write_value(client, W83792D_REG_TEMP1[nr],
+ data->temp1[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* read/write the temperature2-3, includes measured value and limits */
+
+static ssize_t show_temp23(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr
+ = to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%ld\n",
+ (long)TEMP_ADD_FROM_REG(data->temp_add[nr][index],
+ data->temp_add[nr][index+1]));
+}
+
+static ssize_t store_temp23(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr
+ = to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp_add[nr][index] = TEMP_ADD_TO_REG_HIGH(val);
+ data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val);
+ w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index],
+ data->temp_add[nr][index]);
+ w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index+1],
+ data->temp_add[nr][index+1]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* get realtime status of all sensors items: voltage, temp, fan */
+static ssize_t
+alarms_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+static ssize_t show_alarm(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%d\n", (data->alarms >> nr) & 1);
+}
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%d\n", (data->pwm[nr] & 0x0f) << 4);
+}
+
+static ssize_t
+show_pwmenable(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index - 1;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ long pwm_enable_tmp = 1;
+
+ switch (data->pwmenable[nr]) {
+ case 0:
+ pwm_enable_tmp = 1; /* manual mode */
+ break;
+ case 1:
+ pwm_enable_tmp = 3; /*thermal cruise/Smart Fan I */
+ break;
+ case 2:
+ pwm_enable_tmp = 2; /* Smart Fan II */
+ break;
+ }
+
+ return sprintf(buf, "%ld\n", pwm_enable_tmp);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ val = clamp_val(val, 0, 255) >> 4;
+
+ mutex_lock(&data->update_lock);
+ val |= w83792d_read_value(client, W83792D_REG_PWM[nr]) & 0xf0;
+ data->pwm[nr] = val;
+ w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+store_pwmenable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index - 1;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ u8 fan_cfg_tmp, cfg1_tmp, cfg2_tmp, cfg3_tmp, cfg4_tmp;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val < 1 || val > 3)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case 1:
+ data->pwmenable[nr] = 0; /* manual mode */
+ break;
+ case 2:
+ data->pwmenable[nr] = 2; /* Smart Fan II */
+ break;
+ case 3:
+ data->pwmenable[nr] = 1; /* thermal cruise/Smart Fan I */
+ break;
+ }
+ cfg1_tmp = data->pwmenable[0];
+ cfg2_tmp = (data->pwmenable[1]) << 2;
+ cfg3_tmp = (data->pwmenable[2]) << 4;
+ cfg4_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG) & 0xc0;
+ fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp;
+ w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_pwm_mode(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm[nr] >> 7);
+}
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = w83792d_read_value(client, W83792D_REG_PWM[nr]);
+ if (val) { /* PWM mode */
+ data->pwm[nr] |= 0x80;
+ } else { /* DC mode */
+ data->pwm[nr] &= 0x7f;
+ }
+ w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+intrusion0_alarm_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%d\n", data->chassis);
+}
+
+static ssize_t
+intrusion0_alarm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ u8 reg;
+
+ if (kstrtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
+
+ 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 = false; /* Force cache refresh */
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* For Smart Fan I / Thermal Cruise */
+static ssize_t
+show_thermal_cruise(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%ld\n", (long)data->thermal_cruise[nr-1]);
+}
+
+static ssize_t
+store_thermal_cruise(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index - 1;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ u8 target_tmp = 0, target_mask = 0;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ target_tmp = val;
+ target_tmp = target_tmp & 0x7f;
+ mutex_lock(&data->update_lock);
+ target_mask = w83792d_read_value(client,
+ W83792D_REG_THERMAL[nr]) & 0x80;
+ data->thermal_cruise[nr] = clamp_val(target_tmp, 0, 255);
+ w83792d_write_value(client, W83792D_REG_THERMAL[nr],
+ (data->thermal_cruise[nr]) | target_mask);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* For Smart Fan I/Thermal Cruise and Smart Fan II */
+static ssize_t
+show_tolerance(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%ld\n", (long)data->tolerance[nr-1]);
+}
+
+static ssize_t
+store_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index - 1;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ u8 tol_tmp, tol_mask;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ tol_mask = w83792d_read_value(client,
+ W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0);
+ tol_tmp = clamp_val(val, 0, 15);
+ tol_tmp &= 0x0f;
+ data->tolerance[nr] = tol_tmp;
+ if (nr == 1)
+ tol_tmp <<= 4;
+ w83792d_write_value(client, W83792D_REG_TOLERANCE[nr],
+ tol_mask | tol_tmp);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* For Smart Fan II */
+static ssize_t
+show_sf2_point(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr
+ = to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%ld\n", (long)data->sf2_points[index-1][nr-1]);
+}
+
+static ssize_t
+store_sf2_point(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr
+ = to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr - 1;
+ int index = sensor_attr->index - 1;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ u8 mask_tmp = 0;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->sf2_points[index][nr] = clamp_val(val, 0, 127);
+ mask_tmp = w83792d_read_value(client,
+ W83792D_REG_POINTS[index][nr]) & 0x80;
+ w83792d_write_value(client, W83792D_REG_POINTS[index][nr],
+ mask_tmp|data->sf2_points[index][nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_sf2_level(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr
+ = to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83792d_data *data = w83792d_update_device(dev);
+ return sprintf(buf, "%d\n",
+ (((data->sf2_levels[index-1][nr]) * 100) / 15));
+}
+
+static ssize_t
+store_sf2_level(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr
+ = to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index - 1;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ u8 mask_tmp = 0, level_tmp = 0;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->sf2_levels[index][nr] = clamp_val((val * 15) / 100, 0, 15);
+ mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr])
+ & ((nr == 3) ? 0xf0 : 0x0f);
+ if (nr == 3)
+ level_tmp = data->sf2_levels[index][nr];
+ else
+ level_tmp = data->sf2_levels[index][nr] << 4;
+ w83792d_write_value(client, W83792D_REG_LEVELS[index][nr],
+ level_tmp | mask_tmp);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+
+static int
+w83792d_detect_subclients(struct i2c_client *new_client)
+{
+ int i, id;
+ int address = new_client->addr;
+ u8 val;
+ struct i2c_adapter *adapter = new_client->adapter;
+
+ id = i2c_adapter_id(adapter);
+ if (force_subclients[0] == id && force_subclients[1] == address) {
+ for (i = 2; i <= 3; i++) {
+ if (force_subclients[i] < 0x48 ||
+ force_subclients[i] > 0x4f) {
+ dev_err(&new_client->dev,
+ "invalid subclient address %d; must be 0x48-0x4f\n",
+ force_subclients[i]);
+ return -ENODEV;
+ }
+ }
+ w83792d_write_value(new_client, W83792D_REG_I2C_SUBADDR,
+ (force_subclients[2] & 0x07) |
+ ((force_subclients[3] & 0x07) << 4));
+ }
+
+ val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR);
+
+ 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;
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 0);
+static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 3);
+static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 4);
+static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 5);
+static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 6);
+static SENSOR_DEVICE_ATTR(in7_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 7);
+static SENSOR_DEVICE_ATTR(in8_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 8);
+static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 0);
+static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 1);
+static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 2);
+static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 3);
+static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 4);
+static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 5);
+static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 6);
+static SENSOR_DEVICE_ATTR(in7_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 7);
+static SENSOR_DEVICE_ATTR(in8_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 8);
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp23,
+ store_temp23, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp23,
+ store_temp23, 1, 2);
+static SENSOR_DEVICE_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 0, 4);
+static SENSOR_DEVICE_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 1, 4);
+static DEVICE_ATTR_RO(alarms);
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_alarm, NULL, 15);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 19);
+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 20);
+static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 21);
+static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22);
+static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 23);
+static DEVICE_ATTR_RW(intrusion0_alarm);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4);
+static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 5);
+static SENSOR_DEVICE_ATTR(pwm7, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 6);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 1);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 2);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 3);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 1);
+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 2);
+static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 3);
+static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 4);
+static SENSOR_DEVICE_ATTR(pwm6_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 5);
+static SENSOR_DEVICE_ATTR(pwm7_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 6);
+static SENSOR_DEVICE_ATTR(tolerance1, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 1);
+static SENSOR_DEVICE_ATTR(tolerance2, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 2);
+static SENSOR_DEVICE_ATTR(tolerance3, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 3);
+static SENSOR_DEVICE_ATTR(thermal_cruise1, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 1);
+static SENSOR_DEVICE_ATTR(thermal_cruise2, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 2);
+static SENSOR_DEVICE_ATTR(thermal_cruise3, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 3);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 5);
+static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 2);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 3);
+static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 4);
+static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 5);
+static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 6);
+static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 7);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 1);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 2);
+static SENSOR_DEVICE_ATTR(fan3_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 3);
+static SENSOR_DEVICE_ATTR(fan4_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 4);
+static SENSOR_DEVICE_ATTR(fan5_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 5);
+static SENSOR_DEVICE_ATTR(fan6_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 6);
+static SENSOR_DEVICE_ATTR(fan7_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 7);
+
+static struct attribute *w83792d_attributes_fan[4][7] = {
+ {
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ &sensor_dev_attr_pwm4_mode.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_div.dev_attr.attr,
+ &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_mode.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_div.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm6_mode.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_div.dev_attr.attr,
+ &sensor_dev_attr_fan7_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm7.dev_attr.attr,
+ &sensor_dev_attr_pwm7_mode.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group w83792d_group_fan[4] = {
+ { .attrs = w83792d_attributes_fan[0] },
+ { .attrs = w83792d_attributes_fan[1] },
+ { .attrs = w83792d_attributes_fan[2] },
+ { .attrs = w83792d_attributes_fan[3] },
+};
+
+static struct attribute *w83792d_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.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_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_intrusion0_alarm.attr,
+ &sensor_dev_attr_tolerance1.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise1.dev_attr.attr,
+ &sensor_dev_attr_tolerance2.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise2.dev_attr.attr,
+ &sensor_dev_attr_tolerance3.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan3.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group w83792d_group = {
+ .attrs = w83792d_attributes,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int
+w83792d_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int val1, val2;
+ unsigned short address = client->addr;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80)
+ return -ENODEV;
+
+ val1 = w83792d_read_value(client, W83792D_REG_BANK);
+ val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
+ /* Check for Winbond ID if in bank 0 */
+ if (!(val1 & 0x07)) { /* is Bank0 */
+ if ((!(val1 & 0x80) && val2 != 0xa3) ||
+ ((val1 & 0x80) && val2 != 0x5c))
+ return -ENODEV;
+ }
+ /*
+ * If Winbond chip, address of chip and W83792D_REG_I2C_ADDR
+ * should match
+ */
+ if (w83792d_read_value(client, W83792D_REG_I2C_ADDR) != address)
+ return -ENODEV;
+
+ /* Put it now into bank 0 and Vendor ID High Byte */
+ w83792d_write_value(client,
+ W83792D_REG_BANK,
+ (w83792d_read_value(client,
+ W83792D_REG_BANK) & 0x78) | 0x80);
+
+ /* Determine the chip type. */
+ val1 = w83792d_read_value(client, W83792D_REG_WCHIPID);
+ val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
+ if (val1 != 0x7a || val2 != 0x5c)
+ return -ENODEV;
+
+ strscpy(info->type, "w83792d", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int
+w83792d_probe(struct i2c_client *client)
+{
+ struct w83792d_data *data;
+ struct device *dev = &client->dev;
+ int i, val1, err;
+
+ data = devm_kzalloc(dev, sizeof(struct w83792d_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ err = w83792d_detect_subclients(client);
+ if (err)
+ return err;
+
+ /* Initialize the chip */
+ w83792d_init_client(client);
+
+ /* A few vars need to be filled upon startup */
+ for (i = 0; i < 7; i++) {
+ data->fan_min[i] = w83792d_read_value(client,
+ W83792D_REG_FAN_MIN[i]);
+ }
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&dev->kobj, &w83792d_group);
+ if (err)
+ return err;
+
+ /*
+ * Read GPIO enable register to check if pins for fan 4,5 are used as
+ * GPIO
+ */
+ val1 = w83792d_read_value(client, W83792D_REG_GPIO_EN);
+
+ if (!(val1 & 0x40)) {
+ err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[0]);
+ if (err)
+ goto exit_remove_files;
+ }
+
+ if (!(val1 & 0x20)) {
+ err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[1]);
+ if (err)
+ goto exit_remove_files;
+ }
+
+ val1 = w83792d_read_value(client, W83792D_REG_PIN);
+ if (val1 & 0x40) {
+ err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[2]);
+ if (err)
+ goto exit_remove_files;
+ }
+
+ if (val1 & 0x04) {
+ err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[3]);
+ if (err)
+ goto exit_remove_files;
+ }
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ sysfs_remove_group(&dev->kobj, &w83792d_group);
+ for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++)
+ sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]);
+ return err;
+}
+
+static void
+w83792d_remove(struct i2c_client *client)
+{
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ int i;
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &w83792d_group);
+ for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++)
+ sysfs_remove_group(&client->dev.kobj,
+ &w83792d_group_fan[i]);
+}
+
+static void
+w83792d_init_client(struct i2c_client *client)
+{
+ u8 temp2_cfg, temp3_cfg, vid_in_b;
+
+ if (init)
+ w83792d_write_value(client, W83792D_REG_CONFIG, 0x80);
+
+ /*
+ * Clear the bit6 of W83792D_REG_VID_IN_B(set it into 0):
+ * W83792D_REG_VID_IN_B bit6 = 0: the high/low limit of
+ * vin0/vin1 can be modified by user;
+ * W83792D_REG_VID_IN_B bit6 = 1: the high/low limit of
+ * vin0/vin1 auto-updated, can NOT be modified by user.
+ */
+ vid_in_b = w83792d_read_value(client, W83792D_REG_VID_IN_B);
+ w83792d_write_value(client, W83792D_REG_VID_IN_B,
+ vid_in_b & 0xbf);
+
+ temp2_cfg = w83792d_read_value(client, W83792D_REG_TEMP2_CONFIG);
+ temp3_cfg = w83792d_read_value(client, W83792D_REG_TEMP3_CONFIG);
+ w83792d_write_value(client, W83792D_REG_TEMP2_CONFIG,
+ temp2_cfg & 0xe6);
+ w83792d_write_value(client, W83792D_REG_TEMP3_CONFIG,
+ temp3_cfg & 0xe6);
+
+ /* Start monitoring */
+ w83792d_write_value(client, W83792D_REG_CONFIG,
+ (w83792d_read_value(client,
+ W83792D_REG_CONFIG) & 0xf7)
+ | 0x01);
+}
+
+static struct w83792d_data *w83792d_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83792d_data *data = i2c_get_clientdata(client);
+ int i, j;
+ u8 reg_array_tmp[4], reg_tmp;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after
+ (jiffies - data->last_updated, (unsigned long) (HZ * 3))
+ || time_before(jiffies, data->last_updated) || !data->valid) {
+ dev_dbg(dev, "Starting device update\n");
+
+ /* Update the voltages measured value and limits */
+ for (i = 0; i < 9; i++) {
+ data->in[i] = w83792d_read_value(client,
+ W83792D_REG_IN[i]);
+ data->in_max[i] = w83792d_read_value(client,
+ W83792D_REG_IN_MAX[i]);
+ data->in_min[i] = w83792d_read_value(client,
+ W83792D_REG_IN_MIN[i]);
+ }
+ data->low_bits = w83792d_read_value(client,
+ W83792D_REG_LOW_BITS1) +
+ (w83792d_read_value(client,
+ W83792D_REG_LOW_BITS2) << 8);
+ for (i = 0; i < 7; i++) {
+ /* Update the Fan measured value and limits */
+ data->fan[i] = w83792d_read_value(client,
+ W83792D_REG_FAN[i]);
+ data->fan_min[i] = w83792d_read_value(client,
+ W83792D_REG_FAN_MIN[i]);
+ /* Update the PWM/DC Value and PWM/DC flag */
+ data->pwm[i] = w83792d_read_value(client,
+ W83792D_REG_PWM[i]);
+ }
+
+ reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG);
+ data->pwmenable[0] = reg_tmp & 0x03;
+ data->pwmenable[1] = (reg_tmp>>2) & 0x03;
+ data->pwmenable[2] = (reg_tmp>>4) & 0x03;
+
+ for (i = 0; i < 3; i++) {
+ data->temp1[i] = w83792d_read_value(client,
+ W83792D_REG_TEMP1[i]);
+ }
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 6; j++) {
+ data->temp_add[i][j] = w83792d_read_value(
+ client, W83792D_REG_TEMP_ADD[i][j]);
+ }
+ }
+
+ /* Update the Fan Divisor */
+ for (i = 0; i < 4; i++) {
+ reg_array_tmp[i] = w83792d_read_value(client,
+ W83792D_REG_FAN_DIV[i]);
+ }
+ data->fan_div[0] = reg_array_tmp[0] & 0x07;
+ data->fan_div[1] = (reg_array_tmp[0] >> 4) & 0x07;
+ data->fan_div[2] = reg_array_tmp[1] & 0x07;
+ data->fan_div[3] = (reg_array_tmp[1] >> 4) & 0x07;
+ data->fan_div[4] = reg_array_tmp[2] & 0x07;
+ data->fan_div[5] = (reg_array_tmp[2] >> 4) & 0x07;
+ data->fan_div[6] = reg_array_tmp[3] & 0x07;
+
+ /* Update the realtime status */
+ data->alarms = w83792d_read_value(client, W83792D_REG_ALARM1) +
+ (w83792d_read_value(client, W83792D_REG_ALARM2) << 8) +
+ (w83792d_read_value(client, W83792D_REG_ALARM3) << 16);
+
+ /* Update CaseOpen status and it's CLR_CHS. */
+ data->chassis = (w83792d_read_value(client,
+ W83792D_REG_CHASSIS) >> 5) & 0x01;
+
+ /* Update Thermal Cruise/Smart Fan I target value */
+ for (i = 0; i < 3; i++) {
+ data->thermal_cruise[i] =
+ w83792d_read_value(client,
+ W83792D_REG_THERMAL[i]) & 0x7f;
+ }
+
+ /* Update Smart Fan I/II tolerance */
+ reg_tmp = w83792d_read_value(client, W83792D_REG_TOLERANCE[0]);
+ data->tolerance[0] = reg_tmp & 0x0f;
+ data->tolerance[1] = (reg_tmp >> 4) & 0x0f;
+ data->tolerance[2] = w83792d_read_value(client,
+ W83792D_REG_TOLERANCE[2]) & 0x0f;
+
+ /* Update Smart Fan II temperature points */
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 4; j++) {
+ data->sf2_points[i][j]
+ = w83792d_read_value(client,
+ W83792D_REG_POINTS[i][j]) & 0x7f;
+ }
+ }
+
+ /* Update Smart Fan II duty cycle levels */
+ for (i = 0; i < 3; i++) {
+ reg_tmp = w83792d_read_value(client,
+ W83792D_REG_LEVELS[i][0]);
+ data->sf2_levels[i][0] = reg_tmp & 0x0f;
+ data->sf2_levels[i][1] = (reg_tmp >> 4) & 0x0f;
+ reg_tmp = w83792d_read_value(client,
+ W83792D_REG_LEVELS[i][2]);
+ data->sf2_levels[i][2] = (reg_tmp >> 4) & 0x0f;
+ data->sf2_levels[i][3] = reg_tmp & 0x0f;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+#ifdef DEBUG
+ w83792d_print_debug(data, dev);
+#endif
+
+ return data;
+}
+
+#ifdef DEBUG
+static void w83792d_print_debug(struct w83792d_data *data, struct device *dev)
+{
+ int i = 0, j = 0;
+ dev_dbg(dev, "==========The following is the debug message...========\n");
+ dev_dbg(dev, "9 set of Voltages: =====>\n");
+ for (i = 0; i < 9; i++) {
+ dev_dbg(dev, "vin[%d] is: 0x%x\n", i, data->in[i]);
+ dev_dbg(dev, "vin[%d] max is: 0x%x\n", i, data->in_max[i]);
+ dev_dbg(dev, "vin[%d] min is: 0x%x\n", i, data->in_min[i]);
+ }
+ dev_dbg(dev, "Low Bit1 is: 0x%x\n", data->low_bits & 0xff);
+ dev_dbg(dev, "Low Bit2 is: 0x%x\n", data->low_bits >> 8);
+ dev_dbg(dev, "7 set of Fan Counts and Duty Cycles: =====>\n");
+ for (i = 0; i < 7; i++) {
+ dev_dbg(dev, "fan[%d] is: 0x%x\n", i, data->fan[i]);
+ dev_dbg(dev, "fan[%d] min is: 0x%x\n", i, data->fan_min[i]);
+ dev_dbg(dev, "pwm[%d] is: 0x%x\n", i, data->pwm[i]);
+ }
+ dev_dbg(dev, "3 set of Temperatures: =====>\n");
+ for (i = 0; i < 3; i++)
+ dev_dbg(dev, "temp1[%d] is: 0x%x\n", i, data->temp1[i]);
+
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 6; j++) {
+ dev_dbg(dev, "temp_add[%d][%d] is: 0x%x\n", i, j,
+ data->temp_add[i][j]);
+ }
+ }
+
+ for (i = 0; i < 7; i++)
+ dev_dbg(dev, "fan_div[%d] is: 0x%x\n", i, data->fan_div[i]);
+
+ dev_dbg(dev, "==========End of the debug message...================\n");
+ dev_dbg(dev, "\n");
+}
+#endif
+
+module_i2c_driver(w83792d_driver);
+
+MODULE_AUTHOR("Shane Huang (Winbond)");
+MODULE_DESCRIPTION("W83792AD/D driver for linux-2.6");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
new file mode 100644
index 000000000..a4926d907
--- /dev/null
+++ b/drivers/hwmon/w83793.c
@@ -0,0 +1,2144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w83793.c - Linux kernel driver for hardware monitoring
+ * Copyright (C) 2006 Winbond Electronics Corp.
+ * Yuan Mu
+ * Rudolf Marek <r.marek@assembler.cz>
+ * Copyright (C) 2009-2010 Sven Anders <anders@anduras.de>, ANDURAS AG.
+ * Watchdog driver part
+ * (Based partially on fschmd driver,
+ * Copyright 2007-2008 by Hans de Goede)
+ */
+
+/*
+ * Supports following chips:
+ *
+ * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ * w83793 10 12 8 6 0x7b 0x5ca3 yes no
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kref.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/jiffies.h>
+
+/* Default values */
+#define WATCHDOG_TIMEOUT 2 /* 2 minute default timeout */
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
+ I2C_CLIENT_END };
+
+/* Insmod parameters */
+
+static unsigned short force_subclients[4];
+module_param_array(force_subclients, short, NULL, 0);
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+static bool reset;
+module_param(reset, bool, 0);
+MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
+
+static int timeout = WATCHDOG_TIMEOUT; /* default timeout in minutes */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in minutes. 2<= 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) ")");
+
+/*
+ * Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved
+ * as ID, Bank Select registers
+ */
+#define W83793_REG_BANKSEL 0x00
+#define W83793_REG_VENDORID 0x0d
+#define W83793_REG_CHIPID 0x0e
+#define W83793_REG_DEVICEID 0x0f
+
+#define W83793_REG_CONFIG 0x40
+#define W83793_REG_MFC 0x58
+#define W83793_REG_FANIN_CTRL 0x5c
+#define W83793_REG_FANIN_SEL 0x5d
+#define W83793_REG_I2C_ADDR 0x0b
+#define W83793_REG_I2C_SUBADDR 0x0c
+#define W83793_REG_VID_INA 0x05
+#define W83793_REG_VID_INB 0x06
+#define W83793_REG_VID_LATCHA 0x07
+#define W83793_REG_VID_LATCHB 0x08
+#define W83793_REG_VID_CTRL 0x59
+
+#define W83793_REG_WDT_LOCK 0x01
+#define W83793_REG_WDT_ENABLE 0x02
+#define W83793_REG_WDT_STATUS 0x03
+#define W83793_REG_WDT_TIMEOUT 0x04
+
+static u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f };
+
+#define TEMP_READ 0
+#define TEMP_CRIT 1
+#define TEMP_CRIT_HYST 2
+#define TEMP_WARN 3
+#define TEMP_WARN_HYST 4
+/*
+ * only crit and crit_hyst affect real-time alarm status
+ * current crit crit_hyst warn warn_hyst
+ */
+static u16 W83793_REG_TEMP[][5] = {
+ {0x1c, 0x78, 0x79, 0x7a, 0x7b},
+ {0x1d, 0x7c, 0x7d, 0x7e, 0x7f},
+ {0x1e, 0x80, 0x81, 0x82, 0x83},
+ {0x1f, 0x84, 0x85, 0x86, 0x87},
+ {0x20, 0x88, 0x89, 0x8a, 0x8b},
+ {0x21, 0x8c, 0x8d, 0x8e, 0x8f},
+};
+
+#define W83793_REG_TEMP_LOW_BITS 0x22
+
+#define W83793_REG_BEEP(index) (0x53 + (index))
+#define W83793_REG_ALARM(index) (0x4b + (index))
+
+#define W83793_REG_CLR_CHASSIS 0x4a /* SMI MASK4 */
+#define W83793_REG_IRQ_CTRL 0x50
+#define W83793_REG_OVT_CTRL 0x51
+#define W83793_REG_OVT_BEEP 0x52
+
+#define IN_READ 0
+#define IN_MAX 1
+#define IN_LOW 2
+static const u16 W83793_REG_IN[][3] = {
+ /* Current, High, Low */
+ {0x10, 0x60, 0x61}, /* Vcore A */
+ {0x11, 0x62, 0x63}, /* Vcore B */
+ {0x12, 0x64, 0x65}, /* Vtt */
+ {0x14, 0x6a, 0x6b}, /* VSEN1 */
+ {0x15, 0x6c, 0x6d}, /* VSEN2 */
+ {0x16, 0x6e, 0x6f}, /* +3VSEN */
+ {0x17, 0x70, 0x71}, /* +12VSEN */
+ {0x18, 0x72, 0x73}, /* 5VDD */
+ {0x19, 0x74, 0x75}, /* 5VSB */
+ {0x1a, 0x76, 0x77}, /* VBAT */
+};
+
+/* Low Bits of Vcore A/B Vtt Read/High/Low */
+static const u16 W83793_REG_IN_LOW_BITS[] = { 0x1b, 0x68, 0x69 };
+static u8 scale_in[] = { 2, 2, 2, 16, 16, 16, 8, 24, 24, 16 };
+static u8 scale_in_add[] = { 0, 0, 0, 0, 0, 0, 0, 150, 150, 0 };
+
+#define W83793_REG_FAN(index) (0x23 + 2 * (index)) /* High byte */
+#define W83793_REG_FAN_MIN(index) (0x90 + 2 * (index)) /* High byte */
+
+#define W83793_REG_PWM_DEFAULT 0xb2
+#define W83793_REG_PWM_ENABLE 0x207
+#define W83793_REG_PWM_UPTIME 0xc3 /* Unit in 0.1 second */
+#define W83793_REG_PWM_DOWNTIME 0xc4 /* Unit in 0.1 second */
+#define W83793_REG_TEMP_CRITICAL 0xc5
+
+#define PWM_DUTY 0
+#define PWM_START 1
+#define PWM_NONSTOP 2
+#define PWM_STOP_TIME 3
+#define W83793_REG_PWM(index, nr) (((nr) == 0 ? 0xb3 : \
+ (nr) == 1 ? 0x220 : 0x218) + (index))
+
+/* bit field, fan1 is bit0, fan2 is bit1 ... */
+#define W83793_REG_TEMP_FAN_MAP(index) (0x201 + (index))
+#define W83793_REG_TEMP_TOL(index) (0x208 + (index))
+#define W83793_REG_TEMP_CRUISE(index) (0x210 + (index))
+#define W83793_REG_PWM_STOP_TIME(index) (0x228 + (index))
+#define W83793_REG_SF2_TEMP(index, nr) (0x230 + ((index) << 4) + (nr))
+#define W83793_REG_SF2_PWM(index, nr) (0x238 + ((index) << 4) + (nr))
+
+static inline unsigned long FAN_FROM_REG(u16 val)
+{
+ if ((val >= 0xfff) || (val == 0))
+ return 0;
+ return 1350000UL / val;
+}
+
+static inline u16 FAN_TO_REG(long rpm)
+{
+ if (rpm <= 0)
+ return 0x0fff;
+ return clamp_val((1350000 + (rpm >> 1)) / rpm, 1, 0xffe);
+}
+
+static inline unsigned long TIME_FROM_REG(u8 reg)
+{
+ return reg * 100;
+}
+
+static inline u8 TIME_TO_REG(unsigned long val)
+{
+ return clamp_val((val + 50) / 100, 0, 0xff);
+}
+
+static inline long TEMP_FROM_REG(s8 reg)
+{
+ return reg * 1000;
+}
+
+static inline s8 TEMP_TO_REG(long val, s8 min, s8 max)
+{
+ return clamp_val((val + (val < 0 ? -500 : 500)) / 1000, min, max);
+}
+
+struct w83793_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ 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
+ */
+
+ u8 bank;
+ u8 vrm;
+ u8 vid[2];
+ u8 in[10][3]; /* Register value, read/high/low */
+ u8 in_low_bits[3]; /* Additional resolution for VCore A/B Vtt */
+
+ u16 has_fan; /* Only fan1- fan5 has own pins */
+ u16 fan[12]; /* Register value combine */
+ u16 fan_min[12]; /* Register value combine */
+
+ s8 temp[6][5]; /* current, crit, crit_hyst,warn, warn_hyst */
+ u8 temp_low_bits; /* Additional resolution TD1-TD4 */
+ u8 temp_mode[2]; /* byte 0: Temp D1-D4 mode each has 2 bits
+ * byte 1: Temp R1,R2 mode, each has 1 bit
+ */
+ u8 temp_critical; /* If reached all fan will be at full speed */
+ u8 temp_fan_map[6]; /* Temp controls which pwm fan, bit field */
+
+ u8 has_pwm;
+ u8 has_temp;
+ u8 has_vid;
+ u8 pwm_enable; /* Register value, each Temp has 1 bit */
+ u8 pwm_uptime; /* Register value */
+ u8 pwm_downtime; /* Register value */
+ u8 pwm_default; /* All fan default pwm, next poweron valid */
+ u8 pwm[8][3]; /* Register value */
+ u8 pwm_stop_time[8];
+ u8 temp_cruise[6];
+
+ u8 alarms[5]; /* realtime status registers */
+ u8 beeps[5];
+ u8 beep_enable;
+ u8 tolerance[3]; /* Temp tolerance(Smart Fan I/II) */
+ u8 sf2_pwm[6][7]; /* Smart FanII: Fan duty cycle */
+ u8 sf2_temp[6][7]; /* Smart FanII: Temp level point */
+
+ /* watchdog */
+ struct i2c_client *client;
+ struct mutex watchdog_lock;
+ struct list_head list; /* member of the watchdog_data_list */
+ struct kref kref;
+ struct miscdevice watchdog_miscdev;
+ unsigned long watchdog_is_open;
+ char watchdog_expect_close;
+ char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
+ unsigned int watchdog_caused_reboot;
+ int watchdog_timeout; /* watchdog timeout in minutes */
+};
+
+/*
+ * Somewhat ugly :( global data pointer list with all devices, so that
+ * we can find our device data as when using misc_register. There is no
+ * other method to get to one's device data from the open file-op and
+ * for usage in the reboot notifier callback.
+ */
+static LIST_HEAD(watchdog_data_list);
+
+/* Note this lock not only protect list access, but also data.kref access */
+static DEFINE_MUTEX(watchdog_data_mutex);
+
+/*
+ * Release our data struct when we're detached from the i2c client *and* all
+ * references to our watchdog device are released
+ */
+static void w83793_release_resources(struct kref *ref)
+{
+ struct w83793_data *data = container_of(ref, struct w83793_data, kref);
+ kfree(data);
+}
+
+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);
+static int w83793_detect(struct i2c_client *client,
+ struct i2c_board_info *info);
+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);
+
+static const struct i2c_device_id w83793_id[] = {
+ { "w83793", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, w83793_id);
+
+static struct i2c_driver w83793_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "w83793",
+ },
+ .probe_new = w83793_probe,
+ .remove = w83793_remove,
+ .id_table = w83793_id,
+ .detect = w83793_detect,
+ .address_list = normal_i2c,
+};
+
+static ssize_t
+vrm_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83793_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t
+show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83793_data *data = w83793_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid[index], data->vrm));
+}
+
+static ssize_t
+vrm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct w83793_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;
+}
+
+#define ALARM_STATUS 0
+#define BEEP_ENABLE 1
+static ssize_t
+show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83793_data *data = w83793_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index >> 3;
+ int bit = sensor_attr->index & 0x07;
+ u8 val;
+
+ if (nr == ALARM_STATUS) {
+ val = (data->alarms[index] >> (bit)) & 1;
+ } else { /* BEEP_ENABLE */
+ val = (data->beeps[index] >> (bit)) & 1;
+ }
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t
+store_beep(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index >> 3;
+ int shift = sensor_attr->index & 0x07;
+ u8 beep_bit = 1 << shift;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->beeps[index] = w83793_read_value(client, W83793_REG_BEEP(index));
+ data->beeps[index] &= ~beep_bit;
+ data->beeps[index] |= val << shift;
+ w83793_write_value(client, W83793_REG_BEEP(index), data->beeps[index]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_beep_enable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83793_data *data = w83793_update_device(dev);
+ return sprintf(buf, "%u\n", (data->beep_enable >> 1) & 0x01);
+}
+
+static ssize_t
+store_beep_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP)
+ & 0xfd;
+ data->beep_enable |= val << 1;
+ w83793_write_value(client, W83793_REG_OVT_BEEP, data->beep_enable);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* Write 0 to clear chassis alarm */
+static ssize_t
+store_chassis_clear(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ u8 reg;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val)
+ return -EINVAL;
+
+ 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 = false; /* Force cache refresh */
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+#define FAN_INPUT 0
+#define FAN_MIN 1
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83793_data *data = w83793_update_device(dev);
+ u16 val;
+
+ if (nr == FAN_INPUT)
+ val = data->fan[index] & 0x0fff;
+ else
+ val = data->fan_min[index] & 0x0fff;
+
+ return sprintf(buf, "%lu\n", FAN_FROM_REG(val));
+}
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ val = FAN_TO_REG(val);
+
+ mutex_lock(&data->update_lock);
+ data->fan_min[index] = val;
+ w83793_write_value(client, W83793_REG_FAN_MIN(index),
+ (val >> 8) & 0xff);
+ w83793_write_value(client, W83793_REG_FAN_MIN(index) + 1, val & 0xff);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ struct w83793_data *data = w83793_update_device(dev);
+ u16 val;
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+
+ if (nr == PWM_STOP_TIME)
+ val = TIME_FROM_REG(data->pwm_stop_time[index]);
+ else
+ val = (data->pwm[index][nr] & 0x3f) << 2;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if (nr == PWM_STOP_TIME) {
+ val = TIME_TO_REG(val);
+ data->pwm_stop_time[index] = val;
+ w83793_write_value(client, W83793_REG_PWM_STOP_TIME(index),
+ val);
+ } else {
+ val = clamp_val(val, 0, 0xff) >> 2;
+ data->pwm[index][nr] =
+ w83793_read_value(client, W83793_REG_PWM(index, nr)) & 0xc0;
+ data->pwm[index][nr] |= val;
+ w83793_write_value(client, W83793_REG_PWM(index, nr),
+ data->pwm[index][nr]);
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83793_data *data = w83793_update_device(dev);
+ long temp = TEMP_FROM_REG(data->temp[index][nr]);
+
+ if (nr == TEMP_READ && index < 4) { /* Only TD1-TD4 have low bits */
+ int low = ((data->temp_low_bits >> (index * 2)) & 0x03) * 250;
+ temp += temp > 0 ? low : -low;
+ }
+ return sprintf(buf, "%ld\n", temp);
+}
+
+static ssize_t
+store_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ long tmp;
+ int err;
+
+ err = kstrtol(buf, 10, &tmp);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp[index][nr] = TEMP_TO_REG(tmp, -128, 127);
+ w83793_write_value(client, W83793_REG_TEMP[index][nr],
+ data->temp[index][nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * TD1-TD4
+ * each has 4 mode:(2 bits)
+ * 0: Stop monitor
+ * 1: Use internal temp sensor(default)
+ * 2: Reserved
+ * 3: Use sensor in Intel CPU and get result by PECI
+ *
+ * TR1-TR2
+ * each has 2 mode:(1 bit)
+ * 0: Disable temp sensor monitor
+ * 1: To enable temp sensors monitor
+ */
+
+/* 0 disable, 6 PECI */
+static u8 TO_TEMP_MODE[] = { 0, 0, 0, 6 };
+
+static ssize_t
+show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83793_data *data = w83793_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ u8 mask = (index < 4) ? 0x03 : 0x01;
+ u8 shift = (index < 4) ? (2 * index) : (index - 4);
+ u8 tmp;
+ index = (index < 4) ? 0 : 1;
+
+ tmp = (data->temp_mode[index] >> shift) & mask;
+
+ /* for the internal sensor, found out if diode or thermistor */
+ if (tmp == 1)
+ tmp = index == 0 ? 3 : 4;
+ else
+ tmp = TO_TEMP_MODE[tmp];
+
+ return sprintf(buf, "%d\n", tmp);
+}
+
+static ssize_t
+store_temp_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ u8 mask = (index < 4) ? 0x03 : 0x01;
+ u8 shift = (index < 4) ? (2 * index) : (index - 4);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ /* transform the sysfs interface values into table above */
+ if ((val == 6) && (index < 4)) {
+ val -= 3;
+ } else if ((val == 3 && index < 4)
+ || (val == 4 && index >= 4)) {
+ /* transform diode or thermistor into internal enable */
+ val = !!val;
+ } else {
+ return -EINVAL;
+ }
+
+ index = (index < 4) ? 0 : 1;
+ mutex_lock(&data->update_lock);
+ data->temp_mode[index] =
+ w83793_read_value(client, W83793_REG_TEMP_MODE[index]);
+ data->temp_mode[index] &= ~(mask << shift);
+ data->temp_mode[index] |= val << shift;
+ w83793_write_value(client, W83793_REG_TEMP_MODE[index],
+ data->temp_mode[index]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+#define SETUP_PWM_DEFAULT 0
+#define SETUP_PWM_UPTIME 1 /* Unit in 0.1s */
+#define SETUP_PWM_DOWNTIME 2 /* Unit in 0.1s */
+#define SETUP_TEMP_CRITICAL 3
+static ssize_t
+show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ struct w83793_data *data = w83793_update_device(dev);
+ u32 val = 0;
+
+ if (nr == SETUP_PWM_DEFAULT)
+ val = (data->pwm_default & 0x3f) << 2;
+ else if (nr == SETUP_PWM_UPTIME)
+ val = TIME_FROM_REG(data->pwm_uptime);
+ else if (nr == SETUP_PWM_DOWNTIME)
+ val = TIME_FROM_REG(data->pwm_downtime);
+ else if (nr == SETUP_TEMP_CRITICAL)
+ val = TEMP_FROM_REG(data->temp_critical & 0x7f);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+store_sf_setup(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if (nr == SETUP_PWM_DEFAULT) {
+ data->pwm_default =
+ w83793_read_value(client, W83793_REG_PWM_DEFAULT) & 0xc0;
+ data->pwm_default |= clamp_val(val, 0, 0xff) >> 2;
+ w83793_write_value(client, W83793_REG_PWM_DEFAULT,
+ data->pwm_default);
+ } else if (nr == SETUP_PWM_UPTIME) {
+ data->pwm_uptime = TIME_TO_REG(val);
+ data->pwm_uptime += data->pwm_uptime == 0 ? 1 : 0;
+ w83793_write_value(client, W83793_REG_PWM_UPTIME,
+ data->pwm_uptime);
+ } else if (nr == SETUP_PWM_DOWNTIME) {
+ data->pwm_downtime = TIME_TO_REG(val);
+ data->pwm_downtime += data->pwm_downtime == 0 ? 1 : 0;
+ w83793_write_value(client, W83793_REG_PWM_DOWNTIME,
+ data->pwm_downtime);
+ } else { /* SETUP_TEMP_CRITICAL */
+ data->temp_critical =
+ w83793_read_value(client, W83793_REG_TEMP_CRITICAL) & 0x80;
+ data->temp_critical |= TEMP_TO_REG(val, 0, 0x7f);
+ w83793_write_value(client, W83793_REG_TEMP_CRITICAL,
+ data->temp_critical);
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Temp SmartFan control
+ * TEMP_FAN_MAP
+ * Temp channel control which pwm fan, bitfield, bit 0 indicate pwm1...
+ * It's possible two or more temp channels control the same fan, w83793
+ * always prefers to pick the most critical request and applies it to
+ * the related Fan.
+ * It's possible one fan is not in any mapping of 6 temp channels, this
+ * means the fan is manual mode
+ *
+ * TEMP_PWM_ENABLE
+ * Each temp channel has its own SmartFan mode, and temp channel
+ * control fans that are set by TEMP_FAN_MAP
+ * 0: SmartFanII mode
+ * 1: Thermal Cruise Mode
+ *
+ * TEMP_CRUISE
+ * Target temperature in thermal cruise mode, w83793 will try to turn
+ * fan speed to keep the temperature of target device around this
+ * temperature.
+ *
+ * TEMP_TOLERANCE
+ * If Temp higher or lower than target with this tolerance, w83793
+ * will take actions to speed up or slow down the fan to keep the
+ * temperature within the tolerance range.
+ */
+
+#define TEMP_FAN_MAP 0
+#define TEMP_PWM_ENABLE 1
+#define TEMP_CRUISE 2
+#define TEMP_TOLERANCE 3
+static ssize_t
+show_sf_ctrl(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83793_data *data = w83793_update_device(dev);
+ u32 val;
+
+ if (nr == TEMP_FAN_MAP) {
+ val = data->temp_fan_map[index];
+ } else if (nr == TEMP_PWM_ENABLE) {
+ /* +2 to transform into 2 and 3 to conform with sysfs intf */
+ val = ((data->pwm_enable >> index) & 0x01) + 2;
+ } else if (nr == TEMP_CRUISE) {
+ val = TEMP_FROM_REG(data->temp_cruise[index] & 0x7f);
+ } else { /* TEMP_TOLERANCE */
+ val = data->tolerance[index >> 1] >> ((index & 0x01) ? 4 : 0);
+ val = TEMP_FROM_REG(val & 0x0f);
+ }
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+store_sf_ctrl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if (nr == TEMP_FAN_MAP) {
+ val = clamp_val(val, 0, 255);
+ w83793_write_value(client, W83793_REG_TEMP_FAN_MAP(index), val);
+ data->temp_fan_map[index] = val;
+ } else if (nr == TEMP_PWM_ENABLE) {
+ if (val == 2 || val == 3) {
+ data->pwm_enable =
+ w83793_read_value(client, W83793_REG_PWM_ENABLE);
+ if (val - 2)
+ data->pwm_enable |= 1 << index;
+ else
+ data->pwm_enable &= ~(1 << index);
+ w83793_write_value(client, W83793_REG_PWM_ENABLE,
+ data->pwm_enable);
+ } else {
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+ } else if (nr == TEMP_CRUISE) {
+ data->temp_cruise[index] =
+ w83793_read_value(client, W83793_REG_TEMP_CRUISE(index));
+ data->temp_cruise[index] &= 0x80;
+ data->temp_cruise[index] |= TEMP_TO_REG(val, 0, 0x7f);
+
+ w83793_write_value(client, W83793_REG_TEMP_CRUISE(index),
+ data->temp_cruise[index]);
+ } else { /* TEMP_TOLERANCE */
+ int i = index >> 1;
+ u8 shift = (index & 0x01) ? 4 : 0;
+ data->tolerance[i] =
+ w83793_read_value(client, W83793_REG_TEMP_TOL(i));
+
+ data->tolerance[i] &= ~(0x0f << shift);
+ data->tolerance[i] |= TEMP_TO_REG(val, 0, 0x0f) << shift;
+ w83793_write_value(client, W83793_REG_TEMP_TOL(i),
+ data->tolerance[i]);
+ }
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_sf2_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83793_data *data = w83793_update_device(dev);
+
+ return sprintf(buf, "%d\n", (data->sf2_pwm[index][nr] & 0x3f) << 2);
+}
+
+static ssize_t
+store_sf2_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ val = clamp_val(val, 0, 0xff) >> 2;
+
+ mutex_lock(&data->update_lock);
+ data->sf2_pwm[index][nr] =
+ w83793_read_value(client, W83793_REG_SF2_PWM(index, nr)) & 0xc0;
+ data->sf2_pwm[index][nr] |= val;
+ w83793_write_value(client, W83793_REG_SF2_PWM(index, nr),
+ data->sf2_pwm[index][nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_sf2_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83793_data *data = w83793_update_device(dev);
+
+ return sprintf(buf, "%ld\n",
+ TEMP_FROM_REG(data->sf2_temp[index][nr] & 0x7f));
+}
+
+static ssize_t
+store_sf2_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+ val = TEMP_TO_REG(val, 0, 0x7f);
+
+ mutex_lock(&data->update_lock);
+ data->sf2_temp[index][nr] =
+ w83793_read_value(client, W83793_REG_SF2_TEMP(index, nr)) & 0x80;
+ data->sf2_temp[index][nr] |= val;
+ w83793_write_value(client, W83793_REG_SF2_TEMP(index, nr),
+ data->sf2_temp[index][nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* only Vcore A/B and Vtt have additional 2 bits precision */
+static ssize_t
+show_in(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83793_data *data = w83793_update_device(dev);
+ u16 val = data->in[index][nr];
+
+ if (index < 3) {
+ val <<= 2;
+ val += (data->in_low_bits[nr] >> (index * 2)) & 0x3;
+ }
+ /* voltage inputs 5VDD and 5VSB needs 150mV offset */
+ val = val * scale_in[index] + scale_in_add[index];
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+store_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ val = (val + scale_in[index] / 2) / scale_in[index];
+
+ mutex_lock(&data->update_lock);
+ if (index > 2) {
+ /* fix the limit values of 5VDD and 5VSB to ALARM mechanism */
+ if (nr == 1 || nr == 2)
+ val -= scale_in_add[index] / scale_in[index];
+ val = clamp_val(val, 0, 255);
+ } else {
+ val = clamp_val(val, 0, 0x3FF);
+ data->in_low_bits[nr] =
+ w83793_read_value(client, W83793_REG_IN_LOW_BITS[nr]);
+ data->in_low_bits[nr] &= ~(0x03 << (2 * index));
+ data->in_low_bits[nr] |= (val & 0x03) << (2 * index);
+ w83793_write_value(client, W83793_REG_IN_LOW_BITS[nr],
+ data->in_low_bits[nr]);
+ val >>= 2;
+ }
+ data->in[index][nr] = val;
+ w83793_write_value(client, W83793_REG_IN[index][nr],
+ data->in[index][nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+#define NOT_USED -1
+
+#define SENSOR_ATTR_IN(index) \
+ SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \
+ IN_READ, index), \
+ SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \
+ store_in, IN_MAX, index), \
+ SENSOR_ATTR_2(in##index##_min, S_IRUGO | S_IWUSR, show_in, \
+ store_in, IN_LOW, index), \
+ SENSOR_ATTR_2(in##index##_alarm, S_IRUGO, show_alarm_beep, \
+ NULL, ALARM_STATUS, index + ((index > 2) ? 1 : 0)), \
+ SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \
+ show_alarm_beep, store_beep, BEEP_ENABLE, \
+ index + ((index > 2) ? 1 : 0))
+
+#define SENSOR_ATTR_FAN(index) \
+ SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \
+ NULL, ALARM_STATUS, index + 17), \
+ SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \
+ show_alarm_beep, store_beep, BEEP_ENABLE, index + 17), \
+ SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \
+ NULL, FAN_INPUT, index - 1), \
+ SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \
+ show_fan, store_fan_min, FAN_MIN, index - 1)
+
+#define SENSOR_ATTR_PWM(index) \
+ SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \
+ store_pwm, PWM_DUTY, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_NONSTOP, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_START, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_STOP_TIME, index - 1)
+
+#define SENSOR_ATTR_TEMP(index) \
+ SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR, \
+ show_temp_mode, store_temp_mode, NOT_USED, index - 1), \
+ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \
+ NULL, TEMP_READ, index - 1), \
+ SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \
+ store_temp, TEMP_CRIT, index - 1), \
+ SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \
+ SENSOR_ATTR_2(temp##index##_warn, S_IRUGO | S_IWUSR, show_temp, \
+ store_temp, TEMP_WARN, index - 1), \
+ SENSOR_ATTR_2(temp##index##_warn_hyst, S_IRUGO | S_IWUSR, \
+ show_temp, store_temp, TEMP_WARN_HYST, index - 1), \
+ SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \
+ show_alarm_beep, NULL, ALARM_STATUS, index + 11), \
+ SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \
+ show_alarm_beep, store_beep, BEEP_ENABLE, index + 11), \
+ SENSOR_ATTR_2(temp##index##_auto_channels_pwm, \
+ S_IRUGO | S_IWUSR, show_sf_ctrl, store_sf_ctrl, \
+ TEMP_FAN_MAP, index - 1), \
+ SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO, \
+ show_sf_ctrl, store_sf_ctrl, TEMP_PWM_ENABLE, \
+ index - 1), \
+ SENSOR_ATTR_2(thermal_cruise##index, S_IRUGO | S_IWUSR, \
+ show_sf_ctrl, store_sf_ctrl, TEMP_CRUISE, index - 1), \
+ SENSOR_ATTR_2(tolerance##index, S_IRUGO | S_IWUSR, show_sf_ctrl,\
+ store_sf_ctrl, TEMP_TOLERANCE, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point1_pwm, S_IRUGO | S_IWUSR, \
+ show_sf2_pwm, store_sf2_pwm, 0, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point2_pwm, S_IRUGO | S_IWUSR, \
+ show_sf2_pwm, store_sf2_pwm, 1, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point3_pwm, S_IRUGO | S_IWUSR, \
+ show_sf2_pwm, store_sf2_pwm, 2, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point4_pwm, S_IRUGO | S_IWUSR, \
+ show_sf2_pwm, store_sf2_pwm, 3, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point5_pwm, S_IRUGO | S_IWUSR, \
+ show_sf2_pwm, store_sf2_pwm, 4, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point6_pwm, S_IRUGO | S_IWUSR, \
+ show_sf2_pwm, store_sf2_pwm, 5, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point7_pwm, S_IRUGO | S_IWUSR, \
+ show_sf2_pwm, store_sf2_pwm, 6, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point1_temp, S_IRUGO | S_IWUSR,\
+ show_sf2_temp, store_sf2_temp, 0, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point2_temp, S_IRUGO | S_IWUSR,\
+ show_sf2_temp, store_sf2_temp, 1, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point3_temp, S_IRUGO | S_IWUSR,\
+ show_sf2_temp, store_sf2_temp, 2, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point4_temp, S_IRUGO | S_IWUSR,\
+ show_sf2_temp, store_sf2_temp, 3, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point5_temp, S_IRUGO | S_IWUSR,\
+ show_sf2_temp, store_sf2_temp, 4, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\
+ show_sf2_temp, store_sf2_temp, 5, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\
+ show_sf2_temp, store_sf2_temp, 6, index - 1)
+
+static struct sensor_device_attribute_2 w83793_sensor_attr_2[] = {
+ SENSOR_ATTR_IN(0),
+ SENSOR_ATTR_IN(1),
+ SENSOR_ATTR_IN(2),
+ SENSOR_ATTR_IN(3),
+ SENSOR_ATTR_IN(4),
+ SENSOR_ATTR_IN(5),
+ SENSOR_ATTR_IN(6),
+ SENSOR_ATTR_IN(7),
+ SENSOR_ATTR_IN(8),
+ SENSOR_ATTR_IN(9),
+ SENSOR_ATTR_FAN(1),
+ SENSOR_ATTR_FAN(2),
+ SENSOR_ATTR_FAN(3),
+ SENSOR_ATTR_FAN(4),
+ SENSOR_ATTR_FAN(5),
+ SENSOR_ATTR_PWM(1),
+ SENSOR_ATTR_PWM(2),
+ SENSOR_ATTR_PWM(3),
+};
+
+static struct sensor_device_attribute_2 w83793_temp[] = {
+ SENSOR_ATTR_TEMP(1),
+ SENSOR_ATTR_TEMP(2),
+ SENSOR_ATTR_TEMP(3),
+ SENSOR_ATTR_TEMP(4),
+ SENSOR_ATTR_TEMP(5),
+ SENSOR_ATTR_TEMP(6),
+};
+
+/* Fan6-Fan12 */
+static struct sensor_device_attribute_2 w83793_left_fan[] = {
+ SENSOR_ATTR_FAN(6),
+ SENSOR_ATTR_FAN(7),
+ SENSOR_ATTR_FAN(8),
+ SENSOR_ATTR_FAN(9),
+ SENSOR_ATTR_FAN(10),
+ SENSOR_ATTR_FAN(11),
+ SENSOR_ATTR_FAN(12),
+};
+
+/* Pwm4-Pwm8 */
+static struct sensor_device_attribute_2 w83793_left_pwm[] = {
+ SENSOR_ATTR_PWM(4),
+ SENSOR_ATTR_PWM(5),
+ SENSOR_ATTR_PWM(6),
+ SENSOR_ATTR_PWM(7),
+ SENSOR_ATTR_PWM(8),
+};
+
+static struct sensor_device_attribute_2 w83793_vid[] = {
+ SENSOR_ATTR_2(cpu0_vid, S_IRUGO, show_vid, NULL, NOT_USED, 0),
+ SENSOR_ATTR_2(cpu1_vid, S_IRUGO, show_vid, NULL, NOT_USED, 1),
+};
+static DEVICE_ATTR_RW(vrm);
+
+static struct sensor_device_attribute_2 sda_single_files[] = {
+ SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep,
+ store_chassis_clear, ALARM_STATUS, 30),
+ SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable,
+ store_beep_enable, NOT_USED, NOT_USED),
+ SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup,
+ store_sf_setup, SETUP_PWM_DEFAULT, NOT_USED),
+ SENSOR_ATTR_2(pwm_uptime, S_IWUSR | S_IRUGO, show_sf_setup,
+ store_sf_setup, SETUP_PWM_UPTIME, NOT_USED),
+ SENSOR_ATTR_2(pwm_downtime, S_IWUSR | S_IRUGO, show_sf_setup,
+ store_sf_setup, SETUP_PWM_DOWNTIME, NOT_USED),
+ SENSOR_ATTR_2(temp_critical, S_IWUSR | S_IRUGO, show_sf_setup,
+ store_sf_setup, SETUP_TEMP_CRITICAL, NOT_USED),
+};
+
+static void w83793_init_client(struct i2c_client *client)
+{
+ if (reset)
+ w83793_write_value(client, W83793_REG_CONFIG, 0x80);
+
+ /* Start monitoring */
+ w83793_write_value(client, W83793_REG_CONFIG,
+ w83793_read_value(client, W83793_REG_CONFIG) | 0x01);
+}
+
+/*
+ * Watchdog routines
+ */
+
+static int watchdog_set_timeout(struct w83793_data *data, int timeout)
+{
+ unsigned int mtimeout;
+ int ret;
+
+ mtimeout = DIV_ROUND_UP(timeout, 60);
+
+ if (mtimeout > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->watchdog_lock);
+ if (!data->client) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ data->watchdog_timeout = mtimeout;
+
+ /* Set Timeout value (in Minutes) */
+ w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT,
+ data->watchdog_timeout);
+
+ ret = mtimeout * 60;
+
+leave:
+ mutex_unlock(&data->watchdog_lock);
+ return ret;
+}
+
+static int watchdog_get_timeout(struct w83793_data *data)
+{
+ int timeout;
+
+ mutex_lock(&data->watchdog_lock);
+ timeout = data->watchdog_timeout * 60;
+ mutex_unlock(&data->watchdog_lock);
+
+ return timeout;
+}
+
+static int watchdog_trigger(struct w83793_data *data)
+{
+ int ret = 0;
+
+ mutex_lock(&data->watchdog_lock);
+ if (!data->client) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ /* Set Timeout value (in Minutes) */
+ w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT,
+ data->watchdog_timeout);
+
+leave:
+ mutex_unlock(&data->watchdog_lock);
+ return ret;
+}
+
+static int watchdog_enable(struct w83793_data *data)
+{
+ int ret = 0;
+
+ mutex_lock(&data->watchdog_lock);
+ if (!data->client) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ /* Set initial timeout */
+ w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT,
+ data->watchdog_timeout);
+
+ /* Enable Soft Watchdog */
+ w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0x55);
+
+leave:
+ mutex_unlock(&data->watchdog_lock);
+ return ret;
+}
+
+static int watchdog_disable(struct w83793_data *data)
+{
+ int ret = 0;
+
+ mutex_lock(&data->watchdog_lock);
+ if (!data->client) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ /* Disable Soft Watchdog */
+ w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0xAA);
+
+leave:
+ mutex_unlock(&data->watchdog_lock);
+ return ret;
+}
+
+static int watchdog_open(struct inode *inode, struct file *filp)
+{
+ struct w83793_data *pos, *data = NULL;
+ int watchdog_is_open;
+
+ /*
+ * We get called from drivers/char/misc.c with misc_mtx hold, and we
+ * call misc_register() from w83793_probe() with watchdog_data_mutex
+ * hold, as misc_register() takes the misc_mtx lock, this is a possible
+ * deadlock, so we use mutex_trylock here.
+ */
+ if (!mutex_trylock(&watchdog_data_mutex))
+ return -ERESTARTSYS;
+ list_for_each_entry(pos, &watchdog_data_list, list) {
+ if (pos->watchdog_miscdev.minor == iminor(inode)) {
+ data = pos;
+ break;
+ }
+ }
+
+ /* Check, if device is already open */
+ watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open);
+
+ /*
+ * Increase data reference counter (if not already done).
+ * Note we can never not have found data, so we don't check for this
+ */
+ if (!watchdog_is_open)
+ kref_get(&data->kref);
+
+ mutex_unlock(&watchdog_data_mutex);
+
+ /* Check, if device is already open and possibly issue error */
+ if (watchdog_is_open)
+ return -EBUSY;
+
+ /* Enable Soft Watchdog */
+ watchdog_enable(data);
+
+ /* Store pointer to data into filp's private data */
+ filp->private_data = data;
+
+ return stream_open(inode, filp);
+}
+
+static int watchdog_close(struct inode *inode, struct file *filp)
+{
+ struct w83793_data *data = filp->private_data;
+
+ if (data->watchdog_expect_close) {
+ watchdog_disable(data);
+ data->watchdog_expect_close = 0;
+ } else {
+ watchdog_trigger(data);
+ dev_crit(&data->client->dev,
+ "unexpected close, not stopping watchdog!\n");
+ }
+
+ clear_bit(0, &data->watchdog_is_open);
+
+ /* Decrease data reference counter */
+ mutex_lock(&watchdog_data_mutex);
+ kref_put(&data->kref, w83793_release_resources);
+ mutex_unlock(&watchdog_data_mutex);
+
+ return 0;
+}
+
+static ssize_t watchdog_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ ssize_t ret;
+ struct w83793_data *data = filp->private_data;
+
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* Clear it in case it was set with a previous write */
+ data->watchdog_expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ data->watchdog_expect_close = 1;
+ }
+ }
+ ret = watchdog_trigger(data);
+ if (ret < 0)
+ return ret;
+ }
+ return count;
+}
+
+static long watchdog_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_CARDRESET,
+ .identity = "w83793 watchdog"
+ };
+
+ int val, ret = 0;
+ struct w83793_data *data = filp->private_data;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (!nowayout)
+ ident.options |= WDIOF_MAGICCLOSE;
+ if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
+ ret = -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ val = data->watchdog_caused_reboot ? WDIOF_CARDRESET : 0;
+ ret = put_user(val, (int __user *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int __user *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ ret = watchdog_trigger(data);
+ break;
+
+ case WDIOC_GETTIMEOUT:
+ val = watchdog_get_timeout(data);
+ ret = put_user(val, (int __user *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(val, (int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = watchdog_set_timeout(data, val);
+ if (ret > 0)
+ ret = put_user(ret, (int __user *)arg);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(val, (int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ if (val & WDIOS_DISABLECARD)
+ ret = watchdog_disable(data);
+ else if (val & WDIOS_ENABLECARD)
+ ret = watchdog_enable(data);
+ else
+ ret = -EINVAL;
+
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+ return ret;
+}
+
+static const struct file_operations watchdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = watchdog_open,
+ .release = watchdog_close,
+ .write = watchdog_write,
+ .unlocked_ioctl = watchdog_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int watchdog_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ struct w83793_data *data = NULL;
+
+ if (code == SYS_DOWN || code == SYS_HALT) {
+
+ /* Disable each registered watchdog */
+ mutex_lock(&watchdog_data_mutex);
+ list_for_each_entry(data, &watchdog_data_list, list) {
+ if (data->watchdog_miscdev.minor)
+ watchdog_disable(data);
+ }
+ mutex_unlock(&watchdog_data_mutex);
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block watchdog_notifier = {
+ .notifier_call = watchdog_notify_sys,
+};
+
+/*
+ * Init / remove routines
+ */
+
+static void w83793_remove(struct i2c_client *client)
+{
+ struct w83793_data *data = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ int i, tmp;
+
+ /* Unregister the watchdog (if registered) */
+ if (data->watchdog_miscdev.minor) {
+ misc_deregister(&data->watchdog_miscdev);
+
+ if (data->watchdog_is_open) {
+ dev_warn(&client->dev,
+ "i2c client detached with watchdog open! "
+ "Stopping watchdog.\n");
+ watchdog_disable(data);
+ }
+
+ mutex_lock(&watchdog_data_mutex);
+ list_del(&data->list);
+ mutex_unlock(&watchdog_data_mutex);
+
+ /* Tell the watchdog code the client is gone */
+ mutex_lock(&data->watchdog_lock);
+ data->client = NULL;
+ mutex_unlock(&data->watchdog_lock);
+ }
+
+ /* Reset Configuration Register to Disable Watch Dog Registers */
+ tmp = w83793_read_value(client, W83793_REG_CONFIG);
+ w83793_write_value(client, W83793_REG_CONFIG, tmp & ~0x04);
+
+ unregister_reboot_notifier(&watchdog_notifier);
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++)
+ device_remove_file(dev,
+ &w83793_sensor_attr_2[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(sda_single_files); i++)
+ device_remove_file(dev, &sda_single_files[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_vid); i++)
+ device_remove_file(dev, &w83793_vid[i].dev_attr);
+ device_remove_file(dev, &dev_attr_vrm);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++)
+ device_remove_file(dev, &w83793_left_fan[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_left_pwm); i++)
+ device_remove_file(dev, &w83793_left_pwm[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_temp); i++)
+ device_remove_file(dev, &w83793_temp[i].dev_attr);
+
+ /* Decrease data reference counter */
+ mutex_lock(&watchdog_data_mutex);
+ kref_put(&data->kref, w83793_release_resources);
+ mutex_unlock(&watchdog_data_mutex);
+}
+
+static int
+w83793_detect_subclients(struct i2c_client *client)
+{
+ int i, id;
+ int address = client->addr;
+ u8 tmp;
+ struct i2c_adapter *adapter = client->adapter;
+
+ id = i2c_adapter_id(adapter);
+ if (force_subclients[0] == id && force_subclients[1] == address) {
+ for (i = 2; i <= 3; i++) {
+ if (force_subclients[i] < 0x48
+ || force_subclients[i] > 0x4f) {
+ dev_err(&client->dev,
+ "invalid subclient "
+ "address %d; must be 0x48-0x4f\n",
+ force_subclients[i]);
+ return -EINVAL;
+ }
+ }
+ w83793_write_value(client, W83793_REG_I2C_SUBADDR,
+ (force_subclients[2] & 0x07) |
+ ((force_subclients[3] & 0x07) << 4));
+ }
+
+ tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR);
+
+ 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;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int w83793_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ u8 tmp, bank, chip_id;
+ struct i2c_adapter *adapter = client->adapter;
+ unsigned short address = client->addr;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL);
+
+ tmp = bank & 0x80 ? 0x5c : 0xa3;
+ /* Check Winbond vendor ID */
+ if (tmp != i2c_smbus_read_byte_data(client, W83793_REG_VENDORID)) {
+ pr_debug("w83793: Detection failed at check vendor id\n");
+ return -ENODEV;
+ }
+
+ /*
+ * If Winbond chip, address of chip and W83793_REG_I2C_ADDR
+ * should match
+ */
+ if ((bank & 0x07) == 0
+ && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) !=
+ (address << 1)) {
+ pr_debug("w83793: Detection failed at check i2c addr\n");
+ return -ENODEV;
+ }
+
+ /* Determine the chip type now */
+ chip_id = i2c_smbus_read_byte_data(client, W83793_REG_CHIPID);
+ if (chip_id != 0x7b)
+ return -ENODEV;
+
+ strscpy(info->type, "w83793", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int w83793_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ static const int watchdog_minors[] = {
+ WATCHDOG_MINOR, 212, 213, 214, 215
+ };
+ struct w83793_data *data;
+ int i, tmp, val, err;
+ int files_fan = ARRAY_SIZE(w83793_left_fan) / 7;
+ int files_pwm = ARRAY_SIZE(w83793_left_pwm) / 5;
+ int files_temp = ARRAY_SIZE(w83793_temp) / 6;
+
+ data = kzalloc(sizeof(struct w83793_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL);
+ mutex_init(&data->update_lock);
+ mutex_init(&data->watchdog_lock);
+ INIT_LIST_HEAD(&data->list);
+ kref_init(&data->kref);
+
+ /*
+ * Store client pointer in our data struct for watchdog usage
+ * (where the client is found through a data ptr instead of the
+ * otherway around)
+ */
+ data->client = client;
+
+ err = w83793_detect_subclients(client);
+ if (err)
+ goto free_mem;
+
+ /* Initialize the chip */
+ w83793_init_client(client);
+
+ /*
+ * Only fan 1-5 has their own input pins,
+ * Pwm 1-3 has their own pins
+ */
+ data->has_fan = 0x1f;
+ data->has_pwm = 0x07;
+ tmp = w83793_read_value(client, W83793_REG_MFC);
+ val = w83793_read_value(client, W83793_REG_FANIN_CTRL);
+
+ /* check the function of pins 49-56 */
+ if (tmp & 0x80) {
+ data->has_vid |= 0x2; /* has VIDB */
+ } else {
+ data->has_pwm |= 0x18; /* pwm 4,5 */
+ if (val & 0x01) { /* fan 6 */
+ data->has_fan |= 0x20;
+ data->has_pwm |= 0x20;
+ }
+ if (val & 0x02) { /* fan 7 */
+ data->has_fan |= 0x40;
+ data->has_pwm |= 0x40;
+ }
+ if (!(tmp & 0x40) && (val & 0x04)) { /* fan 8 */
+ data->has_fan |= 0x80;
+ data->has_pwm |= 0x80;
+ }
+ }
+
+ /* check the function of pins 37-40 */
+ if (!(tmp & 0x29))
+ data->has_vid |= 0x1; /* has VIDA */
+ if (0x08 == (tmp & 0x0c)) {
+ if (val & 0x08) /* fan 9 */
+ data->has_fan |= 0x100;
+ if (val & 0x10) /* fan 10 */
+ data->has_fan |= 0x200;
+ }
+ if (0x20 == (tmp & 0x30)) {
+ if (val & 0x20) /* fan 11 */
+ data->has_fan |= 0x400;
+ if (val & 0x40) /* fan 12 */
+ data->has_fan |= 0x800;
+ }
+
+ if ((tmp & 0x01) && (val & 0x04)) { /* fan 8, second location */
+ data->has_fan |= 0x80;
+ data->has_pwm |= 0x80;
+ }
+
+ tmp = w83793_read_value(client, W83793_REG_FANIN_SEL);
+ if ((tmp & 0x01) && (val & 0x08)) { /* fan 9, second location */
+ data->has_fan |= 0x100;
+ }
+ if ((tmp & 0x02) && (val & 0x10)) { /* fan 10, second location */
+ data->has_fan |= 0x200;
+ }
+ if ((tmp & 0x04) && (val & 0x20)) { /* fan 11, second location */
+ data->has_fan |= 0x400;
+ }
+ if ((tmp & 0x08) && (val & 0x40)) { /* fan 12, second location */
+ data->has_fan |= 0x800;
+ }
+
+ /* check the temp1-6 mode, ignore former AMDSI selected inputs */
+ tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[0]);
+ if (tmp & 0x01)
+ data->has_temp |= 0x01;
+ if (tmp & 0x04)
+ data->has_temp |= 0x02;
+ if (tmp & 0x10)
+ data->has_temp |= 0x04;
+ if (tmp & 0x40)
+ data->has_temp |= 0x08;
+
+ tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[1]);
+ if (tmp & 0x01)
+ data->has_temp |= 0x10;
+ if (tmp & 0x02)
+ data->has_temp |= 0x20;
+
+ /* Register sysfs hooks */
+ for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++) {
+ err = device_create_file(dev,
+ &w83793_sensor_attr_2[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) {
+ if (!(data->has_vid & (1 << i)))
+ continue;
+ err = device_create_file(dev, &w83793_vid[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->has_vid) {
+ data->vrm = vid_which_vrm();
+ err = device_create_file(dev, &dev_attr_vrm);
+ if (err)
+ goto exit_remove;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) {
+ err = device_create_file(dev, &sda_single_files[i].dev_attr);
+ if (err)
+ goto exit_remove;
+
+ }
+
+ for (i = 0; i < 6; i++) {
+ int j;
+ if (!(data->has_temp & (1 << i)))
+ continue;
+ for (j = 0; j < files_temp; j++) {
+ err = device_create_file(dev,
+ &w83793_temp[(i) * files_temp
+ + j].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
+ for (i = 5; i < 12; i++) {
+ int j;
+ if (!(data->has_fan & (1 << i)))
+ continue;
+ for (j = 0; j < files_fan; j++) {
+ err = device_create_file(dev,
+ &w83793_left_fan[(i - 5) * files_fan
+ + j].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
+ for (i = 3; i < 8; i++) {
+ int j;
+ if (!(data->has_pwm & (1 << i)))
+ continue;
+ for (j = 0; j < files_pwm; j++) {
+ err = device_create_file(dev,
+ &w83793_left_pwm[(i - 3) * files_pwm
+ + j].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ /* Watchdog initialization */
+
+ /* Register boot notifier */
+ err = register_reboot_notifier(&watchdog_notifier);
+ if (err != 0) {
+ dev_err(&client->dev,
+ "cannot register reboot notifier (err=%d)\n", err);
+ goto exit_devunreg;
+ }
+
+ /*
+ * Enable Watchdog registers.
+ * Set Configuration Register to Enable Watch Dog Registers
+ * (Bit 2) = XXXX, X1XX.
+ */
+ tmp = w83793_read_value(client, W83793_REG_CONFIG);
+ w83793_write_value(client, W83793_REG_CONFIG, tmp | 0x04);
+
+ /* Set the default watchdog timeout */
+ data->watchdog_timeout = timeout;
+
+ /* Check, if last reboot was caused by watchdog */
+ data->watchdog_caused_reboot =
+ w83793_read_value(data->client, W83793_REG_WDT_STATUS) & 0x01;
+
+ /* Disable Soft Watchdog during initialiation */
+ watchdog_disable(data);
+
+ /*
+ * We take the data_mutex lock early so that watchdog_open() cannot
+ * run when misc_register() has completed, but we've not yet added
+ * our data to the watchdog_data_list (and set the default timeout)
+ */
+ mutex_lock(&watchdog_data_mutex);
+ for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) {
+ /* Register our watchdog part */
+ snprintf(data->watchdog_name, sizeof(data->watchdog_name),
+ "watchdog%c", (i == 0) ? '\0' : ('0' + i));
+ data->watchdog_miscdev.name = data->watchdog_name;
+ data->watchdog_miscdev.fops = &watchdog_fops;
+ data->watchdog_miscdev.minor = watchdog_minors[i];
+
+ err = misc_register(&data->watchdog_miscdev);
+ if (err == -EBUSY)
+ continue;
+ if (err) {
+ data->watchdog_miscdev.minor = 0;
+ dev_err(&client->dev,
+ "Registering watchdog chardev: %d\n", err);
+ break;
+ }
+
+ list_add(&data->list, &watchdog_data_list);
+
+ dev_info(&client->dev,
+ "Registered watchdog chardev major 10, minor: %d\n",
+ watchdog_minors[i]);
+ break;
+ }
+ if (i == ARRAY_SIZE(watchdog_minors)) {
+ data->watchdog_miscdev.minor = 0;
+ dev_warn(&client->dev,
+ "Couldn't register watchdog chardev (due to no free minor)\n");
+ }
+
+ mutex_unlock(&watchdog_data_mutex);
+
+ return 0;
+
+ /* Unregister hwmon device */
+
+exit_devunreg:
+
+ hwmon_device_unregister(data->hwmon_dev);
+
+ /* Unregister sysfs hooks */
+
+exit_remove:
+ for (i = 0; i < ARRAY_SIZE(w83793_sensor_attr_2); i++)
+ device_remove_file(dev, &w83793_sensor_attr_2[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(sda_single_files); i++)
+ device_remove_file(dev, &sda_single_files[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_vid); i++)
+ device_remove_file(dev, &w83793_vid[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++)
+ device_remove_file(dev, &w83793_left_fan[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_left_pwm); i++)
+ device_remove_file(dev, &w83793_left_pwm[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(w83793_temp); i++)
+ device_remove_file(dev, &w83793_temp[i].dev_attr);
+free_mem:
+ kfree(data);
+exit:
+ return err;
+}
+
+static void w83793_update_nonvolatile(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ int i, j;
+ /*
+ * They are somewhat "stable" registers, and to update them every time
+ * takes so much time, it's just not worthy. Update them in a long
+ * interval to avoid exception.
+ */
+ if (!(time_after(jiffies, data->last_nonvolatile + HZ * 300)
+ || !data->valid))
+ return;
+ /* update voltage limits */
+ for (i = 1; i < 3; i++) {
+ for (j = 0; j < ARRAY_SIZE(data->in); j++) {
+ data->in[j][i] =
+ w83793_read_value(client, W83793_REG_IN[j][i]);
+ }
+ data->in_low_bits[i] =
+ w83793_read_value(client, W83793_REG_IN_LOW_BITS[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+ /* Update the Fan measured value and limits */
+ if (!(data->has_fan & (1 << i)))
+ continue;
+ data->fan_min[i] =
+ w83793_read_value(client, W83793_REG_FAN_MIN(i)) << 8;
+ data->fan_min[i] |=
+ w83793_read_value(client, W83793_REG_FAN_MIN(i) + 1);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->temp_fan_map); i++) {
+ if (!(data->has_temp & (1 << i)))
+ continue;
+ data->temp_fan_map[i] =
+ w83793_read_value(client, W83793_REG_TEMP_FAN_MAP(i));
+ for (j = 1; j < 5; j++) {
+ data->temp[i][j] =
+ w83793_read_value(client, W83793_REG_TEMP[i][j]);
+ }
+ data->temp_cruise[i] =
+ w83793_read_value(client, W83793_REG_TEMP_CRUISE(i));
+ for (j = 0; j < 7; j++) {
+ data->sf2_pwm[i][j] =
+ w83793_read_value(client, W83793_REG_SF2_PWM(i, j));
+ data->sf2_temp[i][j] =
+ w83793_read_value(client,
+ W83793_REG_SF2_TEMP(i, j));
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->temp_mode); i++)
+ data->temp_mode[i] =
+ w83793_read_value(client, W83793_REG_TEMP_MODE[i]);
+
+ for (i = 0; i < ARRAY_SIZE(data->tolerance); i++) {
+ data->tolerance[i] =
+ w83793_read_value(client, W83793_REG_TEMP_TOL(i));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->pwm); i++) {
+ if (!(data->has_pwm & (1 << i)))
+ continue;
+ data->pwm[i][PWM_NONSTOP] =
+ w83793_read_value(client, W83793_REG_PWM(i, PWM_NONSTOP));
+ data->pwm[i][PWM_START] =
+ w83793_read_value(client, W83793_REG_PWM(i, PWM_START));
+ data->pwm_stop_time[i] =
+ w83793_read_value(client, W83793_REG_PWM_STOP_TIME(i));
+ }
+
+ data->pwm_default = w83793_read_value(client, W83793_REG_PWM_DEFAULT);
+ data->pwm_enable = w83793_read_value(client, W83793_REG_PWM_ENABLE);
+ data->pwm_uptime = w83793_read_value(client, W83793_REG_PWM_UPTIME);
+ data->pwm_downtime = w83793_read_value(client, W83793_REG_PWM_DOWNTIME);
+ data->temp_critical =
+ w83793_read_value(client, W83793_REG_TEMP_CRITICAL);
+ data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP);
+
+ for (i = 0; i < ARRAY_SIZE(data->beeps); i++)
+ data->beeps[i] = w83793_read_value(client, W83793_REG_BEEP(i));
+
+ data->last_nonvolatile = jiffies;
+}
+
+static struct w83793_data *w83793_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83793_data *data = i2c_get_clientdata(client);
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (!(time_after(jiffies, data->last_updated + HZ * 2)
+ || !data->valid))
+ goto END;
+
+ /* Update the voltages measured value and limits */
+ for (i = 0; i < ARRAY_SIZE(data->in); i++)
+ data->in[i][IN_READ] =
+ w83793_read_value(client, W83793_REG_IN[i][IN_READ]);
+
+ data->in_low_bits[IN_READ] =
+ w83793_read_value(client, W83793_REG_IN_LOW_BITS[IN_READ]);
+
+ for (i = 0; i < ARRAY_SIZE(data->fan); i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+ data->fan[i] =
+ w83793_read_value(client, W83793_REG_FAN(i)) << 8;
+ data->fan[i] |=
+ w83793_read_value(client, W83793_REG_FAN(i) + 1);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ if (!(data->has_temp & (1 << i)))
+ continue;
+ data->temp[i][TEMP_READ] =
+ w83793_read_value(client, W83793_REG_TEMP[i][TEMP_READ]);
+ }
+
+ data->temp_low_bits =
+ w83793_read_value(client, W83793_REG_TEMP_LOW_BITS);
+
+ for (i = 0; i < ARRAY_SIZE(data->pwm); i++) {
+ if (data->has_pwm & (1 << i))
+ data->pwm[i][PWM_DUTY] =
+ w83793_read_value(client,
+ W83793_REG_PWM(i, PWM_DUTY));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->alarms); i++)
+ data->alarms[i] =
+ w83793_read_value(client, W83793_REG_ALARM(i));
+ if (data->has_vid & 0x01)
+ data->vid[0] = w83793_read_value(client, W83793_REG_VID_INA);
+ if (data->has_vid & 0x02)
+ data->vid[1] = w83793_read_value(client, W83793_REG_VID_INB);
+ w83793_update_nonvolatile(dev);
+ data->last_updated = jiffies;
+ data->valid = true;
+
+END:
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+/*
+ * Ignore the possibility that somebody change bank outside the driver
+ * Must be called with data->update_lock held, except during initialization
+ */
+static u8 w83793_read_value(struct i2c_client *client, u16 reg)
+{
+ struct w83793_data *data = i2c_get_clientdata(client);
+ u8 res;
+ u8 new_bank = reg >> 8;
+
+ new_bank |= data->bank & 0xfc;
+ if (data->bank != new_bank) {
+ if (i2c_smbus_write_byte_data
+ (client, W83793_REG_BANKSEL, new_bank) >= 0)
+ data->bank = new_bank;
+ else {
+ dev_err(&client->dev,
+ "set bank to %d failed, fall back "
+ "to bank %d, read reg 0x%x error\n",
+ new_bank, data->bank, reg);
+ res = 0x0; /* read 0x0 from the chip */
+ goto END;
+ }
+ }
+ res = i2c_smbus_read_byte_data(client, reg & 0xff);
+END:
+ return res;
+}
+
+/* Must be called with data->update_lock held, except during initialization */
+static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value)
+{
+ struct w83793_data *data = i2c_get_clientdata(client);
+ int res;
+ u8 new_bank = reg >> 8;
+
+ new_bank |= data->bank & 0xfc;
+ if (data->bank != new_bank) {
+ res = i2c_smbus_write_byte_data(client, W83793_REG_BANKSEL,
+ new_bank);
+ if (res < 0) {
+ dev_err(&client->dev,
+ "set bank to %d failed, fall back "
+ "to bank %d, write reg 0x%x error\n",
+ new_bank, data->bank, reg);
+ goto END;
+ }
+ data->bank = new_bank;
+ }
+
+ res = i2c_smbus_write_byte_data(client, reg & 0xff, value);
+END:
+ return res;
+}
+
+module_i2c_driver(w83793_driver);
+
+MODULE_AUTHOR("Yuan Mu, Sven Anders");
+MODULE_DESCRIPTION("w83793 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c
new file mode 100644
index 000000000..84ff5c57e
--- /dev/null
+++ b/drivers/hwmon/w83795.c
@@ -0,0 +1,2271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w83795.c - Linux kernel driver for hardware monitoring
+ * Copyright (C) 2008 Nuvoton Technology Corp.
+ * Wei Song
+ * Copyright (C) 2010 Jean Delvare <jdelvare@suse.de>
+ *
+ * Supports following chips:
+ *
+ * Chip #vin #fanin #pwm #temp #dts wchipid vendid i2c ISA
+ * w83795g 21 14 8 6 8 0x79 0x5ca3 yes no
+ * w83795adg 18 14 2 6 8 0x79 0x5ca3 yes no
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+#include <linux/util_macros.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = {
+ 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END
+};
+
+
+static bool reset;
+module_param(reset, bool, 0);
+MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
+
+
+#define W83795_REG_BANKSEL 0x00
+#define W83795_REG_VENDORID 0xfd
+#define W83795_REG_CHIPID 0xfe
+#define W83795_REG_DEVICEID 0xfb
+#define W83795_REG_DEVICEID_A 0xff
+
+#define W83795_REG_I2C_ADDR 0xfc
+#define W83795_REG_CONFIG 0x01
+#define W83795_REG_CONFIG_CONFIG48 0x04
+#define W83795_REG_CONFIG_START 0x01
+
+/* Multi-Function Pin Ctrl Registers */
+#define W83795_REG_VOLT_CTRL1 0x02
+#define W83795_REG_VOLT_CTRL2 0x03
+#define W83795_REG_TEMP_CTRL1 0x04
+#define W83795_REG_TEMP_CTRL2 0x05
+#define W83795_REG_FANIN_CTRL1 0x06
+#define W83795_REG_FANIN_CTRL2 0x07
+#define W83795_REG_VMIGB_CTRL 0x08
+
+#define TEMP_READ 0
+#define TEMP_CRIT 1
+#define TEMP_CRIT_HYST 2
+#define TEMP_WARN 3
+#define TEMP_WARN_HYST 4
+/*
+ * only crit and crit_hyst affect real-time alarm status
+ * current crit crit_hyst warn warn_hyst
+ */
+static const u16 W83795_REG_TEMP[][5] = {
+ {0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */
+ {0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */
+ {0x23, 0x9e, 0x9f, 0xa0, 0xa1}, /* TD3/TR3 */
+ {0x24, 0xa2, 0xa3, 0xa4, 0xa5}, /* TD4/TR4 */
+ {0x1f, 0xa6, 0xa7, 0xa8, 0xa9}, /* TR5 */
+ {0x20, 0xaa, 0xab, 0xac, 0xad}, /* TR6 */
+};
+
+#define IN_READ 0
+#define IN_MAX 1
+#define IN_LOW 2
+static const u16 W83795_REG_IN[][3] = {
+ /* Current, HL, LL */
+ {0x10, 0x70, 0x71}, /* VSEN1 */
+ {0x11, 0x72, 0x73}, /* VSEN2 */
+ {0x12, 0x74, 0x75}, /* VSEN3 */
+ {0x13, 0x76, 0x77}, /* VSEN4 */
+ {0x14, 0x78, 0x79}, /* VSEN5 */
+ {0x15, 0x7a, 0x7b}, /* VSEN6 */
+ {0x16, 0x7c, 0x7d}, /* VSEN7 */
+ {0x17, 0x7e, 0x7f}, /* VSEN8 */
+ {0x18, 0x80, 0x81}, /* VSEN9 */
+ {0x19, 0x82, 0x83}, /* VSEN10 */
+ {0x1A, 0x84, 0x85}, /* VSEN11 */
+ {0x1B, 0x86, 0x87}, /* VTT */
+ {0x1C, 0x88, 0x89}, /* 3VDD */
+ {0x1D, 0x8a, 0x8b}, /* 3VSB */
+ {0x1E, 0x8c, 0x8d}, /* VBAT */
+ {0x1F, 0xa6, 0xa7}, /* VSEN12 */
+ {0x20, 0xaa, 0xab}, /* VSEN13 */
+ {0x21, 0x96, 0x97}, /* VSEN14 */
+ {0x22, 0x9a, 0x9b}, /* VSEN15 */
+ {0x23, 0x9e, 0x9f}, /* VSEN16 */
+ {0x24, 0xa2, 0xa3}, /* VSEN17 */
+};
+#define W83795_REG_VRLSB 0x3C
+
+static const u8 W83795_REG_IN_HL_LSB[] = {
+ 0x8e, /* VSEN1-4 */
+ 0x90, /* VSEN5-8 */
+ 0x92, /* VSEN9-11 */
+ 0x94, /* VTT, 3VDD, 3VSB, 3VBAT */
+ 0xa8, /* VSEN12 */
+ 0xac, /* VSEN13 */
+ 0x98, /* VSEN14 */
+ 0x9c, /* VSEN15 */
+ 0xa0, /* VSEN16 */
+ 0xa4, /* VSEN17 */
+};
+
+#define IN_LSB_REG(index, type) \
+ (((type) == 1) ? W83795_REG_IN_HL_LSB[(index)] \
+ : (W83795_REG_IN_HL_LSB[(index)] + 1))
+
+#define IN_LSB_SHIFT 0
+#define IN_LSB_IDX 1
+static const u8 IN_LSB_SHIFT_IDX[][2] = {
+ /* High/Low LSB shift, LSB No. */
+ {0x00, 0x00}, /* VSEN1 */
+ {0x02, 0x00}, /* VSEN2 */
+ {0x04, 0x00}, /* VSEN3 */
+ {0x06, 0x00}, /* VSEN4 */
+ {0x00, 0x01}, /* VSEN5 */
+ {0x02, 0x01}, /* VSEN6 */
+ {0x04, 0x01}, /* VSEN7 */
+ {0x06, 0x01}, /* VSEN8 */
+ {0x00, 0x02}, /* VSEN9 */
+ {0x02, 0x02}, /* VSEN10 */
+ {0x04, 0x02}, /* VSEN11 */
+ {0x00, 0x03}, /* VTT */
+ {0x02, 0x03}, /* 3VDD */
+ {0x04, 0x03}, /* 3VSB */
+ {0x06, 0x03}, /* VBAT */
+ {0x06, 0x04}, /* VSEN12 */
+ {0x06, 0x05}, /* VSEN13 */
+ {0x06, 0x06}, /* VSEN14 */
+ {0x06, 0x07}, /* VSEN15 */
+ {0x06, 0x08}, /* VSEN16 */
+ {0x06, 0x09}, /* VSEN17 */
+};
+
+
+#define W83795_REG_FAN(index) (0x2E + (index))
+#define W83795_REG_FAN_MIN_HL(index) (0xB6 + (index))
+#define W83795_REG_FAN_MIN_LSB(index) (0xC4 + (index) / 2)
+#define W83795_REG_FAN_MIN_LSB_SHIFT(index) \
+ (((index) & 1) ? 4 : 0)
+
+#define W83795_REG_VID_CTRL 0x6A
+
+#define W83795_REG_ALARM_CTRL 0x40
+#define ALARM_CTRL_RTSACS (1 << 7)
+#define W83795_REG_ALARM(index) (0x41 + (index))
+#define W83795_REG_CLR_CHASSIS 0x4D
+#define W83795_REG_BEEP(index) (0x50 + (index))
+
+#define W83795_REG_OVT_CFG 0x58
+#define OVT_CFG_SEL (1 << 7)
+
+
+#define W83795_REG_FCMS1 0x201
+#define W83795_REG_FCMS2 0x208
+#define W83795_REG_TFMR(index) (0x202 + (index))
+#define W83795_REG_FOMC 0x20F
+
+#define W83795_REG_TSS(index) (0x209 + (index))
+
+#define TSS_MAP_RESERVED 0xff
+static const u8 tss_map[4][6] = {
+ { 0, 1, 2, 3, 4, 5},
+ { 6, 7, 8, 9, 0, 1},
+ {10, 11, 12, 13, 2, 3},
+ { 4, 5, 4, 5, TSS_MAP_RESERVED, TSS_MAP_RESERVED},
+};
+
+#define PWM_OUTPUT 0
+#define PWM_FREQ 1
+#define PWM_START 2
+#define PWM_NONSTOP 3
+#define PWM_STOP_TIME 4
+#define W83795_REG_PWM(index, nr) (0x210 + (nr) * 8 + (index))
+
+#define W83795_REG_FTSH(index) (0x240 + (index) * 2)
+#define W83795_REG_FTSL(index) (0x241 + (index) * 2)
+#define W83795_REG_TFTS 0x250
+
+#define TEMP_PWM_TTTI 0
+#define TEMP_PWM_CTFS 1
+#define TEMP_PWM_HCT 2
+#define TEMP_PWM_HOT 3
+#define W83795_REG_TTTI(index) (0x260 + (index))
+#define W83795_REG_CTFS(index) (0x268 + (index))
+#define W83795_REG_HT(index) (0x270 + (index))
+
+#define SF4_TEMP 0
+#define SF4_PWM 1
+#define W83795_REG_SF4_TEMP(temp_num, index) \
+ (0x280 + 0x10 * (temp_num) + (index))
+#define W83795_REG_SF4_PWM(temp_num, index) \
+ (0x288 + 0x10 * (temp_num) + (index))
+
+#define W83795_REG_DTSC 0x301
+#define W83795_REG_DTSE 0x302
+#define W83795_REG_DTS(index) (0x26 + (index))
+#define W83795_REG_PECI_TBASE(index) (0x320 + (index))
+
+#define DTS_CRIT 0
+#define DTS_CRIT_HYST 1
+#define DTS_WARN 2
+#define DTS_WARN_HYST 3
+#define W83795_REG_DTS_EXT(index) (0xB2 + (index))
+
+#define SETUP_PWM_DEFAULT 0
+#define SETUP_PWM_UPTIME 1
+#define SETUP_PWM_DOWNTIME 2
+#define W83795_REG_SETUP_PWM(index) (0x20C + (index))
+
+static inline u16 in_from_reg(u8 index, u16 val)
+{
+ /* 3VDD, 3VSB and VBAT: 6 mV/bit; other inputs: 2 mV/bit */
+ if (index >= 12 && index <= 14)
+ return val * 6;
+ else
+ return val * 2;
+}
+
+static inline u16 in_to_reg(u8 index, u16 val)
+{
+ if (index >= 12 && index <= 14)
+ return val / 6;
+ else
+ return val / 2;
+}
+
+static inline unsigned long fan_from_reg(u16 val)
+{
+ if ((val == 0xfff) || (val == 0))
+ return 0;
+ return 1350000UL / val;
+}
+
+static inline u16 fan_to_reg(long rpm)
+{
+ if (rpm <= 0)
+ return 0x0fff;
+ return clamp_val((1350000 + (rpm >> 1)) / rpm, 1, 0xffe);
+}
+
+static inline unsigned long time_from_reg(u8 reg)
+{
+ return reg * 100;
+}
+
+static inline u8 time_to_reg(unsigned long val)
+{
+ return clamp_val((val + 50) / 100, 0, 0xff);
+}
+
+static inline long temp_from_reg(s8 reg)
+{
+ return reg * 1000;
+}
+
+static inline s8 temp_to_reg(long val, s8 min, s8 max)
+{
+ return clamp_val(val / 1000, min, max);
+}
+
+static const u16 pwm_freq_cksel0[16] = {
+ 1024, 512, 341, 256, 205, 171, 146, 128,
+ 85, 64, 32, 16, 8, 4, 2, 1
+};
+
+static unsigned int pwm_freq_from_reg(u8 reg, u16 clkin)
+{
+ unsigned long base_clock;
+
+ if (reg & 0x80) {
+ base_clock = clkin * 1000 / ((clkin == 48000) ? 384 : 256);
+ return base_clock / ((reg & 0x7f) + 1);
+ } else
+ return pwm_freq_cksel0[reg & 0x0f];
+}
+
+static u8 pwm_freq_to_reg(unsigned long val, u16 clkin)
+{
+ unsigned long base_clock;
+ u8 reg0, reg1;
+ unsigned long best0, best1;
+
+ /* Best fit for cksel = 0 */
+ reg0 = find_closest_descending(val, pwm_freq_cksel0,
+ ARRAY_SIZE(pwm_freq_cksel0));
+ if (val < 375) /* cksel = 1 can't beat this */
+ return reg0;
+ best0 = pwm_freq_cksel0[reg0];
+
+ /* Best fit for cksel = 1 */
+ base_clock = clkin * 1000 / ((clkin == 48000) ? 384 : 256);
+ reg1 = clamp_val(DIV_ROUND_CLOSEST(base_clock, val), 1, 128);
+ best1 = base_clock / reg1;
+ reg1 = 0x80 | (reg1 - 1);
+
+ /* Choose the closest one */
+ if (abs(val - best0) > abs(val - best1))
+ return reg1;
+ else
+ return reg0;
+}
+
+enum chip_types {w83795g, w83795adg};
+
+struct w83795_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ unsigned long last_updated; /* In jiffies */
+ enum chip_types chip_type;
+
+ u8 bank;
+
+ u32 has_in; /* Enable monitor VIN or not */
+ u8 has_dyn_in; /* Only in2-0 can have this */
+ u16 in[21][3]; /* Register value, read/high/low */
+ u8 in_lsb[10][3]; /* LSB Register value, high/low */
+ u8 has_gain; /* has gain: in17-20 * 8 */
+
+ u16 has_fan; /* Enable fan14-1 or not */
+ u16 fan[14]; /* Register value combine */
+ u16 fan_min[14]; /* Register value combine */
+
+ u8 has_temp; /* Enable monitor temp6-1 or not */
+ s8 temp[6][5]; /* current, crit, crit_hyst, warn, warn_hyst */
+ u8 temp_read_vrlsb[6];
+ u8 temp_mode; /* Bit vector, 0 = TR, 1 = TD */
+ u8 temp_src[3]; /* Register value */
+
+ u8 enable_dts; /*
+ * Enable PECI and SB-TSI,
+ * bit 0: =1 enable, =0 disable,
+ * bit 1: =1 AMD SB-TSI, =0 Intel PECI
+ */
+ u8 has_dts; /* Enable monitor DTS temp */
+ s8 dts[8]; /* Register value */
+ u8 dts_read_vrlsb[8]; /* Register value */
+ s8 dts_ext[4]; /* Register value */
+
+ u8 has_pwm; /*
+ * 795g supports 8 pwm, 795adg only supports 2,
+ * no config register, only affected by chip
+ * type
+ */
+ u8 pwm[8][5]; /*
+ * Register value, output, freq, start,
+ * non stop, stop time
+ */
+ u16 clkin; /* CLKIN frequency in kHz */
+ u8 pwm_fcms[2]; /* Register value */
+ u8 pwm_tfmr[6]; /* Register value */
+ u8 pwm_fomc; /* Register value */
+
+ u16 target_speed[8]; /*
+ * Register value, target speed for speed
+ * cruise
+ */
+ u8 tol_speed; /* tolerance of target speed */
+ u8 pwm_temp[6][4]; /* TTTI, CTFS, HCT, HOT */
+ u8 sf4_reg[6][2][7]; /* 6 temp, temp/dcpwm, 7 registers */
+
+ u8 setup_pwm[3]; /* Register value */
+
+ u8 alarms[6]; /* Register value */
+ u8 enable_beep;
+ u8 beeps[6]; /* Register value */
+
+ bool valid;
+ char valid_limits;
+ char valid_pwm_config;
+};
+
+/*
+ * Hardware access
+ * We assume that nobdody can change the bank outside the driver.
+ */
+
+/* Must be called with data->update_lock held, except during initialization */
+static int w83795_set_bank(struct i2c_client *client, u8 bank)
+{
+ struct w83795_data *data = i2c_get_clientdata(client);
+ int err;
+
+ /* If the same bank is already set, nothing to do */
+ if ((data->bank & 0x07) == bank)
+ return 0;
+
+ /* Change to new bank, preserve all other bits */
+ bank |= data->bank & ~0x07;
+ err = i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, bank);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to set bank to %d, err %d\n",
+ (int)bank, err);
+ return err;
+ }
+ data->bank = bank;
+
+ return 0;
+}
+
+/* Must be called with data->update_lock held, except during initialization */
+static u8 w83795_read(struct i2c_client *client, u16 reg)
+{
+ int err;
+
+ err = w83795_set_bank(client, reg >> 8);
+ if (err < 0)
+ return 0x00; /* Arbitrary */
+
+ err = i2c_smbus_read_byte_data(client, reg & 0xff);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Failed to read from register 0x%03x, err %d\n",
+ (int)reg, err);
+ return 0x00; /* Arbitrary */
+ }
+ return err;
+}
+
+/* Must be called with data->update_lock held, except during initialization */
+static int w83795_write(struct i2c_client *client, u16 reg, u8 value)
+{
+ int err;
+
+ err = w83795_set_bank(client, reg >> 8);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(client, reg & 0xff, value);
+ if (err < 0)
+ dev_err(&client->dev,
+ "Failed to write to register 0x%03x, err %d\n",
+ (int)reg, err);
+ return err;
+}
+
+static void w83795_update_limits(struct i2c_client *client)
+{
+ struct w83795_data *data = i2c_get_clientdata(client);
+ int i, limit;
+ u8 lsb;
+
+ /* Read the voltage limits */
+ for (i = 0; i < ARRAY_SIZE(data->in); i++) {
+ if (!(data->has_in & (1 << i)))
+ continue;
+ data->in[i][IN_MAX] =
+ w83795_read(client, W83795_REG_IN[i][IN_MAX]);
+ data->in[i][IN_LOW] =
+ w83795_read(client, W83795_REG_IN[i][IN_LOW]);
+ }
+ for (i = 0; i < ARRAY_SIZE(data->in_lsb); i++) {
+ if ((i == 2 && data->chip_type == w83795adg) ||
+ (i >= 4 && !(data->has_in & (1 << (i + 11)))))
+ continue;
+ data->in_lsb[i][IN_MAX] =
+ w83795_read(client, IN_LSB_REG(i, IN_MAX));
+ data->in_lsb[i][IN_LOW] =
+ w83795_read(client, IN_LSB_REG(i, IN_LOW));
+ }
+
+ /* Read the fan limits */
+ lsb = 0; /* Silent false gcc warning */
+ for (i = 0; i < ARRAY_SIZE(data->fan); i++) {
+ /*
+ * Each register contains LSB for 2 fans, but we want to
+ * read it only once to save time
+ */
+ if ((i & 1) == 0 && (data->has_fan & (3 << i)))
+ lsb = w83795_read(client, W83795_REG_FAN_MIN_LSB(i));
+
+ if (!(data->has_fan & (1 << i)))
+ continue;
+ data->fan_min[i] =
+ w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4;
+ data->fan_min[i] |=
+ (lsb >> W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F;
+ }
+
+ /* Read the temperature limits */
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ if (!(data->has_temp & (1 << i)))
+ continue;
+ for (limit = TEMP_CRIT; limit <= TEMP_WARN_HYST; limit++)
+ data->temp[i][limit] =
+ w83795_read(client, W83795_REG_TEMP[i][limit]);
+ }
+
+ /* Read the DTS limits */
+ if (data->enable_dts) {
+ for (limit = DTS_CRIT; limit <= DTS_WARN_HYST; limit++)
+ data->dts_ext[limit] =
+ w83795_read(client, W83795_REG_DTS_EXT(limit));
+ }
+
+ /* Read beep settings */
+ if (data->enable_beep) {
+ for (i = 0; i < ARRAY_SIZE(data->beeps); i++)
+ data->beeps[i] =
+ w83795_read(client, W83795_REG_BEEP(i));
+ }
+
+ data->valid_limits = 1;
+}
+
+static struct w83795_data *w83795_update_pwm_config(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ int i, tmp;
+
+ mutex_lock(&data->update_lock);
+
+ if (data->valid_pwm_config)
+ goto END;
+
+ /* Read temperature source selection */
+ for (i = 0; i < ARRAY_SIZE(data->temp_src); i++)
+ data->temp_src[i] = w83795_read(client, W83795_REG_TSS(i));
+
+ /* Read automatic fan speed control settings */
+ data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1);
+ data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2);
+ for (i = 0; i < ARRAY_SIZE(data->pwm_tfmr); i++)
+ data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i));
+ data->pwm_fomc = w83795_read(client, W83795_REG_FOMC);
+ for (i = 0; i < data->has_pwm; i++) {
+ for (tmp = PWM_FREQ; tmp <= PWM_STOP_TIME; tmp++)
+ data->pwm[i][tmp] =
+ w83795_read(client, W83795_REG_PWM(i, tmp));
+ }
+ for (i = 0; i < ARRAY_SIZE(data->target_speed); i++) {
+ data->target_speed[i] =
+ w83795_read(client, W83795_REG_FTSH(i)) << 4;
+ data->target_speed[i] |=
+ w83795_read(client, W83795_REG_FTSL(i)) >> 4;
+ }
+ data->tol_speed = w83795_read(client, W83795_REG_TFTS) & 0x3f;
+
+ for (i = 0; i < ARRAY_SIZE(data->pwm_temp); i++) {
+ data->pwm_temp[i][TEMP_PWM_TTTI] =
+ w83795_read(client, W83795_REG_TTTI(i)) & 0x7f;
+ data->pwm_temp[i][TEMP_PWM_CTFS] =
+ w83795_read(client, W83795_REG_CTFS(i));
+ tmp = w83795_read(client, W83795_REG_HT(i));
+ data->pwm_temp[i][TEMP_PWM_HCT] = tmp >> 4;
+ data->pwm_temp[i][TEMP_PWM_HOT] = tmp & 0x0f;
+ }
+
+ /* Read SmartFanIV trip points */
+ for (i = 0; i < ARRAY_SIZE(data->sf4_reg); i++) {
+ for (tmp = 0; tmp < 7; tmp++) {
+ data->sf4_reg[i][SF4_TEMP][tmp] =
+ w83795_read(client,
+ W83795_REG_SF4_TEMP(i, tmp));
+ data->sf4_reg[i][SF4_PWM][tmp] =
+ w83795_read(client, W83795_REG_SF4_PWM(i, tmp));
+ }
+ }
+
+ /* Read setup PWM */
+ for (i = 0; i < ARRAY_SIZE(data->setup_pwm); i++)
+ data->setup_pwm[i] =
+ w83795_read(client, W83795_REG_SETUP_PWM(i));
+
+ data->valid_pwm_config = 1;
+
+END:
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+static struct w83795_data *w83795_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ u16 tmp;
+ u8 intrusion;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if (!data->valid_limits)
+ w83795_update_limits(client);
+
+ if (!(time_after(jiffies, data->last_updated + HZ * 2)
+ || !data->valid))
+ goto END;
+
+ /* Update the voltages value */
+ for (i = 0; i < ARRAY_SIZE(data->in); i++) {
+ if (!(data->has_in & (1 << i)))
+ continue;
+ tmp = w83795_read(client, W83795_REG_IN[i][IN_READ]) << 2;
+ tmp |= w83795_read(client, W83795_REG_VRLSB) >> 6;
+ data->in[i][IN_READ] = tmp;
+ }
+
+ /* in0-2 can have dynamic limits (W83795G only) */
+ if (data->has_dyn_in) {
+ u8 lsb_max = w83795_read(client, IN_LSB_REG(0, IN_MAX));
+ u8 lsb_low = w83795_read(client, IN_LSB_REG(0, IN_LOW));
+
+ for (i = 0; i < 3; i++) {
+ if (!(data->has_dyn_in & (1 << i)))
+ continue;
+ data->in[i][IN_MAX] =
+ w83795_read(client, W83795_REG_IN[i][IN_MAX]);
+ data->in[i][IN_LOW] =
+ w83795_read(client, W83795_REG_IN[i][IN_LOW]);
+ data->in_lsb[i][IN_MAX] = (lsb_max >> (2 * i)) & 0x03;
+ data->in_lsb[i][IN_LOW] = (lsb_low >> (2 * i)) & 0x03;
+ }
+ }
+
+ /* Update fan */
+ for (i = 0; i < ARRAY_SIZE(data->fan); i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+ data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4;
+ data->fan[i] |= w83795_read(client, W83795_REG_VRLSB) >> 4;
+ }
+
+ /* Update temperature */
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ data->temp[i][TEMP_READ] =
+ w83795_read(client, W83795_REG_TEMP[i][TEMP_READ]);
+ data->temp_read_vrlsb[i] =
+ w83795_read(client, W83795_REG_VRLSB);
+ }
+
+ /* Update dts temperature */
+ if (data->enable_dts) {
+ for (i = 0; i < ARRAY_SIZE(data->dts); i++) {
+ if (!(data->has_dts & (1 << i)))
+ continue;
+ data->dts[i] =
+ w83795_read(client, W83795_REG_DTS(i));
+ data->dts_read_vrlsb[i] =
+ w83795_read(client, W83795_REG_VRLSB);
+ }
+ }
+
+ /* Update pwm output */
+ for (i = 0; i < data->has_pwm; i++) {
+ data->pwm[i][PWM_OUTPUT] =
+ w83795_read(client, W83795_REG_PWM(i, PWM_OUTPUT));
+ }
+
+ /*
+ * Update intrusion and alarms
+ * It is important to read intrusion first, because reading from
+ * register SMI STS6 clears the interrupt status temporarily.
+ */
+ tmp = w83795_read(client, W83795_REG_ALARM_CTRL);
+ /* Switch to interrupt status for intrusion if needed */
+ if (tmp & ALARM_CTRL_RTSACS)
+ w83795_write(client, W83795_REG_ALARM_CTRL,
+ tmp & ~ALARM_CTRL_RTSACS);
+ intrusion = w83795_read(client, W83795_REG_ALARM(5)) & (1 << 6);
+ /* Switch to real-time alarms */
+ w83795_write(client, W83795_REG_ALARM_CTRL, tmp | ALARM_CTRL_RTSACS);
+ for (i = 0; i < ARRAY_SIZE(data->alarms); i++)
+ data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i));
+ data->alarms[5] |= intrusion;
+ /* Restore original configuration if needed */
+ if (!(tmp & ALARM_CTRL_RTSACS))
+ w83795_write(client, W83795_REG_ALARM_CTRL,
+ tmp & ~ALARM_CTRL_RTSACS);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+
+END:
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+/*
+ * Sysfs attributes
+ */
+
+#define ALARM_STATUS 0
+#define BEEP_ENABLE 1
+static ssize_t
+show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = w83795_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index >> 3;
+ int bit = sensor_attr->index & 0x07;
+ u8 val;
+
+ if (nr == ALARM_STATUS)
+ val = (data->alarms[index] >> bit) & 1;
+ else /* BEEP_ENABLE */
+ val = (data->beeps[index] >> bit) & 1;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t
+store_beep(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index >> 3;
+ int shift = sensor_attr->index & 0x07;
+ u8 beep_bit = 1 << shift;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->beeps[index] = w83795_read(client, W83795_REG_BEEP(index));
+ data->beeps[index] &= ~beep_bit;
+ data->beeps[index] |= val << shift;
+ w83795_write(client, W83795_REG_BEEP(index), data->beeps[index]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* Write 0 to clear chassis alarm */
+static ssize_t
+store_chassis_clear(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0 || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ val = w83795_read(client, W83795_REG_CLR_CHASSIS);
+ val |= 0x80;
+ w83795_write(client, W83795_REG_CLR_CHASSIS, val);
+
+ /* Clear status and force cache refresh */
+ w83795_read(client, W83795_REG_ALARM(5));
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+#define FAN_INPUT 0
+#define FAN_MIN 1
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83795_data *data = w83795_update_device(dev);
+ u16 val;
+
+ if (nr == FAN_INPUT)
+ val = data->fan[index] & 0x0fff;
+ else
+ val = data->fan_min[index] & 0x0fff;
+
+ return sprintf(buf, "%lu\n", fan_from_reg(val));
+}
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+ val = fan_to_reg(val);
+
+ mutex_lock(&data->update_lock);
+ data->fan_min[index] = val;
+ w83795_write(client, W83795_REG_FAN_MIN_HL(index), (val >> 4) & 0xff);
+ val &= 0x0f;
+ if (index & 1) {
+ val <<= 4;
+ val |= w83795_read(client, W83795_REG_FAN_MIN_LSB(index))
+ & 0x0f;
+ } else {
+ val |= w83795_read(client, W83795_REG_FAN_MIN_LSB(index))
+ & 0xf0;
+ }
+ w83795_write(client, W83795_REG_FAN_MIN_LSB(index), val & 0xff);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data;
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned int val;
+
+ data = nr == PWM_OUTPUT ? w83795_update_device(dev)
+ : w83795_update_pwm_config(dev);
+
+ switch (nr) {
+ case PWM_STOP_TIME:
+ val = time_from_reg(data->pwm[index][nr]);
+ break;
+ case PWM_FREQ:
+ val = pwm_freq_from_reg(data->pwm[index][nr], data->clkin);
+ break;
+ default:
+ val = data->pwm[index][nr];
+ break;
+ }
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ switch (nr) {
+ case PWM_STOP_TIME:
+ val = time_to_reg(val);
+ break;
+ case PWM_FREQ:
+ val = pwm_freq_to_reg(val, data->clkin);
+ break;
+ default:
+ val = clamp_val(val, 0, 0xff);
+ break;
+ }
+ w83795_write(client, W83795_REG_PWM(index, nr), val);
+ data->pwm[index][nr] = val;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ int index = sensor_attr->index;
+ u8 tmp;
+
+ /* Speed cruise mode */
+ if (data->pwm_fcms[0] & (1 << index)) {
+ tmp = 2;
+ goto out;
+ }
+ /* Thermal cruise or SmartFan IV mode */
+ for (tmp = 0; tmp < 6; tmp++) {
+ if (data->pwm_tfmr[tmp] & (1 << index)) {
+ tmp = 3;
+ goto out;
+ }
+ }
+ /* Manual mode */
+ tmp = 1;
+
+out:
+ return sprintf(buf, "%u\n", tmp);
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ unsigned long val;
+ int i;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ if (val < 1 || val > 2)
+ return -EINVAL;
+
+#ifndef CONFIG_SENSORS_W83795_FANCTRL
+ if (val > 1) {
+ dev_warn(dev, "Automatic fan speed control support disabled\n");
+ dev_warn(dev, "Build with CONFIG_SENSORS_W83795_FANCTRL=y if you want it\n");
+ return -EOPNOTSUPP;
+ }
+#endif
+
+ mutex_lock(&data->update_lock);
+ switch (val) {
+ case 1:
+ /* Clear speed cruise mode bits */
+ data->pwm_fcms[0] &= ~(1 << index);
+ w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]);
+ /* Clear thermal cruise mode bits */
+ for (i = 0; i < 6; i++) {
+ data->pwm_tfmr[i] &= ~(1 << index);
+ w83795_write(client, W83795_REG_TFMR(i),
+ data->pwm_tfmr[i]);
+ }
+ break;
+ case 2:
+ data->pwm_fcms[0] |= (1 << index);
+ w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]);
+ break;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ int index = to_sensor_dev_attr_2(attr)->index;
+ unsigned int mode;
+
+ if (data->pwm_fomc & (1 << index))
+ mode = 0; /* DC */
+ else
+ mode = 1; /* PWM */
+
+ return sprintf(buf, "%u\n", mode);
+}
+
+/*
+ * Check whether a given temperature source can ever be useful.
+ * Returns the number of selectable temperature channels which are
+ * enabled.
+ */
+static int w83795_tss_useful(const struct w83795_data *data, int tsrc)
+{
+ int useful = 0, i;
+
+ for (i = 0; i < 4; i++) {
+ if (tss_map[i][tsrc] == TSS_MAP_RESERVED)
+ continue;
+ if (tss_map[i][tsrc] < 6) /* Analog */
+ useful += (data->has_temp >> tss_map[i][tsrc]) & 1;
+ else /* Digital */
+ useful += (data->has_dts >> (tss_map[i][tsrc] - 6)) & 1;
+ }
+
+ return useful;
+}
+
+static ssize_t
+show_temp_src(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ int index = sensor_attr->index;
+ u8 tmp = data->temp_src[index / 2];
+
+ if (index & 1)
+ tmp >>= 4; /* Pick high nibble */
+ else
+ tmp &= 0x0f; /* Pick low nibble */
+
+ /* Look-up the actual temperature channel number */
+ if (tmp >= 4 || tss_map[tmp][index] == TSS_MAP_RESERVED)
+ return -EINVAL; /* Shouldn't happen */
+
+ return sprintf(buf, "%u\n", (unsigned int)tss_map[tmp][index] + 1);
+}
+
+static ssize_t
+store_temp_src(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ int tmp;
+ unsigned long channel;
+ u8 val = index / 2;
+
+ if (kstrtoul(buf, 10, &channel) < 0 ||
+ channel < 1 || channel > 14)
+ return -EINVAL;
+
+ /* Check if request can be fulfilled */
+ for (tmp = 0; tmp < 4; tmp++) {
+ if (tss_map[tmp][index] == channel - 1)
+ break;
+ }
+ if (tmp == 4) /* No match */
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (index & 1) {
+ tmp <<= 4;
+ data->temp_src[val] &= 0x0f;
+ } else {
+ data->temp_src[val] &= 0xf0;
+ }
+ data->temp_src[val] |= tmp;
+ w83795_write(client, W83795_REG_TSS(val), data->temp_src[val]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+#define TEMP_PWM_ENABLE 0
+#define TEMP_PWM_FAN_MAP 1
+static ssize_t
+show_temp_pwm_enable(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ u8 tmp = 0xff;
+
+ switch (nr) {
+ case TEMP_PWM_ENABLE:
+ tmp = (data->pwm_fcms[1] >> index) & 1;
+ if (tmp)
+ tmp = 4;
+ else
+ tmp = 3;
+ break;
+ case TEMP_PWM_FAN_MAP:
+ tmp = data->pwm_tfmr[index];
+ break;
+ }
+
+ return sprintf(buf, "%u\n", tmp);
+}
+
+static ssize_t
+store_temp_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long tmp;
+
+ if (kstrtoul(buf, 10, &tmp) < 0)
+ return -EINVAL;
+
+ switch (nr) {
+ case TEMP_PWM_ENABLE:
+ if (tmp != 3 && tmp != 4)
+ return -EINVAL;
+ tmp -= 3;
+ mutex_lock(&data->update_lock);
+ data->pwm_fcms[1] &= ~(1 << index);
+ data->pwm_fcms[1] |= tmp << index;
+ w83795_write(client, W83795_REG_FCMS2, data->pwm_fcms[1]);
+ mutex_unlock(&data->update_lock);
+ break;
+ case TEMP_PWM_FAN_MAP:
+ mutex_lock(&data->update_lock);
+ tmp = clamp_val(tmp, 0, 0xff);
+ w83795_write(client, W83795_REG_TFMR(index), tmp);
+ data->pwm_tfmr[index] = tmp;
+ mutex_unlock(&data->update_lock);
+ break;
+ }
+ return count;
+}
+
+#define FANIN_TARGET 0
+#define FANIN_TOL 1
+static ssize_t
+show_fanin(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ u16 tmp = 0;
+
+ switch (nr) {
+ case FANIN_TARGET:
+ tmp = fan_from_reg(data->target_speed[index]);
+ break;
+ case FANIN_TOL:
+ tmp = data->tol_speed;
+ break;
+ }
+
+ return sprintf(buf, "%u\n", tmp);
+}
+
+static ssize_t
+store_fanin(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ switch (nr) {
+ case FANIN_TARGET:
+ val = fan_to_reg(clamp_val(val, 0, 0xfff));
+ w83795_write(client, W83795_REG_FTSH(index), val >> 4);
+ w83795_write(client, W83795_REG_FTSL(index), (val << 4) & 0xf0);
+ data->target_speed[index] = val;
+ break;
+ case FANIN_TOL:
+ val = clamp_val(val, 0, 0x3f);
+ w83795_write(client, W83795_REG_TFTS, val);
+ data->tol_speed = val;
+ break;
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+
+static ssize_t
+show_temp_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ long tmp = temp_from_reg(data->pwm_temp[index][nr]);
+
+ return sprintf(buf, "%ld\n", tmp);
+}
+
+static ssize_t
+store_temp_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long val;
+ u8 tmp;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ val /= 1000;
+
+ mutex_lock(&data->update_lock);
+ switch (nr) {
+ case TEMP_PWM_TTTI:
+ val = clamp_val(val, 0, 0x7f);
+ w83795_write(client, W83795_REG_TTTI(index), val);
+ break;
+ case TEMP_PWM_CTFS:
+ val = clamp_val(val, 0, 0x7f);
+ w83795_write(client, W83795_REG_CTFS(index), val);
+ break;
+ case TEMP_PWM_HCT:
+ val = clamp_val(val, 0, 0x0f);
+ tmp = w83795_read(client, W83795_REG_HT(index));
+ tmp &= 0x0f;
+ tmp |= (val << 4) & 0xf0;
+ w83795_write(client, W83795_REG_HT(index), tmp);
+ break;
+ case TEMP_PWM_HOT:
+ val = clamp_val(val, 0, 0x0f);
+ tmp = w83795_read(client, W83795_REG_HT(index));
+ tmp &= 0xf0;
+ tmp |= val & 0x0f;
+ w83795_write(client, W83795_REG_HT(index), tmp);
+ break;
+ }
+ data->pwm_temp[index][nr] = val;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_sf4_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+
+ return sprintf(buf, "%u\n", data->sf4_reg[index][SF4_PWM][nr]);
+}
+
+static ssize_t
+store_sf4_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ w83795_write(client, W83795_REG_SF4_PWM(index, nr), val);
+ data->sf4_reg[index][SF4_PWM][nr] = val;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_sf4_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+
+ return sprintf(buf, "%u\n",
+ (data->sf4_reg[index][SF4_TEMP][nr]) * 1000);
+}
+
+static ssize_t
+store_sf4_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ val /= 1000;
+
+ mutex_lock(&data->update_lock);
+ w83795_write(client, W83795_REG_SF4_TEMP(index, nr), val);
+ data->sf4_reg[index][SF4_TEMP][nr] = val;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83795_data *data = w83795_update_device(dev);
+ long temp = temp_from_reg(data->temp[index][nr]);
+
+ if (nr == TEMP_READ)
+ temp += (data->temp_read_vrlsb[index] >> 6) * 250;
+ return sprintf(buf, "%ld\n", temp);
+}
+
+static ssize_t
+store_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ long tmp;
+
+ if (kstrtol(buf, 10, &tmp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->temp[index][nr] = temp_to_reg(tmp, -128, 127);
+ w83795_write(client, W83795_REG_TEMP[index][nr], data->temp[index][nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+static ssize_t
+show_dts_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = dev_get_drvdata(dev);
+ int tmp;
+
+ if (data->enable_dts & 2)
+ tmp = 5;
+ else
+ tmp = 6;
+
+ return sprintf(buf, "%d\n", tmp);
+}
+
+static ssize_t
+show_dts(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ struct w83795_data *data = w83795_update_device(dev);
+ long temp = temp_from_reg(data->dts[index]);
+
+ temp += (data->dts_read_vrlsb[index] >> 6) * 250;
+ return sprintf(buf, "%ld\n", temp);
+}
+
+static ssize_t
+show_dts_ext(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ struct w83795_data *data = dev_get_drvdata(dev);
+ long temp = temp_from_reg(data->dts_ext[nr]);
+
+ return sprintf(buf, "%ld\n", temp);
+}
+
+static ssize_t
+store_dts_ext(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ long tmp;
+
+ if (kstrtol(buf, 10, &tmp) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->dts_ext[nr] = temp_to_reg(tmp, -128, 127);
+ w83795_write(client, W83795_REG_DTS_EXT(nr), data->dts_ext[nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+static ssize_t
+show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct w83795_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ int tmp;
+
+ if (data->temp_mode & (1 << index))
+ tmp = 3; /* Thermal diode */
+ else
+ tmp = 4; /* Thermistor */
+
+ return sprintf(buf, "%d\n", tmp);
+}
+
+/* Only for temp1-4 (temp5-6 can only be thermistor) */
+static ssize_t
+store_temp_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int index = sensor_attr->index;
+ int reg_shift;
+ unsigned long val;
+ u8 tmp;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ if ((val != 4) && (val != 3))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (val == 3) {
+ /* Thermal diode */
+ val = 0x01;
+ data->temp_mode |= 1 << index;
+ } else if (val == 4) {
+ /* Thermistor */
+ val = 0x03;
+ data->temp_mode &= ~(1 << index);
+ }
+
+ reg_shift = 2 * index;
+ tmp = w83795_read(client, W83795_REG_TEMP_CTRL2);
+ tmp &= ~(0x03 << reg_shift);
+ tmp |= val << reg_shift;
+ w83795_write(client, W83795_REG_TEMP_CTRL2, tmp);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+/* show/store VIN */
+static ssize_t
+show_in(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83795_data *data = w83795_update_device(dev);
+ u16 val = data->in[index][nr];
+ u8 lsb_idx;
+
+ switch (nr) {
+ case IN_READ:
+ /* calculate this value again by sensors as sensors3.conf */
+ if ((index >= 17) &&
+ !((data->has_gain >> (index - 17)) & 1))
+ val *= 8;
+ break;
+ case IN_MAX:
+ case IN_LOW:
+ lsb_idx = IN_LSB_SHIFT_IDX[index][IN_LSB_IDX];
+ val <<= 2;
+ val |= (data->in_lsb[lsb_idx][nr] >>
+ IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]) & 0x03;
+ if ((index >= 17) &&
+ !((data->has_gain >> (index - 17)) & 1))
+ val *= 8;
+ break;
+ }
+ val = in_from_reg(index, val);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+store_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ u8 tmp;
+ u8 lsb_idx;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+ val = in_to_reg(index, val);
+
+ if ((index >= 17) &&
+ !((data->has_gain >> (index - 17)) & 1))
+ val /= 8;
+ val = clamp_val(val, 0, 0x3FF);
+ mutex_lock(&data->update_lock);
+
+ lsb_idx = IN_LSB_SHIFT_IDX[index][IN_LSB_IDX];
+ tmp = w83795_read(client, IN_LSB_REG(lsb_idx, nr));
+ tmp &= ~(0x03 << IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]);
+ tmp |= (val & 0x03) << IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT];
+ w83795_write(client, IN_LSB_REG(lsb_idx, nr), tmp);
+ data->in_lsb[lsb_idx][nr] = tmp;
+
+ tmp = (val >> 2) & 0xff;
+ w83795_write(client, W83795_REG_IN[index][nr], tmp);
+ data->in[index][nr] = tmp;
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+#ifdef CONFIG_SENSORS_W83795_FANCTRL
+static ssize_t
+show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ struct w83795_data *data = w83795_update_pwm_config(dev);
+ u16 val = data->setup_pwm[nr];
+
+ switch (nr) {
+ case SETUP_PWM_UPTIME:
+ case SETUP_PWM_DOWNTIME:
+ val = time_from_reg(val);
+ break;
+ }
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+store_sf_setup(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83795_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ switch (nr) {
+ case SETUP_PWM_DEFAULT:
+ val = clamp_val(val, 0, 0xff);
+ break;
+ case SETUP_PWM_UPTIME:
+ case SETUP_PWM_DOWNTIME:
+ val = time_to_reg(val);
+ if (val == 0)
+ return -EINVAL;
+ break;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->setup_pwm[nr] = val;
+ w83795_write(client, W83795_REG_SETUP_PWM(nr), val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+#endif
+
+
+#define NOT_USED -1
+
+/*
+ * Don't change the attribute order, _max, _min and _beep are accessed by index
+ * somewhere else in the code
+ */
+#define SENSOR_ATTR_IN(index) { \
+ SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \
+ IN_READ, index), \
+ SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \
+ store_in, IN_MAX, index), \
+ SENSOR_ATTR_2(in##index##_min, S_IRUGO | S_IWUSR, show_in, \
+ store_in, IN_LOW, index), \
+ SENSOR_ATTR_2(in##index##_alarm, S_IRUGO, show_alarm_beep, \
+ NULL, ALARM_STATUS, index + ((index > 14) ? 1 : 0)), \
+ SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \
+ show_alarm_beep, store_beep, BEEP_ENABLE, \
+ index + ((index > 14) ? 1 : 0)) }
+
+/*
+ * Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code
+ */
+#define SENSOR_ATTR_FAN(index) { \
+ SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \
+ NULL, FAN_INPUT, index - 1), \
+ SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \
+ show_fan, store_fan_min, FAN_MIN, index - 1), \
+ SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \
+ NULL, ALARM_STATUS, index + 31), \
+ SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \
+ show_alarm_beep, store_beep, BEEP_ENABLE, index + 31) }
+
+#define SENSOR_ATTR_PWM(index) { \
+ SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \
+ store_pwm, PWM_OUTPUT, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \
+ show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \
+ show_pwm_mode, NULL, NOT_USED, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_FREQ, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_NONSTOP, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_START, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_STOP_TIME, index - 1), \
+ SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \
+ show_fanin, store_fanin, FANIN_TARGET, index - 1) }
+
+/*
+ * Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code
+ */
+#define SENSOR_ATTR_DTS(index) { \
+ SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \
+ show_dts_mode, NULL, NOT_USED, index - 7), \
+ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_dts, \
+ NULL, NOT_USED, index - 7), \
+ SENSOR_ATTR_2(temp##index##_crit, S_IRUGO | S_IWUSR, show_dts_ext, \
+ store_dts_ext, DTS_CRIT, NOT_USED), \
+ SENSOR_ATTR_2(temp##index##_crit_hyst, S_IRUGO | S_IWUSR, \
+ show_dts_ext, store_dts_ext, DTS_CRIT_HYST, NOT_USED), \
+ SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_dts_ext, \
+ store_dts_ext, DTS_WARN, NOT_USED), \
+ SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_dts_ext, store_dts_ext, DTS_WARN_HYST, NOT_USED), \
+ SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \
+ show_alarm_beep, NULL, ALARM_STATUS, index + 17), \
+ SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \
+ show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) }
+
+/*
+ * Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code
+ */
+#define SENSOR_ATTR_TEMP(index) { \
+ SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 5 ? S_IWUSR : 0), \
+ show_temp_mode, store_temp_mode, NOT_USED, index - 1), \
+ SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \
+ NULL, TEMP_READ, index - 1), \
+ SENSOR_ATTR_2(temp##index##_crit, S_IRUGO | S_IWUSR, show_temp, \
+ store_temp, TEMP_CRIT, index - 1), \
+ SENSOR_ATTR_2(temp##index##_crit_hyst, S_IRUGO | S_IWUSR, \
+ show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \
+ SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \
+ store_temp, TEMP_WARN, index - 1), \
+ SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_temp, store_temp, TEMP_WARN_HYST, index - 1), \
+ SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \
+ show_alarm_beep, NULL, ALARM_STATUS, \
+ index + (index > 4 ? 11 : 17)), \
+ SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \
+ show_alarm_beep, store_beep, BEEP_ENABLE, \
+ index + (index > 4 ? 11 : 17)), \
+ SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO, \
+ show_temp_pwm_enable, store_temp_pwm_enable, \
+ TEMP_PWM_ENABLE, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_channels_pwm, S_IWUSR | S_IRUGO, \
+ show_temp_pwm_enable, store_temp_pwm_enable, \
+ TEMP_PWM_FAN_MAP, index - 1), \
+ SENSOR_ATTR_2(thermal_cruise##index, S_IWUSR | S_IRUGO, \
+ show_temp_pwm, store_temp_pwm, TEMP_PWM_TTTI, index - 1), \
+ SENSOR_ATTR_2(temp##index##_warn, S_IWUSR | S_IRUGO, \
+ show_temp_pwm, store_temp_pwm, TEMP_PWM_CTFS, index - 1), \
+ SENSOR_ATTR_2(temp##index##_warn_hyst, S_IWUSR | S_IRUGO, \
+ show_temp_pwm, store_temp_pwm, TEMP_PWM_HCT, index - 1), \
+ SENSOR_ATTR_2(temp##index##_operation_hyst, S_IWUSR | S_IRUGO, \
+ show_temp_pwm, store_temp_pwm, TEMP_PWM_HOT, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point1_pwm, S_IRUGO | S_IWUSR, \
+ show_sf4_pwm, store_sf4_pwm, 0, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point2_pwm, S_IRUGO | S_IWUSR, \
+ show_sf4_pwm, store_sf4_pwm, 1, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point3_pwm, S_IRUGO | S_IWUSR, \
+ show_sf4_pwm, store_sf4_pwm, 2, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point4_pwm, S_IRUGO | S_IWUSR, \
+ show_sf4_pwm, store_sf4_pwm, 3, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point5_pwm, S_IRUGO | S_IWUSR, \
+ show_sf4_pwm, store_sf4_pwm, 4, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point6_pwm, S_IRUGO | S_IWUSR, \
+ show_sf4_pwm, store_sf4_pwm, 5, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point7_pwm, S_IRUGO | S_IWUSR, \
+ show_sf4_pwm, store_sf4_pwm, 6, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point1_temp, S_IRUGO | S_IWUSR,\
+ show_sf4_temp, store_sf4_temp, 0, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point2_temp, S_IRUGO | S_IWUSR,\
+ show_sf4_temp, store_sf4_temp, 1, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point3_temp, S_IRUGO | S_IWUSR,\
+ show_sf4_temp, store_sf4_temp, 2, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point4_temp, S_IRUGO | S_IWUSR,\
+ show_sf4_temp, store_sf4_temp, 3, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point5_temp, S_IRUGO | S_IWUSR,\
+ show_sf4_temp, store_sf4_temp, 4, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\
+ show_sf4_temp, store_sf4_temp, 5, index - 1), \
+ SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\
+ show_sf4_temp, store_sf4_temp, 6, index - 1) }
+
+
+static struct sensor_device_attribute_2 w83795_in[][5] = {
+ SENSOR_ATTR_IN(0),
+ SENSOR_ATTR_IN(1),
+ SENSOR_ATTR_IN(2),
+ SENSOR_ATTR_IN(3),
+ SENSOR_ATTR_IN(4),
+ SENSOR_ATTR_IN(5),
+ SENSOR_ATTR_IN(6),
+ SENSOR_ATTR_IN(7),
+ SENSOR_ATTR_IN(8),
+ SENSOR_ATTR_IN(9),
+ SENSOR_ATTR_IN(10),
+ SENSOR_ATTR_IN(11),
+ SENSOR_ATTR_IN(12),
+ SENSOR_ATTR_IN(13),
+ SENSOR_ATTR_IN(14),
+ SENSOR_ATTR_IN(15),
+ SENSOR_ATTR_IN(16),
+ SENSOR_ATTR_IN(17),
+ SENSOR_ATTR_IN(18),
+ SENSOR_ATTR_IN(19),
+ SENSOR_ATTR_IN(20),
+};
+
+static const struct sensor_device_attribute_2 w83795_fan[][4] = {
+ SENSOR_ATTR_FAN(1),
+ SENSOR_ATTR_FAN(2),
+ SENSOR_ATTR_FAN(3),
+ SENSOR_ATTR_FAN(4),
+ SENSOR_ATTR_FAN(5),
+ SENSOR_ATTR_FAN(6),
+ SENSOR_ATTR_FAN(7),
+ SENSOR_ATTR_FAN(8),
+ SENSOR_ATTR_FAN(9),
+ SENSOR_ATTR_FAN(10),
+ SENSOR_ATTR_FAN(11),
+ SENSOR_ATTR_FAN(12),
+ SENSOR_ATTR_FAN(13),
+ SENSOR_ATTR_FAN(14),
+};
+
+static const struct sensor_device_attribute_2 w83795_temp[][28] = {
+ SENSOR_ATTR_TEMP(1),
+ SENSOR_ATTR_TEMP(2),
+ SENSOR_ATTR_TEMP(3),
+ SENSOR_ATTR_TEMP(4),
+ SENSOR_ATTR_TEMP(5),
+ SENSOR_ATTR_TEMP(6),
+};
+
+static const struct sensor_device_attribute_2 w83795_dts[][8] = {
+ SENSOR_ATTR_DTS(7),
+ SENSOR_ATTR_DTS(8),
+ SENSOR_ATTR_DTS(9),
+ SENSOR_ATTR_DTS(10),
+ SENSOR_ATTR_DTS(11),
+ SENSOR_ATTR_DTS(12),
+ SENSOR_ATTR_DTS(13),
+ SENSOR_ATTR_DTS(14),
+};
+
+static const struct sensor_device_attribute_2 w83795_pwm[][8] = {
+ SENSOR_ATTR_PWM(1),
+ SENSOR_ATTR_PWM(2),
+ SENSOR_ATTR_PWM(3),
+ SENSOR_ATTR_PWM(4),
+ SENSOR_ATTR_PWM(5),
+ SENSOR_ATTR_PWM(6),
+ SENSOR_ATTR_PWM(7),
+ SENSOR_ATTR_PWM(8),
+};
+
+static const struct sensor_device_attribute_2 w83795_tss[6] = {
+ SENSOR_ATTR_2(temp1_source_sel, S_IWUSR | S_IRUGO,
+ show_temp_src, store_temp_src, NOT_USED, 0),
+ SENSOR_ATTR_2(temp2_source_sel, S_IWUSR | S_IRUGO,
+ show_temp_src, store_temp_src, NOT_USED, 1),
+ SENSOR_ATTR_2(temp3_source_sel, S_IWUSR | S_IRUGO,
+ show_temp_src, store_temp_src, NOT_USED, 2),
+ SENSOR_ATTR_2(temp4_source_sel, S_IWUSR | S_IRUGO,
+ show_temp_src, store_temp_src, NOT_USED, 3),
+ SENSOR_ATTR_2(temp5_source_sel, S_IWUSR | S_IRUGO,
+ show_temp_src, store_temp_src, NOT_USED, 4),
+ SENSOR_ATTR_2(temp6_source_sel, S_IWUSR | S_IRUGO,
+ show_temp_src, store_temp_src, NOT_USED, 5),
+};
+
+static const struct sensor_device_attribute_2 sda_single_files[] = {
+ SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep,
+ store_chassis_clear, ALARM_STATUS, 46),
+#ifdef CONFIG_SENSORS_W83795_FANCTRL
+ SENSOR_ATTR_2(speed_cruise_tolerance, S_IWUSR | S_IRUGO, show_fanin,
+ store_fanin, FANIN_TOL, NOT_USED),
+ SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup,
+ store_sf_setup, SETUP_PWM_DEFAULT, NOT_USED),
+ SENSOR_ATTR_2(pwm_uptime, S_IWUSR | S_IRUGO, show_sf_setup,
+ store_sf_setup, SETUP_PWM_UPTIME, NOT_USED),
+ SENSOR_ATTR_2(pwm_downtime, S_IWUSR | S_IRUGO, show_sf_setup,
+ store_sf_setup, SETUP_PWM_DOWNTIME, NOT_USED),
+#endif
+};
+
+static const struct sensor_device_attribute_2 sda_beep_files[] = {
+ SENSOR_ATTR_2(intrusion0_beep, S_IWUSR | S_IRUGO, show_alarm_beep,
+ store_beep, BEEP_ENABLE, 46),
+ SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_alarm_beep,
+ store_beep, BEEP_ENABLE, 47),
+};
+
+/*
+ * Driver interface
+ */
+
+static void w83795_init_client(struct i2c_client *client)
+{
+ struct w83795_data *data = i2c_get_clientdata(client);
+ static const u16 clkin[4] = { /* in kHz */
+ 14318, 24000, 33333, 48000
+ };
+ u8 config;
+
+ if (reset)
+ w83795_write(client, W83795_REG_CONFIG, 0x80);
+
+ /* Start monitoring if needed */
+ config = w83795_read(client, W83795_REG_CONFIG);
+ if (!(config & W83795_REG_CONFIG_START)) {
+ dev_info(&client->dev, "Enabling monitoring operations\n");
+ w83795_write(client, W83795_REG_CONFIG,
+ config | W83795_REG_CONFIG_START);
+ }
+
+ data->clkin = clkin[(config >> 3) & 0x3];
+ dev_dbg(&client->dev, "clkin = %u kHz\n", data->clkin);
+}
+
+static int w83795_get_device_id(struct i2c_client *client)
+{
+ int device_id;
+
+ device_id = i2c_smbus_read_byte_data(client, W83795_REG_DEVICEID);
+
+ /*
+ * Special case for rev. A chips; can't be checked first because later
+ * revisions emulate this for compatibility
+ */
+ if (device_id < 0 || (device_id & 0xf0) != 0x50) {
+ int alt_id;
+
+ alt_id = i2c_smbus_read_byte_data(client,
+ W83795_REG_DEVICEID_A);
+ if (alt_id == 0x50)
+ device_id = alt_id;
+ }
+
+ return device_id;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int w83795_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int bank, vendor_id, device_id, expected, i2c_addr, config;
+ struct i2c_adapter *adapter = client->adapter;
+ unsigned short address = client->addr;
+ const char *chip_name;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+ bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL);
+ if (bank < 0 || (bank & 0x7c)) {
+ dev_dbg(&adapter->dev,
+ "w83795: Detection failed at addr 0x%02hx, check %s\n",
+ address, "bank");
+ return -ENODEV;
+ }
+
+ /* Check Nuvoton vendor ID */
+ vendor_id = i2c_smbus_read_byte_data(client, W83795_REG_VENDORID);
+ expected = bank & 0x80 ? 0x5c : 0xa3;
+ if (vendor_id != expected) {
+ dev_dbg(&adapter->dev,
+ "w83795: Detection failed at addr 0x%02hx, check %s\n",
+ address, "vendor id");
+ return -ENODEV;
+ }
+
+ /* Check device ID */
+ device_id = w83795_get_device_id(client) |
+ (i2c_smbus_read_byte_data(client, W83795_REG_CHIPID) << 8);
+ if ((device_id >> 4) != 0x795) {
+ dev_dbg(&adapter->dev,
+ "w83795: Detection failed at addr 0x%02hx, check %s\n",
+ address, "device id\n");
+ return -ENODEV;
+ }
+
+ /*
+ * If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR
+ * should match
+ */
+ if ((bank & 0x07) == 0) {
+ i2c_addr = i2c_smbus_read_byte_data(client,
+ W83795_REG_I2C_ADDR);
+ if ((i2c_addr & 0x7f) != address) {
+ dev_dbg(&adapter->dev,
+ "w83795: Detection failed at addr 0x%02hx, "
+ "check %s\n", address, "i2c addr");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Check 795 chip type: 795G or 795ADG
+ * Usually we don't write to chips during detection, but here we don't
+ * quite have the choice; hopefully it's OK, we are about to return
+ * success anyway
+ */
+ if ((bank & 0x07) != 0)
+ i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL,
+ bank & ~0x07);
+ config = i2c_smbus_read_byte_data(client, W83795_REG_CONFIG);
+ if (config & W83795_REG_CONFIG_CONFIG48)
+ chip_name = "w83795adg";
+ else
+ chip_name = "w83795g";
+
+ 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);
+
+ return 0;
+}
+
+#ifdef CONFIG_SENSORS_W83795_FANCTRL
+#define NUM_PWM_ATTRIBUTES ARRAY_SIZE(w83795_pwm[0])
+#define NUM_TEMP_ATTRIBUTES ARRAY_SIZE(w83795_temp[0])
+#else
+#define NUM_PWM_ATTRIBUTES 4
+#define NUM_TEMP_ATTRIBUTES 8
+#endif
+
+static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
+ const struct device_attribute *))
+{
+ struct w83795_data *data = dev_get_drvdata(dev);
+ int err, i, j;
+
+ for (i = 0; i < ARRAY_SIZE(w83795_in); i++) {
+ if (!(data->has_in & (1 << i)))
+ continue;
+ for (j = 0; j < ARRAY_SIZE(w83795_in[0]); j++) {
+ if (j == 4 && !data->enable_beep)
+ continue;
+ err = fn(dev, &w83795_in[i][j].dev_attr);
+ if (err)
+ return err;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+ for (j = 0; j < ARRAY_SIZE(w83795_fan[0]); j++) {
+ if (j == 3 && !data->enable_beep)
+ continue;
+ err = fn(dev, &w83795_fan[i][j].dev_attr);
+ if (err)
+ return err;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(w83795_tss); i++) {
+ j = w83795_tss_useful(data, i);
+ if (!j)
+ continue;
+ err = fn(dev, &w83795_tss[i].dev_attr);
+ if (err)
+ return err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) {
+ err = fn(dev, &sda_single_files[i].dev_attr);
+ if (err)
+ return err;
+ }
+
+ if (data->enable_beep) {
+ for (i = 0; i < ARRAY_SIZE(sda_beep_files); i++) {
+ err = fn(dev, &sda_beep_files[i].dev_attr);
+ if (err)
+ return err;
+ }
+ }
+
+ for (i = 0; i < data->has_pwm; i++) {
+ for (j = 0; j < NUM_PWM_ATTRIBUTES; j++) {
+ err = fn(dev, &w83795_pwm[i][j].dev_attr);
+ if (err)
+ return err;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) {
+ if (!(data->has_temp & (1 << i)))
+ continue;
+ for (j = 0; j < NUM_TEMP_ATTRIBUTES; j++) {
+ if (j == 7 && !data->enable_beep)
+ continue;
+ err = fn(dev, &w83795_temp[i][j].dev_attr);
+ if (err)
+ return err;
+ }
+ }
+
+ if (data->enable_dts) {
+ for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) {
+ if (!(data->has_dts & (1 << i)))
+ continue;
+ for (j = 0; j < ARRAY_SIZE(w83795_dts[0]); j++) {
+ if (j == 7 && !data->enable_beep)
+ continue;
+ err = fn(dev, &w83795_dts[i][j].dev_attr);
+ if (err)
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* We need a wrapper that fits in w83795_handle_files */
+static int device_remove_file_wrapper(struct device *dev,
+ const struct device_attribute *attr)
+{
+ device_remove_file(dev, attr);
+ return 0;
+}
+
+static void w83795_check_dynamic_in_limits(struct i2c_client *client)
+{
+ struct w83795_data *data = i2c_get_clientdata(client);
+ u8 vid_ctl;
+ int i, err_max, err_min;
+
+ vid_ctl = w83795_read(client, W83795_REG_VID_CTRL);
+
+ /* Return immediately if VRM isn't configured */
+ if ((vid_ctl & 0x07) == 0x00 || (vid_ctl & 0x07) == 0x07)
+ return;
+
+ data->has_dyn_in = (vid_ctl >> 3) & 0x07;
+ for (i = 0; i < 2; i++) {
+ if (!(data->has_dyn_in & (1 << i)))
+ continue;
+
+ /* Voltage limits in dynamic mode, switch to read-only */
+ err_max = sysfs_chmod_file(&client->dev.kobj,
+ &w83795_in[i][2].dev_attr.attr,
+ S_IRUGO);
+ err_min = sysfs_chmod_file(&client->dev.kobj,
+ &w83795_in[i][3].dev_attr.attr,
+ S_IRUGO);
+ if (err_max || err_min)
+ dev_warn(&client->dev,
+ "Failed to set in%d limits read-only (%d, %d)\n",
+ i, err_max, err_min);
+ else
+ dev_info(&client->dev,
+ "in%d limits set dynamically from VID\n", i);
+ }
+}
+
+/* Check pins that can be used for either temperature or voltage monitoring */
+static void w83795_apply_temp_config(struct w83795_data *data, u8 config,
+ int temp_chan, int in_chan)
+{
+ /* config is a 2-bit value */
+ switch (config) {
+ case 0x2: /* Voltage monitoring */
+ data->has_in |= 1 << in_chan;
+ break;
+ case 0x1: /* Thermal diode */
+ if (temp_chan >= 4)
+ break;
+ data->temp_mode |= 1 << temp_chan;
+ fallthrough;
+ case 0x3: /* Thermistor */
+ data->has_temp |= 1 << temp_chan;
+ break;
+ }
+}
+
+static const struct i2c_device_id w83795_id[];
+
+static int w83795_probe(struct i2c_client *client)
+{
+ int i;
+ u8 tmp;
+ struct device *dev = &client->dev;
+ struct w83795_data *data;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct w83795_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, 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);
+
+ /* Initialize the chip */
+ w83795_init_client(client);
+
+ /* Check which voltages and fans are present */
+ data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1)
+ | (w83795_read(client, W83795_REG_VOLT_CTRL2) << 8);
+ data->has_fan = w83795_read(client, W83795_REG_FANIN_CTRL1)
+ | (w83795_read(client, W83795_REG_FANIN_CTRL2) << 8);
+
+ /* Check which analog temperatures and extra voltages are present */
+ tmp = w83795_read(client, W83795_REG_TEMP_CTRL1);
+ if (tmp & 0x20)
+ data->enable_dts = 1;
+ w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 5, 16);
+ w83795_apply_temp_config(data, tmp & 0x3, 4, 15);
+ tmp = w83795_read(client, W83795_REG_TEMP_CTRL2);
+ w83795_apply_temp_config(data, tmp >> 6, 3, 20);
+ w83795_apply_temp_config(data, (tmp >> 4) & 0x3, 2, 19);
+ w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 1, 18);
+ w83795_apply_temp_config(data, tmp & 0x3, 0, 17);
+
+ /* Check DTS enable status */
+ if (data->enable_dts) {
+ if (1 & w83795_read(client, W83795_REG_DTSC))
+ data->enable_dts |= 2;
+ data->has_dts = w83795_read(client, W83795_REG_DTSE);
+ }
+
+ /* Report PECI Tbase values */
+ if (data->enable_dts == 1) {
+ for (i = 0; i < 8; i++) {
+ if (!(data->has_dts & (1 << i)))
+ continue;
+ tmp = w83795_read(client, W83795_REG_PECI_TBASE(i));
+ dev_info(&client->dev,
+ "PECI agent %d Tbase temperature: %u\n",
+ i + 1, (unsigned int)tmp & 0x7f);
+ }
+ }
+
+ data->has_gain = w83795_read(client, W83795_REG_VMIGB_CTRL) & 0x0f;
+
+ /* pwm and smart fan */
+ if (data->chip_type == w83795g)
+ data->has_pwm = 8;
+ else
+ data->has_pwm = 2;
+
+ /* Check if BEEP pin is available */
+ if (data->chip_type == w83795g) {
+ /* The W83795G has a dedicated BEEP pin */
+ data->enable_beep = 1;
+ } else {
+ /*
+ * The W83795ADG has a shared pin for OVT# and BEEP, so you
+ * can't have both
+ */
+ tmp = w83795_read(client, W83795_REG_OVT_CFG);
+ if ((tmp & OVT_CFG_SEL) == 0)
+ data->enable_beep = 1;
+ }
+
+ err = w83795_handle_files(dev, device_create_file);
+ if (err)
+ goto exit_remove;
+
+ if (data->chip_type == w83795g)
+ w83795_check_dynamic_in_limits(client);
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ w83795_handle_files(dev, device_remove_file_wrapper);
+ return err;
+}
+
+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);
+}
+
+
+static const struct i2c_device_id w83795_id[] = {
+ { "w83795g", w83795g },
+ { "w83795adg", w83795adg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, w83795_id);
+
+static struct i2c_driver w83795_driver = {
+ .driver = {
+ .name = "w83795",
+ },
+ .probe_new = w83795_probe,
+ .remove = w83795_remove,
+ .id_table = w83795_id,
+
+ .class = I2C_CLASS_HWMON,
+ .detect = w83795_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(w83795_driver);
+
+MODULE_AUTHOR("Wei Song, Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("W83795G/ADG hardware monitoring driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
new file mode 100644
index 000000000..f3622de0d
--- /dev/null
+++ b/drivers/hwmon/w83l785ts.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * Copyright (C) 2003-2009 Jean Delvare <jdelvare@suse.de>
+ *
+ * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made
+ * by Winbond. It reports a single external temperature with a 1 deg
+ * resolution and a 3 deg accuracy. Datasheet can be obtained from
+ * Winbond's website at:
+ * http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf
+ *
+ * Ported to Linux 2.6 by Wolfgang Ziegler <nuppla@gmx.at> and Jean Delvare
+ * <jdelvare@suse.de>.
+ *
+ * Thanks to James Bolt <james@evilpenguin.com> for benchmarking the read
+ * error handling mechanism.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* How many retries on register read error */
+#define MAX_RETRIES 5
+
+/*
+ * Address to scan
+ * Address is fully defined internally and cannot be changed.
+ */
+
+static const unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END };
+
+/*
+ * The W83L785TS-S registers
+ * Manufacturer ID is 0x5CA3 for Winbond.
+ */
+
+#define W83L785TS_REG_MAN_ID1 0x4D
+#define W83L785TS_REG_MAN_ID2 0x4C
+#define W83L785TS_REG_CHIP_ID 0x4E
+#define W83L785TS_REG_CONFIG 0x40
+#define W83L785TS_REG_TYPE 0x52
+#define W83L785TS_REG_TEMP 0x27
+#define W83L785TS_REG_TEMP_OVER 0x53 /* not sure about this one */
+
+/*
+ * Conversions
+ * The W83L785TS-S uses signed 8-bit values.
+ */
+
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+/*
+ * Functions declaration
+ */
+
+static int w83l785ts_probe(struct i2c_client *client);
+static int w83l785ts_detect(struct i2c_client *client,
+ struct i2c_board_info *info);
+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);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id w83l785ts_id[] = {
+ { "w83l785ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, w83l785ts_id);
+
+static struct i2c_driver w83l785ts_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "w83l785ts",
+ },
+ .probe_new = w83l785ts_probe,
+ .remove = w83l785ts_remove,
+ .id_table = w83l785ts_id,
+ .detect = w83l785ts_detect,
+ .address_list = normal_i2c,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct w83l785ts_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ bool valid; /* false until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ s8 temp[2]; /* 0: input, 1: critical limit */
+};
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct w83l785ts_data *data = w83l785ts_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1);
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int w83l785ts_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u16 man_id;
+ u8 chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* detection */
+ if ((w83l785ts_read_value(client, W83L785TS_REG_CONFIG, 0) & 0x80)
+ || (w83l785ts_read_value(client, W83L785TS_REG_TYPE, 0) & 0xFC)) {
+ dev_dbg(&adapter->dev,
+ "W83L785TS-S detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ /* Identification */
+ man_id = (w83l785ts_read_value(client, W83L785TS_REG_MAN_ID1, 0) << 8)
+ + w83l785ts_read_value(client, W83L785TS_REG_MAN_ID2, 0);
+ chip_id = w83l785ts_read_value(client, W83L785TS_REG_CHIP_ID, 0);
+
+ if (man_id != 0x5CA3 /* Winbond */
+ || chip_id != 0x70) { /* W83L785TS-S */
+ dev_dbg(&adapter->dev,
+ "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "w83l785ts", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int w83l785ts_probe(struct i2c_client *client)
+{
+ struct w83l785ts_data *data;
+ struct device *dev = &client->dev;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct w83l785ts_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /*
+ * Initialize the W83L785TS chip
+ * Nothing yet, assume it is already started.
+ */
+
+ err = device_create_file(dev, &sensor_dev_attr_temp1_input.dev_attr);
+ if (err)
+ return err;
+
+ err = device_create_file(dev, &sensor_dev_attr_temp1_max.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ /* Register sysfs hooks */
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ device_remove_file(dev, &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(dev, &sensor_dev_attr_temp1_max.dev_attr);
+ return err;
+}
+
+static void w83l785ts_remove(struct i2c_client *client)
+{
+ struct w83l785ts_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ device_remove_file(&client->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&client->dev,
+ &sensor_dev_attr_temp1_max.dev_attr);
+}
+
+static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval)
+{
+ int value, i;
+ struct device *dev;
+ const char *prefix;
+
+ /*
+ * We might be called during detection, at which point the client
+ * isn't yet fully initialized, so we can't use dev_dbg on it
+ */
+ if (i2c_get_clientdata(client)) {
+ dev = &client->dev;
+ prefix = "";
+ } else {
+ dev = &client->adapter->dev;
+ prefix = "w83l785ts: ";
+ }
+
+ /*
+ * Frequent read errors have been reported on Asus boards, so we
+ * retry on read errors. If it still fails (unlikely), return the
+ * default value requested by the caller.
+ */
+ for (i = 1; i <= MAX_RETRIES; i++) {
+ value = i2c_smbus_read_byte_data(client, reg);
+ if (value >= 0) {
+ dev_dbg(dev, "%sRead 0x%02x from register 0x%02x.\n",
+ prefix, value, reg);
+ return value;
+ }
+ dev_dbg(dev, "%sRead failed, will retry in %d.\n", prefix, i);
+ msleep(i);
+ }
+
+ dev_err(dev, "%sCouldn't read value from register 0x%02x.\n", prefix,
+ reg);
+ return defval;
+}
+
+static struct w83l785ts_data *w83l785ts_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83l785ts_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+
+ if (!data->valid || time_after(jiffies, data->last_updated + HZ * 2)) {
+ dev_dbg(&client->dev, "Updating w83l785ts data.\n");
+ data->temp[0] = w83l785ts_read_value(client,
+ W83L785TS_REG_TEMP, data->temp[0]);
+ data->temp[1] = w83l785ts_read_value(client,
+ W83L785TS_REG_TEMP_OVER, data->temp[1]);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+module_i2c_driver(w83l785ts_driver);
+
+MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
+MODULE_DESCRIPTION("W83L785TS-S driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c
new file mode 100644
index 000000000..2c4646fa8
--- /dev/null
+++ b/drivers/hwmon/w83l786ng.c
@@ -0,0 +1,765 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * w83l786ng.c - Linux kernel driver for hardware monitoring
+ * Copyright (c) 2007 Kevin Lo <kevlo@kevlo.org>
+ */
+
+/*
+ * Supports following chips:
+ *
+ * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ * w83l786ng 3 2 2 2 0x7b 0x5ca3 yes no
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-vid.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END };
+
+/* Insmod parameters */
+
+static bool reset;
+module_param(reset, bool, 0);
+MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
+
+#define W83L786NG_REG_IN_MIN(nr) (0x2C + (nr) * 2)
+#define W83L786NG_REG_IN_MAX(nr) (0x2B + (nr) * 2)
+#define W83L786NG_REG_IN(nr) ((nr) + 0x20)
+
+#define W83L786NG_REG_FAN(nr) ((nr) + 0x28)
+#define W83L786NG_REG_FAN_MIN(nr) ((nr) + 0x3B)
+
+#define W83L786NG_REG_CONFIG 0x40
+#define W83L786NG_REG_ALARM1 0x41
+#define W83L786NG_REG_ALARM2 0x42
+#define W83L786NG_REG_GPIO_EN 0x47
+#define W83L786NG_REG_MAN_ID2 0x4C
+#define W83L786NG_REG_MAN_ID1 0x4D
+#define W83L786NG_REG_CHIP_ID 0x4E
+
+#define W83L786NG_REG_DIODE 0x53
+#define W83L786NG_REG_FAN_DIV 0x54
+#define W83L786NG_REG_FAN_CFG 0x80
+
+#define W83L786NG_REG_TOLERANCE 0x8D
+
+static const u8 W83L786NG_REG_TEMP[2][3] = {
+ { 0x25, /* TEMP 0 in DataSheet */
+ 0x35, /* TEMP 0 Over in DataSheet */
+ 0x36 }, /* TEMP 0 Hyst in DataSheet */
+ { 0x26, /* TEMP 1 in DataSheet */
+ 0x37, /* TEMP 1 Over in DataSheet */
+ 0x38 } /* TEMP 1 Hyst in DataSheet */
+};
+
+static const u8 W83L786NG_PWM_MODE_SHIFT[] = {6, 7};
+static const u8 W83L786NG_PWM_ENABLE_SHIFT[] = {2, 4};
+
+/* FAN Duty Cycle, be used to control */
+static const u8 W83L786NG_REG_PWM[] = {0x81, 0x87};
+
+
+static inline u8
+FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = clamp_val(rpm, 1, 1000000);
+ return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
+ ((val) == 255 ? 0 : \
+ 1350000 / ((val) * (div))))
+
+/* for temp */
+#define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \
+ : (val)) / 1000, 0, 0xff))
+#define TEMP_FROM_REG(val) (((val) & 0x80 ? \
+ (val) - 0x100 : (val)) * 1000)
+
+/*
+ * The analog voltage inputs have 8mV LSB. Since the sysfs output is
+ * in mV as would be measured on the chip input pin, need to just
+ * multiply/divide by 8 to translate from/to register values.
+ */
+#define IN_TO_REG(val) (clamp_val((((val) + 4) / 8), 0, 255))
+#define IN_FROM_REG(val) ((val) * 8)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static inline u8
+DIV_TO_REG(long val)
+{
+ int i;
+ val = clamp_val(val, 1, 128) >> 1;
+ for (i = 0; i < 7; i++) {
+ if (val == 0)
+ break;
+ val >>= 1;
+ }
+ return (u8)i;
+}
+
+struct w83l786ng_data {
+ struct i2c_client *client;
+ struct mutex update_lock;
+ 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 */
+
+ u8 in[3];
+ u8 in_max[3];
+ u8 in_min[3];
+ u8 fan[2];
+ u8 fan_div[2];
+ u8 fan_min[2];
+ u8 temp_type[2];
+ u8 temp[2][3];
+ u8 pwm[2];
+ u8 pwm_mode[2]; /* 0->DC variable voltage
+ * 1->PWM variable duty cycle */
+
+ u8 pwm_enable[2]; /* 1->manual
+ * 2->thermal cruise (also called SmartFan I) */
+ u8 tolerance[2];
+};
+
+static u8
+w83l786ng_read_value(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int
+w83l786ng_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static struct w83l786ng_data *w83l786ng_update_device(struct device *dev)
+{
+ struct w83l786ng_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int i, j;
+ u8 reg_tmp, pwmcfg;
+
+ mutex_lock(&data->update_lock);
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ dev_dbg(&client->dev, "Updating w83l786ng data.\n");
+
+ /* Update the voltages measured value and limits */
+ for (i = 0; i < 3; i++) {
+ data->in[i] = w83l786ng_read_value(client,
+ W83L786NG_REG_IN(i));
+ data->in_min[i] = w83l786ng_read_value(client,
+ W83L786NG_REG_IN_MIN(i));
+ data->in_max[i] = w83l786ng_read_value(client,
+ W83L786NG_REG_IN_MAX(i));
+ }
+
+ /* Update the fan counts and limits */
+ for (i = 0; i < 2; i++) {
+ data->fan[i] = w83l786ng_read_value(client,
+ W83L786NG_REG_FAN(i));
+ data->fan_min[i] = w83l786ng_read_value(client,
+ W83L786NG_REG_FAN_MIN(i));
+ }
+
+ /* Update the fan divisor */
+ reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
+ data->fan_div[0] = reg_tmp & 0x07;
+ data->fan_div[1] = (reg_tmp >> 4) & 0x07;
+
+ pwmcfg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
+ for (i = 0; i < 2; i++) {
+ data->pwm_mode[i] =
+ ((pwmcfg >> W83L786NG_PWM_MODE_SHIFT[i]) & 1)
+ ? 0 : 1;
+ data->pwm_enable[i] =
+ ((pwmcfg >> W83L786NG_PWM_ENABLE_SHIFT[i]) & 3) + 1;
+ data->pwm[i] =
+ (w83l786ng_read_value(client, W83L786NG_REG_PWM[i])
+ & 0x0f) * 0x11;
+ }
+
+
+ /* Update the temperature sensors */
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 3; j++) {
+ data->temp[i][j] = w83l786ng_read_value(client,
+ W83L786NG_REG_TEMP[i][j]);
+ }
+ }
+
+ /* Update Smart Fan I/II tolerance */
+ reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_TOLERANCE);
+ data->tolerance[0] = reg_tmp & 0x0f;
+ data->tolerance[1] = (reg_tmp >> 4) & 0x0f;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* following are the sysfs callback functions */
+#define show_in_reg(reg) \
+static ssize_t \
+show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ struct w83l786ng_data *data = w83l786ng_update_device(dev); \
+ return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \
+}
+
+show_in_reg(in)
+show_in_reg(in_min)
+show_in_reg(in_max)
+
+#define store_in_reg(REG, reg) \
+static ssize_t \
+store_in_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ struct w83l786ng_data *data = dev_get_drvdata(dev); \
+ struct i2c_client *client = data->client; \
+ unsigned long val; \
+ int err = kstrtoul(buf, 10, &val); \
+ if (err) \
+ return err; \
+ mutex_lock(&data->update_lock); \
+ data->in_##reg[nr] = IN_TO_REG(val); \
+ w83l786ng_write_value(client, W83L786NG_REG_IN_##REG(nr), \
+ data->in_##reg[nr]); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+store_in_reg(MIN, min)
+store_in_reg(MAX, max)
+
+static struct sensor_device_attribute sda_in_input[] = {
+ SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
+ SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
+ SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
+};
+
+static struct sensor_device_attribute sda_in_min[] = {
+ SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
+ SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
+ SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
+};
+
+static struct sensor_device_attribute sda_in_max[] = {
+ SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
+ SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
+ SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
+};
+
+#define show_fan_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ struct w83l786ng_data *data = w83l786ng_update_device(dev); \
+ return sprintf(buf, "%d\n", \
+ FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
+}
+
+show_fan_reg(fan);
+show_fan_reg(fan_min);
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_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->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
+ w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
+ data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_data *data = w83l786ng_update_device(dev);
+ return sprintf(buf, "%u\n", 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
+ * least surprise; the user doesn't expect the fan minimum to change just
+ * because the divisor changed.
+ */
+static ssize_t
+store_fan_div(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ unsigned long min;
+ u8 tmp_fan_div;
+ u8 fan_div_reg;
+ u8 keep_mask = 0;
+ u8 new_shift = 0;
+
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ /* Save fan_min */
+ mutex_lock(&data->update_lock);
+ min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
+
+ data->fan_div[nr] = DIV_TO_REG(val);
+
+ switch (nr) {
+ case 0:
+ keep_mask = 0xf8;
+ new_shift = 0;
+ break;
+ case 1:
+ keep_mask = 0x8f;
+ new_shift = 4;
+ break;
+ }
+
+ fan_div_reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV)
+ & keep_mask;
+
+ tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask;
+
+ w83l786ng_write_value(client, W83L786NG_REG_FAN_DIV,
+ fan_div_reg | tmp_fan_div);
+
+ /* Restore fan_min */
+ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
+ data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct sensor_device_attribute sda_fan_input[] = {
+ SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
+ SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
+};
+
+static struct sensor_device_attribute sda_fan_min[] = {
+ SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 0),
+ SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 1),
+};
+
+static struct sensor_device_attribute sda_fan_div[] = {
+ SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div,
+ store_fan_div, 0),
+ SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div,
+ store_fan_div, 1),
+};
+
+
+/* read/write the temperature, includes measured value and limits */
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83l786ng_data *data = w83l786ng_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index]));
+}
+
+static ssize_t
+store_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sensor_attr =
+ to_sensor_dev_attr_2(attr);
+ int nr = sensor_attr->nr;
+ int index = sensor_attr->index;
+ struct w83l786ng_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp[nr][index] = TEMP_TO_REG(val);
+ w83l786ng_write_value(client, W83L786NG_REG_TEMP[nr][index],
+ data->temp[nr][index]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct sensor_device_attribute_2 sda_temp_input[] = {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max[] = {
+ SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp, store_temp, 0, 1),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
+ show_temp, store_temp, 1, 1),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp, store_temp, 0, 2),
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp, store_temp, 1, 2),
+};
+
+#define show_pwm_reg(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83l786ng_data *data = w83l786ng_update_device(dev); \
+ int nr = to_sensor_dev_attr(attr)->index; \
+ return sprintf(buf, "%d\n", data->reg[nr]); \
+}
+
+show_pwm_reg(pwm_mode)
+show_pwm_reg(pwm_enable)
+show_pwm_reg(pwm)
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val > 1)
+ return -EINVAL;
+ mutex_lock(&data->update_lock);
+ data->pwm_mode[nr] = val;
+ reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
+ reg &= ~(1 << W83L786NG_PWM_MODE_SHIFT[nr]);
+ if (!val)
+ reg |= 1 << W83L786NG_PWM_MODE_SHIFT[nr];
+ w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_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;
+ val = clamp_val(val, 0, 255);
+ val = DIV_ROUND_CLOSEST(val, 0x11);
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = val * 0x11;
+ val |= w83l786ng_read_value(client, W83L786NG_REG_PWM[nr]) & 0xf0;
+ w83l786ng_write_value(client, W83L786NG_REG_PWM[nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 reg;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (!val || val > 2) /* only modes 1 and 2 are supported */
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
+ data->pwm_enable[nr] = val;
+ reg &= ~(0x03 << W83L786NG_PWM_ENABLE_SHIFT[nr]);
+ reg |= (val - 1) << W83L786NG_PWM_ENABLE_SHIFT[nr];
+ w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_pwm[] = {
+ SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
+ SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
+};
+
+static struct sensor_device_attribute sda_pwm_mode[] = {
+ SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 0),
+ SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 1),
+};
+
+static struct sensor_device_attribute sda_pwm_enable[] = {
+ SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 0),
+ SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 1),
+};
+
+/* For Smart Fan I/Thermal Cruise and Smart Fan II */
+static ssize_t
+show_tolerance(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_data *data = w83l786ng_update_device(dev);
+ return sprintf(buf, "%ld\n", (long)data->tolerance[nr]);
+}
+
+static ssize_t
+store_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int nr = to_sensor_dev_attr(attr)->index;
+ struct w83l786ng_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 tol_tmp, tol_mask;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ tol_mask = w83l786ng_read_value(client,
+ W83L786NG_REG_TOLERANCE) & ((nr == 1) ? 0x0f : 0xf0);
+ tol_tmp = clamp_val(val, 0, 15);
+ tol_tmp &= 0x0f;
+ data->tolerance[nr] = tol_tmp;
+ if (nr == 1)
+ tol_tmp <<= 4;
+
+ w83l786ng_write_value(client, W83L786NG_REG_TOLERANCE,
+ tol_mask | tol_tmp);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_tolerance[] = {
+ SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 0),
+ SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 1),
+};
+
+
+#define IN_UNIT_ATTRS(X) \
+ &sda_in_input[X].dev_attr.attr, \
+ &sda_in_min[X].dev_attr.attr, \
+ &sda_in_max[X].dev_attr.attr
+
+#define FAN_UNIT_ATTRS(X) \
+ &sda_fan_input[X].dev_attr.attr, \
+ &sda_fan_min[X].dev_attr.attr, \
+ &sda_fan_div[X].dev_attr.attr
+
+#define TEMP_UNIT_ATTRS(X) \
+ &sda_temp_input[X].dev_attr.attr, \
+ &sda_temp_max[X].dev_attr.attr, \
+ &sda_temp_max_hyst[X].dev_attr.attr
+
+#define PWM_UNIT_ATTRS(X) \
+ &sda_pwm[X].dev_attr.attr, \
+ &sda_pwm_mode[X].dev_attr.attr, \
+ &sda_pwm_enable[X].dev_attr.attr
+
+#define TOLERANCE_UNIT_ATTRS(X) \
+ &sda_tolerance[X].dev_attr.attr
+
+static struct attribute *w83l786ng_attrs[] = {
+ IN_UNIT_ATTRS(0),
+ IN_UNIT_ATTRS(1),
+ IN_UNIT_ATTRS(2),
+ FAN_UNIT_ATTRS(0),
+ FAN_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(0),
+ TEMP_UNIT_ATTRS(1),
+ PWM_UNIT_ATTRS(0),
+ PWM_UNIT_ATTRS(1),
+ TOLERANCE_UNIT_ATTRS(0),
+ TOLERANCE_UNIT_ATTRS(1),
+ NULL
+};
+
+ATTRIBUTE_GROUPS(w83l786ng);
+
+static int
+w83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ u16 man_id;
+ u8 chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ /* Detection */
+ if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) {
+ dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ /* Identification */
+ man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) +
+ w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
+ chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
+
+ if (man_id != 0x5CA3 || /* Winbond */
+ chip_id != 0x80) { /* W83L786NG */
+ dev_dbg(&adapter->dev,
+ "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
+ }
+
+ strscpy(info->type, "w83l786ng", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static void w83l786ng_init_client(struct i2c_client *client)
+{
+ u8 tmp;
+
+ if (reset)
+ w83l786ng_write_value(client, W83L786NG_REG_CONFIG, 0x80);
+
+ /* Start monitoring */
+ tmp = w83l786ng_read_value(client, W83L786NG_REG_CONFIG);
+ if (!(tmp & 0x01))
+ w83l786ng_write_value(client, W83L786NG_REG_CONFIG, tmp | 0x01);
+}
+
+static int
+w83l786ng_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct w83l786ng_data *data;
+ struct device *hwmon_dev;
+ int i;
+ u8 reg_tmp;
+
+ data = devm_kzalloc(dev, sizeof(struct w83l786ng_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the chip */
+ w83l786ng_init_client(client);
+
+ /* A few vars need to be filled upon startup */
+ for (i = 0; i < 2; i++) {
+ data->fan_min[i] = w83l786ng_read_value(client,
+ W83L786NG_REG_FAN_MIN(i));
+ }
+
+ /* Update the fan divisor */
+ reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
+ data->fan_div[0] = reg_tmp & 0x07;
+ data->fan_div[1] = (reg_tmp >> 4) & 0x07;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ w83l786ng_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id w83l786ng_id[] = {
+ { "w83l786ng", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, w83l786ng_id);
+
+static struct i2c_driver w83l786ng_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "w83l786ng",
+ },
+ .probe_new = w83l786ng_probe,
+ .id_table = w83l786ng_id,
+ .detect = w83l786ng_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(w83l786ng_driver);
+
+MODULE_AUTHOR("Kevin Lo");
+MODULE_DESCRIPTION("w83l786ng driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/wm831x-hwmon.c b/drivers/hwmon/wm831x-hwmon.c
new file mode 100644
index 000000000..38446e9d1
--- /dev/null
+++ b/drivers/hwmon/wm831x-hwmon.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/hwmon/wm831x-hwmon.c - Wolfson Microelectronics WM831x PMIC
+ * hardware monitoring features.
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics plc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/auxadc.h>
+
+static const char * const input_names[] = {
+ [WM831X_AUX_SYSVDD] = "SYSVDD",
+ [WM831X_AUX_USB] = "USB",
+ [WM831X_AUX_BKUP_BATT] = "Backup battery",
+ [WM831X_AUX_BATT] = "Battery",
+ [WM831X_AUX_WALL] = "WALL",
+ [WM831X_AUX_CHIP_TEMP] = "PMIC",
+ [WM831X_AUX_BATT_TEMP] = "Battery",
+};
+
+static ssize_t show_voltage(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wm831x *wm831x = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(attr)->index;
+ int ret;
+
+ ret = wm831x_auxadc_read_uv(wm831x, channel);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret, 1000));
+}
+
+static ssize_t show_chip_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wm831x *wm831x = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(attr)->index;
+ int ret;
+
+ ret = wm831x_auxadc_read(wm831x, channel);
+ if (ret < 0)
+ return ret;
+
+ /* Degrees celsius = (512.18-ret) / 1.0983 */
+ ret = 512180 - (ret * 1000);
+ ret = DIV_ROUND_CLOSEST(ret * 10000, 10983);
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int channel = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", input_names[channel]);
+}
+
+#define WM831X_VOLTAGE(id, name) \
+ static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage, \
+ NULL, name)
+
+#define WM831X_NAMED_VOLTAGE(id, name) \
+ WM831X_VOLTAGE(id, name); \
+ static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \
+ NULL, name)
+
+WM831X_VOLTAGE(0, WM831X_AUX_AUX1);
+WM831X_VOLTAGE(1, WM831X_AUX_AUX2);
+WM831X_VOLTAGE(2, WM831X_AUX_AUX3);
+WM831X_VOLTAGE(3, WM831X_AUX_AUX4);
+
+WM831X_NAMED_VOLTAGE(4, WM831X_AUX_SYSVDD);
+WM831X_NAMED_VOLTAGE(5, WM831X_AUX_USB);
+WM831X_NAMED_VOLTAGE(6, WM831X_AUX_BATT);
+WM831X_NAMED_VOLTAGE(7, WM831X_AUX_WALL);
+WM831X_NAMED_VOLTAGE(8, WM831X_AUX_BKUP_BATT);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL,
+ WM831X_AUX_CHIP_TEMP);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL,
+ WM831X_AUX_CHIP_TEMP);
+/*
+ * Report as a voltage since conversion depends on external components
+ * and that's what the ABI wants.
+ */
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL,
+ WM831X_AUX_BATT_TEMP);
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
+ WM831X_AUX_BATT_TEMP);
+
+static struct attribute *wm831x_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_in4_label.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_label.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_label.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_label.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_label.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+
+ NULL
+};
+
+ATTRIBUTE_GROUPS(wm831x);
+
+static int wm831x_hwmon_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct device *hwmon_dev;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "wm831x",
+ wm831x,
+ wm831x_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver wm831x_hwmon_driver = {
+ .probe = wm831x_hwmon_probe,
+ .driver = {
+ .name = "wm831x-hwmon",
+ },
+};
+
+module_platform_driver(wm831x_hwmon_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x Hardware Monitoring");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-hwmon");
diff --git a/drivers/hwmon/wm8350-hwmon.c b/drivers/hwmon/wm8350-hwmon.c
new file mode 100644
index 000000000..10e17bb79
--- /dev/null
+++ b/drivers/hwmon/wm8350-hwmon.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/hwmon/wm8350-hwmon.c - Wolfson Microelectronics WM8350 PMIC
+ * hardware monitoring features.
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics plc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/comparator.h>
+
+static const char * const input_names[] = {
+ [WM8350_AUXADC_USB] = "USB",
+ [WM8350_AUXADC_LINE] = "Line",
+ [WM8350_AUXADC_BATT] = "Battery",
+};
+
+static ssize_t show_voltage(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+ int channel = to_sensor_dev_attr(attr)->index;
+ int val;
+
+ val = wm8350_read_auxadc(wm8350, channel, 0, 0) * WM8350_AUX_COEFF;
+ val = DIV_ROUND_CLOSEST(val, 1000);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int channel = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", input_names[channel]);
+}
+
+#define WM8350_NAMED_VOLTAGE(id, name) \
+ static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage,\
+ NULL, name); \
+ static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \
+ NULL, name)
+
+WM8350_NAMED_VOLTAGE(0, WM8350_AUXADC_USB);
+WM8350_NAMED_VOLTAGE(1, WM8350_AUXADC_BATT);
+WM8350_NAMED_VOLTAGE(2, WM8350_AUXADC_LINE);
+
+static struct attribute *wm8350_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(wm8350);
+
+static int wm8350_hwmon_probe(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+ struct device *hwmon_dev;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "wm8350",
+ wm8350,
+ wm8350_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver wm8350_hwmon_driver = {
+ .probe = wm8350_hwmon_probe,
+ .driver = {
+ .name = "wm8350-hwmon",
+ },
+};
+
+module_platform_driver(wm8350_hwmon_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8350 Hardware Monitoring");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-hwmon");
diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c
new file mode 100644
index 000000000..78d9f52e2
--- /dev/null
+++ b/drivers/hwmon/xgene-hwmon.c
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * APM X-Gene SoC Hardware Monitoring Driver
+ *
+ * Copyright (c) 2016, Applied Micro Circuits Corporation
+ * Author: Loc Ho <lho@apm.com>
+ * Hoan Tran <hotran@apm.com>
+ *
+ * This driver provides the following features:
+ * - Retrieve CPU total power (uW)
+ * - Retrieve IO total power (uW)
+ * - Retrieve SoC temperature (milli-degree C) and alarm
+ */
+#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <acpi/pcc.h>
+
+/* SLIMpro message defines */
+#define MSG_TYPE_DBG 0
+#define MSG_TYPE_ERR 7
+#define MSG_TYPE_PWRMGMT 9
+
+#define MSG_TYPE(v) (((v) & 0xF0000000) >> 28)
+#define MSG_TYPE_SET(v) (((v) << 28) & 0xF0000000)
+#define MSG_SUBTYPE(v) (((v) & 0x0F000000) >> 24)
+#define MSG_SUBTYPE_SET(v) (((v) << 24) & 0x0F000000)
+
+#define DBG_SUBTYPE_SENSOR_READ 4
+#define SENSOR_RD_MSG 0x04FFE902
+#define SENSOR_RD_EN_ADDR(a) ((a) & 0x000FFFFF)
+#define PMD_PWR_REG 0x20
+#define PMD_PWR_MW_REG 0x26
+#define SOC_PWR_REG 0x21
+#define SOC_PWR_MW_REG 0x27
+#define SOC_TEMP_REG 0x10
+
+#define TEMP_NEGATIVE_BIT 8
+#define SENSOR_INVALID_DATA BIT(15)
+
+#define PWRMGMT_SUBTYPE_TPC 1
+#define TPC_ALARM 2
+#define TPC_GET_ALARM 3
+#define TPC_CMD(v) (((v) & 0x00FF0000) >> 16)
+#define TPC_CMD_SET(v) (((v) << 16) & 0x00FF0000)
+#define TPC_EN_MSG(hndl, cmd, type) \
+ (MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \
+ MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type)
+
+/* PCC defines */
+#define PCC_SIGNATURE_MASK 0x50424300
+#define PCCC_GENERATE_DB_INT BIT(15)
+#define PCCS_CMD_COMPLETE BIT(0)
+#define PCCS_SCI_DOORBEL BIT(1)
+#define PCCS_PLATFORM_NOTIFICATION BIT(3)
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define PCC_NUM_RETRIES 500
+
+#define ASYNC_MSG_FIFO_SIZE 16
+#define MBOX_OP_TIMEOUTMS 1000
+
+#define WATT_TO_mWATT(x) ((x) * 1000)
+#define mWATT_TO_uWATT(x) ((x) * 1000)
+#define CELSIUS_TO_mCELSIUS(x) ((x) * 1000)
+
+#define to_xgene_hwmon_dev(cl) \
+ container_of(cl, struct xgene_hwmon_dev, mbox_client)
+
+enum xgene_hwmon_version {
+ XGENE_HWMON_V1 = 0,
+ XGENE_HWMON_V2 = 1,
+};
+
+struct slimpro_resp_msg {
+ u32 msg;
+ u32 param1;
+ u32 param2;
+} __packed;
+
+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;
+
+ spinlock_t kfifo_lock;
+ struct mutex rd_mutex;
+ struct completion rd_complete;
+ int resp_pending;
+ struct slimpro_resp_msg sync_msg;
+
+ struct work_struct workq;
+ struct kfifo_rec_ptr_1 async_msg_fifo;
+
+ struct device *hwmon_dev;
+ bool temp_critical_alarm;
+
+ phys_addr_t comm_base_addr;
+ void *pcc_comm_addr;
+ u64 usecs_lat;
+};
+
+/*
+ * This function tests and clears a bitmask then returns its old value
+ */
+static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
+{
+ u16 ret, val;
+
+ val = le16_to_cpu(READ_ONCE(*addr));
+ ret = val & mask;
+ val &= ~mask;
+ WRITE_ONCE(*addr, cpu_to_le16(val));
+
+ return ret;
+}
+
+static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
+{
+ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ u32 *ptr = (void *)(generic_comm_base + 1);
+ int rc, i;
+ u16 val;
+
+ mutex_lock(&ctx->rd_mutex);
+ init_completion(&ctx->rd_complete);
+ ctx->resp_pending = true;
+
+ /* Write signature for subspace */
+ WRITE_ONCE(generic_comm_base->signature,
+ cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx));
+
+ /* Write to the shared command region */
+ WRITE_ONCE(generic_comm_base->command,
+ cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT));
+
+ /* Flip CMD COMPLETE bit */
+ val = le16_to_cpu(READ_ONCE(generic_comm_base->status));
+ val &= ~PCCS_CMD_COMPLETE;
+ WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val));
+
+ /* Copy the message to the PCC comm space */
+ for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++)
+ WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));
+
+ /* Ring the doorbell */
+ rc = mbox_send_message(ctx->mbox_chan, msg);
+ if (rc < 0) {
+ dev_err(ctx->dev, "Mailbox send error %d\n", rc);
+ goto err;
+ }
+ if (!wait_for_completion_timeout(&ctx->rd_complete,
+ usecs_to_jiffies(ctx->usecs_lat))) {
+ dev_err(ctx->dev, "Mailbox operation timed out\n");
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* Check for error message */
+ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ msg[0] = ctx->sync_msg.msg;
+ msg[1] = ctx->sync_msg.param1;
+ msg[2] = ctx->sync_msg.param2;
+
+err:
+ mbox_chan_txdone(ctx->mbox_chan, 0);
+ ctx->resp_pending = false;
+ mutex_unlock(&ctx->rd_mutex);
+ return rc;
+}
+
+static int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
+{
+ int rc;
+
+ mutex_lock(&ctx->rd_mutex);
+ init_completion(&ctx->rd_complete);
+ ctx->resp_pending = true;
+
+ rc = mbox_send_message(ctx->mbox_chan, msg);
+ if (rc < 0) {
+ dev_err(ctx->dev, "Mailbox send error %d\n", rc);
+ goto err;
+ }
+
+ if (!wait_for_completion_timeout(&ctx->rd_complete,
+ msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) {
+ dev_err(ctx->dev, "Mailbox operation timed out\n");
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* Check for error message */
+ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ msg[0] = ctx->sync_msg.msg;
+ msg[1] = ctx->sync_msg.param1;
+ msg[2] = ctx->sync_msg.param2;
+
+err:
+ ctx->resp_pending = false;
+ mutex_unlock(&ctx->rd_mutex);
+ return rc;
+}
+
+static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr,
+ u32 *data)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = SENSOR_RD_MSG;
+ msg[1] = SENSOR_RD_EN_ADDR(addr);
+ msg[2] = 0;
+
+ if (acpi_disabled)
+ rc = xgene_hwmon_rd(ctx, msg);
+ else
+ rc = xgene_hwmon_pcc_rd(ctx, msg);
+
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Check if sensor data is valid.
+ */
+ if (msg[1] & SENSOR_INVALID_DATA)
+ return -ENODATA;
+
+ *data = msg[1];
+
+ return rc;
+}
+
+static int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx,
+ u32 *amsg)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0);
+ msg[1] = 0;
+ msg[2] = 0;
+
+ rc = xgene_hwmon_pcc_rd(ctx, msg);
+ if (rc < 0)
+ return rc;
+
+ amsg[0] = msg[0];
+ amsg[1] = msg[1];
+ amsg[2] = msg[2];
+
+ return rc;
+}
+
+static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ u32 watt, mwatt;
+ int rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt);
+ if (rc < 0)
+ return rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt);
+ if (rc < 0)
+ return rc;
+
+ *val = WATT_TO_mWATT(watt) + mwatt;
+ return 0;
+}
+
+static int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ u32 watt, mwatt;
+ int rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt);
+ if (rc < 0)
+ return rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt);
+ if (rc < 0)
+ return rc;
+
+ *val = WATT_TO_mWATT(watt) + mwatt;
+ return 0;
+}
+
+static int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val);
+}
+
+/*
+ * Sensor temperature/power functions
+ */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ int rc, temp;
+ u32 val;
+
+ rc = xgene_hwmon_get_temp(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
+
+ 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 sysfs_emit(buf, "SoC Temperature\n");
+}
+
+static ssize_t temp1_critical_alarm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+
+ 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 sysfs_emit(buf, "CPU power\n");
+}
+
+static ssize_t power2_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "IO power\n");
+}
+
+static ssize_t power1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ u32 val;
+ int rc;
+
+ rc = xgene_hwmon_get_cpu_pwr(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
+}
+
+static ssize_t power2_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ u32 val;
+ int rc;
+
+ rc = xgene_hwmon_get_io_pwr(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
+}
+
+static DEVICE_ATTR_RO(temp1_label);
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RO(temp1_critical_alarm);
+static DEVICE_ATTR_RO(power1_label);
+static DEVICE_ATTR_RO(power1_input);
+static DEVICE_ATTR_RO(power2_label);
+static DEVICE_ATTR_RO(power2_input);
+
+static struct attribute *xgene_hwmon_attrs[] = {
+ &dev_attr_temp1_label.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_critical_alarm.attr,
+ &dev_attr_power1_label.attr,
+ &dev_attr_power1_input.attr,
+ &dev_attr_power2_label.attr,
+ &dev_attr_power2_input.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(xgene_hwmon);
+
+static int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx,
+ struct slimpro_resp_msg *amsg)
+{
+ ctx->temp_critical_alarm = !!amsg->param2;
+ sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm");
+
+ return 0;
+}
+
+static void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx,
+ struct slimpro_resp_msg *amsg)
+{
+ if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) &&
+ (TPC_CMD(amsg->msg) == TPC_ALARM))
+ xgene_hwmon_tpc_alarm(ctx, amsg);
+}
+
+/*
+ * This function is called to process async work queue
+ */
+static void xgene_hwmon_evt_work(struct work_struct *work)
+{
+ struct slimpro_resp_msg amsg;
+ struct xgene_hwmon_dev *ctx;
+ int ret;
+
+ ctx = container_of(work, struct xgene_hwmon_dev, workq);
+ while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg,
+ sizeof(struct slimpro_resp_msg),
+ &ctx->kfifo_lock)) {
+ /*
+ * If PCC, send a consumer command to Platform to get info
+ * If Slimpro Mailbox, get message from specific FIFO
+ */
+ if (!acpi_disabled) {
+ ret = xgene_hwmon_get_notification_msg(ctx,
+ (u32 *)&amsg);
+ if (ret < 0)
+ continue;
+ }
+
+ if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT)
+ xgene_hwmon_process_pwrmsg(ctx, &amsg);
+ }
+}
+
+static int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg)
+{
+ if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) {
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
+ sizeof(struct slimpro_resp_msg),
+ &ctx->kfifo_lock);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called when the SLIMpro Mailbox received a message
+ */
+static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
+{
+ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
+
+ /*
+ * While the driver registers with the mailbox framework, an interrupt
+ * can be pending before the probe function completes its
+ * initialization. If such condition occurs, just queue up the message
+ * as the driver is not ready for servicing the callback.
+ */
+ if (xgene_hwmon_rx_ready(ctx, msg) < 0)
+ return;
+
+ /*
+ * Response message format:
+ * msg[0] is the return code of the operation
+ * msg[1] is the first parameter word
+ * msg[2] is the second parameter word
+ *
+ * As message only supports dword size, just assign it.
+ */
+
+ /* Check for sync query */
+ if (ctx->resp_pending &&
+ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
+ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
+ ctx->sync_msg.msg = ((u32 *)msg)[0];
+ ctx->sync_msg.param1 = ((u32 *)msg)[1];
+ ctx->sync_msg.param2 = ((u32 *)msg)[2];
+
+ /* Operation waiting for response */
+ complete(&ctx->rd_complete);
+
+ return;
+ }
+
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
+ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
+ /* Schedule the bottom handler */
+ schedule_work(&ctx->workq);
+}
+
+/*
+ * This function is called when the PCC Mailbox received a message
+ */
+static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
+{
+ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
+ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ struct slimpro_resp_msg amsg;
+
+ /*
+ * While the driver registers with the mailbox framework, an interrupt
+ * can be pending before the probe function completes its
+ * initialization. If such condition occurs, just queue up the message
+ * as the driver is not ready for servicing the callback.
+ */
+ if (xgene_hwmon_rx_ready(ctx, &amsg) < 0)
+ return;
+
+ msg = generic_comm_base + 1;
+ /* Check if platform sends interrupt */
+ if (!xgene_word_tst_and_clr(&generic_comm_base->status,
+ PCCS_SCI_DOORBEL))
+ return;
+
+ /*
+ * Response message format:
+ * msg[0] is the return code of the operation
+ * msg[1] is the first parameter word
+ * msg[2] is the second parameter word
+ *
+ * As message only supports dword size, just assign it.
+ */
+
+ /* Check for sync query */
+ if (ctx->resp_pending &&
+ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
+ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
+ /* Check if platform completes command */
+ if (xgene_word_tst_and_clr(&generic_comm_base->status,
+ PCCS_CMD_COMPLETE)) {
+ ctx->sync_msg.msg = ((u32 *)msg)[0];
+ ctx->sync_msg.param1 = ((u32 *)msg)[1];
+ ctx->sync_msg.param2 = ((u32 *)msg)[2];
+
+ /* Operation waiting for response */
+ complete(&ctx->rd_complete);
+
+ return;
+ }
+ }
+
+ /*
+ * Platform notifies interrupt to OSPM.
+ * OPSM schedules a consumer command to get this information
+ * in a workqueue. Platform must wait until OSPM has issued
+ * a consumer command that serves this notification.
+ */
+
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg,
+ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
+ /* Schedule the bottom handler */
+ schedule_work(&ctx->workq);
+}
+
+static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+ if (ret) {
+ dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+ } else {
+ dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+ }
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_hwmon_acpi_match[] = {
+ {"APMC0D29", XGENE_HWMON_V1},
+ {"APMC0D8A", XGENE_HWMON_V2},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match);
+#endif
+
+static int xgene_hwmon_probe(struct platform_device *pdev)
+{
+ struct xgene_hwmon_dev *ctx;
+ struct mbox_client *cl;
+ int rc;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ctx);
+ cl = &ctx->mbox_client;
+
+ spin_lock_init(&ctx->kfifo_lock);
+ mutex_init(&ctx->rd_mutex);
+
+ rc = kfifo_alloc(&ctx->async_msg_fifo,
+ sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE,
+ GFP_KERNEL);
+ if (rc)
+ return -ENOMEM;
+
+ INIT_WORK(&ctx->workq, xgene_hwmon_evt_work);
+
+ /* Request mailbox channel */
+ cl->dev = &pdev->dev;
+ cl->tx_done = xgene_hwmon_tx_done;
+ cl->tx_block = false;
+ cl->tx_tout = MBOX_OP_TIMEOUTMS;
+ cl->knows_txdone = false;
+ if (acpi_disabled) {
+ cl->rx_callback = xgene_hwmon_rx_cb;
+ ctx->mbox_chan = mbox_request_channel(cl, 0);
+ if (IS_ERR(ctx->mbox_chan)) {
+ dev_err(&pdev->dev,
+ "SLIMpro mailbox channel request failed\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+ } else {
+ 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) {
+ rc = -EINVAL;
+ goto out_mbox_free;
+ }
+
+ version = (int)acpi_id->driver_data;
+
+ if (device_property_read_u32(&pdev->dev, "pcc-channel",
+ &ctx->mbox_idx)) {
+ dev_err(&pdev->dev, "no pcc-channel property\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+
+ cl->rx_callback = xgene_hwmon_pcc_rx_cb;
+ 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;
+ }
+
+ 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");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * This is the shared communication region
+ * for the OS and Platform to communicate over.
+ */
+ 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 *)devm_ioremap(&pdev->dev,
+ ctx->comm_base_addr,
+ pcc_chan->shmem_size);
+ else
+ ctx->pcc_comm_addr = devm_memremap(&pdev->dev,
+ ctx->comm_base_addr,
+ pcc_chan->shmem_size,
+ MEMREMAP_WB);
+ } else {
+ dev_err(&pdev->dev, "Failed to get PCC comm region\n");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ if (!ctx->pcc_comm_addr) {
+ dev_err(&pdev->dev,
+ "Failed to ioremap PCC comm region\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * 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 * pcc_chan->latency;
+ }
+
+ ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
+ "apm_xgene",
+ ctx,
+ xgene_hwmon_groups);
+ if (IS_ERR(ctx->hwmon_dev)) {
+ dev_err(&pdev->dev, "Failed to register HW monitor device\n");
+ rc = PTR_ERR(ctx->hwmon_dev);
+ goto out;
+ }
+
+ /*
+ * Schedule the bottom handler if there is a pending message.
+ */
+ schedule_work(&ctx->workq);
+
+ dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n");
+
+ return 0;
+
+out:
+ if (acpi_disabled)
+ mbox_free_channel(ctx->mbox_chan);
+ else
+ pcc_mbox_free_channel(ctx->pcc_chan);
+out_mbox_free:
+ kfifo_free(&ctx->async_msg_fifo);
+
+ return rc;
+}
+
+static int xgene_hwmon_remove(struct platform_device *pdev)
+{
+ struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&ctx->workq);
+ hwmon_device_unregister(ctx->hwmon_dev);
+ kfifo_free(&ctx->async_msg_fifo);
+ if (acpi_disabled)
+ mbox_free_channel(ctx->mbox_chan);
+ else
+ pcc_mbox_free_channel(ctx->pcc_chan);
+
+ return 0;
+}
+
+static const struct of_device_id xgene_hwmon_of_match[] = {
+ {.compatible = "apm,xgene-slimpro-hwmon"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
+
+static struct platform_driver xgene_hwmon_driver = {
+ .probe = xgene_hwmon_probe,
+ .remove = xgene_hwmon_remove,
+ .driver = {
+ .name = "xgene-slimpro-hwmon",
+ .of_match_table = xgene_hwmon_of_match,
+ .acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match),
+ },
+};
+module_platform_driver(xgene_hwmon_driver);
+
+MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor");
+MODULE_LICENSE("GPL");