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