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"
7
8 #include "escc.h"
9
10 /* ******************************************************************
11 * serial console functions
12 * ****************************************************************** */
13
14 static volatile unsigned char *escc_serial_dev;
15
16 #define CTRL(addr) (*(volatile unsigned char *)(uintptr_t)(addr))
17 #ifdef CONFIG_DRIVER_ESCC_SUN
18 #define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 2))
19 #else
20 #define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 16))
21 #endif
22
23 /* Conversion routines to/from brg time constants from/to bits
24 * per second.
25 */
26 #define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
27
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. */
34
35 /* Write Register 3 */
36 #define RxENAB 0x1 /* Rx Enable */
37 #define Rx8 0xc0 /* Rx 8 Bits/Character */
38
39 /* Write Register 4 */
40 #define SB1 0x4 /* 1 stop bit/char */
41 #define X16CLK 0x40 /* x16 clock mode */
42
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 */
48
49 /* Write Register 14 (Misc control bits) */
50 #define BRENAB 1 /* Baud rate generator enable */
51 #define BRSRC 2 /* Baud rate generator source */
52
53 /* Read Register 0 */
54 #define Rx_CH_AV 0x1 /* Rx Character Available */
55 #define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
56
escc_uart_charav(uintptr_t port)57 int escc_uart_charav(uintptr_t port)
58 {
59 return (CTRL(port) & Rx_CH_AV) != 0;
60 }
61
escc_uart_getchar(uintptr_t port)62 char escc_uart_getchar(uintptr_t port)
63 {
64 while (!escc_uart_charav(port))
65 ;
66 return DATA(port) & 0177;
67 }
68
escc_uart_port_putchar(uintptr_t port,unsigned char c)69 static void escc_uart_port_putchar(uintptr_t port, unsigned char c)
70 {
71 if (!escc_serial_dev)
72 return;
73
74 if (c == '\n')
75 escc_uart_port_putchar(port, '\r');
76 while (!(CTRL(port) & Tx_BUF_EMP))
77 ;
78 DATA(port) = c;
79 }
80
uart_init_line(volatile unsigned char * port,unsigned long baud)81 static void uart_init_line(volatile unsigned char *port, unsigned long baud)
82 {
83 CTRL(port) = 4; // reg 4
84 CTRL(port) = SB1 | X16CLK; // no parity, async, 1 stop bit, 16x
85 // clock
86
87 baud = BPS_TO_BRG(baud, ESCC_CLOCK / ESCC_CLOCK_DIVISOR);
88
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;
95
96 CTRL(port) = 3; // reg 3
97 CTRL(port) = RxENAB | Rx8; // enable rx, 8 bits/char
98
99 CTRL(port) = 5; // reg 5
100 CTRL(port) = RTS | TxENAB | Tx8 | DTR; // enable tx, 8 bits/char,
101 // set RTS & DTR
102
103 }
104
escc_uart_init(phys_addr_t port,unsigned long speed)105 int escc_uart_init(phys_addr_t port, unsigned long speed)
106 {
107 #ifdef CONFIG_DRIVER_ESCC_SUN
108 escc_serial_dev = (unsigned char *)ofmem_map_io(port & ~7ULL, ZS_REGS);
109 escc_serial_dev += port & 7ULL;
110 #else
111 escc_serial_dev = (unsigned char *)(uintptr_t)port;
112 #endif
113 uart_init_line(escc_serial_dev, speed);
114 return -1;
115 }
116
escc_uart_putchar(int c)117 void escc_uart_putchar(int c)
118 {
119 escc_uart_port_putchar((uintptr_t)escc_serial_dev, (unsigned char) (c & 0xff));
120 }
121
serial_cls(void)122 void serial_cls(void)
123 {
124 escc_uart_putchar(27);
125 escc_uart_putchar('[');
126 escc_uart_putchar('H');
127 escc_uart_putchar(27);
128 escc_uart_putchar('[');
129 escc_uart_putchar('J');
130 }
131
132 /* ( addr len -- actual ) */
133 static void
escc_read(ucell * address)134 escc_read(ucell *address)
135 {
136 char *addr;
137 int len;
138
139 len = POP();
140 addr = (char *)cell2pointer(POP());
141
142 if (len < 1)
143 printk("escc_read: bad len, addr %p len %x\n", addr, len);
144
145 if (escc_uart_charav(*address)) {
146 *addr = (char)escc_uart_getchar(*address);
147 PUSH(1);
148 } else {
149 PUSH(0);
150 }
151 }
152
153 /* ( addr len -- actual ) */
154 static void
escc_write(ucell * address)155 escc_write(ucell *address)
156 {
157 unsigned char *addr;
158 int i, len;
159
160 len = POP();
161 addr = (unsigned char *)cell2pointer(POP());
162
163 for (i = 0; i < len; i++) {
164 escc_uart_port_putchar(*address, addr[i]);
165 }
166 PUSH(len);
167 }
168
169 static void
escc_close(void)170 escc_close(void)
171 {
172 }
173
174 static void
escc_open(ucell * address)175 escc_open(ucell *address)
176 {
177 #ifdef CONFIG_DRIVER_ESCC_SUN
178 int len;
179 phandle_t ph;
180 unsigned long *prop;
181 char *args;
182
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);
195 }
196 #else
197 *address = (unsigned long)escc_serial_dev; // XXX
198 #endif
199 RET ( -1 );
200 }
201
202 DECLARE_UNNAMED_NODE(escc, INSTALL_OPEN, sizeof(ucell));
203
204 NODE_METHODS(escc) = {
205 { "open", escc_open },
206 { "close", escc_close },
207 { "read", escc_read },
208 { "write", escc_write },
209 };
210
211 #ifdef CONFIG_DRIVER_ESCC_SUN
212 static volatile unsigned char *kbd_dev;
213
kbd_init(phys_addr_t base)214 void kbd_init(phys_addr_t base)
215 {
216 kbd_dev = (unsigned char *)ofmem_map_io(base, 2 * 4);
217 kbd_dev += 4;
218 }
219
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 ' ',
232 };
233
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 ' ',
246 };
247
248 static int shiftstate;
249
250 int
keyboard_dataready(void)251 keyboard_dataready(void)
252 {
253 return ((kbd_dev[0] & 1) == 1);
254 }
255
256 unsigned char
keyboard_readdata(void)257 keyboard_readdata(void)
258 {
259 unsigned char ch;
260
261 while (!keyboard_dataready()) { }
262
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);
283
284 return ch;
285 }
286
287 /* ( addr len -- actual ) */
288 static void
escc_read_keyboard(void)289 escc_read_keyboard(void)
290 {
291 unsigned char *addr;
292 int len;
293
294 len = POP();
295 addr = (unsigned char *)POP();
296
297 if (len < 1)
298 printk("escc_read: bad len, addr %p len %x\n", addr, len);
299
300 if (keyboard_dataready()) {
301 *addr = keyboard_readdata();
302 PUSH(1);
303 } else {
304 PUSH(0);
305 }
306 }
307
308 DECLARE_UNNAMED_NODE(escc_keyboard, INSTALL_OPEN, sizeof(ucell));
309
310 NODE_METHODS(escc_keyboard) = {
311 { "open", escc_open },
312 { "close", escc_close },
313 { "read", escc_read_keyboard },
314 };
315
316 void
ob_zs_init(phys_addr_t base,uint64_t offset,int intr,int slave,int keyboard)317 ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard)
318 {
319 char nodebuff[256];
320 phandle_t aliases;
321
322 ob_new_obio_device("zs", "serial");
323
324 ob_reg(base, offset, ZS_REGS, 1);
325
326 PUSH(slave);
327 fword("encode-int");
328 push_str("slave");
329 fword("property");
330
331 if (keyboard) {
332 PUSH(0);
333 PUSH(0);
334 push_str("keyboard");
335 fword("property");
336
337 PUSH(0);
338 PUSH(0);
339 push_str("mouse");
340 fword("property");
341 }
342
343 ob_intr(intr);
344
345 PUSH(0);
346 PUSH(0);
347 push_str("port-a-ignore-cd");
348 fword("property");
349
350 PUSH(0);
351 PUSH(0);
352 push_str("port-b-ignore-cd");
353 fword("property");
354
355 fword("finish-device");
356
357 snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x",
358 (int)offset & 0xffffffff);
359 if (keyboard) {
360 REGISTER_NODE_METHODS(escc_keyboard, nodebuff);
361
362 aliases = find_dev("/aliases");
363 set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1);
364 } else {
365 REGISTER_NODE_METHODS(escc, nodebuff);
366
367 aliases = find_dev("/aliases");
368 snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:a",
369 (int)offset & 0xffffffff);
370 set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1);
371
372 snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:b",
373 (int)offset & 0xffffffff);
374 set_property(aliases, "ttyb", nodebuff, strlen(nodebuff) + 1);
375
376 }
377 }
378
379 #else
380
381 static void
escc_add_channel(const char * path,const char * node,phys_addr_t addr,int esnum)382 escc_add_channel(const char *path, const char *node, phys_addr_t addr,
383 int esnum)
384 {
385 char buf[64], tty[32];
386 phandle_t dnode, aliases;
387
388 cell props[10];
389 ucell offset;
390 int index;
391 int legacy;
392
393 int dbdma_offsets[2][2] = {
394 /* ch-b */
395 { 0x6, 0x7 },
396 /* ch-a */
397 { 0x4, 0x5 }
398 };
399
400 int reg_offsets[2][2][3] = {
401 {
402 /* ch-b */
403 { 0x00, 0x10, 0x40 },
404 /* ch-a */
405 { 0x20, 0x30, 0x50 }
406 },{
407 /* legacy ch-b */
408 { 0x0, 0x4, 0x8 },
409 /* legacy ch-a */
410 { 0x2, 0x6, 0xa }
411 }
412 };
413
414 switch (esnum) {
415 case 2: index = 1; legacy = 0; break;
416 case 3: index = 0; legacy = 0; break;
417 case 4: index = 1; legacy = 1; break;
418 case 5: index = 0; legacy = 1; break;
419 default: return;
420 }
421
422 /* add device */
423
424 snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
425
426 REGISTER_NAMED_NODE(escc, buf);
427
428 activate_device(buf);
429
430 /* add aliases */
431
432 if (!legacy) {
433 aliases = find_dev("/aliases");
434
435 snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
436 OLDWORLD(snprintf(tty, sizeof(tty), "tty%s", node));
437 OLDWORLD(set_property(aliases, tty, buf, strlen(buf) + 1));
438 snprintf(tty, sizeof(tty), "scc%s", node);
439 set_property(aliases, tty, buf, strlen(buf) + 1);
440 }
441 /* add properties */
442
443 dnode = find_dev(buf);
444 set_property(dnode, "device_type", "serial",
445 strlen("serial") + 1);
446
447 snprintf(buf, sizeof(buf), "chrp,es%d", esnum);
448 set_property(dnode, "compatible", buf, 9);
449
450 if (legacy) {
451 offset = IO_ESCC_LEGACY_OFFSET;
452 } else {
453 offset = IO_ESCC_OFFSET;
454 }
455
456 props[0] = offset + reg_offsets[legacy][index][0];
457 props[1] = 0x1;
458 props[2] = offset + reg_offsets[legacy][index][1];
459 props[3] = 0x1;
460 props[4] = offset + reg_offsets[legacy][index][2];
461 props[5] = 0x1;
462 props[6] = 0x8000 + dbdma_offsets[index][0] * 0x100;
463 props[7] = 0x100;
464 props[8] = 0x8000 + dbdma_offsets[index][1] * 0x100;
465 props[9] = 0x100;
466 set_property(dnode, "reg", (char *)&props, 10 * sizeof(cell));
467
468 props[0] = addr + offset + reg_offsets[legacy][index][0];
469 OLDWORLD(set_property(dnode, "AAPL,address",
470 (char *)&props, 1 * sizeof(cell)));
471
472 props[0] = 0x10 - index;
473 OLDWORLD(set_property(dnode, "AAPL,interrupts",
474 (char *)&props, 1 * sizeof(cell)));
475
476 props[0] = (0x24) + index;
477 props[1] = 0x1;
478 props[2] = dbdma_offsets[index][0];
479 props[3] = 0x0;
480 props[4] = dbdma_offsets[index][1];
481 props[5] = 0x0;
482 NEWWORLD(set_property(dnode, "interrupts",
483 (char *)&props, 6 * sizeof(cell)));
484
485 set_int_property(dnode, "slot-names", 0);
486
487 device_end();
488
489 uart_init_line((unsigned char*)addr + offset + reg_offsets[legacy][index][0],
490 CONFIG_SERIAL_SPEED);
491 }
492
493 void
escc_init(const char * path,phys_addr_t addr)494 escc_init(const char *path, phys_addr_t addr)
495 {
496 char buf[64];
497 int props[2];
498 phandle_t dnode;
499
500 push_str(path);
501 fword("find-device");
502 fword("new-device");
503
504 push_str("escc");
505 fword("device-name");
506
507 snprintf(buf, sizeof(buf), "%s/escc", path);
508
509 dnode = find_dev(buf);
510
511 set_int_property(dnode, "#address-cells", 1);
512 props[0] = __cpu_to_be32(IO_ESCC_OFFSET);
513 props[1] = __cpu_to_be32(IO_ESCC_SIZE);
514 set_property(dnode, "reg", (char *)&props, sizeof(props));
515 set_property(dnode, "device_type", "escc",
516 strlen("escc") + 1);
517 set_property(dnode, "compatible", "escc\0CHRP,es0", 14);
518 set_property(dnode, "ranges", "", 0);
519
520 fword("finish-device");
521
522 escc_add_channel(buf, "a", addr, 2);
523 escc_add_channel(buf, "b", addr, 3);
524
525 escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET +
526 (CONFIG_SERIAL_PORT ? 0 : 0x20);
527
528 push_str(path);
529 fword("find-device");
530 fword("new-device");
531
532 push_str("escc-legacy");
533 fword("device-name");
534
535 snprintf(buf, sizeof(buf), "%s/escc-legacy", path);
536
537 dnode = find_dev(buf);
538
539 set_int_property(dnode, "#address-cells", 1);
540 props[0] = __cpu_to_be32(IO_ESCC_LEGACY_OFFSET);
541 props[1] = __cpu_to_be32(IO_ESCC_LEGACY_SIZE);
542 set_property(dnode, "reg", (char *)&props, sizeof(props));
543 set_property(dnode, "device_type", "escc-legacy",
544 strlen("escc-legacy") + 1);
545 set_property(dnode, "compatible", "chrp,es1", 9);
546 set_property(dnode, "ranges", "", 0);
547
548 fword("finish-device");
549
550 escc_add_channel(buf, "a", addr, 4);
551 escc_add_channel(buf, "b", addr, 5);
552 }
553 #endif
554