summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-09-30 14:02:47 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-09-30 14:02:47 +0200
commitf5e536af4860a7d07b769d0388a68e74fe453c9b (patch)
tree84c0c72180e986d0f6e8210549ef5b5bd39ba36c
parent3aa12610b481f99b5e4e3f801ff7f9b7629e4ecf (diff)
parent35af9fb49bc5c6d61ef70b501c3a56fe161cce3e (diff)
downloadlinux-stable-f5e536af4860a7d07b769d0388a68e74fe453c9b.tar.gz
linux-stable-f5e536af4860a7d07b769d0388a68e74fe453c9b.tar.bz2
linux-stable-f5e536af4860a7d07b769d0388a68e74fe453c9b.zip
Merge tag 'fsi-for-v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/joel/fsi into char-misc-next
Joel writes: "FSI changes for v6.1 * Fix a OCC hwmon userspace compatibility regression that was introduced in v5.19 * Device tree bindings for the OCC * A bunch of janitor type fixes" * tag 'fsi-for-v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/joel/fsi: fsi: core: Check error number after calling ida_simple_get hwmon: (occ) Check for device property for setting OCC active during probe fsi: occ: Support probing the hwmon child device from dts node dt-bindings: hwmon: Add IBM OCC bindings fsi: master-ast-cf: Fix missing of_node_put in fsi_master_acf_probe fsi: sbefifo: Add detailed debugging information fsi: cleanup extern usage in function definition fsi: occ: Prevent use after free hwmon (occ): Retry for checksum failure fsi: occ: Fix checksum failure mode fsi: Fix typo in comment
-rw-r--r--Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml39
-rw-r--r--drivers/fsi/fsi-core.c11
-rw-r--r--drivers/fsi/fsi-master-ast-cf.c2
-rw-r--r--drivers/fsi/fsi-master.h2
-rw-r--r--drivers/fsi/fsi-occ.c66
-rw-r--r--drivers/fsi/fsi-sbefifo.c15
-rw-r--r--drivers/hwmon/occ/common.c11
-rw-r--r--drivers/hwmon/occ/p9_sbe.c26
8 files changed, 143 insertions, 29 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml
new file mode 100644
index 000000000000..3dbdc5af2804
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ibm,occ-hwmon.yaml
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/ibm,occ-hwmon.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: IBM On-Chip Controller (OCC) accessed from a service processor
+
+maintainers:
+ - Eddie James <eajames@linux.ibm.com>
+
+description: |
+ The POWER processor On-Chip Controller (OCC) helps manage power and
+ thermals for the system. A service processor or baseboard management
+ controller can query the OCC for it's power and thermal data to report
+ through hwmon.
+
+properties:
+ compatible:
+ enum:
+ - ibm,p9-occ-hwmon
+ - ibm,p10-occ-hwmon
+
+ ibm,no-poll-on-init:
+ description: This property describes whether or not the OCC should
+ be polled during driver initialization.
+ type: boolean
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ hwmon {
+ compatible = "ibm,p10-occ-hwmon";
+ ibm,no-poll-on-init;
+ };
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 3a7b78e36701..694e80c06665 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -392,8 +392,8 @@ int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
}
EXPORT_SYMBOL_GPL(fsi_slave_write);
-extern int fsi_slave_claim_range(struct fsi_slave *slave,
- uint32_t addr, uint32_t size)
+int fsi_slave_claim_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size)
{
if (addr + size < addr)
return -EINVAL;
@@ -406,8 +406,8 @@ extern int fsi_slave_claim_range(struct fsi_slave *slave,
}
EXPORT_SYMBOL_GPL(fsi_slave_claim_range);
-extern void fsi_slave_release_range(struct fsi_slave *slave,
- uint32_t addr, uint32_t size)
+void fsi_slave_release_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size)
{
}
EXPORT_SYMBOL_GPL(fsi_slave_release_range);
@@ -1314,6 +1314,9 @@ int fsi_master_register(struct fsi_master *master)
mutex_init(&master->scan_lock);
master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
+ if (master->idx < 0)
+ return master->idx;
+
dev_set_name(&master->dev, "fsi%d", master->idx);
master->dev.class = &fsi_master_class;
diff --git a/drivers/fsi/fsi-master-ast-cf.c b/drivers/fsi/fsi-master-ast-cf.c
index 24292acdbaf8..5f608ef8b53c 100644
--- a/drivers/fsi/fsi-master-ast-cf.c
+++ b/drivers/fsi/fsi-master-ast-cf.c
@@ -1324,12 +1324,14 @@ static int fsi_master_acf_probe(struct platform_device *pdev)
}
master->cvic = devm_of_iomap(&pdev->dev, np, 0, NULL);
if (IS_ERR(master->cvic)) {
+ of_node_put(np);
rc = PTR_ERR(master->cvic);
dev_err(&pdev->dev, "Error %d mapping CVIC\n", rc);
goto err_free;
}
rc = of_property_read_u32(np, "copro-sw-interrupts",
&master->cvic_sw_irq);
+ of_node_put(np);
if (rc) {
dev_err(&pdev->dev, "Can't find coprocessor SW interrupt\n");
goto err_free;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index cd6bee5e12a7..4762315a46ba 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -51,7 +51,7 @@
#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
-/* MRESB: Reset brindge */
+/* MRESB: Reset bridge */
#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */
diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c
index c9cc75fbdfb9..abdd37d5507f 100644
--- a/drivers/fsi/fsi-occ.c
+++ b/drivers/fsi/fsi-occ.c
@@ -44,6 +44,7 @@ struct occ {
struct device *sbefifo;
char name[32];
int idx;
+ bool platform_hwmon;
u8 sequence_number;
void *buffer;
void *client_buffer;
@@ -94,6 +95,7 @@ static int occ_open(struct inode *inode, struct file *file)
client->occ = occ;
mutex_init(&client->lock);
file->private_data = client;
+ get_device(occ->dev);
/* We allocate a 1-page buffer, make sure it all fits */
BUILD_BUG_ON((OCC_CMD_DATA_BYTES + 3) > PAGE_SIZE);
@@ -197,6 +199,7 @@ static int occ_release(struct inode *inode, struct file *file)
{
struct occ_client *client = file->private_data;
+ put_device(client->occ->dev);
free_page((unsigned long)client->buffer);
kfree(client);
@@ -246,7 +249,7 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
if (checksum != checksum_resp) {
dev_err(occ->dev, "Bad checksum: %04x!=%04x\n", checksum,
checksum_resp);
- return -EBADMSG;
+ return -EBADE;
}
return 0;
@@ -493,12 +496,19 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
for (i = 1; i < req_len - 2; ++i)
checksum += byte_request[i];
- mutex_lock(&occ->occ_lock);
+ rc = mutex_lock_interruptible(&occ->occ_lock);
+ if (rc)
+ return rc;
occ->client_buffer = response;
occ->client_buffer_size = user_resp_len;
occ->client_response_size = 0;
+ if (!occ->buffer) {
+ rc = -ENOENT;
+ goto done;
+ }
+
/*
* Get a sequence number and update the counter. Avoid a sequence
* number of 0 which would pass the response check below even if the
@@ -575,8 +585,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
dev_dbg(dev, "resp_status=%02x resp_data_len=%d\n",
resp->return_status, resp_data_length);
- occ->client_response_size = resp_data_length + 7;
rc = occ_verify_checksum(occ, resp, resp_data_length);
+ if (rc)
+ goto done;
+
+ occ->client_response_size = resp_data_length + 7;
done:
*resp_len = occ->client_response_size;
@@ -586,7 +599,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
}
EXPORT_SYMBOL_GPL(fsi_occ_submit);
-static int occ_unregister_child(struct device *dev, void *data)
+static int occ_unregister_platform_child(struct device *dev, void *data)
{
struct platform_device *hwmon_dev = to_platform_device(dev);
@@ -595,12 +608,25 @@ static int occ_unregister_child(struct device *dev, void *data)
return 0;
}
+static int occ_unregister_of_child(struct device *dev, void *data)
+{
+ struct platform_device *hwmon_dev = to_platform_device(dev);
+
+ of_device_unregister(hwmon_dev);
+ if (dev->of_node)
+ of_node_clear_flag(dev->of_node, OF_POPULATED);
+
+ return 0;
+}
+
static int occ_probe(struct platform_device *pdev)
{
int rc;
u32 reg;
+ char child_name[32];
struct occ *occ;
- struct platform_device *hwmon_dev;
+ struct platform_device *hwmon_dev = NULL;
+ struct device_node *hwmon_node;
struct device *dev = &pdev->dev;
struct platform_device_info hwmon_dev_info = {
.parent = dev,
@@ -659,10 +685,20 @@ static int occ_probe(struct platform_device *pdev)
return rc;
}
- hwmon_dev_info.id = occ->idx;
- hwmon_dev = platform_device_register_full(&hwmon_dev_info);
- if (IS_ERR(hwmon_dev))
- dev_warn(dev, "failed to create hwmon device\n");
+ hwmon_node = of_get_child_by_name(dev->of_node, hwmon_dev_info.name);
+ if (hwmon_node) {
+ snprintf(child_name, sizeof(child_name), "%s.%d", hwmon_dev_info.name, occ->idx);
+ hwmon_dev = of_platform_device_create(hwmon_node, child_name, dev);
+ of_node_put(hwmon_node);
+ }
+
+ if (!hwmon_dev) {
+ occ->platform_hwmon = true;
+ hwmon_dev_info.id = occ->idx;
+ hwmon_dev = platform_device_register_full(&hwmon_dev_info);
+ if (IS_ERR(hwmon_dev))
+ dev_warn(dev, "failed to create hwmon device\n");
+ }
return 0;
}
@@ -671,11 +707,17 @@ static int occ_remove(struct platform_device *pdev)
{
struct occ *occ = platform_get_drvdata(pdev);
- kvfree(occ->buffer);
-
misc_deregister(&occ->mdev);
- device_for_each_child(&pdev->dev, NULL, occ_unregister_child);
+ mutex_lock(&occ->occ_lock);
+ kvfree(occ->buffer);
+ occ->buffer = NULL;
+ mutex_unlock(&occ->occ_lock);
+
+ if (occ->platform_hwmon)
+ device_for_each_child(&pdev->dev, NULL, occ_unregister_platform_child);
+ else
+ device_for_each_child(&pdev->dev, NULL, occ_unregister_of_child);
ida_simple_remove(&occ_ida, occ->idx);
diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index f52a912cdf16..5f93a53846aa 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -477,7 +477,8 @@ static int sbefifo_wait(struct sbefifo *sbefifo, bool up,
if (!ready) {
sysfs_notify(&sbefifo->dev.kobj, NULL, dev_attr_timeout.attr.name);
sbefifo->timed_out = true;
- dev_err(dev, "%s FIFO Timeout ! status=%08x\n", up ? "UP" : "DOWN", sts);
+ dev_err(dev, "%s FIFO Timeout (%u ms)! status=%08x\n",
+ up ? "UP" : "DOWN", jiffies_to_msecs(timeout), sts);
return -ETIMEDOUT;
}
dev_vdbg(dev, "End of wait status: %08x\n", sts);
@@ -497,8 +498,8 @@ static int sbefifo_send_command(struct sbefifo *sbefifo,
u32 status;
int rc;
- dev_vdbg(dev, "sending command (%zd words, cmd=%04x)\n",
- cmd_len, be32_to_cpu(command[1]));
+ dev_dbg(dev, "sending command (%zd words, cmd=%04x)\n",
+ cmd_len, be32_to_cpu(command[1]));
/* As long as there's something to send */
timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_CMD);
@@ -551,21 +552,23 @@ static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *respo
size_t len;
int rc;
- dev_vdbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response));
+ dev_dbg(dev, "reading response, buflen = %zd\n", iov_iter_count(response));
timeout = msecs_to_jiffies(sbefifo->timeout_start_rsp_ms);
for (;;) {
/* Grab FIFO status (this will handle parity errors) */
rc = sbefifo_wait(sbefifo, false, &status, timeout);
- if (rc < 0)
+ if (rc < 0) {
+ dev_dbg(dev, "timeout waiting (%u ms)\n", jiffies_to_msecs(timeout));
return rc;
+ }
timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_RSP);
/* Decode status */
len = sbefifo_populated(status);
eot_set = sbefifo_eot_set(status);
- dev_vdbg(dev, " chunk size %zd eot_set=0x%x\n", len, eot_set);
+ dev_dbg(dev, " chunk size %zd eot_set=0x%x\n", len, eot_set);
/* Go through the chunk */
while(len--) {
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 45407b12db4b..dd690f700d49 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -10,6 +10,7 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/property.h>
#include <linux/sysfs.h>
#include <asm/unaligned.h>
@@ -1216,8 +1217,16 @@ int occ_setup(struct occ *occ)
occ->groups[0] = &occ->group;
rc = occ_setup_sysfs(occ);
- if (rc)
+ if (rc) {
dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
+ return rc;
+ }
+
+ if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) {
+ rc = occ_active(occ, true);
+ if (rc)
+ occ_shutdown_sysfs(occ);
+ }
return rc;
}
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index c1e0a1d96cd4..96521363b696 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -7,6 +7,7 @@
#include <linux/fsi-occ.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/string.h>
@@ -14,6 +15,8 @@
#include "common.h"
+#define OCC_CHECKSUM_RETRIES 3
+
struct p9_sbe_occ {
struct occ occ;
bool sbe_error;
@@ -80,18 +83,23 @@ done:
static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len,
void *resp, size_t resp_len)
{
+ size_t original_resp_len = resp_len;
struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
- int rc;
+ int rc, i;
- rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
- if (rc < 0) {
+ for (i = 0; i < OCC_CHECKSUM_RETRIES; ++i) {
+ rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
+ if (rc >= 0)
+ break;
if (resp_len) {
if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len))
sysfs_notify(&occ->bus_dev->kobj, NULL,
bin_attr_ffdc.attr.name);
+ return rc;
}
-
- return rc;
+ if (rc != -EBADE)
+ return rc;
+ resp_len = original_resp_len;
}
switch (((struct occ_response *)resp)->return_status) {
@@ -174,9 +182,17 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id p9_sbe_occ_of_match[] = {
+ { .compatible = "ibm,p9-occ-hwmon" },
+ { .compatible = "ibm,p10-occ-hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, p9_sbe_occ_of_match);
+
static struct platform_driver p9_sbe_occ_driver = {
.driver = {
.name = "occ-hwmon",
+ .of_match_table = p9_sbe_occ_of_match,
},
.probe = p9_sbe_occ_probe,
.remove = p9_sbe_occ_remove,