1 // SPDX-License-Identifier: GPL-2.0
3 * Privileged ADI driver for sparc64
5 * Author: Tom Hromatka <tom.hromatka@oracle.com>
7 #include <linux/kernel.h>
8 #include <linux/miscdevice.h>
9 #include <linux/module.h>
10 #include <linux/proc_fs.h>
11 #include <linux/slab.h>
12 #include <linux/uaccess.h>
15 #define MAX_BUF_SZ PAGE_SIZE
17 static int read_mcd_tag(unsigned long addr
)
23 "1: ldxa [%[addr]] %[asi], %[ver]\n"
26 " .section .fixup,#alloc,#execinstr\n"
28 "3: sethi %%hi(2b), %%g1\n"
29 " jmpl %%g1 + %%lo(2b), %%g0\n"
30 " mov %[invalid], %[err]\n"
32 " .section __ex_table, \"a\"\n"
36 : [ver
] "=r" (ver
), [err
] "=r" (err
)
37 : [addr
] "r" (addr
), [invalid
] "i" (EFAULT
),
38 [asi
] "i" (ASI_MCD_REAL
)
48 static ssize_t
adi_read(struct file
*file
, char __user
*buf
,
49 size_t count
, loff_t
*offp
)
51 size_t ver_buf_sz
, bytes_read
= 0;
57 ver_buf_sz
= min_t(size_t, count
, MAX_BUF_SZ
);
58 ver_buf
= kmalloc(ver_buf_sz
, GFP_KERNEL
);
62 offset
= (*offp
) * adi_blksize();
64 while (bytes_read
< count
) {
65 ret
= read_mcd_tag(offset
);
69 ver_buf
[ver_buf_idx
] = (u8
)ret
;
71 offset
+= adi_blksize();
73 if (ver_buf_idx
>= ver_buf_sz
) {
74 if (copy_to_user(buf
+ bytes_read
, ver_buf
,
80 bytes_read
+= ver_buf_sz
;
83 ver_buf_sz
= min(count
- bytes_read
,
88 (*offp
) += bytes_read
;
95 static int set_mcd_tag(unsigned long addr
, u8 ver
)
100 "1: stxa %[ver], [%[addr]] %[asi]\n"
103 " .section .fixup,#alloc,#execinstr\n"
105 "3: sethi %%hi(2b), %%g1\n"
106 " jmpl %%g1 + %%lo(2b), %%g0\n"
107 " mov %[invalid], %[err]\n"
109 " .section __ex_table, \"a\"\n"
114 : [ver
] "r" (ver
), [addr
] "r" (addr
),
115 [invalid
] "i" (EFAULT
), [asi
] "i" (ASI_MCD_REAL
)
125 static ssize_t
adi_write(struct file
*file
, const char __user
*buf
,
126 size_t count
, loff_t
*offp
)
128 size_t ver_buf_sz
, bytes_written
= 0;
137 ver_buf_sz
= min_t(size_t, count
, MAX_BUF_SZ
);
138 ver_buf
= kmalloc(ver_buf_sz
, GFP_KERNEL
);
142 offset
= (*offp
) * adi_blksize();
145 if (copy_from_user(ver_buf
, &buf
[bytes_written
],
151 for (i
= 0; i
< ver_buf_sz
; i
++) {
152 ret
= set_mcd_tag(offset
, ver_buf
[i
]);
156 offset
+= adi_blksize();
159 bytes_written
+= ver_buf_sz
;
160 ver_buf_sz
= min(count
- bytes_written
, (size_t)MAX_BUF_SZ
);
161 } while (bytes_written
< count
);
163 (*offp
) += bytes_written
;
166 __asm__
__volatile__("membar #Sync");
171 static loff_t
adi_llseek(struct file
*file
, loff_t offset
, int whence
)
173 loff_t ret
= -EINVAL
;
185 offset
+= file
->f_pos
;
191 if (offset
!= file
->f_pos
) {
192 file
->f_pos
= offset
;
199 static const struct file_operations adi_fops
= {
200 .owner
= THIS_MODULE
,
201 .llseek
= adi_llseek
,
204 .fop_flags
= FOP_UNSIGNED_OFFSET
,
207 static struct miscdevice adi_miscdev
= {
208 .minor
= MISC_DYNAMIC_MINOR
,
209 .name
= KBUILD_MODNAME
,
213 static int __init
adi_init(void)
218 return misc_register(&adi_miscdev
);
221 static void __exit
adi_exit(void)
223 misc_deregister(&adi_miscdev
);
226 module_init(adi_init
);
227 module_exit(adi_exit
);
229 MODULE_AUTHOR("Tom Hromatka <tom.hromatka@oracle.com>");
230 MODULE_DESCRIPTION("Privileged interface to ADI");
231 MODULE_VERSION("1.0");
232 MODULE_LICENSE("GPL v2");