summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-12-15 16:06:14 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2020-12-15 16:06:14 -0800
commit0f97458173a23c8f218f6041767d0a145a13abe6 (patch)
treeb91aa532e5a8c16b9d03b383a1a8ae9c718a684f /drivers
parentce51c2b7ceb23a23eb0dc523c80879d8f35e4f38 (diff)
parent1a033769a4fe9a86ee791fd553b6a996dd76e026 (diff)
Merge tag 'hwmon-for-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "New drivers: - SB-TSI sensors - Lineat Technology LTC2992 - Delta power supplies Q54SJ108A2 - Maxim MAX127 - Corsair PSU - STMicroelectronics PM6764 Voltage Regulator New chip support: - P10 added to fsi/occ driver - NCT6687D added to nct6883 driver - Intel-based Xserves added to applesmc driver - AMD family 19h model 01h added to amd_energy driver And various minor bug fixes and improvements" * tag 'hwmon-for-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (41 commits) dt-bindings: (hwmon/sbtsi_temp) Add SB-TSI hwmon driver bindings hwmon: (sbtsi) Add documentation hwmon: (sbtsi) Add basic support for SB-TSI sensors hwmon: (iio_hwmon) Drop bogus __refdata annotation hwmon: (xgene) Drop bogus __refdata annotation dt-bindings: hwmon: convert AD ADM1275 bindings to dt-schema hwmon: (occ) Add new temperature sensor type fsi: occ: Add support for P10 dt-bindings: fsi: Add P10 OCC device documentation dt-bindings: hwmon: convert TI ADS7828 bindings to dt-schema dt-bindings: hwmon: convert AD AD741x bindings to dt-schema dt-bindings: hwmon: convert TI INA2xx bindings to dt-schema hwmon: (ltc2992) Fix less than zero comparisons with an unsigned integer hwmon: (pmbus/q54sj108a2) Correct title underline length dt-bindings: hwmon: Add documentation for ltc2992 hwmon: (ltc2992) Add support for GPIOs. hwmon: (ltc2992) Add support hwmon: (pmbus) Driver for Delta power supplies Q54SJ108A2 hwmon: Add driver for STMicroelectronics PM6764 Voltage Regulator hwmon: (nct6683) Support NCT6687D. ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/fsi/fsi-occ.c125
-rw-r--r--drivers/hwmon/Kconfig44
-rw-r--r--drivers/hwmon/Makefile4
-rw-r--r--drivers/hwmon/abx500.c2
-rw-r--r--drivers/hwmon/acpi_power_meter.c9
-rw-r--r--drivers/hwmon/adm1177.c10
-rw-r--r--drivers/hwmon/adt7470.c154
-rw-r--r--drivers/hwmon/amd_energy.c1
-rw-r--r--drivers/hwmon/applesmc.c4
-rw-r--r--drivers/hwmon/corsair-psu.c600
-rw-r--r--drivers/hwmon/drivetemp.c2
-rw-r--r--drivers/hwmon/ibmpowernv.c2
-rw-r--r--drivers/hwmon/iio_hwmon.c2
-rw-r--r--drivers/hwmon/ina3221.c4
-rw-r--r--drivers/hwmon/ltc2992.c971
-rw-r--r--drivers/hwmon/max127.c352
-rw-r--r--drivers/hwmon/nct6683.c14
-rw-r--r--drivers/hwmon/occ/common.c75
-rw-r--r--drivers/hwmon/pmbus/Kconfig18
-rw-r--r--drivers/hwmon/pmbus/Makefile2
-rw-r--r--drivers/hwmon/pmbus/adm1266.c1
-rw-r--r--drivers/hwmon/pmbus/adm1275.c1
-rw-r--r--drivers/hwmon/pmbus/bel-pfe.c1
-rw-r--r--drivers/hwmon/pmbus/ibm-cffps.c1
-rw-r--r--drivers/hwmon/pmbus/inspur-ipsps.c1
-rw-r--r--drivers/hwmon/pmbus/ir35221.c1
-rw-r--r--drivers/hwmon/pmbus/ir38064.c1
-rw-r--r--drivers/hwmon/pmbus/irps5401.c1
-rw-r--r--drivers/hwmon/pmbus/isl68137.c1
-rw-r--r--drivers/hwmon/pmbus/lm25066.c1
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c1
-rw-r--r--drivers/hwmon/pmbus/ltc3815.c1
-rw-r--r--drivers/hwmon/pmbus/max16064.c1
-rw-r--r--drivers/hwmon/pmbus/max16601.c1
-rw-r--r--drivers/hwmon/pmbus/max20730.c3
-rw-r--r--drivers/hwmon/pmbus/max20751.c1
-rw-r--r--drivers/hwmon/pmbus/max31785.c1
-rw-r--r--drivers/hwmon/pmbus/max34440.c1
-rw-r--r--drivers/hwmon/pmbus/max8688.c1
-rw-r--r--drivers/hwmon/pmbus/mp2975.c1
-rw-r--r--drivers/hwmon/pmbus/pm6764tr.c75
-rw-r--r--drivers/hwmon/pmbus/pmbus.c1
-rw-r--r--drivers/hwmon/pmbus/pmbus.h1
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c20
-rw-r--r--drivers/hwmon/pmbus/pxe1610.c1
-rw-r--r--drivers/hwmon/pmbus/q54sj108a2.c422
-rw-r--r--drivers/hwmon/pmbus/tps40422.c1
-rw-r--r--drivers/hwmon/pmbus/tps53679.c1
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c1
-rw-r--r--drivers/hwmon/pmbus/ucd9200.c1
-rw-r--r--drivers/hwmon/pmbus/xdpe12284.c1
-rw-r--r--drivers/hwmon/pmbus/zl6100.c1
-rw-r--r--drivers/hwmon/pwm-fan.c164
-rw-r--r--drivers/hwmon/sbtsi_temp.c250
-rw-r--r--drivers/hwmon/xgene-hwmon.c2
55 files changed, 3167 insertions, 192 deletions
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index 9eeb856c8905..10ca2e290655 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -14,6 +14,7 @@
#include <linux/mutex.h>
#include <linux/fsi-occ.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -24,8 +25,13 @@
#define OCC_CMD_DATA_BYTES 4090
#define OCC_RESP_DATA_BYTES 4089
-#define OCC_SRAM_CMD_ADDR 0xFFFBE000
-#define OCC_SRAM_RSP_ADDR 0xFFFBF000
+#define OCC_P9_SRAM_CMD_ADDR 0xFFFBE000
+#define OCC_P9_SRAM_RSP_ADDR 0xFFFBF000
+
+#define OCC_P10_SRAM_CMD_ADDR 0xFFFFD000
+#define OCC_P10_SRAM_RSP_ADDR 0xFFFFE000
+
+#define OCC_P10_SRAM_MODE 0x58 /* Normal mode, OCB channel 2 */
/*
* Assume we don't have much FFDC, if we do we'll overflow and
@@ -37,11 +43,14 @@
#define OCC_TIMEOUT_MS 1000
#define OCC_CMD_IN_PRG_WAIT_MS 50
+enum versions { occ_p9, occ_p10 };
+
struct occ {
struct device *dev;
struct device *sbefifo;
char name[32];
int idx;
+ enum versions version;
struct miscdevice mdev;
struct mutex occ_lock;
};
@@ -235,29 +244,43 @@ static int occ_verify_checksum(struct occ_response *resp, u16 data_length)
return 0;
}
-static int occ_getsram(struct occ *occ, u32 address, void *data, ssize_t len)
+static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
{
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
- size_t resp_len, resp_data_len;
- __be32 *resp, cmd[5];
- int rc;
+ size_t cmd_len, resp_len, resp_data_len;
+ __be32 *resp, cmd[6];
+ int idx = 0, rc;
/*
* Magic sequence to do SBE getsram command. SBE will fetch data from
* specified SRAM address.
*/
- cmd[0] = cpu_to_be32(0x5);
+ switch (occ->version) {
+ default:
+ case occ_p9:
+ cmd_len = 5;
+ cmd[2] = cpu_to_be32(1); /* Normal mode */
+ cmd[3] = cpu_to_be32(OCC_P9_SRAM_RSP_ADDR + offset);
+ break;
+ case occ_p10:
+ idx = 1;
+ cmd_len = 6;
+ cmd[2] = cpu_to_be32(OCC_P10_SRAM_MODE);
+ cmd[3] = 0;
+ cmd[4] = cpu_to_be32(OCC_P10_SRAM_RSP_ADDR + offset);
+ break;
+ }
+
+ cmd[0] = cpu_to_be32(cmd_len);
cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_OCC_SRAM);
- cmd[2] = cpu_to_be32(1);
- cmd[3] = cpu_to_be32(address);
- cmd[4] = cpu_to_be32(data_len);
+ cmd[4 + idx] = cpu_to_be32(data_len);
resp_len = (data_len >> 2) + OCC_SBE_STATUS_WORDS;
resp = kzalloc(resp_len << 2, GFP_KERNEL);
if (!resp)
return -ENOMEM;
- rc = sbefifo_submit(occ->sbefifo, cmd, 5, resp, &resp_len);
+ rc = sbefifo_submit(occ->sbefifo, cmd, cmd_len, resp, &resp_len);
if (rc)
goto free;
@@ -287,20 +310,21 @@ free:
return rc;
}
-static int occ_putsram(struct occ *occ, u32 address, const void *data,
- ssize_t len)
+static int occ_putsram(struct occ *occ, const void *data, ssize_t len)
{
size_t cmd_len, buf_len, resp_len, resp_data_len;
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
__be32 *buf;
- int rc;
+ int idx = 0, rc;
+
+ cmd_len = (occ->version == occ_p10) ? 6 : 5;
/*
* We use the same buffer for command and response, make
* sure it's big enough
*/
resp_len = OCC_SBE_STATUS_WORDS;
- cmd_len = (data_len >> 2) + 5;
+ cmd_len += data_len >> 2;
buf_len = max(cmd_len, resp_len);
buf = kzalloc(buf_len << 2, GFP_KERNEL);
if (!buf)
@@ -312,11 +336,23 @@ static int occ_putsram(struct occ *occ, u32 address, const void *data,
*/
buf[0] = cpu_to_be32(cmd_len);
buf[1] = cpu_to_be32(SBEFIFO_CMD_PUT_OCC_SRAM);
- buf[2] = cpu_to_be32(1);
- buf[3] = cpu_to_be32(address);
- buf[4] = cpu_to_be32(data_len);
- memcpy(&buf[5], data, len);
+ switch (occ->version) {
+ default:
+ case occ_p9:
+ buf[2] = cpu_to_be32(1); /* Normal mode */
+ buf[3] = cpu_to_be32(OCC_P9_SRAM_CMD_ADDR);
+ break;
+ case occ_p10:
+ idx = 1;
+ buf[2] = cpu_to_be32(OCC_P10_SRAM_MODE);
+ buf[3] = 0;
+ buf[4] = cpu_to_be32(OCC_P10_SRAM_CMD_ADDR);
+ break;
+ }
+
+ buf[4 + idx] = cpu_to_be32(data_len);
+ memcpy(&buf[5 + idx], data, len);
rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
if (rc)
@@ -356,21 +392,35 @@ free:
static int occ_trigger_attn(struct occ *occ)
{
__be32 buf[OCC_SBE_STATUS_WORDS];
- size_t resp_len, resp_data_len;
- int rc;
+ size_t cmd_len, resp_len, resp_data_len;
+ int idx = 0, rc;
- BUILD_BUG_ON(OCC_SBE_STATUS_WORDS < 7);
+ BUILD_BUG_ON(OCC_SBE_STATUS_WORDS < 8);
resp_len = OCC_SBE_STATUS_WORDS;
- buf[0] = cpu_to_be32(0x5 + 0x2); /* Chip-op length in words */
+ switch (occ->version) {
+ default:
+ case occ_p9:
+ cmd_len = 7;
+ buf[2] = cpu_to_be32(3); /* Circular mode */
+ buf[3] = 0;
+ break;
+ case occ_p10:
+ idx = 1;
+ cmd_len = 8;
+ buf[2] = cpu_to_be32(0xd0); /* Circular mode, OCB Channel 1 */
+ buf[3] = 0;
+ buf[4] = 0;
+ break;
+ }
+
+ buf[0] = cpu_to_be32(cmd_len); /* Chip-op length in words */
buf[1] = cpu_to_be32(SBEFIFO_CMD_PUT_OCC_SRAM);
- buf[2] = cpu_to_be32(0x3); /* Mode: Circular */
- buf[3] = cpu_to_be32(0x0); /* Address: ignore in mode 3 */
- buf[4] = cpu_to_be32(0x8); /* Data length in bytes */
- buf[5] = cpu_to_be32(0x20010000); /* Trigger OCC attention */
- buf[6] = 0;
+ buf[4 + idx] = cpu_to_be32(8); /* Data length in bytes */
+ buf[5 + idx] = cpu_to_be32(0x20010000); /* Trigger OCC attention */
+ buf[6 + idx] = 0;
- rc = sbefifo_submit(occ->sbefifo, buf, 7, buf, &resp_len);
+ rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
if (rc)
goto error;
@@ -429,7 +479,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
/* Extract the seq_no from the command (first byte) */
seq_no = *(const u8 *)request;
- rc = occ_putsram(occ, OCC_SRAM_CMD_ADDR, request, req_len);
+ rc = occ_putsram(occ, request, req_len);
if (rc)
goto done;
@@ -440,7 +490,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
/* Read occ response header */
start = jiffies;
do {
- rc = occ_getsram(occ, OCC_SRAM_RSP_ADDR, resp, 8);
+ rc = occ_getsram(occ, 0, resp, 8);
if (rc)
goto done;
@@ -476,8 +526,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
/* Grab the rest */
if (resp_data_length > 1) {
/* already got 3 bytes resp, also need 2 bytes checksum */
- rc = occ_getsram(occ, OCC_SRAM_RSP_ADDR + 8,
- &resp->data[3], resp_data_length - 1);
+ rc = occ_getsram(occ, 8, &resp->data[3], resp_data_length - 1);
if (rc)
goto done;
}
@@ -517,6 +566,7 @@ static int occ_probe(struct platform_device *pdev)
if (!occ)
return -ENOMEM;
+ occ->version = (uintptr_t)of_device_get_match_data(dev);
occ->dev = dev;
occ->sbefifo = dev->parent;
mutex_init(&occ->occ_lock);
@@ -575,7 +625,14 @@ static int occ_remove(struct platform_device *pdev)
}
static const struct of_device_id occ_match[] = {
- { .compatible = "ibm,p9-occ" },
+ {
+ .compatible = "ibm,p9-occ",
+ .data = (void *)occ_p9
+ },
+ {
+ .compatible = "ibm,p10-occ",
+ .data = (void *)occ_p10
+ },
{ },
};
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a850e4f0e0bd..1ecf697d8d99 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -449,6 +449,19 @@ config SENSORS_CORSAIR_CPRO
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
@@ -858,6 +871,18 @@ config SENSORS_LTC2990
This driver can also be built as a module. If so, the module will
be called ltc2990.
+config SENSORS_LTC2992
+ tristate "Linear Technology LTC2992"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ If you say yes here you get support for Linear Technology LTC2992
+ I2C System Monitor. The LTC2992 measures current, voltage, and
+ power of two supplies.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2992.
+
config SENSORS_LTC4151
tristate "Linear Technology LTC4151"
depends on I2C
@@ -937,6 +962,15 @@ config SENSORS_MAX1111
This driver can also be built as a module. If so, the module
will be called max1111.
+config SENSORS_MAX127
+ tristate "Maxim MAX127 12-bit 8-channel Data Acquisition System"
+ depends on I2C
+ help
+ Say y here to support Maxim's MAX127 DAS chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called max127.
+
config SENSORS_MAX16065
tristate "Maxim MAX16065 System Manager and compatibles"
depends on I2C
@@ -1499,6 +1533,16 @@ config SENSORS_SL28CPLD
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_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 9db2903b61e5..09a86c5e1d29 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -57,6 +57,7 @@ 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
@@ -118,6 +119,7 @@ obj-$(CONFIG_SENSORS_LTC2947) += ltc2947-core.o
obj-$(CONFIG_SENSORS_LTC2947_I2C) += ltc2947-i2c.o
obj-$(CONFIG_SENSORS_LTC2947_SPI) += ltc2947-spi.o
obj-$(CONFIG_SENSORS_LTC2990) += ltc2990.o
+obj-$(CONFIG_SENSORS_LTC2992) += ltc2992.o
obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o
@@ -126,6 +128,7 @@ 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
@@ -158,6 +161,7 @@ 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_SCH56XX_COMMON)+= sch56xx-common.o
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c
index 50e67cdd8e5e..4b9648819836 100644
--- a/drivers/hwmon/abx500.c
+++ b/drivers/hwmon/abx500.c
@@ -263,7 +263,7 @@ static ssize_t max_alarm_show(struct device *dev,
static umode_t abx500_attrs_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct abx500_temp *data = dev_get_drvdata(dev);
if (data->ops.is_visible)
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index a270b975e90b..848718ab7312 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -725,8 +725,10 @@ static void free_capabilities(struct acpi_power_meter_resource *resource)
int i;
str = &resource->model_number;
- for (i = 0; i < 3; i++, str++)
+ for (i = 0; i < 3; i++, str++) {
kfree(*str);
+ *str = NULL;
+ }
}
static int read_capabilities(struct acpi_power_meter_resource *resource)
@@ -801,9 +803,7 @@ static int read_capabilities(struct acpi_power_meter_resource *resource)
dev_info(&resource->acpi_dev->dev, "Found ACPI power meter.\n");
goto end;
error:
- str = &resource->model_number;
- for (i = 0; i < 3; i++, str++)
- kfree(*str);
+ free_capabilities(resource);
end:
kfree(buffer.pointer);
return res;
@@ -874,7 +874,6 @@ static int acpi_power_meter_add(struct acpi_device *device)
strcpy(acpi_device_class(device), ACPI_POWER_METER_CLASS);
device->driver_data = resource;
- free_capabilities(resource);
res = read_capabilities(resource);
if (res)
goto exit_free;
diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c
index 6e8bb661894b..0c5dbc5e33b4 100644
--- a/drivers/hwmon/adm1177.c
+++ b/drivers/hwmon/adm1177.c
@@ -25,11 +25,11 @@
/**
* struct adm1177_state - driver instance specific data
- * @client pointer to i2c client
- * @reg regulator info for the the power supply of the device
- * @r_sense_uohm current sense resistor value
- * @alert_threshold_ua current limit for shutdown
- * @vrange_high internal voltage divider
+ * @client: pointer to i2c client
+ * @reg: regulator info for the power supply of the device
+ * @r_sense_uohm: current sense resistor value
+ * @alert_threshold_ua: current limit for shutdown
+ * @vrange_high: internal voltage divider
*/
struct adm1177_state {
struct i2c_client *client;
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 740f39a54ab0..2e8feacccf84 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -270,37 +270,11 @@ static int adt7470_update_thread(void *p)
return 0;
}
-static struct adt7470_data *adt7470_update_device(struct device *dev)
+static int adt7470_update_sensors(struct adt7470_data *data)
{
- struct adt7470_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- unsigned long local_jiffies = jiffies;
u8 cfg;
int i;
- int need_sensors = 1;
- int need_limits = 1;
-
- /*
- * Figure out if we need to update the shadow registers.
- * Lockless means that we may occasionally report out of
- * date data.
- */
- if (time_before(local_jiffies, data->sensors_last_updated +
- SENSOR_REFRESH_INTERVAL) &&
- data->sensors_valid)
- need_sensors = 0;
-
- if (time_before(local_jiffies, data->limits_last_updated +
- LIMIT_REFRESH_INTERVAL) &&
- data->limits_valid)
- need_limits = 0;
-
- if (!need_sensors && !need_limits)
- return data;
-
- mutex_lock(&data->lock);
- if (!need_sensors)
- goto no_sensor_update;
if (!data->temperatures_probed)
adt7470_read_temperatures(client, data);
@@ -352,12 +326,13 @@ static struct adt7470_data *adt7470_update_device(struct device *dev)
data->alarms_mask = adt7470_read_word_data(client,
ADT7470_REG_ALARM1_MASK);
- data->sensors_last_updated = local_jiffies;
- data->sensors_valid = 1;
+ return 0;
+}
-no_sensor_update:
- if (!need_limits)
- goto out;
+static int adt7470_update_limits(struct adt7470_data *data)
+{
+ struct i2c_client *client = data->client;
+ int i;
for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
data->temp_min[i] = i2c_smbus_read_byte_data(client,
@@ -382,12 +357,55 @@ no_sensor_update:
ADT7470_REG_PWM_TMIN(i));
}
- data->limits_last_updated = local_jiffies;
- data->limits_valid = 1;
+ return 0;
+}
+static struct adt7470_data *adt7470_update_device(struct device *dev)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ unsigned long local_jiffies = jiffies;
+ int need_sensors = 1;
+ int need_limits = 1;
+ int err;
+
+ /*
+ * Figure out if we need to update the shadow registers.
+ * Lockless means that we may occasionally report out of
+ * date data.
+ */
+ if (time_before(local_jiffies, data->sensors_last_updated +
+ SENSOR_REFRESH_INTERVAL) &&
+ data->sensors_valid)
+ need_sensors = 0;
+
+ if (time_before(local_jiffies, data->limits_last_updated +
+ LIMIT_REFRESH_INTERVAL) &&
+ data->limits_valid)
+ need_limits = 0;
+
+ if (!need_sensors && !need_limits)
+ return data;
+
+ mutex_lock(&data->lock);
+ if (need_sensors) {
+ err = adt7470_update_sensors(data);
+ if (err < 0)
+ goto out;
+ data->sensors_last_updated = local_jiffies;
+ data->sensors_valid = 1;
+ }
+
+ if (need_limits) {
+ err = adt7470_update_limits(data);
+ if (err < 0)
+ goto out;
+ data->limits_last_updated = local_jiffies;
+ data->limits_valid = 1;
+ }
out:
mutex_unlock(&data->lock);
- return data;
+
+ return err < 0 ? ERR_PTR(err) : data;
}
static ssize_t auto_update_interval_show(struct device *dev,
@@ -395,6 +413,10 @@ static ssize_t auto_update_interval_show(struct device *dev,
char *buf)
{
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->auto_update_interval);
}
@@ -422,6 +444,10 @@ static ssize_t num_temp_sensors_show(struct device *dev,
char *buf)
{
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->num_temp_sensors);
}
@@ -451,6 +477,10 @@ static ssize_t temp_min_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]);
}
@@ -483,6 +513,10 @@ static ssize_t temp_max_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]);
}
@@ -515,6 +549,10 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
{
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->temp[attr->index]);
}
@@ -524,6 +562,9 @@ static ssize_t alarm_mask_show(struct device *dev,
{
struct adt7470_data *data = adt7470_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%x\n", data->alarms_mask);
}
@@ -554,6 +595,9 @@ static ssize_t fan_max_show(struct device *dev,
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
if (FAN_DATA_VALID(data->fan_max[attr->index]))
return sprintf(buf, "%d\n",
FAN_PERIOD_TO_RPM(data->fan_max[attr->index]));
@@ -590,6 +634,9 @@ static ssize_t fan_min_show(struct device *dev,
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
if (FAN_DATA_VALID(data->fan_min[attr->index]))
return sprintf(buf, "%d\n",
FAN_PERIOD_TO_RPM(data->fan_min[attr->index]));
@@ -626,6 +673,9 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
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);
+
if (FAN_DATA_VALID(data->fan[attr->index]))
return sprintf(buf, "%d\n",
FAN_PERIOD_TO_RPM(data->fan[attr->index]));
@@ -637,6 +687,10 @@ 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);
}
@@ -670,6 +724,10 @@ static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr,
{
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[attr->index]);
}
@@ -763,6 +821,10 @@ static ssize_t pwm_max_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->pwm_max[attr->index]);
}
@@ -794,6 +856,10 @@ static ssize_t pwm_min_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->pwm_min[attr->index]);
}
@@ -825,6 +891,10 @@ static ssize_t pwm_tmax_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
/* the datasheet says that tmax = tmin + 20C */
return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index]));
}
@@ -834,6 +904,10 @@ static ssize_t pwm_tmin_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]);
}
@@ -866,6 +940,10 @@ static ssize_t pwm_auto_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]);
}
@@ -911,8 +989,12 @@ static ssize_t pwm_auto_temp_show(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adt7470_data *data = adt7470_update_device(dev);
- u8 ctrl = data->pwm_auto_temp[attr->index];
+ u8 ctrl;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ ctrl = data->pwm_auto_temp[attr->index];
if (ctrl)
return sprintf(buf, "%d\n", 1 << (ctrl - 1));
else
diff --git a/drivers/hwmon/amd_energy.c b/drivers/hwmon/amd_energy.c
index 3197cda7bcd9..9b306448b7a0 100644
--- a/drivers/hwmon/amd_energy.c
+++ b/drivers/hwmon/amd_energy.c
@@ -331,6 +331,7 @@ static struct platform_device *amd_energy_platdev;
static const struct x86_cpu_id cpu_ids[] __initconst = {
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL),
+ X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x01, NULL),
{}
};
MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 79b498f816fe..89207af81c48 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -1299,6 +1299,10 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = {
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
DMI_MATCH(DMI_PRODUCT_NAME, "iMac") },
},
+ { applesmc_dmi_match, "Apple Xserve", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Xserve") },
+ },
{ .ident = NULL }
};
diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c
new file mode 100644
index 000000000000..99494056f4bd
--- /dev/null
+++ b/drivers/hwmon/corsair-psu.c
@@ -0,0 +1,600 @@
+// 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 PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */
+#define PSU_CMD_IN_VOLTS 0x88 /* the rest of the commands expect length 3 */
+#define PSU_CMD_IN_AMPS 0x89
+#define PSU_CMD_RAIL_OUT_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_WATTS 0xEE
+#define PSU_CMD_TOTAL_UPTIME 0xD1
+#define PSU_CMD_UPTIME 0xD2
+#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];
+};
+
+/* some values are SMBus LINEAR11 data which need a conversion */
+static int corsairpsu_linear11_to_int(const int val)
+{
+ int exp = (val & 0xFFFF) >> 0x0B;
+ int mant = val & 0x7FF;
+ int i;
+
+ if (exp > 0x0F)
+ exp -= 0x20;
+ if (mant > 0x3FF)
+ mant -= 0x800;
+ if ((mant & 0x01) == 1)
+ ++mant;
+ if (exp < 0) {
+ for (i = 0; i < -exp; ++i)
+ mant /= 2;
+ } else {
+ for (i = 0; i < exp; ++i)
+ mant *= 2;
+ }
+
+ return mant;
+}
+
+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_OUT_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_IN_VOLTS:
+ case PSU_CMD_IN_AMPS:
+ case PSU_CMD_RAIL_OUT_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);
+ 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:
+ *val = tmp;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_temp && (attr == hwmon_temp_input || attr == hwmon_temp_label))
+ return 0444;
+ else if (type == hwmon_fan && (attr == hwmon_fan_input || attr == hwmon_fan_label))
+ return 0444;
+ else if (type == hwmon_power && (attr == hwmon_power_input || attr == hwmon_power_label))
+ return 0444;
+ else if (type == hwmon_in && (attr == hwmon_in_input || attr == hwmon_in_label))
+ return 0444;
+ else if (type == hwmon_curr && (attr == hwmon_curr_input || attr == hwmon_curr_label))
+ return 0444;
+
+ return 0;
+}
+
+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);
+ int ret;
+
+ if (type == hwmon_temp && attr == hwmon_temp_input && channel < 2) {
+ ret = corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, channel,
+ val);
+ } else if (type == hwmon_fan && attr == hwmon_fan_input) {
+ ret = corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val);
+ } else if (type == hwmon_power && attr == hwmon_power_input) {
+ switch (channel) {
+ case 0:
+ ret = corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val);
+ break;
+ case 1 ... 3:
+ ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ } else if (type == hwmon_in && attr == hwmon_in_input) {
+ switch (channel) {
+ case 0:
+ ret = corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val);
+ break;
+ case 1 ... 3:
+ ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_OUT_VOLTS, channel - 1, val);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ } else if (type == hwmon_curr && attr == hwmon_curr_input) {
+ switch (channel) {
+ case 0:
+ ret = corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val);
+ break;
+ case 1 ... 3:
+ ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+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_INPUT | HWMON_T_LABEL),
+ 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_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),
+ 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 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);
+}
+
+#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;
+ }
+
+ priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv,
+ &corsairpsu_chip_info, 0);
+
+ 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;
+}
+
+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 */
+ { 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 */
+ { },
+};
+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,
+};
+module_hid_driver(corsairpsu_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wilken Gottwalt <wilken.gottwalt@posteo.net>");
+MODULE_DESCRIPTION("Linux driver for Corsair power supplies with HID sensors interface");
diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c
index 72c760373957..1eb37106a220 100644
--- a/drivers/hwmon/drivetemp.c
+++ b/drivers/hwmon/drivetemp.c
@@ -10,7 +10,7 @@
* hwmon: Driver for SCSI/ATA temperature sensors
* by Constantin Baranov <const@mimas.ru>, submitted September 2009
*
- * This drive supports reporting the temperatire of SATA drives. It can be
+ * This drive supports reporting the temperature of SATA drives. It can be
* easily extended to report the temperature of SCSI drives.
*
* The primary means to read drive temperatures and temperature limits
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
index a750647e66a4..8e3724728cce 100644
--- a/drivers/hwmon/ibmpowernv.c
+++ b/drivers/hwmon/ibmpowernv.c
@@ -240,7 +240,7 @@ static int get_sensor_index_attr(const char *name, u32 *index, char *attr)
if (err)
return err;
- strncpy(attr, dash_pos + 1, MAX_ATTR_LEN);
+ strscpy(attr, dash_pos + 1, MAX_ATTR_LEN);
return 0;
}
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
index b85a125dd86f..580a7d125b88 100644
--- a/drivers/hwmon/iio_hwmon.c
+++ b/drivers/hwmon/iio_hwmon.c
@@ -169,7 +169,7 @@ static const struct of_device_id iio_hwmon_of_match[] = {
};
MODULE_DEVICE_TABLE(of, iio_hwmon_of_match);
-static struct platform_driver __refdata iio_hwmon_driver = {
+static struct platform_driver iio_hwmon_driver = {
.driver = {
.name = "iio_hwmon",
.of_match_table = iio_hwmon_of_match,
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index 41fb17e0d641..d80bd3efcd6d 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -139,7 +139,7 @@ static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
(ina->reg_config & INA3221_CONFIG_CHx_EN(channel));
}
-/**
+/*
* Helper function to return the resistor value for current summation.
*
* There is a condition to calculate current summation -- all the shunt
@@ -489,7 +489,7 @@ static int ina3221_write_enable(struct device *dev, int channel, bool enable)
/* For enabling routine, increase refcount and resume() at first */
if (enable) {
- ret = pm_runtime_get_sync(ina->pm_dev);
+ ret = pm_runtime_resume_and_get(ina->pm_dev);
if (ret < 0) {
dev_err(dev, "Failed to get PM runtime\n");
return ret;
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
new file mode 100644
index 000000000000..4382105bf142
--- /dev/null
+++ b/drivers/hwmon/ltc2992.c
@@ -0,0 +1,971 @@
+// 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;
+
+ gpio_nr = 0;
+ for_each_set_bit_from(gpio_nr, mask, LTC2992_GPIO_NR) {
+ if (test_bit(LTC2992_GPIO_BIT(gpio_nr), &gpio_status))
+ set_bit(gpio_nr, bits);
+ }
+
+ return 0;
+}
+
+static void ltc2992_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct ltc2992_state *st = gpiochip_get_data(chip);
+ unsigned long gpio_ctrl;
+ int reg;
+
+ mutex_lock(&st->gpio_mutex);
+ reg = ltc2992_read_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1);
+ if (reg < 0) {
+ mutex_unlock(&st->gpio_mutex);
+ return;
+ }
+
+ gpio_ctrl = reg;
+ assign_bit(ltc2992_gpio_addr_map[offset].ctrl_bit, &gpio_ctrl, value);
+
+ ltc2992_write_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1, gpio_ctrl);
+ mutex_unlock(&st->gpio_mutex);
+}
+
+static void ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct ltc2992_state *st = gpiochip_get_data(chip);
+ unsigned long gpio_ctrl_io = 0;
+ unsigned long gpio_ctrl = 0;
+ unsigned int gpio_nr;
+
+ for_each_set_bit(gpio_nr, mask, LTC2992_GPIO_NR) {
+ if (gpio_nr < 3)
+ assign_bit(ltc2992_gpio_addr_map[gpio_nr].ctrl_bit, &gpio_ctrl_io, true);
+
+ if (gpio_nr == 3)
+ assign_bit(ltc2992_gpio_addr_map[gpio_nr].ctrl_bit, &gpio_ctrl, true);
+ }
+
+ mutex_lock(&st->gpio_mutex);
+ ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, gpio_ctrl_io);
+ ltc2992_write_reg(st, LTC2992_GPIO_CTRL, 1, gpio_ctrl);
+ mutex_unlock(&st->gpio_mutex);
+}
+
+static int ltc2992_config_gpio(struct ltc2992_state *st)
+{
+ const char *name = dev_name(&st->client->dev);
+ char *gpio_name;
+ int ret;
+ int i;
+
+ ret = ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, 0);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&st->gpio_mutex);
+
+ for (i = 0; i < ARRAY_SIZE(st->gpio_names); i++) {
+ gpio_name = devm_kasprintf(&st->client->dev, GFP_KERNEL, "ltc2992-%x-%s",
+ st->client->addr, ltc2992_gpio_names[i]);
+ if (!gpio_name)
+ return -ENOMEM;
+
+ st->gpio_names[i] = gpio_name;
+ }
+
+ st->gc.label = name;
+ st->gc.parent = &st->client->dev;
+ st->gc.owner = THIS_MODULE;
+ st->gc.base = -1;
+ st->gc.names = st->gpio_names;
+ st->gc.ngpio = ARRAY_SIZE(st->gpio_names);
+ st->gc.get = ltc2992_gpio_get;
+ st->gc.get_multiple = ltc2992_gpio_get_multiple;
+ st->gc.set = ltc2992_gpio_set;
+ st->gc.set_multiple = ltc2992_gpio_set_multiple;
+
+ ret = devm_gpiochip_add_data(&st->client->dev, &st->gc, st);
+ if (ret)
+ dev_err(&st->client->dev, "GPIO registering failed (%d)\n", ret);
+
+ return ret;
+}
+
+static umode_t ltc2992_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ const struct ltc2992_state *st = data;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_in_reset_history:
+ return 0200;
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_lowest:
+ case hwmon_in_highest:
+ case hwmon_in_min_alarm:
+ case hwmon_in_max_alarm:
+ return 0444;
+ case hwmon_in_min:
+ case hwmon_in_max:
+ return 0644;
+ }
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_lowest:
+ case hwmon_curr_highest:
+ case hwmon_curr_min_alarm:
+ case hwmon_curr_max_alarm:
+ if (st->r_sense_uohm[channel])
+ return 0444;
+ break;
+ case hwmon_curr_min:
+ case hwmon_curr_max:
+ if (st->r_sense_uohm[channel])
+ return 0644;
+ break;
+ }
+ break;
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ case hwmon_power_input_lowest:
+ case hwmon_power_input_highest:
+ case hwmon_power_min_alarm:
+ case hwmon_power_max_alarm:
+ if (st->r_sense_uohm[channel])
+ return 0444;
+ break;
+ case hwmon_power_min:
+ case hwmon_power_max:
+ if (st->r_sense_uohm[channel])
+ return 0644;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ltc2992_get_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long *val)
+{
+ int reg_val;
+
+ reg_val = ltc2992_read_reg(st, reg, 2);
+ if (reg_val < 0)
+ return reg_val;
+
+ reg_val = reg_val >> 4;
+ *val = DIV_ROUND_CLOSEST(reg_val * scale, 1000);
+
+ return 0;
+}
+
+static int ltc2992_set_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long val)
+{
+ val = DIV_ROUND_CLOSEST(val * 1000, scale);
+ val = val << 4;
+
+ return ltc2992_write_reg(st, reg, 2, val);
+}
+
+static int ltc2992_read_gpio_alarm(struct ltc2992_state *st, int nr_gpio, u32 attr, long *val)
+{
+ int reg_val;
+ u32 mask;
+
+ if (attr == hwmon_in_max_alarm)
+ mask = ltc2992_gpio_addr_map[nr_gpio].max_alarm_msk;
+ else
+ mask = ltc2992_gpio_addr_map[nr_gpio].min_alarm_msk;
+
+ reg_val = ltc2992_read_reg(st, ltc2992_gpio_addr_map[nr_gpio].alarm, 1);
+ if (reg_val < 0)
+ return reg_val;
+
+ *val = !!(reg_val & mask);
+ reg_val &= ~mask;
+
+ return ltc2992_write_reg(st, ltc2992_gpio_addr_map[nr_gpio].alarm, 1, reg_val);
+}
+
+static int ltc2992_read_gpios_in(struct device *dev, u32 attr, int nr_gpio, long *val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ switch (attr) {
+ case hwmon_in_input:
+ reg = ltc2992_gpio_addr_map[nr_gpio].data;
+ break;
+ case hwmon_in_lowest:
+ reg = ltc2992_gpio_addr_map[nr_gpio].min;
+ break;
+ case hwmon_in_highest:
+ reg = ltc2992_gpio_addr_map[nr_gpio].max;
+ break;
+ case hwmon_in_min:
+ reg = ltc2992_gpio_addr_map[nr_gpio].min_thresh;
+ break;
+ case hwmon_in_max:
+ reg = ltc2992_gpio_addr_map[nr_gpio].max_thresh;
+ break;
+ case hwmon_in_min_alarm:
+ case hwmon_in_max_alarm:
+ return ltc2992_read_gpio_alarm(st, nr_gpio, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_get_voltage(st, reg, LTC2992_VADC_GPIO_UV_LSB, val);
+}
+
+static int ltc2992_read_in_alarm(struct ltc2992_state *st, int channel, long *val, u32 attr)
+{
+ int reg_val;
+ u32 mask;
+
+ if (attr == hwmon_in_max_alarm)
+ mask = LTC2992_SENSE_FAULT_MSK(1);
+ else
+ mask = LTC2992_SENSE_FAULT_MSK(0);
+
+ reg_val = ltc2992_read_reg(st, LTC2992_SENSE_FAULT(channel), 1);
+ if (reg_val < 0)
+ return reg_val;
+
+ *val = !!(reg_val & mask);
+ reg_val &= ~mask;
+
+ return ltc2992_write_reg(st, LTC2992_SENSE_FAULT(channel), 1, reg_val);
+}
+
+static int ltc2992_read_in(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ if (channel > 1)
+ return ltc2992_read_gpios_in(dev, attr, channel - 2, val);
+
+ switch (attr) {
+ case hwmon_in_input:
+ reg = LTC2992_SENSE(channel);
+ break;
+ case hwmon_in_lowest:
+ reg = LTC2992_SENSE_MIN(channel);
+ break;
+ case hwmon_in_highest:
+ reg = LTC2992_SENSE_MAX(channel);
+ break;
+ case hwmon_in_min:
+ reg = LTC2992_SENSE_MIN_THRESH(channel);
+ break;
+ case hwmon_in_max:
+ reg = LTC2992_SENSE_MAX_THRESH(channel);
+ break;
+ case hwmon_in_min_alarm:
+ case hwmon_in_max_alarm:
+ return ltc2992_read_in_alarm(st, channel, val, attr);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_get_voltage(st, reg, LTC2992_VADC_UV_LSB, val);
+}
+
+static int ltc2992_get_current(struct ltc2992_state *st, u32 reg, u32 channel, long *val)
+{
+ int reg_val;
+
+ reg_val = ltc2992_read_reg(st, reg, 2);
+ if (reg_val < 0)
+ return reg_val;
+
+ reg_val = reg_val >> 4;
+ *val = DIV_ROUND_CLOSEST(reg_val * LTC2992_IADC_NANOV_LSB, st->r_sense_uohm[channel]);
+
+ return 0;
+}
+
+static int ltc2992_set_current(struct ltc2992_state *st, u32 reg, u32 channel, long val)
+{
+ u32 reg_val;
+
+ reg_val = DIV_ROUND_CLOSEST(val * st->r_sense_uohm[channel], LTC2992_IADC_NANOV_LSB);
+ reg_val = reg_val << 4;
+
+ return ltc2992_write_reg(st, reg, 2, reg_val);
+}
+
+static int ltc2992_read_curr_alarm(struct ltc2992_state *st, int channel, long *val, u32 attr)
+{
+ int reg_val;
+ u32 mask;
+
+ if (attr == hwmon_curr_max_alarm)
+ mask = LTC2992_DSENSE_FAULT_MSK(1);
+ else
+ mask = LTC2992_DSENSE_FAULT_MSK(0);
+
+ reg_val = ltc2992_read_reg(st, LTC2992_DSENSE_FAULT(channel), 1);
+ if (reg_val < 0)
+ return reg_val;
+
+ *val = !!(reg_val & mask);
+
+ reg_val &= ~mask;
+ return ltc2992_write_reg(st, LTC2992_DSENSE_FAULT(channel), 1, reg_val);
+}
+
+static int ltc2992_read_curr(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ switch (attr) {
+ case hwmon_curr_input:
+ reg = LTC2992_DSENSE(channel);
+ break;
+ case hwmon_curr_lowest:
+ reg = LTC2992_DSENSE_MIN(channel);
+ break;
+ case hwmon_curr_highest:
+ reg = LTC2992_DSENSE_MAX(channel);
+ break;
+ case hwmon_curr_min:
+ reg = LTC2992_DSENSE_MIN_THRESH(channel);
+ break;
+ case hwmon_curr_max:
+ reg = LTC2992_DSENSE_MAX_THRESH(channel);
+ break;
+ case hwmon_curr_min_alarm:
+ case hwmon_curr_max_alarm:
+ return ltc2992_read_curr_alarm(st, channel, val, attr);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_get_current(st, reg, channel, val);
+}
+
+static int ltc2992_get_power(struct ltc2992_state *st, u32 reg, u32 channel, long *val)
+{
+ int reg_val;
+
+ reg_val = ltc2992_read_reg(st, reg, 3);
+ if (reg_val < 0)
+ return reg_val;
+
+ *val = mul_u64_u32_div(reg_val, LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB,
+ st->r_sense_uohm[channel] * 1000);
+
+ return 0;
+}
+
+static int ltc2992_set_power(struct ltc2992_state *st, u32 reg, u32 channel, long val)
+{
+ u32 reg_val;
+
+ reg_val = mul_u64_u32_div(val, st->r_sense_uohm[channel] * 1000,
+ LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB);
+
+ return ltc2992_write_reg(st, reg, 3, reg_val);
+}
+
+static int ltc2992_read_power_alarm(struct ltc2992_state *st, int channel, long *val, u32 attr)
+{
+ int reg_val;
+ u32 mask;
+
+ if (attr == hwmon_power_max_alarm)
+ mask = LTC2992_POWER_FAULT_MSK(1);
+ else
+ mask = LTC2992_POWER_FAULT_MSK(0);
+
+ reg_val = ltc2992_read_reg(st, LTC2992_POWER_FAULT(channel), 1);
+ if (reg_val < 0)
+ return reg_val;
+
+ *val = !!(reg_val & mask);
+ reg_val &= ~mask;
+
+ return ltc2992_write_reg(st, LTC2992_POWER_FAULT(channel), 1, reg_val);
+}
+
+static int ltc2992_read_power(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ switch (attr) {
+ case hwmon_power_input:
+ reg = LTC2992_POWER(channel);
+ break;
+ case hwmon_power_input_lowest:
+ reg = LTC2992_POWER_MIN(channel);
+ break;
+ case hwmon_power_input_highest:
+ reg = LTC2992_POWER_MAX(channel);
+ break;
+ case hwmon_power_min:
+ reg = LTC2992_POWER_MIN_THRESH(channel);
+ break;
+ case hwmon_power_max:
+ reg = LTC2992_POWER_MAX_THRESH(channel);
+ break;
+ case hwmon_power_min_alarm:
+ case hwmon_power_max_alarm:
+ return ltc2992_read_power_alarm(st, channel, val, attr);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_get_power(st, reg, channel, val);
+}
+
+static int ltc2992_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long *val)
+{
+ switch (type) {
+ case hwmon_in:
+ return ltc2992_read_in(dev, attr, channel, val);
+ case hwmon_curr:
+ return ltc2992_read_curr(dev, attr, channel, val);
+ case hwmon_power:
+ return ltc2992_read_power(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ltc2992_write_curr(struct device *dev, u32 attr, int channel, long val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ switch (attr) {
+ case hwmon_curr_min:
+ reg = LTC2992_DSENSE_MIN_THRESH(channel);
+ break;
+ case hwmon_curr_max:
+ reg = LTC2992_DSENSE_MAX_THRESH(channel);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_set_current(st, reg, channel, val);
+}
+
+static int ltc2992_write_gpios_in(struct device *dev, u32 attr, int nr_gpio, long val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ switch (attr) {
+ case hwmon_in_min:
+ reg = ltc2992_gpio_addr_map[nr_gpio].min_thresh;
+ break;
+ case hwmon_in_max:
+ reg = ltc2992_gpio_addr_map[nr_gpio].max_thresh;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_set_voltage(st, reg, LTC2992_VADC_GPIO_UV_LSB, val);
+}
+
+static int ltc2992_write_in(struct device *dev, u32 attr, int channel, long val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ if (channel > 1)
+ return ltc2992_write_gpios_in(dev, attr, channel - 2, val);
+
+ switch (attr) {
+ case hwmon_in_min:
+ reg = LTC2992_SENSE_MIN_THRESH(channel);
+ break;
+ case hwmon_in_max:
+ reg = LTC2992_SENSE_MAX_THRESH(channel);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_set_voltage(st, reg, LTC2992_VADC_UV_LSB, val);
+}
+
+static int ltc2992_write_power(struct device *dev, u32 attr, int channel, long val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+ u32 reg;
+
+ switch (attr) {
+ case hwmon_power_min:
+ reg = LTC2992_POWER_MIN_THRESH(channel);
+ break;
+ case hwmon_power_max:
+ reg = LTC2992_POWER_MAX_THRESH(channel);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ltc2992_set_power(st, reg, channel, val);
+}
+
+static int ltc2992_write_chip(struct device *dev, u32 attr, int channel, long val)
+{
+ struct ltc2992_state *st = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_chip_in_reset_history:
+ return regmap_update_bits(st->regmap, LTC2992_CTRLB, LTC2992_RESET_HISTORY,
+ LTC2992_RESET_HISTORY);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ltc2992_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return ltc2992_write_chip(dev, attr, channel, val);
+ case hwmon_in:
+ return ltc2992_write_in(dev, attr, channel, val);
+ case hwmon_curr:
+ return ltc2992_write_curr(dev, attr, channel, val);
+ case hwmon_power:
+ return ltc2992_write_power(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_ops ltc2992_hwmon_ops = {
+ .is_visible = ltc2992_is_visible,
+ .read = ltc2992_read,
+ .write = ltc2992_write,
+};
+
+static const u32 ltc2992_chip_config[] = {
+ HWMON_C_IN_RESET_HISTORY,
+ 0
+};
+
+static const struct hwmon_channel_info ltc2992_chip = {
+ .type = hwmon_chip,
+ .config = ltc2992_chip_config,
+};
+
+static const u32 ltc2992_in_config[] = {
+ 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,
+ 0
+};
+
+static const struct hwmon_channel_info ltc2992_in = {
+ .type = hwmon_in,
+ .config = ltc2992_in_config,
+};
+
+static const u32 ltc2992_curr_config[] = {
+ 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,
+ 0
+};
+
+static const struct hwmon_channel_info ltc2992_curr = {
+ .type = hwmon_curr,
+ .config = ltc2992_curr_config,
+};
+
+static const u32 ltc2992_power_config[] = {
+ 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,
+ 0
+};
+
+static const struct hwmon_channel_info ltc2992_power = {
+ .type = hwmon_power,
+ .config = ltc2992_power_config,
+};
+
+static const struct hwmon_channel_info *ltc2992_info[] = {
+ &ltc2992_chip,
+ &ltc2992_in,
+ &ltc2992_curr,
+ &ltc2992_power,
+ 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)
+ return ret;
+
+ if (addr > 1)
+ 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/max127.c b/drivers/hwmon/max127.c
new file mode 100644
index 000000000000..402ffdc2f425
--- /dev/null
+++ b/drivers/hwmon/max127.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for MAX127.
+ *
+ * Copyright (c) 2020 Facebook Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+/*
+ * MAX127 Control Byte. Refer to MAX127 datasheet, Table 1 "Control-Byte
+ * Format" for details.
+ */
+#define MAX127_CTRL_START BIT(7)
+#define MAX127_CTRL_SEL_SHIFT 4
+#define MAX127_CTRL_RNG BIT(3)
+#define MAX127_CTRL_BIP BIT(2)
+#define MAX127_CTRL_PD1 BIT(1)
+#define MAX127_CTRL_PD0 BIT(0)
+
+#define MAX127_NUM_CHANNELS 8
+#define MAX127_SET_CHANNEL(ch) (((ch) & 7) << MAX127_CTRL_SEL_SHIFT)
+
+/*
+ * MAX127 channel input ranges. Refer to MAX127 datasheet, Table 3 "Range
+ * and Polarity Selection" for details.
+ */
+#define MAX127_FULL_RANGE 10000 /* 10V */
+#define MAX127_HALF_RANGE 5000 /* 5V */
+
+/*
+ * MAX127 returns 2 bytes at read:
+ * - the first byte contains data[11:4].
+ * - the second byte contains data[3:0] (MSB) and 4 dummy 0s (LSB).
+ * Refer to MAX127 datasheet, "Read a Conversion (Read Cycle)" section
+ * for details.
+ */
+#define MAX127_DATA_LEN 2
+#define MAX127_DATA_SHIFT 4
+
+#define MAX127_SIGN_BIT BIT(11)
+
+struct max127_data {
+ struct mutex lock;
+ struct i2c_client *client;
+ u8 ctrl_byte[MAX127_NUM_CHANNELS];
+};
+
+static int max127_select_channel(struct i2c_client *client, u8 ctrl_byte)
+{
+ int status;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = sizeof(ctrl_byte),
+ .buf = &ctrl_byte,
+ };
+
+ status = i2c_transfer(client->adapter, &msg, 1);
+ if (status < 0)
+ return status;
+ if (status != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int max127_read_channel(struct i2c_client *client, long *val)
+{
+ int status;
+ u8 i2c_data[MAX127_DATA_LEN];
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(i2c_data),
+ .buf = i2c_data,
+ };
+
+ status = i2c_transfer(client->adapter, &msg, 1);
+ if (status < 0)
+ return status;
+ if (status != 1)
+ return -EIO;
+
+ *val = (i2c_data[1] >> MAX127_DATA_SHIFT) |
+ ((u16)i2c_data[0] << MAX127_DATA_SHIFT);
+ return 0;
+}
+
+static long max127_process_raw(u8 ctrl_byte, long raw)
+{
+ long scale, weight;
+
+ /*
+ * MAX127's data coding is binary in unipolar mode with 1 LSB =
+ * (Full-Scale/4096) and two’s complement binary in bipolar mode
+ * with 1 LSB = [(2 x |FS|)/4096].
+ * Refer to MAX127 datasheet, "Transfer Function" section for
+ * details.
+ */
+ scale = (ctrl_byte & MAX127_CTRL_RNG) ? MAX127_FULL_RANGE :
+ MAX127_HALF_RANGE;
+ if (ctrl_byte & MAX127_CTRL_BIP) {
+ weight = (raw & MAX127_SIGN_BIT);
+ raw &= ~MAX127_SIGN_BIT;
+ raw -= weight;
+ raw *= 2;
+ }
+
+ return raw * scale / 4096;
+}
+
+static int max127_read_input(struct max127_data *data, int channel, long *val)
+{
+ long raw;
+ int status;
+ struct i2c_client *client = data->client;
+ u8 ctrl_byte = data->ctrl_byte[channel];
+
+ mutex_lock(&data->lock);
+
+ status = max127_select_channel(client, ctrl_byte);
+ if (status)
+ goto exit;
+
+ status = max127_read_channel(client, &raw);
+ if (status)
+ goto exit;
+
+ *val = max127_process_raw(ctrl_byte, raw);
+
+exit:
+ mutex_unlock(&data->lock);
+ return status;
+}
+
+static int max127_read_min(struct max127_data *data, int channel, long *val)
+{
+ u8 rng_bip = (data->ctrl_byte[channel] >> 2) & 3;
+ static const int min_input_map[4] = {
+ 0, /* RNG=0, BIP=0 */
+ -MAX127_HALF_RANGE, /* RNG=0, BIP=1 */
+ 0, /* RNG=1, BIP=0 */
+ -MAX127_FULL_RANGE, /* RNG=1, BIP=1 */
+ };
+
+ *val = min_input_map[rng_bip];
+ return 0;
+}
+
+static int max127_read_max(struct max127_data *data, int channel, long *val)
+{
+ u8 rng_bip = (data->ctrl_byte[channel] >> 2) & 3;
+ static const int max_input_map[4] = {
+ MAX127_HALF_RANGE, /* RNG=0, BIP=0 */
+ MAX127_HALF_RANGE, /* RNG=0, BIP=1 */
+ MAX127_FULL_RANGE, /* RNG=1, BIP=0 */
+ MAX127_FULL_RANGE, /* RNG=1, BIP=1 */
+ };
+
+ *val = max_input_map[rng_bip];
+ return 0;
+}
+
+static int max127_write_min(struct max127_data *data, int channel, long val)
+{
+ u8 ctrl;
+
+ mutex_lock(&data->lock);
+
+ ctrl = data->ctrl_byte[channel];
+ if (val <= -MAX127_FULL_RANGE) {
+ ctrl |= (MAX127_CTRL_RNG | MAX127_CTRL_BIP);
+ } else if (val < 0) {
+ ctrl |= MAX127_CTRL_BIP;
+ ctrl &= ~MAX127_CTRL_RNG;
+ } else {
+ ctrl &= ~MAX127_CTRL_BIP;
+ }
+ data->ctrl_byte[channel] = ctrl;
+
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static int max127_write_max(struct max127_data *data, int channel, long val)
+{
+ mutex_lock(&data->lock);
+
+ if (val >= MAX127_FULL_RANGE)
+ data->ctrl_byte[channel] |= MAX127_CTRL_RNG;
+ else
+ data->ctrl_byte[channel] &= ~MAX127_CTRL_RNG;
+
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static umode_t max127_is_visible(const void *_data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type == hwmon_in) {
+ switch (attr) {
+ case hwmon_in_input:
+ return 0444;
+
+ case hwmon_in_min:
+ case hwmon_in_max:
+ return 0644;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int max127_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ int status;
+ struct max127_data *data = dev_get_drvdata(dev);
+
+ if (type != hwmon_in)
+ return -EOPNOTSUPP;
+
+ switch (attr) {
+ case hwmon_in_input:
+ status = max127_read_input(data, channel, val);
+ break;
+
+ case hwmon_in_min:
+ status = max127_read_min(data, channel, val);
+ break;
+
+ case hwmon_in_max:
+ status = max127_read_max(data, channel, val);
+ break;
+
+ default:
+ status = -EOPNOTSUPP;
+ break;
+ }
+
+ return status;
+}
+
+static int max127_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ int status;
+ struct max127_data *data = dev_get_drvdata(dev);
+
+ if (type != hwmon_in)
+ return -EOPNOTSUPP;
+
+ switch (attr) {
+ case hwmon_in_min:
+ status = max127_write_min(data, channel, val);
+ break;
+
+ case hwmon_in_max:
+ status = max127_write_max(data, channel, val);
+ break;
+
+ default:
+ status = -EOPNOTSUPP;
+ break;
+ }
+
+ return status;
+}
+
+static const struct hwmon_ops max127_hwmon_ops = {
+ .is_visible = max127_is_visible,
+ .read = max127_read,
+ .write = max127_write,
+};
+
+static const struct hwmon_channel_info *max127_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX),
+ NULL,
+};
+
+static const struct hwmon_chip_info max127_chip_info = {
+ .ops = &max127_hwmon_ops,
+ .info = max127_info,
+};
+
+static int max127_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i;
+ struct device *hwmon_dev;
+ struct max127_data *data;
+ struct device *dev = &client->dev;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+ for (i = 0; i < ARRAY_SIZE(data->ctrl_byte); i++)
+ data->ctrl_byte[i] = (MAX127_CTRL_START |
+ MAX127_SET_CHANNEL(i));
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &max127_chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max127_id[] = {
+ { "max127", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max127_id);
+
+static struct i2c_driver max127_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max127",
+ },
+ .probe = max127_probe,
+ .id_table = max127_id,
+};
+
+module_i2c_driver(max127_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Choi <mikechoi@fb.com>");
+MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>");
+MODULE_DESCRIPTION("MAX127 Hardware Monitoring driver");
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index 2d299149f4d2..7f7e30f0de7b 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* nct6683 - Driver for the hardware monitoring functionality of
- * Nuvoton NCT6683D eSIO
+ * Nuvoton NCT6683D/NCT6687D eSIO
*
* Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net>
*
@@ -12,6 +12,7 @@
*
* Chip #vin #fan #pwm #temp chip ID
* nct6683d 21(1) 16 8 32(1) 0xc730
+ * nct6687d 21(1) 16 8 32(1) 0xd590
*
* Notes:
* (1) Total number of vin and temp inputs is 32.
@@ -32,7 +33,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
-enum kinds { nct6683 };
+enum kinds { nct6683, nct6687 };
static bool force;
module_param(force, bool, 0);
@@ -40,10 +41,12 @@ MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors");
static const char * const nct6683_device_names[] = {
"nct6683",
+ "nct6687",
};
static const char * const nct6683_chip_names[] = {
"NCT6683D",
+ "NCT6687D",
};
#define DRVNAME "nct6683"
@@ -63,6 +66,7 @@ static const char * const nct6683_chip_names[] = {
#define SIO_NCT6681_ID 0xb270 /* for later */
#define SIO_NCT6683_ID 0xc730
+#define SIO_NCT6687_ID 0xd590
#define SIO_ID_MASK 0xFFF0
static inline void
@@ -164,6 +168,7 @@ superio_exit(int ioreg)
#define NCT6683_REG_CUSTOMER_ID 0x602
#define NCT6683_CUSTOMER_ID_INTEL 0x805
#define NCT6683_CUSTOMER_ID_MITAC 0xa0e
+#define NCT6683_CUSTOMER_ID_MSI 0x201
#define NCT6683_REG_BUILD_YEAR 0x604
#define NCT6683_REG_BUILD_MONTH 0x605
@@ -1218,6 +1223,8 @@ static int nct6683_probe(struct platform_device *pdev)
break;
case NCT6683_CUSTOMER_ID_MITAC:
break;
+ case NCT6683_CUSTOMER_ID_MSI:
+ break;
default:
if (!force)
return -ENODEV;
@@ -1352,6 +1359,9 @@ static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
case SIO_NCT6683_ID:
sio_data->kind = nct6683;
break;
+ case SIO_NCT6687_ID:
+ sio_data->kind = nct6687;
+ break;
default:
if (val != 0xffff)
pr_debug("unsupported chip ID: 0x%04x\n", val);
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index a71777990d49..7a5e539b567b 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -41,6 +41,14 @@ struct temp_sensor_2 {
u8 value;
} __packed;
+struct temp_sensor_10 {
+ u32 sensor_id;
+ u8 fru_type;
+ u8 value;
+ u8 throttle;
+ u8 reserved;
+} __packed;
+
struct freq_sensor_1 {
u16 sensor_id;
u16 value;
@@ -307,6 +315,60 @@ static ssize_t occ_show_temp_2(struct device *dev,
return snprintf(buf, PAGE_SIZE - 1, "%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;
+
+ /*
+ * 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;
+ case 4:
+ val = temp->throttle * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
static ssize_t occ_show_freq_1(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -745,6 +807,10 @@ static int occ_setup_sensor_attrs(struct occ *occ)
num_attrs += (sensors->temp.num_sensors * 4);
show_temp = occ_show_temp_2;
break;
+ case 0x10:
+ num_attrs += (sensors->temp.num_sensors * 5);
+ show_temp = occ_show_temp_10;
+ break;
default:
sensors->temp.num_sensors = 0;
}
@@ -844,6 +910,15 @@ static int occ_setup_sensor_attrs(struct occ *occ)
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
show_temp, NULL, 3, i);
attr++;
+
+ if (sensors->temp.version == 0x10) {
+ snprintf(attr->name, sizeof(attr->name),
+ "temp%d_max", s);
+ attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+ show_temp, NULL,
+ 4, i);
+ attr++;
+ }
}
}
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a25faf69fce3..03606d4298a4 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -220,6 +220,15 @@ config SENSORS_MP2975
This driver can also be built as a module. If so, the module will
be called mp2975.
+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
@@ -229,6 +238,15 @@ config SENSORS_PXE1610
This driver can also be built as a module. If so, the module will
be called pxe1610.
+config SENSORS_Q54SJ108A2
+ tristate "Delta Power Supplies Q54SJ108A2"
+ help
+ If you say yes here you get hardware monitoring support for Delta
+ Q54SJ108A2 series Power Supplies.
+
+ This driver can also be built as a module. If so, the module will
+ be called q54sj108a2.
+
config SENSORS_TPS40422
tristate "TI TPS40422"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 4c97ad0bd791..6a4ba0fdc1db 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -25,7 +25,9 @@ obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
+obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o
+obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index c7b373ba92f2..4d2e4ddcfbfd 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -502,7 +502,6 @@ static struct i2c_driver adm1266_driver = {
.of_match_table = adm1266_of_match,
},
.probe_new = adm1266_probe,
- .remove = pmbus_do_remove,
.id_table = adm1266_id,
};
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index e7997f37b266..38a6515b0763 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -797,7 +797,6 @@ static struct i2c_driver adm1275_driver = {
.name = "adm1275",
},
.probe_new = adm1275_probe,
- .remove = pmbus_do_remove,
.id_table = adm1275_id,
};
diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c
index 2c5b853d6c7f..aed7542d7ce5 100644
--- a/drivers/hwmon/pmbus/bel-pfe.c
+++ b/drivers/hwmon/pmbus/bel-pfe.c
@@ -121,7 +121,6 @@ static struct i2c_driver pfe_pmbus_driver = {
.name = "bel-pfe",
},
.probe_new = pfe_pmbus_probe,
- .remove = pmbus_do_remove,
.id_table = pfe_device_id,
};
diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 2fb7540ee952..d6bbbb223871 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -617,7 +617,6 @@ static struct i2c_driver ibm_cffps_driver = {
.of_match_table = ibm_cffps_of_match,
},
.probe_new = ibm_cffps_probe,
- .remove = pmbus_do_remove,
.id_table = ibm_cffps_id,
};
diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c
index be493182174d..88c5865c4d6f 100644
--- a/drivers/hwmon/pmbus/inspur-ipsps.c
+++ b/drivers/hwmon/pmbus/inspur-ipsps.c
@@ -216,7 +216,6 @@ static struct i2c_driver ipsps_driver = {
.of_match_table = of_match_ptr(ipsps_of_match),
},
.probe_new = ipsps_probe,
- .remove = pmbus_do_remove,
.id_table = ipsps_id,
};
diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c
index 5fadb1def49f..3aebeb1443fd 100644
--- a/drivers/hwmon/pmbus/ir35221.c
+++ b/drivers/hwmon/pmbus/ir35221.c
@@ -137,7 +137,6 @@ static struct i2c_driver ir35221_driver = {
.name = "ir35221",
},
.probe_new = ir35221_probe,
- .remove = pmbus_do_remove,
.id_table = ir35221_id,
};
diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c
index 9ac563ce7dd8..46f17c4b4873 100644
--- a/drivers/hwmon/pmbus/ir38064.c
+++ b/drivers/hwmon/pmbus/ir38064.c
@@ -53,7 +53,6 @@ static struct i2c_driver ir38064_driver = {
.name = "ir38064",
},
.probe_new = ir38064_probe,
- .remove = pmbus_do_remove,
.id_table = ir38064_id,
};
diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c
index 44aeafcbd56c..93ef6d64a33a 100644
--- a/drivers/hwmon/pmbus/irps5401.c
+++ b/drivers/hwmon/pmbus/irps5401.c
@@ -55,7 +55,6 @@ static struct i2c_driver irps5401_driver = {
.name = "irps5401",
},
.probe_new = irps5401_probe,
- .remove = pmbus_do_remove,
.id_table = irps5401_id,
};
diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c
index 7cad76e07f70..2bee930d3900 100644
--- a/drivers/hwmon/pmbus/isl68137.c
+++ b/drivers/hwmon/pmbus/isl68137.c
@@ -324,7 +324,6 @@ static struct i2c_driver isl68137_driver = {
.name = "isl68137",
},
.probe_new = isl68137_probe,
- .remove = pmbus_do_remove,
.id_table = raa_dmpvr_id,
};
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index 429172a42902..c75a6bf39641 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -508,7 +508,6 @@ static struct i2c_driver lm25066_driver = {
.name = "lm25066",
},
.probe_new = lm25066_probe,
- .remove = pmbus_do_remove,
.id_table = lm25066_id,
};
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 9a024cf70145..7e53fa95b92d 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -875,7 +875,6 @@ static struct i2c_driver ltc2978_driver = {
.of_match_table = of_match_ptr(ltc2978_of_match),
},
.probe_new = ltc2978_probe,
- .remove = pmbus_do_remove,
.id_table = ltc2978_id,
};
diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c
index 8328fb367ad6..e45e14d26c9a 100644
--- a/drivers/hwmon/pmbus/ltc3815.c
+++ b/drivers/hwmon/pmbus/ltc3815.c
@@ -200,7 +200,6 @@ static struct i2c_driver ltc3815_driver = {
.name = "ltc3815",
},
.probe_new = ltc3815_probe,
- .remove = pmbus_do_remove,
.id_table = ltc3815_id,
};
diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c
index 26e7f5ef9d7f..d79add99083e 100644
--- a/drivers/hwmon/pmbus/max16064.c
+++ b/drivers/hwmon/pmbus/max16064.c
@@ -103,7 +103,6 @@ static struct i2c_driver max16064_driver = {
.name = "max16064",
},
.probe_new = max16064_probe,
- .remove = pmbus_do_remove,
.id_table = max16064_id,
};
diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c
index 71bb74e27a5c..a960b86e72d2 100644
--- a/drivers/hwmon/pmbus/max16601.c
+++ b/drivers/hwmon/pmbus/max16601.c
@@ -302,7 +302,6 @@ static struct i2c_driver max16601_driver = {
.name = "max16601",
},
.probe_new = max16601_probe,
- .remove = pmbus_do_remove,
.id_table = max16601_id,
};
diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c
index be83b98411c7..9dd3dd79bc18 100644
--- a/drivers/hwmon/pmbus/max20730.c
+++ b/drivers/hwmon/pmbus/max20730.c
@@ -328,8 +328,6 @@ static int max20730_init_debugfs(struct i2c_client *client,
return -ENOENT;
max20730_dir = debugfs_create_dir(client->name, debugfs);
- if (!max20730_dir)
- return -ENOENT;
for (i = 0; i < MAX20730_DEBUGFS_NUM_ENTRIES; ++i)
psu->debugfs_entries[i] = i;
@@ -779,7 +777,6 @@ static struct i2c_driver max20730_driver = {
.of_match_table = max20730_of_match,
},
.probe_new = max20730_probe,
- .remove = pmbus_do_remove,
.id_table = max20730_id,
};
diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c
index 921e92d82aec..9d42f82fdd99 100644
--- a/drivers/hwmon/pmbus/max20751.c
+++ b/drivers/hwmon/pmbus/max20751.c
@@ -43,7 +43,6 @@ static struct i2c_driver max20751_driver = {
.name = "max20751",
},
.probe_new = max20751_probe,
- .remove = pmbus_do_remove,
.id_table = max20751_id,
};
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
index 839b957bc03e..e5a9f4019cd5 100644
--- a/drivers/hwmon/pmbus/max31785.c
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -390,7 +390,6 @@ static struct i2c_driver max31785_driver = {
.of_match_table = max31785_of_match,
},
.probe_new = max31785_probe,
- .remove = pmbus_do_remove,
.id_table = max31785_id,
};
diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c
index f4cb196aaaf3..dad66b3c0116 100644
--- a/drivers/hwmon/pmbus/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -521,7 +521,6 @@ static struct i2c_driver max34440_driver = {
.name = "max34440",
},
.probe_new = max34440_probe,
- .remove = pmbus_do_remove,
.id_table = max34440_id,
};
diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c
index 4b2239a6afd3..329dc851fc59 100644
--- a/drivers/hwmon/pmbus/max8688.c
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -183,7 +183,6 @@ static struct i2c_driver max8688_driver = {
.name = "max8688",
},
.probe_new = max8688_probe,
- .remove = pmbus_do_remove,
.id_table = max8688_id,
};
diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c
index 1c3e2a9453b1..60fbdb371332 100644
--- a/drivers/hwmon/pmbus/mp2975.c
+++ b/drivers/hwmon/pmbus/mp2975.c
@@ -758,7 +758,6 @@ static struct i2c_driver mp2975_driver = {
.of_match_table = of_match_ptr(mp2975_of_match),
},
.probe_new = mp2975_probe,
- .remove = pmbus_do_remove,
.id_table = mp2975_id,
};
diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c
new file mode 100644
index 000000000000..d97cb6d6c87f
--- /dev/null
+++ b/drivers/hwmon/pmbus/pm6764tr.c
@@ -0,0 +1,75 @@
+// 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");
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index 20f1af9165c2..a1b4260e75b2 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -238,7 +238,6 @@ static struct i2c_driver pmbus_driver = {
.name = "pmbus",
},
.probe_new = pmbus_probe,
- .remove = pmbus_do_remove,
.id_table = pmbus_id,
};
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 88a5df2633fb..4c30ec89f5bf 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -490,7 +490,6 @@ 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);
-int pmbus_do_remove(struct i2c_client *client);
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client
*client);
int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index b0e2820a2d57..192442b3b7a2 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -2395,6 +2395,13 @@ static int pmbus_debugfs_set_pec(void *data, u64 val)
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec,
pmbus_debugfs_set_pec, "%llu\n");
+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)
{
@@ -2530,7 +2537,8 @@ static int pmbus_init_debugfs(struct i2c_client *client,
}
}
- return 0;
+ return devm_add_action_or_reset(data->dev,
+ pmbus_remove_debugfs, data->debugfs);
}
#else
static int pmbus_init_debugfs(struct i2c_client *client,
@@ -2617,16 +2625,6 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
}
EXPORT_SYMBOL_GPL(pmbus_do_probe);
-int pmbus_do_remove(struct i2c_client *client)
-{
- struct pmbus_data *data = i2c_get_clientdata(client);
-
- debugfs_remove_recursive(data->debugfs);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pmbus_do_remove);
-
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c
index fa5c5dd29b7a..da27ce34ee3f 100644
--- a/drivers/hwmon/pmbus/pxe1610.c
+++ b/drivers/hwmon/pmbus/pxe1610.c
@@ -131,7 +131,6 @@ static struct i2c_driver pxe1610_driver = {
.name = "pxe1610",
},
.probe_new = pxe1610_probe,
- .remove = pmbus_do_remove,
.id_table = pxe1610_id,
};
diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c
new file mode 100644
index 000000000000..aec512766c31
--- /dev/null
+++ b/drivers/hwmon/pmbus/q54sj108a2.c
@@ -0,0 +1,422 @@
+// 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 != 5 || 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");
diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c
index edbdfa809d51..f7f00ab6f46c 100644
--- a/drivers/hwmon/pmbus/tps40422.c
+++ b/drivers/hwmon/pmbus/tps40422.c
@@ -43,7 +43,6 @@ static struct i2c_driver tps40422_driver = {
.name = "tps40422",
},
.probe_new = tps40422_probe,
- .remove = pmbus_do_remove,
.id_table = tps40422_id,
};
diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c
index db2bdf2a1f02..ba838fa311c3 100644
--- a/drivers/hwmon/pmbus/tps53679.c
+++ b/drivers/hwmon/pmbus/tps53679.c
@@ -251,7 +251,6 @@ static struct i2c_driver tps53679_driver = {
.of_match_table = of_match_ptr(tps53679_of_match),
},
.probe_new = tps53679_probe,
- .remove = pmbus_do_remove,
.id_table = tps53679_id,
};
diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index f8017993e2b4..a15e6fe3e425 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -621,7 +621,6 @@ static struct i2c_driver ucd9000_driver = {
.of_match_table = of_match_ptr(ucd9000_of_match),
},
.probe_new = ucd9000_probe,
- .remove = pmbus_do_remove,
.id_table = ucd9000_id,
};
diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c
index e111e25e1619..47cc7ca9d329 100644
--- a/drivers/hwmon/pmbus/ucd9200.c
+++ b/drivers/hwmon/pmbus/ucd9200.c
@@ -201,7 +201,6 @@ static struct i2c_driver ucd9200_driver = {
.of_match_table = of_match_ptr(ucd9200_of_match),
},
.probe_new = ucd9200_probe,
- .remove = pmbus_do_remove,
.id_table = ucd9200_id,
};
diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c
index c95ac934fde4..f8bc0f41cd5f 100644
--- a/drivers/hwmon/pmbus/xdpe12284.c
+++ b/drivers/hwmon/pmbus/xdpe12284.c
@@ -160,7 +160,6 @@ static struct i2c_driver xdpe122_driver = {
.of_match_table = of_match_ptr(xdpe122_of_match),
},
.probe_new = xdpe122_probe,
- .remove = pmbus_do_remove,
.id_table = xdpe122_id,
};
diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c
index e8bda340482b..69120ca7aaa8 100644
--- a/drivers/hwmon/pmbus/zl6100.c
+++ b/drivers/hwmon/pmbus/zl6100.c
@@ -396,7 +396,6 @@ static struct i2c_driver zl6100_driver = {
.name = "zl6100",
},
.probe_new = zl6100_probe,
- .remove = pmbus_do_remove,
.id_table = zl6100_id,
};
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 1f63807c0399..777439f48c14 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -8,7 +8,6 @@
*/
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -39,6 +38,28 @@ struct pwm_fan_ctx {
unsigned int pwm_fan_max_state;
unsigned int *pwm_fan_cooling_levels;
struct thermal_cooling_device *cdev;
+
+ struct hwmon_chip_info info;
+};
+
+static const u32 pwm_fan_channel_config_pwm[] = {
+ HWMON_PWM_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info pwm_fan_channel_pwm = {
+ .type = hwmon_pwm,
+ .config = pwm_fan_channel_config_pwm,
+};
+
+static const u32 pwm_fan_channel_config_fan[] = {
+ HWMON_F_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info pwm_fan_channel_fan = {
+ .type = hwmon_fan,
+ .config = pwm_fan_channel_config_fan,
};
/* This handler assumes self resetting edge triggered interrupt. */
@@ -103,70 +124,62 @@ static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
ctx->pwm_fan_state = i;
}
-static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int pwm_fan_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
- unsigned long pwm;
int ret;
- if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
+ if (val < 0 || val > MAX_PWM)
return -EINVAL;
- ret = __set_pwm(ctx, pwm);
+ ret = __set_pwm(ctx, val);
if (ret)
return ret;
- pwm_fan_update_state(ctx, pwm);
- return count;
+ pwm_fan_update_state(ctx, val);
+ return 0;
}
-static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+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);
- return sprintf(buf, "%u\n", ctx->pwm_value);
-}
+ switch (type) {
+ case hwmon_pwm:
+ *val = ctx->pwm_value;
+ return 0;
-static ssize_t rpm_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ case hwmon_fan:
+ *val = ctx->rpm;
+ return 0;
- return sprintf(buf, "%u\n", ctx->rpm);
+ default:
+ return -ENOTSUPP;
+ }
}
-static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
-static SENSOR_DEVICE_ATTR_RO(fan1_input, rpm, 0);
-
-static struct attribute *pwm_fan_attrs[] = {
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- NULL,
-};
-
-static umode_t pwm_fan_attrs_visible(struct kobject *kobj, struct attribute *a,
- int n)
+static umode_t pwm_fan_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ switch (type) {
+ case hwmon_pwm:
+ return 0644;
- /* Hide fan_input in case no interrupt is available */
- if (n == 1 && ctx->irq <= 0)
- return 0;
+ case hwmon_fan:
+ return 0444;
- return a->mode;
+ default:
+ return 0;
+ }
}
-static const struct attribute_group pwm_fan_group = {
- .attrs = pwm_fan_attrs,
- .is_visible = pwm_fan_attrs_visible,
-};
-
-static const struct attribute_group *pwm_fan_groups[] = {
- &pwm_fan_group,
- NULL,
+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 */
@@ -286,7 +299,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
struct device *hwmon;
int ret;
struct pwm_state state = { };
- u32 ppr = 2;
+ int tach_count;
+ const struct hwmon_channel_info **channels;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -300,10 +314,6 @@ static int pwm_fan_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ctx);
- ctx->irq = platform_get_irq_optional(pdev, 0);
- if (ctx->irq == -EPROBE_DEFER)
- return ctx->irq;
-
ctx->reg_en = devm_regulator_get_optional(dev, "fan");
if (IS_ERR(ctx->reg_en)) {
if (PTR_ERR(ctx->reg_en) != -ENODEV)
@@ -339,26 +349,58 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (ret)
return ret;
- of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr);
- ctx->pulses_per_revolution = ppr;
- if (!ctx->pulses_per_revolution) {
- dev_err(dev, "pulses-per-revolution can't be zero.\n");
- return -EINVAL;
- }
+ tach_count = platform_irq_count(pdev);
+ if (tach_count < 0)
+ return dev_err_probe(dev, tach_count,
+ "Could not get number of fan tachometer inputs\n");
- if (ctx->irq > 0) {
- ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
- pdev->name, ctx);
- if (ret) {
- dev_err(dev, "Failed to request interrupt: %d\n", ret);
- return ret;
+ channels = devm_kcalloc(dev, tach_count + 2,
+ sizeof(struct hwmon_channel_info *), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ channels[0] = &pwm_fan_channel_pwm;
+
+ if (tach_count > 0) {
+ u32 ppr = 2;
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq == -EPROBE_DEFER)
+ return ctx->irq;
+ if (ctx->irq > 0) {
+ ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
+ pdev->name, ctx);
+ if (ret) {
+ dev_err(dev,
+ "Failed to request interrupt: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ of_property_read_u32(dev->of_node,
+ "pulses-per-revolution",
+ &ppr);
+ ctx->pulses_per_revolution = ppr;
+ if (!ctx->pulses_per_revolution) {
+ dev_err(dev, "pulses-per-revolution can't be zero.\n");
+ return -EINVAL;
}
+
+ dev_dbg(dev, "tach: irq=%d, pulses_per_revolution=%d\n",
+ ctx->irq, ctx->pulses_per_revolution);
+
ctx->sample_start = ktime_get();
mod_timer(&ctx->rpm_timer, jiffies + HZ);
+
+ channels[1] = &pwm_fan_channel_fan;
}
- hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan",
- ctx, pwm_fan_groups);
+ ctx->info.ops = &pwm_fan_hwmon_ops;
+ ctx->info.info = channels;
+
+ hwmon = devm_hwmon_device_register_with_info(dev, "pwmfan",
+ ctx, &ctx->info, NULL);
if (IS_ERR(hwmon)) {
dev_err(dev, "Failed to register hwmon device\n");
return PTR_ERR(hwmon);
diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c
new file mode 100644
index 000000000000..e35357c48b8e
--- /dev/null
+++ b/drivers/hwmon/sbtsi_temp.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * sbtsi_temp.c - hwmon driver for a SBI Temperature Sensor Interface (SB-TSI)
+ * compliant AMD SoC temperature device.
+ *
+ * Copyright (c) 2020, Google Inc.
+ * Copyright (c) 2020, Kun Yi <kunyi@google.com>
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+
+/*
+ * SB-TSI registers only support SMBus byte data access. "_INT" registers are
+ * the integer part of a temperature value or limit, and "_DEC" registers are
+ * corresponding decimal parts.
+ */
+#define SBTSI_REG_TEMP_INT 0x01 /* RO */
+#define SBTSI_REG_STATUS 0x02 /* RO */
+#define SBTSI_REG_CONFIG 0x03 /* RO */
+#define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */
+#define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */
+#define SBTSI_REG_TEMP_DEC 0x10 /* RW */
+#define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */
+#define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */
+
+#define SBTSI_CONFIG_READ_ORDER_SHIFT 5
+
+#define SBTSI_TEMP_MIN 0
+#define SBTSI_TEMP_MAX 255875
+
+/* Each client has this additional data */
+struct sbtsi_data {
+ struct i2c_client *client;
+ struct mutex lock;
+};
+
+/*
+ * From SB-TSI spec: CPU temperature readings and limit registers encode the
+ * temperature in increments of 0.125 from 0 to 255.875. The "high byte"
+ * register encodes the base-2 of the integer portion, and the upper 3 bits of
+ * the "low byte" encode in base-2 the decimal portion.
+ *
+ * e.g. INT=0x19, DEC=0x20 represents 25.125 degrees Celsius
+ *
+ * Therefore temperature in millidegree Celsius =
+ * (INT + DEC / 256) * 1000 = (INT * 8 + DEC / 32) * 125
+ */
+static inline int sbtsi_reg_to_mc(s32 integer, s32 decimal)
+{
+ return ((integer << 3) + (decimal >> 5)) * 125;
+}
+
+/*
+ * Inversely, given temperature in millidegree Celsius
+ * INT = (TEMP / 125) / 8
+ * DEC = ((TEMP / 125) % 8) * 32
+ * Caller have to make sure temp doesn't exceed 255875, the max valid value.
+ */
+static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal)
+{
+ temp /= 125;
+ *integer = temp >> 3;
+ *decimal = (temp & 0x7) << 5;
+}
+
+static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct sbtsi_data *data = dev_get_drvdata(dev);
+ s32 temp_int, temp_dec;
+ int err;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ /*
+ * ReadOrder bit specifies the reading order of integer and
+ * decimal part of CPU temp for atomic reads. If bit == 0,
+ * reading integer part triggers latching of the decimal part,
+ * so integer part should be read first. If bit == 1, read
+ * order should be reversed.
+ */
+ err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->lock);
+ if (err & BIT(SBTSI_CONFIG_READ_ORDER_SHIFT)) {
+ temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
+ temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
+ } else {
+ temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
+ temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
+ }
+ mutex_unlock(&data->lock);
+ break;
+ case hwmon_temp_max:
+ mutex_lock(&data->lock);
+ temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT);
+ temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC);
+ mutex_unlock(&data->lock);
+ break;
+ case hwmon_temp_min:
+ mutex_lock(&data->lock);
+ temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT);
+ temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC);
+ mutex_unlock(&data->lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ if (temp_int < 0)
+ return temp_int;
+ if (temp_dec < 0)
+ return temp_dec;
+
+ *val = sbtsi_reg_to_mc(temp_int, temp_dec);
+
+ return 0;
+}
+
+static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct sbtsi_data *data = dev_get_drvdata(dev);
+ int reg_int, reg_dec, err;
+ u8 temp_int, temp_dec;
+
+ switch (attr) {
+ case hwmon_temp_max:
+ reg_int = SBTSI_REG_TEMP_HIGH_INT;
+ reg_dec = SBTSI_REG_TEMP_HIGH_DEC;
+ break;
+ case hwmon_temp_min:
+ reg_int = SBTSI_REG_TEMP_LOW_INT;
+ reg_dec = SBTSI_REG_TEMP_LOW_DEC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX);
+ sbtsi_mc_to_reg(val, &temp_int, &temp_dec);
+
+ mutex_lock(&data->lock);
+ err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int);
+ if (err)
+ goto exit;
+
+ err = i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec);
+exit:
+ mutex_unlock(&data->lock);
+ return err;
+}
+
+static umode_t sbtsi_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ case hwmon_temp_min:
+ return 0644;
+ case hwmon_temp_max:
+ return 0644;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct hwmon_channel_info *sbtsi_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX),
+ NULL
+};
+
+static const struct hwmon_ops sbtsi_hwmon_ops = {
+ .is_visible = sbtsi_is_visible,
+ .read = sbtsi_read,
+ .write = sbtsi_write,
+};
+
+static const struct hwmon_chip_info sbtsi_chip_info = {
+ .ops = &sbtsi_hwmon_ops,
+ .info = sbtsi_info,
+};
+
+static int sbtsi_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct sbtsi_data *data;
+
+ data = devm_kzalloc(dev, sizeof(struct sbtsi_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &sbtsi_chip_info,
+ NULL);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id sbtsi_id[] = {
+ {"sbtsi", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sbtsi_id);
+
+static const struct of_device_id __maybe_unused sbtsi_of_match[] = {
+ {
+ .compatible = "amd,sbtsi",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sbtsi_of_match);
+
+static struct i2c_driver sbtsi_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "sbtsi",
+ .of_match_table = of_match_ptr(sbtsi_of_match),
+ },
+ .probe = sbtsi_probe,
+ .id_table = sbtsi_id,
+};
+
+module_i2c_driver(sbtsi_driver);
+
+MODULE_AUTHOR("Kun Yi <kunyi@google.com>");
+MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c
index f2a5af239c95..1489e83cb0c4 100644
--- a/drivers/hwmon/xgene-hwmon.c
+++ b/drivers/hwmon/xgene-hwmon.c
@@ -784,7 +784,7 @@ static const struct of_device_id xgene_hwmon_of_match[] = {
};
MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
-static struct platform_driver xgene_hwmon_driver __refdata = {
+static struct platform_driver xgene_hwmon_driver = {
.probe = xgene_hwmon_probe,
.remove = xgene_hwmon_remove,
.driver = {