r3751 - trunk/src/host/qemu-neo1973/hw
andrew at sita.openmoko.org
andrew at sita.openmoko.org
Fri Dec 28 21:50:56 CET 2007
Author: andrew
Date: 2007-12-28 21:50:54 +0100 (Fri, 28 Dec 2007)
New Revision: 3751
Modified:
trunk/src/host/qemu-neo1973/hw/ar6000.c
Log:
AR6000 interrupt logic. Fix reset.
Modified: trunk/src/host/qemu-neo1973/hw/ar6000.c
===================================================================
--- trunk/src/host/qemu-neo1973/hw/ar6000.c 2007-12-28 18:13:57 UTC (rev 3750)
+++ trunk/src/host/qemu-neo1973/hw/ar6000.c 2007-12-28 20:50:54 UTC (rev 3751)
@@ -26,6 +26,7 @@
#include "hw.h"
#include "net.h"
+#include "qemu-timer.h"
#include "sd.h"
#include "pcmcia.h"
@@ -944,6 +945,7 @@
struct ar6k_s {
struct sdio_s sd;
NICInfo *nd;
+
struct {
uint8_t host_int_stat;
uint8_t cpu_int_stat;
@@ -956,9 +958,10 @@
uint8_t cpu_int_stat_ena;
uint8_t err_int_stat_ena;
uint8_t cnt_int_stat_ena;
- uint8_t cnt[8];
- uint32_t cnt_dec[8];
+ uint8_t cnt[4];
+ uint32_t cnt_tx[4];
uint8_t scratch[8];
+ uint8_t wlan_int;
uint8_t mbox[0x800 * 4];
int mbox_count[4];
@@ -966,22 +969,94 @@
struct {
int done;
} bmi;
+
+ QEMUTimer *cnt_irq_update;
+
uint8_t cis[0];
};
+static inline void ar6k_hif_intr_update(struct ar6k_s *s)
+{
+ qemu_set_irq(s->sd.func_irq[0],
+ !!(s->hif.host_int_stat & s->hif.int_stat_ena));
+}
+
+static void ar6k_hif_error_intr_update(struct ar6k_s *s)
+{
+ uint8_t orig = s->hif.host_int_stat;
+
+ if (s->hif.error_int_stat & s->hif.err_int_stat_ena)
+ s->hif.host_int_stat |= 1 << 7; /* STATUS_ERROR */
+ else
+ s->hif.host_int_stat &= ~(1 << 7); /* STATUS_ERROR */
+
+ if (orig != s->hif.host_int_stat)
+ ar6k_hif_intr_update(s);
+}
+
+static void ar6k_hif_cpu_intr_update(struct ar6k_s *s)
+{
+ uint8_t orig = s->hif.host_int_stat;
+
+ if (s->hif.cpu_int_stat & s->hif.cpu_int_stat_ena)
+ s->hif.host_int_stat |= 1 << 6; /* STATUS_CPU */
+ else
+ s->hif.host_int_stat &= ~(1 << 6); /* STATUS_CPU */
+
+ if (orig != s->hif.host_int_stat)
+ ar6k_hif_intr_update(s);
+}
+
+static void ar6k_hif_counter_intr_update(struct ar6k_s *s)
+{
+ uint8_t orig = s->hif.host_int_stat;
+
+ if (s->hif.counter_int_stat & s->hif.cnt_int_stat_ena)
+ s->hif.host_int_stat |= 1 << 4; /* STATUS_COUNTER */
+ else
+ s->hif.host_int_stat &= ~(1 << 4); /* STATUS_COUNTER */
+
+ if (orig != s->hif.host_int_stat)
+ ar6k_hif_intr_update(s);
+}
+
+static void ar6k_hif_counter_intr_sched(struct ar6k_s *s)
+{
+ qemu_mod_timer(s->cnt_irq_update, qemu_get_clock(vm_clock) +
+ (ticks_per_sec >> 6));
+}
+
+static void ar6k_hif_cnt_irq_tick(void *opaque)
+{
+ struct ar6k_s *s = (void *) opaque;
+
+ ar6k_hif_counter_intr_update(s);
+}
+
static void ar6k_bmi_reset(struct ar6k_s *s)
{
int i;
- for (i = 0; i < 8; i ++) {
+ for (i = 0; i < 4; i ++) {
s->hif.cnt[i] = 0x00;
- s->hif.cnt_dec[i] = 0xff;
+ s->hif.cnt_tx[i] = 0xff;
+ s->hif.mbox_count[i] = 0;
}
- for (i = 0; i < 4; i ++)
- s->hif.mbox_count[i] = 0;
+ s->bmi.done = 0;
- s->bmi.done = 0;
+ s->hif.host_int_stat = 0x00;
+ s->hif.cpu_int_stat = 0x00;
+ s->hif.error_int_stat = 0x00;
+ s->hif.counter_int_stat = 0xf0;
+ s->hif.int_stat_ena = 0x00;
+ s->hif.cpu_int_stat_ena = 0x00;
+ s->hif.err_int_stat_ena = 0x00;
+ s->hif.cnt_int_stat_ena = 0x00;
+
+ ar6k_hif_cpu_intr_update(s);
+ ar6k_hif_error_intr_update(s);
+ ar6k_hif_counter_intr_update(s);
}
enum {
@@ -1044,10 +1119,27 @@
return;
}
- s->hif.cnt_dec[4] = 0xff;
- s->hif.cnt[4] = rlen;
+ s->hif.cnt[0] = rlen;
}
+static void ar6k_hif_txcredit_reset(struct ar6k_s *s, int mbox)
+{
+ s->hif.cnt_tx[mbox] = 0;
+ if (!(s->hif.counter_int_stat & (1 << mbox))) {
+ s->hif.counter_int_stat |= 1 << mbox;
+ ar6k_hif_counter_intr_sched(s);
+ }
+}
+
+static void ar6k_hif_txcredit_grant(struct ar6k_s *s, int mbox)
+{
+ s->hif.cnt_tx[mbox] = 0xff;
+ if (!(s->hif.counter_int_stat & (1 << (mbox + 4)))) {
+ s->hif.counter_int_stat |= 1 << (mbox + 4);
+ ar6k_hif_counter_intr_sched(s);
+ }
+}
+
#define AR6K_HOST_INT_STAT 0x400
#define AR6K_CPU_INT_STAT 0x401
#define AR6K_ERROR_INT_STAT 0x402
@@ -1063,7 +1155,8 @@
#define AR6K_ERROR_STAT_ENABLE 0x41a
#define AR6K_COUNTER_INT_STAT_ENABLE 0x41b
#define AR6K_COUNT 0x420
-#define AR6K_COUNT_DEC 0x440
+#define AR6K_COUNT_RESET 0x440
+#define AR6K_COUNT_DEC 0x450
#define AR6K_SCRATCH 0x460
#define AR6K_FIFO_TIMEOUT 0x468
#define AR6K_FIFO_TIMEOUT_ENABLE 0x469
@@ -1089,6 +1182,30 @@
int mbox;
switch (addr) {
+ case AR6K_HOST_INT_STAT:
+ if (s->hif.host_int_stat & value) {
+ s->hif.host_int_stat &= ~value;
+ ar6k_hif_intr_update(s);
+ }
+ break;
+ case AR6K_CPU_INT_STAT:
+ if (s->hif.cpu_int_stat & value) {
+ s->hif.cpu_int_stat &= ~value;
+ ar6k_hif_cpu_intr_update(s);
+ }
+ break;
+ case AR6K_ERROR_INT_STAT:
+ if (s->hif.error_int_stat & value) {
+ s->hif.error_int_stat &= ~value;
+ ar6k_hif_error_intr_update(s);
+ }
+ break;
+ case AR6K_COUNTER_INT_STAT:
+ if (s->hif.counter_int_stat & value) {
+ s->hif.counter_int_stat &= ~value;
+ ar6k_hif_counter_intr_update(s);
+ }
+ break;
case AR6K_MBOX_FRAME:
s->hif.mbox_frame = value;
break;
@@ -1108,16 +1225,28 @@
s->hif.rx_la[3] = value;
break;
case AR6K_INT_STAT_ENABLE:
- s->hif.int_stat_ena = value;
+ if (s->hif.int_stat_ena ^ value) {
+ s->hif.int_stat_ena = value;
+ ar6k_hif_intr_update(s);
+ }
break;
case AR6K_CPU_INT_STAT_ENABLE:
- s->hif.cpu_int_stat_ena = value;
+ if (s->hif.cpu_int_stat_ena ^ value) {
+ s->hif.cpu_int_stat_ena = value;
+ ar6k_hif_cpu_intr_update(s);
+ }
break;
case AR6K_ERROR_STAT_ENABLE:
- s->hif.err_int_stat_ena = value;
+ if (s->hif.err_int_stat_ena ^ value) {
+ s->hif.err_int_stat_ena = value;
+ ar6k_hif_error_intr_update(s);
+ }
break;
case AR6K_COUNTER_INT_STAT_ENABLE:
- s->hif.cnt_int_stat_ena = value;
+ if (s->hif.cnt_int_stat_ena ^ value) {
+ s->hif.cnt_int_stat_ena = value;
+ ar6k_hif_counter_intr_sched(s);
+ }
break;
case AR6K_SCRATCH ... (AR6K_SCRATCH + 7):
s->hif.scratch[addr - AR6K_SCRATCH] = value;
@@ -1127,25 +1256,35 @@
case AR6K_DISABLE_SLEEP:
case AR6K_LOCAL_BUS_ENDIAN:
case AR6K_LOCAL_BUS:
+ goto bad_reg;
+
case AR6K_INT_WLAN:
+ s->hif.wlan_int = value;
+ if (value)
+ fprintf(stderr, "%s: WLAN interrupt\n", __FUNCTION__);
+ break;
+
case AR6K_WINDOW_DATA:
case AR6K_WRITE_ADDR:
case AR6K_READ_ADDR:
case AR6K_SPI_CONFIG:
goto bad_reg;
+
case AR6K_HIF_MBOX_BASE ... AR6K_HIF_MBOX_END:
mbox = (addr - AR6K_HIF_MBOX_BASE) >> 11;
s->hif.mbox[addr - AR6K_HIF_MBOX_BASE] = value;
s->hif.mbox_count[mbox] ++;
- /* XXX how do we know when a BMI command is executed? */
- if (mbox == 0 && !s->bmi.done) {
- s->hif.cnt_dec[4] = 0x00;
- s->hif.cnt[4] = 0x00;
- if ((addr & 0x7ff) == 0x7ff) {
+ /* XXX how else do we know when a command is executed? */
+ if ((addr & 0x7ff) == 0x7ff) {
+ ar6k_hif_txcredit_reset(s, mbox);
+ if (mbox == 0 && !s->bmi.done) {
ar6k_bmi_write(s, s->hif.mbox + (mbox << 11),
- s->hif.mbox_count[0]);
- s->hif.mbox_count[mbox] = 0;
+ s->hif.mbox_count[mbox]);
+ } else {
+ /* TODO */
}
+ s->hif.mbox_count[mbox] = 0;
+ ar6k_hif_txcredit_grant(s, mbox);
}
break;
default:
@@ -1157,6 +1296,8 @@
static uint8_t ar6k_hif_read(struct ar6k_s *s, uint32_t addr)
{
+ int mbox = 0;
+
switch (addr) {
case AR6K_HOST_INT_STAT:
return s->hif.host_int_stat;
@@ -1166,8 +1307,10 @@
return s->hif.error_int_stat;
case AR6K_COUNTER_INT_STAT:
return s->hif.counter_int_stat;
+
case AR6K_MBOX_FRAME:
return s->hif.mbox_frame;
+
case AR6K_RX_LOOKAHEAD_VALID:
return s->hif.rx_la_valid;
case AR6K_RX_LOOKAHEAD0:
@@ -1178,6 +1321,7 @@
return s->hif.rx_la[2];
case AR6K_RX_LOOKAHEAD3:
return s->hif.rx_la[3];
+
case AR6K_INT_STAT_ENABLE:
return s->hif.int_stat_ena;
case AR6K_CPU_INT_STAT_ENABLE:
@@ -1186,25 +1330,41 @@
return s->hif.err_int_stat_ena;
case AR6K_COUNTER_INT_STAT_ENABLE:
return s->hif.cnt_int_stat_ena;
- case AR6K_COUNT ... (AR6K_COUNT + 0x7):
- return s->hif.cnt[addr - AR6K_COUNT];
- case AR6K_COUNT_DEC + 0x00:
- case AR6K_COUNT_DEC + 0x04:
- case AR6K_COUNT_DEC + 0x08:
- case AR6K_COUNT_DEC + 0x0c:
- case AR6K_COUNT_DEC + 0x10:
- case AR6K_COUNT_DEC + 0x14:
- case AR6K_COUNT_DEC + 0x18:
- case AR6K_COUNT_DEC + 0x1c:
- return s->hif.cnt_dec[(addr - AR6K_COUNT_DEC) >> 2];
+
+ case (AR6K_COUNT + 0x4) ... (AR6K_COUNT + 0x7):
+ /* XXX What's at (AR6K_COUNT + 0x0) ... (AR6K_COUNT + 0x3)? */
+ /* FIXME clear some interrupts etc */
+ return s->hif.cnt[addr - AR6K_COUNT - 4];
+
+ case AR6K_COUNT_RESET + 0xc: mbox ++;
+ case AR6K_COUNT_RESET + 0x8: mbox ++;
+ case AR6K_COUNT_RESET + 0x4: mbox ++;
+ case AR6K_COUNT_RESET + 0x0:
+ if (s->hif.counter_int_stat & (1 << mbox)) {
+ s->hif.counter_int_stat &= ~(1 << mbox);
+ ar6k_hif_counter_intr_update(s);
+ }
+ return s->hif.cnt_tx[mbox];
+
+ case AR6K_COUNT_DEC + 0xc: mbox ++;
+ case AR6K_COUNT_DEC + 0x8: mbox ++;
+ case AR6K_COUNT_DEC + 0x4: mbox ++;
+ case AR6K_COUNT_DEC + 0x0:
+ return s->hif.cnt_tx[mbox];
+
case AR6K_SCRATCH ... (AR6K_SCRATCH + 7):
return s->hif.scratch[addr - AR6K_SCRATCH];
+
case AR6K_FIFO_TIMEOUT:
case AR6K_FIFO_TIMEOUT_ENABLE:
case AR6K_DISABLE_SLEEP:
case AR6K_LOCAL_BUS_ENDIAN:
case AR6K_LOCAL_BUS:
+ goto bad_reg;
+
case AR6K_INT_WLAN:
+ return s->hif.wlan_int;
+
case AR6K_WINDOW_DATA:
case AR6K_WRITE_ADDR:
case AR6K_READ_ADDR:
@@ -1326,6 +1486,7 @@
struct sd_card_s *ret = sdio_init(&s->sd);
s->nd = nd;
+ s->cnt_irq_update = qemu_new_timer(vm_clock, ar6k_hif_cnt_irq_tick, s);
s->sd.reset = (void *) ar6k_reset;
s->sd.fbr[0].stdfn = 0 | sdio_fn_none;
s->sd.fbr[0].ext_stdfn = sdio_ext_fn_none;
More information about the commitlog
mailing list