578 lines
14 KiB
C
578 lines
14 KiB
C
#include "config.h"
|
|
#include "libopenbios/bindings.h"
|
|
#include "libc/byteorder.h"
|
|
#include "libc/vsprintf.h"
|
|
#include "drivers/drivers.h"
|
|
#include "libopenbios/ofmem.h"
|
|
|
|
#include "escc.h"
|
|
|
|
/* ******************************************************************
|
|
* serial console functions
|
|
* ****************************************************************** */
|
|
|
|
static volatile unsigned char *escc_serial_dev;
|
|
|
|
#define CTRL(addr) (*(volatile unsigned char *)(uintptr_t)(addr))
|
|
#ifdef CONFIG_DRIVER_ESCC_SUN
|
|
#define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 2))
|
|
#else
|
|
#define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 16))
|
|
#endif
|
|
|
|
/* Conversion routines to/from brg time constants from/to bits
|
|
* per second.
|
|
*/
|
|
#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
|
|
|
|
#ifdef CONFIG_DRIVER_ESCC_SUN
|
|
#define ESCC_CLOCK 4915200 /* Zilog input clock rate. */
|
|
#else
|
|
#define ESCC_CLOCK 3686400
|
|
#endif
|
|
#define ESCC_CLOCK_DIVISOR 16 /* Divisor this driver uses. */
|
|
|
|
/* Write Register 3 */
|
|
#define RxENAB 0x1 /* Rx Enable */
|
|
#define Rx8 0xc0 /* Rx 8 Bits/Character */
|
|
|
|
/* Write Register 4 */
|
|
#define SB1 0x4 /* 1 stop bit/char */
|
|
#define X16CLK 0x40 /* x16 clock mode */
|
|
|
|
/* Write Register 5 */
|
|
#define RTS 0x2 /* RTS */
|
|
#define TxENAB 0x8 /* Tx Enable */
|
|
#define Tx8 0x60 /* Tx 8 bits/character */
|
|
#define DTR 0x80 /* DTR */
|
|
|
|
/* Write Register 14 (Misc control bits) */
|
|
#define BRENAB 1 /* Baud rate generator enable */
|
|
#define BRSRC 2 /* Baud rate generator source */
|
|
|
|
/* Read Register 0 */
|
|
#define Rx_CH_AV 0x1 /* Rx Character Available */
|
|
#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
|
|
|
|
int escc_uart_charav(uintptr_t port)
|
|
{
|
|
return (CTRL(port) & Rx_CH_AV) != 0;
|
|
}
|
|
|
|
char escc_uart_getchar(uintptr_t port)
|
|
{
|
|
while (!escc_uart_charav(port))
|
|
;
|
|
return DATA(port) & 0177;
|
|
}
|
|
|
|
static void escc_uart_port_putchar(uintptr_t port, unsigned char c)
|
|
{
|
|
if (!escc_serial_dev)
|
|
return;
|
|
|
|
if (c == '\n')
|
|
escc_uart_port_putchar(port, '\r');
|
|
while (!(CTRL(port) & Tx_BUF_EMP))
|
|
;
|
|
DATA(port) = c;
|
|
}
|
|
|
|
static void uart_init_line(volatile unsigned char *port, unsigned long baud)
|
|
{
|
|
CTRL(port) = 4; // reg 4
|
|
CTRL(port) = SB1 | X16CLK; // no parity, async, 1 stop bit, 16x
|
|
// clock
|
|
|
|
baud = BPS_TO_BRG(baud, ESCC_CLOCK / ESCC_CLOCK_DIVISOR);
|
|
|
|
CTRL(port) = 12; // reg 12
|
|
CTRL(port) = baud & 0xff;
|
|
CTRL(port) = 13; // reg 13
|
|
CTRL(port) = (baud >> 8) & 0xff;
|
|
CTRL(port) = 14; // reg 14
|
|
CTRL(port) = BRSRC | BRENAB;
|
|
|
|
CTRL(port) = 3; // reg 3
|
|
CTRL(port) = RxENAB | Rx8; // enable rx, 8 bits/char
|
|
|
|
CTRL(port) = 5; // reg 5
|
|
CTRL(port) = RTS | TxENAB | Tx8 | DTR; // enable tx, 8 bits/char,
|
|
// set RTS & DTR
|
|
|
|
}
|
|
|
|
int escc_uart_init(phys_addr_t port, unsigned long speed)
|
|
{
|
|
#ifdef CONFIG_DRIVER_ESCC_SUN
|
|
escc_serial_dev = (unsigned char *)ofmem_map_io(port & ~7ULL, ZS_REGS);
|
|
escc_serial_dev += port & 7ULL;
|
|
#else
|
|
escc_serial_dev = (unsigned char *)(uintptr_t)port;
|
|
#endif
|
|
uart_init_line(escc_serial_dev, speed);
|
|
return -1;
|
|
}
|
|
|
|
void escc_uart_putchar(int c)
|
|
{
|
|
escc_uart_port_putchar((uintptr_t)escc_serial_dev, (unsigned char) (c & 0xff));
|
|
}
|
|
|
|
void serial_cls(void)
|
|
{
|
|
escc_uart_putchar(27);
|
|
escc_uart_putchar('[');
|
|
escc_uart_putchar('H');
|
|
escc_uart_putchar(27);
|
|
escc_uart_putchar('[');
|
|
escc_uart_putchar('J');
|
|
}
|
|
|
|
/* ( addr len -- actual ) */
|
|
static void
|
|
escc_port_read(ucell *address)
|
|
{
|
|
char *addr;
|
|
int len;
|
|
|
|
len = POP();
|
|
addr = (char *)cell2pointer(POP());
|
|
|
|
if (len < 1)
|
|
printk("escc_read: bad len, addr %p len %x\n", addr, len);
|
|
|
|
if (escc_uart_charav(*address)) {
|
|
*addr = (char)escc_uart_getchar(*address);
|
|
PUSH(1);
|
|
} else {
|
|
PUSH(0);
|
|
}
|
|
}
|
|
|
|
/* ( addr len -- actual ) */
|
|
static void
|
|
escc_port_write(ucell *address)
|
|
{
|
|
unsigned char *addr;
|
|
int i, len;
|
|
|
|
len = POP();
|
|
addr = (unsigned char *)cell2pointer(POP());
|
|
|
|
for (i = 0; i < len; i++) {
|
|
escc_uart_port_putchar(*address, addr[i]);
|
|
}
|
|
PUSH(len);
|
|
}
|
|
|
|
static void
|
|
escc_port_close(void)
|
|
{
|
|
}
|
|
|
|
#ifdef CONFIG_DRIVER_ESCC_SUN
|
|
static void
|
|
escc_port_open(ucell *address)
|
|
{
|
|
|
|
int len;
|
|
phandle_t ph;
|
|
unsigned long *prop;
|
|
char *args;
|
|
|
|
fword("my-self");
|
|
fword("ihandle>phandle");
|
|
ph = (phandle_t)POP();
|
|
prop = (unsigned long *)get_property(ph, "address", &len);
|
|
*address = *prop;
|
|
fword("my-args");
|
|
args = pop_fstr_copy();
|
|
if (args) {
|
|
if (args[0] == 'a')
|
|
*address += 4;
|
|
//printk("escc_open: address %lx, args %s\n", *address, args);
|
|
free(args);
|
|
}
|
|
|
|
RET ( -1 );
|
|
}
|
|
|
|
#else
|
|
|
|
static void
|
|
escc_port_open(ucell *address)
|
|
{
|
|
*address = (unsigned long)escc_serial_dev; // XXX
|
|
RET(-1);
|
|
}
|
|
|
|
static void
|
|
escc_open(int *idx)
|
|
{
|
|
RET(-1);
|
|
}
|
|
|
|
static void
|
|
escc_close(int *idx)
|
|
{
|
|
}
|
|
|
|
DECLARE_UNNAMED_NODE(escc, 0, sizeof(int *));
|
|
|
|
NODE_METHODS(escc) = {
|
|
{ "open", escc_open },
|
|
{ "close", escc_close },
|
|
};
|
|
|
|
#endif
|
|
|
|
DECLARE_UNNAMED_NODE(escc_port, 0, sizeof(ucell));
|
|
|
|
NODE_METHODS(escc_port) = {
|
|
{ "open", escc_port_open },
|
|
{ "close", escc_port_close },
|
|
{ "read", escc_port_read },
|
|
{ "write", escc_port_write },
|
|
};
|
|
|
|
#ifdef CONFIG_DRIVER_ESCC_SUN
|
|
static volatile unsigned char *kbd_dev;
|
|
|
|
void kbd_init(phys_addr_t base)
|
|
{
|
|
kbd_dev = (unsigned char *)ofmem_map_io(base, 2 * 4);
|
|
kbd_dev += 4;
|
|
}
|
|
|
|
static const unsigned char sunkbd_keycode[128] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 8,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
|
|
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', 13,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
' ',
|
|
};
|
|
|
|
static const unsigned char sunkbd_keycode_shifted[128] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 8,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
|
|
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '|', 13,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
' ',
|
|
};
|
|
|
|
static int shiftstate;
|
|
|
|
int
|
|
keyboard_dataready(void)
|
|
{
|
|
return ((kbd_dev[0] & 1) == 1);
|
|
}
|
|
|
|
unsigned char
|
|
keyboard_readdata(void)
|
|
{
|
|
volatile unsigned char ch;
|
|
|
|
while (!keyboard_dataready()) { }
|
|
|
|
do {
|
|
ch = kbd_dev[2] & 0xff;
|
|
if (ch == 99)
|
|
shiftstate |= 1;
|
|
else if (ch == 110)
|
|
shiftstate |= 2;
|
|
else if (ch == 227)
|
|
shiftstate &= ~1;
|
|
else if (ch == 238)
|
|
shiftstate &= ~2;
|
|
//printk("getch: %d\n", ch);
|
|
} // If release, wait for key press
|
|
while ((ch & 0x80) == 0x80 || ch == 238 || ch == 227);
|
|
//printk("getch rel: %d\n", ch);
|
|
ch &= 0x7f;
|
|
if (shiftstate)
|
|
ch = sunkbd_keycode_shifted[ch];
|
|
else
|
|
ch = sunkbd_keycode[ch];
|
|
//printk("getch xlate: %d\n", ch);
|
|
|
|
return ch;
|
|
}
|
|
|
|
/* ( addr len -- actual ) */
|
|
static void
|
|
escc_read_keyboard(void)
|
|
{
|
|
unsigned char *addr;
|
|
int len;
|
|
|
|
len = POP();
|
|
addr = (unsigned char *)POP();
|
|
|
|
if (len < 1)
|
|
printk("escc_read: bad len, addr %p len %x\n", addr, len);
|
|
|
|
if (keyboard_dataready()) {
|
|
*addr = keyboard_readdata();
|
|
PUSH(1);
|
|
} else {
|
|
PUSH(0);
|
|
}
|
|
}
|
|
|
|
DECLARE_UNNAMED_NODE(escc_keyboard, 0, sizeof(ucell));
|
|
|
|
NODE_METHODS(escc_keyboard) = {
|
|
{ "open", escc_port_open },
|
|
{ "close", escc_port_close },
|
|
{ "read", escc_read_keyboard },
|
|
};
|
|
|
|
void
|
|
ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard)
|
|
{
|
|
char nodebuff[256];
|
|
phandle_t aliases;
|
|
|
|
ob_new_obio_device("zs", "serial");
|
|
|
|
ob_reg(base, offset, ZS_REGS, 1);
|
|
|
|
PUSH(slave);
|
|
fword("encode-int");
|
|
push_str("slave");
|
|
fword("property");
|
|
|
|
if (keyboard) {
|
|
PUSH(0);
|
|
PUSH(0);
|
|
push_str("keyboard");
|
|
fword("property");
|
|
|
|
PUSH(0);
|
|
PUSH(0);
|
|
push_str("mouse");
|
|
fword("property");
|
|
}
|
|
|
|
ob_intr(intr);
|
|
|
|
PUSH(0);
|
|
PUSH(0);
|
|
push_str("port-a-ignore-cd");
|
|
fword("property");
|
|
|
|
PUSH(0);
|
|
PUSH(0);
|
|
push_str("port-b-ignore-cd");
|
|
fword("property");
|
|
|
|
if (keyboard) {
|
|
BIND_NODE_METHODS(get_cur_dev(), escc_keyboard);
|
|
} else {
|
|
BIND_NODE_METHODS(get_cur_dev(), escc_port);
|
|
}
|
|
|
|
fword("finish-device");
|
|
|
|
aliases = find_dev("/aliases");
|
|
if (keyboard) {
|
|
snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x",
|
|
(int)offset & 0xffffffff);
|
|
set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1);
|
|
} else {
|
|
snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:a",
|
|
(int)offset & 0xffffffff);
|
|
set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1);
|
|
|
|
snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:b",
|
|
(int)offset & 0xffffffff);
|
|
set_property(aliases, "ttyb", nodebuff, strlen(nodebuff) + 1);
|
|
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
static void
|
|
escc_add_channel(const char *path, const char *node, phys_addr_t addr,
|
|
int esnum)
|
|
{
|
|
char buf[64], tty[32];
|
|
phandle_t dnode, aliases;
|
|
|
|
cell props[10];
|
|
ucell offset;
|
|
int index;
|
|
int legacy;
|
|
|
|
int dbdma_offsets[2][2] = {
|
|
/* ch-b */
|
|
{ 0x6, 0x7 },
|
|
/* ch-a */
|
|
{ 0x4, 0x5 }
|
|
};
|
|
|
|
int reg_offsets[2][2][3] = {
|
|
{
|
|
/* ch-b */
|
|
{ 0x00, 0x10, 0x40 },
|
|
/* ch-a */
|
|
{ 0x20, 0x30, 0x50 }
|
|
},{
|
|
/* legacy ch-b */
|
|
{ 0x0, 0x4, 0x8 },
|
|
/* legacy ch-a */
|
|
{ 0x2, 0x6, 0xa }
|
|
}
|
|
};
|
|
|
|
switch (esnum) {
|
|
case 2: index = 1; legacy = 0; break;
|
|
case 3: index = 0; legacy = 0; break;
|
|
case 4: index = 1; legacy = 1; break;
|
|
case 5: index = 0; legacy = 1; break;
|
|
default: return;
|
|
}
|
|
|
|
/* add device */
|
|
|
|
fword("new-device");
|
|
|
|
snprintf(buf, sizeof(buf), "ch-%s", node);
|
|
push_str(buf);
|
|
fword("device-name");
|
|
|
|
BIND_NODE_METHODS(get_cur_dev(), escc_port);
|
|
|
|
/* add aliases */
|
|
|
|
if (!legacy) {
|
|
aliases = find_dev("/aliases");
|
|
|
|
snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
|
|
OLDWORLD(snprintf(tty, sizeof(tty), "tty%s", node));
|
|
OLDWORLD(set_property(aliases, tty, buf, strlen(buf) + 1));
|
|
snprintf(tty, sizeof(tty), "scc%s", node);
|
|
set_property(aliases, tty, buf, strlen(buf) + 1);
|
|
}
|
|
/* add properties */
|
|
|
|
dnode = get_cur_dev();
|
|
set_property(dnode, "device_type", "serial",
|
|
strlen("serial") + 1);
|
|
|
|
snprintf(buf, sizeof(buf), "chrp,es%d", esnum);
|
|
set_property(dnode, "compatible", buf, 9);
|
|
|
|
if (legacy) {
|
|
offset = IO_ESCC_LEGACY_OFFSET;
|
|
} else {
|
|
offset = IO_ESCC_OFFSET;
|
|
}
|
|
|
|
props[0] = offset + reg_offsets[legacy][index][0];
|
|
props[1] = 0x1;
|
|
props[2] = offset + reg_offsets[legacy][index][1];
|
|
props[3] = 0x1;
|
|
props[4] = offset + reg_offsets[legacy][index][2];
|
|
props[5] = 0x1;
|
|
props[6] = 0x8000 + dbdma_offsets[index][0] * 0x100;
|
|
props[7] = 0x100;
|
|
props[8] = 0x8000 + dbdma_offsets[index][1] * 0x100;
|
|
props[9] = 0x100;
|
|
set_property(dnode, "reg", (char *)&props, 10 * sizeof(cell));
|
|
|
|
props[0] = addr + offset + reg_offsets[legacy][index][0];
|
|
OLDWORLD(set_property(dnode, "AAPL,address",
|
|
(char *)&props, 1 * sizeof(cell)));
|
|
|
|
props[0] = 0x10 - index;
|
|
OLDWORLD(set_property(dnode, "AAPL,interrupts",
|
|
(char *)&props, 1 * sizeof(cell)));
|
|
|
|
props[0] = (0x24) + index;
|
|
props[1] = 0x1;
|
|
props[2] = dbdma_offsets[index][0];
|
|
props[3] = 0x0;
|
|
props[4] = dbdma_offsets[index][1];
|
|
props[5] = 0x0;
|
|
NEWWORLD(set_property(dnode, "interrupts",
|
|
(char *)&props, 6 * sizeof(cell)));
|
|
|
|
set_int_property(dnode, "slot-names", 0);
|
|
|
|
fword("finish-device");
|
|
|
|
uart_init_line((unsigned char*)addr + offset + reg_offsets[legacy][index][0],
|
|
CONFIG_SERIAL_SPEED);
|
|
}
|
|
|
|
void
|
|
escc_init(const char *path, phys_addr_t addr)
|
|
{
|
|
char buf[64];
|
|
int props[2];
|
|
phandle_t dnode;
|
|
|
|
fword("new-device");
|
|
|
|
push_str("escc");
|
|
fword("device-name");
|
|
|
|
dnode = get_cur_dev();
|
|
set_int_property(dnode, "#address-cells", 1);
|
|
props[0] = __cpu_to_be32(IO_ESCC_OFFSET);
|
|
props[1] = __cpu_to_be32(IO_ESCC_SIZE);
|
|
set_property(dnode, "reg", (char *)&props, sizeof(props));
|
|
set_property(dnode, "device_type", "escc",
|
|
strlen("escc") + 1);
|
|
set_property(dnode, "compatible", "escc\0CHRP,es0", 14);
|
|
set_property(dnode, "ranges", "", 0);
|
|
|
|
snprintf(buf, sizeof(buf), "%s/escc", path);
|
|
escc_add_channel(buf, "a", addr, 2);
|
|
escc_add_channel(buf, "b", addr, 3);
|
|
|
|
BIND_NODE_METHODS(dnode, escc);
|
|
fword("finish-device");
|
|
|
|
escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET +
|
|
(CONFIG_SERIAL_PORT ? 0 : 0x20);
|
|
|
|
fword("new-device");
|
|
|
|
push_str("escc-legacy");
|
|
fword("device-name");
|
|
|
|
dnode = get_cur_dev();
|
|
set_int_property(dnode, "#address-cells", 1);
|
|
props[0] = __cpu_to_be32(IO_ESCC_LEGACY_OFFSET);
|
|
props[1] = __cpu_to_be32(IO_ESCC_LEGACY_SIZE);
|
|
set_property(dnode, "reg", (char *)&props, sizeof(props));
|
|
set_property(dnode, "device_type", "escc-legacy",
|
|
strlen("escc-legacy") + 1);
|
|
set_property(dnode, "compatible", "chrp,es1", 9);
|
|
set_property(dnode, "ranges", "", 0);
|
|
|
|
snprintf(buf, sizeof(buf), "%s/escc-legacy", path);
|
|
escc_add_channel(buf, "a", addr, 4);
|
|
escc_add_channel(buf, "b", addr, 5);
|
|
|
|
BIND_NODE_METHODS(dnode, escc);
|
|
fword("finish-device");
|
|
}
|
|
#endif
|