/*
- * TI TWL4030 for beagle board
- * register implementation based on TPS65950 ES1.0 specification
+ * TI TWL4030 emulation
*
* Copyright (C) 2008 yajin<yajin@vm-kernel.org>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Register implementation based on TPS65950 ES1.0 specification.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
#include "console.h"
#include "cpu-all.h"
-#define VERBOSE 1
-#define TRACEW(regname, value) fprintf(stderr, "%s: %s = 0x%02x\n", __FUNCTION__, regname, value);
+//#define VERBOSE 1
+
+#ifdef VERBOSE
+#define TRACE(fmt, ...) fprintf(stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__)
+#else
+#define TRACE(...)
+#endif
+
+typedef struct TWL4030State TWL4030State;
+typedef struct TWL4030NodeState TWL4030NodeState;
-//extern CPUState *cpu_single_env;
+typedef uint8_t (*twl4030_read_func)(TWL4030NodeState *s,
+ uint8_t addr);
+typedef void (*twl4030_write_func)(TWL4030NodeState *s,
+ uint8_t addr, uint8_t value);
-struct twl4030_i2c_s {
+struct TWL4030NodeState {
i2c_slave i2c;
int firstbyte;
uint8_t reg;
- qemu_irq irq;
+
+ twl4030_read_func read_func;
+ twl4030_write_func write_func;
+ TWL4030State *twl4030;
+
uint8 reg_data[256];
- struct twl4030_s *twl4030;
};
-struct twl4030_s {
- struct twl4030_i2c_s *i2c[5];
-
+struct TWL4030State {
+ qemu_irq irq;
+
int key_cfg;
int key_tst;
+ TWL4030NodeState *i2c[5];
+
uint8_t seq_mem[64][4]; /* power-management sequencing memory */
};
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10...0x17 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0x18...0x1f */
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, /* 0x20...0x27 */
- 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28...0x2f */
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x60, 0x00, /* 0x28...0x2f */
0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0xbf, 0xbf, /* 0x30...0x37 */
0xbf, 0xab, 0x00, 0x08, 0x3f, 0x15, 0x40, 0x0e, /* 0x38...0x3f */
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40...0x47 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xf8...0xff */
};
-static uint8_t twl4030_48_read(void *opaque, uint8_t addr)
+static uint8_t twl4030_48_read(TWL4030NodeState *s, uint8_t addr)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
-
+ TRACE("addr=0x%02x", addr);
switch (addr) {
case 0x00: /* VENDOR_ID_LO */
case 0x01: /* VENDOR_ID_HI */
case 0xfd: /* PHY_PWR_CTRL */
case 0xfe: /* PHY_CLK_CTRL */
return s->reg_data[addr];
- case 0xff: /* PHY_CLK_CTRL */
- return s->reg_data[0xfe] & 0x1;
+ case 0xff: /* PHY_CLK_CTRL_STS */
+ if (s->reg_data[0xfd] & 1) /* PHY_PWR_CTRL */
+ return 0;
+ if (s->reg_data[0xfe] & 1) /* REQ_PHY_DPLL_CLK */
+ return 1;
+ return (s->reg_data[0x04] >> 6) & 1; /* SUSPENDM */
default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x pc %x \n", __FUNCTION__, addr,cpu_single_env->regs[15] );
- //printf("%s: unknown register %02x \n", __FUNCTION__, addr);
-#endif
- exit(-1);
+ fprintf(stderr, "%s: unknown register 0x%02x\n",
+ __FUNCTION__, addr);
break;
}
+ return 0;
}
-static void twl4030_48_write(void *opaque, uint8_t addr, uint8_t value)
+static void twl4030_48_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
-
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
switch (addr) {
- case 0x04: /* IFC_CTRL */
- s->reg_data[0x04] = value & 0x80;
+ case 0x04: /* FUNC_CTRL */
+ s->reg_data[0x04] = value & 0x7f;
break;
- case 0x05: /* IFC_CRTL_SET */
- s->reg_data[0x04] = (s->reg_data[0x04] | value) & 0x80;
+ case 0x05: /* FUNC_CRTL_SET */
+ s->reg_data[0x04] = (s->reg_data[0x04] | value) & 0x7f;
break;
- case 0x06: /* IFC_CRTL_CLEAR */
- s->reg_data[0x04] = (s->reg_data[0x04] & ~value) & 0x80;
+ case 0x06: /* FUNC_CTRL_CLEAR */
+ s->reg_data[0x04] = (s->reg_data[0x04] & ~value) & 0x7f;
break;
case 0x07: /* IFC_CTRL */
- s->reg_data[0x07] = value & 0x61;
+ s->reg_data[0x07] = value & 0x9e;
break;
case 0x08: /* IFC_CRTL_SET */
- s->reg_data[0x07] = (s->reg_data[0x07] | value) & 0x61;
+ s->reg_data[0x07] = (s->reg_data[0x07] | value) & 0x9e;
break;
case 0x09: /* IFC_CRTL_CLEAR */
- s->reg_data[0x07] = (s->reg_data[0x07] & ~value) & 0x61;
+ s->reg_data[0x07] = (s->reg_data[0x07] & ~value) & 0x9e;
+ break;
+ case 0xa1: /* CARKIT_SM_CTRL */
+ s->reg_data[0xa1] = value & 0x3f;
+ break;
+ case 0xa2: /* CARKIT_SM_CTRL_SET */
+ s->reg_data[0xa1] = (s->reg_data[0xa1] | value) & 0x3f;
+ break;
+ case 0xa3: /* CARKIT_SM_CTRL_CLR */
+ s->reg_data[0xa1] = (s->reg_data[0xa1] & ~value) & 0x3f;
break;
case 0xac: /* POWER_CTRL */
s->reg_data[0xac] = value & 0x20;
break;
case 0xad: /* POWER_SET */
- s->reg_data[0xac] = (s->reg_data[0xac] | value) & 0x20;
+ s->reg_data[0xac] = (s->reg_data[0xac] | value) & 0x20;
break;
case 0xae: /* POWER_CLEAR */
- s->reg_data[0xac] = (s->reg_data[0xac] & ~value) & 0x20;
+ s->reg_data[0xac] = (s->reg_data[0xac] & ~value) & 0x20;
+ break;
+ case 0xbb: /* CARKIT_ANA_CTRL */
+ s->reg_data[0xbb] = value;
+ break;
+ case 0xbc: /* CARKIT_ANA_CTRL_SET */
+ s->reg_data[0xbb] |= value;
+ break;
+ case 0xbd: /* CARKIT_ANA_CTRL_CLR */
+ s->reg_data[0xbb] &= ~value;
break;
case 0xfd: /* PHY_PWR_CTRL */
s->reg_data[addr] = value & 0x1;
s->reg_data[addr] = value & 0x7;
break;
default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x pc %x \n", __FUNCTION__, addr,cpu_single_env->regs[15] );
- //printf("%s: unknown register %02x \n", __FUNCTION__, addr);
-#endif
- exit(-1);
+ fprintf(stderr, "%s: unknown register 0x%02x\n",
+ __FUNCTION__, addr);
break;
}
}
-static int twl4030_48_tx(i2c_slave *i2c, uint8_t data)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- /* Interpret register address byte */
- if (s->firstbyte) {
- s->reg = data;
- s->firstbyte = 0;
- } else
- twl4030_48_write(s, s->reg++, data);
-
- return 0;
-}
-
-static int twl4030_48_rx(i2c_slave *i2c)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- return twl4030_48_read(s, s->reg++);
-}
-
-static void twl4030_48_reset(i2c_slave *i2c)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- s->reg = 0x00;
- memcpy(s->reg_data, addr_48_reset_values, 256);
-}
-
-static void twl4030_48_event(i2c_slave *i2c, enum i2c_event event)
+static uint8_t twl4030_49_read(TWL4030NodeState *s, uint8_t addr)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- if (event == I2C_START_SEND)
- s->firstbyte = 1;
-}
-
-static uint8_t twl4030_49_read(void *opaque, uint8_t addr)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
-
+ TRACE("addr=0x%02x", addr);
switch (addr) {
- case 0x98: /* GPIO_DATAIN1 */
- case 0x99: /* GPIO_DATAIN2 */
- case 0x9a: /* GPIO_DATAIN3 */
- case 0x9b: /* GPIO_DATADIR1 */
- case 0x9c: /* GPIO_DATADIR2 */
- case 0x9d: /* GPIO_DATADIR3 */
- case 0xb1: /* GPIO_ISR1A */
- case 0xb2: /* GPIO_ISR2A */
- case 0xb3: /* GPIO_ISR3A */
- case 0xc0: /* GPIO_EDR1 */
- case 0xc1: /* GPIO_EDR2 */
- case 0xc2: /* GPIO_EDR3 */
- case 0xc3: /* GPIO_EDR4 */
- case 0xc4: /* GPIO_EDR5 */
+ /* AUDIO_VOICE region */
+ case 0x01 ... 0x49:
+ return s->reg_data[addr];
+ /* Test region */
+ case 0x4c ... 0x60:
+ return s->reg_data[addr];
+ /* PIH region */
+ case 0x81: /* PIH_ISR_P1 */
+ case 0x82: /* PIH_ISR_P2 */
+ case 0x83: /* PIH_SIR */
+ return s->reg_data[addr];
+ /* INTBR region */
+ case 0x85 ... 0x97:
+ return s->reg_data[addr];
+ /* GPIO region */
+ case 0x98 ... 0xc5:
return s->reg_data[addr];
default:
-#ifdef VERBOSE
- fprintf(stderr, "%s: unknown register %02x pc %x\n",
- __FUNCTION__, addr,cpu_single_env->regs[15]);
-#endif
- exit(-1);
+ fprintf(stderr, "%s: unknown register 0x%02x\n",
+ __FUNCTION__, addr);
+ break;
}
+ return 0;
}
-static void twl4030_49_write(void *opaque, uint8_t addr, uint8_t value)
+static void twl4030_49_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
-
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
switch (addr) {
- case 0x9b: /* GPIODATADIR1 */
- case 0x9c: /* GPIODATADIR2 */
- case 0x9d: /* GPIODATADIR3 */
- case 0x9e: /* GPIODATAOUT1 */
- case 0x9f: /* GPIODATAOUT2 */
- case 0xa0: /* GPIODATAOUT3 */
- case 0xa1: /* CLEARGPIODATAOUT1 */
- case 0xa2: /* CLEARGPIODATAOUT2 */
- case 0xa3: /* CLEARGPIODATAOUT3 */
- case 0xa4: /* SETGPIODATAOUT1 */
- case 0xa5: /* SETGPIODATAOUT2 */
- case 0xa6: /* SETGPIODATAOUT3 */
- case 0xa7: /* GPIO_DEBEN1 */
- case 0xa8: /* GPIO_DEBEN2 */
- case 0xa9: /* GPIO_DEBEN3 */
- case 0xaa: /* GPIO_CTRL */
- case 0xab: /* GPIOPUPDCTR1 */
- case 0xac: /* GPIOPUPDCTR2 */
- case 0xad: /* GPIOPUPDCTR3 */
- case 0xae: /* GPIOPUPDCTR4 */
+ /* AUDIO_VOICE region */
+ case 0x01 ... 0x49:
+ s->reg_data[addr] = value;
+ break;
+ /* Test region */
+ case 0x4c ... 0x59:
+ s->reg_data[addr] = value;
+ break;
+ case 0x5a ... 0x60:
+ /* read-only, ignore */
+ break;
+ /* PIH region */
+ case 0x81: /* PIH_ISR_P1 */
+ case 0x82: /* PIH_ISR_P2 */
+ case 0x83: /* PIH_SIR */
+ s->reg_data[addr] = value;
+ break;
+ /* INTBR region */
+ case 0x85 ... 0x90:
+ /* read-only, ignore */
+ break;
+ case 0x91 ... 0x97:
+ s->reg_data[addr] = value;
+ break;
+ /* GPIO region */
+ case 0x98 ... 0x9a:
+ /* read-only, ignore */
+ break;
+ case 0x9b ... 0xae:
s->reg_data[addr] = value;
break;
case 0xaf: /* GPIOPUPDCTR5 */
s->reg_data[addr] = value & 0x0f;
break;
- case 0xb4: /* GPIO_IMR1A */
- case 0xb5: /* GPIO_IMR2A */
+ case 0xb0 ... 0xb5:
s->reg_data[addr] = value;
break;
case 0xb6: /* GPIO_IMR3A */
s->reg_data[addr] = value & 0x03;
break;
- case 0xc0: /* GPIO_EDR1 */
- case 0xc1: /* GPIO_EDR2 */
- case 0xc2: /* GPIO_EDR3 */
- case 0xc3: /* GPIO_EDR4 */
- case 0xc4: /* GPIO_EDR5 */
+ case 0xb7 ... 0xc4:
s->reg_data[addr] = value;
break;
case 0xc5: /* GPIO_SIH_CTRL */
s->reg_data[addr] = value & 0x07;
break;
default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x pc %x \n", __FUNCTION__, addr,
- cpu_single_env->regs[15]);
- //printf("%s: unknown register %02x \n", __FUNCTION__, addr);
-#endif
- exit(-1);
+ fprintf(stderr, "%s: unknown register 0x%02x\n",
+ __FUNCTION__, addr);
break;
}
}
-
-static int twl4030_49_tx(i2c_slave *i2c, uint8_t data)
+static uint8_t twl4030_4a_read(TWL4030NodeState *s, uint8_t addr)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- /* Interpret register address byte */
- if (s->firstbyte) {
- s->reg = data;
- s->firstbyte = 0;
- } else
- twl4030_49_write(s, s->reg++, data);
-
- return 0;
-}
-
-static int twl4030_49_rx(i2c_slave *i2c)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- return twl4030_49_read(s, s->reg++);
-}
-
-static void twl4030_49_reset(i2c_slave *i2c)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- s->reg = 0x00;
- memcpy(s->reg_data, addr_49_reset_values, 256);
-}
-
-static void twl4030_49_event(i2c_slave *i2c, enum i2c_event event)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- if (event == I2C_START_SEND)
- s->firstbyte = 1;
-}
-
-static uint8_t twl4030_4a_read(void *opaque, uint8_t addr)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
-
+ TRACE("addr=0x%02x", addr);
switch (addr) {
- case 0x61: /* MADC_ISR1 */
- case 0xb9: /* BCIISR1A */
- case 0xba: /* BCIISR2A */
- case 0xe3: /* KEYP_ISR1 */
+ /* MADC region */
+ case 0x00 ... 0x13:
+ case 0x61 ... 0x67:
+ return s->reg_data[addr];
+ case 0x17 ... 0x36: /* RT conversion registers */
+ case 0x37 ... 0x56: /* GP conversion registers */
+ case 0x57 ... 0x60: /* BCI conversion registers */
+ return (addr & 1) ? 0 : 0x60;
+ /* MAIN_CHARGE region */
+ case 0x74 ... 0xa6:
+ return s->reg_data[addr];
+ /* Interrupt region */
+ case 0xb9 ... 0xc6:
+ return s->reg_data[addr];
+ /* KEYPAD region */
+ case 0xd2 ... 0xe9:
+ return s->reg_data[addr];
+ /* LED region */
case 0xee: /* LEDEN */
return s->reg_data[addr];
+ /* PWMA region */
+ case 0xef: /* PWMAON */
+ case 0xf0: /* PWMAOFF */
+ return s->reg_data[addr];
+ /* PWMB region */
+ case 0xf1: /* PWMBON */
+ case 0xf2: /* PWMBOFF */
+ return s->reg_data[addr];
+ /* PWM0 region */
+ case 0xf8: /* PWM0ON */
+ case 0xf9: /* PWM0OFF */
+ return s->reg_data[addr];
+ /* PWM1 region */
+ case 0xfb: /* PWM1ON */
+ case 0xfc: /* PWM1OFF */
+ return s->reg_data[addr];
default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x pc %x \n", __FUNCTION__, addr,cpu_single_env->regs[15] );
-#endif
- exit(-1);
+ fprintf(stderr, "%s: unknown register 0x%02x\n",
+ __FUNCTION__, addr);
break;
}
+ return 0;
}
-static void twl4030_4a_write(void *opaque, uint8_t addr, uint8_t value)
+static void twl4030_4a_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
-
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
switch (addr) {
+ case 0x00: /* CTRL1 */
+ s->reg_data[addr] = value;
+ break;
+ case 0x06: /* SW1SELECT_LSB */
+ case 0x07: /* SW1SELECT_MSB */
+ case 0x08: /* SW1AVERAGE_LSB */
+ case 0x09: /* SW1AVERAGE_MSB */
+ s->reg_data[addr] = value;
+ break;
+ case 0x12: /* CTRL_SW1 */
+ s->reg_data[addr] = 0xde;
+ break;
case 0x61: /* MADC_ISR1 */
- s->reg_data[value] &= ~(value & 0x0f);
+ s->reg_data[addr] &= ~(value & 0x0f);
break;
case 0x62: /* MADC_IMR1 */
- s->reg_data[value] = value & 0x0f;
+ s->reg_data[addr] = value & 0x0f;
+ break;
+ case 0x97: /* BCICTL1 */
+ s->reg_data[addr] = value;
break;
case 0xb9: /* BCIISR1A */
- s->reg_data[value] &= ~value;
+ s->reg_data[addr] &= ~value;
break;
case 0xba: /* BCIISR2A */
- s->reg_data[value] &= ~(value & 0x0f);
+ s->reg_data[addr] &= ~(value & 0x0f);
break;
case 0xbb: /* BCIIMR1A */
s->reg_data[addr] = value;
case 0xbc: /* BCIIMR2A */
s->reg_data[addr] = value & 0x0f;
break;
+ case 0xd2: /* KEYP_CTRL_REG */
+ s->reg_data[addr] = value & 0x7f;
+ break;
case 0xe4: /* KEYP_IMR1 */
s->reg_data[addr] = value & 0x0f;
break;
break;
case 0xee: /* LEDEN */
s->reg_data[addr] = value;
-#ifdef VERBOSE
- fprintf(stderr, "%s: LEDA power=%s/enable=%s, LEDB power=%s/enable=%s\n", __FUNCTION__,
+ TRACE("LEDA power=%s/enable=%s, LEDB power=%s/enable=%s",
value & 0x10 ? "on" : "off", value & 0x01 ? "yes" : "no",
value & 0x20 ? "on" : "off", value & 0x02 ? "yes" : "no");
-#endif
break;
case 0xef: /* PWMAON */
+ case 0xf8: /* PWM0ON */
+ case 0xfb: /* PWM1ON */
s->reg_data[addr] = value;
break;
case 0xf0: /* PWMAOFF */
+ case 0xf9: /* PWM0OFF */
+ case 0xfc: /* PWM1OFF */
s->reg_data[addr] = value & 0x7f;
break;
default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x pc %x \n", __FUNCTION__, addr,cpu_single_env->regs[15] );
-#endif
- exit(-1);
+ fprintf(stderr, "%s: unknown register 0x%02x\n",
+ __FUNCTION__, addr);
break;
}
}
-static int twl4030_4a_tx(i2c_slave *i2c, uint8_t data)
+static uint8_t twl4030_4b_read(TWL4030NodeState *s, uint8_t addr)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- /* Interpret register address byte */
- if (s->firstbyte) {
- s->reg = data;
- s->firstbyte = 0;
- } else
- twl4030_4a_write(s, s->reg++, data);
-
- return 0;
-}
-
-static int twl4030_4a_rx(i2c_slave *i2c)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- return twl4030_4a_read(s, s->reg++);
-}
-
-static void twl4030_4a_reset(i2c_slave *i2c)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- s->reg = 0x00;
- memcpy(s->reg_data, addr_4a_reset_values, 256);
-}
-
-static void twl4030_4a_event(i2c_slave *i2c, enum i2c_event event)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- if (event == I2C_START_SEND)
- s->firstbyte = 1;
-}
-
-static uint8_t twl4030_4b_read(void *opaque, uint8_t addr)
-{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
-
+ TRACE("addr=0x%02x", addr);
switch (addr) {
- case 0x1c: /* RTC */
- case 0x1d:
- case 0x1e:
- case 0x1f:
- case 0x20:
- case 0x21:
- case 0x22:
- case 0x23:
- case 0x24:
- case 0x25:
- case 0x26:
- case 0x27:
- case 0x28:
- case 0x29:
- case 0x2a:
- case 0x2b:
- case 0x2c:
- case 0x2d: /*RTC end */
- case 0x2e: /* PWR_ISR1 */
- case 0x33: /* PWR_EDR1 */
- case 0x34: /* PWR_EDR2 */
- case 0x45: /* STS_HW_CONDITIONS */
+ /* SECURED_REG region */
+ case 0x00 ... 0x13:
+ return s->reg_data[addr];
+ /* BACKUP_REG region */
+ case 0x14 ... 0x1b:
+ return s->reg_data[addr];
+ /* RTC region */
+ case 0x1c ... 0x2d:
+ return s->reg_data[addr];
+ /* INT region */
+ case 0x2e ... 0x35:
+ return s->reg_data[addr];
+ /* PM_MASTER region */
+ case 0x36 ... 0x44:
+ return s->reg_data[addr];
+ case 0x45: /* STS_HW_CONDITIONS - USB plugged, no VBUS -> host usb */
+ return 0x4;
+ case 0x46 ... 0x5a:
+ return s->reg_data[addr];
+ /* PM_RECEIVER region */
+ case 0x5b ... 0xf1:
return s->reg_data[addr];
default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x pc %x \n", __FUNCTION__, addr,cpu_single_env->regs[15] );
- //printf("%s: unknown register %02x \n", __FUNCTION__, addr);
-#endif
- exit(-1);
+ fprintf(stderr, "%s: unknown register 0x%02x\n",
+ __FUNCTION__, addr);
break;
}
+ return 0;
}
-static void twl4030_4b_write(void *opaque, uint8_t addr, uint8_t value)
+static void twl4030_4b_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) opaque;
uint8_t seq_addr, seq_sub;
-
+
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
switch (addr) {
- case 0x1c: /* RTC */
- case 0x1d:
- case 0x1e:
- case 0x1f:
- case 0x20:
- case 0x21:
- case 0x22:
- case 0x23:
- case 0x24:
- case 0x25:
- case 0x26:
- case 0x27:
- case 0x28:
+ case 0x1c: /* SECONDS_REG */
+ case 0x1d: /* MINUTES_REG */
+ case 0x23: /* ALARM_SECONDS_REG */
+ case 0x24: /* ALARM_MINUTES_REG */
+ s->reg_data[addr] = value & 0x7f;
+ break;
+ case 0x1e: /* HOURS_REG */
+ case 0x25: /* ALARM_HOURS_REG */
+ s->reg_data[addr] = value & 0xbf;
+ break;
+ case 0x1f: /* DAYS_REG */
+ case 0x26: /* ALARM_DAYS_REG */
+ s->reg_data[addr] = value & 0x3f;
+ break;
+ case 0x20: /* MONTHS_REG */
+ case 0x27: /* ALARM_MONTHS_REG */
+ s->reg_data[addr] = value & 0x1f;
+ break;
+ case 0x21: /* YEARS_REG */
+ case 0x28: /* ALARM_YEARS_REG */
+ s->reg_data[addr] = value;
+ break;
+ case 0x22: /* WEEKS_REG */
+ s->reg_data[addr] = value & 0x07;
+ break;
case 0x29: /* RTC_CTRL_REG */
+ s->reg_data[addr] = value & 0x7f;
+ break;
case 0x2a: /* RTC_STATUS_REG */
+ s->reg_data[addr] = value & 0xfe;
+ break;
case 0x2b: /* RTC_INTERRUPTS_REG */
+ s->reg_data[addr] = value & 0x0f;
+ break;
+ case 0x2c: /* RTC_COMP_LSB_REG */
+ case 0x2d: /* RTC_COMP_MSB_REG */
+ s->reg_data[addr] = value;
+ break;
+ case 0x2e: /* PWR_ISR1 */
+ case 0x2f: /* PWR_IMR1 */
+ s->reg_data[addr] = value;
+ break;
case 0x33: /* PWR_EDR1 */
case 0x34: /* PWR_EDR2 */
s->reg_data[addr] = value;
break;
+ case 0x35: /* PWR_SIH_CTRL */
+ s->reg_data[addr] = value & 0x07;
+ break;
+ case 0x3b: /* CFG_BOOT */
+ if (s->twl4030->key_cfg)
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x8f);
+ break;
+ case 0x44: /* PROTECT_KEY */
+ s->twl4030->key_cfg = 0;
+ s->twl4030->key_tst = 0;
+ switch (value) {
+ case 0x0C:
+ if (s->reg_data[addr] == 0xC0)
+ s->twl4030->key_cfg = 1;
+ break;
+ case 0xE0:
+ if (s->reg_data[addr] == 0x0E)
+ s->twl4030->key_tst = 1;
+ break;
+ case 0xEC:
+ if (s->reg_data[addr] == 0xCE) {
+ s->twl4030->key_cfg = 1;
+ s->twl4030->key_tst = 1;
+ }
+ break;
+ default:
+ break;
+ }
+ s->reg_data[addr] = value;
+ break;
case 0x46: /* P1_SW_EVENTS */
case 0x47: /* P2_SW_EVENTS */
case 0x48: /* P3_SW_EVENTS */
s->reg_data[addr] = value & 0x78;
break;
+ case 0x4a: /* PB_CFG */
+ s->reg_data[addr] = value & 0xf;
+ case 0x4b: /* PB_MSB */
+ case 0x4c: /* PB_LSB */
+ s->reg_data[addr] = value;
case 0x52: /* SEQ_ADD_W2P */
case 0x53: /* SEQ_ADD_P2A */
case 0x54: /* SEQ_ADD_A2W */
seq_addr = s->reg_data[0x59];
seq_sub = seq_addr & 3;
seq_addr >>= 2;
- if ((seq_addr >= 0x2b && seq_addr <= 0x3e) || (seq_addr <= 0x0e && seq_sub == 3))
+ if ((seq_addr >= 0x2b && seq_addr <= 0x3e) ||
+ (seq_addr <= 0x0e && seq_sub == 3))
s->twl4030->seq_mem[seq_addr][seq_sub] = value;
}
- s->reg_data[0x59]++; /* TODO: check if autoincrement is write-protected as well */
+ /* TODO: check if autoincrement is write-protected as well */
+ s->reg_data[0x59]++;
break;
+ case 0x5e: /* WATCHDOG_CFG */
+ s->reg_data[addr] = value & 0x1f;
+ case 0x68: /* MISC_CFG */
+ s->reg_data[addr] = value;
+ break;
+ case 0x76: /* VAUX1_DEV_GRP */
case 0x7a: /* VAUX3_DEV_GRP */
case 0x82: /* VMMC1_DEV_GRP */
+ case 0x86: /* VMMC2_DEV_GRP */
case 0x8e: /* VPLL2_DEV_GRP */
+ case 0x92: /* VSIM_DEV_GRP */
case 0x96: /* VDAC_DEV_GRP */
case 0xcc: /* VUSB1V5_DEV_GRP */
case 0xcf: /* VUSB1V8_DEV_GRP */
case 0xe6: /* HFCLKOUT_DEV_GRP */
s->reg_data[addr] = (s->reg_data[addr] & 0x0f) | (value & 0xf0);
break;
- case 0x2f: /* PWR_IMR1 */
- s->reg_data[addr] = value;
- break;
- case 0x35: /* PWR_SIH_CTRL */
- s->reg_data[addr] = value & 0x07;
- break;
- case 0x3b: /* CFG_BOOT */
- if (s->twl4030->key_cfg)
- s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x8f);
- break;
- case 0x44: /* PROTECT_KEY */
- s->twl4030->key_cfg = 0;
- s->twl4030->key_tst = 0;
- switch (value) {
- case 0x0C:
- if (s->reg_data[addr] == 0xC0)
- s->twl4030->key_cfg = 1;
- break;
- case 0xE0:
- if (s->reg_data[addr] == 0x0E)
- s->twl4030->key_tst = 1;
- break;
- case 0xEC:
- if (s->reg_data[addr] == 0xCE) {
- s->twl4030->key_cfg = 1;
- s->twl4030->key_tst = 1;
- }
- break;
- default:
- break;
- }
- s->reg_data[addr] = value;
- break;
+ case 0x75: /* VAUX1_DEDICATED */
case 0x7d: /* VAUX3_DEDICATED */
+ case 0x95: /* VSIM_DEDICATED */
if (s->twl4030->key_tst)
s->reg_data[addr] = value & 0x77;
else
s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x07);
break;
+ case 0x79: /* VAUX2_DEDICATED */
+ case 0x81: /* VAUX4_DEDICATED */
+ case 0x91: /* VPLL2_DEDICATED */
+ if (s->twl4030->key_tst)
+ s->reg_data[addr] = value & 0x7f;
+ else
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x0f);
+ break;
case 0x85: /* VMMC1_DEDICATED */
case 0x99: /* VDAC_DEDICATED */
if (s->twl4030->key_tst)
else
s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x03);
break;
- case 0x91: /* VPLL2_DEDICATED */
- if (s->twl4030->key_tst)
- s->reg_data[addr] = value & 0x7f;
- else
- s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x0f);
+ case 0x74: /* VAUX1_REMAP */
+ case 0x78: /* VAUX2_REMAP */
+ case 0x7c: /* VAUX3_REMAP */
+ case 0x80: /* VAUX4_REMAP */
+ case 0x90: /* VPLL2_REMAP */
+ s->reg_data[addr] = value;
break;
case 0xcd: /* VUSB1V5_TYPE */
case 0xd0: /* VUSB1V8_TYPE */
break;
default:
-#ifdef VERBOSE
- fprintf(stderr, "%s: unknown register %02x value %0x pc %x \n", __FUNCTION__,
- addr, value, cpu_single_env->regs[15]);
-#endif
- exit(-1);
+ fprintf(stderr,
+ "%s: unknown register 0x%02x value 0x%02x\n",
+ __FUNCTION__, addr, value);
break;
}
}
-static int twl4030_4b_tx(i2c_slave *i2c, uint8_t data)
+static void twl4030_node_init(TWL4030NodeState *s,
+ twl4030_read_func read,
+ twl4030_write_func write,
+ const uint8_t *reset_values)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- /* Interpret register address byte */
- if (s->firstbyte) {
- s->reg = data;
- s->firstbyte = 0;
- } else
- twl4030_4b_write(s, s->reg++, data);
-
- return 1;
+ s->read_func = read;
+ s->write_func = write;
+ s->reg = 0x00;
+ memcpy(s->reg_data, reset_values, 256);
}
-static int twl4030_4b_rx(i2c_slave *i2c)
+static void twl4030_48_init(i2c_slave *i2c)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- return twl4030_4b_read(s, s->reg++);
+ twl4030_node_init(FROM_I2C_SLAVE(TWL4030NodeState, i2c),
+ twl4030_48_read, twl4030_48_write,
+ addr_48_reset_values);
}
-static void twl4030_4b_reset(i2c_slave *i2c)
+static void twl4030_49_init(i2c_slave *i2c)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
- s->reg = 0x00;
- memcpy(s->reg_data, addr_4b_reset_values, 256);
- s->twl4030->key_cfg = 0;
- s->twl4030->key_tst = 0;
+ twl4030_node_init(FROM_I2C_SLAVE(TWL4030NodeState, i2c),
+ twl4030_49_read, twl4030_49_write,
+ addr_49_reset_values);
}
-static void twl4030_4b_event(i2c_slave *i2c, enum i2c_event event)
+static void twl4030_4a_init(i2c_slave *i2c)
{
- struct twl4030_i2c_s *s = (struct twl4030_i2c_s *) i2c;
-
- if (event == I2C_START_SEND)
- s->firstbyte = 1;
+ twl4030_node_init(FROM_I2C_SLAVE(TWL4030NodeState, i2c),
+ twl4030_4a_read, twl4030_4a_write,
+ addr_4a_reset_values);
}
-struct twl4030_s *twl4030_init(i2c_bus *bus, qemu_irq irq)
+static void twl4030_4b_init(i2c_slave *i2c)
{
- int i;
-
- struct twl4030_s *s = (struct twl4030_s *) qemu_mallocz(sizeof(*s));
-
- if (!s)
- {
- fprintf(stderr,"can not alloc memory space for twl4030_s \n");
- exit(-1);
- }
- for (i=0;i<5;i++)
- {
- s->i2c[i]=(struct twl4030_i2c_s *)i2c_slave_init(bus, 0, sizeof(struct twl4030_i2c_s));
- s->i2c[i]->irq = irq;
- s->i2c[i]->twl4030 = s;
- }
- s->i2c[0]->i2c.event = twl4030_48_event;
- s->i2c[0]->i2c.recv = twl4030_48_rx;
- s->i2c[0]->i2c.send = twl4030_48_tx;
- twl4030_48_reset(&s->i2c[0]->i2c);
- i2c_set_slave_address((i2c_slave *)&s->i2c[0]->i2c,0x48);
-
- s->i2c[1]->i2c.event = twl4030_49_event;
- s->i2c[1]->i2c.recv = twl4030_49_rx;
- s->i2c[1]->i2c.send = twl4030_49_tx;
- twl4030_49_reset(&s->i2c[1]->i2c);
- i2c_set_slave_address((i2c_slave *)&s->i2c[1]->i2c,0x49);
-
- s->i2c[2]->i2c.event = twl4030_4a_event;
- s->i2c[2]->i2c.recv = twl4030_4a_rx;
- s->i2c[2]->i2c.send = twl4030_4a_tx;
- twl4030_4a_reset(&s->i2c[2]->i2c);
- i2c_set_slave_address((i2c_slave *)&s->i2c[2]->i2c,0x4a);
-
- s->i2c[3]->i2c.event = twl4030_4b_event;
- s->i2c[3]->i2c.recv = twl4030_4b_rx;
- s->i2c[3]->i2c.send = twl4030_4b_tx;
- twl4030_4b_reset(&s->i2c[3]->i2c);
- i2c_set_slave_address((i2c_slave *)&s->i2c[3]->i2c,0x4b);
- /*TODO:other group*/
-
-
- //register_savevm("menelaus", -1, 0, menelaus_save, menelaus_load, s);
- return s;
+ twl4030_node_init(FROM_I2C_SLAVE(TWL4030NodeState, i2c),
+ twl4030_4b_read, twl4030_4b_write,
+ addr_4b_reset_values);
}
-#if 0
-static uint8_t twl4030_read(void *opaque, uint8_t addr)
+static void twl4030_event(i2c_slave *i2c, enum i2c_event event)
{
-// struct twl4030_s *s = (struct twl4030_s *) opaque;
-// int reg = 0;
-
- printf("twl4030_read addr %x\n",addr);
-
- switch (addr)
- {
- default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x\n", __FUNCTION__, addr);
-#endif
- //exit(-1);
- break;
+ if (event == I2C_START_SEND) {
+ TWL4030NodeState *s = FROM_I2C_SLAVE(TWL4030NodeState, i2c);
+ s->firstbyte = 1;
}
- return 0x00;
}
-static void twl4030_write(void *opaque, uint8_t addr, uint8_t value)
+static int twl4030_rx(i2c_slave *i2c)
{
-// struct twl4030_s *s = (struct twl4030_s *) opaque;
-// int line;
-// int reg = 0;
-// struct tm tm;
-
- printf("twl4030_write addr %x value %x \n",addr,value);
-
- switch (addr)
- {
- case 0x82:
- case 0x85:
- /*mmc*/
- break;
- default:
-#ifdef VERBOSE
- printf("%s: unknown register %02x\n", __FUNCTION__, addr);
-#endif
- //exit(-1);
- break;
- }
+ TWL4030NodeState *s = FROM_I2C_SLAVE(TWL4030NodeState, i2c);
+ return s->read_func(s, s->reg++);
}
-
static int twl4030_tx(i2c_slave *i2c, uint8_t data)
{
- struct twl4030_s *s = (struct twl4030_s *) i2c;
- /* Interpret register address byte */
+ TWL4030NodeState *s = FROM_I2C_SLAVE(TWL4030NodeState, i2c);
if (s->firstbyte) {
s->reg = data;
s->firstbyte = 0;
- } else
- twl4030_write(s, s->reg ++, data);
-
- return 0;
-}
-
-static int twl4030_rx(i2c_slave *i2c)
-{
- struct twl4030_s *s = (struct twl4030_s *) i2c;
-
- return twl4030_read(s, s->reg ++);
+ } else {
+ s->write_func(s, s->reg++, data);
+ }
+ return 1;
}
-static void twl4030_reset(i2c_slave *i2c)
+static void twl4030_save_state(QEMUFile *f, void *opaque)
{
- struct twl4030_s *s = (struct twl4030_s *) i2c;
- s->reg = 0x00;
+ TWL4030State *s = (TWL4030State *)opaque;
+ int i;
+
+ qemu_put_sbe32(f, s->key_cfg);
+ qemu_put_sbe32(f, s->key_tst);
+ for (i = 0; i < 64; i++)
+ qemu_put_buffer(f, s->seq_mem[i], 4);
+ for (i = 0; i < 5; i++) {
+ qemu_put_sbe32(f, s->i2c[i]->firstbyte);
+ qemu_put_byte(f, s->i2c[i]->reg);
+ qemu_put_buffer(f, s->i2c[i]->reg_data, sizeof(s->i2c[i]->reg_data));
+ }
}
-static void twl4030_event(i2c_slave *i2c, enum i2c_event event)
+static int twl4030_load_state(QEMUFile *f, void *opaque, int version_id)
{
- struct twl4030_s *s = (struct twl4030_s *) i2c;
-
- if (event == I2C_START_SEND)
- s->firstbyte = 1;
+ TWL4030State *s = (TWL4030State *)opaque;
+ int i;
+
+ if (version_id)
+ return -EINVAL;
+
+ s->key_cfg = qemu_get_sbe32(f);
+ s->key_tst = qemu_get_sbe32(f);
+ for (i = 0; i < 64; i++)
+ qemu_get_buffer(f, s->seq_mem[i], 4);
+ for (i = 0; i < 5; i++) {
+ s->i2c[i]->firstbyte = qemu_get_sbe32(f);
+ s->i2c[i]->reg = qemu_get_byte(f);
+ qemu_get_buffer(f, s->i2c[i]->reg_data, sizeof(s->i2c[i]->reg_data));
+ }
+
+ return 0;
}
-i2c_slave *twl4030_init(i2c_bus *bus, qemu_irq irq)
+void *twl4030_init(i2c_bus *gp_bus, qemu_irq irq)
{
- struct twl4030_s *s = (struct twl4030_s *)
- i2c_slave_init(bus, 0, sizeof(struct twl4030_s));
-
- s->i2c.event = twl4030_event;
- s->i2c.recv = twl4030_rx;
- s->i2c.send = twl4030_tx;
-
+ TWL4030State *s = (TWL4030State *)qemu_mallocz(sizeof(*s));
+
s->irq = irq;
- //s->rtc.hz_tm = qemu_new_timer(rt_clock, menelaus_rtc_hz, s);
- //s->in = qemu_allocate_irqs(menelaus_gpio_set, s, 3);
- //s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0];
+ s->key_cfg = 0;
+ s->key_tst = 0;
+
+ int i;
+ for (i = 0; i < 4; i++) {
+ char name[16];
+ sprintf(name, "twl4030_id%d", i + 1);
+ DeviceState *ds = i2c_create_slave(gp_bus, name, 0x48 + i);
+ s->i2c[i] = FROM_I2C_SLAVE(TWL4030NodeState, I2C_SLAVE_FROM_QDEV(ds));
+ s->i2c[i]->twl4030 = s;
+ }
- twl4030_reset(&s->i2c);
+ register_savevm("twl4030", -1, 0,
+ twl4030_save_state, twl4030_load_state, s);
- //register_savevm("menelaus", -1, 0, menelaus_save, menelaus_load, s);
+ return s;
+}
- return &s->i2c;
+static I2CSlaveInfo twl4030_info[4] = {
+ {
+ .init = twl4030_48_init,
+ .event = twl4030_event,
+ .recv = twl4030_rx,
+ .send = twl4030_tx
+ },
+ {
+ .init = twl4030_49_init,
+ .event = twl4030_event,
+ .recv = twl4030_rx,
+ .send = twl4030_tx
+ },
+ {
+ .init = twl4030_4a_init,
+ .event = twl4030_event,
+ .recv = twl4030_rx,
+ .send = twl4030_tx
+ },
+ {
+ .init = twl4030_4b_init,
+ .event = twl4030_event,
+ .recv = twl4030_rx,
+ .send = twl4030_tx
+ },
+};
+
+static void twl4030_register_devices(void)
+{
+ I2CSlaveInfo *p = twl4030_info;
+ int i;
+ for (i = 0; i < 4; p++, i++) {
+ char name[16];
+ sprintf(name, "twl4030_id%d", i + 1);
+ i2c_register_slave(name, sizeof(TWL4030NodeState), p);
+ }
}
-#endif
-
+device_init(twl4030_register_devices);