Merge branch 'master' of /home/nchip/public_html/qemu into garage-push
[qemu] / hw / twl4030.c
index fdbaa54..d641306 100644 (file)
@@ -1,8 +1,10 @@
 /*
- * 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 */
 };
 
@@ -161,7 +179,7 @@ static const uint8_t addr_4b_reset_values[256] = {
     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 */
@@ -190,10 +208,9 @@ static const uint8_t addr_4b_reset_values[256] = {
     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 */
@@ -215,49 +232,68 @@ static uint8_t twl4030_48_read(void *opaque, uint8_t addr)
         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;
@@ -266,209 +302,176 @@ static void twl4030_48_write(void *opaque, uint8_t addr, uint8_t value)
             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;
@@ -476,6 +479,9 @@ static void twl4030_4a_write(void *opaque, uint8_t addr, uint8_t 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;
@@ -484,132 +490,154 @@ static void twl4030_4a_write(void *opaque, uint8_t addr, uint8_t value)
             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 */
@@ -630,14 +658,24 @@ static void twl4030_4b_write(void *opaque, uint8_t addr, uint8_t value)
                 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 */
@@ -645,45 +683,22 @@ static void twl4030_4b_write(void *opaque, uint8_t addr, uint8_t value)
         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) 
@@ -691,11 +706,12 @@ static void twl4030_4b_write(void *opaque, uint8_t addr, uint8_t value)
             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 */
@@ -710,198 +726,174 @@ static void twl4030_4b_write(void *opaque, uint8_t addr, uint8_t value)
             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);