switch-arch: Prepare ppc64 support
[openbios/afaerber.git] / drivers / escc.c
blob809cfa8be1c4e9d511b610203be1859e9c7637ed
1 #include "config.h"
2 #include "libopenbios/bindings.h"
3 #include "libc/byteorder.h"
4 #include "libc/vsprintf.h"
5 #include "drivers/drivers.h"
6 #include "libopenbios/ofmem.h"
8 #include "escc.h"
10 /* ******************************************************************
11 * serial console functions
12 * ****************************************************************** */
14 static volatile unsigned char *serial_dev;
16 #define CTRL(addr) (*(volatile unsigned char *)(addr))
17 #ifdef CONFIG_DRIVER_ESCC_SUN
18 #define DATA(addr) (*(volatile unsigned char *)(addr + 2))
19 #else
20 #define DATA(addr) (*(volatile unsigned char *)(addr + 16))
21 #endif
23 /* Conversion routines to/from brg time constants from/to bits
24 * per second.
26 #define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
28 #ifdef CONFIG_DRIVER_ESCC_SUN
29 #define ESCC_CLOCK 4915200 /* Zilog input clock rate. */
30 #else
31 #define ESCC_CLOCK 3686400
32 #endif
33 #define ESCC_CLOCK_DIVISOR 16 /* Divisor this driver uses. */
35 /* Write Register 3 */
36 #define RxENAB 0x1 /* Rx Enable */
37 #define Rx8 0xc0 /* Rx 8 Bits/Character */
39 /* Write Register 4 */
40 #define SB1 0x4 /* 1 stop bit/char */
41 #define X16CLK 0x40 /* x16 clock mode */
43 /* Write Register 5 */
44 #define RTS 0x2 /* RTS */
45 #define TxENAB 0x8 /* Tx Enable */
46 #define Tx8 0x60 /* Tx 8 bits/character */
47 #define DTR 0x80 /* DTR */
49 /* Write Register 14 (Misc control bits) */
50 #define BRENAB 1 /* Baud rate generator enable */
51 #define BRSRC 2 /* Baud rate generator source */
53 /* Read Register 0 */
54 #define Rx_CH_AV 0x1 /* Rx Character Available */
55 #define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
57 int uart_charav(int port)
59 return (CTRL(port) & Rx_CH_AV) != 0;
62 char uart_getchar(int port)
64 while (!uart_charav(port))
66 return DATA(port) & 0177;
69 static void uart_putchar(int port, unsigned char c)
71 if (!serial_dev)
72 return;
74 if (c == '\n')
75 uart_putchar(port, '\r');
76 while (!(CTRL(port) & Tx_BUF_EMP))
78 DATA(port) = c;
81 static void uart_init_line(volatile unsigned char *port, unsigned long baud)
83 CTRL(port) = 4; // reg 4
84 CTRL(port) = SB1 | X16CLK; // no parity, async, 1 stop bit, 16x
85 // clock
87 baud = BPS_TO_BRG(baud, ESCC_CLOCK / ESCC_CLOCK_DIVISOR);
89 CTRL(port) = 12; // reg 12
90 CTRL(port) = baud & 0xff;
91 CTRL(port) = 13; // reg 13
92 CTRL(port) = (baud >> 8) & 0xff;
93 CTRL(port) = 14; // reg 14
94 CTRL(port) = BRSRC | BRENAB;
96 CTRL(port) = 3; // reg 3
97 CTRL(port) = RxENAB | Rx8; // enable rx, 8 bits/char
99 CTRL(port) = 5; // reg 5
100 CTRL(port) = RTS | TxENAB | Tx8 | DTR; // enable tx, 8 bits/char,
101 // set RTS & DTR
105 int uart_init(uint64_t port, unsigned long speed)
107 #ifdef CONFIG_DRIVER_ESCC_SUN
108 serial_dev = map_io(port & ~7ULL, ZS_REGS);
109 serial_dev += port & 7ULL;
110 #else
111 serial_dev = (unsigned char *)(unsigned long)port;
112 #endif
113 uart_init_line(serial_dev, speed);
114 return -1;
117 void serial_putchar(int c)
119 uart_putchar((int)serial_dev, (unsigned char) (c & 0xff));
122 void serial_cls(void)
124 serial_putchar(27);
125 serial_putchar('[');
126 serial_putchar('H');
127 serial_putchar(27);
128 serial_putchar('[');
129 serial_putchar('J');
132 /* ( addr len -- actual ) */
133 static void
134 escc_read(unsigned long *address)
136 char *addr;
137 int len;
139 len = POP();
140 addr = (char *)cell2pointer(POP());
142 if (len < 1)
143 printk("escc_read: bad len, addr %x len %x\n", (unsigned int)addr, len);
145 if (uart_charav(*address)) {
146 *addr = (char)uart_getchar(*address);
147 PUSH(1);
148 } else {
149 PUSH(0);
153 /* ( addr len -- actual ) */
154 static void
155 escc_write(unsigned long *address)
157 unsigned char *addr;
158 int i, len;
160 len = POP();
161 addr = (unsigned char *)cell2pointer(POP());
163 for (i = 0; i < len; i++) {
164 uart_putchar(*address, addr[i]);
166 PUSH(len);
169 static void
170 escc_close(void)
174 static void
175 escc_open(unsigned long *address)
177 #ifdef CONFIG_DRIVER_ESCC_SUN
178 int len;
179 phandle_t ph;
180 unsigned long *prop;
181 char *args;
183 fword("my-self");
184 fword("ihandle>phandle");
185 ph = (phandle_t)POP();
186 prop = (unsigned long *)get_property(ph, "address", &len);
187 *address = *prop;
188 fword("my-args");
189 args = pop_fstr_copy();
190 if (args) {
191 if (args[0] == 'a')
192 *address += 4;
193 //printk("escc_open: address %lx, args %s\n", *address, args);
194 free(args);
196 #else
197 *address = (unsigned long)serial_dev; // XXX
198 #endif
199 RET ( -1 );
202 DECLARE_UNNAMED_NODE(escc, INSTALL_OPEN, sizeof(unsigned long));
204 NODE_METHODS(escc) = {
205 { "open", escc_open },
206 { "close", escc_close },
207 { "read", escc_read },
208 { "write", escc_write },
211 #ifdef CONFIG_DRIVER_ESCC_SUN
212 static volatile unsigned char *kbd_dev;
214 void kbd_init(uint64_t base)
216 kbd_dev = map_io(base, 2 * 4);
217 kbd_dev += 4;
220 static const unsigned char sunkbd_keycode[128] = {
221 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
223 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 8,
224 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
225 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
226 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
227 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', 13,
228 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
229 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
230 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
231 ' ',
234 static const unsigned char sunkbd_keycode_shifted[128] = {
235 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
236 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
237 '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 8,
238 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
239 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',
240 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
241 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '|', 13,
242 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
243 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
244 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
245 ' ',
248 static int shiftstate;
251 keyboard_dataready(void)
253 return ((kbd_dev[0] & 1) == 1);
256 unsigned char
257 keyboard_readdata(void)
259 unsigned char ch;
261 while (!keyboard_dataready()) { }
263 do {
264 ch = kbd_dev[2] & 0xff;
265 if (ch == 99)
266 shiftstate |= 1;
267 else if (ch == 110)
268 shiftstate |= 2;
269 else if (ch == 227)
270 shiftstate &= ~1;
271 else if (ch == 238)
272 shiftstate &= ~2;
273 //printk("getch: %d\n", ch);
274 } // If release, wait for key press
275 while ((ch & 0x80) == 0x80 || ch == 238 || ch == 227);
276 //printk("getch rel: %d\n", ch);
277 ch &= 0x7f;
278 if (shiftstate)
279 ch = sunkbd_keycode_shifted[ch];
280 else
281 ch = sunkbd_keycode[ch];
282 //printk("getch xlate: %d\n", ch);
284 return ch;
287 /* ( addr len -- actual ) */
288 static void
289 escc_read_keyboard(void)
291 unsigned char *addr;
292 int len;
294 len = POP();
295 addr = (unsigned char *)POP();
297 if (len < 1)
298 printk("escc_read: bad len, addr %x len %x\n", (unsigned int)addr, len);
300 if (keyboard_dataready()) {
301 *addr = keyboard_readdata();
302 PUSH(1);
303 } else {
304 PUSH(0);
308 DECLARE_UNNAMED_NODE(escc_keyboard, INSTALL_OPEN, sizeof(unsigned long));
310 NODE_METHODS(escc_keyboard) = {
311 { "open", escc_open },
312 { "close", escc_close },
313 { "read", escc_read_keyboard },
316 void
317 ob_zs_init(uint64_t base, uint64_t offset, int intr, int slave, int keyboard)
319 char nodebuff[256];
320 phandle_t aliases;
322 ob_new_obio_device("zs", "serial");
324 ob_reg(base, offset, ZS_REGS, 1);
326 PUSH(slave);
327 fword("encode-int");
328 push_str("slave");
329 fword("property");
331 if (keyboard) {
332 PUSH(-1);
333 fword("encode-int");
334 push_str("keyboard");
335 fword("property");
337 PUSH(-1);
338 fword("encode-int");
339 push_str("mouse");
340 fword("property");
343 ob_intr(intr);
345 fword("finish-device");
347 snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x",
348 (int)offset & 0xffffffff);
349 if (keyboard) {
350 REGISTER_NODE_METHODS(escc_keyboard, nodebuff);
352 aliases = find_dev("/aliases");
353 set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1);
354 } else {
355 REGISTER_NODE_METHODS(escc, nodebuff);
357 aliases = find_dev("/aliases");
358 snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:a",
359 (int)offset & 0xffffffff);
360 set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1);
362 snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:b",
363 (int)offset & 0xffffffff);
364 set_property(aliases, "ttyb", nodebuff, strlen(nodebuff) + 1);
369 #else
371 static void
372 escc_add_channel(const char *path, const char *node, uint32_t addr,
373 uint32_t offset)
375 char buf[64], tty[32];
376 phandle_t dnode, aliases;
377 int len;
378 cell props[2];
380 /* add device */
382 snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
384 REGISTER_NAMED_NODE(escc, buf);
386 activate_device(buf);
388 /* add aliases */
390 aliases = find_dev("/aliases");
392 snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
393 OLDWORLD(snprintf(tty, sizeof(tty), "tty%s", node));
394 OLDWORLD(set_property(aliases, tty, buf, strlen(buf) + 1));
395 snprintf(tty, sizeof(tty), "scc%s", node);
396 set_property(aliases, tty, buf, strlen(buf) + 1);
398 /* add properties */
400 dnode = find_dev(buf);
401 set_property(dnode, "device_type", "serial",
402 strlen("serial") + 1);
404 snprintf(buf, sizeof(buf), "ch-%s", node);
405 len = strlen(buf) + 1;
406 snprintf(buf + len, sizeof(buf) - len, "CHRP,es2");
407 set_property(dnode, "compatible", buf, len + 9);
409 props[0] = IO_ESCC_OFFSET + offset * 0x20;
410 props[1] = 0x00000020;
411 set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell));
413 props[0] = addr + IO_ESCC_OFFSET + offset * 0x20;
414 OLDWORLD(set_property(dnode, "AAPL,address",
415 (char *)&props, 1 * sizeof(cell)));
417 props[0] = 0x00000010 - offset;
418 OLDWORLD(set_property(dnode, "AAPL,interrupts",
419 (char *)&props, 1 * sizeof(cell)));
421 props[0] = (0x24) + offset;
422 props[1] = 0;
423 NEWWORLD(set_property(dnode, "interrupts",
424 (char *)&props, 2 * sizeof(cell)));
426 device_end();
428 uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20,
429 CONFIG_SERIAL_SPEED);
432 void
433 escc_init(const char *path, unsigned long addr)
435 char buf[64];
436 int props[2];
437 phandle_t dnode;
439 push_str(path);
440 fword("find-device");
441 fword("new-device");
443 push_str("escc");
444 fword("device-name");
446 snprintf(buf, sizeof(buf), "%s/escc", path);
448 dnode = find_dev(buf);
450 set_int_property(dnode, "#address-cells", 1);
451 props[0] = __cpu_to_be32(IO_ESCC_OFFSET);
452 props[1] = __cpu_to_be32(IO_ESCC_SIZE);
453 set_property(dnode, "reg", (char *)&props, sizeof(props));
454 set_property(dnode, "device_type", "escc",
455 strlen("escc") + 1);
456 set_property(dnode, "compatible", "escc\0CHRP,es0", 14);
458 fword("finish-device");
460 escc_add_channel(buf, "a", addr, 1);
461 escc_add_channel(buf, "b", addr, 0);
463 serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET +
464 (CONFIG_SERIAL_PORT ? 0 : 0x20);
466 #endif