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
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
* 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,
};
|