brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · b5993c7 Raw
204 lines · c
1// SPDX-License-Identifier: GPL-2.0+2// Copyright IBM Corp 20193 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	319 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			 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 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_new = 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");204