Implement the PXA2xx I2C master controller.
authorbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Wed, 23 May 2007 21:47:51 +0000 (21:47 +0000)
committerbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Wed, 23 May 2007 21:47:51 +0000 (21:47 +0000)
Fix PXA270-specific timers and make minor changes in other PXA parts.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2853 c046a42c-6fe2-441c-8c8c-71466251a162

hw/pxa.h
hw/pxa2xx.c
hw/pxa2xx_dma.c
hw/pxa2xx_lcd.c
hw/pxa2xx_pcmcia.c
hw/pxa2xx_timer.c
hw/smbus.c
hw/smbus.h

index 8e82c28..986dffa 100644 (file)
--- a/hw/pxa.h
+++ b/hw/pxa.h
 qemu_irq *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env);
 
 /* pxa2xx_timer.c */
-void pxa25x_timer_init(target_phys_addr_t base,
-                qemu_irq *irqs, CPUState *cpustate);
-void pxa27x_timer_init(target_phys_addr_t base,
-                qemu_irq *irqs, qemu_irq irq4, CPUState *cpustate);
+void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs);
+void pxa27x_timer_init(target_phys_addr_t base, qemu_irq *irqs, qemu_irq irq4);
 
 /* pxa2xx_gpio.c */
 struct pxa2xx_gpio_info_s;
@@ -117,6 +115,11 @@ void pxa2xx_ssp_attach(struct pxa2xx_ssp_s *port,
                 uint32_t (*readfn)(void *opaque),
                 void (*writefn)(void *opaque, uint32_t value), void *opaque);
 
+struct pxa2xx_i2c_s;
+struct pxa2xx_i2c_s *pxa2xx_i2c_init(target_phys_addr_t base,
+                qemu_irq irq, int ioregister);
+i2c_bus *pxa2xx_i2c_bus(struct pxa2xx_i2c_s *s);
+
 struct pxa2xx_i2s_s;
 struct pxa2xx_fir_s;
 
@@ -127,6 +130,7 @@ struct pxa2xx_state_s {
     struct pxa2xx_gpio_info_s *gpio;
     struct pxa2xx_lcdc_s *lcd;
     struct pxa2xx_ssp_s **ssp;
+    struct pxa2xx_i2c_s *i2c[2];
     struct pxa2xx_mmci_s *mmc;
     struct pxa2xx_pcmcia_s *pcmcia[2];
     struct pxa2xx_i2s_s *i2s;
index a791f08..913c0bd 100644 (file)
@@ -69,9 +69,16 @@ static struct {
 #define PCMD0  0x80    /* Power Manager I2C Command register File 0 */
 #define PCMD31 0xfc    /* Power Manager I2C Command register File 31 */
 
+static uint32_t pxa2xx_i2c_read(void *, target_phys_addr_t);
+static void pxa2xx_i2c_write(void *, target_phys_addr_t, uint32_t);
+
 static uint32_t pxa2xx_pm_read(void *opaque, target_phys_addr_t addr)
 {
     struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    if (addr > s->pm_base + PCMD31) {
+        /* Special case: PWRI2C registers appear in the same range.  */
+        return pxa2xx_i2c_read(s->i2c[1], addr);
+    }
     addr -= s->pm_base;
 
     switch (addr) {
@@ -92,6 +99,11 @@ static void pxa2xx_pm_write(void *opaque, target_phys_addr_t addr,
                 uint32_t value)
 {
     struct pxa2xx_state_s *s = (struct pxa2xx_state_s *) opaque;
+    if (addr > s->pm_base + PCMD31) {
+        /* Special case: PWRI2C registers appear in the same range.  */
+        pxa2xx_i2c_write(s->i2c[1], addr, value);
+        return;
+    }
     addr -= s->pm_base;
 
     switch (addr) {
@@ -1086,6 +1098,225 @@ static CPUWriteMemoryFunc *pxa2xx_rtc_writefn[] = {
     pxa2xx_rtc_write,
 };
 
+/* I2C Interface */
+struct pxa2xx_i2c_s {
+    i2c_slave slave;
+    i2c_bus *bus;
+    target_phys_addr_t base;
+    qemu_irq irq;
+
+    uint16_t control;
+    uint16_t status;
+    uint8_t ibmr;
+    uint8_t data;
+};
+
+#define IBMR   0x80    /* I2C Bus Monitor register */
+#define IDBR   0x88    /* I2C Data Buffer register */
+#define ICR    0x90    /* I2C Control register */
+#define ISR    0x98    /* I2C Status register */
+#define ISAR   0xa0    /* I2C Slave Address register */
+
+static void pxa2xx_i2c_update(struct pxa2xx_i2c_s *s)
+{
+    uint16_t level = 0;
+    level |= s->status & s->control & (1 << 10);               /* BED */
+    level |= (s->status & (1 << 7)) && (s->control & (1 << 9));        /* IRF */
+    level |= (s->status & (1 << 6)) && (s->control & (1 << 8));        /* ITE */
+    level |= s->status & (1 << 9);                             /* SAD */
+    qemu_set_irq(s->irq, !!level);
+}
+
+/* These are only stubs now.  */
+static void pxa2xx_i2c_event(i2c_slave *i2c, enum i2c_event event)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) i2c;
+
+    switch (event) {
+    case I2C_START_SEND:
+        s->status |= (1 << 9);                         /* set SAD */
+        s->status &= ~(1 << 0);                                /* clear RWM */
+        break;
+    case I2C_START_RECV:
+        s->status |= (1 << 9);                         /* set SAD */
+        s->status |= 1 << 0;                           /* set RWM */
+        break;
+    case I2C_FINISH:
+        s->status |= (1 << 4);                         /* set SSD */
+        break;
+    case I2C_NACK:
+        s->status |= 1 << 1;                           /* set ACKNAK */
+        break;
+    }
+    pxa2xx_i2c_update(s);
+}
+
+static int pxa2xx_i2c_rx(i2c_slave *i2c)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) i2c;
+    if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
+        return 0;
+
+    if (s->status & (1 << 0)) {                        /* RWM */
+        s->status |= 1 << 6;                   /* set ITE */
+    }
+    pxa2xx_i2c_update(s);
+
+    return s->data;
+}
+
+static int pxa2xx_i2c_tx(i2c_slave *i2c, uint8_t data)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) i2c;
+    if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
+        return 1;
+
+    if (!(s->status & (1 << 0))) {             /* RWM */
+        s->status |= 1 << 7;                   /* set IRF */
+        s->data = data;
+    }
+    pxa2xx_i2c_update(s);
+
+    return 1;
+}
+
+static uint32_t pxa2xx_i2c_read(void *opaque, target_phys_addr_t addr)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque;
+    addr -= s->base;
+
+    switch (addr) {
+    case ICR:
+        return s->control;
+    case ISR:
+        return s->status | (i2c_bus_busy(s->bus) << 2);
+    case ISAR:
+        return s->slave.address;
+    case IDBR:
+        return s->data;
+    case IBMR:
+        if (s->status & (1 << 2))
+            s->ibmr ^= 3;      /* Fake SCL and SDA pin changes */
+        else
+            s->ibmr = 0;
+        return s->ibmr;
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void pxa2xx_i2c_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *) opaque;
+    int ack;
+    addr -= s->base;
+
+    switch (addr) {
+    case ICR:
+        s->control = value & 0xfff7;
+        if ((value & (1 << 3)) && (value & (1 << 6))) {        /* TB and IUE */
+            /* TODO: slave mode */
+            if (value & (1 << 0)) {                    /* START condition */
+                if (s->data & 1)
+                    s->status |= 1 << 0;               /* set RWM */
+                else
+                    s->status &= ~(1 << 0);            /* clear RWM */
+                ack = !i2c_start_transfer(s->bus, s->data >> 1, s->data & 1);
+            } else {
+                if (s->status & (1 << 0)) {            /* RWM */
+                    s->data = i2c_recv(s->bus);
+                    if (value & (1 << 2))              /* ACKNAK */
+                        i2c_nack(s->bus);
+                    ack = 1;
+                } else
+                    ack = !i2c_send(s->bus, s->data);
+            }
+
+            if (value & (1 << 1))                      /* STOP condition */
+                i2c_end_transfer(s->bus);
+
+            if (ack) {
+                if (value & (1 << 0))                  /* START condition */
+                    s->status |= 1 << 6;               /* set ITE */
+                else
+                    if (s->status & (1 << 0))          /* RWM */
+                        s->status |= 1 << 7;           /* set IRF */
+                    else
+                        s->status |= 1 << 6;           /* set ITE */
+                s->status &= ~(1 << 1);                        /* clear ACKNAK */
+            } else {
+                s->status |= 1 << 6;                   /* set ITE */
+                s->status |= 1 << 10;                  /* set BED */
+                s->status |= 1 << 1;                   /* set ACKNAK */
+            }
+        }
+        if (!(value & (1 << 3)) && (value & (1 << 6))) /* !TB and IUE */
+            if (value & (1 << 4))                      /* MA */
+                i2c_end_transfer(s->bus);
+        pxa2xx_i2c_update(s);
+        break;
+
+    case ISR:
+        s->status &= ~(value & 0x07f0);
+        pxa2xx_i2c_update(s);
+        break;
+
+    case ISAR:
+        i2c_set_slave_address(&s->slave, value & 0x7f);
+        break;
+
+    case IDBR:
+        s->data = value & 0xff;
+        break;
+
+    default:
+        printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+    }
+}
+
+static CPUReadMemoryFunc *pxa2xx_i2c_readfn[] = {
+    pxa2xx_i2c_read,
+    pxa2xx_i2c_read,
+    pxa2xx_i2c_read,
+};
+
+static CPUWriteMemoryFunc *pxa2xx_i2c_writefn[] = {
+    pxa2xx_i2c_write,
+    pxa2xx_i2c_write,
+    pxa2xx_i2c_write,
+};
+
+struct pxa2xx_i2c_s *pxa2xx_i2c_init(target_phys_addr_t base,
+                qemu_irq irq, int ioregister)
+{
+    int iomemtype;
+    struct pxa2xx_i2c_s *s = (struct pxa2xx_i2c_s *)
+            qemu_mallocz(sizeof(struct pxa2xx_i2c_s));
+
+    s->base = base;
+    s->irq = irq;
+    s->slave.event = pxa2xx_i2c_event;
+    s->slave.recv = pxa2xx_i2c_rx;
+    s->slave.send = pxa2xx_i2c_tx;
+    s->bus = i2c_init_bus();
+
+    if (ioregister) {
+        iomemtype = cpu_register_io_memory(0, pxa2xx_i2c_readfn,
+                        pxa2xx_i2c_writefn, s);
+        cpu_register_physical_memory(s->base & 0xfffff000, 0xfff, iomemtype);
+    }
+
+    return s;
+}
+
+i2c_bus *pxa2xx_i2c_bus(struct pxa2xx_i2c_s *s)
+{
+    return s->bus;
+}
+
 /* PXA Inter-IC Sound Controller */
 static void pxa2xx_i2s_reset(struct pxa2xx_i2s_s *i2s)
 {
@@ -1544,7 +1775,7 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size,
     s->dma = pxa27x_dma_init(0x40000000, s->pic[PXA2XX_PIC_DMA]);
 
     pxa27x_timer_init(0x40a00000, &s->pic[PXA2XX_PIC_OST_0],
-                    s->pic[PXA27X_PIC_OST_4_11], s->env);
+                    s->pic[PXA27X_PIC_OST_4_11]);
 
     s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 121);
 
@@ -1608,6 +1839,12 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size,
     cpu_register_physical_memory(s->rtc_base, 0xfff, iomemtype);
     pxa2xx_rtc_reset(s);
 
+    /* Note that PM registers are in the same page with PWRI2C registers.
+     * As a workaround we don't map PWRI2C into memory and we expect
+     * PM handlers to call PWRI2C handlers when appropriate.  */
+    s->i2c[0] = pxa2xx_i2c_init(0x40301600, s->pic[PXA2XX_PIC_I2C], 1);
+    s->i2c[1] = pxa2xx_i2c_init(0x40f00100, s->pic[PXA2XX_PIC_PWRI2C], 0);
+
     s->pm_base = 0x40f00000;
     iomemtype = cpu_register_io_memory(0, pxa2xx_pm_readfn,
                     pxa2xx_pm_writefn, s);
@@ -1643,7 +1880,7 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size,
 
     s->dma = pxa255_dma_init(0x40000000, s->pic[PXA2XX_PIC_DMA]);
 
-    pxa25x_timer_init(0x40a00000, &s->pic[PXA2XX_PIC_OST_0], s->env);
+    pxa25x_timer_init(0x40a00000, &s->pic[PXA2XX_PIC_OST_0]);
 
     s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 85);
 
@@ -1707,6 +1944,12 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size,
     cpu_register_physical_memory(s->rtc_base, 0xfff, iomemtype);
     pxa2xx_rtc_reset(s);
 
+    /* Note that PM registers are in the same page with PWRI2C registers.
+     * As a workaround we don't map PWRI2C into memory and we expect
+     * PM handlers to call PWRI2C handlers when appropriate.  */
+    s->i2c[0] = pxa2xx_i2c_init(0x40301600, s->pic[PXA2XX_PIC_I2C], 1);
+    s->i2c[1] = pxa2xx_i2c_init(0x40f00100, s->pic[PXA2XX_PIC_PWRI2C], 0);
+
     s->pm_base = 0x40f00000;
     iomemtype = cpu_register_io_memory(0, pxa2xx_pm_readfn,
                     pxa2xx_pm_writefn, s);
index 63d2fb7..d2ed623 100644 (file)
@@ -443,16 +443,16 @@ static struct pxa2xx_dma_state_s *pxa2xx_dma_init(target_phys_addr_t base,
     s->base = base;
     s->irq = irq;
     s->handler = (pxa2xx_dma_handler_t) pxa2xx_dma_request;
-    s->req = qemu_mallocz(sizeof(int) * PXA2XX_DMA_NUM_REQUESTS);
+    s->req = qemu_mallocz(sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS);
 
     memset(s->chan, 0, sizeof(struct pxa2xx_dma_channel_s) * s->channels);
     for (i = 0; i < s->channels; i ++)
         s->chan[i].state = DCSR_STOPINTR;
 
-    memset(s->req, 0, sizeof(int) * PXA2XX_DMA_NUM_REQUESTS);
+    memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS);
 
     iomemtype = cpu_register_io_memory(0, pxa2xx_dma_readfn,
-                   pxa2xx_dma_writefn, s);
+                    pxa2xx_dma_writefn, s);
     cpu_register_physical_memory(base, 0x0000ffff, iomemtype);
 
     return s;
index db20a93..f8ddbb4 100644 (file)
@@ -793,8 +793,7 @@ static void pxa2xx_lcdc_dma0_redraw_vert(struct pxa2xx_lcdc_s *s,
                             dest, src, s->xres, -dest_width);
             if (addr < start)
                 start = addr;
-            if (new_addr > end)
-                end = new_addr;
+            end = new_addr;
             if (y < *miny)
                 *miny = y;
             if (y >= *maxy)
index 171d902..586fd8c 100644 (file)
@@ -171,6 +171,7 @@ struct pxa2xx_pcmcia_s *pxa2xx_pcmcia_init(target_phys_addr_t base)
         s->slot.slot_string = "PXA PC Card Socket 0";
     s->slot.irq = qemu_allocate_irqs(pxa2xx_pcmcia_set_irq, s, 1)[0];
     pcmcia_socket_register(&s->slot);
+
     return s;
 }
 
index fe55ce0..073806a 100644 (file)
@@ -75,7 +75,7 @@ struct pxa2xx_timer4_s {
 };
 
 typedef struct {
-    uint32_t base;
+    target_phys_addr_t base;
     int32_t clock;
     int32_t oldclock;
     uint64_t lastload;
@@ -85,8 +85,6 @@ typedef struct {
     uint32_t events;
     uint32_t irq_enabled;
     uint32_t reset3;
-    CPUState *cpustate;
-    int64_t qemu_ticks;
     uint32_t snapshot;
 } pxa2xx_timer_info;
 
@@ -121,7 +119,7 @@ static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n)
         counter = counters[n];
 
     if (!s->tm4[counter].freq) {
-        qemu_del_timer(s->timer[n].qtimer);
+        qemu_del_timer(s->tm4[n].tm.qtimer);
         return;
     }
 
@@ -131,7 +129,7 @@ static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n)
 
     new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm),
                     ticks_per_sec, s->tm4[counter].freq);
-    qemu_mod_timer(s->timer[n].qtimer, new_qemu);
+    qemu_mod_timer(s->tm4[n].tm.qtimer, new_qemu);
 }
 
 static uint32_t pxa2xx_timer_read(void *opaque, target_phys_addr_t offset)
@@ -350,7 +348,7 @@ static void pxa2xx_timer_tick(void *opaque)
     if (t->num == 3)
         if (i->reset3 & 1) {
             i->reset3 = 0;
-            cpu_reset(i->cpustate);
+            qemu_system_reset_request();
         }
 }
 
@@ -367,7 +365,7 @@ static void pxa2xx_timer_tick4(void *opaque)
 }
 
 static pxa2xx_timer_info *pxa2xx_timer_init(target_phys_addr_t base,
-                qemu_irq *irqs, CPUState *cpustate)
+                qemu_irq *irqs)
 {
     int i;
     int iomemtype;
@@ -380,7 +378,6 @@ static pxa2xx_timer_info *pxa2xx_timer_init(target_phys_addr_t base,
     s->clock = 0;
     s->lastload = qemu_get_clock(vm_clock);
     s->reset3 = 0;
-    s->cpustate = cpustate;
 
     for (i = 0; i < 4; i ++) {
         s->timer[i].value = 0;
@@ -398,18 +395,17 @@ static pxa2xx_timer_info *pxa2xx_timer_init(target_phys_addr_t base,
     return s;
 }
 
-void pxa25x_timer_init(target_phys_addr_t base,
-                qemu_irq *irqs, CPUState *cpustate)
+void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs)
 {
-    pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs, cpustate);
+    pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs);
     s->freq = PXA25X_FREQ;
     s->tm4 = 0;
 }
 
 void pxa27x_timer_init(target_phys_addr_t base,
-                qemu_irq *irqs, qemu_irq irq4, CPUState *cpustate)
+                qemu_irq *irqs, qemu_irq irq4)
 {
-    pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs, cpustate);
+    pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs);
     int i;
     s->freq = PXA27X_FREQ;
     s->tm4 = (struct pxa2xx_timer4_s *) qemu_mallocz(8 *
index 651a7a0..5189d51 100644 (file)
@@ -193,6 +193,9 @@ SMBusDevice *smbus_device_init(i2c_bus *bus, int address, int size)
 {
     SMBusDevice *dev;
 
+    if (size < sizeof(SMBusDevice))
+        cpu_abort(cpu_single_env, "SMBus struct too small");
+
     dev = (SMBusDevice *)i2c_slave_init(bus, address, size);
     dev->i2c.event = smbus_i2c_event;
     dev->i2c.recv = smbus_i2c_recv;
index 9125896..3697773 100644 (file)
@@ -37,7 +37,7 @@ struct SMBusDevice {
        (if present).  The device is responsible figuring out what type of
        command  this is.  */
     void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len);
-    /* Likewise we can't distinguish between defferent reads, or even know
+    /* Likewise we can't distinguish between different reads, or even know
        the length of the read until the read is complete, so read data a
        byte at a time.  The device is responsible for adding the length
        byte on block reads.  */