handle the case where several PCI irqs share the same PIC irq
[qemu] / hw / m48t59.c
index fbee94f..5ab5816 100644 (file)
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-
-#include <stdlib.h>
-#include <stdio.h> /* needed by vl.h */
-#include <stdint.h>
-#include <string.h>
-#include <time.h>
-
 #include "vl.h"
+#include "m48t59.h"
 
-//#define NVRAM_DEBUG
+//#define DEBUG_NVRAM
 
-#if defined(NVRAM_DEBUG)
+#if defined(DEBUG_NVRAM)
 #define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
 #else
 #define NVRAM_PRINTF(fmt, args...) do { } while (0)
 #endif
 
-typedef struct m48t59_t {
+struct m48t59_t {
     /* Hardware parameters */
     int      IRQ;
+    int mem_index;
+    uint32_t mem_base;
     uint32_t io_base;
     uint16_t size;
     /* RTC management */
@@ -51,12 +47,10 @@ typedef struct m48t59_t {
     struct QEMUTimer *alrm_timer;
     struct QEMUTimer *wd_timer;
     /* NVRAM storage */
+    uint8_t  lock;
     uint16_t addr;
     uint8_t *buffer;
-} m48t59_t;
-
-static m48t59_t *NVRAMs;
-static int nb_NVRAMs;
+};
 
 /* Fake timer functions */
 /* Generic helpers for BCD */
@@ -76,7 +70,11 @@ static void get_time (m48t59_t *NVRAM, struct tm *tm)
     time_t t;
 
     t = time(NULL) + NVRAM->time_offset;
-    localtime_r(&t, tm);
+#ifdef _WIN32
+    memcpy(tm,localtime(&t),sizeof(*tm));
+#else
+    localtime_r (&t, tm) ;
+#endif
 }
 
 static void set_time (m48t59_t *NVRAM, struct tm *tm)
@@ -138,7 +136,11 @@ static void alarm_cb (void *opaque)
 
 static void get_alarm (m48t59_t *NVRAM, struct tm *tm)
 {
-    localtime_r(&NVRAM->alarm, tm);
+#ifdef _WIN32
+    memcpy(tm,localtime(&NVRAM->alarm),sizeof(*tm));
+#else
+    localtime_r (&NVRAM->alarm, tm);
+#endif
 }
 
 static void set_alarm (m48t59_t *NVRAM, struct tm *tm)
@@ -161,7 +163,8 @@ static void watchdog_cb (void *opaque)
     if (NVRAM->buffer[0x1FF7] & 0x80) {
        NVRAM->buffer[0x1FF7] = 0x00;
        NVRAM->buffer[0x1FFC] &= ~0x40;
-       //      reset_CPU();
+        /* May it be a hw CPU Reset instead ? */
+        qemu_system_reset_request();
     } else {
        pic_set_irq(NVRAM->IRQ, 1);
        pic_set_irq(NVRAM->IRQ, 0);
@@ -185,9 +188,8 @@ static void set_up_watchdog (m48t59_t *NVRAM, uint8_t value)
 }
 
 /* Direct access to NVRAM */
-void m48t59_write (void *opaque, uint32_t val)
+void m48t59_write (m48t59_t *NVRAM, uint32_t val)
 {
-    m48t59_t *NVRAM = opaque;
     struct tm tm;
     int tmp;
 
@@ -325,6 +327,11 @@ void m48t59_write (void *opaque, uint32_t val)
        }
         break;
     default:
+        /* Check lock registers state */
+        if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1))
+            break;
+        if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2))
+            break;
         if (NVRAM->addr < 0x1FF0 ||
            (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
             NVRAM->buffer[NVRAM->addr] = val & 0xFF;
@@ -333,9 +340,8 @@ void m48t59_write (void *opaque, uint32_t val)
     }
 }
 
-uint32_t m48t59_read (void *opaque)
+uint32_t m48t59_read (m48t59_t *NVRAM)
 {
-    m48t59_t *NVRAM = opaque;
     struct tm tm;
     uint32_t retval = 0xFF;
 
@@ -405,6 +411,11 @@ uint32_t m48t59_read (void *opaque)
         retval = toBCD(tm.tm_year);
         break;
     default:
+        /* Check lock registers state */
+        if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1))
+            break;
+        if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2))
+            break;
         if (NVRAM->addr < 0x1FF0 ||
            (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) {
        do_read:
@@ -418,19 +429,23 @@ uint32_t m48t59_read (void *opaque)
     return retval;
 }
 
-void m48t59_set_addr (void *opaque, uint32_t addr)
+void m48t59_set_addr (m48t59_t *NVRAM, uint32_t addr)
 {
-    m48t59_t *NVRAM = opaque;
-
     NVRAM->addr = addr;
 }
 
+void m48t59_toggle_lock (m48t59_t *NVRAM, int lock)
+{
+    NVRAM->lock ^= 1 << lock;
+}
+
 /* IO access to NVRAM */
 static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
 {
     m48t59_t *NVRAM = opaque;
 
     addr -= NVRAM->io_base;
+    NVRAM_PRINTF("0x%08x => 0x%08x\n", addr, val);
     switch (addr) {
     case 0:
         NVRAM->addr &= ~0x00FF;
@@ -452,35 +467,136 @@ static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
 static uint32_t NVRAM_readb (void *opaque, uint32_t addr)
 {
     m48t59_t *NVRAM = opaque;
+    uint32_t retval;
+
+    addr -= NVRAM->io_base;
+    switch (addr) {
+    case 3:
+        retval = m48t59_read(NVRAM);
+        break;
+    default:
+        retval = -1;
+        break;
+    }
+    NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval);
+
+    return retval;
+}
+
+static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    m48t59_t *NVRAM = opaque;
+    
+    addr -= NVRAM->mem_base;
+    if (addr < 0x1FF0)
+        NVRAM->buffer[addr] = value;
+}
+
+static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    m48t59_t *NVRAM = opaque;
+    
+    addr -= NVRAM->mem_base;
+    if (addr < 0x1FF0) {
+        NVRAM->buffer[addr] = value >> 8;
+        NVRAM->buffer[addr + 1] = value;
+    }
+}
 
-    if (addr == NVRAM->io_base + 3)
-        return m48t59_read(NVRAM);
+static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    m48t59_t *NVRAM = opaque;
+    
+    addr -= NVRAM->mem_base;
+    if (addr < 0x1FF0) {
+        NVRAM->buffer[addr] = value >> 24;
+        NVRAM->buffer[addr + 1] = value >> 16;
+        NVRAM->buffer[addr + 2] = value >> 8;
+        NVRAM->buffer[addr + 3] = value;
+    }
+}
 
-    return 0xFF;
+static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
+{
+    m48t59_t *NVRAM = opaque;
+    uint32_t retval = 0;
+    
+    addr -= NVRAM->mem_base;
+    if (addr < 0x1FF0)
+        retval = NVRAM->buffer[addr];
+
+    return retval;
 }
 
+static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
+{
+    m48t59_t *NVRAM = opaque;
+    uint32_t retval = 0;
+    
+    addr -= NVRAM->mem_base;
+    if (addr < 0x1FF0) {
+        retval = NVRAM->buffer[addr] << 8;
+        retval |= NVRAM->buffer[addr + 1];
+    }
+
+    return retval;
+}
+
+static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
+{
+    m48t59_t *NVRAM = opaque;
+    uint32_t retval = 0;
+    
+    addr -= NVRAM->mem_base;
+    if (addr < 0x1FF0) {
+        retval = NVRAM->buffer[addr] << 24;
+        retval |= NVRAM->buffer[addr + 1] << 16;
+        retval |= NVRAM->buffer[addr + 2] << 8;
+        retval |= NVRAM->buffer[addr + 3];
+    }
+
+    return retval;
+}
+
+static CPUWriteMemoryFunc *nvram_write[] = {
+    &nvram_writeb,
+    &nvram_writew,
+    &nvram_writel,
+};
+
+static CPUReadMemoryFunc *nvram_read[] = {
+    &nvram_readb,
+    &nvram_readw,
+    &nvram_readl,
+};
 /* Initialisation routine */
-void *m48t59_init (int IRQ, uint32_t io_base, uint16_t size)
+m48t59_t *m48t59_init (int IRQ, uint32_t mem_base,
+                       uint32_t io_base, uint16_t size)
 {
-    m48t59_t *tmp;
+    m48t59_t *s;
 
-    tmp = realloc(NVRAMs, (nb_NVRAMs + 1) * sizeof(m48t59_t));
-    if (tmp == NULL)
+    s = qemu_mallocz(sizeof(m48t59_t));
+    if (!s)
        return NULL;
-    NVRAMs = tmp;
-    tmp[nb_NVRAMs].buffer = malloc(size);
-    if (tmp[nb_NVRAMs].buffer == NULL)
-       return NULL;
-    memset(tmp[nb_NVRAMs].buffer, 0, size);
-    tmp[nb_NVRAMs].IRQ = IRQ;
-    tmp[nb_NVRAMs].size = size;
-    tmp[nb_NVRAMs].io_base = io_base;
-    tmp[nb_NVRAMs].addr = 0;
-    register_ioport_read(io_base, 0x04, 1, NVRAM_readb, &NVRAMs[nb_NVRAMs]);
-    register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, &NVRAMs[nb_NVRAMs]);
-    tmp[nb_NVRAMs].alrm_timer = qemu_new_timer(vm_clock, &alarm_cb,
-                                              &tmp[nb_NVRAMs]);
-    tmp[nb_NVRAMs].wd_timer = qemu_new_timer(vm_clock, &watchdog_cb,
-                                            &tmp[nb_NVRAMs]);
-    return &NVRAMs[nb_NVRAMs++];
+    s->buffer = qemu_mallocz(size);
+    if (!s->buffer) {
+        qemu_free(s);
+        return NULL;
+    }
+    s->IRQ = IRQ;
+    s->size = size;
+    s->mem_base = mem_base;
+    s->io_base = io_base;
+    s->addr = 0;
+    register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s);
+    register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s);
+    if (mem_base != 0) {
+        s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s);
+        cpu_register_physical_memory(mem_base, 0x4000, s->mem_index);
+    }
+    s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s);
+    s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s);
+    s->lock = 0;
+
+    return s;
 }