2 * OPAL Operator Panel Display Driver
4 * Copyright 2016, Suraj Jitindar Singh, IBM Corporation.
7 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/kernel.h>
13 #include <linux/device.h>
14 #include <linux/errno.h>
15 #include <linux/mutex.h>
17 #include <linux/slab.h>
18 #include <linux/platform_device.h>
19 #include <linux/miscdevice.h>
24 * This driver creates a character device (/dev/op_panel) which exposes the
25 * operator panel (character LCD display) on IBM Power Systems machines
27 * A character buffer written to the device will be displayed on the
31 static DEFINE_MUTEX(oppanel_mutex
);
33 static u32 num_lines
, oppanel_size
;
34 static oppanel_line_t
*oppanel_lines
;
35 static char *oppanel_data
;
37 static loff_t
oppanel_llseek(struct file
*filp
, loff_t offset
, int whence
)
39 return fixed_size_llseek(filp
, offset
, whence
, oppanel_size
);
42 static ssize_t
oppanel_read(struct file
*filp
, char __user
*userbuf
, size_t len
,
45 return simple_read_from_buffer(userbuf
, len
, f_pos
, oppanel_data
,
49 static int __op_panel_update_display(void)
54 token
= opal_async_get_token_interruptible();
56 if (token
!= -ERESTARTSYS
)
57 pr_debug("Couldn't get OPAL async token [token=%d]\n",
62 rc
= opal_write_oppanel_async(token
, oppanel_lines
, num_lines
);
64 case OPAL_ASYNC_COMPLETION
:
65 rc
= opal_async_wait_response(token
, &msg
);
67 pr_debug("Failed to wait for async response [rc=%d]\n",
71 rc
= opal_get_async_rc(msg
);
72 if (rc
!= OPAL_SUCCESS
) {
73 pr_debug("OPAL async call returned failed [rc=%d]\n",
80 pr_debug("OPAL write op-panel call failed [rc=%d]\n", rc
);
83 opal_async_release_token(token
);
87 static ssize_t
oppanel_write(struct file
*filp
, const char __user
*userbuf
,
88 size_t len
, loff_t
*f_pos
)
90 loff_t f_pos_prev
= *f_pos
;
95 memset(oppanel_data
, ' ', oppanel_size
);
96 else if (*f_pos
>= oppanel_size
)
99 ret
= simple_write_to_buffer(oppanel_data
, oppanel_size
, f_pos
, userbuf
,
102 rc
= __op_panel_update_display();
103 if (rc
!= OPAL_SUCCESS
) {
104 pr_err_ratelimited("OPAL call failed to write to op panel display [rc=%d]\n",
113 static int oppanel_open(struct inode
*inode
, struct file
*filp
)
115 if (!mutex_trylock(&oppanel_mutex
)) {
116 pr_debug("Device Busy\n");
122 static int oppanel_release(struct inode
*inode
, struct file
*filp
)
124 mutex_unlock(&oppanel_mutex
);
128 static const struct file_operations oppanel_fops
= {
129 .owner
= THIS_MODULE
,
130 .llseek
= oppanel_llseek
,
131 .read
= oppanel_read
,
132 .write
= oppanel_write
,
133 .open
= oppanel_open
,
134 .release
= oppanel_release
137 static struct miscdevice oppanel_dev
= {
138 .minor
= MISC_DYNAMIC_MINOR
,
140 .fops
= &oppanel_fops
143 static int oppanel_probe(struct platform_device
*pdev
)
145 struct device_node
*np
= pdev
->dev
.of_node
;
149 rc
= of_property_read_u32(np
, "#length", &line_len
);
151 pr_err_ratelimited("Operator panel length property not found\n");
154 rc
= of_property_read_u32(np
, "#lines", &num_lines
);
156 pr_err_ratelimited("Operator panel lines property not found\n");
159 oppanel_size
= line_len
* num_lines
;
161 pr_devel("Operator panel of size %u found with %u lines of length %u\n",
162 oppanel_size
, num_lines
, line_len
);
164 oppanel_data
= kcalloc(oppanel_size
, sizeof(*oppanel_data
), GFP_KERNEL
);
168 oppanel_lines
= kcalloc(num_lines
, sizeof(oppanel_line_t
), GFP_KERNEL
);
169 if (!oppanel_lines
) {
171 goto free_oppanel_data
;
174 memset(oppanel_data
, ' ', oppanel_size
);
175 for (i
= 0; i
< num_lines
; i
++) {
176 oppanel_lines
[i
].line_len
= cpu_to_be64(line_len
);
177 oppanel_lines
[i
].line
= cpu_to_be64(__pa(&oppanel_data
[i
*
181 rc
= misc_register(&oppanel_dev
);
183 pr_err_ratelimited("Failed to register as misc device\n");
190 kfree(oppanel_lines
);
196 static int oppanel_remove(struct platform_device
*pdev
)
198 misc_deregister(&oppanel_dev
);
199 kfree(oppanel_lines
);
204 static const struct of_device_id oppanel_match
[] = {
205 { .compatible
= "ibm,opal-oppanel" },
209 static struct platform_driver oppanel_driver
= {
211 .name
= "powernv-op-panel",
212 .of_match_table
= oppanel_match
,
214 .probe
= oppanel_probe
,
215 .remove
= oppanel_remove
,
218 module_platform_driver(oppanel_driver
);
220 MODULE_DEVICE_TABLE(of
, oppanel_match
);
221 MODULE_LICENSE("GPL v2");
222 MODULE_DESCRIPTION("PowerNV Operator Panel LCD Display Driver");
223 MODULE_AUTHOR("Suraj Jitindar Singh <sjitindarsingh@gmail.com>");