Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / drivers / cuda.c
diff --git a/qemu/roms/openbios/drivers/cuda.c b/qemu/roms/openbios/drivers/cuda.c
new file mode 100644 (file)
index 0000000..9555dea
--- /dev/null
@@ -0,0 +1,424 @@
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "drivers/drivers.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "macio.h"
+#include "cuda.h"
+
+//#define DEBUG_CUDA
+#ifdef DEBUG_CUDA
+#define CUDA_DPRINTF(fmt, args...) \
+       do { printk("CUDA - %s: " fmt, __func__ , ##args); } while (0)
+#else
+#define CUDA_DPRINTF(fmt, args...) do { } while (0)
+#endif
+
+#define IO_CUDA_OFFSET 0x00016000
+#define IO_CUDA_SIZE   0x00002000
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS              0x200           /* skip between registers */
+#define B               0               /* B-side data */
+#define A               RS              /* A-side data */
+#define DIRB            (2*RS)          /* B-side direction (1=output) */
+#define DIRA            (3*RS)          /* A-side direction (1=output) */
+#define T1CL            (4*RS)          /* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH            (5*RS)          /* Timer 1 counter (high 8 bits) */
+#define T1LL            (6*RS)          /* Timer 1 latch (low 8 bits) */
+#define T1LH            (7*RS)          /* Timer 1 latch (high 8 bits) */
+#define T2CL            (8*RS)          /* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH            (9*RS)          /* Timer 2 counter (high 8 bits) */
+#define SR              (10*RS)         /* Shift register */
+#define ACR             (11*RS)         /* Auxiliary control register */
+#define PCR             (12*RS)         /* Peripheral control register */
+#define IFR             (13*RS)         /* Interrupt flag register */
+#define IER             (14*RS)         /* Interrupt enable register */
+#define ANH             (15*RS)         /* A-side data, no handshake */
+
+/* Bits in B data register: all active low */
+#define TREQ            0x08            /* Transfer request (input) */
+#define TACK            0x10            /* Transfer acknowledge (output) */
+#define TIP             0x20            /* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL         0x1c            /* Shift register control bits */
+#define SR_EXT          0x0c            /* Shift on external clock */
+#define SR_OUT          0x10            /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET         0x80            /* set bits in IER */
+#define IER_CLR         0               /* clear bits in IER */
+#define SR_INT          0x04            /* Shift register full/empty */
+
+#define CUDA_BUF_SIZE 16
+
+#define ADB_PACKET      0
+#define CUDA_PACKET     1
+
+/* CUDA commands (2nd byte) */
+#define CUDA_GET_TIME                  0x03
+#define CUDA_SET_TIME                  0x09
+#define CUDA_POWERDOWN                  0x0a
+#define CUDA_RESET_SYSTEM               0x11
+
+static uint8_t cuda_readb (cuda_t *dev, int reg)
+{
+           return *(volatile uint8_t *)(dev->base + reg);
+}
+
+static void cuda_writeb (cuda_t *dev, int reg, uint8_t val)
+{
+           *(volatile uint8_t *)(dev->base + reg) = val;
+}
+
+static void cuda_wait_irq (cuda_t *dev)
+{
+    int val;
+
+//    CUDA_DPRINTF("\n");
+    for(;;) {
+        val = cuda_readb(dev, IFR);
+        cuda_writeb(dev, IFR, val & 0x7f);
+        if (val & SR_INT)
+            break;
+    }
+}
+
+
+
+static int cuda_request (cuda_t *dev, uint8_t pkt_type, const uint8_t *buf,
+                         int buf_len, uint8_t *obuf)
+{
+    int i, obuf_len, val;
+
+    cuda_writeb(dev, ACR, cuda_readb(dev, ACR) | SR_OUT);
+    cuda_writeb(dev, SR, pkt_type);
+    cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP);
+    if (buf) {
+        //CUDA_DPRINTF("Send buf len: %d\n", buf_len);
+        /* send 'buf' */
+        for(i = 0; i < buf_len; i++) {
+            cuda_wait_irq(dev);
+            cuda_writeb(dev, SR, buf[i]);
+            cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK);
+        }
+    }
+    cuda_wait_irq(dev);
+    cuda_writeb(dev, ACR, cuda_readb(dev, ACR) & ~SR_OUT);
+    cuda_readb(dev, SR);
+    cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK);
+
+    obuf_len = 0;
+    if (obuf) {
+        cuda_wait_irq(dev);
+        cuda_readb(dev, SR);
+        cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP);
+        for(;;) {
+            cuda_wait_irq(dev);
+            val = cuda_readb(dev, SR);
+            if (obuf_len < CUDA_BUF_SIZE)
+                obuf[obuf_len++] = val;
+            if (cuda_readb(dev, B) & TREQ)
+                break;
+            cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK);
+        }
+        cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK);
+
+        cuda_wait_irq(dev);
+        cuda_readb(dev, SR);
+    }
+//    CUDA_DPRINTF("Got len: %d\n", obuf_len);
+
+    return obuf_len;
+}
+
+
+
+static int cuda_adb_req (void *host, const uint8_t *snd_buf, int len,
+                         uint8_t *rcv_buf)
+{
+    uint8_t buffer[CUDA_BUF_SIZE], *pos;
+
+ //   CUDA_DPRINTF("len: %d %02x\n", len, snd_buf[0]);
+    len = cuda_request(host, ADB_PACKET, snd_buf, len, buffer);
+    if (len > 1 && buffer[0] == ADB_PACKET) {
+        pos = buffer + 2;
+        len -= 2;
+    } else {
+        pos = buffer + 1;
+        len = -1;
+    }
+    memcpy(rcv_buf, pos, len);
+
+    return len;
+}
+
+
+DECLARE_UNNAMED_NODE(ob_cuda, INSTALL_OPEN, sizeof(int));
+
+static cuda_t *main_cuda;
+
+static void
+ppc32_reset_all(void)
+{
+        uint8_t cmdbuf[2], obuf[64];
+
+        cmdbuf[0] = CUDA_RESET_SYSTEM;
+        cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+}
+
+static void
+ppc32_poweroff(void)
+{
+        uint8_t cmdbuf[2], obuf[64];
+
+        cmdbuf[0] = CUDA_POWERDOWN;
+        cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+}
+
+static void
+ob_cuda_initialize (int *idx)
+{
+       phandle_t ph=get_cur_dev();
+       int props[2];
+
+       push_str("via-cuda");
+       fword("device-type");
+
+       set_int_property(ph, "#address-cells", 1);
+        set_int_property(ph, "#size-cells", 0);
+
+       set_property(ph, "compatible", "cuda", 5);
+
+       props[0] = __cpu_to_be32(IO_CUDA_OFFSET);
+       props[1] = __cpu_to_be32(IO_CUDA_SIZE);
+
+       set_property(ph, "reg", (char *)&props, sizeof(props));
+
+       /* on newworld machines the cuda is on interrupt 0x19 */
+
+       props[0] = 0x19;
+       props[1] = 0;
+       NEWWORLD(set_property(ph, "interrupts", (char *)props, sizeof(props)));
+       NEWWORLD(set_int_property(ph, "#interrupt-cells", 2));
+
+       /* we emulate an oldworld hardware, so we must use
+        * non-standard oldworld property (needed by linux 2.6.18)
+        */
+
+       OLDWORLD(set_int_property(ph, "AAPL,interrupts", 0x12));
+
+        bind_func("ppc32-reset-all", ppc32_reset_all);
+        push_str("' ppc32-reset-all to reset-all");
+        fword("eval");
+}
+
+static void
+ob_cuda_open(int *idx)
+{
+       RET(-1);
+}
+
+static void
+ob_cuda_close(int *idx)
+{
+}
+
+NODE_METHODS(ob_cuda) = {
+       { NULL,                 ob_cuda_initialize      },
+       { "open",               ob_cuda_open            },
+       { "close",              ob_cuda_close           },
+};
+
+DECLARE_UNNAMED_NODE(rtc, INSTALL_OPEN, sizeof(int));
+
+static void
+rtc_open(int *idx)
+{
+       RET(-1);
+}
+
+/*
+ * get-time ( -- second minute hour day month year )
+ *
+ */
+
+static const int days_month[12] =
+       { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static const int days_month_leap[12] =
+       { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static inline int is_leap(int year)
+{
+       return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
+}
+
+static  void
+rtc_get_time(int *idx)
+{
+        uint8_t cmdbuf[2], obuf[64];
+       ucell second, minute, hour, day, month, year;
+       uint32_t now;
+       int current;
+       const int *days;
+
+        cmdbuf[0] = CUDA_GET_TIME;
+        cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+
+       /* seconds since 01/01/1904 */
+
+       now = (obuf[3] << 24) + (obuf[4] << 16) + (obuf[5] << 8) + obuf[6];
+
+       second =  now % 60;
+       now /= 60;
+
+       minute = now % 60;
+       now /= 60;
+
+       hour = now % 24;
+       now /= 24;
+
+       year = now * 100 / 36525;
+       now -= year * 36525 / 100;
+       year += 1904;
+
+       days = is_leap(year) ?  days_month_leap : days_month;
+
+       current = 0;
+       month = 0;
+       while (month < 12) {
+               if (now <= current + days[month]) {
+                       break;
+               }
+               current += days[month];
+               month++;
+       }
+       month++;
+
+       day = now - current;
+
+       PUSH(second);
+       PUSH(minute);
+       PUSH(hour);
+       PUSH(day);
+       PUSH(month);
+       PUSH(year);
+}
+
+/*
+ * set-time ( second minute hour day month year -- )
+ *
+ */
+
+static  void
+rtc_set_time(int *idx)
+{
+        uint8_t cmdbuf[5], obuf[3];
+       ucell second, minute, hour, day, month, year;
+       const int *days;
+       uint32_t now;
+       unsigned int nb_days;
+       int i;
+
+       year = POP();
+       month = POP();
+       day = POP();
+       hour = POP();
+       minute = POP();
+       second = POP();
+
+       days = is_leap(year) ?  days_month_leap : days_month;
+       nb_days = (year - 1904) * 36525 / 100 + day;
+       for (i = 0; i < month - 1; i++)
+               nb_days += days[i];
+
+       now = (((nb_days * 24) + hour) * 60 + minute) * 60 + second;
+
+        cmdbuf[0] = CUDA_SET_TIME;
+       cmdbuf[1] = now >> 24;
+       cmdbuf[2] = now >> 16;
+       cmdbuf[3] = now >> 8;
+       cmdbuf[4] = now;
+
+        cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+}
+
+NODE_METHODS(rtc) = {
+       { "open",               rtc_open                },
+       { "get-time",           rtc_get_time            },
+       { "set-time",           rtc_set_time            },
+};
+
+static void
+rtc_init(char *path)
+{
+       phandle_t ph, aliases;
+       char buf[64];
+
+        snprintf(buf, sizeof(buf), "%s/rtc", path);
+       REGISTER_NAMED_NODE(rtc, buf);
+
+       ph = find_dev(buf);
+       set_property(ph, "device_type", "rtc", 4);
+       set_property(ph, "compatible", "rtc", 4);
+
+       aliases = find_dev("/aliases");
+       set_property(aliases, "rtc", buf, strlen(buf) + 1);
+
+}
+
+static void
+powermgt_init(char *path)
+{
+       phandle_t ph;
+       char buf[64];
+
+        snprintf(buf, sizeof(buf), "%s/power-mgt", path);
+       REGISTER_NAMED_NODE(rtc, buf);
+
+       ph = find_dev(buf);
+       set_property(ph, "device_type", "power-mgt", 10);
+       set_property(ph, "compatible", "power-mgt", 10);
+}
+
+cuda_t *cuda_init (const char *path, phys_addr_t base)
+{
+       cuda_t *cuda;
+       char buf[64];
+       phandle_t aliases;
+
+       base += IO_CUDA_OFFSET;
+       CUDA_DPRINTF(" base=" FMT_plx "\n", base);
+       cuda = malloc(sizeof(cuda_t));
+       if (cuda == NULL)
+           return NULL;
+
+       snprintf(buf, sizeof(buf), "%s/via-cuda", path);
+       REGISTER_NAMED_NODE(ob_cuda, buf);
+
+       aliases = find_dev("/aliases");
+       set_property(aliases, "via-cuda", buf, strlen(buf) + 1);
+
+       cuda->base = base;
+       cuda_writeb(cuda, B, cuda_readb(cuda, B) | TREQ | TIP);
+#ifdef CONFIG_DRIVER_ADB
+       cuda->adb_bus = adb_bus_new(cuda, &cuda_adb_req);
+       if (cuda->adb_bus == NULL) {
+           free(cuda);
+           return NULL;
+       }
+       adb_bus_init(buf, cuda->adb_bus);
+#endif
+
+       rtc_init(buf);
+       powermgt_init(buf);
+
+        main_cuda = cuda;
+
+       device_end();
+       bind_func("poweroff", ppc32_poweroff);
+
+       return cuda;
+}