Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.17 631 lines 15 kB view raw
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");