Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / drivers / escc.c
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
57 int escc_uart_charav(uintptr_t port)
58 {
59     return (CTRL(port) & Rx_CH_AV) != 0;
60 }
61
62 char escc_uart_getchar(uintptr_t port)
63 {
64     while (!escc_uart_charav(port))
65         ;
66     return DATA(port) & 0177;
67 }
68
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
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
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
117 void escc_uart_putchar(int c)
118 {
119     escc_uart_port_putchar((uintptr_t)escc_serial_dev, (unsigned char) (c & 0xff));
120 }
121
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
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
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
170 escc_close(void)
171 {
172 }
173
174 static void
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
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
251 keyboard_dataready(void)
252 {
253     return ((kbd_dev[0] & 1) == 1);
254 }
255
256 unsigned char
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
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
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
382 escc_add_channel(const char *path, const char *node, phys_addr_t addr,
383                  uint32_t offset)
384 {
385     char buf[64], tty[32];
386     phandle_t dnode, aliases;
387     int len;
388     cell props[2];
389
390     /* add device */
391
392     snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
393
394     REGISTER_NAMED_NODE(escc, buf);
395
396     activate_device(buf);
397
398     /* add aliases */
399
400     aliases = find_dev("/aliases");
401
402     snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
403     OLDWORLD(snprintf(tty, sizeof(tty), "tty%s", node));
404     OLDWORLD(set_property(aliases, tty, buf, strlen(buf) + 1));
405     snprintf(tty, sizeof(tty), "scc%s", node);
406     set_property(aliases, tty, buf, strlen(buf) + 1);
407
408     /* add properties */
409
410     dnode = find_dev(buf);
411     set_property(dnode, "device_type", "serial",
412                  strlen("serial") + 1);
413
414     snprintf(buf, sizeof(buf), "ch-%s", node);
415     len = strlen(buf) + 1;
416     snprintf(buf + len, sizeof(buf) - len, "CHRP,es2");
417     set_property(dnode, "compatible", buf, len + 9);
418
419     props[0] = IO_ESCC_OFFSET + offset * 0x20;
420     props[1] = 0x00000020;
421     set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell));
422
423     props[0] = addr + IO_ESCC_OFFSET + offset * 0x20;
424     OLDWORLD(set_property(dnode, "AAPL,address",
425             (char *)&props, 1 * sizeof(cell)));
426
427     props[0] = 0x00000010 - offset;
428     OLDWORLD(set_property(dnode, "AAPL,interrupts",
429             (char *)&props, 1 * sizeof(cell)));
430
431     props[0] = (0x24) + offset;
432     props[1] = 0;
433     NEWWORLD(set_property(dnode, "interrupts",
434              (char *)&props, 2 * sizeof(cell)));
435
436     device_end();
437
438     uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20,
439                    CONFIG_SERIAL_SPEED);
440 }
441
442 void
443 escc_init(const char *path, phys_addr_t addr)
444 {
445     char buf[64];
446     int props[2];
447     phandle_t dnode;
448
449     push_str(path);
450     fword("find-device");
451     fword("new-device");
452
453     push_str("escc");
454     fword("device-name");
455
456     snprintf(buf, sizeof(buf), "%s/escc", path);
457
458     dnode = find_dev(buf);
459
460     set_int_property(dnode, "#address-cells", 1);
461     props[0] = __cpu_to_be32(IO_ESCC_OFFSET);
462     props[1] = __cpu_to_be32(IO_ESCC_SIZE);
463     set_property(dnode, "reg", (char *)&props, sizeof(props));
464     set_property(dnode, "device_type", "escc",
465                  strlen("escc") + 1);
466     set_property(dnode, "compatible", "escc\0CHRP,es0", 14);
467
468     fword("finish-device");
469
470     escc_add_channel(buf, "a", addr, 1);
471     escc_add_channel(buf, "b", addr, 0);
472
473     escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET +
474                  (CONFIG_SERIAL_PORT ? 0 : 0x20);
475 }
476 #endif