Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide
[linux-2.6-block.git] / drivers / fsi / fsi-scom.c
CommitLineData
6b99076c 1// SPDX-License-Identifier: GPL-2.0-only
680ca6dc
CB
2/*
3 * SCOM FSI Client device driver
4 *
5 * Copyright (C) IBM Corporation 2016
680ca6dc
CB
6 */
7
8#include <linux/fsi.h>
9#include <linux/module.h>
10#include <linux/cdev.h>
11#include <linux/delay.h>
12#include <linux/fs.h>
13#include <linux/uaccess.h>
14#include <linux/slab.h>
680ca6dc 15#include <linux/list.h>
680ca6dc 16
6b293258
BH
17#include <uapi/linux/fsi.h>
18
680ca6dc
CB
19#define FSI_ENGID_SCOM 0x5
20
680ca6dc
CB
21/* SCOM engine register set */
22#define SCOM_DATA0_REG 0x00
23#define SCOM_DATA1_REG 0x04
24#define SCOM_CMD_REG 0x08
f1433044
BH
25#define SCOM_FSI2PIB_RESET_REG 0x18
26#define SCOM_STATUS_REG 0x1C /* Read */
27#define SCOM_PIB_RESET_REG 0x1C /* Write */
680ca6dc 28
f1433044 29/* Command register */
680ca6dc 30#define SCOM_WRITE_CMD 0x80000000
f1433044
BH
31#define SCOM_READ_CMD 0x00000000
32
33/* Status register bits */
34#define SCOM_STATUS_ERR_SUMMARY 0x80000000
35#define SCOM_STATUS_PROTECTION 0x01000000
6b293258 36#define SCOM_STATUS_PARITY 0x04000000
f1433044
BH
37#define SCOM_STATUS_PIB_ABORT 0x00100000
38#define SCOM_STATUS_PIB_RESP_MASK 0x00007000
39#define SCOM_STATUS_PIB_RESP_SHIFT 12
40
41#define SCOM_STATUS_ANY_ERR (SCOM_STATUS_ERR_SUMMARY | \
42 SCOM_STATUS_PROTECTION | \
6b293258 43 SCOM_STATUS_PARITY | \
f1433044
BH
44 SCOM_STATUS_PIB_ABORT | \
45 SCOM_STATUS_PIB_RESP_MASK)
6b293258
BH
46/* SCOM address encodings */
47#define XSCOM_ADDR_IND_FLAG BIT_ULL(63)
48#define XSCOM_ADDR_INF_FORM1 BIT_ULL(60)
49
50/* SCOM indirect stuff */
51#define XSCOM_ADDR_DIRECT_PART 0x7fffffffull
52#define XSCOM_ADDR_INDIRECT_PART 0x000fffff00000000ull
53#define XSCOM_DATA_IND_READ BIT_ULL(63)
54#define XSCOM_DATA_IND_COMPLETE BIT_ULL(31)
55#define XSCOM_DATA_IND_ERR_MASK 0x70000000ull
56#define XSCOM_DATA_IND_ERR_SHIFT 28
57#define XSCOM_DATA_IND_DATA 0x0000ffffull
58#define XSCOM_DATA_IND_FORM1_DATA 0x000fffffffffffffull
59#define XSCOM_ADDR_FORM1_LOW 0x000ffffffffull
60#define XSCOM_ADDR_FORM1_HI 0xfff00000000ull
61#define XSCOM_ADDR_FORM1_HI_SHIFT 20
62
63/* Retries */
64#define SCOM_MAX_RETRIES 100 /* Retries on busy */
65#define SCOM_MAX_IND_RETRIES 10 /* Retries indirect not ready */
680ca6dc
CB
66
67struct scom_device {
68 struct list_head link;
69 struct fsi_device *fsi_dev;
d8f45876
BH
70 struct device dev;
71 struct cdev cdev;
162c3946 72 struct mutex lock;
d8f45876 73 bool dead;
680ca6dc
CB
74};
75
6b293258
BH
76static int __put_scom(struct scom_device *scom_dev, uint64_t value,
77 uint32_t addr, uint32_t *status)
680ca6dc 78{
6b293258 79 __be32 data, raw_status;
680ca6dc 80 int rc;
680ca6dc 81
680ca6dc
CB
82 data = cpu_to_be32((value >> 32) & 0xffffffff);
83 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
84 sizeof(uint32_t));
85 if (rc)
6b293258 86 return rc;
680ca6dc
CB
87
88 data = cpu_to_be32(value & 0xffffffff);
89 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
90 sizeof(uint32_t));
91 if (rc)
6b293258 92 return rc;
680ca6dc
CB
93
94 data = cpu_to_be32(SCOM_WRITE_CMD | addr);
162c3946 95 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
680ca6dc 96 sizeof(uint32_t));
6b293258
BH
97 if (rc)
98 return rc;
99 rc = fsi_device_read(scom_dev->fsi_dev, SCOM_STATUS_REG, &raw_status,
100 sizeof(uint32_t));
101 if (rc)
102 return rc;
103 *status = be32_to_cpu(raw_status);
104
105 return 0;
680ca6dc
CB
106}
107
6b293258
BH
108static int __get_scom(struct scom_device *scom_dev, uint64_t *value,
109 uint32_t addr, uint32_t *status)
680ca6dc 110{
6b293258 111 __be32 data, raw_status;
680ca6dc
CB
112 int rc;
113
162c3946 114
680ca6dc 115 *value = 0ULL;
f1433044 116 data = cpu_to_be32(SCOM_READ_CMD | addr);
680ca6dc
CB
117 rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
118 sizeof(uint32_t));
119 if (rc)
6b293258
BH
120 return rc;
121 rc = fsi_device_read(scom_dev->fsi_dev, SCOM_STATUS_REG, &raw_status,
122 sizeof(uint32_t));
123 if (rc)
124 return rc;
680ca6dc 125
6b293258
BH
126 /*
127 * Read the data registers even on error, so we don't have
128 * to interpret the status register here.
129 */
130 rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
680ca6dc
CB
131 sizeof(uint32_t));
132 if (rc)
6b293258
BH
133 return rc;
134 *value |= (uint64_t)be32_to_cpu(data) << 32;
135 rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
680ca6dc
CB
136 sizeof(uint32_t));
137 if (rc)
6b293258
BH
138 return rc;
139 *value |= be32_to_cpu(data);
140 *status = be32_to_cpu(raw_status);
141
142 return rc;
143}
144
145static int put_indirect_scom_form0(struct scom_device *scom, uint64_t value,
146 uint64_t addr, uint32_t *status)
147{
148 uint64_t ind_data, ind_addr;
149 int rc, retries, err = 0;
150
151 if (value & ~XSCOM_DATA_IND_DATA)
152 return -EINVAL;
153
154 ind_addr = addr & XSCOM_ADDR_DIRECT_PART;
155 ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | value;
156 rc = __put_scom(scom, ind_data, ind_addr, status);
157 if (rc || (*status & SCOM_STATUS_ANY_ERR))
158 return rc;
159
160 for (retries = 0; retries < SCOM_MAX_IND_RETRIES; retries++) {
161 rc = __get_scom(scom, &ind_data, addr, status);
162 if (rc || (*status & SCOM_STATUS_ANY_ERR))
163 return rc;
164
165 err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
166 *status = err << SCOM_STATUS_PIB_RESP_SHIFT;
167 if ((ind_data & XSCOM_DATA_IND_COMPLETE) || (err != SCOM_PIB_BLOCKED))
168 return 0;
169
170 msleep(1);
171 }
172 return rc;
173}
174
175static int put_indirect_scom_form1(struct scom_device *scom, uint64_t value,
176 uint64_t addr, uint32_t *status)
177{
178 uint64_t ind_data, ind_addr;
179
180 if (value & ~XSCOM_DATA_IND_FORM1_DATA)
181 return -EINVAL;
182
183 ind_addr = addr & XSCOM_ADDR_FORM1_LOW;
184 ind_data = value | (addr & XSCOM_ADDR_FORM1_HI) << XSCOM_ADDR_FORM1_HI_SHIFT;
185 return __put_scom(scom, ind_data, ind_addr, status);
186}
187
188static int get_indirect_scom_form0(struct scom_device *scom, uint64_t *value,
189 uint64_t addr, uint32_t *status)
190{
191 uint64_t ind_data, ind_addr;
192 int rc, retries, err = 0;
193
194 ind_addr = addr & XSCOM_ADDR_DIRECT_PART;
195 ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | XSCOM_DATA_IND_READ;
196 rc = __put_scom(scom, ind_data, ind_addr, status);
197 if (rc || (*status & SCOM_STATUS_ANY_ERR))
198 return rc;
199
200 for (retries = 0; retries < SCOM_MAX_IND_RETRIES; retries++) {
201 rc = __get_scom(scom, &ind_data, addr, status);
202 if (rc || (*status & SCOM_STATUS_ANY_ERR))
203 return rc;
204
205 err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
206 *status = err << SCOM_STATUS_PIB_RESP_SHIFT;
207 *value = ind_data & XSCOM_DATA_IND_DATA;
208
209 if ((ind_data & XSCOM_DATA_IND_COMPLETE) || (err != SCOM_PIB_BLOCKED))
210 return 0;
211
212 msleep(1);
213 }
214 return rc;
215}
216
217static int raw_put_scom(struct scom_device *scom, uint64_t value,
218 uint64_t addr, uint32_t *status)
219{
220 if (addr & XSCOM_ADDR_IND_FLAG) {
221 if (addr & XSCOM_ADDR_INF_FORM1)
222 return put_indirect_scom_form1(scom, value, addr, status);
223 else
224 return put_indirect_scom_form0(scom, value, addr, status);
225 } else
226 return __put_scom(scom, value, addr, status);
227}
228
229static int raw_get_scom(struct scom_device *scom, uint64_t *value,
230 uint64_t addr, uint32_t *status)
231{
232 if (addr & XSCOM_ADDR_IND_FLAG) {
233 if (addr & XSCOM_ADDR_INF_FORM1)
234 return -ENXIO;
235 return get_indirect_scom_form0(scom, value, addr, status);
236 } else
237 return __get_scom(scom, value, addr, status);
238}
239
240static int handle_fsi2pib_status(struct scom_device *scom, uint32_t status)
241{
242 uint32_t dummy = -1;
243
244 if (status & SCOM_STATUS_PROTECTION)
245 return -EPERM;
246 if (status & SCOM_STATUS_PARITY) {
247 fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
248 sizeof(uint32_t));
249 return -EIO;
250 }
251 /* Return -EBUSY on PIB abort to force a retry */
252 if (status & SCOM_STATUS_PIB_ABORT)
253 return -EBUSY;
254 if (status & SCOM_STATUS_ERR_SUMMARY) {
255 fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
256 sizeof(uint32_t));
257 return -EIO;
258 }
259 return 0;
260}
261
262static int handle_pib_status(struct scom_device *scom, uint8_t status)
263{
264 uint32_t dummy = -1;
265
266 if (status == SCOM_PIB_SUCCESS)
267 return 0;
268 if (status == SCOM_PIB_BLOCKED)
269 return -EBUSY;
270
271 /* Reset the bridge */
272 fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
273 sizeof(uint32_t));
274
275 switch(status) {
276 case SCOM_PIB_OFFLINE:
277 return -ENODEV;
278 case SCOM_PIB_BAD_ADDR:
279 return -ENXIO;
280 case SCOM_PIB_TIMEOUT:
281 return -ETIMEDOUT;
282 case SCOM_PIB_PARTIAL:
283 case SCOM_PIB_CLK_ERR:
284 case SCOM_PIB_PARITY_ERR:
285 default:
286 return -EIO;
287 }
288}
289
290static int put_scom(struct scom_device *scom, uint64_t value,
291 uint64_t addr)
292{
293 uint32_t status, dummy = -1;
294 int rc, retries;
295
296 for (retries = 0; retries < SCOM_MAX_RETRIES; retries++) {
297 rc = raw_put_scom(scom, value, addr, &status);
298 if (rc) {
299 /* Try resetting the bridge if FSI fails */
300 if (rc != -ENODEV && retries == 0) {
301 fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG,
302 &dummy, sizeof(uint32_t));
303 rc = -EBUSY;
304 } else
305 return rc;
306 } else
307 rc = handle_fsi2pib_status(scom, status);
308 if (rc && rc != -EBUSY)
309 break;
310 if (rc == 0) {
311 rc = handle_pib_status(scom,
312 (status & SCOM_STATUS_PIB_RESP_MASK)
313 >> SCOM_STATUS_PIB_RESP_SHIFT);
314 if (rc && rc != -EBUSY)
315 break;
316 }
317 if (rc == 0)
318 break;
319 msleep(1);
320 }
321 return rc;
322}
680ca6dc 323
6b293258
BH
324static int get_scom(struct scom_device *scom, uint64_t *value,
325 uint64_t addr)
326{
327 uint32_t status, dummy = -1;
328 int rc, retries;
329
330 for (retries = 0; retries < SCOM_MAX_RETRIES; retries++) {
331 rc = raw_get_scom(scom, value, addr, &status);
332 if (rc) {
333 /* Try resetting the bridge if FSI fails */
334 if (rc != -ENODEV && retries == 0) {
335 fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG,
336 &dummy, sizeof(uint32_t));
337 rc = -EBUSY;
338 } else
339 return rc;
340 } else
341 rc = handle_fsi2pib_status(scom, status);
342 if (rc && rc != -EBUSY)
343 break;
344 if (rc == 0) {
345 rc = handle_pib_status(scom,
346 (status & SCOM_STATUS_PIB_RESP_MASK)
347 >> SCOM_STATUS_PIB_RESP_SHIFT);
348 if (rc && rc != -EBUSY)
349 break;
350 }
351 if (rc == 0)
352 break;
353 msleep(1);
354 }
162c3946 355 return rc;
680ca6dc
CB
356}
357
358static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
bd213364 359 loff_t *offset)
680ca6dc 360{
d8f45876 361 struct scom_device *scom = filep->private_data;
680ca6dc
CB
362 struct device *dev = &scom->fsi_dev->dev;
363 uint64_t val;
6b293258 364 int rc;
680ca6dc
CB
365
366 if (len != sizeof(uint64_t))
367 return -EINVAL;
368
6b293258 369 mutex_lock(&scom->lock);
d8f45876
BH
370 if (scom->dead)
371 rc = -ENODEV;
372 else
373 rc = get_scom(scom, &val, *offset);
6b293258 374 mutex_unlock(&scom->lock);
680ca6dc
CB
375 if (rc) {
376 dev_dbg(dev, "get_scom fail:%d\n", rc);
377 return rc;
378 }
379
380 rc = copy_to_user(buf, &val, len);
381 if (rc)
382 dev_dbg(dev, "copy to user failed:%d\n", rc);
383
384 return rc ? rc : len;
385}
386
387static ssize_t scom_write(struct file *filep, const char __user *buf,
bd213364 388 size_t len, loff_t *offset)
680ca6dc
CB
389{
390 int rc;
d8f45876 391 struct scom_device *scom = filep->private_data;
680ca6dc
CB
392 struct device *dev = &scom->fsi_dev->dev;
393 uint64_t val;
394
395 if (len != sizeof(uint64_t))
396 return -EINVAL;
397
398 rc = copy_from_user(&val, buf, len);
399 if (rc) {
400 dev_dbg(dev, "copy from user failed:%d\n", rc);
401 return -EINVAL;
402 }
403
6b293258 404 mutex_lock(&scom->lock);
d8f45876
BH
405 if (scom->dead)
406 rc = -ENODEV;
407 else
408 rc = put_scom(scom, val, *offset);
6b293258 409 mutex_unlock(&scom->lock);
680ca6dc
CB
410 if (rc) {
411 dev_dbg(dev, "put_scom failed with:%d\n", rc);
412 return rc;
413 }
414
415 return len;
416}
417
418static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
419{
420 switch (whence) {
421 case SEEK_CUR:
422 break;
423 case SEEK_SET:
424 file->f_pos = offset;
425 break;
426 default:
427 return -EINVAL;
428 }
429
430 return offset;
431}
432
6b293258
BH
433static void raw_convert_status(struct scom_access *acc, uint32_t status)
434{
435 acc->pib_status = (status & SCOM_STATUS_PIB_RESP_MASK) >>
436 SCOM_STATUS_PIB_RESP_SHIFT;
437 acc->intf_errors = 0;
438
439 if (status & SCOM_STATUS_PROTECTION)
440 acc->intf_errors |= SCOM_INTF_ERR_PROTECTION;
441 else if (status & SCOM_STATUS_PARITY)
442 acc->intf_errors |= SCOM_INTF_ERR_PARITY;
443 else if (status & SCOM_STATUS_PIB_ABORT)
444 acc->intf_errors |= SCOM_INTF_ERR_ABORT;
445 else if (status & SCOM_STATUS_ERR_SUMMARY)
446 acc->intf_errors |= SCOM_INTF_ERR_UNKNOWN;
447}
448
449static int scom_raw_read(struct scom_device *scom, void __user *argp)
450{
451 struct scom_access acc;
452 uint32_t status;
453 int rc;
454
455 if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
456 return -EFAULT;
457
458 rc = raw_get_scom(scom, &acc.data, acc.addr, &status);
459 if (rc)
460 return rc;
461 raw_convert_status(&acc, status);
462 if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
463 return -EFAULT;
464 return 0;
465}
466
467static int scom_raw_write(struct scom_device *scom, void __user *argp)
468{
469 u64 prev_data, mask, data;
470 struct scom_access acc;
471 uint32_t status;
472 int rc;
473
474 if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
475 return -EFAULT;
476
477 if (acc.mask) {
478 rc = raw_get_scom(scom, &prev_data, acc.addr, &status);
479 if (rc)
480 return rc;
481 if (status & SCOM_STATUS_ANY_ERR)
482 goto fail;
483 mask = acc.mask;
484 } else {
485 prev_data = mask = -1ull;
486 }
487 data = (prev_data & ~mask) | (acc.data & mask);
488 rc = raw_put_scom(scom, data, acc.addr, &status);
489 if (rc)
490 return rc;
491 fail:
492 raw_convert_status(&acc, status);
493 if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
494 return -EFAULT;
495 return 0;
496}
497
498static int scom_reset(struct scom_device *scom, void __user *argp)
499{
500 uint32_t flags, dummy = -1;
501 int rc = 0;
502
503 if (get_user(flags, (__u32 __user *)argp))
504 return -EFAULT;
505 if (flags & SCOM_RESET_PIB)
506 rc = fsi_device_write(scom->fsi_dev, SCOM_PIB_RESET_REG, &dummy,
507 sizeof(uint32_t));
508 if (!rc && (flags & (SCOM_RESET_PIB | SCOM_RESET_INTF)))
509 rc = fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
510 sizeof(uint32_t));
511 return rc;
512}
513
514static int scom_check(struct scom_device *scom, void __user *argp)
515{
516 /* Still need to find out how to get "protected" */
517 return put_user(SCOM_CHECK_SUPPORTED, (__u32 __user *)argp);
518}
519
520static long scom_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
521{
d8f45876 522 struct scom_device *scom = file->private_data;
6b293258
BH
523 void __user *argp = (void __user *)arg;
524 int rc = -ENOTTY;
525
526 mutex_lock(&scom->lock);
d8f45876
BH
527 if (scom->dead) {
528 mutex_unlock(&scom->lock);
529 return -ENODEV;
530 }
6b293258
BH
531 switch(cmd) {
532 case FSI_SCOM_CHECK:
533 rc = scom_check(scom, argp);
534 break;
535 case FSI_SCOM_READ:
536 rc = scom_raw_read(scom, argp);
537 break;
538 case FSI_SCOM_WRITE:
539 rc = scom_raw_write(scom, argp);
540 break;
541 case FSI_SCOM_RESET:
542 rc = scom_reset(scom, argp);
543 break;
544 }
545 mutex_unlock(&scom->lock);
546 return rc;
547}
548
d8f45876
BH
549static int scom_open(struct inode *inode, struct file *file)
550{
551 struct scom_device *scom = container_of(inode->i_cdev, struct scom_device, cdev);
552
553 file->private_data = scom;
554
555 return 0;
556}
557
680ca6dc 558static const struct file_operations scom_fops = {
6b293258 559 .owner = THIS_MODULE,
d8f45876 560 .open = scom_open,
6b293258
BH
561 .llseek = scom_llseek,
562 .read = scom_read,
563 .write = scom_write,
564 .unlocked_ioctl = scom_ioctl,
680ca6dc
CB
565};
566
d8f45876
BH
567static void scom_free(struct device *dev)
568{
569 struct scom_device *scom = container_of(dev, struct scom_device, dev);
570
571 put_device(&scom->fsi_dev->dev);
572 kfree(scom);
573}
574
680ca6dc
CB
575static int scom_probe(struct device *dev)
576{
577 struct fsi_device *fsi_dev = to_fsi_dev(dev);
578 struct scom_device *scom;
d8f45876 579 int rc, didx;
680ca6dc 580
d8f45876 581 scom = kzalloc(sizeof(*scom), GFP_KERNEL);
680ca6dc
CB
582 if (!scom)
583 return -ENOMEM;
d8f45876 584 dev_set_drvdata(dev, scom);
162c3946 585 mutex_init(&scom->lock);
d8f45876
BH
586
587 /* Grab a reference to the device (parent of our cdev), we'll drop it later */
588 if (!get_device(dev)) {
589 kfree(scom);
590 return -ENODEV;
591 }
aa1221b2 592 scom->fsi_dev = fsi_dev;
d8f45876
BH
593
594 /* Create chardev for userspace access */
595 scom->dev.type = &fsi_cdev_type;
596 scom->dev.parent = dev;
597 scom->dev.release = scom_free;
598 device_initialize(&scom->dev);
599
600 /* Allocate a minor in the FSI space */
601 rc = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
602 if (rc)
603 goto err;
604
605 dev_set_name(&scom->dev, "scom%d", didx);
606 cdev_init(&scom->cdev, &scom_fops);
607 rc = cdev_device_add(&scom->cdev, &scom->dev);
608 if (rc) {
609 dev_err(dev, "Error %d creating char device %s\n",
610 rc, dev_name(&scom->dev));
611 goto err_free_minor;
612 }
613
614 return 0;
615 err_free_minor:
616 fsi_free_minor(scom->dev.devt);
617 err:
618 put_device(&scom->dev);
619 return rc;
680ca6dc
CB
620}
621
622static int scom_remove(struct device *dev)
623{
d8f45876 624 struct scom_device *scom = dev_get_drvdata(dev);
680ca6dc 625
d8f45876
BH
626 mutex_lock(&scom->lock);
627 scom->dead = true;
628 mutex_unlock(&scom->lock);
629 cdev_device_del(&scom->cdev, &scom->dev);
630 fsi_free_minor(scom->dev.devt);
631 put_device(&scom->dev);
680ca6dc
CB
632
633 return 0;
634}
635
636static struct fsi_device_id scom_ids[] = {
637 {
638 .engine_type = FSI_ENGID_SCOM,
639 .version = FSI_VERSION_ANY,
640 },
641 { 0 }
642};
643
644static struct fsi_driver scom_drv = {
645 .id_table = scom_ids,
646 .drv = {
647 .name = "scom",
648 .bus = &fsi_bus_type,
649 .probe = scom_probe,
650 .remove = scom_remove,
651 }
652};
653
654static int scom_init(void)
655{
680ca6dc
CB
656 return fsi_driver_register(&scom_drv);
657}
658
659static void scom_exit(void)
660{
680ca6dc
CB
661 fsi_driver_unregister(&scom_drv);
662}
663
664module_init(scom_init);
665module_exit(scom_exit);
666MODULE_LICENSE("GPL");