1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2004, Psyent Corporation <www.psyent.com>
4 * Scott McNutt <smcnutt@psyent.com>
5 */
6
7#include <common.h>
8#include <dm.h>
9#include <errno.h>
10#include <serial.h>
11#include <asm/io.h>
12#include <linux/bitops.h>
13
14/* data register */
15#define ALTERA_JTAG_RVALID	BIT(15)	/* Read valid */
16
17/* control register */
18#define ALTERA_JTAG_AC		BIT(10)	/* activity indicator */
19#define ALTERA_JTAG_RRDY	BIT(12)	/* read available */
20#define ALTERA_JTAG_WSPACE(d)	((d) >> 16)	/* Write space avail */
21/* Write fifo size. FIXME: this should be extracted with sopc2dts */
22#define ALTERA_JTAG_WRITE_DEPTH	64
23
24struct altera_jtaguart_regs {
25	u32	data;			/* Data register */
26	u32	control;		/* Control register */
27};
28
29struct altera_jtaguart_plat {
30	struct altera_jtaguart_regs *regs;
31};
32
33static int altera_jtaguart_setbrg(struct udevice *dev, int baudrate)
34{
35	return 0;
36}
37
38static int altera_jtaguart_putc(struct udevice *dev, const char ch)
39{
40	struct altera_jtaguart_plat *plat = dev_get_plat(dev);
41	struct altera_jtaguart_regs *const regs = plat->regs;
42	u32 st = readl(&regs->control);
43
44#ifdef CONFIG_ALTERA_JTAG_UART_BYPASS
45	if (!(st & ALTERA_JTAG_AC)) /* no connection yet */
46		return -ENETUNREACH;
47#endif
48
49	if (ALTERA_JTAG_WSPACE(st) == 0)
50		return -EAGAIN;
51
52	writel(ch, &regs->data);
53
54	return 0;
55}
56
57static int altera_jtaguart_pending(struct udevice *dev, bool input)
58{
59	struct altera_jtaguart_plat *plat = dev_get_plat(dev);
60	struct altera_jtaguart_regs *const regs = plat->regs;
61	u32 st = readl(&regs->control);
62
63	if (input)
64		return st & ALTERA_JTAG_RRDY ? 1 : 0;
65	else
66		return !(ALTERA_JTAG_WSPACE(st) == ALTERA_JTAG_WRITE_DEPTH);
67}
68
69static int altera_jtaguart_getc(struct udevice *dev)
70{
71	struct altera_jtaguart_plat *plat = dev_get_plat(dev);
72	struct altera_jtaguart_regs *const regs = plat->regs;
73	u32 val;
74
75	val = readl(&regs->data);
76
77	if (!(val & ALTERA_JTAG_RVALID))
78		return -EAGAIN;
79
80	return val & 0xff;
81}
82
83static int altera_jtaguart_probe(struct udevice *dev)
84{
85#ifdef CONFIG_ALTERA_JTAG_UART_BYPASS
86	struct altera_jtaguart_plat *plat = dev_get_plat(dev);
87	struct altera_jtaguart_regs *const regs = plat->regs;
88
89	writel(ALTERA_JTAG_AC, &regs->control); /* clear AC flag */
90#endif
91	return 0;
92}
93
94static int altera_jtaguart_of_to_plat(struct udevice *dev)
95{
96	struct altera_jtaguart_plat *plat = dev_get_plat(dev);
97
98	plat->regs = map_physmem(dev_read_addr(dev),
99				 sizeof(struct altera_jtaguart_regs),
100				 MAP_NOCACHE);
101
102	return 0;
103}
104
105static const struct dm_serial_ops altera_jtaguart_ops = {
106	.putc = altera_jtaguart_putc,
107	.pending = altera_jtaguart_pending,
108	.getc = altera_jtaguart_getc,
109	.setbrg = altera_jtaguart_setbrg,
110};
111
112static const struct udevice_id altera_jtaguart_ids[] = {
113	{ .compatible = "altr,juart-1.0" },
114	{}
115};
116
117U_BOOT_DRIVER(altera_jtaguart) = {
118	.name	= "altera_jtaguart",
119	.id	= UCLASS_SERIAL,
120	.of_match = altera_jtaguart_ids,
121	.of_to_plat = altera_jtaguart_of_to_plat,
122	.plat_auto	= sizeof(struct altera_jtaguart_plat),
123	.probe = altera_jtaguart_probe,
124	.ops	= &altera_jtaguart_ops,
125};
126
127#ifdef CONFIG_DEBUG_UART_ALTERA_JTAGUART
128
129#include <debug_uart.h>
130
131static inline void _debug_uart_init(void)
132{
133}
134
135static inline void _debug_uart_putc(int ch)
136{
137	struct altera_jtaguart_regs *regs = (void *)CONFIG_VAL(DEBUG_UART_BASE);
138
139	while (1) {
140		u32 st = readl(&regs->control);
141
142		if (ALTERA_JTAG_WSPACE(st))
143			break;
144	}
145
146	writel(ch, &regs->data);
147}
148
149DEBUG_UART_FUNCS
150
151#endif
152