Add WM8750 and MAX7310 chips (I2C slaves).

Wolfson Microsystems WM8750 audio chip and Maxim MAX7310 gpio expander chip are used in the Spitz.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2854 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
balrog
2007-05-23 22:04:23 +00:00
parent 3f582262e5
commit adb86c372e
5 changed files with 805 additions and 2 deletions
+2 -2
View File
@@ -459,8 +459,8 @@ VL_OBJS+= versatile_pci.o sd.o ptimer.o
VL_OBJS+= arm_gic.o realview.o arm_sysctl.o
VL_OBJS+= arm-semi.o
VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o
VL_OBJS+= spitz.o ads7846.o ide.o serial.o nand.o $(AUDIODRV)
VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o
VL_OBJS+= spitz.o ads7846.o ide.o serial.o nand.o $(AUDIODRV) wm8750.o
CPPFLAGS += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), sh4)
+14
View File
@@ -46,4 +46,18 @@ void i2c_nack(i2c_bus *bus);
int i2c_send(i2c_bus *bus, uint8_t data);
int i2c_recv(i2c_bus *bus);
/* max7310.c */
i2c_slave *max7310_init(i2c_bus *bus);
void max7310_reset(i2c_slave *i2c);
qemu_irq *max7310_gpio_in_get(i2c_slave *i2c);
void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler);
/* wm8750.c */
i2c_slave *wm8750_init(i2c_bus *bus, AudioState *audio);
void wm8750_reset(i2c_slave *i2c);
void wm8750_data_req_set(i2c_slave *i2c,
void (*data_req)(void *, int, int), void *opaque);
void wm8750_dac_dat(void *opaque, uint32_t sample);
uint32_t wm8750_adc_dat(void *opaque);
#endif
+188
View File
@@ -0,0 +1,188 @@
/*
* MAX7310 8-port GPIO expansion chip.
*
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
* This file is licensed under GNU GPL.
*/
#include "vl.h"
struct max7310_s {
i2c_slave i2c;
int i2c_command_byte;
int len;
uint8_t level;
uint8_t direction;
uint8_t polarity;
uint8_t status;
uint8_t command;
qemu_irq handler[8];
qemu_irq *gpio_in;
};
void max7310_reset(i2c_slave *i2c)
{
struct max7310_s *s = (struct max7310_s *) i2c;
s->level &= s->direction;
s->direction = 0xff;
s->polarity = 0xf0;
s->status = 0x01;
s->command = 0x00;
}
static int max7310_rx(i2c_slave *i2c)
{
struct max7310_s *s = (struct max7310_s *) i2c;
switch (s->command) {
case 0x00: /* Input port */
return s->level ^ s->polarity;
break;
case 0x01: /* Output port */
return s->level & ~s->direction;
break;
case 0x02: /* Polarity inversion */
return s->polarity;
case 0x03: /* Configuration */
return s->direction;
case 0x04: /* Timeout */
return s->status;
break;
case 0xff: /* Reserved */
return 0xff;
default:
#ifdef VERBOSE
printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
#endif
break;
}
return 0xff;
}
static int max7310_tx(i2c_slave *i2c, uint8_t data)
{
struct max7310_s *s = (struct max7310_s *) i2c;
uint8_t diff;
int line;
if (s->len ++ > 1) {
#ifdef VERBOSE
printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
#endif
return 1;
}
if (s->i2c_command_byte) {
s->command = data;
s->i2c_command_byte = 0;
return 0;
}
switch (s->command) {
case 0x01: /* Output port */
for (diff = (data ^ s->level) & ~s->direction; diff;
diff &= ~(1 << line)) {
line = ffs(diff) - 1;
if (s->handler[line])
qemu_set_irq(s->handler[line], (data >> line) & 1);
}
s->level = (s->level & s->direction) | (data & ~s->direction);
break;
case 0x02: /* Polarity inversion */
s->polarity = data;
break;
case 0x03: /* Configuration */
s->level &= ~(s->direction ^ data);
s->direction = data;
break;
case 0x04: /* Timeout */
s->status = data;
break;
case 0x00: /* Input port - ignore writes */
break;
default:
#ifdef VERBOSE
printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
#endif
return 1;
}
return 0;
}
static void max7310_event(i2c_slave *i2c, enum i2c_event event)
{
struct max7310_s *s = (struct max7310_s *) i2c;
s->len = 0;
switch (event) {
case I2C_START_SEND:
s->i2c_command_byte = 1;
break;
case I2C_FINISH:
if (s->len == 1)
#ifdef VERBOSE
printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
#endif
break;
default:
break;
}
}
static void max7310_gpio_set(void *opaque, int line, int level)
{
struct max7310_s *s = (struct max7310_s *) opaque;
if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0)
cpu_abort(cpu_single_env, "bad GPIO line");
if (level)
s->level |= s->direction & (1 << line);
else
s->level &= ~(s->direction & (1 << line));
}
/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
* but also accepts sequences that are not SMBus so return an I2C device. */
struct i2c_slave *max7310_init(i2c_bus *bus)
{
struct max7310_s *s = (struct max7310_s *)
i2c_slave_init(bus, 0, sizeof(struct max7310_s));
s->i2c.event = max7310_event;
s->i2c.recv = max7310_rx;
s->i2c.send = max7310_tx;
s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s,
sizeof(s->handler) / sizeof(*s->handler));
max7310_reset(&s->i2c);
return &s->i2c;
}
qemu_irq *max7310_gpio_in_get(i2c_slave *i2c)
{
struct max7310_s *s = (struct max7310_s *) i2c;
return s->gpio_in;
}
void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
{
struct max7310_s *s = (struct max7310_s *) i2c;
if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0)
cpu_abort(cpu_single_env, "bad GPIO line");
s->handler[line] = handler;
}
+56
View File
@@ -801,6 +801,57 @@ static void spitz_microdrive_attach(struct pxa2xx_state_s *cpu)
}
}
/* Wm8750 and Max7310 on I2C */
#define AKITA_MAX_ADDR 0x18
#define SPITZ_WM_ADDRL 0x1a
#define SPITZ_WM_ADDRH 0x1b
#define SPITZ_GPIO_WM 5
#ifdef HAS_AUDIO
static void spitz_wm8750_addr(int line, int level, void *opaque)
{
i2c_slave *wm = (i2c_slave *) opaque;
if (level)
i2c_set_slave_address(wm, SPITZ_WM_ADDRH);
else
i2c_set_slave_address(wm, SPITZ_WM_ADDRL);
}
#endif
static void spitz_i2c_setup(struct pxa2xx_state_s *cpu)
{
/* Attach the CPU on one end of our I2C bus. */
i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]);
#ifdef HAS_AUDIO
AudioState *audio;
i2c_slave *wm;
audio = AUD_init();
if (!audio)
return;
/* Attach a WM8750 to the bus */
wm = wm8750_init(bus, audio);
spitz_wm8750_addr(0, 0, wm);
pxa2xx_gpio_handler_set(cpu->gpio, SPITZ_GPIO_WM, spitz_wm8750_addr, wm);
/* .. and to the sound interface. */
cpu->i2s->opaque = wm;
cpu->i2s->codec_out = wm8750_dac_dat;
cpu->i2s->codec_in = wm8750_adc_dat;
wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s);
#endif
}
static void spitz_akita_i2c_setup(struct pxa2xx_state_s *cpu)
{
/* Attach a Max7310 to Akita I2C bus. */
i2c_set_slave_address(max7310_init(pxa2xx_i2c_bus(cpu->i2c[0])),
AKITA_MAX_ADDR);
}
/* Other peripherals */
static void spitz_charge_switch(int line, int level, void *opaque)
@@ -1026,6 +1077,11 @@ static void spitz_common_init(int ram_size, int vga_ram_size,
spitz_gpio_setup(cpu, (model == akita) ? 1 : 2);
spitz_i2c_setup(cpu);
if (model == akita)
spitz_akita_i2c_setup(cpu);
if (model == terrier)
/* A 6.0 GB microdrive is permanently sitting in CF slot 0. */
spitz_microdrive_attach(cpu);
+545
View File
File diff suppressed because it is too large Load Diff