Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 // Copyright IBM Corp 2019
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;    /* lock access to ffdc data */
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;      /* P9 OCC poll data */
0144     occ->send_cmd = p9_sbe_occ_send_cmd;
0145 
0146     rc = occ_setup(occ);
0147     if (rc == -ESHUTDOWN)
0148         rc = -ENODEV;   /* Host is shutdown, don't spew errors */
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");