1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, Kuan-Wei Chiu <[email protected]>
* Goldfish TTY driver for U-Boot
*/
#include <dm.h>
#include <goldfish_tty.h>
#include <mapmem.h>
#include <serial.h>
#include <asm/io.h>
#include <linux/types.h>
/* Goldfish TTY Register Offsets */
#define GOLDFISH_TTY_PUT_CHAR 0x00
#define GOLDFISH_TTY_BYTES_READY 0x04
#define GOLDFISH_TTY_CMD 0x08
#define GOLDFISH_TTY_DATA_PTR 0x10
#define GOLDFISH_TTY_DATA_LEN 0x14
#define GOLDFISH_TTY_DATA_PTR_HIGH 0x18
#define GOLDFISH_TTY_VERSION 0x20
/* Commands */
#define CMD_WRITE_BUFFER 2
#define CMD_READ_BUFFER 3
struct goldfish_tty_priv {
void __iomem *base;
u8 rx_buf;
};
static int goldfish_serial_getc(struct udevice *dev)
{
struct goldfish_tty_priv *priv = dev_get_priv(dev);
unsigned long paddr;
u32 count;
count = __raw_readl(priv->base + GOLDFISH_TTY_BYTES_READY);
if (count == 0)
return -EAGAIN;
paddr = virt_to_phys((void *)&priv->rx_buf);
__raw_writel(0, priv->base + GOLDFISH_TTY_DATA_PTR_HIGH);
__raw_writel(paddr, priv->base + GOLDFISH_TTY_DATA_PTR);
__raw_writel(1, priv->base + GOLDFISH_TTY_DATA_LEN);
__raw_writel(CMD_READ_BUFFER, priv->base + GOLDFISH_TTY_CMD);
return priv->rx_buf;
}
static int goldfish_serial_putc(struct udevice *dev, const char ch)
{
struct goldfish_tty_priv *priv = dev_get_priv(dev);
__raw_writel(ch, priv->base + GOLDFISH_TTY_PUT_CHAR);
return 0;
}
static int goldfish_serial_pending(struct udevice *dev, bool input)
{
struct goldfish_tty_priv *priv = dev_get_priv(dev);
if (input)
return __raw_readl(priv->base + GOLDFISH_TTY_BYTES_READY);
return 0;
}
static int goldfish_serial_of_to_plat(struct udevice *dev)
{
struct goldfish_tty_plat *plat = dev_get_plat(dev);
fdt_addr_t addr;
addr = dev_read_addr(dev);
if (addr != FDT_ADDR_T_NONE)
plat->reg = addr;
return 0;
}
static int goldfish_serial_probe(struct udevice *dev)
{
struct goldfish_tty_plat *plat = dev_get_plat(dev);
struct goldfish_tty_priv *priv = dev_get_priv(dev);
if (!plat->reg)
return -EINVAL;
priv->base = map_sysmem(plat->reg, 0x1000);
return 0;
}
static const struct dm_serial_ops goldfish_serial_ops = {
.putc = goldfish_serial_putc,
.pending = goldfish_serial_pending,
.getc = goldfish_serial_getc,
};
static const struct udevice_id goldfish_serial_ids[] = {
{ .compatible = "google,goldfish-tty" },
{ }
};
U_BOOT_DRIVER(serial_goldfish) = {
.name = "serial_goldfish",
.id = UCLASS_SERIAL,
.of_match = goldfish_serial_ids,
.of_to_plat = goldfish_serial_of_to_plat,
.plat_auto = sizeof(struct goldfish_tty_plat),
.probe = goldfish_serial_probe,
.ops = &goldfish_serial_ops,
.priv_auto = sizeof(struct goldfish_tty_priv),
.flags = DM_FLAG_PRE_RELOC,
};
#ifdef CONFIG_DEBUG_UART_GOLDFISH
#include <debug_uart.h>
static inline void _debug_uart_init(void)
{
}
static inline void _debug_uart_putc(int ch)
{
void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE);
__raw_writel(ch, base + GOLDFISH_TTY_PUT_CHAR);
}
DEBUG_UART_FUNCS
#endif
|