summaryrefslogtreecommitdiff
path: root/drivers/hwmon/pmbus
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/pmbus')
-rw-r--r--drivers/hwmon/pmbus/Kconfig9
-rw-r--r--drivers/hwmon/pmbus/Makefile1
-rw-r--r--drivers/hwmon/pmbus/acbel-fsg032.c85
-rw-r--r--drivers/hwmon/pmbus/fsp-3y.c1
-rw-r--r--drivers/hwmon/pmbus/ibm-cffps.c272
-rw-r--r--drivers/hwmon/pmbus/pmbus.h4
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c395
7 files changed, 519 insertions, 248 deletions
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 59d9a7430499..270b6336b76d 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -27,6 +27,15 @@ config SENSORS_PMBUS
This driver can also be built as a module. If so, the module will
be called pmbus.
+config SENSORS_ACBEL_FSG032
+ tristate "ACBEL FSG032 Power Supply"
+ help
+ If you say yes here you get hardware monitoring support for the ACBEL
+ FSG032 Power Supply.
+
+ This driver can also be built as a module. If so, the module will
+ be called acbel-fsg032.
+
config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
select CRC8
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 3ae019916267..84ee960a6c2d 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_PMBUS) += pmbus_core.o
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
+obj-$(CONFIG_SENSORS_ACBEL_FSG032) += acbel-fsg032.o
obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o
diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c
new file mode 100644
index 000000000000..28a25f30e2cf
--- /dev/null
+++ b/drivers/hwmon/pmbus/acbel-fsg032.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2023 IBM Corp.
+ */
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pmbus.h>
+#include <linux/hwmon-sysfs.h>
+#include "pmbus.h"
+
+static const struct i2c_device_id acbel_fsg032_id[] = {
+ { "acbel_fsg032" },
+ {}
+};
+
+static struct pmbus_driver_info acbel_fsg032_info = {
+ .pages = 1,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
+ PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_FAN12,
+};
+
+static int acbel_fsg032_probe(struct i2c_client *client)
+{
+ u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+ struct device *dev = &client->dev;
+ int rc;
+
+ rc = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
+ if (rc < 0) {
+ dev_err(dev, "Failed to read PMBUS_MFR_ID\n");
+ return rc;
+ }
+ if (strncmp(buf, "ACBEL", 5)) {
+ buf[rc] = '\0';
+ dev_err(dev, "Manufacturer '%s' not supported\n", buf);
+ return -ENODEV;
+ }
+
+ rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+ if (rc < 0) {
+ dev_err(dev, "Failed to read PMBUS_MFR_MODEL\n");
+ return rc;
+ }
+
+ if (strncmp(buf, "FSG032", 6)) {
+ buf[rc] = '\0';
+ dev_err(dev, "Model '%s' not supported\n", buf);
+ return -ENODEV;
+ }
+
+ rc = pmbus_do_probe(client, &acbel_fsg032_info);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static const struct of_device_id acbel_fsg032_of_match[] = {
+ { .compatible = "acbel,fsg032" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, acbel_fsg032_of_match);
+
+static struct i2c_driver acbel_fsg032_driver = {
+ .driver = {
+ .name = "acbel-fsg032",
+ .of_match_table = acbel_fsg032_of_match,
+ },
+ .probe_new = acbel_fsg032_probe,
+ .id_table = acbel_fsg032_id,
+};
+
+module_i2c_driver(acbel_fsg032_driver);
+
+MODULE_AUTHOR("Lakshmi Yadlapati");
+MODULE_DESCRIPTION("PMBus driver for AcBel Power System power supplies");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c
index aec294cc72d1..c7469d2cdedc 100644
--- a/drivers/hwmon/pmbus/fsp-3y.c
+++ b/drivers/hwmon/pmbus/fsp-3y.c
@@ -180,7 +180,6 @@ static struct pmbus_driver_info fsp3y_info[] = {
PMBUS_HAVE_FAN12,
.func[YM2151_PAGE_5VSB_LOG] =
PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT,
- PMBUS_HAVE_IIN,
.read_word_data = fsp3y_read_word_data,
.read_byte_data = fsp3y_read_byte_data,
},
diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index e3294a1a54bb..76e72e9acda7 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -18,12 +18,6 @@
#include "pmbus.h"
-#define CFFPS_MFG_ID_CMD 0x99
-#define CFFPS_FRU_CMD 0x9A
-#define CFFPS_PN_CMD 0x9B
-#define CFFPS_HEADER_CMD 0x9C
-#define CFFPS_SN_CMD 0x9E
-#define CFFPS_MAX_POWER_OUT_CMD 0xA7
#define CFFPS_CCIN_CMD 0xBD
#define CFFPS_FW_CMD 0xFA
#define CFFPS1_FW_NUM_BYTES 4
@@ -32,7 +26,7 @@
#define CFFPS_12VCS_VOUT_CMD 0xDE
#define CFFPS_INPUT_HISTORY_CMD 0xD6
-#define CFFPS_INPUT_HISTORY_SIZE 100
+#define CFFPS_INPUT_HISTORY_SIZE 101
#define CFFPS_CCIN_REVISION GENMASK(7, 0)
#define CFFPS_CCIN_REVISION_LEGACY 0xde
@@ -57,13 +51,7 @@
#define CFFPS_BLINK_RATE_MS 250
enum {
- CFFPS_DEBUGFS_INPUT_HISTORY = 0,
- CFFPS_DEBUGFS_MFG_ID,
- CFFPS_DEBUGFS_FRU,
- CFFPS_DEBUGFS_PN,
- CFFPS_DEBUGFS_HEADER,
- CFFPS_DEBUGFS_SN,
- CFFPS_DEBUGFS_MAX_POWER_OUT,
+ CFFPS_DEBUGFS_MAX_POWER_OUT = 0,
CFFPS_DEBUGFS_CCIN,
CFFPS_DEBUGFS_FW,
CFFPS_DEBUGFS_ON_OFF_CONFIG,
@@ -72,19 +60,11 @@ enum {
enum versions { cffps1, cffps2, cffps_unknown };
-struct ibm_cffps_input_history {
- struct mutex update_lock;
- unsigned long last_update;
-
- u8 byte_count;
- u8 data[CFFPS_INPUT_HISTORY_SIZE];
-};
-
struct ibm_cffps {
enum versions version;
struct i2c_client *client;
- struct ibm_cffps_input_history input_history;
+ u8 input_history[CFFPS_INPUT_HISTORY_SIZE];
int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];
@@ -93,118 +73,98 @@ struct ibm_cffps {
struct led_classdev led;
};
-static const struct i2c_device_id ibm_cffps_id[];
-
#define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
-static ssize_t ibm_cffps_read_input_history(struct ibm_cffps *psu,
- char __user *buf, size_t count,
- loff_t *ppos)
+static ssize_t ibm_cffps_debugfs_read_input_history(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
{
int rc;
- u8 msgbuf0[1] = { CFFPS_INPUT_HISTORY_CMD };
- u8 msgbuf1[CFFPS_INPUT_HISTORY_SIZE + 1] = { 0 };
+ u8 cmd = CFFPS_INPUT_HISTORY_CMD;
+ struct ibm_cffps *psu = file->private_data;
struct i2c_msg msg[2] = {
{
.addr = psu->client->addr,
.flags = psu->client->flags,
.len = 1,
- .buf = msgbuf0,
+ .buf = &cmd,
}, {
.addr = psu->client->addr,
.flags = psu->client->flags | I2C_M_RD,
- .len = CFFPS_INPUT_HISTORY_SIZE + 1,
- .buf = msgbuf1,
+ .len = CFFPS_INPUT_HISTORY_SIZE,
+ .buf = psu->input_history,
},
};
if (!*ppos) {
- mutex_lock(&psu->input_history.update_lock);
- if (time_after(jiffies, psu->input_history.last_update + HZ)) {
- /*
- * Use a raw i2c transfer, since we need more bytes
- * than Linux I2C supports through smbus xfr (only 32).
- */
- rc = i2c_transfer(psu->client->adapter, msg, 2);
- if (rc < 0) {
- mutex_unlock(&psu->input_history.update_lock);
- return rc;
- }
+ rc = pmbus_lock_interruptible(psu->client);
+ if (rc)
+ return rc;
- psu->input_history.byte_count = msgbuf1[0];
- memcpy(psu->input_history.data, &msgbuf1[1],
- CFFPS_INPUT_HISTORY_SIZE);
- psu->input_history.last_update = jiffies;
+ rc = pmbus_set_page(psu->client, 0, 0xff);
+ if (rc) {
+ pmbus_unlock(psu->client);
+ return rc;
}
- mutex_unlock(&psu->input_history.update_lock);
+ /*
+ * Use a raw i2c transfer, since we need more bytes
+ * than Linux I2C supports through smbus xfr (only 32).
+ */
+ rc = i2c_transfer(psu->client->adapter, msg, 2);
+ pmbus_unlock(psu->client);
+ if (rc < 0)
+ return rc;
}
return simple_read_from_buffer(buf, count, ppos,
- psu->input_history.data,
- psu->input_history.byte_count);
+ psu->input_history + 1,
+ psu->input_history[0]);
}
+static const struct file_operations ibm_cffps_input_history_fops = {
+ .llseek = noop_llseek,
+ .read = ibm_cffps_debugfs_read_input_history,
+ .open = simple_open,
+};
+
static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- u8 cmd;
int i, rc;
int *idxp = file->private_data;
int idx = *idxp;
struct ibm_cffps *psu = to_psu(idxp, idx);
char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
- pmbus_set_page(psu->client, 0, 0xff);
+ rc = pmbus_lock_interruptible(psu->client);
+ if (rc)
+ return rc;
+
+ rc = pmbus_set_page(psu->client, 0, 0xff);
+ if (rc)
+ goto unlock;
switch (idx) {
- case CFFPS_DEBUGFS_INPUT_HISTORY:
- return ibm_cffps_read_input_history(psu, buf, count, ppos);
- case CFFPS_DEBUGFS_MFG_ID:
- cmd = CFFPS_MFG_ID_CMD;
- break;
- case CFFPS_DEBUGFS_FRU:
- cmd = CFFPS_FRU_CMD;
- break;
- case CFFPS_DEBUGFS_PN:
- cmd = CFFPS_PN_CMD;
- break;
- case CFFPS_DEBUGFS_HEADER:
- cmd = CFFPS_HEADER_CMD;
- break;
- case CFFPS_DEBUGFS_SN:
- cmd = CFFPS_SN_CMD;
- break;
case CFFPS_DEBUGFS_MAX_POWER_OUT:
- if (psu->version == cffps1) {
- rc = i2c_smbus_read_word_swapped(psu->client,
- CFFPS_MAX_POWER_OUT_CMD);
- } else {
- rc = i2c_smbus_read_word_data(psu->client,
- CFFPS_MAX_POWER_OUT_CMD);
- }
-
- if (rc < 0)
- return rc;
-
- rc = snprintf(data, I2C_SMBUS_BLOCK_MAX, "%d", rc);
- goto done;
+ if (psu->version == cffps1)
+ rc = i2c_smbus_read_word_swapped(psu->client, PMBUS_MFR_POUT_MAX);
+ else
+ rc = i2c_smbus_read_word_data(psu->client, PMBUS_MFR_POUT_MAX);
+ if (rc >= 0)
+ rc = snprintf(data, I2C_SMBUS_BLOCK_MAX, "%d", rc);
+ break;
case CFFPS_DEBUGFS_CCIN:
rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD);
- if (rc < 0)
- return rc;
-
- rc = snprintf(data, 5, "%04X", rc);
- goto done;
+ if (rc >= 0)
+ rc = snprintf(data, 5, "%04X", rc);
+ break;
case CFFPS_DEBUGFS_FW:
switch (psu->version) {
case cffps1:
for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {
- rc = i2c_smbus_read_byte_data(psu->client,
- CFFPS_FW_CMD +
- i);
+ rc = i2c_smbus_read_byte_data(psu->client, CFFPS_FW_CMD + i);
if (rc < 0)
- return rc;
+ goto unlock;
snprintf(&data[i * 2], 3, "%02X", rc);
}
@@ -213,11 +173,9 @@ static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf,
break;
case cffps2:
for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {
- rc = i2c_smbus_read_word_data(psu->client,
- CFFPS_FW_CMD +
- i);
+ rc = i2c_smbus_read_word_data(psu->client, CFFPS_FW_CMD + i);
if (rc < 0)
- return rc;
+ goto unlock;
snprintf(&data[i * 4], 5, "%04X", rc);
}
@@ -225,26 +183,25 @@ static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf,
rc = i * 4;
break;
default:
- return -EOPNOTSUPP;
+ rc = -EOPNOTSUPP;
+ break;
}
- goto done;
+ break;
case CFFPS_DEBUGFS_ON_OFF_CONFIG:
- rc = i2c_smbus_read_byte_data(psu->client,
- PMBUS_ON_OFF_CONFIG);
- if (rc < 0)
- return rc;
-
- rc = snprintf(data, 3, "%02x", rc);
- goto done;
+ rc = i2c_smbus_read_byte_data(psu->client, PMBUS_ON_OFF_CONFIG);
+ if (rc >= 0)
+ rc = snprintf(data, 3, "%02x", rc);
+ break;
default:
- return -EINVAL;
+ rc = -EINVAL;
+ break;
}
- rc = i2c_smbus_read_block_data(psu->client, cmd, data);
+unlock:
+ pmbus_unlock(psu->client);
if (rc < 0)
return rc;
-done:
data[rc] = '\n';
rc += 2;
@@ -263,14 +220,22 @@ static ssize_t ibm_cffps_debugfs_write(struct file *file,
switch (idx) {
case CFFPS_DEBUGFS_ON_OFF_CONFIG:
- pmbus_set_page(psu->client, 0, 0xff);
-
rc = simple_write_to_buffer(&data, 1, ppos, buf, count);
if (rc <= 0)
return rc;
- rc = i2c_smbus_write_byte_data(psu->client,
- PMBUS_ON_OFF_CONFIG, data);
+ rc = pmbus_lock_interruptible(psu->client);
+ if (rc)
+ return rc;
+
+ rc = pmbus_set_page(psu->client, 0, 0xff);
+ if (rc) {
+ pmbus_unlock(psu->client);
+ return rc;
+ }
+
+ rc = i2c_smbus_write_byte_data(psu->client, PMBUS_ON_OFF_CONFIG, data);
+ pmbus_unlock(psu->client);
if (rc)
return rc;
@@ -396,10 +361,19 @@ static int ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
dev_dbg(&psu->client->dev, "LED brightness set: %d. Command: %d.\n",
brightness, next_led_state);
- pmbus_set_page(psu->client, 0, 0xff);
+ rc = pmbus_lock_interruptible(psu->client);
+ if (rc)
+ return rc;
+
+ rc = pmbus_set_page(psu->client, 0, 0xff);
+ if (rc) {
+ pmbus_unlock(psu->client);
+ return rc;
+ }
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
next_led_state);
+ pmbus_unlock(psu->client);
if (rc < 0)
return rc;
@@ -418,10 +392,19 @@ static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
dev_dbg(&psu->client->dev, "LED blink set.\n");
- pmbus_set_page(psu->client, 0, 0xff);
+ rc = pmbus_lock_interruptible(psu->client);
+ if (rc)
+ return rc;
+
+ rc = pmbus_set_page(psu->client, 0, 0xff);
+ if (rc) {
+ pmbus_unlock(psu->client);
+ return rc;
+ }
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
CFFPS_LED_BLINK);
+ pmbus_unlock(psu->client);
if (rc < 0)
return rc;
@@ -474,9 +457,11 @@ static struct pmbus_driver_info ibm_cffps_info[] = {
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_VMON,
- .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
- PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
- PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_PIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
+ PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_STATUS_TEMP,
.read_byte_data = ibm_cffps_read_byte_data,
.read_word_data = ibm_cffps_read_word_data,
},
@@ -486,12 +471,19 @@ static struct pmbus_platform_data ibm_cffps_pdata = {
.flags = PMBUS_SKIP_STATUS_CHECK | PMBUS_NO_CAPABILITY,
};
+static const struct i2c_device_id ibm_cffps_id[] = {
+ { "ibm_cffps1", cffps1 },
+ { "ibm_cffps2", cffps2 },
+ { "ibm_cffps", cffps_unknown },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
+
static int ibm_cffps_probe(struct i2c_client *client)
{
int i, rc;
enum versions vs = cffps_unknown;
struct dentry *debugfs;
- struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
const void *md = of_device_get_match_data(&client->dev);
const struct i2c_device_id *id;
@@ -560,8 +552,6 @@ static int ibm_cffps_probe(struct i2c_client *client)
psu->version = vs;
psu->client = client;
- mutex_init(&psu->input_history.update_lock);
- psu->input_history.last_update = jiffies - HZ;
ibm_cffps_create_led_class(psu);
@@ -570,55 +560,29 @@ static int ibm_cffps_probe(struct i2c_client *client)
if (!debugfs)
return 0;
- ibm_cffps_dir = debugfs_create_dir(client->name, debugfs);
- if (!ibm_cffps_dir)
- return 0;
-
for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)
psu->debugfs_entries[i] = i;
- debugfs_create_file("input_history", 0444, ibm_cffps_dir,
- &psu->debugfs_entries[CFFPS_DEBUGFS_INPUT_HISTORY],
- &ibm_cffps_fops);
- debugfs_create_file("mfg_id", 0444, ibm_cffps_dir,
- &psu->debugfs_entries[CFFPS_DEBUGFS_MFG_ID],
- &ibm_cffps_fops);
- debugfs_create_file("fru", 0444, ibm_cffps_dir,
- &psu->debugfs_entries[CFFPS_DEBUGFS_FRU],
- &ibm_cffps_fops);
- debugfs_create_file("part_number", 0444, ibm_cffps_dir,
- &psu->debugfs_entries[CFFPS_DEBUGFS_PN],
- &ibm_cffps_fops);
- debugfs_create_file("header", 0444, ibm_cffps_dir,
- &psu->debugfs_entries[CFFPS_DEBUGFS_HEADER],
- &ibm_cffps_fops);
- debugfs_create_file("serial_number", 0444, ibm_cffps_dir,
- &psu->debugfs_entries[CFFPS_DEBUGFS_SN],
- &ibm_cffps_fops);
- debugfs_create_file("max_power_out", 0444, ibm_cffps_dir,
+ debugfs_create_file("input_history", 0444, debugfs, psu, &ibm_cffps_input_history_fops);
+ debugfs_create_file("max_power_out", 0444, debugfs,
&psu->debugfs_entries[CFFPS_DEBUGFS_MAX_POWER_OUT],
&ibm_cffps_fops);
- debugfs_create_file("ccin", 0444, ibm_cffps_dir,
+ debugfs_create_file("ccin", 0444, debugfs,
&psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],
&ibm_cffps_fops);
- debugfs_create_file("fw_version", 0444, ibm_cffps_dir,
+ debugfs_create_file("fw_version", 0444, debugfs,
&psu->debugfs_entries[CFFPS_DEBUGFS_FW],
&ibm_cffps_fops);
- debugfs_create_file("on_off_config", 0644, ibm_cffps_dir,
+ debugfs_create_file("on_off_config", 0644, debugfs,
&psu->debugfs_entries[CFFPS_DEBUGFS_ON_OFF_CONFIG],
&ibm_cffps_fops);
+ /* For compatibility with users of the old naming scheme. */
+ debugfs_create_symlink(client->name, debugfs, ".");
+
return 0;
}
-static const struct i2c_device_id ibm_cffps_id[] = {
- { "ibm_cffps1", cffps1 },
- { "ibm_cffps2", cffps2 },
- { "ibm_cffps", cffps_unknown },
- {}
-};
-MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
-
static const struct of_device_id ibm_cffps_of_match[] = {
{
.compatible = "ibm,cffps1",
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 713ea7915425..b0832a4c690d 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -26,7 +26,7 @@ enum pmbus_regs {
PMBUS_CAPABILITY = 0x19,
PMBUS_QUERY = 0x1A,
-
+ PMBUS_SMBALERT_MASK = 0x1B,
PMBUS_VOUT_MODE = 0x20,
PMBUS_VOUT_COMMAND = 0x21,
PMBUS_VOUT_TRIM = 0x22,
@@ -505,6 +505,8 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode);
int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode);
+int pmbus_lock_interruptible(struct i2c_client *client);
+void pmbus_unlock(struct i2c_client *client);
int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 config, u8 mask, u16 command);
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client);
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 95e95783972a..9d14954da94f 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -81,6 +81,7 @@ struct pmbus_label {
struct pmbus_data {
struct device *dev;
struct device *hwmon_dev;
+ struct regulator_dev **rdevs;
u32 flags; /* from platform data */
@@ -1272,7 +1273,7 @@ struct pmbus_thermal_data {
static int pmbus_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
{
- struct pmbus_thermal_data *tdata = tz->devdata;
+ struct pmbus_thermal_data *tdata = thermal_zone_device_priv(tz);
struct pmbus_sensor *sensor = tdata->sensor;
struct pmbus_data *pmbus_data = tdata->pmbus_data;
struct i2c_client *client = to_i2c_client(pmbus_data->dev);
@@ -2692,18 +2693,64 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
return 0;
}
-#if IS_ENABLED(CONFIG_REGULATOR)
-static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
+/* A PMBus status flag and the corresponding REGULATOR_ERROR_* and REGULATOR_EVENTS_* flag */
+struct pmbus_status_assoc {
+ int pflag, rflag, eflag;
+};
+
+/* PMBus->regulator bit mappings for a PMBus status register */
+struct pmbus_status_category {
+ int func;
+ int reg;
+ const struct pmbus_status_assoc *bits; /* zero-terminated */
+};
+
+static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[] = {
+ {
+ .func = PMBUS_HAVE_STATUS_VOUT,
+ .reg = PMBUS_STATUS_VOUT,
+ .bits = (const struct pmbus_status_assoc[]) {
+ { PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN,
+ REGULATOR_EVENT_UNDER_VOLTAGE_WARN },
+ { PB_VOLTAGE_UV_FAULT, REGULATOR_ERROR_UNDER_VOLTAGE,
+ REGULATOR_EVENT_UNDER_VOLTAGE },
+ { PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN,
+ REGULATOR_EVENT_OVER_VOLTAGE_WARN },
+ { PB_VOLTAGE_OV_FAULT, REGULATOR_ERROR_REGULATION_OUT,
+ REGULATOR_EVENT_OVER_VOLTAGE_WARN },
+ { },
+ },
+ }, {
+ .func = PMBUS_HAVE_STATUS_IOUT,
+ .reg = PMBUS_STATUS_IOUT,
+ .bits = (const struct pmbus_status_assoc[]) {
+ { PB_IOUT_OC_WARNING, REGULATOR_ERROR_OVER_CURRENT_WARN,
+ REGULATOR_EVENT_OVER_CURRENT_WARN },
+ { PB_IOUT_OC_FAULT, REGULATOR_ERROR_OVER_CURRENT,
+ REGULATOR_EVENT_OVER_CURRENT },
+ { PB_IOUT_OC_LV_FAULT, REGULATOR_ERROR_OVER_CURRENT,
+ REGULATOR_EVENT_OVER_CURRENT },
+ { },
+ },
+ }, {
+ .func = PMBUS_HAVE_STATUS_TEMP,
+ .reg = PMBUS_STATUS_TEMPERATURE,
+ .bits = (const struct pmbus_status_assoc[]) {
+ { PB_TEMP_OT_WARNING, REGULATOR_ERROR_OVER_TEMP_WARN,
+ REGULATOR_EVENT_OVER_TEMP_WARN },
+ { PB_TEMP_OT_FAULT, REGULATOR_ERROR_OVER_TEMP,
+ REGULATOR_EVENT_OVER_TEMP },
+ { },
+ },
+ },
+};
+
+static int _pmbus_is_enabled(struct device *dev, u8 page)
{
- struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_data *data = i2c_get_clientdata(client);
- u8 page = rdev_get_id(rdev);
int ret;
- mutex_lock(&data->update_lock);
ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
- mutex_unlock(&data->update_lock);
if (ret < 0)
return ret;
@@ -2711,106 +2758,77 @@ static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
return !!(ret & PB_OPERATION_CONTROL_ON);
}
-static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
+static int __maybe_unused pmbus_is_enabled(struct device *dev, u8 page)
{
- struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
- u8 page = rdev_get_id(rdev);
int ret;
mutex_lock(&data->update_lock);
- ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
- PB_OPERATION_CONTROL_ON,
- enable ? PB_OPERATION_CONTROL_ON : 0);
+ ret = _pmbus_is_enabled(dev, page);
mutex_unlock(&data->update_lock);
- return ret;
+ return !!(ret & PB_OPERATION_CONTROL_ON);
}
-static int pmbus_regulator_enable(struct regulator_dev *rdev)
-{
- return _pmbus_regulator_on_off(rdev, 1);
-}
+#define to_dev_attr(_dev_attr) \
+ container_of(_dev_attr, struct device_attribute, attr)
-static int pmbus_regulator_disable(struct regulator_dev *rdev)
+static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
{
- return _pmbus_regulator_on_off(rdev, 0);
-}
-
-/* A PMBus status flag and the corresponding REGULATOR_ERROR_* flag */
-struct pmbus_regulator_status_assoc {
- int pflag, rflag;
-};
+ int i;
-/* PMBus->regulator bit mappings for a PMBus status register */
-struct pmbus_regulator_status_category {
- int func;
- int reg;
- const struct pmbus_regulator_status_assoc *bits; /* zero-terminated */
-};
+ for (i = 0; i < data->num_attributes; i++) {
+ struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int index = attr->index;
+ u16 smask = pb_index_to_mask(index);
+ u8 spage = pb_index_to_page(index);
+ u16 sreg = pb_index_to_reg(index);
+
+ if (reg == sreg && page == spage && (smask & flags)) {
+ dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
+ sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
+ kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
+ flags &= ~smask;
+ }
-static const struct pmbus_regulator_status_category pmbus_regulator_flag_map[] = {
- {
- .func = PMBUS_HAVE_STATUS_VOUT,
- .reg = PMBUS_STATUS_VOUT,
- .bits = (const struct pmbus_regulator_status_assoc[]) {
- { PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN },
- { PB_VOLTAGE_UV_FAULT, REGULATOR_ERROR_UNDER_VOLTAGE },
- { PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN },
- { PB_VOLTAGE_OV_FAULT, REGULATOR_ERROR_REGULATION_OUT },
- { },
- },
- }, {
- .func = PMBUS_HAVE_STATUS_IOUT,
- .reg = PMBUS_STATUS_IOUT,
- .bits = (const struct pmbus_regulator_status_assoc[]) {
- { PB_IOUT_OC_WARNING, REGULATOR_ERROR_OVER_CURRENT_WARN },
- { PB_IOUT_OC_FAULT, REGULATOR_ERROR_OVER_CURRENT },
- { PB_IOUT_OC_LV_FAULT, REGULATOR_ERROR_OVER_CURRENT },
- { },
- },
- }, {
- .func = PMBUS_HAVE_STATUS_TEMP,
- .reg = PMBUS_STATUS_TEMPERATURE,
- .bits = (const struct pmbus_regulator_status_assoc[]) {
- { PB_TEMP_OT_WARNING, REGULATOR_ERROR_OVER_TEMP_WARN },
- { PB_TEMP_OT_FAULT, REGULATOR_ERROR_OVER_TEMP },
- { },
- },
- },
-};
+ if (!flags)
+ break;
+ }
+}
-static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
+ unsigned int *event, bool notify)
{
int i, status;
- const struct pmbus_regulator_status_category *cat;
- const struct pmbus_regulator_status_assoc *bit;
- struct device *dev = rdev_get_dev(rdev);
- struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_data *data = i2c_get_clientdata(client);
- u8 page = rdev_get_id(rdev);
+ const struct pmbus_status_category *cat;
+ const struct pmbus_status_assoc *bit;
+ struct device *dev = data->dev;
+ struct i2c_client *client = to_i2c_client(dev);
int func = data->info->func[page];
*flags = 0;
+ *event = 0;
- mutex_lock(&data->update_lock);
-
- for (i = 0; i < ARRAY_SIZE(pmbus_regulator_flag_map); i++) {
- cat = &pmbus_regulator_flag_map[i];
+ for (i = 0; i < ARRAY_SIZE(pmbus_status_flag_map); i++) {
+ cat = &pmbus_status_flag_map[i];
if (!(func & cat->func))
continue;
status = _pmbus_read_byte_data(client, page, cat->reg);
- if (status < 0) {
- mutex_unlock(&data->update_lock);
+ if (status < 0)
return status;
- }
- for (bit = cat->bits; bit->pflag; bit++) {
- if (status & bit->pflag)
+ for (bit = cat->bits; bit->pflag; bit++)
+ if (status & bit->pflag) {
*flags |= bit->rflag;
- }
+ *event |= bit->eflag;
+ }
+
+ if (notify && status)
+ pmbus_notify(data, page, cat->reg, status);
+
}
/*
@@ -2823,25 +2841,32 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
* REGULATOR_ERROR_<foo>_WARN.
*/
status = pmbus_get_status(client, page, PMBUS_STATUS_WORD);
- mutex_unlock(&data->update_lock);
if (status < 0)
return status;
- if (pmbus_regulator_is_enabled(rdev)) {
- if (status & PB_STATUS_OFF)
+ if (_pmbus_is_enabled(dev, page)) {
+ if (status & PB_STATUS_OFF) {
*flags |= REGULATOR_ERROR_FAIL;
+ *event |= REGULATOR_EVENT_FAIL;
+ }
- if (status & PB_STATUS_POWER_GOOD_N)
+ if (status & PB_STATUS_POWER_GOOD_N) {
*flags |= REGULATOR_ERROR_REGULATION_OUT;
+ *event |= REGULATOR_EVENT_REGULATION_OUT;
+ }
}
/*
* Unlike most other status bits, PB_STATUS_{IOUT_OC,VOUT_OV} are
* defined strictly as fault indicators (not warnings).
*/
- if (status & PB_STATUS_IOUT_OC)
+ if (status & PB_STATUS_IOUT_OC) {
*flags |= REGULATOR_ERROR_OVER_CURRENT;
- if (status & PB_STATUS_VOUT_OV)
+ *event |= REGULATOR_EVENT_OVER_CURRENT;
+ }
+ if (status & PB_STATUS_VOUT_OV) {
*flags |= REGULATOR_ERROR_REGULATION_OUT;
+ *event |= REGULATOR_EVENT_FAIL;
+ }
/*
* If we haven't discovered any thermal faults or warnings via
@@ -2849,12 +2874,70 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
* a (conservative) best-effort interpretation.
*/
if (!(*flags & (REGULATOR_ERROR_OVER_TEMP | REGULATOR_ERROR_OVER_TEMP_WARN)) &&
- (status & PB_STATUS_TEMPERATURE))
+ (status & PB_STATUS_TEMPERATURE)) {
*flags |= REGULATOR_ERROR_OVER_TEMP_WARN;
+ *event |= REGULATOR_EVENT_OVER_TEMP_WARN;
+ }
+
return 0;
}
+static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
+ unsigned int *event, bool notify)
+{
+ int ret;
+
+ mutex_lock(&data->update_lock);
+ ret = _pmbus_get_flags(data, page, flags, event, notify);
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_REGULATOR)
+static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ return pmbus_is_enabled(rdev_get_dev(rdev), rdev_get_id(rdev));
+}
+
+static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ u8 page = rdev_get_id(rdev);
+ int ret;
+
+ mutex_lock(&data->update_lock);
+ ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
+ PB_OPERATION_CONTROL_ON,
+ enable ? PB_OPERATION_CONTROL_ON : 0);
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static int pmbus_regulator_enable(struct regulator_dev *rdev)
+{
+ return _pmbus_regulator_on_off(rdev, 1);
+}
+
+static int pmbus_regulator_disable(struct regulator_dev *rdev)
+{
+ return _pmbus_regulator_on_off(rdev, 0);
+}
+
+static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ int event;
+
+ return pmbus_get_flags(data, rdev_get_id(rdev), flags, &event, false);
+}
+
static int pmbus_regulator_get_status(struct regulator_dev *rdev)
{
struct device *dev = rdev_get_dev(rdev);
@@ -3050,9 +3133,13 @@ static int pmbus_regulator_register(struct pmbus_data *data)
struct device *dev = data->dev;
const struct pmbus_driver_info *info = data->info;
const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
- struct regulator_dev *rdev;
int i;
+ data->rdevs = devm_kzalloc(dev, sizeof(struct regulator_dev *) * info->num_regulators,
+ GFP_KERNEL);
+ if (!data->rdevs)
+ return -ENOMEM;
+
for (i = 0; i < info->num_regulators; i++) {
struct regulator_config config = { };
@@ -3062,23 +3149,113 @@ static int pmbus_regulator_register(struct pmbus_data *data)
if (pdata && pdata->reg_init_data)
config.init_data = &pdata->reg_init_data[i];
- rdev = devm_regulator_register(dev, &info->reg_desc[i],
- &config);
- if (IS_ERR(rdev))
- return dev_err_probe(dev, PTR_ERR(rdev),
+ data->rdevs[i] = devm_regulator_register(dev, &info->reg_desc[i],
+ &config);
+ if (IS_ERR(data->rdevs[i]))
+ return dev_err_probe(dev, PTR_ERR(data->rdevs[i]),
"Failed to register %s regulator\n",
info->reg_desc[i].name);
}
return 0;
}
+
+static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
+{
+ int j;
+
+ for (j = 0; j < data->info->num_regulators; j++) {
+ if (page == rdev_get_id(data->rdevs[j])) {
+ regulator_notifier_call_chain(data->rdevs[j], event, NULL);
+ break;
+ }
+ }
+ return 0;
+}
#else
static int pmbus_regulator_register(struct pmbus_data *data)
{
return 0;
}
+
+static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
+{
+ return 0;
+}
#endif
+static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
+{
+ return pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
+}
+
+static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
+{
+ struct pmbus_data *data = pdata;
+ struct i2c_client *client = to_i2c_client(data->dev);
+
+ int i, status, event;
+ mutex_lock(&data->update_lock);
+ for (i = 0; i < data->info->pages; i++) {
+ _pmbus_get_flags(data, i, &status, &event, true);
+
+ if (event)
+ pmbus_regulator_notify(data, i, event);
+ }
+
+ pmbus_clear_faults(client);
+ mutex_unlock(&data->update_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
+{
+ struct device *dev = &client->dev;
+ const struct pmbus_status_category *cat;
+ const struct pmbus_status_assoc *bit;
+ int i, j, err, func;
+ u8 mask;
+
+ static const u8 misc_status[] = {PMBUS_STATUS_CML, PMBUS_STATUS_OTHER,
+ PMBUS_STATUS_MFR_SPECIFIC, PMBUS_STATUS_FAN_12,
+ PMBUS_STATUS_FAN_34};
+
+ if (!client->irq)
+ return 0;
+
+ for (i = 0; i < data->info->pages; i++) {
+ func = data->info->func[i];
+
+ for (j = 0; j < ARRAY_SIZE(pmbus_status_flag_map); j++) {
+ cat = &pmbus_status_flag_map[j];
+ if (!(func & cat->func))
+ continue;
+ mask = 0;
+ for (bit = cat->bits; bit->pflag; bit++)
+ mask |= bit->pflag;
+
+ err = pmbus_write_smbalert_mask(client, i, cat->reg, ~mask);
+ if (err)
+ dev_dbg_once(dev, "Failed to set smbalert for reg 0x%02x\n",
+ cat->reg);
+ }
+
+ for (j = 0; j < ARRAY_SIZE(misc_status); j++)
+ pmbus_write_smbalert_mask(client, i, misc_status[j], 0xff);
+ }
+
+ /* Register notifiers */
+ err = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler,
+ IRQF_ONESHOT, "pmbus-irq", data);
+ if (err) {
+ dev_err(dev, "failed to request an irq %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
#if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -3086,8 +3263,13 @@ static int pmbus_debugfs_get(void *data, u64 *val)
{
int rc;
struct pmbus_debugfs_entry *entry = data;
+ struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
+ rc = mutex_lock_interruptible(&pdata->update_lock);
+ if (rc)
+ return rc;
rc = _pmbus_read_byte_data(entry->client, entry->page, entry->reg);
+ mutex_unlock(&pdata->update_lock);
if (rc < 0)
return rc;
@@ -3104,7 +3286,11 @@ static int pmbus_debugfs_get_status(void *data, u64 *val)
struct pmbus_debugfs_entry *entry = data;
struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
+ rc = mutex_lock_interruptible(&pdata->update_lock);
+ if (rc)
+ return rc;
rc = pdata->read_status(entry->client, entry->page);
+ mutex_unlock(&pdata->update_lock);
if (rc < 0)
return rc;
@@ -3120,10 +3306,15 @@ static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
{
int rc;
struct pmbus_debugfs_entry *entry = file->private_data;
+ struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
+ rc = mutex_lock_interruptible(&pdata->update_lock);
+ if (rc)
+ return rc;
rc = pmbus_read_block_data(entry->client, entry->page, entry->reg,
data);
+ mutex_unlock(&pdata->update_lock);
if (rc < 0)
return rc;
@@ -3441,6 +3632,10 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
if (ret)
return ret;
+ ret = pmbus_irq_setup(client, data);
+ if (ret)
+ return ret;
+
ret = pmbus_init_debugfs(client, data);
if (ret)
dev_warn(dev, "Failed to register debugfs\n");
@@ -3457,6 +3652,22 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
}
EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS);
+int pmbus_lock_interruptible(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ return mutex_lock_interruptible(&data->update_lock);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, PMBUS);
+
+void pmbus_unlock(struct i2c_client *client)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ mutex_unlock(&data->update_lock);
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_unlock, PMBUS);
+
static int __init pmbus_core_init(void)
{
pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL);