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_FSI2PIB_ERROR       (SCOM_STATUS_PROTECTION |       \
  42                                         SCOM_STATUS_PARITY |           \
  43                                         SCOM_STATUS_PIB_ABORT)
  44#define SCOM_STATUS_ANY_ERR             (SCOM_STATUS_FSI2PIB_ERROR |    \
  45                                         SCOM_STATUS_PIB_RESP_MASK)
  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_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_FSI2PIB_ERROR)
 244                fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
 245                                 sizeof(uint32_t));
 246
 247        if (status & SCOM_STATUS_PROTECTION)
 248                return -EPERM;
 249        if (status & SCOM_STATUS_PARITY)
 250                return -EIO;
 251
 252        if (status & SCOM_STATUS_PIB_ABORT)
 253                return -EBUSY;
 254        return 0;
 255}
 256
 257static int handle_pib_status(struct scom_device *scom, uint8_t status)
 258{
 259        uint32_t dummy = -1;
 260
 261        if (status == SCOM_PIB_SUCCESS)
 262                return 0;
 263        if (status == SCOM_PIB_BLOCKED)
 264                return -EBUSY;
 265
 266        /* Reset the bridge */
 267        fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
 268                         sizeof(uint32_t));
 269
 270        switch(status) {
 271        case SCOM_PIB_OFFLINE:
 272                return -ENODEV;
 273        case SCOM_PIB_BAD_ADDR:
 274                return -ENXIO;
 275        case SCOM_PIB_TIMEOUT:
 276                return -ETIMEDOUT;
 277        case SCOM_PIB_PARTIAL:
 278        case SCOM_PIB_CLK_ERR:
 279        case SCOM_PIB_PARITY_ERR:
 280        default:
 281                return -EIO;
 282        }
 283}
 284
 285static int put_scom(struct scom_device *scom, uint64_t value,
 286                    uint64_t addr)
 287{
 288        uint32_t status;
 289        int rc;
 290
 291        rc = raw_put_scom(scom, value, addr, &status);
 292        if (rc == -ENODEV)
 293                return rc;
 294
 295        rc = handle_fsi2pib_status(scom, status);
 296        if (rc)
 297                return rc;
 298
 299        return handle_pib_status(scom,
 300                                 (status & SCOM_STATUS_PIB_RESP_MASK)
 301                                 >> SCOM_STATUS_PIB_RESP_SHIFT);
 302}
 303
 304static int get_scom(struct scom_device *scom, uint64_t *value,
 305                    uint64_t addr)
 306{
 307        uint32_t status;
 308        int rc;
 309
 310        rc = raw_get_scom(scom, value, addr, &status);
 311        if (rc == -ENODEV)
 312                return rc;
 313
 314        rc = handle_fsi2pib_status(scom, status);
 315        if (rc)
 316                return rc;
 317
 318        return handle_pib_status(scom,
 319                                 (status & SCOM_STATUS_PIB_RESP_MASK)
 320                                 >> SCOM_STATUS_PIB_RESP_SHIFT);
 321}
 322
 323static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
 324                         loff_t *offset)
 325{
 326        struct scom_device *scom = filep->private_data;
 327        struct device *dev = &scom->fsi_dev->dev;
 328        uint64_t val;
 329        int rc;
 330
 331        if (len != sizeof(uint64_t))
 332                return -EINVAL;
 333
 334        mutex_lock(&scom->lock);
 335        if (scom->dead)
 336                rc = -ENODEV;
 337        else
 338                rc = get_scom(scom, &val, *offset);
 339        mutex_unlock(&scom->lock);
 340        if (rc) {
 341                dev_dbg(dev, "get_scom fail:%d\n", rc);
 342                return rc;
 343        }
 344
 345        rc = copy_to_user(buf, &val, len);
 346        if (rc)
 347                dev_dbg(dev, "copy to user failed:%d\n", rc);
 348
 349        return rc ? rc : len;
 350}
 351
 352static ssize_t scom_write(struct file *filep, const char __user *buf,
 353                          size_t len, loff_t *offset)
 354{
 355        int rc;
 356        struct scom_device *scom = filep->private_data;
 357        struct device *dev = &scom->fsi_dev->dev;
 358        uint64_t val;
 359
 360        if (len != sizeof(uint64_t))
 361                return -EINVAL;
 362
 363        rc = copy_from_user(&val, buf, len);
 364        if (rc) {
 365                dev_dbg(dev, "copy from user failed:%d\n", rc);
 366                return -EINVAL;
 367        }
 368
 369        mutex_lock(&scom->lock);
 370        if (scom->dead)
 371                rc = -ENODEV;
 372        else
 373                rc = put_scom(scom, val, *offset);
 374        mutex_unlock(&scom->lock);
 375        if (rc) {
 376                dev_dbg(dev, "put_scom failed with:%d\n", rc);
 377                return rc;
 378        }
 379
 380        return len;
 381}
 382
 383static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
 384{
 385        switch (whence) {
 386        case SEEK_CUR:
 387                break;
 388        case SEEK_SET:
 389                file->f_pos = offset;
 390                break;
 391        default:
 392                return -EINVAL;
 393        }
 394
 395        return offset;
 396}
 397
 398static void raw_convert_status(struct scom_access *acc, uint32_t status)
 399{
 400        acc->pib_status = (status & SCOM_STATUS_PIB_RESP_MASK) >>
 401                SCOM_STATUS_PIB_RESP_SHIFT;
 402        acc->intf_errors = 0;
 403
 404        if (status & SCOM_STATUS_PROTECTION)
 405                acc->intf_errors |= SCOM_INTF_ERR_PROTECTION;
 406        else if (status & SCOM_STATUS_PARITY)
 407                acc->intf_errors |= SCOM_INTF_ERR_PARITY;
 408        else if (status & SCOM_STATUS_PIB_ABORT)
 409                acc->intf_errors |= SCOM_INTF_ERR_ABORT;
 410        else if (status & SCOM_STATUS_ERR_SUMMARY)
 411                acc->intf_errors |= SCOM_INTF_ERR_UNKNOWN;
 412}
 413
 414static int scom_raw_read(struct scom_device *scom, void __user *argp)
 415{
 416        struct scom_access acc;
 417        uint32_t status;
 418        int rc;
 419
 420        if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
 421                return -EFAULT;
 422
 423        rc = raw_get_scom(scom, &acc.data, acc.addr, &status);
 424        if (rc)
 425                return rc;
 426        raw_convert_status(&acc, status);
 427        if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
 428                return -EFAULT;
 429        return 0;
 430}
 431
 432static int scom_raw_write(struct scom_device *scom, void __user *argp)
 433{
 434        u64 prev_data, mask, data;
 435        struct scom_access acc;
 436        uint32_t status;
 437        int rc;
 438
 439        if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
 440                return -EFAULT;
 441
 442        if (acc.mask) {
 443                rc = raw_get_scom(scom, &prev_data, acc.addr, &status);
 444                if (rc)
 445                        return rc;
 446                if (status & SCOM_STATUS_ANY_ERR)
 447                        goto fail;
 448                mask = acc.mask;
 449        } else {
 450                prev_data = mask = -1ull;
 451        }
 452        data = (prev_data & ~mask) | (acc.data & mask);
 453        rc = raw_put_scom(scom, data, acc.addr, &status);
 454        if (rc)
 455                return rc;
 456 fail:
 457        raw_convert_status(&acc, status);
 458        if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
 459                return -EFAULT;
 460        return 0;
 461}
 462
 463static int scom_reset(struct scom_device *scom, void __user *argp)
 464{
 465        uint32_t flags, dummy = -1;
 466        int rc = 0;
 467
 468        if (get_user(flags, (__u32 __user *)argp))
 469                return -EFAULT;
 470        if (flags & SCOM_RESET_PIB)
 471                rc = fsi_device_write(scom->fsi_dev, SCOM_PIB_RESET_REG, &dummy,
 472                                      sizeof(uint32_t));
 473        if (!rc && (flags & (SCOM_RESET_PIB | SCOM_RESET_INTF)))
 474                rc = fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
 475                                      sizeof(uint32_t));
 476        return rc;
 477}
 478
 479static int scom_check(struct scom_device *scom, void __user *argp)
 480{
 481        /* Still need to find out how to get "protected" */
 482        return put_user(SCOM_CHECK_SUPPORTED, (__u32 __user *)argp);
 483}
 484
 485static long scom_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 486{
 487        struct scom_device *scom = file->private_data;
 488        void __user *argp = (void __user *)arg;
 489        int rc = -ENOTTY;
 490
 491        mutex_lock(&scom->lock);
 492        if (scom->dead) {
 493                mutex_unlock(&scom->lock);
 494                return -ENODEV;
 495        }
 496        switch(cmd) {
 497        case FSI_SCOM_CHECK:
 498                rc = scom_check(scom, argp);
 499                break;
 500        case FSI_SCOM_READ:
 501                rc = scom_raw_read(scom, argp);
 502                break;
 503        case FSI_SCOM_WRITE:
 504                rc = scom_raw_write(scom, argp);
 505                break;
 506        case FSI_SCOM_RESET:
 507                rc = scom_reset(scom, argp);
 508                break;
 509        }
 510        mutex_unlock(&scom->lock);
 511        return rc;
 512}
 513
 514static int scom_open(struct inode *inode, struct file *file)
 515{
 516        struct scom_device *scom = container_of(inode->i_cdev, struct scom_device, cdev);
 517
 518        file->private_data = scom;
 519
 520        return 0;
 521}
 522
 523static const struct file_operations scom_fops = {
 524        .owner          = THIS_MODULE,
 525        .open           = scom_open,
 526        .llseek         = scom_llseek,
 527        .read           = scom_read,
 528        .write          = scom_write,
 529        .unlocked_ioctl = scom_ioctl,
 530};
 531
 532static void scom_free(struct device *dev)
 533{
 534        struct scom_device *scom = container_of(dev, struct scom_device, dev);
 535
 536        put_device(&scom->fsi_dev->dev);
 537        kfree(scom);
 538}
 539
 540static int scom_probe(struct device *dev)
 541{
 542        struct fsi_device *fsi_dev = to_fsi_dev(dev);
 543        struct scom_device *scom;
 544        int rc, didx;
 545
 546        scom = kzalloc(sizeof(*scom), GFP_KERNEL);
 547        if (!scom)
 548                return -ENOMEM;
 549        dev_set_drvdata(dev, scom);
 550        mutex_init(&scom->lock);
 551
 552        /* Grab a reference to the device (parent of our cdev), we'll drop it later */
 553        if (!get_device(dev)) {
 554                kfree(scom);
 555                return -ENODEV;
 556        }
 557        scom->fsi_dev = fsi_dev;
 558
 559        /* Create chardev for userspace access */
 560        scom->dev.type = &fsi_cdev_type;
 561        scom->dev.parent = dev;
 562        scom->dev.release = scom_free;
 563        device_initialize(&scom->dev);
 564
 565        /* Allocate a minor in the FSI space */
 566        rc = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
 567        if (rc)
 568                goto err;
 569
 570        dev_set_name(&scom->dev, "scom%d", didx);
 571        cdev_init(&scom->cdev, &scom_fops);
 572        rc = cdev_device_add(&scom->cdev, &scom->dev);
 573        if (rc) {
 574                dev_err(dev, "Error %d creating char device %s\n",
 575                        rc, dev_name(&scom->dev));
 576                goto err_free_minor;
 577        }
 578
 579        return 0;
 580 err_free_minor:
 581        fsi_free_minor(scom->dev.devt);
 582 err:
 583        put_device(&scom->dev);
 584        return rc;
 585}
 586
 587static int scom_remove(struct device *dev)
 588{
 589        struct scom_device *scom = dev_get_drvdata(dev);
 590
 591        mutex_lock(&scom->lock);
 592        scom->dead = true;
 593        mutex_unlock(&scom->lock);
 594        cdev_device_del(&scom->cdev, &scom->dev);
 595        fsi_free_minor(scom->dev.devt);
 596        put_device(&scom->dev);
 597
 598        return 0;
 599}
 600
 601static const struct fsi_device_id scom_ids[] = {
 602        {
 603                .engine_type = FSI_ENGID_SCOM,
 604                .version = FSI_VERSION_ANY,
 605        },
 606        { 0 }
 607};
 608
 609static struct fsi_driver scom_drv = {
 610        .id_table = scom_ids,
 611        .drv = {
 612                .name = "scom",
 613                .bus = &fsi_bus_type,
 614                .probe = scom_probe,
 615                .remove = scom_remove,
 616        }
 617};
 618
 619static int scom_init(void)
 620{
 621        return fsi_driver_register(&scom_drv);
 622}
 623
 624static void scom_exit(void)
 625{
 626        fsi_driver_unregister(&scom_drv);
 627}
 628
 629module_init(scom_init);
 630module_exit(scom_exit);
 631MODULE_LICENSE("GPL");
 632