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 -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+nHMAt9PCBDH63wBa3ZZB4FHcJ4FAmM0M4UACgkQa3ZZB4FH cJ5kixAAhrKwdGgy/Y0icswgKdixjY7ZkNlVQilKEykMD4KcfarFoRf/6BnyZXMG w09nCUjMLmwzYh3ebAEisVv++yNB/FuctCdUjd0UyITqhMkzuVru54ifZuTrcWMw 8MPRjVZDkbsyDT/gaVRdZgj9FHBybuuQJiGluuw4FgXt78/KBC1dpb/U7zJqsJQt eWebyu2giZ/bNm9U4rqZrx0Ld5xOXC/Ry623J3XBbdR9lOgzyAIjbWu6H54sI7eV DGsiOLotEPLX4ZZk2VphwaN+WTgV8/ZRfY5VIzH6ZncllJCg8ULCzKLnvF6V3ecr j9GLzo9lo0qMcHfN0fiT11jLPWvs6Ji7WjoEc9mGU8NZtVzG+qgJ7z0MQs9IcT1a IcrOueCpYfOJjuYey9hWGCIOmm7Fql5f+UPwEkXU6cMFUP0EMtwVp/4ZT51uoZZJ JZ4GURMeJnlFLZstCDUhKTg4HiPymzHQPeUjoIsnXAtO4Q45xgaFii2KerIgiTsQ 5NB2lPqaIbdI2XqoJ/eiTsfEoGppyYKw5bjDcQeICO3nWSNUUjGrdbpvb60XWEBd mLDeoTpMBDdDYl6/v1JIvd3jHSsMz2l9X3VshIj6U11TsIsfyvmanY3ac9NljNQF Z4W6YHRgGeGM4+jwTsGNXVvel6ZuBO4Y9ld4NOUZWP3x8taYtt0= =ZFpc -----END PGP SIGNATURE----- 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
This commit is contained in:
Коммит
f5e536af48
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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--) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче