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