diff options
author | balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-05-23 21:47:51 +0000 |
---|---|---|
committer | balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-05-23 21:47:51 +0000 |
commit | 3f582262e5443ded25ba6c8f016a114279a3b59f (patch) | |
tree | 5604a51f782ca8d8062ee175db5a07a772d095df /hw | |
parent | 209a4e691d230b01cf44c9f954a34db04d5708be (diff) |
Implement the PXA2xx I2C master controller.
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
Diffstat (limited to 'hw')
-rw-r--r-- | hw/pxa.h | 12 | ||||
-rw-r--r-- | hw/pxa2xx.c | 247 | ||||
-rw-r--r-- | hw/pxa2xx_dma.c | 6 | ||||
-rw-r--r-- | hw/pxa2xx_lcd.c | 3 | ||||
-rw-r--r-- | hw/pxa2xx_pcmcia.c | 1 | ||||
-rw-r--r-- | hw/pxa2xx_timer.c | 22 | ||||
-rw-r--r-- | hw/smbus.c | 3 | ||||
-rw-r--r-- | hw/smbus.h | 2 |
8 files changed, 271 insertions, 25 deletions
@@ -65,10 +65,8 @@ 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; diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index a791f0845c..913c0bd764 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -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); diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c index 63d2fb79c5..d2ed623573 100644 --- a/hw/pxa2xx_dma.c +++ b/hw/pxa2xx_dma.c @@ -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; diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c index db20a93663..f8ddbb4a41 100644 --- a/hw/pxa2xx_lcd.c +++ b/hw/pxa2xx_lcd.c @@ -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) diff --git a/hw/pxa2xx_pcmcia.c b/hw/pxa2xx_pcmcia.c index 171d902796..586fd8c953 100644 --- a/hw/pxa2xx_pcmcia.c +++ b/hw/pxa2xx_pcmcia.c @@ -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; } diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c index fe55ce0bd1..073806a645 100644 --- a/hw/pxa2xx_timer.c +++ b/hw/pxa2xx_timer.c @@ -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 * diff --git a/hw/smbus.c b/hw/smbus.c index 651a7a0b97..5189d51dfd 100644 --- a/hw/smbus.c +++ b/hw/smbus.c @@ -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; diff --git a/hw/smbus.h b/hw/smbus.h index 9125896cc9..36977731ef 100644 --- a/hw/smbus.h +++ b/hw/smbus.h @@ -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. */ |