1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) IBM Corporation 2017 |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | */ |
14 | |
15 | #include <linux/device.h> |
16 | #include <linux/errno.h> |
17 | #include <linux/fs.h> |
18 | #include <linux/fsi.h> |
19 | #include <linux/fsi-sbefifo.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/cdev.h> |
22 | #include <linux/module.h> |
23 | #include <linux/mutex.h> |
24 | #include <linux/of.h> |
25 | #include <linux/of_platform.h> |
26 | #include <linux/platform_device.h> |
27 | #include <linux/sched.h> |
28 | #include <linux/slab.h> |
29 | #include <linux/uaccess.h> |
30 | #include <linux/delay.h> |
31 | #include <linux/uio.h> |
32 | #include <linux/vmalloc.h> |
33 | #include <linux/mm.h> |
34 | |
35 | #include <uapi/linux/fsi.h> |
36 | |
37 | /* |
38 | * The SBEFIFO is a pipe-like FSI device for communicating with |
39 | * the self boot engine on POWER processors. |
40 | */ |
41 | |
42 | #define DEVICE_NAME "sbefifo" |
43 | #define FSI_ENGID_SBE 0x22 |
44 | |
45 | /* |
46 | * Register layout |
47 | */ |
48 | |
49 | /* Register banks */ |
50 | #define SBEFIFO_UP 0x00 /* FSI -> Host */ |
51 | #define SBEFIFO_DOWN 0x40 /* Host -> FSI */ |
52 | |
53 | /* Per-bank registers */ |
54 | #define SBEFIFO_FIFO 0x00 /* The FIFO itself */ |
55 | #define SBEFIFO_STS 0x04 /* Status register */ |
56 | #define SBEFIFO_STS_PARITY_ERR 0x20000000 |
57 | #define SBEFIFO_STS_RESET_REQ 0x02000000 |
58 | #define SBEFIFO_STS_GOT_EOT 0x00800000 |
59 | #define SBEFIFO_STS_MAX_XFER_LIMIT 0x00400000 |
60 | #define SBEFIFO_STS_FULL 0x00200000 |
61 | #define SBEFIFO_STS_EMPTY 0x00100000 |
62 | #define SBEFIFO_STS_ECNT_MASK 0x000f0000 |
63 | #define SBEFIFO_STS_ECNT_SHIFT 16 |
64 | #define SBEFIFO_STS_VALID_MASK 0x0000ff00 |
65 | #define SBEFIFO_STS_VALID_SHIFT 8 |
66 | #define SBEFIFO_STS_EOT_MASK 0x000000ff |
67 | #define SBEFIFO_STS_EOT_SHIFT 0 |
68 | #define SBEFIFO_EOT_RAISE 0x08 /* (Up only) Set End Of Transfer */ |
69 | #define SBEFIFO_REQ_RESET 0x0C /* (Up only) Reset Request */ |
70 | #define SBEFIFO_PERFORM_RESET 0x10 /* (Down only) Perform Reset */ |
71 | #define SBEFIFO_EOT_ACK 0x14 /* (Down only) Acknowledge EOT */ |
72 | #define SBEFIFO_DOWN_MAX 0x18 /* (Down only) Max transfer */ |
73 | |
74 | /* CFAM GP Mailbox SelfBoot Message register */ |
75 | #define CFAM_GP_MBOX_SBM_ADDR 0x2824 /* Converted 0x2809 */ |
76 | |
77 | #define CFAM_SBM_SBE_BOOTED 0x80000000 |
78 | #define CFAM_SBM_SBE_ASYNC_FFDC 0x40000000 |
79 | #define CFAM_SBM_SBE_STATE_MASK 0x00f00000 |
80 | #define CFAM_SBM_SBE_STATE_SHIFT 20 |
81 | |
82 | enum sbe_state |
83 | { |
84 | SBE_STATE_UNKNOWN = 0x0, // Unknown, initial state |
85 | SBE_STATE_IPLING = 0x1, // IPL'ing - autonomous mode (transient) |
86 | SBE_STATE_ISTEP = 0x2, // ISTEP - Running IPL by steps (transient) |
87 | SBE_STATE_MPIPL = 0x3, // MPIPL |
88 | SBE_STATE_RUNTIME = 0x4, // SBE Runtime |
89 | SBE_STATE_DMT = 0x5, // Dead Man Timer State (transient) |
90 | SBE_STATE_DUMP = 0x6, // Dumping |
91 | SBE_STATE_FAILURE = 0x7, // Internal SBE failure |
92 | SBE_STATE_QUIESCE = 0x8, // Final state - needs SBE reset to get out |
93 | }; |
94 | |
95 | /* FIFO depth */ |
96 | #define SBEFIFO_FIFO_DEPTH 8 |
97 | |
98 | /* Helpers */ |
99 | #define sbefifo_empty(sts) ((sts) & SBEFIFO_STS_EMPTY) |
100 | #define sbefifo_full(sts) ((sts) & SBEFIFO_STS_FULL) |
101 | #define sbefifo_parity_err(sts) ((sts) & SBEFIFO_STS_PARITY_ERR) |
102 | #define sbefifo_populated(sts) (((sts) & SBEFIFO_STS_ECNT_MASK) >> SBEFIFO_STS_ECNT_SHIFT) |
103 | #define sbefifo_vacant(sts) (SBEFIFO_FIFO_DEPTH - sbefifo_populated(sts)) |
104 | #define sbefifo_eot_set(sts) (((sts) & SBEFIFO_STS_EOT_MASK) >> SBEFIFO_STS_EOT_SHIFT) |
105 | |
106 | /* Reset request timeout in ms */ |
107 | #define SBEFIFO_RESET_TIMEOUT 10000 |
108 | |
109 | /* Timeouts for commands in ms */ |
110 | #define SBEFIFO_TIMEOUT_START_CMD 10000 |
111 | #define SBEFIFO_TIMEOUT_IN_CMD 1000 |
112 | #define SBEFIFO_TIMEOUT_START_RSP 10000 |
113 | #define SBEFIFO_TIMEOUT_IN_RSP 1000 |
114 | |
115 | /* Other constants */ |
116 | #define SBEFIFO_MAX_USER_CMD_LEN (0x100000 + PAGE_SIZE) |
117 | #define SBEFIFO_RESET_MAGIC 0x52534554 /* "RSET" */ |
118 | |
119 | struct sbefifo { |
120 | uint32_t magic; |
121 | #define SBEFIFO_MAGIC 0x53424546 /* "SBEF" */ |
122 | struct fsi_device *fsi_dev; |
123 | struct device dev; |
124 | struct cdev cdev; |
125 | struct mutex lock; |
126 | bool broken; |
127 | bool dead; |
128 | bool async_ffdc; |
129 | bool timed_out; |
130 | u32 timeout_in_cmd_ms; |
131 | u32 timeout_start_rsp_ms; |
132 | }; |
133 | |
134 | struct sbefifo_user { |
135 | struct sbefifo *sbefifo; |
136 | struct mutex file_lock; |
137 | void *cmd_page; |
138 | void *pending_cmd; |
139 | size_t pending_len; |
140 | u32 cmd_timeout_ms; |
141 | u32 read_timeout_ms; |
142 | }; |
143 | |
144 | static DEFINE_MUTEX(sbefifo_ffdc_mutex); |
145 | |
146 | static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, |
147 | char *buf) |
148 | { |
149 | struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev); |
150 | |
151 | return sysfs_emit(buf, fmt: "%d\n" , sbefifo->timed_out ? 1 : 0); |
152 | } |
153 | static DEVICE_ATTR_RO(timeout); |
154 | |
155 | static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc, |
156 | size_t ffdc_sz, bool internal) |
157 | { |
158 | int pack = 0; |
159 | #define FFDC_LSIZE 60 |
160 | static char ffdc_line[FFDC_LSIZE]; |
161 | char *p = ffdc_line; |
162 | |
163 | while (ffdc_sz) { |
164 | u32 w0, w1, w2, i; |
165 | if (ffdc_sz < 3) { |
166 | dev_err(dev, "SBE invalid FFDC package size %zd\n" , ffdc_sz); |
167 | return; |
168 | } |
169 | w0 = be32_to_cpu(*(ffdc++)); |
170 | w1 = be32_to_cpu(*(ffdc++)); |
171 | w2 = be32_to_cpu(*(ffdc++)); |
172 | ffdc_sz -= 3; |
173 | if ((w0 >> 16) != 0xFFDC) { |
174 | dev_err(dev, "SBE invalid FFDC package signature %08x %08x %08x\n" , |
175 | w0, w1, w2); |
176 | break; |
177 | } |
178 | w0 &= 0xffff; |
179 | if (w0 > ffdc_sz) { |
180 | dev_err(dev, "SBE FFDC package len %d words but only %zd remaining\n" , |
181 | w0, ffdc_sz); |
182 | w0 = ffdc_sz; |
183 | break; |
184 | } |
185 | if (internal) { |
186 | dev_warn(dev, "+---- SBE FFDC package %d for async err -----+\n" , |
187 | pack++); |
188 | } else { |
189 | dev_warn(dev, "+---- SBE FFDC package %d for cmd %02x:%02x -----+\n" , |
190 | pack++, (w1 >> 8) & 0xff, w1 & 0xff); |
191 | } |
192 | dev_warn(dev, "| Response code: %08x |\n" , w2); |
193 | dev_warn(dev, "|-------------------------------------------|\n" ); |
194 | for (i = 0; i < w0; i++) { |
195 | if ((i & 3) == 0) { |
196 | p = ffdc_line; |
197 | p += sprintf(buf: p, fmt: "| %04x:" , i << 4); |
198 | } |
199 | p += sprintf(buf: p, fmt: " %08x" , be32_to_cpu(*(ffdc++))); |
200 | ffdc_sz--; |
201 | if ((i & 3) == 3 || i == (w0 - 1)) { |
202 | while ((i & 3) < 3) { |
203 | p += sprintf(buf: p, fmt: " " ); |
204 | i++; |
205 | } |
206 | dev_warn(dev, "%s |\n" , ffdc_line); |
207 | } |
208 | } |
209 | dev_warn(dev, "+-------------------------------------------+\n" ); |
210 | } |
211 | } |
212 | |
213 | static void sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc, |
214 | size_t ffdc_sz, bool internal) |
215 | { |
216 | mutex_lock(&sbefifo_ffdc_mutex); |
217 | __sbefifo_dump_ffdc(dev, ffdc, ffdc_sz, internal); |
218 | mutex_unlock(lock: &sbefifo_ffdc_mutex); |
219 | } |
220 | |
221 | int sbefifo_parse_status(struct device *dev, u16 cmd, __be32 *response, |
222 | size_t resp_len, size_t *data_len) |
223 | { |
224 | u32 dh, s0, s1; |
225 | size_t ffdc_sz; |
226 | |
227 | if (resp_len < 3) { |
228 | pr_debug("sbefifo: cmd %04x, response too small: %zd\n" , |
229 | cmd, resp_len); |
230 | return -ENXIO; |
231 | } |
232 | dh = be32_to_cpu(response[resp_len - 1]); |
233 | if (dh > resp_len || dh < 3) { |
234 | dev_err(dev, "SBE cmd %02x:%02x status offset out of range: %d/%zd\n" , |
235 | cmd >> 8, cmd & 0xff, dh, resp_len); |
236 | return -ENXIO; |
237 | } |
238 | s0 = be32_to_cpu(response[resp_len - dh]); |
239 | s1 = be32_to_cpu(response[resp_len - dh + 1]); |
240 | if (((s0 >> 16) != 0xC0DE) || ((s0 & 0xffff) != cmd)) { |
241 | dev_err(dev, "SBE cmd %02x:%02x, status signature invalid: 0x%08x 0x%08x\n" , |
242 | cmd >> 8, cmd & 0xff, s0, s1); |
243 | return -ENXIO; |
244 | } |
245 | if (s1 != 0) { |
246 | ffdc_sz = dh - 3; |
247 | dev_warn(dev, "SBE error cmd %02x:%02x status=%04x:%04x\n" , |
248 | cmd >> 8, cmd & 0xff, s1 >> 16, s1 & 0xffff); |
249 | if (ffdc_sz) |
250 | sbefifo_dump_ffdc(dev, ffdc: &response[resp_len - dh + 2], |
251 | ffdc_sz, internal: false); |
252 | } |
253 | if (data_len) |
254 | *data_len = resp_len - dh; |
255 | |
256 | /* |
257 | * Primary status don't have the top bit set, so can't be confused with |
258 | * Linux negative error codes, so return the status word whole. |
259 | */ |
260 | return s1; |
261 | } |
262 | EXPORT_SYMBOL_GPL(sbefifo_parse_status); |
263 | |
264 | static int sbefifo_regr(struct sbefifo *sbefifo, int reg, u32 *word) |
265 | { |
266 | __be32 raw_word; |
267 | int rc; |
268 | |
269 | rc = fsi_device_read(dev: sbefifo->fsi_dev, addr: reg, val: &raw_word, |
270 | size: sizeof(raw_word)); |
271 | if (rc) |
272 | return rc; |
273 | |
274 | *word = be32_to_cpu(raw_word); |
275 | |
276 | return 0; |
277 | } |
278 | |
279 | static int sbefifo_regw(struct sbefifo *sbefifo, int reg, u32 word) |
280 | { |
281 | __be32 raw_word = cpu_to_be32(word); |
282 | |
283 | return fsi_device_write(dev: sbefifo->fsi_dev, addr: reg, val: &raw_word, |
284 | size: sizeof(raw_word)); |
285 | } |
286 | |
287 | static int sbefifo_check_sbe_state(struct sbefifo *sbefifo) |
288 | { |
289 | __be32 raw_word; |
290 | u32 sbm; |
291 | int rc; |
292 | |
293 | rc = fsi_slave_read(slave: sbefifo->fsi_dev->slave, CFAM_GP_MBOX_SBM_ADDR, |
294 | val: &raw_word, size: sizeof(raw_word)); |
295 | if (rc) |
296 | return rc; |
297 | sbm = be32_to_cpu(raw_word); |
298 | |
299 | /* SBE booted at all ? */ |
300 | if (!(sbm & CFAM_SBM_SBE_BOOTED)) |
301 | return -ESHUTDOWN; |
302 | |
303 | /* Check its state */ |
304 | switch ((sbm & CFAM_SBM_SBE_STATE_MASK) >> CFAM_SBM_SBE_STATE_SHIFT) { |
305 | case SBE_STATE_UNKNOWN: |
306 | return -ESHUTDOWN; |
307 | case SBE_STATE_DMT: |
308 | return -EBUSY; |
309 | case SBE_STATE_IPLING: |
310 | case SBE_STATE_ISTEP: |
311 | case SBE_STATE_MPIPL: |
312 | case SBE_STATE_RUNTIME: |
313 | case SBE_STATE_DUMP: /* Not sure about that one */ |
314 | break; |
315 | case SBE_STATE_FAILURE: |
316 | case SBE_STATE_QUIESCE: |
317 | return -ESHUTDOWN; |
318 | } |
319 | |
320 | /* Is there async FFDC available ? Remember it */ |
321 | if (sbm & CFAM_SBM_SBE_ASYNC_FFDC) |
322 | sbefifo->async_ffdc = true; |
323 | |
324 | return 0; |
325 | } |
326 | |
327 | /* Don't flip endianness of data to/from FIFO, just pass through. */ |
328 | static int sbefifo_down_read(struct sbefifo *sbefifo, __be32 *word) |
329 | { |
330 | return fsi_device_read(dev: sbefifo->fsi_dev, SBEFIFO_DOWN, val: word, |
331 | size: sizeof(*word)); |
332 | } |
333 | |
334 | static int sbefifo_up_write(struct sbefifo *sbefifo, __be32 word) |
335 | { |
336 | return fsi_device_write(dev: sbefifo->fsi_dev, SBEFIFO_UP, val: &word, |
337 | size: sizeof(word)); |
338 | } |
339 | |
340 | static int sbefifo_request_reset(struct sbefifo *sbefifo) |
341 | { |
342 | struct device *dev = &sbefifo->fsi_dev->dev; |
343 | unsigned long end_time; |
344 | u32 status; |
345 | int rc; |
346 | |
347 | dev_dbg(dev, "Requesting FIFO reset\n" ); |
348 | |
349 | /* Mark broken first, will be cleared if reset succeeds */ |
350 | sbefifo->broken = true; |
351 | |
352 | /* Send reset request */ |
353 | rc = sbefifo_regw(sbefifo, SBEFIFO_UP | SBEFIFO_REQ_RESET, word: 1); |
354 | if (rc) { |
355 | dev_err(dev, "Sending reset request failed, rc=%d\n" , rc); |
356 | return rc; |
357 | } |
358 | |
359 | /* Wait for it to complete */ |
360 | end_time = jiffies + msecs_to_jiffies(SBEFIFO_RESET_TIMEOUT); |
361 | while (!time_after(jiffies, end_time)) { |
362 | rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, word: &status); |
363 | if (rc) { |
364 | dev_err(dev, "Failed to read UP fifo status during reset" |
365 | " , rc=%d\n" , rc); |
366 | return rc; |
367 | } |
368 | |
369 | if (!(status & SBEFIFO_STS_RESET_REQ)) { |
370 | dev_dbg(dev, "FIFO reset done\n" ); |
371 | sbefifo->broken = false; |
372 | return 0; |
373 | } |
374 | |
375 | cond_resched(); |
376 | } |
377 | dev_err(dev, "FIFO reset timed out\n" ); |
378 | |
379 | return -ETIMEDOUT; |
380 | } |
381 | |
382 | static int sbefifo_cleanup_hw(struct sbefifo *sbefifo) |
383 | { |
384 | struct device *dev = &sbefifo->fsi_dev->dev; |
385 | u32 up_status, down_status; |
386 | bool need_reset = false; |
387 | int rc; |
388 | |
389 | rc = sbefifo_check_sbe_state(sbefifo); |
390 | if (rc) { |
391 | dev_dbg(dev, "SBE state=%d\n" , rc); |
392 | return rc; |
393 | } |
394 | |
395 | /* If broken, we don't need to look at status, go straight to reset */ |
396 | if (sbefifo->broken) |
397 | goto do_reset; |
398 | |
399 | rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, word: &up_status); |
400 | if (rc) { |
401 | dev_err(dev, "Cleanup: Reading UP status failed, rc=%d\n" , rc); |
402 | |
403 | /* Will try reset again on next attempt at using it */ |
404 | sbefifo->broken = true; |
405 | return rc; |
406 | } |
407 | |
408 | rc = sbefifo_regr(sbefifo, SBEFIFO_DOWN | SBEFIFO_STS, word: &down_status); |
409 | if (rc) { |
410 | dev_err(dev, "Cleanup: Reading DOWN status failed, rc=%d\n" , rc); |
411 | |
412 | /* Will try reset again on next attempt at using it */ |
413 | sbefifo->broken = true; |
414 | return rc; |
415 | } |
416 | |
417 | /* The FIFO already contains a reset request from the SBE ? */ |
418 | if (down_status & SBEFIFO_STS_RESET_REQ) { |
419 | dev_info(dev, "Cleanup: FIFO reset request set, resetting\n" ); |
420 | rc = sbefifo_regw(sbefifo, SBEFIFO_DOWN, SBEFIFO_PERFORM_RESET); |
421 | if (rc) { |
422 | sbefifo->broken = true; |
423 | dev_err(dev, "Cleanup: Reset reg write failed, rc=%d\n" , rc); |
424 | return rc; |
425 | } |
426 | sbefifo->broken = false; |
427 | return 0; |
428 | } |
429 | |
430 | /* Parity error on either FIFO ? */ |
431 | if ((up_status | down_status) & SBEFIFO_STS_PARITY_ERR) |
432 | need_reset = true; |
433 | |
434 | /* Either FIFO not empty ? */ |
435 | if (!((up_status & down_status) & SBEFIFO_STS_EMPTY)) |
436 | need_reset = true; |
437 | |
438 | if (!need_reset) |
439 | return 0; |
440 | |
441 | dev_info(dev, "Cleanup: FIFO not clean (up=0x%08x down=0x%08x)\n" , |
442 | up_status, down_status); |
443 | |
444 | do_reset: |
445 | |
446 | /* Mark broken, will be cleared if/when reset succeeds */ |
447 | return sbefifo_request_reset(sbefifo); |
448 | } |
449 | |
450 | static int sbefifo_wait(struct sbefifo *sbefifo, bool up, |
451 | u32 *status, unsigned long timeout) |
452 | { |
453 | struct device *dev = &sbefifo->fsi_dev->dev; |
454 | unsigned long end_time; |
455 | bool ready = false; |
456 | u32 addr, sts = 0; |
457 | int rc; |
458 | |
459 | dev_vdbg(dev, "Wait on %s fifo...\n" , up ? "up" : "down" ); |
460 | |
461 | addr = (up ? SBEFIFO_UP : SBEFIFO_DOWN) | SBEFIFO_STS; |
462 | |
463 | end_time = jiffies + timeout; |
464 | while (!time_after(jiffies, end_time)) { |
465 | cond_resched(); |
466 | rc = sbefifo_regr(sbefifo, reg: addr, word: &sts); |
467 | if (rc < 0) { |
468 | dev_err(dev, "FSI error %d reading status register\n" , rc); |
469 | return rc; |
470 | } |
471 | if (!up && sbefifo_parity_err(sts)) { |
472 | dev_err(dev, "Parity error in DOWN FIFO\n" ); |
473 | return -ENXIO; |
474 | } |
475 | ready = !(up ? sbefifo_full(sts) : sbefifo_empty(sts)); |
476 | if (ready) |
477 | break; |
478 | } |
479 | if (!ready) { |
480 | sysfs_notify(kobj: &sbefifo->dev.kobj, NULL, attr: dev_attr_timeout.attr.name); |
481 | sbefifo->timed_out = true; |
482 | dev_err(dev, "%s FIFO Timeout (%u ms)! status=%08x\n" , |
483 | up ? "UP" : "DOWN" , jiffies_to_msecs(timeout), sts); |
484 | return -ETIMEDOUT; |
485 | } |
486 | dev_vdbg(dev, "End of wait status: %08x\n" , sts); |
487 | |
488 | sbefifo->timed_out = false; |
489 | *status = sts; |
490 | |
491 | return 0; |
492 | } |
493 | |
494 | static int sbefifo_send_command(struct sbefifo *sbefifo, |
495 | const __be32 *command, size_t cmd_len) |
496 | { |
497 | struct device *dev = &sbefifo->fsi_dev->dev; |
498 | size_t len, chunk, vacant = 0, remaining = cmd_len; |
499 | unsigned long timeout; |
500 | u32 status; |
501 | int rc; |
502 | |
503 | dev_dbg(dev, "sending command (%zd words, cmd=%04x)\n" , |
504 | cmd_len, be32_to_cpu(command[1])); |
505 | |
506 | /* As long as there's something to send */ |
507 | timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_START_CMD); |
508 | while (remaining) { |
509 | /* Wait for room in the FIFO */ |
510 | rc = sbefifo_wait(sbefifo, up: true, status: &status, timeout); |
511 | if (rc < 0) |
512 | return rc; |
513 | timeout = msecs_to_jiffies(m: sbefifo->timeout_in_cmd_ms); |
514 | |
515 | vacant = sbefifo_vacant(status); |
516 | len = chunk = min(vacant, remaining); |
517 | |
518 | dev_vdbg(dev, " status=%08x vacant=%zd chunk=%zd\n" , |
519 | status, vacant, chunk); |
520 | |
521 | /* Write as much as we can */ |
522 | while (len--) { |
523 | rc = sbefifo_up_write(sbefifo, word: *(command++)); |
524 | if (rc) { |
525 | dev_err(dev, "FSI error %d writing UP FIFO\n" , rc); |
526 | return rc; |
527 | } |
528 | } |
529 | remaining -= chunk; |
530 | vacant -= chunk; |
531 | } |
532 | |
533 | /* If there's no room left, wait for some to write EOT */ |
534 | if (!vacant) { |
535 | rc = sbefifo_wait(sbefifo, up: true, status: &status, timeout); |
536 | if (rc) |
537 | return rc; |
538 | } |
539 | |
540 | /* Send an EOT */ |
541 | rc = sbefifo_regw(sbefifo, SBEFIFO_UP | SBEFIFO_EOT_RAISE, word: 0); |
542 | if (rc) |
543 | dev_err(dev, "FSI error %d writing EOT\n" , rc); |
544 | return rc; |
545 | } |
546 | |
547 | static int sbefifo_read_response(struct sbefifo *sbefifo, struct iov_iter *response) |
548 | { |
549 | struct device *dev = &sbefifo->fsi_dev->dev; |
550 | u32 status, eot_set; |
551 | unsigned long timeout; |
552 | bool overflow = false; |
553 | __be32 data; |
554 | size_t len; |
555 | int rc; |
556 | |
557 | dev_dbg(dev, "reading response, buflen = %zd\n" , iov_iter_count(response)); |
558 | |
559 | timeout = msecs_to_jiffies(m: sbefifo->timeout_start_rsp_ms); |
560 | for (;;) { |
561 | /* Grab FIFO status (this will handle parity errors) */ |
562 | rc = sbefifo_wait(sbefifo, up: false, status: &status, timeout); |
563 | if (rc < 0) { |
564 | dev_dbg(dev, "timeout waiting (%u ms)\n" , jiffies_to_msecs(timeout)); |
565 | return rc; |
566 | } |
567 | timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_RSP); |
568 | |
569 | /* Decode status */ |
570 | len = sbefifo_populated(status); |
571 | eot_set = sbefifo_eot_set(status); |
572 | |
573 | dev_dbg(dev, " chunk size %zd eot_set=0x%x\n" , len, eot_set); |
574 | |
575 | /* Go through the chunk */ |
576 | while(len--) { |
577 | /* Read the data */ |
578 | rc = sbefifo_down_read(sbefifo, word: &data); |
579 | if (rc < 0) |
580 | return rc; |
581 | |
582 | /* Was it an EOT ? */ |
583 | if (eot_set & 0x80) { |
584 | /* |
585 | * There should be nothing else in the FIFO, |
586 | * if there is, mark broken, this will force |
587 | * a reset on next use, but don't fail the |
588 | * command. |
589 | */ |
590 | if (len) { |
591 | dev_warn(dev, "FIFO read hit" |
592 | " EOT with still %zd data\n" , |
593 | len); |
594 | sbefifo->broken = true; |
595 | } |
596 | |
597 | /* We are done */ |
598 | rc = sbefifo_regw(sbefifo, |
599 | SBEFIFO_DOWN | SBEFIFO_EOT_ACK, word: 0); |
600 | |
601 | /* |
602 | * If that write fail, still complete the request but mark |
603 | * the fifo as broken for subsequent reset (not much else |
604 | * we can do here). |
605 | */ |
606 | if (rc) { |
607 | dev_err(dev, "FSI error %d ack'ing EOT\n" , rc); |
608 | sbefifo->broken = true; |
609 | } |
610 | |
611 | /* Tell whether we overflowed */ |
612 | return overflow ? -EOVERFLOW : 0; |
613 | } |
614 | |
615 | /* Store it if there is room */ |
616 | if (iov_iter_count(i: response) >= sizeof(__be32)) { |
617 | if (copy_to_iter(addr: &data, bytes: sizeof(__be32), i: response) < sizeof(__be32)) |
618 | return -EFAULT; |
619 | } else { |
620 | dev_vdbg(dev, "Response overflowed !\n" ); |
621 | |
622 | overflow = true; |
623 | } |
624 | |
625 | /* Next EOT bit */ |
626 | eot_set <<= 1; |
627 | } |
628 | } |
629 | /* Shouldn't happen */ |
630 | return -EIO; |
631 | } |
632 | |
633 | static int sbefifo_do_command(struct sbefifo *sbefifo, |
634 | const __be32 *command, size_t cmd_len, |
635 | struct iov_iter *response) |
636 | { |
637 | /* Try sending the command */ |
638 | int rc = sbefifo_send_command(sbefifo, command, cmd_len); |
639 | if (rc) |
640 | return rc; |
641 | |
642 | /* Now, get the response */ |
643 | return sbefifo_read_response(sbefifo, response); |
644 | } |
645 | |
646 | static void sbefifo_collect_async_ffdc(struct sbefifo *sbefifo) |
647 | { |
648 | struct device *dev = &sbefifo->fsi_dev->dev; |
649 | struct iov_iter ffdc_iter; |
650 | struct kvec ffdc_iov; |
651 | __be32 *ffdc; |
652 | size_t ffdc_sz; |
653 | __be32 cmd[2]; |
654 | int rc; |
655 | |
656 | sbefifo->async_ffdc = false; |
657 | ffdc = vmalloc(SBEFIFO_MAX_FFDC_SIZE); |
658 | if (!ffdc) { |
659 | dev_err(dev, "Failed to allocate SBE FFDC buffer\n" ); |
660 | return; |
661 | } |
662 | ffdc_iov.iov_base = ffdc; |
663 | ffdc_iov.iov_len = SBEFIFO_MAX_FFDC_SIZE; |
664 | iov_iter_kvec(i: &ffdc_iter, ITER_DEST, kvec: &ffdc_iov, nr_segs: 1, SBEFIFO_MAX_FFDC_SIZE); |
665 | cmd[0] = cpu_to_be32(2); |
666 | cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_SBE_FFDC); |
667 | rc = sbefifo_do_command(sbefifo, command: cmd, cmd_len: 2, response: &ffdc_iter); |
668 | if (rc != 0) { |
669 | dev_err(dev, "Error %d retrieving SBE FFDC\n" , rc); |
670 | goto bail; |
671 | } |
672 | ffdc_sz = SBEFIFO_MAX_FFDC_SIZE - iov_iter_count(i: &ffdc_iter); |
673 | ffdc_sz /= sizeof(__be32); |
674 | rc = sbefifo_parse_status(dev, SBEFIFO_CMD_GET_SBE_FFDC, ffdc, |
675 | ffdc_sz, &ffdc_sz); |
676 | if (rc != 0) { |
677 | dev_err(dev, "Error %d decoding SBE FFDC\n" , rc); |
678 | goto bail; |
679 | } |
680 | if (ffdc_sz > 0) |
681 | sbefifo_dump_ffdc(dev, ffdc, ffdc_sz, internal: true); |
682 | bail: |
683 | vfree(addr: ffdc); |
684 | |
685 | } |
686 | |
687 | static int __sbefifo_submit(struct sbefifo *sbefifo, |
688 | const __be32 *command, size_t cmd_len, |
689 | struct iov_iter *response) |
690 | { |
691 | struct device *dev = &sbefifo->fsi_dev->dev; |
692 | int rc; |
693 | |
694 | if (sbefifo->dead) |
695 | return -ENODEV; |
696 | |
697 | if (cmd_len < 2 || be32_to_cpu(command[0]) != cmd_len) { |
698 | dev_vdbg(dev, "Invalid command len %zd (header: %d)\n" , |
699 | cmd_len, be32_to_cpu(command[0])); |
700 | return -EINVAL; |
701 | } |
702 | |
703 | /* First ensure the HW is in a clean state */ |
704 | rc = sbefifo_cleanup_hw(sbefifo); |
705 | if (rc) |
706 | return rc; |
707 | |
708 | /* Look for async FFDC first if any */ |
709 | if (sbefifo->async_ffdc) |
710 | sbefifo_collect_async_ffdc(sbefifo); |
711 | |
712 | rc = sbefifo_do_command(sbefifo, command, cmd_len, response); |
713 | if (rc != 0 && rc != -EOVERFLOW) |
714 | goto fail; |
715 | return rc; |
716 | fail: |
717 | /* |
718 | * On failure, attempt a reset. Ignore the result, it will mark |
719 | * the fifo broken if the reset fails |
720 | */ |
721 | sbefifo_request_reset(sbefifo); |
722 | |
723 | /* Return original error */ |
724 | return rc; |
725 | } |
726 | |
727 | /** |
728 | * sbefifo_submit() - Submit and SBE fifo command and receive response |
729 | * @dev: The sbefifo device |
730 | * @command: The raw command data |
731 | * @cmd_len: The command size (in 32-bit words) |
732 | * @response: The output response buffer |
733 | * @resp_len: In: Response buffer size, Out: Response size |
734 | * |
735 | * This will perform the entire operation. If the response buffer |
736 | * overflows, returns -EOVERFLOW |
737 | */ |
738 | int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len, |
739 | __be32 *response, size_t *resp_len) |
740 | { |
741 | struct sbefifo *sbefifo; |
742 | struct iov_iter resp_iter; |
743 | struct kvec resp_iov; |
744 | size_t rbytes; |
745 | int rc; |
746 | |
747 | if (!dev) |
748 | return -ENODEV; |
749 | sbefifo = dev_get_drvdata(dev); |
750 | if (!sbefifo) |
751 | return -ENODEV; |
752 | if (WARN_ON_ONCE(sbefifo->magic != SBEFIFO_MAGIC)) |
753 | return -ENODEV; |
754 | if (!resp_len || !command || !response) |
755 | return -EINVAL; |
756 | |
757 | /* Prepare iov iterator */ |
758 | rbytes = (*resp_len) * sizeof(__be32); |
759 | resp_iov.iov_base = response; |
760 | resp_iov.iov_len = rbytes; |
761 | iov_iter_kvec(i: &resp_iter, ITER_DEST, kvec: &resp_iov, nr_segs: 1, count: rbytes); |
762 | |
763 | /* Perform the command */ |
764 | rc = mutex_lock_interruptible(&sbefifo->lock); |
765 | if (rc) |
766 | return rc; |
767 | rc = __sbefifo_submit(sbefifo, command, cmd_len, response: &resp_iter); |
768 | mutex_unlock(lock: &sbefifo->lock); |
769 | |
770 | /* Extract the response length */ |
771 | rbytes -= iov_iter_count(i: &resp_iter); |
772 | *resp_len = rbytes / sizeof(__be32); |
773 | |
774 | return rc; |
775 | } |
776 | EXPORT_SYMBOL_GPL(sbefifo_submit); |
777 | |
778 | /* |
779 | * Char device interface |
780 | */ |
781 | |
782 | static void sbefifo_release_command(struct sbefifo_user *user) |
783 | { |
784 | if (is_vmalloc_addr(x: user->pending_cmd)) |
785 | vfree(addr: user->pending_cmd); |
786 | user->pending_cmd = NULL; |
787 | user->pending_len = 0; |
788 | } |
789 | |
790 | static int sbefifo_user_open(struct inode *inode, struct file *file) |
791 | { |
792 | struct sbefifo *sbefifo = container_of(inode->i_cdev, struct sbefifo, cdev); |
793 | struct sbefifo_user *user; |
794 | |
795 | user = kzalloc(size: sizeof(struct sbefifo_user), GFP_KERNEL); |
796 | if (!user) |
797 | return -ENOMEM; |
798 | |
799 | file->private_data = user; |
800 | user->sbefifo = sbefifo; |
801 | user->cmd_page = (void *)__get_free_page(GFP_KERNEL); |
802 | if (!user->cmd_page) { |
803 | kfree(objp: user); |
804 | return -ENOMEM; |
805 | } |
806 | mutex_init(&user->file_lock); |
807 | user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD; |
808 | user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP; |
809 | |
810 | return 0; |
811 | } |
812 | |
813 | static ssize_t sbefifo_user_read(struct file *file, char __user *buf, |
814 | size_t len, loff_t *offset) |
815 | { |
816 | struct sbefifo_user *user = file->private_data; |
817 | struct sbefifo *sbefifo; |
818 | struct iov_iter resp_iter; |
819 | struct iovec resp_iov; |
820 | size_t cmd_len; |
821 | int rc; |
822 | |
823 | if (!user) |
824 | return -EINVAL; |
825 | sbefifo = user->sbefifo; |
826 | if (len & 3) |
827 | return -EINVAL; |
828 | |
829 | mutex_lock(&user->file_lock); |
830 | |
831 | /* Cronus relies on -EAGAIN after a short read */ |
832 | if (user->pending_len == 0) { |
833 | rc = -EAGAIN; |
834 | goto bail; |
835 | } |
836 | if (user->pending_len < 8) { |
837 | rc = -EINVAL; |
838 | goto bail; |
839 | } |
840 | cmd_len = user->pending_len >> 2; |
841 | |
842 | /* Prepare iov iterator */ |
843 | resp_iov.iov_base = buf; |
844 | resp_iov.iov_len = len; |
845 | iov_iter_init(i: &resp_iter, ITER_DEST, iov: &resp_iov, nr_segs: 1, count: len); |
846 | |
847 | /* Perform the command */ |
848 | rc = mutex_lock_interruptible(&sbefifo->lock); |
849 | if (rc) |
850 | goto bail; |
851 | sbefifo->timeout_in_cmd_ms = user->cmd_timeout_ms; |
852 | sbefifo->timeout_start_rsp_ms = user->read_timeout_ms; |
853 | rc = __sbefifo_submit(sbefifo, command: user->pending_cmd, cmd_len, response: &resp_iter); |
854 | sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP; |
855 | sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD; |
856 | mutex_unlock(lock: &sbefifo->lock); |
857 | if (rc < 0) |
858 | goto bail; |
859 | |
860 | /* Extract the response length */ |
861 | rc = len - iov_iter_count(i: &resp_iter); |
862 | bail: |
863 | sbefifo_release_command(user); |
864 | mutex_unlock(lock: &user->file_lock); |
865 | return rc; |
866 | } |
867 | |
868 | static ssize_t sbefifo_user_write(struct file *file, const char __user *buf, |
869 | size_t len, loff_t *offset) |
870 | { |
871 | struct sbefifo_user *user = file->private_data; |
872 | struct sbefifo *sbefifo; |
873 | int rc = len; |
874 | |
875 | if (!user) |
876 | return -EINVAL; |
877 | sbefifo = user->sbefifo; |
878 | if (len > SBEFIFO_MAX_USER_CMD_LEN) |
879 | return -EINVAL; |
880 | if (len & 3) |
881 | return -EINVAL; |
882 | |
883 | mutex_lock(&user->file_lock); |
884 | |
885 | /* Can we use the pre-allocate buffer ? If not, allocate */ |
886 | if (len <= PAGE_SIZE) |
887 | user->pending_cmd = user->cmd_page; |
888 | else |
889 | user->pending_cmd = vmalloc(size: len); |
890 | if (!user->pending_cmd) { |
891 | rc = -ENOMEM; |
892 | goto bail; |
893 | } |
894 | |
895 | /* Copy the command into the staging buffer */ |
896 | if (copy_from_user(to: user->pending_cmd, from: buf, n: len)) { |
897 | rc = -EFAULT; |
898 | goto bail; |
899 | } |
900 | |
901 | /* Check for the magic reset command */ |
902 | if (len == 4 && be32_to_cpu(*(__be32 *)user->pending_cmd) == |
903 | SBEFIFO_RESET_MAGIC) { |
904 | |
905 | /* Clear out any pending command */ |
906 | user->pending_len = 0; |
907 | |
908 | /* Trigger reset request */ |
909 | rc = mutex_lock_interruptible(&sbefifo->lock); |
910 | if (rc) |
911 | goto bail; |
912 | rc = sbefifo_request_reset(sbefifo: user->sbefifo); |
913 | mutex_unlock(lock: &sbefifo->lock); |
914 | if (rc == 0) |
915 | rc = 4; |
916 | goto bail; |
917 | } |
918 | |
919 | /* Update the staging buffer size */ |
920 | user->pending_len = len; |
921 | bail: |
922 | if (!user->pending_len) |
923 | sbefifo_release_command(user); |
924 | |
925 | mutex_unlock(lock: &user->file_lock); |
926 | |
927 | /* And that's it, we'll issue the command on a read */ |
928 | return rc; |
929 | } |
930 | |
931 | static int sbefifo_user_release(struct inode *inode, struct file *file) |
932 | { |
933 | struct sbefifo_user *user = file->private_data; |
934 | |
935 | if (!user) |
936 | return -EINVAL; |
937 | |
938 | sbefifo_release_command(user); |
939 | free_page((unsigned long)user->cmd_page); |
940 | kfree(objp: user); |
941 | |
942 | return 0; |
943 | } |
944 | |
945 | static int sbefifo_cmd_timeout(struct sbefifo_user *user, void __user *argp) |
946 | { |
947 | struct device *dev = &user->sbefifo->dev; |
948 | u32 timeout; |
949 | |
950 | if (get_user(timeout, (__u32 __user *)argp)) |
951 | return -EFAULT; |
952 | |
953 | if (timeout == 0) { |
954 | user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD; |
955 | dev_dbg(dev, "Command timeout reset to %us\n" , user->cmd_timeout_ms / 1000); |
956 | return 0; |
957 | } |
958 | |
959 | user->cmd_timeout_ms = timeout * 1000; /* user timeout is in sec */ |
960 | dev_dbg(dev, "Command timeout set to %us\n" , timeout); |
961 | return 0; |
962 | } |
963 | |
964 | static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp) |
965 | { |
966 | struct device *dev = &user->sbefifo->dev; |
967 | u32 timeout; |
968 | |
969 | if (get_user(timeout, (__u32 __user *)argp)) |
970 | return -EFAULT; |
971 | |
972 | if (timeout == 0) { |
973 | user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP; |
974 | dev_dbg(dev, "Timeout reset to %us\n" , user->read_timeout_ms / 1000); |
975 | return 0; |
976 | } |
977 | |
978 | user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */ |
979 | dev_dbg(dev, "Timeout set to %us\n" , timeout); |
980 | return 0; |
981 | } |
982 | |
983 | static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
984 | { |
985 | struct sbefifo_user *user = file->private_data; |
986 | int rc = -ENOTTY; |
987 | |
988 | if (!user) |
989 | return -EINVAL; |
990 | |
991 | mutex_lock(&user->file_lock); |
992 | switch (cmd) { |
993 | case FSI_SBEFIFO_CMD_TIMEOUT_SECONDS: |
994 | rc = sbefifo_cmd_timeout(user, argp: (void __user *)arg); |
995 | break; |
996 | case FSI_SBEFIFO_READ_TIMEOUT_SECONDS: |
997 | rc = sbefifo_read_timeout(user, argp: (void __user *)arg); |
998 | break; |
999 | } |
1000 | mutex_unlock(lock: &user->file_lock); |
1001 | return rc; |
1002 | } |
1003 | |
1004 | static const struct file_operations sbefifo_fops = { |
1005 | .owner = THIS_MODULE, |
1006 | .open = sbefifo_user_open, |
1007 | .read = sbefifo_user_read, |
1008 | .write = sbefifo_user_write, |
1009 | .release = sbefifo_user_release, |
1010 | .unlocked_ioctl = sbefifo_user_ioctl, |
1011 | }; |
1012 | |
1013 | static void sbefifo_free(struct device *dev) |
1014 | { |
1015 | struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev); |
1016 | |
1017 | put_device(dev: &sbefifo->fsi_dev->dev); |
1018 | kfree(objp: sbefifo); |
1019 | } |
1020 | |
1021 | /* |
1022 | * Probe/remove |
1023 | */ |
1024 | |
1025 | static int sbefifo_probe(struct device *dev) |
1026 | { |
1027 | struct fsi_device *fsi_dev = to_fsi_dev(dev); |
1028 | struct sbefifo *sbefifo; |
1029 | struct device_node *np; |
1030 | struct platform_device *child; |
1031 | char child_name[32]; |
1032 | int rc, didx, child_idx = 0; |
1033 | |
1034 | dev_dbg(dev, "Found sbefifo device\n" ); |
1035 | |
1036 | sbefifo = kzalloc(size: sizeof(*sbefifo), GFP_KERNEL); |
1037 | if (!sbefifo) |
1038 | return -ENOMEM; |
1039 | |
1040 | /* Grab a reference to the device (parent of our cdev), we'll drop it later */ |
1041 | if (!get_device(dev)) { |
1042 | kfree(objp: sbefifo); |
1043 | return -ENODEV; |
1044 | } |
1045 | |
1046 | sbefifo->magic = SBEFIFO_MAGIC; |
1047 | sbefifo->fsi_dev = fsi_dev; |
1048 | dev_set_drvdata(dev, data: sbefifo); |
1049 | mutex_init(&sbefifo->lock); |
1050 | sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD; |
1051 | sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP; |
1052 | |
1053 | /* Create chardev for userspace access */ |
1054 | sbefifo->dev.type = &fsi_cdev_type; |
1055 | sbefifo->dev.parent = dev; |
1056 | sbefifo->dev.release = sbefifo_free; |
1057 | device_initialize(dev: &sbefifo->dev); |
1058 | |
1059 | /* Allocate a minor in the FSI space */ |
1060 | rc = fsi_get_new_minor(fdev: fsi_dev, type: fsi_dev_sbefifo, out_dev: &sbefifo->dev.devt, out_index: &didx); |
1061 | if (rc) |
1062 | goto err; |
1063 | |
1064 | dev_set_name(dev: &sbefifo->dev, name: "sbefifo%d" , didx); |
1065 | cdev_init(&sbefifo->cdev, &sbefifo_fops); |
1066 | rc = cdev_device_add(cdev: &sbefifo->cdev, dev: &sbefifo->dev); |
1067 | if (rc) { |
1068 | dev_err(dev, "Error %d creating char device %s\n" , |
1069 | rc, dev_name(&sbefifo->dev)); |
1070 | goto err_free_minor; |
1071 | } |
1072 | |
1073 | /* Create platform devs for dts child nodes (occ, etc) */ |
1074 | for_each_available_child_of_node(dev->of_node, np) { |
1075 | snprintf(buf: child_name, size: sizeof(child_name), fmt: "%s-dev%d" , |
1076 | dev_name(dev: &sbefifo->dev), child_idx++); |
1077 | child = of_platform_device_create(np, bus_id: child_name, parent: dev); |
1078 | if (!child) |
1079 | dev_warn(dev, "failed to create child %s dev\n" , |
1080 | child_name); |
1081 | } |
1082 | |
1083 | device_create_file(device: &sbefifo->dev, entry: &dev_attr_timeout); |
1084 | |
1085 | return 0; |
1086 | err_free_minor: |
1087 | fsi_free_minor(dev: sbefifo->dev.devt); |
1088 | err: |
1089 | put_device(dev: &sbefifo->dev); |
1090 | return rc; |
1091 | } |
1092 | |
1093 | static int sbefifo_unregister_child(struct device *dev, void *data) |
1094 | { |
1095 | struct platform_device *child = to_platform_device(dev); |
1096 | |
1097 | of_device_unregister(ofdev: child); |
1098 | if (dev->of_node) |
1099 | of_node_clear_flag(n: dev->of_node, OF_POPULATED); |
1100 | |
1101 | return 0; |
1102 | } |
1103 | |
1104 | static int sbefifo_remove(struct device *dev) |
1105 | { |
1106 | struct sbefifo *sbefifo = dev_get_drvdata(dev); |
1107 | |
1108 | dev_dbg(dev, "Removing sbefifo device...\n" ); |
1109 | |
1110 | device_remove_file(dev: &sbefifo->dev, attr: &dev_attr_timeout); |
1111 | |
1112 | mutex_lock(&sbefifo->lock); |
1113 | sbefifo->dead = true; |
1114 | mutex_unlock(lock: &sbefifo->lock); |
1115 | |
1116 | cdev_device_del(cdev: &sbefifo->cdev, dev: &sbefifo->dev); |
1117 | fsi_free_minor(dev: sbefifo->dev.devt); |
1118 | device_for_each_child(dev, NULL, fn: sbefifo_unregister_child); |
1119 | put_device(dev: &sbefifo->dev); |
1120 | |
1121 | return 0; |
1122 | } |
1123 | |
1124 | static const struct fsi_device_id sbefifo_ids[] = { |
1125 | { |
1126 | .engine_type = FSI_ENGID_SBE, |
1127 | .version = FSI_VERSION_ANY, |
1128 | }, |
1129 | { 0 } |
1130 | }; |
1131 | |
1132 | static struct fsi_driver sbefifo_drv = { |
1133 | .id_table = sbefifo_ids, |
1134 | .drv = { |
1135 | .name = DEVICE_NAME, |
1136 | .bus = &fsi_bus_type, |
1137 | .probe = sbefifo_probe, |
1138 | .remove = sbefifo_remove, |
1139 | } |
1140 | }; |
1141 | |
1142 | static int sbefifo_init(void) |
1143 | { |
1144 | return fsi_driver_register(fsi_drv: &sbefifo_drv); |
1145 | } |
1146 | |
1147 | static void sbefifo_exit(void) |
1148 | { |
1149 | fsi_driver_unregister(fsi_drv: &sbefifo_drv); |
1150 | } |
1151 | |
1152 | module_init(sbefifo_init); |
1153 | module_exit(sbefifo_exit); |
1154 | MODULE_LICENSE("GPL" ); |
1155 | MODULE_AUTHOR("Brad Bishop <bradleyb@fuzziesquirrel.com>" ); |
1156 | MODULE_AUTHOR("Eddie James <eajames@linux.vnet.ibm.com>" ); |
1157 | MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>" ); |
1158 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>" ); |
1159 | MODULE_DESCRIPTION("Linux device interface to the POWER Self Boot Engine" ); |
1160 | |