]>
Commit | Line | Data |
---|---|---|
d6ba787e AP |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2022 Ventana Micro Systems Inc. | |
4 | */ | |
5 | ||
d678a59d | 6 | #include <common.h> |
d6ba787e AP |
7 | #include <dm.h> |
8 | #include <errno.h> | |
9 | #include <fdtdec.h> | |
10 | #include <log.h> | |
11 | #include <watchdog.h> | |
12 | #include <asm/global_data.h> | |
13 | #include <asm/io.h> | |
14 | #include <linux/compiler.h> | |
15 | #include <serial.h> | |
16 | #include <linux/err.h> | |
17 | ||
18 | DECLARE_GLOBAL_DATA_PTR; | |
19 | ||
20 | #define HTIF_DATA_BITS 48 | |
21 | #define HTIF_DATA_MASK ((1ULL << HTIF_DATA_BITS) - 1) | |
22 | #define HTIF_DATA_SHIFT 0 | |
23 | #define HTIF_CMD_BITS 8 | |
24 | #define HTIF_CMD_MASK ((1ULL << HTIF_CMD_BITS) - 1) | |
25 | #define HTIF_CMD_SHIFT 48 | |
26 | #define HTIF_DEV_BITS 8 | |
27 | #define HTIF_DEV_MASK ((1ULL << HTIF_DEV_BITS) - 1) | |
28 | #define HTIF_DEV_SHIFT 56 | |
29 | ||
30 | #define HTIF_DEV_SYSTEM 0 | |
31 | #define HTIF_DEV_CONSOLE 1 | |
32 | ||
33 | #define HTIF_CONSOLE_CMD_GETC 0 | |
34 | #define HTIF_CONSOLE_CMD_PUTC 1 | |
35 | ||
36 | #if __riscv_xlen == 64 | |
37 | # define TOHOST_CMD(dev, cmd, payload) \ | |
38 | (((u64)(dev) << HTIF_DEV_SHIFT) | \ | |
39 | ((u64)(cmd) << HTIF_CMD_SHIFT) | \ | |
40 | (u64)(payload)) | |
41 | #else | |
42 | # define TOHOST_CMD(dev, cmd, payload) ({ \ | |
43 | if ((dev) || (cmd)) \ | |
44 | __builtin_trap(); \ | |
45 | (payload); }) | |
46 | #endif | |
47 | #define FROMHOST_DEV(fromhost_value) \ | |
48 | ((u64)((fromhost_value) >> HTIF_DEV_SHIFT) & HTIF_DEV_MASK) | |
49 | #define FROMHOST_CMD(fromhost_value) \ | |
50 | ((u64)((fromhost_value) >> HTIF_CMD_SHIFT) & HTIF_CMD_MASK) | |
51 | #define FROMHOST_DATA(fromhost_value) \ | |
52 | ((u64)((fromhost_value) >> HTIF_DATA_SHIFT) & HTIF_DATA_MASK) | |
53 | ||
54 | struct htif_plat { | |
55 | void *fromhost; | |
56 | void *tohost; | |
57 | int console_char; | |
58 | }; | |
59 | ||
60 | static void __check_fromhost(struct htif_plat *plat) | |
61 | { | |
62 | u64 fh = readq(plat->fromhost); | |
63 | ||
64 | if (!fh) | |
65 | return; | |
66 | writeq(0, plat->fromhost); | |
67 | ||
68 | /* this should be from the console */ | |
69 | if (FROMHOST_DEV(fh) != HTIF_DEV_CONSOLE) | |
70 | __builtin_trap(); | |
71 | switch (FROMHOST_CMD(fh)) { | |
72 | case HTIF_CONSOLE_CMD_GETC: | |
73 | plat->console_char = 1 + (u8)FROMHOST_DATA(fh); | |
74 | break; | |
75 | case HTIF_CONSOLE_CMD_PUTC: | |
76 | break; | |
77 | default: | |
78 | __builtin_trap(); | |
79 | } | |
80 | } | |
81 | ||
82 | static void __set_tohost(struct htif_plat *plat, | |
83 | u64 dev, u64 cmd, u64 data) | |
84 | { | |
85 | while (readq(plat->tohost)) | |
86 | __check_fromhost(plat); | |
87 | writeq(TOHOST_CMD(dev, cmd, data), plat->tohost); | |
88 | } | |
89 | ||
90 | static int htif_serial_putc(struct udevice *dev, const char ch) | |
91 | { | |
92 | struct htif_plat *plat = dev_get_plat(dev); | |
93 | ||
94 | __set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_PUTC, ch); | |
95 | return 0; | |
96 | } | |
97 | ||
98 | static int htif_serial_getc(struct udevice *dev) | |
99 | { | |
100 | int ch; | |
101 | struct htif_plat *plat = dev_get_plat(dev); | |
102 | ||
103 | if (plat->console_char < 0) | |
104 | __check_fromhost(plat); | |
105 | ||
106 | if (plat->console_char >= 0) { | |
107 | ch = plat->console_char; | |
108 | plat->console_char = -1; | |
109 | __set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0); | |
110 | return (ch) ? ch - 1 : -EAGAIN; | |
111 | } | |
112 | ||
113 | return -EAGAIN; | |
114 | } | |
115 | ||
116 | static int htif_serial_pending(struct udevice *dev, bool input) | |
117 | { | |
118 | struct htif_plat *plat = dev_get_plat(dev); | |
119 | ||
120 | if (!input) | |
121 | return 0; | |
122 | ||
123 | if (plat->console_char < 0) | |
124 | __check_fromhost(plat); | |
125 | ||
126 | return (plat->console_char >= 0) ? 1 : 0; | |
127 | } | |
128 | ||
129 | static int htif_serial_probe(struct udevice *dev) | |
130 | { | |
131 | struct htif_plat *plat = dev_get_plat(dev); | |
132 | ||
133 | /* Queue first getc request */ | |
134 | __set_tohost(plat, HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static int htif_serial_of_to_plat(struct udevice *dev) | |
140 | { | |
141 | fdt_addr_t addr; | |
142 | struct htif_plat *plat = dev_get_plat(dev); | |
143 | ||
144 | addr = dev_read_addr_index(dev, 0); | |
145 | if (addr == FDT_ADDR_T_NONE) | |
146 | return -ENODEV; | |
147 | plat->fromhost = (void *)(uintptr_t)addr; | |
148 | plat->tohost = plat->fromhost + sizeof(u64); | |
149 | ||
150 | addr = dev_read_addr_index(dev, 1); | |
151 | if (addr != FDT_ADDR_T_NONE) | |
152 | plat->tohost = (void *)(uintptr_t)addr; | |
153 | ||
154 | plat->console_char = -1; | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | static const struct dm_serial_ops htif_serial_ops = { | |
160 | .putc = htif_serial_putc, | |
161 | .getc = htif_serial_getc, | |
162 | .pending = htif_serial_pending, | |
163 | }; | |
164 | ||
165 | static const struct udevice_id htif_serial_ids[] = { | |
166 | { .compatible = "ucb,htif0" }, | |
167 | { } | |
168 | }; | |
169 | ||
170 | U_BOOT_DRIVER(serial_htif) = { | |
171 | .name = "serial_htif", | |
172 | .id = UCLASS_SERIAL, | |
173 | .of_match = htif_serial_ids, | |
174 | .of_to_plat = htif_serial_of_to_plat, | |
175 | .plat_auto = sizeof(struct htif_plat), | |
176 | .probe = htif_serial_probe, | |
177 | .ops = &htif_serial_ops, | |
178 | }; |