File Index
Files and directories
Sign in
FILE

p9_sbe.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/occ/p9_sbe.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2// Copyright IBM Corp 2019
  3
  4#include <linux/device.h>
  5#include <linux/errno.h>
  6#include <linux/slab.h>
  7#include <linux/fsi-occ.h>
  8#include <linux/mm.h>
  9#include <linux/module.h>
 10#include <linux/mod_devicetable.h>
 11#include <linux/mutex.h>
 12#include <linux/platform_device.h>
 13#include <linux/string.h>
 14#include <linux/sysfs.h>
 15
 16#include "common.h"
 17
 18#define OCC_CHECKSUM_RETRIES	3
 19
 20struct p9_sbe_occ {
 21	struct occ occ;
 22	bool sbe_error;
 23	void *ffdc;
 24	size_t ffdc_len;
 25	size_t ffdc_size;
 26	struct mutex sbe_error_lock;	/* lock access to ffdc data */
 27	struct device *sbe;
 28};
 29
 30#define to_p9_sbe_occ(x)	container_of((x), struct p9_sbe_occ, occ)
 31
 32static ssize_t ffdc_read(struct file *filp, struct kobject *kobj,
 33			 const struct bin_attribute *battr, char *buf, loff_t pos,
 34			 size_t count)
 35{
 36	ssize_t rc = 0;
 37	struct occ *occ = dev_get_drvdata(kobj_to_dev(kobj));
 38	struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
 39
 40	mutex_lock(&ctx->sbe_error_lock);
 41	if (ctx->sbe_error) {
 42		rc = memory_read_from_buffer(buf, count, &pos, ctx->ffdc,
 43					     ctx->ffdc_len);
 44		if (pos >= ctx->ffdc_len)
 45			ctx->sbe_error = false;
 46	}
 47	mutex_unlock(&ctx->sbe_error_lock);
 48
 49	return rc;
 50}
 51static const BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4);
 52
 53static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp,
 54				 size_t resp_len)
 55{
 56	bool notify = false;
 57
 58	mutex_lock(&ctx->sbe_error_lock);
 59	if (!ctx->sbe_error) {
 60		if (resp_len > ctx->ffdc_size) {
 61			kvfree(ctx->ffdc);
 62			ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
 63			if (!ctx->ffdc) {
 64				ctx->ffdc_len = 0;
 65				ctx->ffdc_size = 0;
 66				goto done;
 67			}
 68
 69			ctx->ffdc_size = resp_len;
 70		}
 71
 72		notify = true;
 73		ctx->sbe_error = true;
 74		ctx->ffdc_len = resp_len;
 75		memcpy(ctx->ffdc, resp, resp_len);
 76	}
 77
 78done:
 79	mutex_unlock(&ctx->sbe_error_lock);
 80	return notify;
 81}
 82
 83static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len,
 84			       void *resp, size_t resp_len)
 85{
 86	size_t original_resp_len = resp_len;
 87	struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
 88	int rc, i;
 89
 90	for (i = 0; i < OCC_CHECKSUM_RETRIES; ++i) {
 91		rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
 92		if (rc >= 0)
 93			break;
 94		if (resp_len) {
 95			if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len))
 96				sysfs_notify(&occ->bus_dev->kobj, NULL,
 97					     bin_attr_ffdc.attr.name);
 98			return rc;
 99		}
100		if (rc != -EBADE)
101			return rc;
102		resp_len = original_resp_len;
103	}
104
105	switch (((struct occ_response *)resp)->return_status) {
106	case OCC_RESP_CMD_IN_PRG:
107		rc = -ETIMEDOUT;
108		break;
109	case OCC_RESP_SUCCESS:
110		rc = 0;
111		break;
112	case OCC_RESP_CMD_INVAL:
113	case OCC_RESP_CMD_LEN_INVAL:
114	case OCC_RESP_DATA_INVAL:
115	case OCC_RESP_CHKSUM_ERR:
116		rc = -EINVAL;
117		break;
118	case OCC_RESP_INT_ERR:
119	case OCC_RESP_BAD_STATE:
120	case OCC_RESP_CRIT_EXCEPT:
121	case OCC_RESP_CRIT_INIT:
122	case OCC_RESP_CRIT_WATCHDOG:
123	case OCC_RESP_CRIT_OCB:
124	case OCC_RESP_CRIT_HW:
125		rc = -EREMOTEIO;
126		break;
127	default:
128		rc = -EPROTO;
129	}
130
131	return rc;
132}
133
134static int p9_sbe_occ_probe(struct platform_device *pdev)
135{
136	int rc;
137	struct occ *occ;
138	struct p9_sbe_occ *ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx),
139					      GFP_KERNEL);
140	if (!ctx)
141		return -ENOMEM;
142
143	mutex_init(&ctx->sbe_error_lock);
144
145	ctx->sbe = pdev->dev.parent;
146	occ = &ctx->occ;
147	occ->bus_dev = &pdev->dev;
148	platform_set_drvdata(pdev, occ);
149
150	occ->powr_sample_time_us = 500;
151	occ->poll_cmd_data = 0x20;		/* P9 OCC poll data */
152	occ->send_cmd = p9_sbe_occ_send_cmd;
153
154	rc = occ_setup(occ);
155	if (rc == -ESHUTDOWN)
156		rc = -ENODEV;	/* Host is shutdown, don't spew errors */
157
158	if (!rc) {
159		rc = device_create_bin_file(occ->bus_dev, &bin_attr_ffdc);
160		if (rc) {
161			dev_warn(occ->bus_dev,
162				 "failed to create SBE error ffdc file\n");
163			rc = 0;
164		}
165	}
166
167	return rc;
168}
169
170static void p9_sbe_occ_remove(struct platform_device *pdev)
171{
172	struct occ *occ = platform_get_drvdata(pdev);
173	struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
174
175	device_remove_bin_file(occ->bus_dev, &bin_attr_ffdc);
176
177	ctx->sbe = NULL;
178	occ_shutdown(occ);
179
180	kvfree(ctx->ffdc);
181}
182
183static const struct of_device_id p9_sbe_occ_of_match[] = {
184	{ .compatible = "ibm,p9-occ-hwmon" },
185	{ .compatible = "ibm,p10-occ-hwmon" },
186	{}
187};
188MODULE_DEVICE_TABLE(of, p9_sbe_occ_of_match);
189
190static struct platform_driver p9_sbe_occ_driver = {
191	.driver = {
192		.name = "occ-hwmon",
193		.of_match_table = p9_sbe_occ_of_match,
194	},
195	.probe = p9_sbe_occ_probe,
196	.remove = p9_sbe_occ_remove,
197};
198
199module_platform_driver(p9_sbe_occ_driver);
200
201MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
202MODULE_DESCRIPTION("BMC P9 OCC hwmon driver");
203MODULE_LICENSE("GPL");
~/ Read-only file preview.