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