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_port_read(ucell * address)134 escc_port_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_port_write(ucell * address)155 escc_port_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_port_close(void)170 escc_port_close(void)
171 {
172 }
173 
174 #ifdef CONFIG_DRIVER_ESCC_SUN
175 static void
escc_port_open(ucell * address)176 escc_port_open(ucell *address)
177 {
178 
179     int len;
180     phandle_t ph;
181     unsigned long *prop;
182     char *args;
183 
184     fword("my-self");
185     fword("ihandle>phandle");
186     ph = (phandle_t)POP();
187     prop = (unsigned long *)get_property(ph, "address", &len);
188     *address = *prop;
189     fword("my-args");
190     args = pop_fstr_copy();
191     if (args) {
192         if (args[0] == 'a')
193             *address += 4;
194         //printk("escc_open: address %lx, args %s\n", *address, args);
195         free(args);
196     }
197 
198     RET ( -1 );
199 }
200 
201 #else
202 
203 static void
escc_port_open(ucell * address)204 escc_port_open(ucell *address)
205 {
206     *address = (unsigned long)escc_serial_dev; // XXX
207     RET(-1);
208 }
209 
210 static void
escc_open(int * idx)211 escc_open(int *idx)
212 {
213     RET(-1);
214 }
215 
216 static void
escc_close(int * idx)217 escc_close(int *idx)
218 {
219 }
220 
221 DECLARE_UNNAMED_NODE(escc, 0, sizeof(int *));
222 
223 NODE_METHODS(escc) = {
224     { "open",               escc_open      },
225     { "close",              escc_close     },
226 };
227 
228 #endif
229 
230 DECLARE_UNNAMED_NODE(escc_port, 0, sizeof(ucell));
231 
232 NODE_METHODS(escc_port) = {
233     { "open",               escc_port_open      },
234     { "close",              escc_port_close     },
235     { "read",               escc_port_read      },
236     { "write",              escc_port_write     },
237 };
238 
239 #ifdef CONFIG_DRIVER_ESCC_SUN
240 static volatile unsigned char *kbd_dev;
241 
kbd_init(phys_addr_t base)242 void kbd_init(phys_addr_t base)
243 {
244     kbd_dev = (unsigned char *)ofmem_map_io(base, 2 * 4);
245     kbd_dev += 4;
246 }
247 
248 static const unsigned char sunkbd_keycode[128] = {
249     0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
250     0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0,
251     '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 8,
252     0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
253     'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
254     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255     'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', 13,
256     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
257     'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
258     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
259     ' ',
260 };
261 
262 static const unsigned char sunkbd_keycode_shifted[128] = {
263     0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
264     0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0,
265     '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 8,
266     0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
267     'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',
268     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
269     'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '|', 13,
270     0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
271     'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
272     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
273     ' ',
274 };
275 
276 static int shiftstate;
277 
278 int
keyboard_dataready(void)279 keyboard_dataready(void)
280 {
281     return ((kbd_dev[0] & 1) == 1);
282 }
283 
284 unsigned char
keyboard_readdata(void)285 keyboard_readdata(void)
286 {
287     volatile unsigned char ch;
288 
289     while (!keyboard_dataready()) { }
290 
291     do {
292         ch = kbd_dev[2] & 0xff;
293         if (ch == 99)
294             shiftstate |= 1;
295         else if (ch == 110)
296             shiftstate |= 2;
297         else if (ch == 227)
298             shiftstate &= ~1;
299         else if (ch == 238)
300             shiftstate &= ~2;
301         //printk("getch: %d\n", ch);
302     } // If release, wait for key press
303     while ((ch & 0x80) == 0x80 || ch == 238 || ch == 227);
304     //printk("getch rel: %d\n", ch);
305     ch &= 0x7f;
306     if (shiftstate)
307         ch = sunkbd_keycode_shifted[ch];
308     else
309         ch = sunkbd_keycode[ch];
310     //printk("getch xlate: %d\n", ch);
311 
312     return ch;
313 }
314 
315 /* ( addr len -- actual ) */
316 static void
escc_read_keyboard(void)317 escc_read_keyboard(void)
318 {
319     unsigned char *addr;
320     int len;
321 
322     len = POP();
323     addr = (unsigned char *)POP();
324 
325     if (len < 1)
326         printk("escc_read: bad len, addr %p len %x\n", addr, len);
327 
328     if (keyboard_dataready()) {
329         *addr = keyboard_readdata();
330         PUSH(1);
331     } else {
332         PUSH(0);
333     }
334 }
335 
336 DECLARE_UNNAMED_NODE(escc_keyboard, 0, sizeof(ucell));
337 
338 NODE_METHODS(escc_keyboard) = {
339     { "open",               escc_port_open         },
340     { "close",              escc_port_close        },
341     { "read",               escc_read_keyboard     },
342 };
343 
344 void
ob_zs_init(phys_addr_t base,uint64_t offset,int intr,int slave,int keyboard)345 ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard)
346 {
347     char nodebuff[256];
348     phandle_t aliases;
349 
350     ob_new_obio_device("zs", "serial");
351 
352     ob_reg(base, offset, ZS_REGS, 1);
353 
354     PUSH(slave);
355     fword("encode-int");
356     push_str("slave");
357     fword("property");
358 
359     if (keyboard) {
360         PUSH(0);
361         PUSH(0);
362         push_str("keyboard");
363         fword("property");
364 
365         PUSH(0);
366         PUSH(0);
367         push_str("mouse");
368         fword("property");
369     }
370 
371     ob_intr(intr);
372 
373     PUSH(0);
374     PUSH(0);
375     push_str("port-a-ignore-cd");
376     fword("property");
377 
378     PUSH(0);
379     PUSH(0);
380     push_str("port-b-ignore-cd");
381     fword("property");
382 
383     if (keyboard) {
384         BIND_NODE_METHODS(get_cur_dev(), escc_keyboard);
385     } else {
386         BIND_NODE_METHODS(get_cur_dev(), escc_port);
387     }
388 
389     fword("finish-device");
390 
391     aliases = find_dev("/aliases");
392     if (keyboard) {
393         snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x",
394              (int)offset & 0xffffffff);
395         set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1);
396     } else {
397         snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:a",
398                  (int)offset & 0xffffffff);
399         set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1);
400 
401         snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:b",
402                  (int)offset & 0xffffffff);
403         set_property(aliases, "ttyb", nodebuff, strlen(nodebuff) + 1);
404 
405     }
406 }
407 
408 #else
409 
410 static void
escc_add_channel(const char * path,const char * node,phys_addr_t addr,int esnum)411 escc_add_channel(const char *path, const char *node, phys_addr_t addr,
412                  int esnum)
413 {
414     char buf[64], tty[32];
415     phandle_t dnode, aliases;
416 
417     cell props[10];
418     ucell offset;
419     int index;
420     int legacy;
421 
422     int dbdma_offsets[2][2] = {
423         /* ch-b */
424         { 0x6, 0x7 },
425         /* ch-a */
426         { 0x4, 0x5 }
427     };
428 
429     int reg_offsets[2][2][3] = {
430         {
431             /* ch-b */
432             { 0x00, 0x10, 0x40 },
433             /* ch-a */
434             { 0x20, 0x30, 0x50 }
435         },{
436             /* legacy ch-b */
437             { 0x0, 0x4, 0x8 },
438             /* legacy ch-a */
439             { 0x2, 0x6, 0xa }
440         }
441     };
442 
443     switch (esnum) {
444         case 2: index = 1; legacy = 0; break;
445         case 3: index = 0; legacy = 0; break;
446         case 4: index = 1; legacy = 1; break;
447         case 5: index = 0; legacy = 1; break;
448         default: return;
449     }
450 
451     /* add device */
452 
453     fword("new-device");
454 
455     snprintf(buf, sizeof(buf), "ch-%s", node);
456     push_str(buf);
457     fword("device-name");
458 
459     BIND_NODE_METHODS(get_cur_dev(), escc_port);
460 
461     /* add aliases */
462 
463     if (!legacy) {
464             aliases = find_dev("/aliases");
465 
466             snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
467             OLDWORLD(snprintf(tty, sizeof(tty), "tty%s", node));
468             OLDWORLD(set_property(aliases, tty, buf, strlen(buf) + 1));
469             snprintf(tty, sizeof(tty), "scc%s", node);
470             set_property(aliases, tty, buf, strlen(buf) + 1);
471     }
472     /* add properties */
473 
474     dnode = get_cur_dev();
475     set_property(dnode, "device_type", "serial",
476                  strlen("serial") + 1);
477 
478     snprintf(buf, sizeof(buf), "chrp,es%d", esnum);
479     set_property(dnode, "compatible", buf, 9);
480 
481     if (legacy) {
482         offset = IO_ESCC_LEGACY_OFFSET;
483     } else {
484         offset = IO_ESCC_OFFSET;
485     }
486 
487     props[0] = offset + reg_offsets[legacy][index][0];
488     props[1] = 0x1;
489     props[2] = offset + reg_offsets[legacy][index][1];
490     props[3] = 0x1;
491     props[4] = offset + reg_offsets[legacy][index][2];
492     props[5] = 0x1;
493     props[6] = 0x8000 + dbdma_offsets[index][0] * 0x100;
494     props[7] = 0x100;
495     props[8] = 0x8000 + dbdma_offsets[index][1] * 0x100;
496     props[9] = 0x100;
497     set_property(dnode, "reg", (char *)&props, 10 * sizeof(cell));
498 
499     props[0] = addr + offset + reg_offsets[legacy][index][0];
500     OLDWORLD(set_property(dnode, "AAPL,address",
501             (char *)&props, 1 * sizeof(cell)));
502 
503     props[0] = 0x10 - index;
504     OLDWORLD(set_property(dnode, "AAPL,interrupts",
505             (char *)&props, 1 * sizeof(cell)));
506 
507     props[0] = (0x24) + index;
508     props[1] = 0x1;
509     props[2] = dbdma_offsets[index][0];
510     props[3] = 0x0;
511     props[4] = dbdma_offsets[index][1];
512     props[5] = 0x0;
513     NEWWORLD(set_property(dnode, "interrupts",
514              (char *)&props, 6 * sizeof(cell)));
515 
516     set_int_property(dnode, "slot-names", 0);
517 
518     fword("finish-device");
519 
520     uart_init_line((unsigned char*)addr + offset + reg_offsets[legacy][index][0],
521                    CONFIG_SERIAL_SPEED);
522 }
523 
524 void
escc_init(const char * path,phys_addr_t addr)525 escc_init(const char *path, phys_addr_t addr)
526 {
527     char buf[64];
528     int props[2];
529     phandle_t dnode;
530 
531     fword("new-device");
532 
533     push_str("escc");
534     fword("device-name");
535 
536     dnode = get_cur_dev();
537     set_int_property(dnode, "#address-cells", 1);
538     props[0] = __cpu_to_be32(IO_ESCC_OFFSET);
539     props[1] = __cpu_to_be32(IO_ESCC_SIZE);
540     set_property(dnode, "reg", (char *)&props, sizeof(props));
541     set_property(dnode, "device_type", "escc",
542                  strlen("escc") + 1);
543     set_property(dnode, "compatible", "escc\0CHRP,es0", 14);
544     set_property(dnode, "ranges", "", 0);
545 
546     snprintf(buf, sizeof(buf), "%s/escc", path);
547     escc_add_channel(buf, "a", addr, 2);
548     escc_add_channel(buf, "b", addr, 3);
549 
550     BIND_NODE_METHODS(dnode, escc);
551     fword("finish-device");
552 
553     escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET +
554                  (CONFIG_SERIAL_PORT ? 0 : 0x20);
555 
556     fword("new-device");
557 
558     push_str("escc-legacy");
559     fword("device-name");
560 
561     dnode = get_cur_dev();
562     set_int_property(dnode, "#address-cells", 1);
563     props[0] = __cpu_to_be32(IO_ESCC_LEGACY_OFFSET);
564     props[1] = __cpu_to_be32(IO_ESCC_LEGACY_SIZE);
565     set_property(dnode, "reg", (char *)&props, sizeof(props));
566     set_property(dnode, "device_type", "escc-legacy",
567                  strlen("escc-legacy") + 1);
568     set_property(dnode, "compatible", "chrp,es1", 9);
569     set_property(dnode, "ranges", "", 0);
570 
571     snprintf(buf, sizeof(buf), "%s/escc-legacy", path);
572     escc_add_channel(buf, "a", addr, 4);
573     escc_add_channel(buf, "b", addr, 5);
574 
575     BIND_NODE_METHODS(dnode, escc);
576     fword("finish-device");
577 }
578 #endif
579