18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OPAL Runtime Diagnostics interface driver 48c2ecf20Sopenharmony_ci * Supported on POWERNV platform 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright IBM Corporation 2015 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "opal-prd: " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/fs.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/poll.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <asm/opal-prd.h> 228c2ecf20Sopenharmony_ci#include <asm/opal.h> 238c2ecf20Sopenharmony_ci#include <asm/io.h> 248c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * The msg member must be at the end of the struct, as it's followed by the 298c2ecf20Sopenharmony_ci * message data. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistruct opal_prd_msg_queue_item { 328c2ecf20Sopenharmony_ci struct list_head list; 338c2ecf20Sopenharmony_ci struct opal_prd_msg_header msg; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct device_node *prd_node; 378c2ecf20Sopenharmony_cistatic LIST_HEAD(opal_prd_msg_queue); 388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(opal_prd_msg_queue_lock); 398c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(opal_prd_msg_wait); 408c2ecf20Sopenharmony_cistatic atomic_t prd_usage; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic bool opal_prd_range_is_valid(uint64_t addr, uint64_t size) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct device_node *parent, *node; 458c2ecf20Sopenharmony_ci bool found; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (addr + size < addr) 488c2ecf20Sopenharmony_ci return false; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci parent = of_find_node_by_path("/reserved-memory"); 518c2ecf20Sopenharmony_ci if (!parent) 528c2ecf20Sopenharmony_ci return false; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci found = false; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci for_each_child_of_node(parent, node) { 578c2ecf20Sopenharmony_ci uint64_t range_addr, range_size, range_end; 588c2ecf20Sopenharmony_ci const __be32 *addrp; 598c2ecf20Sopenharmony_ci const char *label; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci addrp = of_get_address(node, 0, &range_size, NULL); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci range_addr = of_read_number(addrp, 2); 648c2ecf20Sopenharmony_ci range_end = range_addr + range_size; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci label = of_get_property(node, "ibm,prd-label", NULL); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* PRD ranges need a label */ 698c2ecf20Sopenharmony_ci if (!label) 708c2ecf20Sopenharmony_ci continue; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (range_end <= range_addr) 738c2ecf20Sopenharmony_ci continue; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (addr >= range_addr && addr + size <= range_end) { 768c2ecf20Sopenharmony_ci found = true; 778c2ecf20Sopenharmony_ci of_node_put(node); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci of_node_put(parent); 838c2ecf20Sopenharmony_ci return found; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int opal_prd_open(struct inode *inode, struct file *file) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * Prevent multiple (separate) processes from concurrent interactions 908c2ecf20Sopenharmony_ci * with the FW PRD channel 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci if (atomic_xchg(&prd_usage, 1) == 1) 938c2ecf20Sopenharmony_ci return -EBUSY; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * opal_prd_mmap - maps firmware-provided ranges into userspace 1008c2ecf20Sopenharmony_ci * @file: file structure for the device 1018c2ecf20Sopenharmony_ci * @vma: VMA to map the registers into 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int opal_prd_mmap(struct file *file, struct vm_area_struct *vma) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci size_t addr, size; 1078c2ecf20Sopenharmony_ci pgprot_t page_prot; 1088c2ecf20Sopenharmony_ci int rc; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci pr_devel("opal_prd_mmap(0x%016lx, 0x%016lx, 0x%lx, 0x%lx)\n", 1118c2ecf20Sopenharmony_ci vma->vm_start, vma->vm_end, vma->vm_pgoff, 1128c2ecf20Sopenharmony_ci vma->vm_flags); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci addr = vma->vm_pgoff << PAGE_SHIFT; 1158c2ecf20Sopenharmony_ci size = vma->vm_end - vma->vm_start; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* ensure we're mapping within one of the allowable ranges */ 1188c2ecf20Sopenharmony_ci if (!opal_prd_range_is_valid(addr, size)) 1198c2ecf20Sopenharmony_ci return -EINVAL; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci page_prot = phys_mem_access_prot(file, vma->vm_pgoff, 1228c2ecf20Sopenharmony_ci size, vma->vm_page_prot); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rc = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, 1258c2ecf20Sopenharmony_ci page_prot); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return rc; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic bool opal_msg_queue_empty(void) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned long flags; 1338c2ecf20Sopenharmony_ci bool ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_prd_msg_queue_lock, flags); 1368c2ecf20Sopenharmony_ci ret = list_empty(&opal_prd_msg_queue); 1378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic __poll_t opal_prd_poll(struct file *file, 1438c2ecf20Sopenharmony_ci struct poll_table_struct *wait) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci poll_wait(file, &opal_prd_msg_wait, wait); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!opal_msg_queue_empty()) 1488c2ecf20Sopenharmony_ci return EPOLLIN | EPOLLRDNORM; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic ssize_t opal_prd_read(struct file *file, char __user *buf, 1548c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct opal_prd_msg_queue_item *item; 1578c2ecf20Sopenharmony_ci unsigned long flags; 1588c2ecf20Sopenharmony_ci ssize_t size, err; 1598c2ecf20Sopenharmony_ci int rc; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* we need at least a header's worth of data */ 1628c2ecf20Sopenharmony_ci if (count < sizeof(item->msg)) 1638c2ecf20Sopenharmony_ci return -EINVAL; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (*ppos) 1668c2ecf20Sopenharmony_ci return -ESPIPE; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci item = NULL; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (;;) { 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_prd_msg_queue_lock, flags); 1738c2ecf20Sopenharmony_ci if (!list_empty(&opal_prd_msg_queue)) { 1748c2ecf20Sopenharmony_ci item = list_first_entry(&opal_prd_msg_queue, 1758c2ecf20Sopenharmony_ci struct opal_prd_msg_queue_item, list); 1768c2ecf20Sopenharmony_ci list_del(&item->list); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (item) 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 1848c2ecf20Sopenharmony_ci return -EAGAIN; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci rc = wait_event_interruptible(opal_prd_msg_wait, 1878c2ecf20Sopenharmony_ci !opal_msg_queue_empty()); 1888c2ecf20Sopenharmony_ci if (rc) 1898c2ecf20Sopenharmony_ci return -EINTR; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci size = be16_to_cpu(item->msg.size); 1938c2ecf20Sopenharmony_ci if (size > count) { 1948c2ecf20Sopenharmony_ci err = -EINVAL; 1958c2ecf20Sopenharmony_ci goto err_requeue; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci rc = copy_to_user(buf, &item->msg, size); 1998c2ecf20Sopenharmony_ci if (rc) { 2008c2ecf20Sopenharmony_ci err = -EFAULT; 2018c2ecf20Sopenharmony_ci goto err_requeue; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci kfree(item); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return size; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cierr_requeue: 2098c2ecf20Sopenharmony_ci /* eep! re-queue at the head of the list */ 2108c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_prd_msg_queue_lock, flags); 2118c2ecf20Sopenharmony_ci list_add(&item->list, &opal_prd_msg_queue); 2128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags); 2138c2ecf20Sopenharmony_ci return err; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic ssize_t opal_prd_write(struct file *file, const char __user *buf, 2178c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct opal_prd_msg_header hdr; 2208c2ecf20Sopenharmony_ci ssize_t size; 2218c2ecf20Sopenharmony_ci void *msg; 2228c2ecf20Sopenharmony_ci int rc; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci size = sizeof(hdr); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (count < size) 2278c2ecf20Sopenharmony_ci return -EINVAL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* grab the header */ 2308c2ecf20Sopenharmony_ci rc = copy_from_user(&hdr, buf, sizeof(hdr)); 2318c2ecf20Sopenharmony_ci if (rc) 2328c2ecf20Sopenharmony_ci return -EFAULT; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci size = be16_to_cpu(hdr.size); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci msg = memdup_user(buf, size); 2378c2ecf20Sopenharmony_ci if (IS_ERR(msg)) 2388c2ecf20Sopenharmony_ci return PTR_ERR(msg); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci rc = opal_prd_msg(msg); 2418c2ecf20Sopenharmony_ci if (rc) { 2428c2ecf20Sopenharmony_ci pr_warn("write: opal_prd_msg returned %d\n", rc); 2438c2ecf20Sopenharmony_ci size = -EIO; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci kfree(msg); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return size; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int opal_prd_release(struct inode *inode, struct file *file) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct opal_prd_msg_header msg; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci msg.size = cpu_to_be16(sizeof(msg)); 2568c2ecf20Sopenharmony_ci msg.type = OPAL_PRD_MSG_TYPE_FINI; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci opal_prd_msg((struct opal_prd_msg *)&msg); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci atomic_xchg(&prd_usage, 0); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic long opal_prd_ioctl(struct file *file, unsigned int cmd, 2668c2ecf20Sopenharmony_ci unsigned long param) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct opal_prd_info info; 2698c2ecf20Sopenharmony_ci struct opal_prd_scom scom; 2708c2ecf20Sopenharmony_ci int rc = 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci switch (cmd) { 2738c2ecf20Sopenharmony_ci case OPAL_PRD_GET_INFO: 2748c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 2758c2ecf20Sopenharmony_ci info.version = OPAL_PRD_KERNEL_VERSION; 2768c2ecf20Sopenharmony_ci rc = copy_to_user((void __user *)param, &info, sizeof(info)); 2778c2ecf20Sopenharmony_ci if (rc) 2788c2ecf20Sopenharmony_ci return -EFAULT; 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci case OPAL_PRD_SCOM_READ: 2828c2ecf20Sopenharmony_ci rc = copy_from_user(&scom, (void __user *)param, sizeof(scom)); 2838c2ecf20Sopenharmony_ci if (rc) 2848c2ecf20Sopenharmony_ci return -EFAULT; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci scom.rc = opal_xscom_read(scom.chip, scom.addr, 2878c2ecf20Sopenharmony_ci (__be64 *)&scom.data); 2888c2ecf20Sopenharmony_ci scom.data = be64_to_cpu(scom.data); 2898c2ecf20Sopenharmony_ci pr_devel("ioctl SCOM_READ: chip %llx addr %016llx data %016llx rc %lld\n", 2908c2ecf20Sopenharmony_ci scom.chip, scom.addr, scom.data, scom.rc); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci rc = copy_to_user((void __user *)param, &scom, sizeof(scom)); 2938c2ecf20Sopenharmony_ci if (rc) 2948c2ecf20Sopenharmony_ci return -EFAULT; 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci case OPAL_PRD_SCOM_WRITE: 2988c2ecf20Sopenharmony_ci rc = copy_from_user(&scom, (void __user *)param, sizeof(scom)); 2998c2ecf20Sopenharmony_ci if (rc) 3008c2ecf20Sopenharmony_ci return -EFAULT; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci scom.rc = opal_xscom_write(scom.chip, scom.addr, scom.data); 3038c2ecf20Sopenharmony_ci pr_devel("ioctl SCOM_WRITE: chip %llx addr %016llx data %016llx rc %lld\n", 3048c2ecf20Sopenharmony_ci scom.chip, scom.addr, scom.data, scom.rc); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci rc = copy_to_user((void __user *)param, &scom, sizeof(scom)); 3078c2ecf20Sopenharmony_ci if (rc) 3088c2ecf20Sopenharmony_ci return -EFAULT; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci default: 3128c2ecf20Sopenharmony_ci rc = -EINVAL; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return rc; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const struct file_operations opal_prd_fops = { 3198c2ecf20Sopenharmony_ci .open = opal_prd_open, 3208c2ecf20Sopenharmony_ci .mmap = opal_prd_mmap, 3218c2ecf20Sopenharmony_ci .poll = opal_prd_poll, 3228c2ecf20Sopenharmony_ci .read = opal_prd_read, 3238c2ecf20Sopenharmony_ci .write = opal_prd_write, 3248c2ecf20Sopenharmony_ci .unlocked_ioctl = opal_prd_ioctl, 3258c2ecf20Sopenharmony_ci .release = opal_prd_release, 3268c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic struct miscdevice opal_prd_dev = { 3308c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 3318c2ecf20Sopenharmony_ci .name = "opal-prd", 3328c2ecf20Sopenharmony_ci .fops = &opal_prd_fops, 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* opal interface */ 3368c2ecf20Sopenharmony_cistatic int opal_prd_msg_notifier(struct notifier_block *nb, 3378c2ecf20Sopenharmony_ci unsigned long msg_type, void *_msg) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct opal_prd_msg_queue_item *item; 3408c2ecf20Sopenharmony_ci struct opal_prd_msg_header *hdr; 3418c2ecf20Sopenharmony_ci struct opal_msg *msg = _msg; 3428c2ecf20Sopenharmony_ci int msg_size, item_size; 3438c2ecf20Sopenharmony_ci unsigned long flags; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (msg_type != OPAL_MSG_PRD && msg_type != OPAL_MSG_PRD2) 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Calculate total size of the message and item we need to store. The 3498c2ecf20Sopenharmony_ci * 'size' field in the header includes the header itself. */ 3508c2ecf20Sopenharmony_ci hdr = (void *)msg->params; 3518c2ecf20Sopenharmony_ci msg_size = be16_to_cpu(hdr->size); 3528c2ecf20Sopenharmony_ci item_size = msg_size + sizeof(*item) - sizeof(item->msg); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci item = kzalloc(item_size, GFP_ATOMIC); 3558c2ecf20Sopenharmony_ci if (!item) 3568c2ecf20Sopenharmony_ci return -ENOMEM; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci memcpy(&item->msg, msg->params, msg_size); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci spin_lock_irqsave(&opal_prd_msg_queue_lock, flags); 3618c2ecf20Sopenharmony_ci list_add_tail(&item->list, &opal_prd_msg_queue); 3628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci wake_up_interruptible(&opal_prd_msg_wait); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic struct notifier_block opal_prd_event_nb = { 3708c2ecf20Sopenharmony_ci .notifier_call = opal_prd_msg_notifier, 3718c2ecf20Sopenharmony_ci .next = NULL, 3728c2ecf20Sopenharmony_ci .priority = 0, 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic struct notifier_block opal_prd_event_nb2 = { 3768c2ecf20Sopenharmony_ci .notifier_call = opal_prd_msg_notifier, 3778c2ecf20Sopenharmony_ci .next = NULL, 3788c2ecf20Sopenharmony_ci .priority = 0, 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int opal_prd_probe(struct platform_device *pdev) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int rc; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (!pdev || !pdev->dev.of_node) 3868c2ecf20Sopenharmony_ci return -ENODEV; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* We should only have one prd driver instance per machine; ensure 3898c2ecf20Sopenharmony_ci * that we only get a valid probe on a single OF node. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci if (prd_node) 3928c2ecf20Sopenharmony_ci return -EBUSY; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci prd_node = pdev->dev.of_node; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci rc = opal_message_notifier_register(OPAL_MSG_PRD, &opal_prd_event_nb); 3978c2ecf20Sopenharmony_ci if (rc) { 3988c2ecf20Sopenharmony_ci pr_err("Couldn't register event notifier\n"); 3998c2ecf20Sopenharmony_ci return rc; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci rc = opal_message_notifier_register(OPAL_MSG_PRD2, &opal_prd_event_nb2); 4038c2ecf20Sopenharmony_ci if (rc) { 4048c2ecf20Sopenharmony_ci pr_err("Couldn't register PRD2 event notifier\n"); 4058c2ecf20Sopenharmony_ci opal_message_notifier_unregister(OPAL_MSG_PRD, &opal_prd_event_nb); 4068c2ecf20Sopenharmony_ci return rc; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci rc = misc_register(&opal_prd_dev); 4108c2ecf20Sopenharmony_ci if (rc) { 4118c2ecf20Sopenharmony_ci pr_err("failed to register miscdev\n"); 4128c2ecf20Sopenharmony_ci opal_message_notifier_unregister(OPAL_MSG_PRD, 4138c2ecf20Sopenharmony_ci &opal_prd_event_nb); 4148c2ecf20Sopenharmony_ci opal_message_notifier_unregister(OPAL_MSG_PRD2, 4158c2ecf20Sopenharmony_ci &opal_prd_event_nb2); 4168c2ecf20Sopenharmony_ci return rc; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int opal_prd_remove(struct platform_device *pdev) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci misc_deregister(&opal_prd_dev); 4258c2ecf20Sopenharmony_ci opal_message_notifier_unregister(OPAL_MSG_PRD, &opal_prd_event_nb); 4268c2ecf20Sopenharmony_ci opal_message_notifier_unregister(OPAL_MSG_PRD2, &opal_prd_event_nb2); 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic const struct of_device_id opal_prd_match[] = { 4318c2ecf20Sopenharmony_ci { .compatible = "ibm,opal-prd" }, 4328c2ecf20Sopenharmony_ci { }, 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct platform_driver opal_prd_driver = { 4368c2ecf20Sopenharmony_ci .driver = { 4378c2ecf20Sopenharmony_ci .name = "opal-prd", 4388c2ecf20Sopenharmony_ci .of_match_table = opal_prd_match, 4398c2ecf20Sopenharmony_ci }, 4408c2ecf20Sopenharmony_ci .probe = opal_prd_probe, 4418c2ecf20Sopenharmony_ci .remove = opal_prd_remove, 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cimodule_platform_driver(opal_prd_driver); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, opal_prd_match); 4478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PowerNV OPAL runtime diagnostic driver"); 4488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 449