1/*
2 * Console via Blackfin JTAG Communication
3 *
4 * Copyright 2008-2011 Analog Devices Inc.
5 *
6 * Enter bugs at http://blackfin.uclinux.org/
7 *
8 * Licensed under the GPL-2 or later.
9 */
10
11#include <linux/console.h>
12#include <linux/delay.h>
13#include <linux/err.h>
14#include <linux/init.h>
15#include <linux/moduleparam.h>
16#include <linux/types.h>
17
18#include "hvc_console.h"
19
20/* See the Debug/Emulation chapter in the HRM */
21#define EMUDOF   0x00000001	/* EMUDAT_OUT full & valid */
22#define EMUDIF   0x00000002	/* EMUDAT_IN full & valid */
23#define EMUDOOVF 0x00000004	/* EMUDAT_OUT overflow */
24#define EMUDIOVF 0x00000008	/* EMUDAT_IN overflow */
25
26/* Helper functions to glue the register API to simple C operations */
27static inline uint32_t bfin_write_emudat(uint32_t emudat)
28{
29	__asm__ __volatile__("emudat = %0;" : : "d"(emudat));
30	return emudat;
31}
32
33static inline uint32_t bfin_read_emudat(void)
34{
35	uint32_t emudat;
36	__asm__ __volatile__("%0 = emudat;" : "=d"(emudat));
37	return emudat;
38}
39
40/* Send data to the host */
41static int hvc_bfin_put_chars(uint32_t vt, const char *buf, int count)
42{
43	static uint32_t outbound_len;
44	uint32_t emudat;
45	int ret;
46
47	if (bfin_read_DBGSTAT() & EMUDOF)
48		return 0;
49
50	if (!outbound_len) {
51		outbound_len = count;
52		bfin_write_emudat(outbound_len);
53		return 0;
54	}
55
56	ret = min(outbound_len, (uint32_t)4);
57	memcpy(&emudat, buf, ret);
58	bfin_write_emudat(emudat);
59	outbound_len -= ret;
60
61	return ret;
62}
63
64/* Receive data from the host */
65static int hvc_bfin_get_chars(uint32_t vt, char *buf, int count)
66{
67	static uint32_t inbound_len;
68	uint32_t emudat;
69	int ret;
70
71	if (!(bfin_read_DBGSTAT() & EMUDIF))
72		return 0;
73	emudat = bfin_read_emudat();
74
75	if (!inbound_len) {
76		inbound_len = emudat;
77		return 0;
78	}
79
80	ret = min(inbound_len, (uint32_t)4);
81	memcpy(buf, &emudat, ret);
82	inbound_len -= ret;
83
84	return ret;
85}
86
87/* Glue the HVC layers to the Blackfin layers */
88static const struct hv_ops hvc_bfin_get_put_ops = {
89	.get_chars = hvc_bfin_get_chars,
90	.put_chars = hvc_bfin_put_chars,
91};
92
93static int __init hvc_bfin_console_init(void)
94{
95	hvc_instantiate(0, 0, &hvc_bfin_get_put_ops);
96	return 0;
97}
98console_initcall(hvc_bfin_console_init);
99
100static int __init hvc_bfin_init(void)
101{
102	hvc_alloc(0, 0, &hvc_bfin_get_put_ops, 128);
103	return 0;
104}
105device_initcall(hvc_bfin_init);
106