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