Omap DPLL & APLL locking logic.

Reset I2C fifo on new transfers.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4919 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/hw/omap2.c b/hw/omap2.c
index ce5955f..8c9ac6a 100644
--- a/hw/omap2.c
+++ b/hw/omap2.c
@@ -2727,6 +2727,8 @@
 
     uint32_t ev;
     uint32_t evtime[2];
+
+    int dpll_lock, apll_lock[2];
 };
 
 static void omap_prcm_int_update(struct omap_prcm_s *s, int dom)
@@ -2739,6 +2741,7 @@
 {
     struct omap_prcm_s *s = (struct omap_prcm_s *) opaque;
     int offset = addr - s->base;
+    uint32_t ret;
 
     switch (offset) {
     case 0x000:	/* PRCM_REVISION */
@@ -2922,14 +2925,17 @@
     case 0x500:	/* CM_CLKEN_PLL */
         return s->clken[9];
     case 0x520:	/* CM_IDLEST_CKGEN */
-        /* Core uses 32-kHz clock */
+        ret = 0x0000070 | (s->apll_lock[0] << 9) | (s->apll_lock[1] << 8);
         if (!(s->clksel[6] & 3))
-            return 0x00000377;
-        /* DPLL not in lock mode, core uses ref_clk */
-        if ((s->clken[9] & 3) != 3)
-            return 0x00000375;
-        /* Core uses DPLL */
-        return 0x00000376;
+            /* Core uses 32-kHz clock */
+            ret |= 3 << 0;
+        else if (!s->dpll_lock)
+            /* DPLL not locked, core uses ref_clk */
+            ret |= 1 << 0;
+        else
+            /* Core uses DPLL */
+            ret |= 2 << 0;
+        return ret;
     case 0x530:	/* CM_AUTOIDLE_PLL */
         return s->clkidle[5];
     case 0x540:	/* CM_CLKSEL1_PLL */
@@ -2976,6 +2982,69 @@
     return 0;
 }
 
+static void omap_prcm_apll_update(struct omap_prcm_s *s)
+{
+    int mode[2];
+
+    mode[0] = (s->clken[9] >> 6) & 3;
+    s->apll_lock[0] = (mode[0] == 3);
+    mode[1] = (s->clken[9] >> 2) & 3;
+    s->apll_lock[1] = (mode[1] == 3);
+    /* TODO: update clocks */
+
+    if (mode[0] == 1 || mode[0] == 2 || mode[1] == 1 || mode[2] == 2)
+        fprintf(stderr, "%s: bad EN_54M_PLL or bad EN_96M_PLL\n",
+                        __FUNCTION__);
+}
+
+static void omap_prcm_dpll_update(struct omap_prcm_s *s)
+{
+    omap_clk dpll = omap_findclk(s->mpu, "dpll");
+    omap_clk dpll_x2 = omap_findclk(s->mpu, "dpll");
+    omap_clk core = omap_findclk(s->mpu, "core_clk");
+    int mode = (s->clken[9] >> 0) & 3;
+    int mult, div;
+
+    mult = (s->clksel[5] >> 12) & 0x3ff;
+    div = (s->clksel[5] >> 8) & 0xf;
+    if (mult == 0 || mult == 1)
+        mode = 1;	/* Bypass */
+
+    s->dpll_lock = 0;
+    switch (mode) {
+    case 0:
+        fprintf(stderr, "%s: bad EN_DPLL\n", __FUNCTION__);
+        break;
+    case 1:	/* Low-power bypass mode (Default) */
+    case 2:	/* Fast-relock bypass mode */
+        omap_clk_setrate(dpll, 1, 1);
+        omap_clk_setrate(dpll_x2, 1, 1);
+        break;
+    case 3:	/* Lock mode */
+        s->dpll_lock = 1; /* After 20 FINT cycles (ref_clk / (div + 1)).  */
+
+        omap_clk_setrate(dpll, div + 1, mult);
+        omap_clk_setrate(dpll_x2, div + 1, mult * 2);
+        break;
+    }
+
+    switch ((s->clksel[6] >> 0) & 3) {
+    case 0:
+        omap_clk_reparent(core, omap_findclk(s->mpu, "clk32-kHz"));
+        break;
+    case 1:
+        omap_clk_reparent(core, dpll);
+        break;
+    case 2:
+        /* Default */
+        omap_clk_reparent(core, dpll_x2);
+        break;
+    case 3:
+        fprintf(stderr, "%s: bad CORE_CLK_SRC\n", __FUNCTION__);
+        break;
+    }
+}
+
 static void omap_prcm_write(void *opaque, target_phys_addr_t addr,
                 uint32_t value)
 {
@@ -3235,20 +3304,44 @@
         break;
 
     case 0x500:	/* CM_CLKEN_PLL */
-        s->clken[9] = value & 0xcf;
-        /* TODO update clocks */
+        if (value & 0xffffff30)
+            fprintf(stderr, "%s: write 0s in CM_CLKEN_PLL for "
+                            "future compatiblity\n", __FUNCTION__);
+        if ((s->clken[9] ^ value) & 0xcc) {
+            s->clken[9] &= ~0xcc;
+            s->clken[9] |= value & 0xcc;
+            omap_prcm_apll_update(s);
+        }
+        if ((s->clken[9] ^ value) & 3) {
+            s->clken[9] &= ~3;
+            s->clken[9] |= value & 3;
+            omap_prcm_dpll_update(s);
+        }
         break;
     case 0x530:	/* CM_AUTOIDLE_PLL */
         s->clkidle[5] = value & 0x000000cf;
         /* TODO update clocks */
         break;
     case 0x540:	/* CM_CLKSEL1_PLL */
+        if (value & 0xfc4000d7)
+            fprintf(stderr, "%s: write 0s in CM_CLKSEL1_PLL for "
+                            "future compatiblity\n", __FUNCTION__);
+        if ((s->clksel[5] ^ value) & 0x003fff00) {
+            s->clksel[5] = value & 0x03bfff28;
+            omap_prcm_dpll_update(s);
+        }
+        /* TODO update the other clocks */
+
         s->clksel[5] = value & 0x03bfff28;
-        /* TODO update clocks */
         break;
     case 0x544:	/* CM_CLKSEL2_PLL */
-        s->clksel[6] = value & 3;
-        /* TODO update clocks */
+        if (value & ~3)
+            fprintf(stderr, "%s: write 0s in CM_CLKSEL2_PLL[31:2] for "
+                            "future compatiblity\n", __FUNCTION__);
+        if (s->clksel[6] != (value & 3)) {
+            s->clksel[6] = value & 3;
+            omap_prcm_dpll_update(s);
+        }
         break;
 
     case 0x800:	/* CM_FCLKEN_DSP */
@@ -3373,6 +3466,8 @@
     s->power[3] = 0x14;
     s->rstctrl[0] = 1;
     s->rst[3] = 1;
+    omap_prcm_apll_update(s);
+    omap_prcm_dpll_update(s);
 }
 
 static void omap_prcm_coldreset(struct omap_prcm_s *s)
diff --git a/hw/omap_clk.c b/hw/omap_clk.c
index da03e156..13d7411 100644
--- a/hw/omap_clk.c
+++ b/hw/omap_clk.c
@@ -510,18 +510,25 @@
     .parent	= &xtal_osc32k,
 };
 
+static struct clk ref_clk = {
+    .name	= "ref_clk",
+    .flags	= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+    .rate	= 12000000,	/* 12 MHz or 13 MHz or 19.2 MHz */
+    /*.parent	= sys.xtalin */
+};
+
 static struct clk apll_96m = {
     .name	= "apll_96m",
     .flags	= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
     .rate	= 96000000,
-    /*.parent	= sys.xtalin */
+    /*.parent	= ref_clk */
 };
 
 static struct clk apll_54m = {
     .name	= "apll_54m",
     .flags	= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
     .rate	= 54000000,
-    /*.parent	= sys.xtalin */
+    /*.parent	= ref_clk */
 };
 
 static struct clk sys_clk = {
@@ -541,13 +548,13 @@
 static struct clk dpll_ck = {
     .name	= "dpll",
     .flags	= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
-    /*.parent	= sys.xtalin */
+    .parent	= &ref_clk,
 };
 
 static struct clk dpll_x2_ck = {
     .name	= "dpll_x2",
     .flags	= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
-    /*.parent	= sys.xtalin */
+    .parent	= &ref_clk,
 };
 
 static struct clk wdt1_sys_clk = {
@@ -600,7 +607,7 @@
 static struct clk core_clk = {
     .name	= "core_clk",
     .flags	= CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
-    .parent	= &dpll_ck,
+    .parent	= &dpll_x2_ck,	/* Switchable between dpll_ck and clk32k */
 };
 
 static struct clk l3_clk = {
@@ -1009,6 +1016,7 @@
 
     /* OMAP 2 */
 
+    &ref_clk,
     &apll_96m,
     &apll_54m,
     &sys_clk,
diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c
index f3ccbf0..127e69c 100644
--- a/hw/omap_i2c.c
+++ b/hw/omap_i2c.c
@@ -395,6 +395,7 @@
                             (~value >> 9) & 1);			/* TRX */
             s->stat |= nack << 1;				/* NACK */
             s->control &= ~(1 << 0);				/* STT */
+            s->fifo = 0;
             if (nack)
                 s->control &= ~(1 << 1);			/* STP */
             else {