aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/nseries.c1053
1 files changed, 1053 insertions, 0 deletions
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);