0001
0002
0003
0004 #include <linux/device.h>
0005 #include <linux/errno.h>
0006 #include <linux/slab.h>
0007 #include <linux/fsi-occ.h>
0008 #include <linux/mm.h>
0009 #include <linux/module.h>
0010 #include <linux/mutex.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/string.h>
0013 #include <linux/sysfs.h>
0014
0015 #include "common.h"
0016
0017 struct p9_sbe_occ {
0018 struct occ occ;
0019 bool sbe_error;
0020 void *ffdc;
0021 size_t ffdc_len;
0022 size_t ffdc_size;
0023 struct mutex sbe_error_lock;
0024 struct device *sbe;
0025 };
0026
0027 #define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ)
0028
0029 static ssize_t ffdc_read(struct file *filp, struct kobject *kobj,
0030 struct bin_attribute *battr, char *buf, loff_t pos,
0031 size_t count)
0032 {
0033 ssize_t rc = 0;
0034 struct occ *occ = dev_get_drvdata(kobj_to_dev(kobj));
0035 struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
0036
0037 mutex_lock(&ctx->sbe_error_lock);
0038 if (ctx->sbe_error) {
0039 rc = memory_read_from_buffer(buf, count, &pos, ctx->ffdc,
0040 ctx->ffdc_len);
0041 if (pos >= ctx->ffdc_len)
0042 ctx->sbe_error = false;
0043 }
0044 mutex_unlock(&ctx->sbe_error_lock);
0045
0046 return rc;
0047 }
0048 static BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4);
0049
0050 static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp,
0051 size_t resp_len)
0052 {
0053 bool notify = false;
0054
0055 mutex_lock(&ctx->sbe_error_lock);
0056 if (!ctx->sbe_error) {
0057 if (resp_len > ctx->ffdc_size) {
0058 kvfree(ctx->ffdc);
0059 ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
0060 if (!ctx->ffdc) {
0061 ctx->ffdc_len = 0;
0062 ctx->ffdc_size = 0;
0063 goto done;
0064 }
0065
0066 ctx->ffdc_size = resp_len;
0067 }
0068
0069 notify = true;
0070 ctx->sbe_error = true;
0071 ctx->ffdc_len = resp_len;
0072 memcpy(ctx->ffdc, resp, resp_len);
0073 }
0074
0075 done:
0076 mutex_unlock(&ctx->sbe_error_lock);
0077 return notify;
0078 }
0079
0080 static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len,
0081 void *resp, size_t resp_len)
0082 {
0083 struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
0084 int rc;
0085
0086 rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
0087 if (rc < 0) {
0088 if (resp_len) {
0089 if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len))
0090 sysfs_notify(&occ->bus_dev->kobj, NULL,
0091 bin_attr_ffdc.attr.name);
0092 }
0093
0094 return rc;
0095 }
0096
0097 switch (((struct occ_response *)resp)->return_status) {
0098 case OCC_RESP_CMD_IN_PRG:
0099 rc = -ETIMEDOUT;
0100 break;
0101 case OCC_RESP_SUCCESS:
0102 rc = 0;
0103 break;
0104 case OCC_RESP_CMD_INVAL:
0105 case OCC_RESP_CMD_LEN_INVAL:
0106 case OCC_RESP_DATA_INVAL:
0107 case OCC_RESP_CHKSUM_ERR:
0108 rc = -EINVAL;
0109 break;
0110 case OCC_RESP_INT_ERR:
0111 case OCC_RESP_BAD_STATE:
0112 case OCC_RESP_CRIT_EXCEPT:
0113 case OCC_RESP_CRIT_INIT:
0114 case OCC_RESP_CRIT_WATCHDOG:
0115 case OCC_RESP_CRIT_OCB:
0116 case OCC_RESP_CRIT_HW:
0117 rc = -EREMOTEIO;
0118 break;
0119 default:
0120 rc = -EPROTO;
0121 }
0122
0123 return rc;
0124 }
0125
0126 static int p9_sbe_occ_probe(struct platform_device *pdev)
0127 {
0128 int rc;
0129 struct occ *occ;
0130 struct p9_sbe_occ *ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx),
0131 GFP_KERNEL);
0132 if (!ctx)
0133 return -ENOMEM;
0134
0135 mutex_init(&ctx->sbe_error_lock);
0136
0137 ctx->sbe = pdev->dev.parent;
0138 occ = &ctx->occ;
0139 occ->bus_dev = &pdev->dev;
0140 platform_set_drvdata(pdev, occ);
0141
0142 occ->powr_sample_time_us = 500;
0143 occ->poll_cmd_data = 0x20;
0144 occ->send_cmd = p9_sbe_occ_send_cmd;
0145
0146 rc = occ_setup(occ);
0147 if (rc == -ESHUTDOWN)
0148 rc = -ENODEV;
0149
0150 if (!rc) {
0151 rc = device_create_bin_file(occ->bus_dev, &bin_attr_ffdc);
0152 if (rc) {
0153 dev_warn(occ->bus_dev,
0154 "failed to create SBE error ffdc file\n");
0155 rc = 0;
0156 }
0157 }
0158
0159 return rc;
0160 }
0161
0162 static int p9_sbe_occ_remove(struct platform_device *pdev)
0163 {
0164 struct occ *occ = platform_get_drvdata(pdev);
0165 struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
0166
0167 device_remove_bin_file(occ->bus_dev, &bin_attr_ffdc);
0168
0169 ctx->sbe = NULL;
0170 occ_shutdown(occ);
0171
0172 kvfree(ctx->ffdc);
0173
0174 return 0;
0175 }
0176
0177 static struct platform_driver p9_sbe_occ_driver = {
0178 .driver = {
0179 .name = "occ-hwmon",
0180 },
0181 .probe = p9_sbe_occ_probe,
0182 .remove = p9_sbe_occ_remove,
0183 };
0184
0185 module_platform_driver(p9_sbe_occ_driver);
0186
0187 MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
0188 MODULE_DESCRIPTION("BMC P9 OCC hwmon driver");
0189 MODULE_LICENSE("GPL");