Updated `README.md` with instructions for building/using the kernel module.
[xeon-phi-kernel-module] / vcons / hvc_mic.c
CommitLineData
800f879a
AT
1/*
2 * Copyright 2010-2017 Intel Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * Disclaimer: The codes contained in these modules may be specific to
14 * the Intel Software Development Platform codenamed Knights Ferry,
15 * and the Intel product codenamed Knights Corner, and are not backward
16 * compatible with other Intel products. Additionally, Intel will NOT
17 * support the codes or instruction set in future products.
18 *
19 * Intel offers no warranty of any kind regarding the code. This code is
20 * licensed on an "AS IS" basis and Intel is not obligated to provide
21 * any support, assistance, installation, training, or other services
22 * of any kind. Intel is also not obligated to provide any updates,
23 * enhancements or extensions. Intel specifically disclaims any warranty
24 * of merchantability, non-infringement, fitness for any particular
25 * purpose, and any other warranty.
26 *
27 * Further, Intel disclaims all liability of any kind, including but
28 * not limited to liability for infringement of any proprietary rights,
29 * relating to the use of the code, even if Intel is notified of the
30 * possibility of such liability. Except as expressly stated in an Intel
31 * license agreement provided with this code and agreed upon with Intel,
32 * no license, express or implied, by estoppel or otherwise, to any
33 * intellectual property rights is granted herein.
34 */
35
36#include <linux/module.h>
37#include <linux/mm.h>
38#include "hvc_console.h"
39#include <mic/micscif_rb.h>
40#include <mic/micvcons.h>
41#include <asm/io.h>
42#include <linux/errno.h>
43#include <linux/hardirq.h>
44#include <linux/types.h>
45#include <linux/capability.h>
46#include <linux/slab.h>
47#include <linux/string.h>
48#include <linux/gfp.h>
49#include <linux/vmalloc.h>
50#include <asm/io.h>
51#include <linux/kernel.h>
52#include <linux/mm_types.h>
53#include <linux/jiffies.h>
54#include <linux/timer.h>
55#include <linux/irqflags.h>
56#include <linux/time.h>
57#include <linux/spinlock.h>
58#include <linux/mutex.h>
59#include <linux/semaphore.h>
60#include <linux/kthread.h>
61#include <linux/sched.h>
62#include <linux/delay.h>
63#include <linux/wait.h>
64#include <asm/bug.h>
65#include <linux/pci.h>
66#include <linux/device.h>
67#include <linux/fs.h>
68#include <linux/list.h>
69#include <linux/workqueue.h>
70#include <linux/interrupt.h>
71#include <asm/atomic.h>
72#include <linux/netdevice.h>
73#include <linux/debugfs.h>
74#include <linux/interrupt.h>
75#include <asm/mic/mic_common.h>
76
77#define MIC_COOKIE 0xc0c0
78#define MIC_KNC 1
79
80static long vcons_hdr_addr;
81static struct micscif_rb mic_out_buf;
82static struct micscif_rb mic_in_buf;
83
84struct vcons_info {
85 struct vcons_buf *hdr;
86 struct vcons_mic_header *mic_hdr;
87 char *vcons_op_buf;
88 char *vcons_ip_buf;
89};
90
91static struct vcons_info vcons_info;
92static int dbg = 0;
93
94/* Receive data from the host (mic i/p buffer) */
95static int hvc_mic_get_chars(uint32_t vt, char *buf, int count)
96{
97 int ret, len, get_count;
98
99 len = micscif_rb_count(&mic_in_buf, count);
100 get_count = min(len, count);
101 ret = micscif_rb_get_next(&mic_in_buf, buf, get_count);
102 if (ret == get_count)
103 micscif_rb_update_read_ptr(&mic_in_buf);
104
105 return ret;
106}
107
108/* Send data to the host (mic o/p buffer) */
109static int hvc_mic_put_chars(uint32_t vt, const char *buf, int count)
110{
111 int ret;
112 int put_count;
113 volatile int *host_status =
114 (volatile int *)&vcons_info.mic_hdr->host_status;
115
116 put_count = min(micscif_rb_space(&mic_out_buf), count);
117 if (put_count) {
118 ret = micscif_rb_write(&mic_out_buf, (void *)buf, put_count);
119 BUG_ON(ret);
120 micscif_rb_commit(&mic_out_buf);
121 } else if (*host_status != MIC_VCONS_HOST_OPEN)
122 return count;
123 return put_count;
124}
125
126
127static irqreturn_t hvc_mic_handle_interrupt(int irq, void *dev_id)
128{
129 struct hvc_struct *hp = (struct hvc_struct *)dev_id;
130 if (hvc_poll(hp)) {
131 hvc_kick();
132 return IRQ_HANDLED;
133 }
134 return IRQ_NONE;
135}
136
137static int hvc_mic_notifier_add_irq(struct hvc_struct *hp, int irq)
138{
139 int ret = request_irq(get_sbox_irq(HVC_SBOX_INT_IDX),
140 hvc_mic_handle_interrupt, IRQF_DISABLED,
141 "hvc intr", hp);
142 if (ret) {
143 printk("Unable to register interrupt\n");
144 return ret;
145 }
146 hp->irq_requested = 1;
147 return 0;
148}
149
150static void hvc_mic_notifier_del_irq(struct hvc_struct *hp, int irq)
151{
152 if (hp->irq_requested) {
153 free_irq(get_sbox_irq(HVC_SBOX_INT_IDX), hp);
154 hp->irq_requested = 0;
155 }
156}
157
158static void hvc_mic_notifier_hangup_irq(struct hvc_struct *hp, int irq)
159{
160 hvc_mic_notifier_del_irq(hp, irq);
161}
162
163static const struct hv_ops hvc_mic_ops = {
164 .get_chars = hvc_mic_get_chars,
165 .put_chars = hvc_mic_put_chars,
166 .notifier_add = hvc_mic_notifier_add_irq,
167 .notifier_del = hvc_mic_notifier_del_irq,
168 .notifier_hangup = hvc_mic_notifier_hangup_irq,
169};
170
171static void dump_vcons_hdr(struct vcons_buf *hdr)
172{
173 printk(KERN_ERR "host_magic\t%x\n", readl(&hdr->host_magic));
174 printk(KERN_ERR "mic_magic\t%x\n", readl(&hdr->mic_magic));
175 printk(KERN_ERR "o_buf_dma_addr\t%x\n", readl(&hdr->o_buf_dma_addr));
176 printk(KERN_ERR "o_wr\t%x\n", readl(&hdr->o_wr));
177 printk(KERN_ERR "o_size\t%x\n", readl(&hdr->o_size));
178 printk(KERN_ERR "i_hdr_addr\t%lx\n", readq(&hdr->i_hdr_addr));
179 printk(KERN_ERR "i_buf_addr\t%lx\n", readq(&hdr->i_buf_addr));
180 printk(KERN_ERR "i_rd\t%x\n", readl(&hdr->i_rd));
181}
182
183static int mic_cons_init(void)
184{
185 int rc;
186
187 if ((rc = hvc_instantiate(MIC_COOKIE, 0, &hvc_mic_ops)))
188 printk(KERN_ERR "error instantiating hvc console\n");
189
190 return rc;
191}
192
193static struct hvc_struct *hp;
194static int __init hvc_mic_init(void)
195{
196 struct vcons_buf *hdr = NULL;
197 struct vcons_buf tmp_hdr;
198 int err = 0;
199 char *hvc_buf;
200 u8 card_type=0;
201 uint16_t host_rb_ver, mic_rb_ver;
202
203#if defined(CONFIG_MK1OM)
204 card_type = MIC_KNC;
205#endif
206 hvc_buf = (char *)get_zeroed_page(GFP_KERNEL);
207 if (!hvc_buf) {
208 printk(KERN_ERR "unable to allocate vcons buffer\n");
209 return -ENOMEM;
210 }
211 if (card_type == MIC_KNC) {
212 vcons_info.vcons_ip_buf = hvc_buf;
213 vcons_info.mic_hdr = (struct vcons_mic_header *)kzalloc(sizeof(struct vcons_mic_header), GFP_KERNEL);
214 if (!vcons_info.mic_hdr) {
215 free_page((unsigned long)hvc_buf);
216 printk(KERN_ERR "unable to allocate vcons header\n");
217 return -ENOMEM;
218 }
219 } else {
220 vcons_info.vcons_ip_buf = hvc_buf + PAGE_SIZE/2;
221 vcons_info.mic_hdr = (struct vcons_mic_header *)hvc_buf;
222 }
223
224 vcons_info.hdr = hdr = ioremap_nocache(vcons_hdr_addr,
225 sizeof(struct vcons_buf));
226 if (!hdr) {
227 printk(KERN_ERR "unable to map vcons header\n");
228 err = -ENOMEM;
229 goto error;
230 }
231
232 if (dbg)
233 dump_vcons_hdr(hdr);
234
235 if (readl(&hdr->host_magic) != MIC_HOST_VCONS_READY) {
236 printk(KERN_ERR "host not ready, giving up\n");
237 err = -ENODEV;
238 goto error;
239 }
240
241 host_rb_ver = readw(&hdr->host_rb_ver);
242 mic_rb_ver = micscif_rb_get_version();
243 writew(mic_rb_ver, &hdr->mic_rb_ver);
244 if (host_rb_ver != mic_rb_ver) {
245 printk(KERN_ERR "Card and host ring buffer versions mismatch.");
246 printk(KERN_ERR "Card ver: %d, Host ver: %d \n", mic_rb_ver,
247 host_rb_ver);
248 writel(MIC_VCONS_RB_VER_ERR, &hdr->mic_magic);
249 err = -ENXIO;
250 goto error;
251 }
252 memcpy_fromio(&tmp_hdr, hdr, sizeof(struct vcons_buf));
253
254 if (!(vcons_info.vcons_op_buf = ioremap_nocache(tmp_hdr.o_buf_dma_addr,
255 tmp_hdr.o_size))) {
256 printk(KERN_ERR "unable to map vcons output buffer\n");
257 err = -ENOMEM;
258 goto error;
259 }
260
261 tmp_hdr.i_hdr_addr = virt_to_phys(vcons_info.mic_hdr);
262 tmp_hdr.i_buf_addr = virt_to_phys(vcons_info.vcons_ip_buf);
263
264 if (card_type == MIC_KNC)
265 tmp_hdr.i_size = PAGE_SIZE;
266 else
267 tmp_hdr.i_size = PAGE_SIZE/2;
268
269 micscif_rb_init(&mic_out_buf, (volatile uint32_t *)&vcons_info.mic_hdr->o_rd,
270 (volatile uint32_t *)&hdr->o_wr,
271 (volatile uint32_t *)vcons_info.vcons_op_buf,
272 tmp_hdr.o_size);
273
274 micscif_rb_init(&mic_in_buf,
275 (volatile uint32_t *)&hdr->i_rd,
276 (volatile uint32_t *)&vcons_info.mic_hdr->i_wr,
277 (volatile uint32_t *)vcons_info.vcons_ip_buf,
278 tmp_hdr.i_size);
279
280 mic_cons_init();
281 hp = hvc_alloc(MIC_COOKIE, 2, &hvc_mic_ops, 128);
282
283 if (IS_ERR(hp)) {
284 printk(KERN_ERR "unable to allocate hvc console\n");
285 err = PTR_ERR(hp);
286 } else {
287 writeq(tmp_hdr.i_hdr_addr, &hdr->i_hdr_addr);
288 writeq(tmp_hdr.i_buf_addr, &hdr->i_buf_addr);
289 writel(tmp_hdr.i_size, &hdr->i_size);
290 writel(MIC_VCONS_READY, &hdr->mic_magic);
291 if (dbg)
292 dump_vcons_hdr(hdr);
293
294 return 0;
295 }
296error:
297 if (hdr)
298 iounmap(hdr);
299 if (vcons_info.vcons_op_buf)
300 iounmap(vcons_info.vcons_op_buf);
301#if defined(CONFIG_MK1OM)
302 free_page((unsigned long)vcons_info.vcons_ip_buf);
303 kfree(vcons_info.mic_hdr);
304#else
305 free_page((unsigned long)vcons_info.mic_hdr);
306#endif
307 return err;
308}
309
310static void __exit hvc_mic_exit(void)
311{
312 char buf[8];
313 int ret, len;
314
315 writel(0, &vcons_info.hdr->mic_magic);
316
317 do {
318 len = micscif_rb_count(&mic_in_buf, sizeof(buf));
319 ret = micscif_rb_get_next(&mic_in_buf, buf,
320 min(len, (int)sizeof(buf)));
321 } while (ret > 0);
322
323 iounmap(vcons_info.hdr);
324 iounmap(vcons_info.vcons_op_buf);
325#if defined(CONFIG_MK1OM)
326 free_page((unsigned long)vcons_info.vcons_ip_buf);
327 kfree(vcons_info.mic_hdr);
328#else
329 free_page((unsigned long)vcons_info.mic_hdr);
330#endif
331 if (hp)
332 hvc_remove(hp);
333}
334
335MODULE_PARM_DESC(vcons_hdr_addr, "mic address of vcons hdr");
336module_param(vcons_hdr_addr, long, S_IRUGO);
337module_param(dbg, int, S_IRUGO);
338MODULE_LICENSE("GPL");
339module_init(hvc_mic_init);
340module_exit(hvc_mic_exit);
341