2 * OPAL Runtime Diagnostics interface driver
3 * Supported on POWERNV platform
5 * Copyright IBM Corporation 2015
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #define pr_fmt(fmt) "opal-prd: " fmt
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/miscdevice.h>
25 #include <linux/of_address.h>
26 #include <linux/poll.h>
28 #include <linux/slab.h>
29 #include <asm/opal-prd.h>
32 #include <linux/uaccess.h>
36 * The msg member must be at the end of the struct, as it's followed by the
39 struct opal_prd_msg_queue_item
{
40 struct list_head list
;
41 struct opal_prd_msg_header msg
;
44 static struct device_node
*prd_node
;
45 static LIST_HEAD(opal_prd_msg_queue
);
46 static DEFINE_SPINLOCK(opal_prd_msg_queue_lock
);
47 static DECLARE_WAIT_QUEUE_HEAD(opal_prd_msg_wait
);
48 static atomic_t prd_usage
;
50 static bool opal_prd_range_is_valid(uint64_t addr
, uint64_t size
)
52 struct device_node
*parent
, *node
;
55 if (addr
+ size
< addr
)
58 parent
= of_find_node_by_path("/reserved-memory");
64 for_each_child_of_node(parent
, node
) {
65 uint64_t range_addr
, range_size
, range_end
;
69 addrp
= of_get_address(node
, 0, &range_size
, NULL
);
71 range_addr
= of_read_number(addrp
, 2);
72 range_end
= range_addr
+ range_size
;
74 label
= of_get_property(node
, "ibm,prd-label", NULL
);
76 /* PRD ranges need a label */
80 if (range_end
<= range_addr
)
83 if (addr
>= range_addr
&& addr
+ size
<= range_end
) {
94 static int opal_prd_open(struct inode
*inode
, struct file
*file
)
97 * Prevent multiple (separate) processes from concurrent interactions
98 * with the FW PRD channel
100 if (atomic_xchg(&prd_usage
, 1) == 1)
107 * opal_prd_mmap - maps firmware-provided ranges into userspace
108 * @file: file structure for the device
109 * @vma: VMA to map the registers into
112 static int opal_prd_mmap(struct file
*file
, struct vm_area_struct
*vma
)
118 pr_devel("opal_prd_mmap(0x%016lx, 0x%016lx, 0x%lx, 0x%lx)\n",
119 vma
->vm_start
, vma
->vm_end
, vma
->vm_pgoff
,
122 addr
= vma
->vm_pgoff
<< PAGE_SHIFT
;
123 size
= vma
->vm_end
- vma
->vm_start
;
125 /* ensure we're mapping within one of the allowable ranges */
126 if (!opal_prd_range_is_valid(addr
, size
))
129 page_prot
= phys_mem_access_prot(file
, vma
->vm_pgoff
,
130 size
, vma
->vm_page_prot
);
132 rc
= remap_pfn_range(vma
, vma
->vm_start
, vma
->vm_pgoff
, size
,
138 static bool opal_msg_queue_empty(void)
143 spin_lock_irqsave(&opal_prd_msg_queue_lock
, flags
);
144 ret
= list_empty(&opal_prd_msg_queue
);
145 spin_unlock_irqrestore(&opal_prd_msg_queue_lock
, flags
);
150 static __poll_t
opal_prd_poll(struct file
*file
,
151 struct poll_table_struct
*wait
)
153 poll_wait(file
, &opal_prd_msg_wait
, wait
);
155 if (!opal_msg_queue_empty())
156 return EPOLLIN
| EPOLLRDNORM
;
161 static ssize_t
opal_prd_read(struct file
*file
, char __user
*buf
,
162 size_t count
, loff_t
*ppos
)
164 struct opal_prd_msg_queue_item
*item
;
169 /* we need at least a header's worth of data */
170 if (count
< sizeof(item
->msg
))
180 spin_lock_irqsave(&opal_prd_msg_queue_lock
, flags
);
181 if (!list_empty(&opal_prd_msg_queue
)) {
182 item
= list_first_entry(&opal_prd_msg_queue
,
183 struct opal_prd_msg_queue_item
, list
);
184 list_del(&item
->list
);
186 spin_unlock_irqrestore(&opal_prd_msg_queue_lock
, flags
);
191 if (file
->f_flags
& O_NONBLOCK
)
194 rc
= wait_event_interruptible(opal_prd_msg_wait
,
195 !opal_msg_queue_empty());
200 size
= be16_to_cpu(item
->msg
.size
);
206 rc
= copy_to_user(buf
, &item
->msg
, size
);
217 /* eep! re-queue at the head of the list */
218 spin_lock_irqsave(&opal_prd_msg_queue_lock
, flags
);
219 list_add(&item
->list
, &opal_prd_msg_queue
);
220 spin_unlock_irqrestore(&opal_prd_msg_queue_lock
, flags
);
224 static ssize_t
opal_prd_write(struct file
*file
, const char __user
*buf
,
225 size_t count
, loff_t
*ppos
)
227 struct opal_prd_msg_header hdr
;
237 /* grab the header */
238 rc
= copy_from_user(&hdr
, buf
, sizeof(hdr
));
242 size
= be16_to_cpu(hdr
.size
);
244 msg
= memdup_user(buf
, size
);
248 rc
= opal_prd_msg(msg
);
250 pr_warn("write: opal_prd_msg returned %d\n", rc
);
259 static int opal_prd_release(struct inode
*inode
, struct file
*file
)
261 struct opal_prd_msg_header msg
;
263 msg
.size
= cpu_to_be16(sizeof(msg
));
264 msg
.type
= OPAL_PRD_MSG_TYPE_FINI
;
266 opal_prd_msg((struct opal_prd_msg
*)&msg
);
268 atomic_xchg(&prd_usage
, 0);
273 static long opal_prd_ioctl(struct file
*file
, unsigned int cmd
,
276 struct opal_prd_info info
;
277 struct opal_prd_scom scom
;
281 case OPAL_PRD_GET_INFO
:
282 memset(&info
, 0, sizeof(info
));
283 info
.version
= OPAL_PRD_KERNEL_VERSION
;
284 rc
= copy_to_user((void __user
*)param
, &info
, sizeof(info
));
289 case OPAL_PRD_SCOM_READ
:
290 rc
= copy_from_user(&scom
, (void __user
*)param
, sizeof(scom
));
294 scom
.rc
= opal_xscom_read(scom
.chip
, scom
.addr
,
295 (__be64
*)&scom
.data
);
296 scom
.data
= be64_to_cpu(scom
.data
);
297 pr_devel("ioctl SCOM_READ: chip %llx addr %016llx data %016llx rc %lld\n",
298 scom
.chip
, scom
.addr
, scom
.data
, scom
.rc
);
300 rc
= copy_to_user((void __user
*)param
, &scom
, sizeof(scom
));
305 case OPAL_PRD_SCOM_WRITE
:
306 rc
= copy_from_user(&scom
, (void __user
*)param
, sizeof(scom
));
310 scom
.rc
= opal_xscom_write(scom
.chip
, scom
.addr
, scom
.data
);
311 pr_devel("ioctl SCOM_WRITE: chip %llx addr %016llx data %016llx rc %lld\n",
312 scom
.chip
, scom
.addr
, scom
.data
, scom
.rc
);
314 rc
= copy_to_user((void __user
*)param
, &scom
, sizeof(scom
));
326 static const struct file_operations opal_prd_fops
= {
327 .open
= opal_prd_open
,
328 .mmap
= opal_prd_mmap
,
329 .poll
= opal_prd_poll
,
330 .read
= opal_prd_read
,
331 .write
= opal_prd_write
,
332 .unlocked_ioctl
= opal_prd_ioctl
,
333 .release
= opal_prd_release
,
334 .owner
= THIS_MODULE
,
337 static struct miscdevice opal_prd_dev
= {
338 .minor
= MISC_DYNAMIC_MINOR
,
340 .fops
= &opal_prd_fops
,
344 static int opal_prd_msg_notifier(struct notifier_block
*nb
,
345 unsigned long msg_type
, void *_msg
)
347 struct opal_prd_msg_queue_item
*item
;
348 struct opal_prd_msg_header
*hdr
;
349 struct opal_msg
*msg
= _msg
;
350 int msg_size
, item_size
;
353 if (msg_type
!= OPAL_MSG_PRD
)
356 /* Calculate total size of the message and item we need to store. The
357 * 'size' field in the header includes the header itself. */
358 hdr
= (void *)msg
->params
;
359 msg_size
= be16_to_cpu(hdr
->size
);
360 item_size
= msg_size
+ sizeof(*item
) - sizeof(item
->msg
);
362 item
= kzalloc(item_size
, GFP_ATOMIC
);
366 memcpy(&item
->msg
, msg
->params
, msg_size
);
368 spin_lock_irqsave(&opal_prd_msg_queue_lock
, flags
);
369 list_add_tail(&item
->list
, &opal_prd_msg_queue
);
370 spin_unlock_irqrestore(&opal_prd_msg_queue_lock
, flags
);
372 wake_up_interruptible(&opal_prd_msg_wait
);
377 static struct notifier_block opal_prd_event_nb
= {
378 .notifier_call
= opal_prd_msg_notifier
,
383 static int opal_prd_probe(struct platform_device
*pdev
)
387 if (!pdev
|| !pdev
->dev
.of_node
)
390 /* We should only have one prd driver instance per machine; ensure
391 * that we only get a valid probe on a single OF node.
396 prd_node
= pdev
->dev
.of_node
;
398 rc
= opal_message_notifier_register(OPAL_MSG_PRD
, &opal_prd_event_nb
);
400 pr_err("Couldn't register event notifier\n");
404 rc
= misc_register(&opal_prd_dev
);
406 pr_err("failed to register miscdev\n");
407 opal_message_notifier_unregister(OPAL_MSG_PRD
,
415 static int opal_prd_remove(struct platform_device
*pdev
)
417 misc_deregister(&opal_prd_dev
);
418 opal_message_notifier_unregister(OPAL_MSG_PRD
, &opal_prd_event_nb
);
422 static const struct of_device_id opal_prd_match
[] = {
423 { .compatible
= "ibm,opal-prd" },
427 static struct platform_driver opal_prd_driver
= {
430 .of_match_table
= opal_prd_match
,
432 .probe
= opal_prd_probe
,
433 .remove
= opal_prd_remove
,
436 module_platform_driver(opal_prd_driver
);
438 MODULE_DEVICE_TABLE(of
, opal_prd_match
);
439 MODULE_DESCRIPTION("PowerNV OPAL runtime diagnostic driver");
440 MODULE_LICENSE("GPL");