From c5724aba27c1f2f450b5bd6cc9d063b1e5e6474e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 4 Jul 2012 11:18:47 +0000 Subject: hw/nseries.c: Add n900 machine add n900 to nseries.c --- hw/nseries.c | 1053 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1053 insertions(+) diff --git a/hw/nseries.c b/hw/nseries.c index e86901c1d8..4497f232c2 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -31,6 +31,7 @@ #include "flash.h" #include "hw.h" #include "bt.h" +#include "net.h" #include "loader.h" #include "blockdev.h" #include "sysbus.h" @@ -1565,8 +1566,1059 @@ static QEMUMachine n810_machine = { .init = n810_init, }; +#define N900_SDRAM_SIZE (256 * 1024 * 1024) +#define N900_ONENAND_CS 0 +#define N900_ONENAND_BUFSIZE (0xc000 << 1) +#define N900_SMC_CS 1 + +#define N900_ONENAND_GPIO 65 +#define N900_CAMFOCUS_GPIO 68 +#define N900_CAMLAUNCH_GPIO 69 +#define N900_SLIDE_GPIO 71 +#define N900_PROXIMITY_GPIO 89 +#define N900_HEADPHONE_EN_GPIO 98 +#define N900_TSC2005_IRQ_GPIO 100 +#define N900_TSC2005_RESET_GPIO 104 +#define N900_CAMCOVER_GPIO 110 +#define N900_KBLOCK_GPIO 113 +#define N900_HEADPHONE_GPIO 177 +#define N900_LIS302DL_INT2_GPIO 180 +#define N900_LIS302DL_INT1_GPIO 181 + +//#define DEBUG_BQ2415X +//#define DEBUG_TPA6130 +//#define DEBUG_LIS302DL + +#define N900_TRACE(fmt, ...) \ + fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#ifdef DEBUG_BQ2415X +#define TRACE_BQ2415X(fmt, ...) N900_TRACE(fmt, ##__VA_ARGS__) +#else +#define TRACE_BQ2415X(...) +#endif +#ifdef DEBUG_TPA6130 +#define TRACE_TPA6130(fmt, ...) N900_TRACE(fmt, ##__VA_ARGS__) +#else +#define TRACE_TPA6130(...) +#endif +#ifdef DEBUG_LIS302DL +#define TRACE_LIS302DL(fmt, ...) N900_TRACE(fmt, ##__VA_ARGS__) +#else +#define TRACE_LIS302DL(...) +#endif + +static uint64_t ssi_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + switch (addr) { + case 0x00: /* REVISION */ + return 0x10; + case 0x14: /* SYSSTATUS */ + return 1; /* RESETDONE */ + default: + break; + } + //printf("%s: addr= " OMAP_FMT_plx "\n", __FUNCTION__, addr); + return 0; +} + +static void ssi_write(void *opaque, target_phys_addr_t addr, uint64_t value, + unsigned size) +{ + //printf("%s: addr=" OMAP_FMT_plx ", value=0x%08x\n", __FUNCTION__, addr, value); +} + +static const MemoryRegionOps ssi_ops = { + .read = ssi_read, + .write = ssi_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +typedef struct LIS302DLState_s { + I2CSlave i2c; + int firstbyte; + uint8_t reg; + + qemu_irq irq[2]; + int8_t axis_max, axis_step; + int noise, dr_test_ack; + + uint8_t ctrl1, ctrl2, ctrl3; + uint8_t status; + struct { + uint8_t cfg, src, ths, dur; + } ff_wu[2]; + struct { + uint8_t cfg, src, thsy_x, thsz; + uint8_t timelimit, latency, window; + } click; + + int32_t x, y, z; +} LIS302DLState; + +static void lis302dl_interrupt_update(LIS302DLState *s) +{ +#ifdef DEBUG_LIS302DL + static const char *rules[8] = { + "GND", "FF_WU_1", "FF_WU_2", "FF_WU_1|2", "DR", + "???", "???", "CLICK" + }; +#endif + int active = (s->ctrl3 & 0x80) ? 0 : 1; + int cond, latch; + int i; + for (i = 0; i < 2; i++) { + switch ((s->ctrl3 >> (i * 3)) & 0x07) { + case 0: + cond = 0; + break; + case 1: + cond = s->ff_wu[0].src & 0x40; + latch = s->ff_wu[0].cfg & 0x40; + break; + case 2: + cond = s->ff_wu[1].src & 0x40; + latch = s->ff_wu[1].cfg & 0x40; + break; + case 3: + cond = ((s->ff_wu[0].src | s->ff_wu[1].src) & 0x40); + latch = ((s->ff_wu[0].cfg | s->ff_wu[1].cfg) & 0x40); + break; + case 4: + cond = (((s->ff_wu[0].src | s->ff_wu[1].src) & 0x3f) & + (((s->ctrl1 & 0x01) ? 0x03 : 0x00) | + ((s->ctrl1 & 0x02) ? 0x0c : 0x00) | + ((s->ctrl1 & 0x04) ? 0x30 : 0x00))); + latch = 0; + break; + case 7: + cond = s->click.src & 0x40; + latch = s->click.cfg & 0x40; + break; + default: + TRACE_LIS302DL("unsupported irq config (%d)", + (s->ctrl3 >> (i * 3)) & 0x07); + cond = 0; + latch = 0; + break; + } + TRACE_LIS302DL("%s: %s irq%d", rules[(s->ctrl3 >> (i * 3)) & 0x07], + cond ? (latch ? "activate" : "pulse") : "deactivate", + i); + qemu_set_irq(s->irq[i], cond ? active : !active); + if (cond && !latch) { + qemu_set_irq(s->irq[i], !active); + } + } +} + +static void lis302dl_trigger(LIS302DLState *s, int axis, int value) +{ + if (value > s->axis_max) value = s->axis_max; + if (value < -s->axis_max) value = -s->axis_max; + switch (axis) { + case 0: s->x = value; break; + case 1: s->y = value; break; + case 2: s->z = value; break; + default: break; + } + if (s->status & (0x01 << axis)) { + s->status |= 0x10 << axis; + } else { + s->status |= 0x01 << axis; + } + if ((s->status & 0x07) == 0x07) { + s->status |= 0x08; + } + if ((s->status & 0x70) == 0x70) { + s->status |= 0x80; + } + uint8_t bit = 0x02 << (axis << 1); /* over threshold */ + s->ff_wu[0].src |= bit; + s->ff_wu[1].src |= bit; + + int i = 0; + for (; i < 2; i++) { + if (s->ff_wu[i].src & 0x3f) { + if (s->ff_wu[i].cfg & 0x80) { + if ((s->ff_wu[i].cfg & 0x3f) == (s->ff_wu[i].src & 0x3f)) { + s->ff_wu[i].src |= 0x40; + } + } else { + if (s->ff_wu[i].src & s->ff_wu[i].cfg & 0x3f) { + s->ff_wu[i].src |= 0x40; + } + } + } + TRACE_LIS302DL("FF_WU_%d: CFG=0x%02x, SRC=0x%02x", + i, s->ff_wu[i].cfg, s->ff_wu[i].src); + } + + lis302dl_interrupt_update(s); +} + +static void lis302dl_step(void *opaque, int axis, int high, int activate) +{ + TRACE_LIS302DL("axis=%d, high=%d, activate=%d", axis, high, activate); + LIS302DLState *s = opaque; + if (activate) { + int v = 0; + switch (axis) { + case 0: v = s->x + (high ? s->axis_step : -s->axis_step); break; + case 1: v = s->y + (high ? s->axis_step : -s->axis_step); break; + case 2: v = s->z + (high ? s->axis_step : -s->axis_step); break; + default: break; + } + if (v > s->axis_max) v = -(s->axis_max - s->axis_step); + if (v < -s->axis_max) v = s->axis_max - s->axis_step; + lis302dl_trigger(s, axis, v); + } +} + +static void lis302dl_reset(DeviceState *ds) +{ + LIS302DLState *s = FROM_I2C_SLAVE(LIS302DLState, I2C_SLAVE_FROM_QDEV(ds)); + + s->firstbyte = 0; + s->reg = 0; + + s->noise = 4; + s->dr_test_ack = 0; + + s->ctrl1 = 0x03; + s->ctrl2 = 0x00; + s->ctrl3 = 0x00; + s->status = 0x00; + + memset(s->ff_wu, 0x00, sizeof(s->ff_wu)); + memset(&s->click, 0x00, sizeof(s->click)); + + s->x = 0; + s->y = -s->axis_max; + s->z = 0; + + lis302dl_interrupt_update(s); +} + +static void lis302dl_event(I2CSlave *i2c, enum i2c_event event) +{ + LIS302DLState *s = FROM_I2C_SLAVE(LIS302DLState, i2c); + if (event == I2C_START_SEND) + s->firstbyte = 1; +} + +static uint8_t lis302dl_readcoord(LIS302DLState *s, int coord) +{ + int v; + + switch (coord) { + case 0: + v = s->x; + break; + case 1: + v = s->y; + break; + case 2: + v = s->z; + break; + default: + hw_error("%s: unknown axis %d", __FUNCTION__, coord); + break; + } + s->status &= ~(0x88 | (0x11 << coord)); + if (s->ctrl1 & 0x10) { + switch (coord) { + case 0: + v -= s->noise; + break; + case 1: + case 2: + v += s->noise; + break; + default: + break; + } + if (++s->noise == 32) { + s->noise = 4; + } + int dr1 = ((s->ctrl3 & 0x07) == 4); + int dr2 = (((s->ctrl3 >> 3) & 0x07) == 4); + if (!s->dr_test_ack++) { + if (dr1) { + qemu_irq_pulse(s->irq[0]); + } + if (dr2) { + qemu_irq_pulse(s->irq[1]); + } + } else if (s->dr_test_ack == 1 + (dr1 + dr2) * 3) { + s->dr_test_ack = 0; + } + } + return (uint8_t)v; +} + +static int lis302dl_rx(I2CSlave *i2c) +{ + LIS302DLState *s = FROM_I2C_SLAVE(LIS302DLState, i2c); + int value = -1; + int n = 0; + switch (s->reg & 0x7f) { + case 0x00 ... 0x0e: + case 0x10 ... 0x1f: + case 0x23 ... 0x26: + case 0x28: + case 0x2a: + case 0x2c: + case 0x2e ... 0x2f: + case 0x3a: + value = 0; + TRACE_LIS302DL("reg 0x%02x = 0x%02x (unused/reserved reg)", + s->reg & 0x7f, value); + break; + case 0x0f: + value = 0x3b; + TRACE_LIS302DL("WHOAMI = 0x%02x", value); + break; + case 0x20: + value = s->ctrl1; + TRACE_LIS302DL("CTRL1 = 0x%02x", value); + break; + case 0x21: + value = s->ctrl2; + TRACE_LIS302DL("CTRL2 = 0x%02x", value); + break; + case 0x22: + value = s->ctrl3; + TRACE_LIS302DL("CTRL3 = 0x%02x", value); + break; + case 0x27: + value = s->status; + TRACE_LIS302DL("STATUS = 0x%02x", value); + break; + case 0x29: + value = lis302dl_readcoord(s, 0); + TRACE_LIS302DL("X = 0x%02x", value); + break; + case 0x2b: + value = lis302dl_readcoord(s, 1); + TRACE_LIS302DL("Y = 0x%02x", value); + break; + case 0x2d: + value = lis302dl_readcoord(s, 2); + TRACE_LIS302DL("Z = 0x%02x", value); + break; + case 0x34: n++; + case 0x30: + value = s->ff_wu[n].cfg; + TRACE_LIS302DL("FF_WU%d.CFG = 0x%02x", n + 1, value); + break; + case 0x35: n++; + case 0x31: + value = s->ff_wu[n].src; + TRACE_LIS302DL("FF_WU%d.SRC = 0x%02x", n + 1, value); + s->ff_wu[n].src = 0; //&= ~0x40; + lis302dl_interrupt_update(s); + break; + case 0x36: n++; + case 0x32: + value = s->ff_wu[n].ths; + TRACE_LIS302DL("FF_WU%d.THS = 0x%02x", n + 1, value); + break; + case 0x37: n++; + case 0x33: + value = s->ff_wu[n].dur; + TRACE_LIS302DL("FF_WU%d.DUR = 0x%02x", n + 1, value); + break; + case 0x38: + value = s->click.cfg; + TRACE_LIS302DL("CLICK_CFG = 0x%02x", value); + break; + case 0x39: + value = s->click.src; + TRACE_LIS302DL("CLICK_SRC = 0x%02x", value); + s->click.src &= ~0x40; + lis302dl_interrupt_update(s); + break; + case 0x3b: + value = s->click.thsy_x; + TRACE_LIS302DL("CLICK_THSY_X = 0x%02x", value); + break; + case 0x3c: + value = s->click.thsz; + TRACE_LIS302DL("CLICK_THSZ = 0x%02x", value); + break; + case 0x3d: + value = s->click.timelimit; + TRACE_LIS302DL("CLICK_TIMELIMIT = 0x%02x", value); + break; + case 0x3e: + value = s->click.latency; + TRACE_LIS302DL("CLICK_LATENCY = 0x%02x", value); + break; + case 0x3f: + value = s->click.window; + TRACE_LIS302DL("CLICK_WINDOW = 0x%02x", value); + break; + default: + hw_error("%s: unknown register 0x%02x", __FUNCTION__, + s->reg & 0x7f); + value = 0; + break; + } + if (s->reg & 0x80) { /* auto-increment? */ + s->reg = (s->reg + 1) | 0x80; + } + return value; +} + +static int lis302dl_tx(I2CSlave *i2c, uint8_t data) +{ + LIS302DLState *s = FROM_I2C_SLAVE(LIS302DLState, i2c); + if (s->firstbyte) { + s->reg = data; + s->firstbyte = 0; + } else { + int n = 0; + switch (s->reg & 0x7f) { + case 0x20: + TRACE_LIS302DL("CTRL1 = 0x%02x", data); + s->ctrl1 = data; + break; + case 0x21: + TRACE_LIS302DL("CTRL2 = 0x%02x", data); + s->ctrl2 = data; + break; + case 0x22: + TRACE_LIS302DL("CTRL3 = 0x%02x", data); + s->ctrl3 = data; + lis302dl_interrupt_update(s); + break; + case 0x34: n++; + case 0x30: + TRACE_LIS302DL("FF_WU%d.CFG = 0x%02x", n + 1, data); + s->ff_wu[n].cfg = data; + break; + case 0x36: n++; + case 0x32: + TRACE_LIS302DL("FF_WU%d.THS = 0x%02x", n + 1, data); + s->ff_wu[n].ths = data; + break; + case 0x37: n++; + case 0x33: + TRACE_LIS302DL("FF_WU%d.DUR = 0x%02x", n + 1, data); + s->ff_wu[n].dur = data; + break; + case 0x38: + TRACE_LIS302DL("CLICK_CFG = 0x%02x", data); + s->click.cfg = data; + break; + case 0x39: + TRACE_LIS302DL("CLICK_SRC = 0x%02x", data); + s->click.src = data; + break; + case 0x3b: + TRACE_LIS302DL("CLICK_THSY_X = 0x%02x", data); + s->click.thsy_x = data; + break; + case 0x3c: + TRACE_LIS302DL("CLICK_THSZ = 0x%02x", data); + s->click.thsz = data; + break; + case 0x3d: + TRACE_LIS302DL("CLICK_TIMELIMIT = 0x%02x", data); + s->click.timelimit = data; + break; + case 0x3e: + TRACE_LIS302DL("CLICK_LATENCY = 0x%02x", data); + s->click.latency = data; + break; + case 0x3f: + TRACE_LIS302DL("CLICK_WINDOW = 0x%02x", data); + s->click.window = data; + break; + default: + hw_error("%s: unknown register 0x%02x (value 0x%02x)", + __FUNCTION__, s->reg & 0x7f, data); + break; + } + if (s->reg & 0x80) { /* auto-increment? */ + s->reg = (s->reg + 1) | 0x80; + } + } + return 1; +} + +static int lis302dl_init(I2CSlave *i2c) +{ + LIS302DLState *s = FROM_I2C_SLAVE(LIS302DLState, i2c); + s->axis_max = 58; + s->axis_step = s->axis_max;// / 2; + qdev_init_gpio_out(&i2c->qdev, s->irq, 2); + return 0; +} + +/* TODO: ideally x, y, z should be runtime modifiable properties, + * which can be set by calling + * lis302dl_trigger(s, axis, value); + * where axis is 0,1,2 for x,y,z + */ +static Property lis302dl_properties[] = { + DEFINE_PROP_INT32("x", LIS302DLState, x, 0), + DEFINE_PROP_INT32("y", LIS302DLState, y, 0), + DEFINE_PROP_INT32("z", LIS302DLState, z, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void lis302dl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + k->init = lis302dl_init; + k->event = lis302dl_event; + k->recv = lis302dl_rx; + k->send = lis302dl_tx; + dc->props = lis302dl_properties; + dc->reset = lis302dl_reset; +} + +static TypeInfo lis302dl_info = { + .name = "lis302dl", + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(LIS302DLState), + .class_init = lis302dl_class_init, +}; + +typedef struct BQ2415XState_s { + I2CSlave i2c; + int firstbyte; + uint8 reg; + + uint8_t id; + uint8_t st_ctrl; + uint8_t ctrl; + uint8_t bat_v; + uint8_t tcc; +} BQ2415XState; + +static void bq2415x_reset(DeviceState *ds) +{ + BQ2415XState *s = FROM_I2C_SLAVE(BQ2415XState, I2C_SLAVE_FROM_QDEV(ds)); + + s->firstbyte = 0; + s->reg = 0; + + s->st_ctrl = 0x50 | 0x80; // 40 + s->ctrl = 0x30; + s->bat_v = 0x0a; + s->tcc = 0xa1; // 89 +} + +static void bq2415x_event(I2CSlave *i2c, enum i2c_event event) +{ + BQ2415XState *s = FROM_I2C_SLAVE(BQ2415XState, i2c); + if (event == I2C_START_SEND) + s->firstbyte = 1; +} + +static int bq2415x_rx(I2CSlave *i2c) +{ + BQ2415XState *s = FROM_I2C_SLAVE(BQ2415XState, i2c); + int value = -1; + switch (s->reg) { + case 0x00: + value = s->st_ctrl; + TRACE_BQ2415X("st_ctrl = 0x%02x", value); + break; + case 0x01: + value = s->ctrl; + TRACE_BQ2415X("ctrl = 0x%02x", value); + break; + case 0x02: + value = s->bat_v; + TRACE_BQ2415X("bat_v = 0x%02x", value); + break; + case 0x03: + case 0x3b: + value = s->id; + TRACE_BQ2415X("id = 0x%02x", value); + break; + case 0x04: + value = s->tcc; + TRACE_BQ2415X("tcc = 0x%02x", value); + break; + default: + TRACE_BQ2415X("unknown register 0x%02x", s->reg); + value = 0; + break; + } + s->reg++; + return value; +} + +static int bq2415x_tx(I2CSlave *i2c, uint8_t data) +{ + BQ2415XState *s = FROM_I2C_SLAVE(BQ2415XState, i2c); + if (s->firstbyte) { + s->reg = data; + s->firstbyte = 0; + } else { + switch (s->reg) { + case 0x00: + TRACE_BQ2415X("st_ctrl = 0x%02x", data); + s->st_ctrl = (s->st_ctrl & 0x3f) | (data & 0x40) | 0x80; + break; + case 0x01: + TRACE_BQ2415X("ctrl = 0x%02x", data); + s->ctrl = data; + break; + case 0x02: + TRACE_BQ2415X("bat_v = 0x%02x", data); + s->bat_v = data; + break; + case 0x04: + TRACE_BQ2415X("tcc = 0x%02x", data); + s->tcc = data | 0x80; + break; + default: + TRACE_BQ2415X("unknown register 0x%02x (value 0x%02x)", + s->reg, data); + break; + } + s->reg++; + } + return 1; +} + +static int bq2415x_init(I2CSlave *i2c) +{ + return 0; +} + +static Property bq2415x_properties[] = { + DEFINE_PROP_UINT8("id", BQ2415XState, id, 0x49), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bq2415x_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + k->init = bq2415x_init; + k->event = bq2415x_event; + k->recv = bq2415x_rx; + k->send = bq2415x_tx; + dc->props = bq2415x_properties; + dc->reset = bq2415x_reset; +} + +static TypeInfo bq2415x_info = { + .name = "bq2415x", + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(BQ2415XState), + .class_init = bq2415x_class_init, +}; + +typedef struct tpa6130_s { + I2CSlave i2c; + int firstbyte; + int reg; + uint8_t data[3]; +} TPA6130State; + +static void tpa6130_reset(DeviceState *ds) +{ + TPA6130State *s = FROM_I2C_SLAVE(TPA6130State, I2C_SLAVE_FROM_QDEV(ds)); + s->firstbyte = 0; + s->reg = 0; + memset(s->data, 0, sizeof(s->data)); +} + +static void tpa6130_event(I2CSlave *i2c, enum i2c_event event) +{ + TPA6130State *s = FROM_I2C_SLAVE(TPA6130State, i2c); + if (event == I2C_START_SEND) + s->firstbyte = 1; +} + +static int tpa6130_rx(I2CSlave *i2c) +{ + TPA6130State *s = FROM_I2C_SLAVE(TPA6130State, i2c); + int value = 0; + switch (s->reg) { + case 1 ... 3: + value = s->data[s->reg - 1]; + TRACE_TPA6130("reg %d = 0x%02x", s->reg, value); + break; + case 4: /* VERSION */ + value = 0x01; + TRACE_TPA6130("version = 0x%02x", value); + break; + default: + TRACE_TPA6130("unknown register 0x%02x", s->reg); + break; + } + s->reg++; + return value; +} + +static int tpa6130_tx(I2CSlave *i2c, uint8_t data) +{ + TPA6130State *s = FROM_I2C_SLAVE(TPA6130State, i2c); + if (s->firstbyte) { + s->reg = data; + s->firstbyte = 0; + } else { + switch (s->reg) { + case 1 ... 3: + TRACE_TPA6130("reg %d = 0x%02x", s->reg, data); + s->data[s->reg - 1] = data; + break; + default: + TRACE_TPA6130("unknown register 0x%02x", s->reg); + break; + } + s->reg++; + } + return 1; +} + +static void tpa6130_irq(void *opaque, int n, int level) +{ + if (n) { + hw_error("%s: unknown interrupt source %d\n", __FUNCTION__, n); + } else { + /* headphone enable */ + TRACE_TPA6130("enable = %d", level); + } +} + +static int tpa6130_init(I2CSlave *i2c) +{ + qdev_init_gpio_in(&i2c->qdev, tpa6130_irq, 1); + return 0; +} + +static void tpa6130_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + k->init = tpa6130_init; + k->event = tpa6130_event; + k->recv = tpa6130_rx; + k->send = tpa6130_tx; + dc->reset = tpa6130_reset; +} + +static TypeInfo tpa6130_info = { + .name = "tpa6130", + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(TPA6130State), + .class_init = tpa6130_class_init, +}; + +struct n900_s { + struct omap_mpu_state_s *cpu; + void *twl4030; + DeviceState *nand; + DeviceState *mipid; + DeviceState *tsc2005; + DeviceState *bq2415x; + DeviceState *tpa6130; + DeviceState *lis302dl; + DeviceState *smc; +#ifdef CONFIG_GLES2 + void *gles2; +#endif + int extended_key; + int slide_open; + int camera_cover_open; + int headphone_connected; +}; + +/* this takes care of the keys which are not located on the + * n900 keypad (note that volume up/down keys are handled by + * the keypad eventhough the keys are not located on the keypad) + * as well as triggering some other hardware button/switch-like + * events that are mapped to the host keyboard: + * + * escape ... power + * f1 ....... keypad slider open/close + * f2 ....... keypad lock + * f3 ....... camera lens cover open/close + * f4 ....... camera focus + * f5 ....... camera take picture + * f6 ....... stereo headphone connect/disconnect + * kp1 ...... decrease accelerometer x axis value + * kp2 ...... increase accelerometer x axis value + * kp4 ...... decrease accelerometer y axis value + * kp5 ...... increase accelerometer y axis value + * kp7 ...... decrease accelerometer z axis value + * kp8 ...... increase accelerometer z axis value + */ +static void n900_key_handler(void *opaque, int keycode) +{ + struct n900_s *s = opaque; + if (!s->extended_key && keycode == 0xe0) { + s->extended_key = 0x80; + } else { + int release = keycode & 0x80; + keycode = (keycode & 0x7f) | s->extended_key; + s->extended_key = 0; + switch (keycode) { + case 0x01: /* escape */ + twl4030_set_powerbutton_state(s->twl4030, !release); + break; + case 0x3b: /* f1 */ + if (release) { + s->slide_open = !s->slide_open; + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, + N900_SLIDE_GPIO), + !s->slide_open); + } + break; + case 0x3c: /* f2 */ + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, N900_KBLOCK_GPIO), + !!release); + break; + case 0x3d: /* f3 */ + if (release) { + s->camera_cover_open = !s->camera_cover_open; + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, + N900_CAMCOVER_GPIO), + s->camera_cover_open); + } + break; + case 0x3e: /* f4 */ + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, + N900_CAMFOCUS_GPIO), + !!release); + break; + case 0x3f: /* f5 */ + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, + N900_CAMLAUNCH_GPIO), + !!release); + break; + case 0x40: /* f6 */ + if (release) { + s->headphone_connected = !s->headphone_connected; + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, + N900_HEADPHONE_GPIO), + !s->headphone_connected); + } + break; + case 0x4f ... 0x50: /* kp1,2 */ + lis302dl_step(s->lis302dl, 0, keycode - 0x4f, !release); + break; + case 0x4b ... 0x4c: /* kp4,5 */ + lis302dl_step(s->lis302dl, 1, keycode - 0x4b, !release); + break; + case 0x47 ... 0x48: /* kp7,8 */ + lis302dl_step(s->lis302dl, 2, keycode - 0x47, !release); + break; + default: + break; + } + } +} + +static void n900_reset(void *opaque) +{ + struct n900_s *s = opaque; + s->slide_open = 1; + s->camera_cover_open = 0; + s->headphone_connected = 0; + qemu_irq_raise(qdev_get_gpio_in(s->cpu->gpio, N900_KBLOCK_GPIO)); + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, N900_HEADPHONE_GPIO), + !s->headphone_connected); + qemu_irq_raise(qdev_get_gpio_in(s->cpu->gpio, N900_CAMLAUNCH_GPIO)); + qemu_irq_raise(qdev_get_gpio_in(s->cpu->gpio, N900_CAMFOCUS_GPIO)); + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, N900_CAMCOVER_GPIO), + s->camera_cover_open); + qemu_set_irq(qdev_get_gpio_in(s->cpu->gpio, N900_SLIDE_GPIO), + !s->slide_open); + omap3_boot_rom_emu(s->cpu); +} + + +static uint16_t n900_twl4030_madc_callback(twl4030_adc_type type, int ch) +{ + return 0x3ff; +} + +static const TWL4030KeyMap n900_twl4030_keymap[] = { + {0x10, 0, 0}, /* Q */ + {0x11, 0, 1}, /* W */ + {0x12, 0, 2}, /* E */ + {0x13, 0, 3}, /* R */ + {0x14, 0, 4}, /* T */ + {0x15, 0, 5}, /* Y */ + {0x16, 0, 6}, /* U */ + {0x17, 0, 7}, /* I */ + {0x18, 1, 0}, /* O */ + {0x20, 1, 1}, /* D */ + {0x34, 1, 2}, /* . */ + {0x2f, 1, 3}, /* V */ + {0xd0, 1, 4}, /* DOWN */ + {0x41, 1, 7}, /* F7 -- volume/zoom down */ + {0x19, 2, 0}, /* P */ + {0x21, 2, 1}, /* F */ + {0xc8, 2, 2}, /* UP */ + {0x30, 2, 3}, /* B */ + {0xcd, 2, 4}, /* RIGHT */ + {0x42, 2, 7}, /* F8 -- volume/zoom up */ + {0x33, 3, 0}, /* , */ + {0x22, 3, 1}, /* G */ + {0x1c, 3, 2}, /* ENTER */ + {0x31, 3, 3}, /* N */ + {0x0e, 4, 0}, /* BACKSPACE */ + {0x23, 4, 1}, /* H */ + {0x32, 4, 3}, /* M */ + {0x1d, 4, 4}, /* LEFTCTRL */ + {0x9d, 4, 4}, /* RIGHTCTRL */ + {0x24, 5, 1}, /* J */ + {0x2c, 5, 2}, /* Z */ + {0x39, 5, 3}, /* SPACE */ + {0x38, 5, 4}, /* LEFTALT -- "fn" */ + {0xb8, 5, 4}, /* RIGHTALT -- "fn" */ + {0x1e, 6, 0}, /* A */ + {0x25, 6, 1}, /* K */ + {0x2d, 6, 2}, /* X */ + {0x39, 6, 3}, /* SPACE */ + {0x2a, 6, 4}, /* LEFTSHIFT */ + {0x36, 6, 4}, /* RIGHTSHIFT */ + {0x1f, 7, 0}, /* S */ + {0x26, 7, 1}, /* L */ + {0x2e, 7, 2}, /* C */ + {0xcb, 7, 3}, /* LEFT */ + // {0x10, 0xff, 2}, /* F9 */ + // {0x10, 0xff, 4}, /* F10 */ + // {0x10, 0xff, 5}, /* F11 */ + {-1, -1, -1} +}; + +static MouseTransformInfo n900_pointercal = { + .x = 800, + .y = 480, + .a = {14114, 18, -2825064, 34, -8765, 32972906, 65536}, +}; + +static void n900_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ssi_iomem = g_new(MemoryRegion, 1); + struct n900_s *s = g_malloc0(sizeof(*s)); + DriveInfo *dmtd = drive_get(IF_MTD, 0, 0); + DriveInfo *dsd = drive_get(IF_SD, 0, 0); + + if (!dmtd && !dsd) { + hw_error("%s: SD or NAND image required", __FUNCTION__); + } +#if MAX_SERIAL_PORTS < 3 +#error MAX_SERIAL_PORTS must be at least 3! +#endif + s->cpu = omap3_mpu_init(sysmem, omap3430, N900_SDRAM_SIZE, + serial_hds[1], serial_hds[2], + serial_hds[0], NULL); + omap_lcd_panel_attach(s->cpu->dss); + + s->tsc2005 = spi_create_device(omap_mcspi_bus(s->cpu->mcspi, 0), + "tsc2005", 0); + qdev_connect_gpio_out(s->tsc2005, 0, + qdev_get_gpio_in(s->cpu->gpio, + N900_TSC2005_IRQ_GPIO)); + tsc2005_set_transform(s->tsc2005, &n900_pointercal, 600, 1500); + cursor_hide = 0; // who wants to use touchscreen without a pointer? + + s->mipid = spi_create_device_noinit(omap_mcspi_bus(s->cpu->mcspi, 0), + "lcd_mipid", 2); + qdev_prop_set_uint32(s->mipid, "id", 0x101234); + qdev_prop_set_uint8(s->mipid, "n900", 1); + qdev_init_nofail(s->mipid); + + + s->nand = qdev_create(NULL, "onenand"); + qdev_prop_set_uint16(s->nand, "manufacturer_id", NAND_MFR_SAMSUNG); + qdev_prop_set_uint16(s->nand, "device_id", 0x40); + qdev_prop_set_uint16(s->nand, "version_id", 0x121); + qdev_prop_set_int32(s->nand, "shift", 1); + if (dmtd && dmtd->bdrv) { + qdev_prop_set_drive_nofail(s->nand, "drive", dmtd->bdrv); + } + qdev_init_nofail(s->nand); + sysbus_connect_irq(sysbus_from_qdev(s->nand), 0, + qdev_get_gpio_in(s->cpu->gpio, N900_ONENAND_GPIO)); + omap_gpmc_attach(s->cpu->gpmc, 0, + sysbus_mmio_get_region(sysbus_from_qdev(s->nand), 0)); + + if (dsd) { + omap3_mmc_attach(s->cpu->omap3_mmc[1], dsd->bdrv, 0, 1); + } + if ((dsd = drive_get(IF_SD, 0, 1)) != NULL) { + omap3_mmc_attach(s->cpu->omap3_mmc[0], dsd->bdrv, 0, 0); + //qemu_irq_raise(omap2_gpio_in_get(s->cpu->gpif, N900_SDCOVER_GPIO)); + } + + memory_region_init_io(ssi_iomem, &ssi_ops, 0, "n900_ssi", 0x3c00); + memory_region_add_subregion(sysmem, 0x48058000, ssi_iomem); + + s->twl4030 = twl4030_init(omap_i2c_bus(s->cpu->i2c[0]), + qdev_get_gpio_in(s->cpu->ih[0], + OMAP_INT_3XXX_SYS_NIRQ), + NULL, n900_twl4030_keymap); + twl4030_madc_attach(s->twl4030, n900_twl4030_madc_callback); + i2c_bus *i2c2 = omap_i2c_bus(s->cpu->i2c[1]); + s->bq2415x = i2c_create_slave(i2c2, "bq2415x", 0x6b); + s->tpa6130 = i2c_create_slave(i2c2, "tpa6130", 0x60); + qdev_connect_gpio_out(s->cpu->gpio, N900_HEADPHONE_EN_GPIO, + qdev_get_gpio_in(s->tpa6130, 0)); + i2c_bus *i2c3 = omap_i2c_bus(s->cpu->i2c[2]); + s->lis302dl = i2c_create_slave(i2c3, "lis302dl", 0x1d); + qdev_connect_gpio_out(s->lis302dl, 0, + qdev_get_gpio_in(s->cpu->gpio, + N900_LIS302DL_INT1_GPIO)); + qdev_connect_gpio_out(s->lis302dl, 1, + qdev_get_gpio_in(s->cpu->gpio, + N900_LIS302DL_INT2_GPIO)); + + int i; + for (i = 0; i < nb_nics; i++) { + if (!nd_table[i].model || !strcmp(nd_table[i].model, "smc91c111")) { + break; + } + } + if (i < nb_nics) { + s->smc = qdev_create(NULL, "smc91c111"); + qdev_set_nic_properties(s->smc, &nd_table[i]); + qdev_init_nofail(s->smc); + sysbus_connect_irq(sysbus_from_qdev(s->smc), 0, + qdev_get_gpio_in(s->cpu->gpio, 54)); + omap_gpmc_attach(s->cpu->gpmc, 1, + sysbus_mmio_get_region(sysbus_from_qdev(s->smc), 0)); + } else { + hw_error("%s: no NIC for smc91c111\n", __FUNCTION__); + } + + qemu_add_kbd_event_handler(n900_key_handler, s); + + qemu_register_reset(n900_reset, s); +} + +static QEMUMachine n900_machine = { + .name = "n900", + .desc = "Nokia N900 (OMAP3)", + .init = n900_init, +}; + static void nseries_register_types(void) { + type_register_static(&bq2415x_info); + type_register_static(&tpa6130_info); + type_register_static(&lis302dl_info); type_register_static(&mipid_info); } @@ -1574,6 +2626,7 @@ static void nseries_machine_init(void) { qemu_register_machine(&n800_machine); qemu_register_machine(&n810_machine); + qemu_register_machine(&n900_machine); } type_init(nseries_register_types); -- cgit v1.2.3