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
This commit is contained in:
balrog 2007-05-23 21:47:51 +00:00
parent 209a4e691d
commit 3f582262e5
8 changed files with 271 additions and 25 deletions

View File

@ -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;

View 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);

View 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;

View 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)

View 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;
}

View 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 *

View 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;

View 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. */