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