OMAP3 DPLL5 clock fix and added the missing omap3_boot.c.
authorJuha Riihimäki <juhriihi@esdhcp039194.research.nokia.com>
Fri, 13 Mar 2009 14:20:42 +0000 (16:20 +0200)
committerJuha Riihimäki <juhriihi@esdhcp039194.research.nokia.com>
Fri, 13 Mar 2009 14:20:42 +0000 (16:20 +0200)
hw/omap3.c
hw/omap3_boot.c [new file with mode: 0644]

index 9abceaf..403e584 100644 (file)
@@ -1802,138 +1802,134 @@ struct omap3_prm_s *omap3_prm_init(struct omap_target_agent_s *ta,
     return s;
 }
 
-
-struct omap3_cm_s
-{
+struct omap3_cm_s {
     qemu_irq irq[3];
     struct omap_mpu_state_s *mpu;
 
-    /*IVA2_CM Register */
-    uint32_t cm_fclken_iva2;    /*0x4800 4000 */
-    uint32_t cm_clken_pll_iva2; /*0x4800 4004 */
-    uint32_t cm_idlest_iva2;    /*0x4800 4020 */
-    uint32_t cm_idlest_pll_iva2;        /*0x4800 4024 */
-    uint32_t cm_autoidle_pll_iva2;      /*0x4800 4034 */
-    uint32_t cm_clksel1_pll_iva2;       /*0x4800 4040 */
-    uint32_t cm_clksel2_pll_iva2;       /*0x4800 4044 */
-    uint32_t cm_clkstctrl_iva2; /*0x4800 4048 */
-    uint32_t cm_clkstst_iva2;   /*0x4800 404c */
-
-    /*OCP_System_Reg_CM */
-    uint32_t cm_revision;       /*0x4800 4800 */
-    uint32_t cm_sysconfig;      /*0x4800 4810 */
-
-    /*MPU_CM Register */
-    uint32_t cm_clken_pll_mpu;  /*0x4800 4904 */
-    uint32_t cm_idlest_mpu;     /*0x4800 4920 */
-    uint32_t cm_idlest_pll_mpu; /*0x4800 4924 */
-    uint32_t cm_autoidle_pll_mpu;       /*0x4800 4934 */
-    uint32_t cm_clksel1_pll_mpu;        /*0x4800 4940 */
-    uint32_t cm_clksel2_pll_mpu;        /*0x4800 4944 */
-    uint32_t cm_clkstctrl_mpu;  /*0x4800 4948 */
-    uint32_t cm_clkstst_mpu;    /*0x4800 494c */
-
-    /*CORE_CM Register */
-    uint32_t cm_fclken1_core;   /*0x4800 4a00 */
-    uint32_t cm_fclken3_core;   /*0x4800 4a08 */
-    uint32_t cm_iclken1_core;   /*0x4800 4a10 */
-    uint32_t cm_iclken2_core;   /*0x4800 4a14 */
-    uint32_t cm_iclken3_core;   /*0x4800 4a18 */
-    uint32_t cm_idlest1_core;   /*0x4800 4a20 */
-    uint32_t cm_idlest2_core;   /*0x4800 4a24 */
-    uint32_t cm_idlest3_core;   /*0x4800 4a28 */
-    uint32_t cm_autoidle1_core; /*0x4800 4a30 */
-    uint32_t cm_autoidle2_core; /*0x4800 4a34 */
-    uint32_t cm_autoidle3_core; /*0x4800 4a38 */
-    uint32_t cm_clksel_core;    /*0x4800 4a40 */
-    uint32_t cm_clkstctrl_core; /*0x4800 4a48 */
-    uint32_t cm_clkstst_core;   /*0x4800 4a4c */
-
-    /*SGX_CM Register */
-    uint32_t cm_fclken_sgx;     /*0x4800 4b00 */
-    uint32_t cm_iclken_sgx;     /*0x4800 4b10 */
-    uint32_t cm_idlest_sgx;     /*0x4800 4b20 */
-    uint32_t cm_clksel_sgx;     /*0x4800 4b40 */
-    uint32_t cm_sleepdep_sgx;   /*0x4800 4b44 */
-    uint32_t cm_clkstctrl_sgx;  /*0x4800 4b48 */
-    uint32_t cm_clkstst_sgx;    /*0x4800 4b4c */
-
-    /*WKUP_CM Register */
-    uint32_t cm_fclken_wkup;    /*0x4800 4c00 */
-    uint32_t cm_iclken_wkup;    /*0x4800 4c10 */
-    uint32_t cm_idlest_wkup;    /*0x4800 4c20 */
-    uint32_t cm_autoidle_wkup;  /*0x4800 4c30 */
-    uint32_t cm_clksel_wkup;    /*0x4800 4c40 */
-    uint32_t cm_c48;                  /*0x4800 4c48 */
-
-    /*Clock_Control_Reg_CM Register */
-    uint32_t cm_clken_pll;      /*0x4800 4d00 */
-    uint32_t cm_clken2_pll;     /*0x4800 4d04 */
-    uint32_t cm_idlest_ckgen;   /*0x4800 4d20 */
-    uint32_t cm_idlest2_ckgen;  /*0x4800 4d24 */
-    uint32_t cm_autoidle_pll;   /*0x4800 4d30 */
-    uint32_t cm_autoidle2_pll;  /*0x4800 4d34 */
-    uint32_t cm_clksel1_pll;    /*0x4800 4d40 */
-    uint32_t cm_clksel2_pll;    /*0x4800 4d44 */
-    uint32_t cm_clksel3_pll;    /*0x4800 4d48 */
-    uint32_t cm_clksel4_pll;    /*0x4800 4d4c */
-    uint32_t cm_clksel5_pll;    /*0x4800 4d50 */
-    uint32_t cm_clkout_ctrl;    /*0x4800 4d70 */
-
-    /*DSS_CM Register */
-    uint32_t cm_fclken_dss;     /*0x4800 4e00 */
-    uint32_t cm_iclken_dss;     /*0x4800 4e10 */
-    uint32_t cm_idlest_dss;     /*0x4800 4e20 */
-    uint32_t cm_autoidle_dss;   /*0x4800 4e30 */
-    uint32_t cm_clksel_dss;     /*0x4800 4e40 */
-    uint32_t cm_sleepdep_dss;   /*0x4800 4e44 */
-    uint32_t cm_clkstctrl_dss;  /*0x4800 4e48 */
-    uint32_t cm_clkstst_dss;    /*0x4800 4e4c */
-
-
-    /*CAM_CM Register */
-    uint32_t cm_fclken_cam;     /*0x4800 4f00 */
-    uint32_t cm_iclken_cam;     /*0x4800 4f10 */
-    uint32_t cm_idlest_cam;     /*0x4800 4f20 */
-    uint32_t cm_autoidle_cam;   /*0x4800 4f30 */
-    uint32_t cm_clksel_cam;     /*0x4800 4f40 */
-    uint32_t cm_sleepdep_cam;   /*0x4800 4f44 */
-    uint32_t cm_clkstctrl_cam;  /*0x4800 4f48 */
-    uint32_t cm_clkstst_cam;    /*0x4800 4f4c */
-
-    /*PER_CM Register */
-    uint32_t cm_fclken_per;     /*0x4800 5000 */
-    uint32_t cm_iclken_per;     /*0x4800 5010 */
-    uint32_t cm_idlest_per;     /*0x4800 5020 */
-    uint32_t cm_autoidle_per;   /*0x4800 5030 */
-    uint32_t cm_clksel_per;     /*0x4800 5040 */
-    uint32_t cm_sleepdep_per;   /*0x4800 5044 */
-    uint32_t cm_clkstctrl_per;  /*0x4800 5048 */
-    uint32_t cm_clkstst_per;    /*0x4800 504c */
-
-    /*EMU_CM Register */
-    uint32_t cm_clksel1_emu;    /*0x4800 5140 */
-    uint32_t cm_clkstctrl_emu;  /*0x4800 5148 */
-    uint32_t cm_clkstst_emu;    /*0x4800 514c */
-    uint32_t cm_clksel2_emu;    /*0x4800 5150 */
-    uint32_t cm_clksel3_emu;    /*0x4800 5154 */
-
-    /*Global_Reg_CM Register */
-    uint32_t cm_polctrl;        /*0x4800 529c */
-
-    /*NEON_CM Register */
-    uint32_t cm_idlest_neon;    /*0x4800 5320 */
-    uint32_t cm_clkstctrl_neon; /*0x4800 5348 */
-
-    /*USBHOST_CM Register */
-    uint32_t cm_fclken_usbhost; /*0x4800 5400 */
-    uint32_t cm_iclken_usbhost; /*0x4800 5410 */
-    uint32_t cm_idlest_usbhost; /*0x4800 5420 */
-    uint32_t cm_autoidle_usbhost;       /*0x4800 5430 */
-    uint32_t cm_sleepdep_usbhost;       /*0x4800 5444 */
-    uint32_t cm_clkstctrl_usbhost;      /*0x4800 5448 */
-    uint32_t cm_clkstst_usbhost;        /*0x4800 544c */
-
+    /* IVA2_CM: base + 0x0000 */
+    uint32_t cm_fclken_iva2;       /* 00 */
+    uint32_t cm_clken_pll_iva2;    /* 04 */
+    uint32_t cm_idlest_iva2;       /* 20 */
+    uint32_t cm_idlest_pll_iva2;   /* 24 */
+    uint32_t cm_autoidle_pll_iva2; /* 34 */
+    uint32_t cm_clksel1_pll_iva2;  /* 40 */
+    uint32_t cm_clksel2_pll_iva2;  /* 44 */
+    uint32_t cm_clkstctrl_iva2;    /* 48 */
+    uint32_t cm_clkstst_iva2;      /* 4c */
+
+    /* OCP_System_Reg_CM: base + 0x0800 */
+    uint32_t cm_revision;  /* 00 */
+    uint32_t cm_sysconfig; /* 10 */
+
+    /* MPU_CM: base + 0x0900 */
+    uint32_t cm_clken_pll_mpu;    /* 04 */
+    uint32_t cm_idlest_mpu;       /* 20 */
+    uint32_t cm_idlest_pll_mpu;   /* 24 */
+    uint32_t cm_autoidle_pll_mpu; /* 34 */
+    uint32_t cm_clksel1_pll_mpu;  /* 40 */
+    uint32_t cm_clksel2_pll_mpu;  /* 44 */
+    uint32_t cm_clkstctrl_mpu;    /* 48 */
+    uint32_t cm_clkstst_mpu;      /* 4c */
+
+    /* CORE_CM: base + 0x0a00 */
+    uint32_t cm_fclken1_core;   /* 0a00 */
+    uint32_t cm_fclken3_core;   /* 0a08 */
+    uint32_t cm_iclken1_core;   /* 0a10 */
+    uint32_t cm_iclken2_core;   /* 0a14 */
+    uint32_t cm_iclken3_core;   /* 0a18 */
+    uint32_t cm_idlest1_core;   /* 0a20 */
+    uint32_t cm_idlest2_core;   /* 0a24 */
+    uint32_t cm_idlest3_core;   /* 0a28 */
+    uint32_t cm_autoidle1_core; /* 0a30 */
+    uint32_t cm_autoidle2_core; /* 0a34 */
+    uint32_t cm_autoidle3_core; /* 0a38 */
+    uint32_t cm_clksel_core;    /* 0a40 */
+    uint32_t cm_clkstctrl_core; /* 0a48 */
+    uint32_t cm_clkstst_core;   /* 0a4c */
+
+    /* SGX_CM: base + 0x0b00 */
+    uint32_t cm_fclken_sgx;    /* 00 */
+    uint32_t cm_iclken_sgx;    /* 10 */
+    uint32_t cm_idlest_sgx;    /* 20 */
+    uint32_t cm_clksel_sgx;    /* 40 */
+    uint32_t cm_sleepdep_sgx;  /* 44 */
+    uint32_t cm_clkstctrl_sgx; /* 48 */
+    uint32_t cm_clkstst_sgx;   /* 4c */
+
+    /* WKUP_CM: base + 0x0c00 */
+    uint32_t cm_fclken_wkup;   /* 00 */
+    uint32_t cm_iclken_wkup;   /* 10 */
+    uint32_t cm_idlest_wkup;   /* 20 */
+    uint32_t cm_autoidle_wkup; /* 30 */
+    uint32_t cm_clksel_wkup;   /* 40 */
+    uint32_t cm_c48;           /* 48 */
+
+    /* Clock_Control_Reg_CM: base + 0x0d00 */
+    uint32_t cm_clken_pll;     /* 00 */
+    uint32_t cm_clken2_pll;    /* 04 */
+    uint32_t cm_idlest_ckgen;  /* 20 */
+    uint32_t cm_idlest2_ckgen; /* 24 */
+    uint32_t cm_autoidle_pll;  /* 30 */
+    uint32_t cm_autoidle2_pll; /* 34 */
+    uint32_t cm_clksel1_pll;   /* 40 */
+    uint32_t cm_clksel2_pll;   /* 44 */
+    uint32_t cm_clksel3_pll;   /* 48 */
+    uint32_t cm_clksel4_pll;   /* 4c */
+    uint32_t cm_clksel5_pll;   /* 50 */
+    uint32_t cm_clkout_ctrl;   /* 70 */
+
+    /* DSS_CM: base + 0x0e00 */
+    uint32_t cm_fclken_dss;    /* 00 */
+    uint32_t cm_iclken_dss;    /* 10 */
+    uint32_t cm_idlest_dss;    /* 20 */
+    uint32_t cm_autoidle_dss;  /* 30 */
+    uint32_t cm_clksel_dss;    /* 40 */
+    uint32_t cm_sleepdep_dss;  /* 44 */
+    uint32_t cm_clkstctrl_dss; /* 48 */
+    uint32_t cm_clkstst_dss;   /* 4c */
+
+   /* CAM_CM: base + 0x0f00 */
+    uint32_t cm_fclken_cam;    /* 00 */
+    uint32_t cm_iclken_cam;    /* 10 */
+    uint32_t cm_idlest_cam;    /* 20 */
+    uint32_t cm_autoidle_cam;  /* 30 */
+    uint32_t cm_clksel_cam;    /* 40 */
+    uint32_t cm_sleepdep_cam;  /* 44 */
+    uint32_t cm_clkstctrl_cam; /* 48 */
+    uint32_t cm_clkstst_cam;   /* 4c */
+
+    /* PER_CM: base + 0x1000 */
+    uint32_t cm_fclken_per;    /* 00 */
+    uint32_t cm_iclken_per;    /* 10 */
+    uint32_t cm_idlest_per;    /* 20 */
+    uint32_t cm_autoidle_per;  /* 30 */
+    uint32_t cm_clksel_per;    /* 40 */
+    uint32_t cm_sleepdep_per;  /* 44 */
+    uint32_t cm_clkstctrl_per; /* 48 */
+    uint32_t cm_clkstst_per;   /* 4c */
+
+    /* EMU_CM: base + 0x1100 */
+    uint32_t cm_clksel1_emu;   /* 40 */
+    uint32_t cm_clkstctrl_emu; /* 48 */
+    uint32_t cm_clkstst_emu;   /* 4c */
+    uint32_t cm_clksel2_emu;   /* 50 */
+    uint32_t cm_clksel3_emu;   /* 54 */
+
+    /* Global_Reg_CM: base + 0x1200 */
+    uint32_t cm_polctrl; /* 9c */
+
+    /* NEON_CM: base + 0x1300 */
+    uint32_t cm_idlest_neon;    /* 20 */
+    uint32_t cm_clkstctrl_neon; /* 48 */
+
+    /* USBHOST_CM: base + 0x1400 */
+    uint32_t cm_fclken_usbhost;    /* 00 */
+    uint32_t cm_iclken_usbhost;    /* 10 */
+    uint32_t cm_idlest_usbhost;    /* 20 */
+    uint32_t cm_autoidle_usbhost;  /* 30 */
+    uint32_t cm_sleepdep_usbhost;  /* 44 */
+    uint32_t cm_clkstctrl_usbhost; /* 48 */
+    uint32_t cm_clkstst_usbhost;   /* 4c */
 };
 
 /*
@@ -1981,249 +1977,191 @@ static inline void omap3_cm_clksel_wkup_update(struct omap3_cm_s *s,
     /*TODO:CM_USIM_CLK CLKSEL_RM */
 }
 
-static inline void omap3_cm_mpu_update(struct omap3_cm_s *s)
-{
-    uint32_t m, n, divide, m2, cm_clken_pll_mpu;
-    uint32_t bypass = 1;
-
-    cm_clken_pll_mpu = s->cm_clken_pll_mpu;
-    omap_clk mpu_clk = omap_findclk(s->mpu, "omap3_mpu_clk");
-
-    if ((cm_clken_pll_mpu & 0x7) == 0x5)
-    {
-        bypass = 1;
-    }
-    else if ((cm_clken_pll_mpu & 0x7) == 0x7)
-    {
-        m = (s->cm_clksel1_pll_mpu & 0x7ff00) >> 8;
-        if ((m == 0) || (m == 1))
-            bypass = 1;
-        else
-            bypass = 0;
-    }
-    if (bypass == 1)
-    {
-        /*BYPASS Model */
-        divide = (s->cm_clksel1_pll_mpu & 0x380000) >> 19;
-        //OMAP3_DEBUG(("divide %d\n",divide));
-        omap_clk_reparent(mpu_clk, omap_findclk(s->mpu, "omap3_core_clk"));
-        omap_clk_setrate(mpu_clk, divide, 1);
-
-    }
-    else
-    {
-        n = (s->cm_clksel1_pll_mpu & 0x7F);
-        m2 = (s->cm_clksel2_pll_mpu & 0x1F);
-        //OMAP3_DEBUG(("M  %d N %d M2 %d \n",m,n,m2 ));
-        omap_clk_reparent(mpu_clk, omap_findclk(s->mpu, "omap3_sys_clk"));
-        omap_clk_setrate(mpu_clk, (n + 1) * m2, m);
-        //OMAP3_DEBUG(("mpu %d \n",omap_clk_getrate(mpu_clk)));
-
-    }
-
-}
-
 static inline void omap3_cm_iva2_update(struct omap3_cm_s *s)
 {
-    uint32_t m, n, divide, m2, cm_clken_pll_iva2;
-    uint32_t bypass = 1;
-
-    cm_clken_pll_iva2 = s->cm_clken_pll_iva2;
+    uint32_t m = ((s->cm_clksel1_pll_iva2 >> 8) & 0x7ff);
+    uint32_t n, divide, m2;
     omap_clk iva2_clk = omap_findclk(s->mpu, "omap3_iva2_clk");
 
-    if (((cm_clken_pll_iva2 & 0x7) == 0x5)
-        || ((cm_clken_pll_iva2 & 0x7) == 0x1))
-    {
-        bypass = 1;
-    }
-    else if ((cm_clken_pll_iva2 & 0x7) == 0x7)
-    {
-        m = (s->cm_clksel1_pll_iva2 & 0x7ff00) >> 8;
-        if ((m == 0) || (m == 1))
-            bypass = 1;
-        else
-            bypass = 0;
+    switch ((s->cm_clken_pll_iva2 & 0x7)) {
+        case 0x01: /* low power stop mode */
+        case 0x05: /* low power bypass mode */
+            s->cm_idlest_pll_iva2 &= ~1;
+            break;
+        case 0x07: /* locked */
+            if (m < 2)
+                s->cm_idlest_pll_iva2 &= ~1;
+            else
+                s->cm_idlest_pll_iva2 |= 1;
+            break;
+        default:
+            break;
     }
-    if (bypass == 1)
-    {
-        /*BYPASS Model */
+    
+    if (s->cm_idlest_pll_iva2 & 1) {
+        n = (s->cm_clksel1_pll_iva2 & 0x7f);
+        m2 = (s->cm_clksel2_pll_iva2 & 0x1f);
+        omap_clk_reparent(iva2_clk, omap_findclk(s->mpu, "omap3_sys_clk"));
+        omap_clk_setrate(iva2_clk, (n + 1) * m2, m);
+    } else {
+        /* bypass mode */
         divide = (s->cm_clksel1_pll_iva2 & 0x380000) >> 19;
-        //OMAP3_DEBUG(("divide %d\n",divide));
         omap_clk_reparent(iva2_clk, omap_findclk(s->mpu, "omap3_core_clk"));
         omap_clk_setrate(iva2_clk, divide, 1);
-
     }
-    else
-    {
-        n = (s->cm_clksel1_pll_iva2 & 0x7F);
-        m2 = (s->cm_clksel2_pll_iva2 & 0x1F);
-        //OMAP3_DEBUG(("M  %d N %d M2 %d \n",m,n,m2 ));
-        omap_clk_reparent(iva2_clk, omap_findclk(s->mpu, "omap3_sys_clk"));
-        omap_clk_setrate(iva2_clk, (n + 1) * m2, m);
-        //OMAP3_DEBUG(("iva2_clk %d \n",omap_clk_getrate(iva2_clk)));
+}
 
+static inline void omap3_cm_mpu_update(struct omap3_cm_s *s)
+{
+    uint32_t m = ((s->cm_clksel1_pll_mpu >> 8) & 0x7ff);
+    uint32_t n, divide, m2;
+    omap_clk mpu_clk = omap_findclk(s->mpu, "omap3_mpu_clk");
+    
+    switch ((s->cm_clken_pll_mpu & 0x7)) {
+        case 0x05: /* low power bypass mode */
+            s->cm_idlest_pll_mpu &= ~1;
+            break;
+        case 0x07: /* locked */
+            if (m < 2)
+                s->cm_idlest_pll_mpu &= ~1;
+            else
+                s->cm_idlest_pll_mpu |= 1;
+            break;
+        default:
+            break;
+    }
+    
+    if (s->cm_idlest_pll_mpu & 1) {
+        n = (s->cm_clksel1_pll_mpu & 0x7f);
+        m2 = (s->cm_clksel2_pll_mpu & 0x1f);
+        omap_clk_reparent(mpu_clk, omap_findclk(s->mpu, "omap3_sys_clk"));
+        omap_clk_setrate(mpu_clk, (n + 1) * m2, m);
+    } else {
+        /* bypass mode */
+        divide = (s->cm_clksel1_pll_mpu & 0x380000) >> 19;
+        omap_clk_reparent(mpu_clk, omap_findclk(s->mpu, "omap3_core_clk"));
+        omap_clk_setrate(mpu_clk, divide, 1);
     }
-
 }
 
 static inline void omap3_cm_dpll3_update(struct omap3_cm_s *s)
 {
-    uint32_t m, n, m2, m3, cm_clken_pll;
-    uint32_t bypass = 1;
-
-    cm_clken_pll = s->cm_clken_pll;
+    uint32_t m = ((s->cm_clksel1_pll >> 16) & 0x7ff);
+    uint32_t n, m2, m3;
 
-    /*dpll3 bypass mode. parent clock is always omap3_sys_clk */
-    if (((cm_clken_pll & 0x7) == 0x5) || ((cm_clken_pll & 0x7) == 0x6))
-    {
-        bypass = 1;
-    }
-    else if ((cm_clken_pll & 0x7) == 0x7)
-    {
-        m = (s->cm_clksel1_pll & 0x7ff0000) >> 16;
-        if ((m == 0) || (m == 1))
-            bypass = 1;
-        else
-            bypass = 0;
-    }
-    if (bypass == 1)
-    {
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core_clk"), 1, 1);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core2_clk"), 1, 1);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_emu_core_alwon_clk"), 1,
-                         1);
+    switch ((s->cm_clken_pll & 0x7)) {
+        case 0x05: /* low power bypass */
+        case 0x06: /* fast relock bypass */
+            s->cm_idlest_ckgen &= ~1;
+            break;
+        case 0x07: /* locked */
+            if (m < 2)
+                s->cm_idlest_ckgen &= ~1;
+            else
+                s->cm_idlest_ckgen |= 1;
+            break;
+        default:
+            break;
     }
-    else
-    {
+
+    if (s->cm_idlest_ckgen & 1) {
         n = (s->cm_clksel1_pll & 0x3f00) >> 8;
         m2 = (s->cm_clksel1_pll & 0xf8000000) >> 27;
         m3 = (s->cm_clksel1_emu & 0x1f0000) >> 16;
-
-        if (s->cm_clksel2_emu&0x80000)
-        {
-               /*override control of DPLL3*/
-               m = (s->cm_clksel2_emu&0x7ff)>>8;
-               n =  s->cm_clksel2_emu&0x7f;
-               TRACE("DPLL3 override, m 0x%x n 0x%x",m,n);
+        
+        if (s->cm_clksel2_emu & 0x80000) {
+               /* override control of DPLL3 */
+               m = (s->cm_clksel2_emu & 0x7ff) >> 8;
+               n = s->cm_clksel2_emu & 0x7f;
         }
-
-        //OMAP3_DEBUG(("dpll3 cm_clksel1_pll %x m  %d n %d m2 %d  m3 %d\n",s->cm_clksel1_pll,m,n,m2,m3 ));
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core_clk"), (n + 1) * m2,
-                         m);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core2_clk"), (n + 1) * m2,
-                         m * 2);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_emu_core_alwon_clk"),
-                         (n + 1) * m3, m * 2);
-        TRACE("coreclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_core_clk")));
+        
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core_clk"), (n + 1) * m2, m);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core2_clk"), (n + 1) * m2, m * 2);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_emu_core_alwon_clk"), (n + 1) * m3, m * 2);
+    } else {
+        /* bypass mode */
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core_clk"), 1, 1);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_core2_clk"), 1, 1);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_emu_core_alwon_clk"), 1, 1);
     }
-
-
 }
 
 static inline void omap3_cm_dpll4_update(struct omap3_cm_s *s)
 {
-    uint32_t m, n, m2, m3, m4, m5, m6, cm_clken_pll;
-    cm_clken_pll = s->cm_clken_pll;
-    uint32_t bypass = 1;
+    uint32_t m = ((s->cm_clksel2_pll >> 8) & 0x7ff);
+    uint32_t n, m2, m3, m4, m5, m6;
 
-    /*dpll3 bypass mode. parent clock is always omap3_sys_clk */
-    /*DPLL4 */
-    if ((cm_clken_pll & 0x70000) == 0x10000)
-    {
-        bypass = 1;
-    }
-    else if ((cm_clken_pll & 0x70000) == 0x70000)
-    {
-        m = (s->cm_clksel2_pll & 0x7ff00) >> 8;
-        if ((m == 0) || (m == 1))
-            bypass = 1;
-        else
-            bypass = 0;
-    }
-    if (bypass == 1)
-    {
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_96m_fclk"), 1, 1);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_54m_fclk"), 1, 1);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_dss1_alwon_fclk"), 1, 1);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_cam_mclk"), 1, 1);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_per_alwon_clk"), 1, 1);
+    switch (((s->cm_clken_pll >> 16) & 0x7)) {
+        case 0x01: /* lower power stop mode */
+            s->cm_idlest_ckgen &= ~2;
+            break;
+        case 0x07: /* locked */
+            if (m < 2)
+                s->cm_idlest_ckgen &= ~2;
+            else
+                s->cm_idlest_ckgen |= 2;
+            break;
+        default:
+            break;
     }
-    else
-    {
+
+    if (s->cm_idlest_ckgen & 2) {
         n = (s->cm_clksel2_pll & 0x7f);
         m2 = s->cm_clksel3_pll & 0x1f;
         m3 = (s->cm_clksel_dss & 0x1f00) >> 8;
         m4 = s->cm_clksel_dss & 0x1f;
         m5 = s->cm_clksel_cam & 0x1f;
         m6 = (s->cm_clksel1_emu & 0x1f000000) >> 24;
-
-        if (s->cm_clksel3_emu&0x80000)
-        {
-               /*override control of DPLL4*/
-               m = (s->cm_clksel3_emu&0x7ff)>>8;
-               n =  s->cm_clksel3_emu&0x7f;
-               TRACE("DPLL4 override, m 0x%x n 0x%x",m,n);
+        
+        if (s->cm_clksel3_emu & 0x80000) {
+               /* override control of DPLL4 */
+               m = (s->cm_clksel3_emu & 0x7ff) >> 8;
+               n =  s->cm_clksel3_emu & 0x7f;
         }
-
-
-        //OMAP3_DEBUG(("dpll4 m  %d n %d m2 %d  m3 %d m4 %d m5 %d m6 %d \n",m,n,m2,m3,m4,m5,m6 ));
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_96m_fclk"), (n + 1) * m2,
-                         m * 2);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_54m_fclk"), (n + 1) * m3,
-                         m * 2);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_dss1_alwon_fclk"),
-                         (n + 1) * m4, m * 2);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_cam_mclk"), (n + 1) * m5,
-                         m * 2);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_per_alwon_clk"),
-                         (n + 1) * m6, m * 2);
-
-        TRACE("omap3_96m_fclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_96m_fclk")));
-        TRACE("omap3_54m_fclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_54m_fclk")));
-        TRACE("omap3_dss1_alwon_fclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_dss1_alwon_fclk")));
-        TRACE("omap3_cam_mclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_cam_mclk")));
-        TRACE("omap3_per_alwon_clk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_per_alwon_clk")));
-        TRACE("omap3_48m_fclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_48m_fclk")));
-        TRACE("omap3_12m_fclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_12m_fclk")));
+        
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_96m_fclk"), (n + 1) * m2, m * 2);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_54m_fclk"), (n + 1) * m3, m * 2);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_dss1_alwon_fclk"), (n + 1) * m4, m * 2);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_cam_mclk"), (n + 1) * m5, m * 2);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_per_alwon_clk"), (n + 1) * m6, m * 2);
+    } else {
+        /* bypass mode */
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_96m_fclk"), 1, 1);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_54m_fclk"), 1, 1);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_dss1_alwon_fclk"), 1, 1);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_cam_mclk"), 1, 1);
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_per_alwon_clk"), 1, 1);
     }
 }
 
 static inline void omap3_cm_dpll5_update(struct omap3_cm_s *s)
 {
-        uint32_t m, n, m2, cm_idlest2_ckgen;
-    uint32_t bypass = 1;
+    uint32_t m = ((s->cm_clksel4_pll >> 8) & 0x7ff);
+    uint32_t n, m2;
 
-    cm_idlest2_ckgen = s->cm_idlest2_ckgen;;
-
-    /*dpll5 bypass mode */
-    if ((cm_idlest2_ckgen & 0x1) == 0x0) 
-    {
-        bypass = 1;
+    switch ((s->cm_clken2_pll & 0x7)) {
+        case 0x01: /* low power stop mode */
+            s->cm_idlest2_ckgen &= ~1;
+            break;
+        case 0x07: /* locked */
+            if (m < 2)
+                s->cm_idlest2_ckgen &= ~1;
+            else
+                s->cm_idlest2_ckgen |= 1;
+            break;
+        default:
+            break;
     }
 
-    if (bypass == 1)
-    {
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_120m_fclk"), 1, 1);
-    }
-    else
-    {
-        m = (s->cm_clksel4_pll & 0x7ff00)>>8;
+    if (s->cm_idlest2_ckgen & 1) {
+        m = (s->cm_clksel4_pll & 0x7ff00)>>8;
         n = s->cm_clksel4_pll & 0x3f00;
         m2 = s->cm_clksel5_pll & 0x1f;
-
-        TRACE("dpll5 m %d n %d m2 %d",m,n,m2);
-        omap_clk_setrate(omap_findclk(s->mpu, "omap3_120m_fclk"), (n + 1) * m2,
-                         m);
-        TRACE("omap3_120m_fclk %lld",
-              omap_clk_getrate(omap_findclk(s->mpu, "omap3_120m_fclk")));
+        
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_120m_fclk"), (n + 1) * m2, m);
+    } else {
+        /* bypass mode */
+        omap_clk_setrate(omap_findclk(s->mpu, "omap3_120m_fclk"), 1, 1);
     }
 }
 
@@ -2425,7 +2363,7 @@ static void omap3_cm_reset(struct omap3_cm_s *s)
     s->cm_fclken_iva2 = 0x0;
     s->cm_clken_pll_iva2 = 0x11;
     s->cm_idlest_iva2 = 0x1;
-    s->cm_idlest_pll_iva2 = 0x0;
+    s->cm_idlest_pll_iva2 = 0;
     s->cm_autoidle_pll_iva2 = 0x0;
     s->cm_clksel1_pll_iva2 = 0x80000;
     s->cm_clksel2_pll_iva2 = 0x1;
@@ -2437,7 +2375,7 @@ static void omap3_cm_reset(struct omap3_cm_s *s)
 
     s->cm_clken_pll_mpu = 0x15;
     s->cm_idlest_mpu = 0x1;
-    s->cm_idlest_pll_mpu = 0x0;
+    s->cm_idlest_pll_mpu = 0;
     s->cm_autoidle_pll_mpu = 0x0;
     s->cm_clksel1_pll_mpu = 0x80000;
     s->cm_clksel2_pll_mpu = 0x1;
@@ -2478,8 +2416,8 @@ static void omap3_cm_reset(struct omap3_cm_s *s)
 
     s->cm_clken_pll = 0x110015;
     s->cm_clken2_pll = 0x11;
-    s->cm_idlest_ckgen = 0x0;
-    s->cm_idlest2_ckgen = 0x0;
+    s->cm_idlest_ckgen = 0x3f3c; /* FIXME: provide real clock statuses */
+    s->cm_idlest2_ckgen = 0xa; /* FIXME: provide real clock statuses */
     s->cm_autoidle_pll = 0x0;
     s->cm_autoidle2_pll = 0x0;
     s->cm_clksel1_pll = 0x8000040;
@@ -2542,603 +2480,309 @@ static void omap3_cm_reset(struct omap3_cm_s *s)
 static uint32_t omap3_cm_read(void *opaque, target_phys_addr_t addr)
 {
     struct omap3_cm_s *s = (struct omap3_cm_s *) opaque;
-    uint32_t ret;
-    uint32_t bypass = 0, m;
 
-    TRACE("%04x", addr);
-    switch (addr)
-    {
-    case 0x0:
-       return s->cm_fclken_iva2;
-    case 0x04:
-        return s->cm_clken_pll_iva2;
-    case 0x20:
-       return s->cm_idlest_iva2;
-    case 0x24:
-        if (((s->cm_clken_pll_iva2 & 0x7) == 0x5)
-            || ((s->cm_clken_pll_iva2 & 0x7) == 0x1))
-        {
-            bypass = 1;
-        }
-        else if ((s->cm_clken_pll_iva2 & 0x7) == 0x7)
-        {
-            m = (s->cm_clksel1_pll_iva2 & 0x7ff00) >> 8;
-            if ((m == 0) || (m == 1))
-                bypass = 1;
-            else
-                bypass = 0;
-        }
-        if (bypass)
-            return 0;
-        else
-            return 1;
-    case 0x34:
-       return s->cm_autoidle_pll_iva2;
-    case 0x40:
-        return s->cm_clksel1_pll_iva2;
-    case 0x44:
-        return s->cm_clksel2_pll_iva2;
-    case 0x48:
-       return s->cm_clkstctrl_iva2;
-    case 0x4c:
-       return s->cm_clkstst_iva2;
-
-   case 0x800:
-               return s->cm_revision;
-       case 0x810:
-               return s->cm_sysconfig;
-
-       
-    case 0x904:                /*CM_CLKEN_PLL_MPU */
-        return s->cm_clken_pll_mpu;
-   case 0x920:
-               return s->cm_idlest_mpu & 0x0;  /*MPU is active*/
-    case 0x924:
-        if ((s->cm_clken_pll_mpu & 0x7) == 0x5)
-        {
-            bypass = 1;
-        }
-        else if ((s->cm_clken_pll_mpu & 0x7) == 0x7)
-        {
-            m = (s->cm_clksel1_pll_mpu & 0x7ff00) >> 8;
-            if ((m == 0) || (m == 1))
-                bypass = 1;
-            else
-                bypass = 0;
-        }
-        if (bypass)
-            return 0;
-        else
-            return 1;
-    case 0x934:
-       return s->cm_autoidle_pll_mpu;
-    case 0x940:
-        return s->cm_clksel1_pll_mpu;
-    case 0x944:
-        return s->cm_clksel2_pll_mpu;
-     case 0x948:
-       return s->cm_clkstctrl_mpu;
-     case 0x94c:
-       return s->cm_clkstst_mpu;
-
-
-       
-    case 0xa00:
-        return s->cm_fclken1_core;
-    case 0xa08:
-       return s->cm_fclken3_core;
-    case 0xa10:
-        return s->cm_iclken1_core;
-    case 0xa14:
-        return s->cm_iclken2_core;
-    case 0xa20:
-       return s->cm_idlest1_core;
-    case 0xa24:
-       return s->cm_idlest2_core;
-    case 0xa28:
-       return s->cm_idlest3_core;
-    case 0xa30:
-       return s->cm_autoidle1_core;
-    case 0xa34:
-       return s->cm_autoidle2_core;
-    case 0xa38:
-       return s->cm_autoidle3_core;
-    case 0xa40:                /*CM_CLKSEL_CORE */
-        return s->cm_clksel_core;
-    case 0xa48:
-        return s->cm_clkstctrl_core;
-     case 0xa4c:
-       return s->cm_clkstst_core;
-
-   case 0xb00:
-               return s->cm_fclken_sgx;
-       case 0xb10:
-               return s->cm_iclken_sgx;
-       case 0xb20:
-               return s->cm_idlest_sgx&0x0;
-   case 0xb40:                /*CM_CLKSEL_SGX */
-        return s->cm_clksel_sgx;
-   case 0xb48:
-               return s->cm_clkstctrl_sgx;
-       case 0xb4c:
-               return s->cm_clkstst_sgx;
-
-               
-    case 0xc00:                /*CM_FCLKEN_WKUP */
-        return s->cm_fclken_wkup;
-    case 0xc10:                /*CM_ICLKEN_WKUP */
-        return s->cm_iclken_wkup;
-    case 0xc20:                /*CM_IDLEST_WKUP */
-        /*TODO: Check whether the timer can be accessed. */
-        return 0x0;
-    case 0xc30:
-       return s->cm_idlest_wkup;
-    case 0xc40:
-        return s->cm_clksel_wkup;
-    case 0xc48:
-       return s->cm_c48;
-
-       
-    case 0xd00:                /*CM_CLKEN_PLL */
-        return s->cm_clken_pll;
-    case 0xd04:
-       return s->cm_clken2_pll;
-    case 0xd20:
-        /*FIXME: all clock is active. we do not care it. */
-        ret = 0x3ffff;
-
-       /*DPLL3*/
-       bypass = 0;
-       if (((s->cm_clken_pll & 0x7) == 0x5) || ((s->cm_clken_pll & 0x7) == 0x6))
-               bypass = 1;
-        else if ((s->cm_clken_pll & 0x7) == 0x7) {
-            m = (s->cm_clksel1_pll & 0x7ff0000) >> 16;
-            if ((m == 0) || (m == 1))
-                bypass = 1;
-            else
-                bypass = 0;
-        }
-        if (bypass)
-            ret &= 0xfffe;
-        
-        /*DPLL4*/
-           bypass = 0;
-           if ((s->cm_clken_pll & 0x70000) == 0x10000)
-            bypass = 1;
-        else if ((s->cm_clken_pll & 0x70000) == 0x70000) {
-            m = (s->cm_clksel2_pll & 0x7ff00) >> 8;
-            if ((m == 0) || (m == 1))
-                bypass = 1;
-            else
-                bypass = 0;
-        }
-        if (bypass)
-            ret &= 0xfffd;
-       return ret;
-       
-    case 0xd24:
-       return s->cm_idlest2_ckgen;
-    case 0xd30:
-       return s->cm_autoidle_pll;
-    case 0xd34:
-       return s->cm_autoidle2_pll;
-    case 0xd40:                /*CM_CLKSEL1_PLL */
-        return s->cm_clksel1_pll;
-    case 0xd44:
-        return s->cm_clksel2_pll;
-    case 0xd48:                /*CM_CLKSEL3_PLL */
-        return s->cm_clksel3_pll;
-    case 0xd4c:
-        return s->cm_clksel4_pll;
-    case 0xd50:                /*CM_CLKSEL5_PLL */
-        return s->cm_clksel5_pll;
-    case 0xd70:
-        return s->cm_clkout_ctrl;
-
-        
-    case 0xe00:
-       return s->cm_fclken_dss;
-       case 0xe10:
-       return s->cm_iclken_dss;
-    case 0xe20:
-       return s->cm_idlest_dss;
-    case 0xe30:
-       return s->cm_autoidle_dss;
-    case 0xe40:
-        return s->cm_clksel_dss;
-    case 0xe44:
-        return s->cm_sleepdep_dss;
-    case 0xe48:
-        return s->cm_clkstctrl_dss;
-    case 0xe4c:
-        return s->cm_clkstst_dss;
-
-        
-    case 0xf00:
-       return s->cm_fclken_cam;
-    case 0xf10:
-       return s->cm_iclken_cam;
-    case 0xf20:
-       return s->cm_idlest_cam&0x0;
-    case 0xf30:
-       return s->cm_autoidle_cam;
-    case 0xf40:
-        return s->cm_clksel_cam;
-    case 0xf44:
-       return s->cm_sleepdep_cam;
-    case 0xf48:
-       return s->cm_clkstctrl_cam;
-    case 0xf4c:
-       return s->cm_clkstst_cam;
-
-       
-    case 0x1000:
-        return s->cm_fclken_per;
-    case 0x1010:
-        return s->cm_iclken_per;
-    case 0x1020:
-       return s->cm_idlest_per ;
-    case 0x1030:
-       return s->cm_autoidle_per;
-    case 0x1040:
-        return s->cm_clksel_per;
-    case 0x1044:
-       return s->cm_sleepdep_per;
-    case 0x1048:
-       return s->cm_clkstctrl_per;
-    case 0x104c:
-               return s->cm_clkstst_per;
-
-       
-    case 0x1140:               /*CM_CLKSEL1_EMU */
-        return s->cm_clksel1_emu;
-    case 0x1148:
-        return s->cm_clkstctrl_emu;
-    case 0x114c:
-       return s->cm_clkstst_emu&0x0;
-    case 0x1150:
-       return s->cm_clksel2_emu;
-    case 0x1154:
-       return s->cm_clksel3_emu;
-
-   case 0x129c:
-               return s->cm_polctrl;
-
-       case 0x1320:
-               return s->cm_idlest_neon&0x0;
-       case 0x1348:
-               return s->cm_clkstctrl_neon;
-
-       case 0x1400:
-               return s->cm_fclken_usbhost;
-       case 0x1410:
-               return s->cm_iclken_usbhost;
-       case 0x1420:
-               return s->cm_idlest_usbhost&0x0;
-    case 0x1430:
-       return s->cm_autoidle_usbhost;
-    case 0x1444:
-       return s->cm_sleepdep_usbhost;
-    case 0x1448:
-       return s->cm_clkstctrl_usbhost;
-    case 0x144c:
-       return s->cm_clkstst_usbhost;
-
-    default:
-        OMAP_BAD_REG(addr);
-        exit(-1);
+    switch (addr) {
+        /* IVA2_CM */
+        case 0x0000: return s->cm_fclken_iva2;
+        case 0x0004: return s->cm_clken_pll_iva2;
+        case 0x0020: return s->cm_idlest_iva2;
+        case 0x0024: return s->cm_idlest_pll_iva2;
+        case 0x0034: return s->cm_autoidle_pll_iva2;
+        case 0x0040: return s->cm_clksel1_pll_iva2;
+        case 0x0044: return s->cm_clksel2_pll_iva2;
+        case 0x0048: return s->cm_clkstctrl_iva2;
+        case 0x004c: return s->cm_clkstst_iva2;
+        /* OCP_System_Reg_CM */
+        case 0x0800: return s->cm_revision;
+        case 0x0810: return s->cm_sysconfig;
+        /* MPU_CM */
+        case 0x0904: return s->cm_clken_pll_mpu;
+        case 0x0920: return s->cm_idlest_mpu & 0x0; /*MPU is active*/
+        case 0x0924: return s->cm_idlest_pll_mpu;
+        case 0x0934: return s->cm_autoidle_pll_mpu;
+        case 0x0940: return s->cm_clksel1_pll_mpu;
+        case 0x0944: return s->cm_clksel2_pll_mpu;
+        case 0x0948: return s->cm_clkstctrl_mpu;
+        case 0x094c: return s->cm_clkstst_mpu;
+        /* CORE_CM */
+        case 0x0a00: return s->cm_fclken1_core;
+        case 0x0a08: return s->cm_fclken3_core;
+        case 0x0a10: return s->cm_iclken1_core;
+        case 0x0a14: return s->cm_iclken2_core;
+        case 0x0a20: return s->cm_idlest1_core;
+        case 0x0a24: return s->cm_idlest2_core;
+        case 0x0a28: return s->cm_idlest3_core;
+        case 0x0a30: return s->cm_autoidle1_core;
+        case 0x0a34: return s->cm_autoidle2_core;
+        case 0x0a38: return s->cm_autoidle3_core;
+        case 0x0a40: return s->cm_clksel_core;
+        case 0x0a48: return s->cm_clkstctrl_core;
+        case 0x0a4c: return s->cm_clkstst_core;
+        /* SGX_CM */
+        case 0x0b00: return s->cm_fclken_sgx;
+        case 0x0b10: return s->cm_iclken_sgx;
+        case 0x0b20: return s->cm_idlest_sgx & 0x0;
+        case 0x0b40: return s->cm_clksel_sgx;
+        case 0x0b48: return s->cm_clkstctrl_sgx;
+        case 0x0b4c: return s->cm_clkstst_sgx;
+        /* WKUP_CM */
+        case 0x0c00: return s->cm_fclken_wkup;
+        case 0x0c10: return s->cm_iclken_wkup;
+        case 0x0c20: return 0; /* TODO: Check if the timer can be accessed. */
+        case 0x0c30: return s->cm_idlest_wkup;
+        case 0x0c40: return s->cm_clksel_wkup;
+        case 0x0c48: return s->cm_c48;
+        /* Clock_Control_Reg_CM */
+        case 0x0d00: return s->cm_clken_pll;
+        case 0x0d04: return s->cm_clken2_pll;
+        case 0x0d20: return s->cm_idlest_ckgen;
+        case 0x0d24: return s->cm_idlest2_ckgen;
+        case 0x0d30: return s->cm_autoidle_pll;
+        case 0x0d34: return s->cm_autoidle2_pll;
+        case 0x0d40: return s->cm_clksel1_pll;
+        case 0x0d44: return s->cm_clksel2_pll;
+        case 0x0d48: return s->cm_clksel3_pll;
+        case 0x0d4c: return s->cm_clksel4_pll;
+        case 0x0d50: return s->cm_clksel5_pll;
+        case 0x0d70: return s->cm_clkout_ctrl;
+        /* DSS_CM */
+        case 0x0e00: return s->cm_fclken_dss;
+        case 0x0e10: return s->cm_iclken_dss;
+        case 0x0e20: return s->cm_idlest_dss;
+        case 0x0e30: return s->cm_autoidle_dss;
+        case 0x0e40: return s->cm_clksel_dss;
+        case 0x0e44: return s->cm_sleepdep_dss;
+        case 0x0e48: return s->cm_clkstctrl_dss;
+        case 0x0e4c: return s->cm_clkstst_dss;
+        /* CAM_CM */
+        case 0x0f00: return s->cm_fclken_cam;
+        case 0x0f10: return s->cm_iclken_cam;
+        case 0x0f20: return s->cm_idlest_cam & 0x0;
+        case 0x0f30: return s->cm_autoidle_cam;
+        case 0x0f40: return s->cm_clksel_cam;
+        case 0x0f44: return s->cm_sleepdep_cam;
+        case 0x0f48: return s->cm_clkstctrl_cam;
+        case 0x0f4c: return s->cm_clkstst_cam;
+        /* PER_CM */
+        case 0x1000: return s->cm_fclken_per;
+        case 0x1010: return s->cm_iclken_per;
+        case 0x1020: return s->cm_idlest_per ;
+        case 0x1030: return s->cm_autoidle_per;
+        case 0x1040: return s->cm_clksel_per;
+        case 0x1044: return s->cm_sleepdep_per;
+        case 0x1048: return s->cm_clkstctrl_per;
+        case 0x104c: return s->cm_clkstst_per;
+        /* EMU_CM */
+        case 0x1140: return s->cm_clksel1_emu;
+        case 0x1148: return s->cm_clkstctrl_emu;
+        case 0x114c: return s->cm_clkstst_emu & 0x0;
+        case 0x1150: return s->cm_clksel2_emu;
+        case 0x1154: return s->cm_clksel3_emu;
+        /* Global_Reg_CM */
+        case 0x129c: return s->cm_polctrl;
+        /* NEON_CM */
+        case 0x1320: return s->cm_idlest_neon & 0x0;
+        case 0x1348: return s->cm_clkstctrl_neon;
+        /* USBHOST_CM */
+        case 0x1400: return s->cm_fclken_usbhost;
+        case 0x1410: return s->cm_iclken_usbhost;
+        case 0x1420: return s->cm_idlest_usbhost & 0x0;
+        case 0x1430: return s->cm_autoidle_usbhost;
+        case 0x1444: return s->cm_sleepdep_usbhost;
+        case 0x1448: return s->cm_clkstctrl_usbhost;
+        case 0x144c: return s->cm_clkstst_usbhost;
+        /* unknown */
+        default: break;
     }
+    OMAP_BAD_REG(addr);
+    return 0;
 }
 
-
-static void omap3_cm_write(void *opaque, target_phys_addr_t addr,
+static void omap3_cm_write(void *opaque,
+                           target_phys_addr_t addr,
                            uint32_t value)
 {
-    struct omap3_cm_s *s = (struct omap3_cm_s *) opaque;
-
-    switch (addr)
-    {
-    case 0x20:
-    case 0x24:
-    case 0x4c:
-    case 0x800:
-    case 0x920:
-    case 0x924:
-    case 0x94c:
-    case 0xa20:
-    case 0xa24:
-    case 0xa28:
-    case 0xa4c:
-    case 0xb20:
-    case 0xb4c:
-    case 0xc20:                /*CM_IDLEST_WKUP */
-    case 0xd20:
-    case 0xd24:
-    case 0xe20:
-    case 0xe4c:
-    case 0xf20:
-    case 0xf4c:
-    case 0x1020:
-    case 0x104c:
-    case 0x114c:
-    case 0x1320:
-    case 0x1420:
-    case 0x144c:
-        OMAP_RO_REGV(addr, value);
-        exit(-1);
-        break;
-        
-    case 0x0:
-       s->cm_fclken_iva2 = value & 0x1;
-       break;
-    case 0x4:                  /*CM_CLKEN_PLL_IVA2 */
-        s->cm_clken_pll_iva2 = value & 0x7ff;
-        omap3_cm_iva2_update(s);
-        break;
-    case 0x34:
-       s->cm_autoidle_pll_iva2 = value & 0x7;
-       break;
-    case 0x40:
-        s->cm_clksel1_pll_iva2 = value & 0x3fff7f;
-        //printf("value %x s->cm_clksel1_pll_iva2 %x \n",value,s->cm_clksel1_pll_iva2);
-        omap3_cm_iva2_update(s);
-        break;
-    case 0x44:
-        s->cm_clksel2_pll_iva2 = value & 0x1f;
-        omap3_cm_iva2_update(s);
-        break;
-    case 0x48:
-       s->cm_clkstctrl_iva2 = value& 0x3;
-       break;
-
-    case 0x810:
-       s->cm_sysconfig = value & 0x1;
-       break;
-
-        
-    case 0x904:                /*CM_CLKEN_PLL_MPU */
-        s->cm_clken_pll_mpu = value & 0x7ff;
-        omap3_cm_mpu_update(s);
-        break;
-    case 0x934:
-       s->cm_autoidle_pll_mpu = value & 0x7;
-       break;
-    case 0x940:
-        //printf("s->cm_clksel1_pll_mpu  %x\n",s->cm_clksel1_pll_mpu );
-        s->cm_clksel1_pll_mpu = value & 0x3fff7f;
-        omap3_cm_mpu_update(s);
-        break;
-    case 0x944:
-        s->cm_clksel2_pll_mpu = value & 0x1f;
-        omap3_cm_mpu_update(s);
-        break;
-    case 0x948:
-       s->cm_clkstctrl_mpu = value & 0x3;
-       break;
-
-       
-    case 0xa00:
-        s->cm_fclken1_core = value & 0x43fffe00;
-         break;
-    case 0xa08:
-        s->cm_fclken3_core = value & 0x7;
-        break;
-    case 0xa10:
-        s->cm_iclken1_core = value & 0x637ffed2;
-        s->cm_idlest1_core = ~s->cm_iclken1_core;
-        /* TODO: replace code below with real implementation */
-        s->cm_idlest1_core &= ~0x20; /* HS OTG USB idle */
-        s->cm_idlest1_core |= 4; /* SDMA in standby */
-        break;
-    case 0xa14:
-        s->cm_iclken2_core = value & 0x1f;
-        break;
-    case 0xa18:
-       s->cm_iclken3_core = value & 0x4;
-        s->cm_idlest3_core = 0xd & ~(s->cm_iclken3_core & 4);
-       break;
-    case 0xa30:
-       s->cm_autoidle1_core = value & 0x7ffffed0;
-       break;
-    case 0xa34:
-       s->cm_autoidle2_core = value & 0x1f;
-       break;
-    case 0xa38:
-       s->cm_autoidle3_core = value & 0x2;
-       break;
-    case 0xa40:                /*CM_CLKSEL_CORE */
-        s->cm_clksel_core = (value & 0xff);
-        s->cm_clksel_core |= 0x100;
-        omap3_cm_gp10_update(s);
-        omap3_cm_gp11_update(s);
-        omap3_cm_l3clk_update(s);
-        omap3_cm_l4clk_update(s);
-        break;
-    case 0xa48:
-       s->cm_clkstctrl_core = value & 0xf;
-       break;
-
-    case 0xb00:
-       s->cm_fclken_sgx = value &0x2;
-       break;
-    case 0xb10:
-       s->cm_iclken_sgx = value & 0x1;
-       break;
-    case 0xb40:                /*CM_CLKSEL_SGX */
-        /*TODO: SGX Clock!! */
-        s->cm_clksel_sgx = value;
-        break;
-    case 0xb44:
-       s->cm_sleepdep_sgx = value &0x2;
-       break;
-    case 0xb48:
-       s->cm_clkstctrl_sgx = value & 0x3;
-       break;
+    struct omap3_cm_s *s = (struct omap3_cm_s *)opaque;
 
-    
-    case 0xc00:                /*CM_FCLKEN_WKUP */
-        s->cm_fclken_wkup = value & 0x2e9;
-        break;
-    case 0xc10:                /*CM_ICLKEN_WKUP */
-        s->cm_iclken_wkup = value & 0x2ff;
-        break;
-    case 0xc30:
-       s->cm_autoidle_wkup = value & 0x23f;
-       break;
-    case 0xc40:                /*CM_CLKSEL_WKUP */
-        s->cm_clksel_wkup = value & 0x7f;
-        omap3_cm_clksel_wkup_update(s, s->cm_clksel_wkup);
-        break;
-
-        
-    case 0xd00:                /*CM_CLKEN_PLL */
-        s->cm_clken_pll = value & 0xffff17ff;
-        omap3_cm_dpll3_update(s);
-        omap3_cm_dpll4_update(s);
-        break;
-    case 0xd04:
-       s->cm_clken2_pll = value & 0x7ff;
-       break;
-    case 0xd30:
-       s->cm_autoidle_pll = value & 0x3f;
-       break;
-    case 0xd34:
-       s->cm_autoidle2_pll = value & 0x7;
-       break;
-    case 0xd40:                /*CM_CLKSEL1_PLL */
-        //OMAP3_DEBUG(("WD40 value %x \n",value));
-        s->cm_clksel1_pll = value & 0xffffbffc;
-        //OMAP3_DEBUG(("WD40 value %x \n",value));
-        omap3_cm_dpll3_update(s);
-        omap3_cm_48m_update(s);
-        break;
-    case 0xd44:
-        s->cm_clksel2_pll = value & 0x7ff7f;
-        omap3_cm_dpll4_update(s);
-        break;
-    case 0xd48:                /*CM_CLKSEL3_PLL */
-        s->cm_clksel3_pll = value & 0x1f;
-        omap3_cm_dpll4_update(s);
-        break;
-    case 0xd4c:                /*CM_CLKSEL4_PLL */  
-       s->cm_clksel4_pll = value & 0x7ff7f;
-        omap3_cm_dpll5_update(s);
-        break;
-     case 0xd50:                /*CM_CLKSEL5_PLL */
-        s->cm_clksel5_pll = value & 0x1f;
-        omap3_cm_dpll5_update(s);
-        break;
-    case 0xd70:
-       s->cm_clkout_ctrl = value & 0xbb;
-       omap3_cm_clkout2_update(s);
-       break;
-        
-    case 0xe00:
-       s->cm_fclken_dss = value & 0x7;
-       break;
-       case 0xe10:
-       s->cm_iclken_dss = value & 0x1;
-       break;
-    case 0xe30:
-       s->cm_autoidle_dss = value & 0x1;
-       break;
-    case 0xe40:
-        s->cm_clksel_dss = value & 0x1f1f;
-        omap3_cm_dpll4_update(s);
-        break;
-   case 0xe44:
-               s->cm_sleepdep_dss = value & 0x7;
-       break;
-   case 0xe48:
-               s->cm_clkstctrl_dss = value & 0x3;
-       break;
-        
-    case 0xf00:
-       s->cm_fclken_cam = value & 0x3;
-       break;
-    case 0xf10:
-       s->cm_iclken_cam = value & 0x1;
-       break;
-    case 0xf30:
-       s->cm_autoidle_cam = value & 0x1;
-       break;
-    case 0xf40:
-        s->cm_clksel_cam = value & 0x1f;
-        omap3_cm_dpll4_update(s);
-        break;
-    case 0xf44:
-       s->cm_sleepdep_cam = value & 0x2;
-       break;
-    case 0xf48:
-       s->cm_clkstctrl_cam = value & 0x3;
-       break;
-   
-    case 0x1000:
-        s->cm_fclken_per = value & 0x3ffff;
-        break;
-    case 0x1010:
-        s->cm_iclken_per = value & 0x3ffff;
-        break;
-    
-    case 0x1030:
-       s->cm_autoidle_per = value &0x3ffff;
-       break;
-    case 0x1040:
-        s->cm_clksel_per = value & 0xff;
-        omap3_cm_per_gptimer_update(s);
-        break;
-    case 0x1044:
-       s->cm_sleepdep_per = value & 0x6;
-       break;
-    case 0x1048:
-        s->cm_clkstctrl_per = value &0x7;
-        break;
-        
-    case 0x1140:               /*CM_CLKSEL1_EMU */
-        s->cm_clksel1_emu = value & 0x1f1f3fff;
-        //printf("cm_clksel1_emu %x\n",s->cm_clksel1_emu);
-        omap3_cm_dpll3_update(s);
-        omap3_cm_dpll4_update(s);
-        break;
-    case 0x1148:
-       s->cm_clkstctrl_emu = value & 0x3;
-       break;
-        case 0x1150:
-                s->cm_clksel2_emu = value & 0xfff7f;
-                omap3_cm_dpll3_update(s);
-        break;
-    case 0x1154:
-        s->cm_clksel3_emu = value & 0xfff7f;
-                omap3_cm_dpll4_update(s);
-        break;
-
-    case 0x129c:
-        s->cm_polctrl = value & 0x1;
-        break;
-
-   case 0x1348:
-               s->cm_clkstctrl_neon = value & 0x3;
-               break;
-
-       case 0x1400:
-               s->cm_fclken_usbhost = value & 0x3;
-               break;
-       case 0x1410:
-               s->cm_iclken_usbhost = value & 0x1;
-               break;
-    case 0x1430:
-       s->cm_autoidle_usbhost = value & 0x1;
-       break;
-    case 0x1444:
-       s->cm_sleepdep_usbhost = value & 0x6;
-       break;
-    case 0x1448:
-       s->cm_clkstctrl_usbhost = value & 0x3;
-       break;
-   
-    default:
-        OMAP_BAD_REGV(addr, value);
-        exit(-1);
+    switch (addr) {
+        case 0x0020:
+        case 0x0024:
+        case 0x004c:
+        case 0x0800:
+        case 0x0920:
+        case 0x0924:
+        case 0x094c:
+        case 0x0a20:
+        case 0x0a24:
+        case 0x0a28:
+        case 0x0a4c:
+        case 0x0b20:
+        case 0x0b4c:
+        case 0x0c20:
+        case 0x0d20:
+        case 0x0d24:
+        case 0x0e20:
+        case 0x0e4c:
+        case 0x0f20:
+        case 0x0f4c:
+        case 0x1020:
+        case 0x104c:
+        case 0x114c:
+        case 0x1320:
+        case 0x1420:
+        case 0x144c:
+            OMAP_RO_REGV(addr, value);
+            break;
+        /* IVA2_CM */
+        case 0x0000: s->cm_fclken_iva2 = value & 0x1; break;
+        case 0x0004: s->cm_clken_pll_iva2 = value & 0x7ff; omap3_cm_iva2_update(s); break;
+        case 0x0034: s->cm_autoidle_pll_iva2 = value & 0x7; break;
+        case 0x0040: s->cm_clksel1_pll_iva2 = value & 0x3fff7f; omap3_cm_iva2_update(s); break;
+        case 0x0044: s->cm_clksel2_pll_iva2 = value & 0x1f; omap3_cm_iva2_update(s); break;
+        case 0x0048: s->cm_clkstctrl_iva2 = value & 0x3; break;
+        /* OCP_System_Reg_CM */
+        case 0x0810: s->cm_sysconfig = value & 0x1; break;
+        /* MPU_CM */
+        case 0x0904: s->cm_clken_pll_mpu = value & 0x7ff; omap3_cm_mpu_update(s); break;
+        case 0x0934: s->cm_autoidle_pll_mpu = value & 0x7; break;
+        case 0x0940: s->cm_clksel1_pll_mpu = value & 0x3fff7f; omap3_cm_mpu_update(s); break;
+        case 0x0944: s->cm_clksel2_pll_mpu = value & 0x1f; omap3_cm_mpu_update(s); break;
+        case 0x0948: s->cm_clkstctrl_mpu = value & 0x3; break;
+        /* CORE_CM */
+        case 0xa00: s->cm_fclken1_core = value & 0x43fffe00; break;
+        case 0xa08: s->cm_fclken3_core = value & 0x7; break;
+        case 0xa10:
+            s->cm_iclken1_core = value & 0x637ffed2;
+            s->cm_idlest1_core = ~s->cm_iclken1_core;
+            /* TODO: replace code below with real implementation */
+            s->cm_idlest1_core &= ~0x20; /* HS OTG USB idle */
+            s->cm_idlest1_core |= 4; /* SDMA in standby */
+            break;
+        case 0xa14: s->cm_iclken2_core = value & 0x1f; break;
+        case 0xa18:
+            s->cm_iclken3_core = value & 0x4;
+            s->cm_idlest3_core = 0xd & ~(s->cm_iclken3_core & 4);
+            break;
+        case 0xa30: s->cm_autoidle1_core = value & 0x7ffffed0; break;
+        case 0xa34: s->cm_autoidle2_core = value & 0x1f; break;
+        case 0xa38: s->cm_autoidle3_core = value & 0x2; break;
+        case 0xa40:
+            s->cm_clksel_core = (value & 0xff);
+            s->cm_clksel_core |= 0x100;
+            omap3_cm_gp10_update(s);
+            omap3_cm_gp11_update(s);
+            omap3_cm_l3clk_update(s);
+            omap3_cm_l4clk_update(s);
+            break;
+        case 0xa48: s->cm_clkstctrl_core = value & 0xf; break;
+        /* SGX_CM */
+        case 0xb00: s->cm_fclken_sgx = value & 0x2; break;
+        case 0xb10: s->cm_iclken_sgx = value & 0x1; break;
+        case 0xb40: s->cm_clksel_sgx = value; break; /* TODO: SGX clock */
+        case 0xb44: s->cm_sleepdep_sgx = value &0x2; break;
+        case 0xb48: s->cm_clkstctrl_sgx = value & 0x3; break;
+        /* WKUP_CM */
+        case 0xc00: s->cm_fclken_wkup = value & 0x2e9; break;
+        case 0xc10: s->cm_iclken_wkup = value & 0x2ff; break;
+        case 0xc30: s->cm_autoidle_wkup = value & 0x23f; break;
+        case 0xc40:
+            s->cm_clksel_wkup = value & 0x7f;
+            omap3_cm_clksel_wkup_update(s, s->cm_clksel_wkup);
+            break;
+        /* Clock_Control_Reg_CM */
+        case 0xd00:
+            s->cm_clken_pll = value & 0xffff17ff;
+            omap3_cm_dpll3_update(s);
+            omap3_cm_dpll4_update(s);
+            break;
+        case 0xd04:
+            s->cm_clken2_pll = value & 0x7ff;
+            omap3_cm_dpll5_update(s);
+            break;
+        case 0xd30: s->cm_autoidle_pll = value & 0x3f; break;
+        case 0xd34: s->cm_autoidle2_pll = value & 0x7; break;
+        case 0xd40:
+            s->cm_clksel1_pll = value & 0xffffbffc;
+            omap3_cm_dpll3_update(s);
+            omap3_cm_48m_update(s);
+            break;
+        case 0xd44:
+            s->cm_clksel2_pll = value & 0x7ff7f;
+            omap3_cm_dpll4_update(s);
+            break;
+        case 0xd48:                /*CM_CLKSEL3_PLL */
+            s->cm_clksel3_pll = value & 0x1f;
+            omap3_cm_dpll4_update(s);
+            break;
+        case 0xd4c:                /*CM_CLKSEL4_PLL */  
+            s->cm_clksel4_pll = value & 0x7ff7f;
+            omap3_cm_dpll5_update(s);
+            break;
+        case 0xd50:                /*CM_CLKSEL5_PLL */
+            s->cm_clksel5_pll = value & 0x1f;
+            omap3_cm_dpll5_update(s);
+            break;
+        case 0xd70:
+            s->cm_clkout_ctrl = value & 0xbb;
+            omap3_cm_clkout2_update(s);
+            break;
+        /* DSS_CM */
+        case 0xe00: s->cm_fclken_dss = value & 0x7; break;
+        case 0xe10: s->cm_iclken_dss = value & 0x1; break;
+        case 0xe30: s->cm_autoidle_dss = value & 0x1; break;
+        case 0xe40:
+            s->cm_clksel_dss = value & 0x1f1f;
+            omap3_cm_dpll4_update(s);
+            break;
+        case 0xe44: s->cm_sleepdep_dss = value & 0x7; break;
+        case 0xe48: s->cm_clkstctrl_dss = value & 0x3; break;
+        /* CAM_CM */
+        case 0xf00: s->cm_fclken_cam = value & 0x3; break;
+        case 0xf10: s->cm_iclken_cam = value & 0x1; break;
+        case 0xf30: s->cm_autoidle_cam = value & 0x1; break;
+        case 0xf40:
+            s->cm_clksel_cam = value & 0x1f;
+            omap3_cm_dpll4_update(s);
+            break;
+        case 0xf44: s->cm_sleepdep_cam = value & 0x2; break;
+        case 0xf48: s->cm_clkstctrl_cam = value & 0x3; break;
+        /* PER_CM */
+        case 0x1000: s->cm_fclken_per = value & 0x3ffff; break;
+        case 0x1010: s->cm_iclken_per = value & 0x3ffff; break;
+        case 0x1030: s->cm_autoidle_per = value &0x3ffff; break;
+        case 0x1040:
+            s->cm_clksel_per = value & 0xff;
+            omap3_cm_per_gptimer_update(s);
+            break;
+        case 0x1044: s->cm_sleepdep_per = value & 0x6; break;
+        case 0x1048: s->cm_clkstctrl_per = value &0x7; break;
+        /* EMU_CM */
+        case 0x1140:
+            s->cm_clksel1_emu = value & 0x1f1f3fff;
+            omap3_cm_dpll3_update(s);
+            omap3_cm_dpll4_update(s);
+            break;
+        case 0x1148: s->cm_clkstctrl_emu = value & 0x3; break;
+        case 0x1150:
+            s->cm_clksel2_emu = value & 0xfff7f;
+            omap3_cm_dpll3_update(s);
+            break;
+        case 0x1154:
+            s->cm_clksel3_emu = value & 0xfff7f;
+            omap3_cm_dpll4_update(s);
+            break;
+        /* Global_Reg_CM */
+        case 0x129c: s->cm_polctrl = value & 0x1; break;
+        /* NEON_CM */
+        case 0x1348: s->cm_clkstctrl_neon = value & 0x3; break;
+        /* USBHOST_CM */
+        case 0x1400: s->cm_fclken_usbhost = value & 0x3; break;
+        case 0x1410: s->cm_iclken_usbhost = value & 0x1; break;
+        case 0x1430: s->cm_autoidle_usbhost = value & 0x1; break;
+        case 0x1444: s->cm_sleepdep_usbhost = value & 0x6; break;
+        case 0x1448: s->cm_clkstctrl_usbhost = value & 0x3; break;
+        /* unknown */
+        default: OMAP_BAD_REGV(addr, value); break;
     }
 }
 
diff --git a/hw/omap3_boot.c b/hw/omap3_boot.c
new file mode 100644 (file)
index 0000000..2626075
--- /dev/null
@@ -0,0 +1,831 @@
+/*
+ * TI OMAP3 boot ROM emulation. Based on information in the OMAP34xx 3.1
+ * Technical Reference Manual from Texas Instruments.
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "hw.h"
+#include "arm-misc.h"
+#include "omap.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "flash.h"
+#include "block.h"
+
+/* list of supported NAND devices according to the OMAP34xx TRM */
+static const struct {
+    uint8_t id;
+    uint32_t pagesize;
+    uint32_t capacity_Mb;
+} omap3_boot_nand_devices[] = {
+    {0xe6,  512,    64}, {0x33,  512,   128}, {0x73,  512,   128},
+    {0x43,  512,   128}, {0x53,  512,   128}, {0x35,  512,   256},
+    {0x75,  512,   256}, {0x45,  512,   256}, {0x55,  512,   256},
+    {0x36,  512,   512}, {0x76,  512,   512}, {0x46,  512,   512},
+    {0x56,  512,   512}, {0xa2, 2048,   512}, {0xf2, 2048,   512},
+    {0xb2, 2048,   512}, {0xc2, 2048,   512}, {0x39,  512,  1024},
+    {0x79,  512,  1024}, {0x49,  512,  1024}, {0x59,  512,  1024},
+    {0x78,  512,  1024}, {0x72,  512,  1024}, {0x74,  512,  1024},
+    {0xa1, 2048,  1024}, {0xf1, 2048,  1024}, {0xb1, 2048,  1024},
+    {0xc1, 2048,  1024}, {0xaa, 2048,  2048}, {0xda, 2048,  2048},
+    {0xba, 2048,  2048}, {0xca, 2048,  2048}, {0x71,  512,  2048},
+    {0x51,  512,  2048}, {0x31,  512,  2048}, {0x41,  512,  2048},
+    {0xac, 2048,  4096}, {0xdc, 2048,  4096}, {0xbc, 2048,  4096},
+    {0xcc, 2048,  4096}, {0xa3, 2048,  8192}, {0xd3, 2048,  8192},
+    {0xb3, 2048,  8192}, {0xc3, 2048,  8192}, {0xa5, 2048, 16384},
+    {0xd5, 2048, 16384}, {0xb5, 2048, 16384}, {0xc5, 2048, 16384},
+    {0xa7, 2048, 32768}, {0xb7, 2048, 32768}, {0xae, 2048, 65536},
+    {0xbe, 2048, 65536},
+    {0, 0, 0}
+};
+
+struct omap3_nand_boot_desc_s {
+    uint32_t pagesize;
+    uint32_t capacity_Mb;
+    uint8_t bus16;
+};
+
+/* first 80kB (reserved section) */
+static const uint8_t omap3_boot_rom_block1[] = { /* 0x40000000-0x40013fff */
+    /* this gets mapped to zero at power-up so let's have a branch
+     * table to the upper block ROM exception vectors here */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x40014000 */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x40014004 */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x40014008 */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x4001400c */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x40014010 */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x40014014 */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x40014018 */
+    0xfe, 0x4f, 0x00, 0xea, /* b 0x4001401c */
+};
+
+/* upper 32kB */
+static const uint8_t omap3_boot_rom_block2[] = { /* 0x40014000-0x4001bfff */
+    /* 0x40014000: ROM Exception vectors */
+    0x3e, 0x00, 0x00, 0xea, /* b 0x40014100 */
+    0x18, 0xf0, 0x9f, 0xe5, /* ldr pc, [pc, #0x18] */
+    0x18, 0xf0, 0x9f, 0xe5, /* ldr pc, [pc, #0x18] */
+    0x18, 0xf0, 0x9f, 0xe5, /* ldr pc, [pc, #0x18] */
+    0x18, 0xf0, 0x9f, 0xe5, /* ldr pc, [pc, #0x18] */
+    0x18, 0xf0, 0x9f, 0xe5, /* ldr pc, [pc, #0x18] */
+    0x18, 0xf0, 0x9f, 0xe5, /* ldr pc, [pc, #0x18] */
+    0x18, 0xf0, 0x9f, 0xe5, /* ldr pc, [pc, #0x18] */
+    /* 0x40014020: ROM CRC */
+    0xff, 0xff, 0xff, 0xff, 
+    /* 0x40014024: unused(?), we use it for some data */
+    0xc8, 0xff, 0x20, 0x40, /* 0x40014024: undef sram vector address */
+    0xcc, 0xff, 0x20, 0x40, /* 0x40014028: swi sram vector address */
+    0xd0, 0xff, 0x20, 0x40, /* 0x4001402c: pabt sram vector address */
+    0xd4, 0xff, 0x20, 0x40, /* 0x40014030: dabt sram vector address */
+    0xd8, 0xff, 0x20, 0x40, /* 0x40014034: unused sram vector address */
+    0xdc, 0xff, 0x20, 0x40, /* 0x40014038: irq sram vector address */
+    0xe0, 0xff, 0x20, 0x40, /* 0x4001403c: fiq sram vector address */
+    0xff, 0xff, 0xff, 0xff, /* 0x40014040: boot loader image start address */
+    0xff, 0xff, 0xff, 0xff, /* 0x40014044: booting parameter structure 0-3 */
+    0xff, 0xff, 0xff, 0xff, /* 0x40014048: booting parameter structure 4-7 */
+    0xff, 0xff, 0xff, 0xff, /* 0x4001404c: booting parameter structure 8-11 */
+    0x0e, 0xf0, 0xb0, 0xe1, /* 0x40014050: "movs pc, lr" */
+    0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    /* 0x40014080: Dead loops */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x40014080 @ undefined exception */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x40014084 @ swi exception */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x40014088 @ prefetch abort exception */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x4001408c @ data abort exception */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x40014090 @ unused exception */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x40014094 @ irq exception */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x40014098 @ fiq exception */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x4001409c @ validation tests pass */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140a0 @ validation tests fail */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140a4 @ boot failed: no more devices */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140a8 @ image not executed or returned */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140ac @ reserved */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140b0 @ reserved */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140b4 @ reserved */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140b8 @ reserved */
+    0xfe, 0xff, 0xff, 0xea, /* b 0x400140bc @ reserved */
+    /* 0x400140c0: should perform a software reset & jump to r0 */
+    0x00, 0xf0, 0xa0, 0xe1, /* mov pc, r0 */
+    0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    /* 0x40014100: code, ROM emulation uses this to launch the
+     * boot loader after it has been read into memory */
+    0xc8, 0x10, 0x1f, 0xe5, /* ldr r1, [#0x40014040] @ boot loader start */
+    0xb0, 0x0c, 0x0f, 0xe3, /* movw r0, #0xfcb0 */
+    0x20, 0x00, 0x44, 0xe3, /* movt r0, #0x4020   @ stack top at 0x4020fcb0 */
+    0xdf, 0xf0, 0x21, 0xe3, /* msr cpsr_c, #0xdf  @ enter SYS mode */
+    0x00, 0xd0, 0xa0, 0xe1, /* mov sp, r0 */
+    0x80, 0x0c, 0x40, 0xe2, /* sub r0, r0, #32768 @ 32kB SYS/USR stack */
+    0xd1, 0xf0, 0x21, 0xe3, /* msr cpsr_c, #0xd1  @ enter FIQ mode */
+    0x00, 0xd0, 0xa0, 0xe1, /* mov sp, r0 */
+    0x08, 0x0c, 0x40, 0xe2, /* sub r0, r0, #2048  @ 2kB FIQ stack */
+    0xd2, 0xf0, 0x21, 0xe3, /* msr cpsr_c, #0xd2  @ enter IRQ mode */
+    0x00, 0xd0, 0xa0, 0xe1, /* mov sp, r0 */
+    0x08, 0x0c, 0x40, 0xe2, /* sub r0, r0, #2048  @ 2kB IRQ stack */
+    0xd7, 0xf0, 0x21, 0xe3, /* msr cpsr_c, #0xd7  @ enter ABT mode */
+    0x00, 0xd0, 0xa0, 0xe1, /* mov sp, r0 */
+    0x08, 0x0c, 0x40, 0xe2, /* sub r0, r0, #2048  @ 2kB ABT stack */
+    0xdb, 0xf0, 0x21, 0xe3, /* msr cpsr_c, #0xdb  @ enter UND mode */
+    0x00, 0xd0, 0xa0, 0xe1, /* mov sp, r0 */
+    0x08, 0x0c, 0x40, 0xe2, /* sub r0, r0, #2048  @ 2kB UND stack */
+    0xd3, 0xf0, 0x21, 0xe3, /* msr cpsr_c, #0xd3  @ enter SVC mode */
+    0x00, 0xd0, 0xa0, 0xe1, /* mov sp, r0         @ 23kB left for SVC stack */
+    0x44, 0x00, 0x04, 0xe3, /* movw r0, #0x4044 */
+    0x01, 0x00, 0x44, 0xe3, /* movt r0, #0x4001   @ r0 -> booting parameter struct */
+    0x01, 0xf0, 0xa0, 0xe1, /* mov pc, r1 */
+};
+
+/* SRAM exception vectors, to be placed at 0x4020ffc8 */
+static const uint8_t omap3_sram_vectors[] = {
+    0x14, 0xf0, 0x9f, 0xe5, /* ldr pc, [#0x4020ffe4] @ undefined */
+    0x14, 0xf0, 0x9f, 0xe5, /* ldr pc, [#0x4020ffe8] @ swi */
+    0x14, 0xf0, 0x9f, 0xe5, /* ldr pc, [#0x4020ffec] @ prefetch abort */
+    0x14, 0xf0, 0x9f, 0xe5, /* ldr pc, [#0x4020fff0] @ data abort */
+    0x14, 0xf0, 0x9f, 0xe5, /* ldr pc, [#0x4020fff4] @ unused */
+    0x14, 0xf0, 0x9f, 0xe5, /* ldr pc, [#0x4020fff8] @ irq */
+    0x14, 0xf0, 0x9f, 0xe5, /* ldr pc, [#0x4020fffc] @ fiq */
+    0x80, 0x40, 0x01, 0x00, /* 0x14080 */
+    0x50, 0x40, 0x01, 0x40, /* 0x40014050 (default is 0x14084) */
+    0x88, 0x40, 0x01, 0x00, /* 0x14088 */
+    0x8c, 0x40, 0x01, 0x00, /* 0x1408c */
+    0x90, 0x40, 0x01, 0x00, /* 0x14090 */
+    0x94, 0x40, 0x01, 0x00, /* 0x14094 */
+    0x98, 0x40, 0x01, 0x00, /* 0x14098 */
+};
+
+static inline uint32_t omap3_get_le32(const void *p)
+{
+    const uint8_t *q = (const uint8_t *)p;
+    uint32_t v;
+    v = q[3]; v <<= 8;
+    v |= q[2]; v <<= 8;
+    v |= q[1]; v <<= 8;
+    v |= q[0];
+    return v;
+}
+
+static inline uint32_t omap3_get_le16(const void *p)
+{
+    const uint8_t *q = (const uint8_t *)p;
+    uint32_t v;
+    v = q[1]; v <<= 8;
+    v |= q[0];
+    return v;
+}
+
+static inline void omap3_boot_setlsb(target_phys_addr_t addr, uint16_t lsb)
+{
+    uint8_t x[4];
+    
+    cpu_physical_memory_read(addr, x, 4);
+    x[0] = lsb & 0xff;
+    x[1] = (lsb >> 8) & 0xff;
+    cpu_physical_memory_write(addr, x, 4);
+}
+
+typedef enum {
+    xip = 1,
+    nand,
+    onenand,
+    doc,
+    mmc2,
+    mmc1,
+    xipwait,
+    uart = 0x10,
+    hsusb,
+}  omap3_boot_device_t;
+
+struct omap3_boot_s {
+    struct omap_mpu_state_s *mpu;
+    omap3_boot_device_t devicetype;
+    enum {
+        undefined = 0,
+        confighdr,
+        chdone,
+        imagehdr,
+        copy,
+        done
+    } state;
+    uint8_t chflags;
+    target_phys_addr_t addr;
+    uint32_t count;
+};
+
+static struct omap3_boot_s *omap3_boot_init(struct omap_mpu_state_s *mpu,
+                                            omap3_boot_device_t dtype,
+                                            const uint8_t *data,
+                                            uint32_t data_len)
+{
+    struct omap3_boot_s *s = qemu_mallocz(sizeof(struct omap3_boot_s));
+    s->mpu = mpu;
+    s->devicetype = dtype;
+    s->state = chdone;
+    if (data_len >= 512) {
+        if (!strncasecmp((char *)(data + 0x14), "chsettings", 10)
+            || !strncasecmp((char *)(data + 0x14), "chram", 5)
+            || !strncasecmp((char *)(data + 0x14), "chflash", 7)
+            || !strncasecmp((char *)(data + 0x14), "chmmcsd", 7))
+            s->state = confighdr;
+    }
+    return s;
+}
+
+static void omap3_boot_chsettings(struct omap3_boot_s *boot,
+                                  const uint8_t *chtoc)
+{
+    uint32_t flags, x;
+    
+    if (omap3_get_le32(chtoc) != 0xc0c0c0c1) {
+        fprintf(stderr, "%s: invalid section verification key\n", __FUNCTION__);
+        return;
+    }
+    if (!chtoc[4]) { /* section disabled? */
+        return;
+    }
+    if (omap3_get_le16(chtoc + 5) != 0x0001) {
+        fprintf(stderr, "%s: unsupported CH version (0x%04x)\n", __FUNCTION__,
+                omap3_get_le16(chtoc));
+        return;
+    }
+    boot->chflags |= 0x01;
+    flags = omap3_get_le32(chtoc + 8);
+    chtoc += 12;
+    if (flags & 1) {
+        cpu_physical_memory_write(0x48307270, chtoc + 0x00, 4); /* PRM_CLKSRC_CTRL */
+        cpu_physical_memory_write(0x48306d40, chtoc + 0x04, 4); /* PRM_CLKSEL */
+        cpu_physical_memory_write(0x48005140, chtoc + 0x08, 4); /* CM_CLKSEL1_EMU */
+        if (flags & (1 << 2)) { /* clock configuration */
+            cpu_physical_memory_write(0x48004a40, chtoc + 0x0c, 4); /* CM_CLKSEL_CORE */
+            cpu_physical_memory_write(0x48004c40, chtoc + 0x10, 4); /* CM_CLKSEL_WKUP */
+        }
+        if (flags & (1 << 5)) { /* DPLL3 CORE */
+            if (flags & (1 << 8)) { /* enable DPLL3 bypass */
+                cpu_physical_memory_read(0x48004d00, (uint8_t *)&x, 4);
+                x &= ~7; x |= 5; /* set DPLL3 bypass */
+                cpu_physical_memory_write(0x48004d00, (uint8_t *)&x, 4);
+            }
+            cpu_physical_memory_write(0x48004d00, chtoc + 0x14, 4); /* CM_CLKEN_PLL */
+            cpu_physical_memory_write(0x48004d30, chtoc + 0x18, 4); /* CM_AUTOIDLE_PLL */
+            cpu_physical_memory_write(0x48004d40, chtoc + 0x1c, 4); /* CM_CLKSEL1_PLL */
+        }
+        if (flags & (1 << 3)) { /* DPLL4 PER */
+            if (flags & (1 << 6)) { /* enable DPLL4 bypass */
+                cpu_physical_memory_read(0x48004d00, (uint8_t *)&x, 4);
+                x &= ~0x70000; x |= 0x10000; /* set DPLL4 in stop mode */
+                cpu_physical_memory_write(0x48004d00, (uint8_t *)&x, 4);
+            }
+            cpu_physical_memory_write(0x48004d00, chtoc + 0x20, 4); /* CM_CLKEN_PLL */
+            cpu_physical_memory_write(0x48004d30, chtoc + 0x24, 4); /* CM_AUTOIDLE_PLL */
+            cpu_physical_memory_write(0x48004d44, chtoc + 0x28, 4); /* CM_CLKSEL2_PLL */
+            cpu_physical_memory_write(0x48004d48, chtoc + 0x2c, 4); /* CM_CLKSEL3_PLL */
+        }
+        if (flags & (1 << 3)) { /* DPLL1 MPU */
+            if (flags & (1 << 7)) { /* enable DPLL1 bypass */
+                cpu_physical_memory_read(0x48004904, (uint8_t *)&x, 4);
+                x &= ~7; x |= 5; /* set DPLL1 bypass */
+                cpu_physical_memory_write(0x48004904, (uint8_t *)&x, 4);
+            }
+            cpu_physical_memory_write(0x48004904, chtoc + 0x30, 4); /* CM_CLKEN_PLL_MPU */
+            cpu_physical_memory_write(0x48004934, chtoc + 0x34, 4); /* CM_AUTOIDLE_PLL_MPU */
+            cpu_physical_memory_write(0x48004940, chtoc + 0x38, 4); /* CM_CLKSEL1_PLL_MPU */
+            cpu_physical_memory_write(0x48004944, chtoc + 0x3c, 4); /* CM_CLKSEL2_PLL_MPU */
+            cpu_physical_memory_write(0x48004948, chtoc + 0x40, 4); /* CM_CLKSTCTRL_MPU */
+        }
+        switch ((flags >> 24) & 0xff) {
+            case 0x01: x = 0; break; /* 12MHz */
+            case 0x02: x = 1; break; /* 13MHz */
+            case 0x03: x = 5; break; /* 16.8MHz */
+            case 0x04: x = 2; break; /* 19.2MHz */
+            case 0x05: x = 3; break; /* 26MHz */
+            case 0x06: x = 4; break; /* 38.4MHz */
+            default:
+                fprintf(stderr, "%s: unsupported SYS.CLK setting\n", __FUNCTION__);
+                x = 1;
+                break;
+        }
+        if (x != omap3_get_le32(chtoc + 0x04)) {
+            fprintf(stderr, "%s: mismatch in SYS.CLK id and PRM_CLKSEL value\n", __FUNCTION__);
+        }
+    }
+}
+
+static void omap3_boot_chram(struct omap3_boot_s *boot,
+                             const uint8_t *chtoc)
+{
+    if (omap3_get_le32(chtoc) != 0xc0c0c0c2) {
+        fprintf(stderr, "%s: invalid section verification key\n", __FUNCTION__);
+        return;
+    }
+    if (!chtoc[4]) { /* section disabled? */
+        return;
+    }
+    boot->chflags |= 0x02;
+    omap3_boot_setlsb(0x6d000040, omap3_get_le16(chtoc + 0x0a)); /* SDRC_CS_CFG */
+    omap3_boot_setlsb(0x6d000044, omap3_get_le16(chtoc + 0x0c)); /* SDRC_SHARING */
+    cpu_physical_memory_write(0x6d000060, chtoc + 0x10, 4);      /* SDRC_DLLA_CTRL */
+    
+    cpu_physical_memory_write(0x6d000080, chtoc + 0x20, 4);      /* SDRC_MCFG_0 */
+    omap3_boot_setlsb(0x6d000084, omap3_get_le16(chtoc + 0x24)); /* SDRC_MR_0 */
+    omap3_boot_setlsb(0x6d000088, omap3_get_le16(chtoc + 0x26)); /* SDRC_EMR1_0? */
+    omap3_boot_setlsb(0x6d00008c, omap3_get_le16(chtoc + 0x28)); /* SDRC_EMR2_0 */
+    omap3_boot_setlsb(0x6d000090, omap3_get_le16(chtoc + 0x2a)); /* SDRC_EMR3_0? */
+    cpu_physical_memory_write(0x6d00009c, chtoc + 0x2c, 4);      /* SDRC_ACTIM_CTRLA_0 */
+    cpu_physical_memory_write(0x6d0000a0, chtoc + 0x30, 4);      /* SDRC_ACTIM_CTRLB_0 */
+    cpu_physical_memory_write(0x6d0000a4, chtoc + 0x34, 4);      /* SDRC_RFR_CTRL_0 */
+    
+    cpu_physical_memory_write(0x6d0000b0, chtoc + 0x20, 4);      /* SDRC_MCFG_1 */
+    omap3_boot_setlsb(0x6d0000b4, omap3_get_le16(chtoc + 0x24)); /* SDRC_MR_1 */
+    omap3_boot_setlsb(0x6d0000b8, omap3_get_le16(chtoc + 0x26)); /* SDRC_EMR1_1? */
+    omap3_boot_setlsb(0x6d0000bc, omap3_get_le16(chtoc + 0x28)); /* SDRC_EMR2_1 */
+    omap3_boot_setlsb(0x6d0000c0, omap3_get_le16(chtoc + 0x2a)); /* SDRC_EMR3_1? */
+    cpu_physical_memory_write(0x6d0000cc, chtoc + 0x2c, 4);      /* SDRC_ACTIM_CTRLA_1 */
+    cpu_physical_memory_write(0x6d0000d0, chtoc + 0x30, 4);      /* SDRC_ACTIM_CTRLB_1 */
+    cpu_physical_memory_write(0x6d0000d4, chtoc + 0x34, 4);      /* SDRC_RFR_CTRL_1 */
+}
+
+static void omap3_boot_chflash(struct omap3_boot_s *boot,
+                               const uint8_t *chtoc)
+{
+    if (omap3_get_le32(chtoc) != 0xc0c0c0c3) {
+        fprintf(stderr, "%s: invalid section verification key\n", __FUNCTION__);
+        return;
+    }
+    if (!chtoc[4]) { /* section disabled? */
+        return;
+    }
+    boot->chflags |= 0x04;
+    omap3_boot_setlsb(0x6e000010, omap3_get_le16(chtoc + 0x08)); /* GPMC_SYSCONFIG */
+    omap3_boot_setlsb(0x6e00001c, omap3_get_le16(chtoc + 0x0a)); /* GPMC_IRQENABLE */
+    omap3_boot_setlsb(0x6e000040, omap3_get_le16(chtoc + 0x0c)); /* GPMC_TIMEOUT_CONTROL */
+    omap3_boot_setlsb(0x6e000050, omap3_get_le16(chtoc + 0x0e)); /* GPMC_CONFIG */
+    cpu_physical_memory_write(0x6e000060, chtoc + 0x10, 4);      /* GPMC_CONFIG1_0 */
+    cpu_physical_memory_write(0x6e000064, chtoc + 0x14, 4);      /* GPMC_CONFIG2_0 */
+    cpu_physical_memory_write(0x6e000068, chtoc + 0x18, 4);      /* GPMC_CONFIG3_0 */
+    cpu_physical_memory_write(0x6e00006c, chtoc + 0x1c, 4);      /* GPMC_CONFIG4_0 */
+    cpu_physical_memory_write(0x6e000070, chtoc + 0x20, 4);      /* GPMC_CONFIG5_0 */
+    cpu_physical_memory_write(0x6e000074, chtoc + 0x24, 4);      /* GPMC_CONFIG6_0 */
+    cpu_physical_memory_write(0x6e000078, chtoc + 0x28, 4);      /* GPMC_CONFIG7_0 */
+    cpu_physical_memory_write(0x6e0001e0, chtoc + 0x2c, 4);      /* GPMC_PREFETCH_CONFIG1 */
+    omap3_boot_setlsb(0x6e0001e4, omap3_get_le16(chtoc + 0x30)); /* GPMC_PREFETCH_CONFIG2 */
+    omap3_boot_setlsb(0x6e0001ec, omap3_get_le16(chtoc + 0x32)); /* GPMC_PREFETCH_CONTROL */
+    /* TODO: ECC config registers. The TRM spec is not clear on these */
+}
+
+static void omap3_boot_chmmcsd(struct omap3_boot_s *boot,
+                               const uint8_t *chtoc)
+{
+    if (omap3_get_le32(chtoc) != 0xc0c0c0c4) {
+        fprintf(stderr, "%s: invalid section verification key\n", __FUNCTION__);
+        return;
+    }
+    if (!chtoc[4]) { /* section disabled? */
+        return;
+    }
+    boot->chflags |= 0x08;
+    /* TODO: MMCHS registers */
+}
+
+/* returns non-zero if more blocks are needed */
+static uint32_t omap3_boot_block(const uint8_t *data,
+                                 uint32_t data_len,
+                                 struct omap3_boot_s *s)
+{
+    const uint8_t *p = 0;
+    uint32_t i = 0;
+    
+    switch (s->state) {
+        case confighdr:
+            i = data_len;
+            for (p = data; i >= 32 && omap3_get_le32(p) != 0xffffffff; p += 32, i -= 32) {
+                if (!strcasecmp((char *)(p + 0x14), "chsettings"))
+                    omap3_boot_chsettings(s, p + omap3_get_le32(p));
+                else if (!strcasecmp((char *)(p + 0x14), "chram"))
+                    omap3_boot_chram(s, p + omap3_get_le32(p));
+                else if (!strcasecmp((char *)(p + 0x14), "chflash"))
+                    omap3_boot_chflash(s, p + omap3_get_le32(p));
+                else if (!strcasecmp((char *)(p + 0x14), "chmmcsd"))
+                    omap3_boot_chmmcsd(s, p + omap3_get_le32(p));
+                else
+                    fprintf(stderr, "%s: unknown CHTOC item \"%s\"\n",
+                            __FUNCTION__, (char *)(p + 0x14));
+            }
+            data += 512;
+            data_len -= 512;
+            s->state = chdone;
+            /* fallthrough */
+        case chdone:
+            s->state = imagehdr;
+            /* fallthrough */
+        case imagehdr:
+            if (!data_len)
+                return 1;
+            if (data_len < 8)
+                break;
+            s->count = omap3_get_le32(data);
+            s->addr = omap3_get_le32(data + 4);
+            if (!s->count || (s->count >> 24) || !s->addr || s->addr == 0xffffffff)
+                break;
+            /* patch image start address in boot ROM */
+            cpu_physical_memory_write_rom(0x40014040, data + 4, 4);
+            /* start copying image */
+            data += 8;
+            data_len -= 8;
+            s->state = copy;
+            /* fallthrough */
+        case copy:
+            i = (s->count >= data_len) ? data_len : s->count;
+            cpu_physical_memory_write(s->addr, data, i);
+            s->addr += i;
+            s->count -= i;
+            if (!s->count)
+                s->state = done;
+            return s->count;
+        default:
+            break;
+    }
+    return 0;
+}
+
+/* returns non-zero if boot has finished succesfully */
+static int omap3_boot_finish(struct omap3_boot_s *s)
+{
+    uint8_t x[12] = {
+        0, 0, 0, 0, /* last received booting message */
+        (uint8_t)s->devicetype,
+        0,
+        1, /* POR */
+        s->chflags,
+        0, 0, 0, 0 /* device descriptor */
+    };
+    int result = (s->state == done);
+
+    if (result) {
+        /* fill in the booting parameter structure */
+        cpu_physical_memory_write_rom(0x40014044, x, 12);
+    }
+    free(s);
+    return result;
+}
+
+/* returns ptr to matching dir entry / zero entry or 0 if unsuccessful */
+static const uint8_t *omap3_scan_fat_dir_sector(const uint8_t *s)
+{
+    int i;
+    
+    /* there are 0x10 items with 0x20 bytes per item */
+    for (i = 0x10; i--; s += 0x20) {
+        if (*s == 0xe5 || (s[0x0b] & 0x0f) == 0x0f) continue; /* erased/LFN */
+        if (!*s || !strncasecmp((void *)s, "mlo        ", 8+3)) return s;
+    }
+    return 0;
+}
+
+struct omap3_fat_drv_s {
+    BlockDriverState *bs;
+    uint8_t ptype; /* 12, 16, 32 */
+    uint64_t c0;   /* physical byte offset for data cluster 0 */
+    uint64_t fat;  /* physical byte offset for used FAT sector 0 */
+    uint32_t spc;  /* sectors per cluster */
+};
+
+/* returns cluster data in the buffer and next cluster chain number
+ or 0 if unsuccessful */
+static uint32_t omap3_read_fat_cluster(uint8_t *data,
+                                       struct omap3_fat_drv_s *drv,
+                                       uint32_t cl)
+{
+    uint8_t buf[ 4 ];
+    uint32_t len = drv->spc * 0x200; /* number of bytes to read */
+    
+    switch (drv->ptype) { /* check for EOF */
+        case 12: if (cl > 0xff0) return 0; break;
+        case 16: if (cl > 0xfff0) return 0; break;
+        case 32: if (cl > 0x0ffffff0) return 0; break;
+        default: return 0;
+    }
+    
+    if (bdrv_pread(drv->bs, 
+                   drv->c0 + ((drv->ptype == 32 ? cl - 2 : cl) * len),
+                   data, len) != len)
+        return 0;
+    
+    switch (drv->ptype) { /* determine next cluster # */
+        case 12:
+            fprintf(stderr, "%s: FAT12 parsing not implemented!\n",
+                    __FUNCTION__);
+            break;
+        case 16:
+            return (bdrv_pread(drv->bs, drv->fat + cl * 2, buf, 2) != 2)
+            ? 0 : omap3_get_le16(buf);
+        case 32:
+            return (bdrv_pread(drv->bs, drv->fat + cl * 4, buf, 4) != 4)
+            ? 0 : omap3_get_le32(buf) & 0x0fffffff;
+        default:
+            break;
+    }
+    return 0;
+}
+
+static int omap3_mmc_fat_boot(BlockDriverState *bs,
+                              uint8_t *sector,
+                              uint32_t pstart,
+                              struct omap_mpu_state_s *mpu)
+{
+    struct omap3_fat_drv_s drv;
+    struct omap3_boot_s *boot;
+    uint32_t i, j, cluster0, fatsize, bootsize, rootsize;
+    const uint8_t *p, *q;
+    uint8_t *cluster;
+    int result = 0;
+    
+    /* determine FAT type */
+    
+    drv.bs = bs;
+    fatsize = omap3_get_le16(sector + 0x16);
+    if (!fatsize) 
+        fatsize = omap3_get_le32(sector + 0x24);
+    bootsize = omap3_get_le16(sector + 0x0e);
+    cluster0 = bootsize + fatsize * sector[0x10];
+    rootsize = omap3_get_le16(sector + 0x11);
+    if (rootsize & 0x0f)
+        rootsize += 0x10;
+    rootsize >>= 4;
+    drv.spc = sector[0x0d];
+    i = omap3_get_le16(sector + 0x13);
+    if (!i)
+        i = omap3_get_le32(sector + 0x20);
+    i = (i - (cluster0 + rootsize)) / drv.spc;
+    drv.ptype = (i < 4085) ? 12 : (i < 65525) ? 16 : 32;
+    
+    /* search for boot loader file */
+    
+    drv.fat = (bootsize + pstart) * 0x200;
+    drv.c0 = (cluster0 + pstart) * 0x200;
+    if (drv.ptype == 32) {
+        i = omap3_get_le32(sector + 0x2c); /* first root cluster # */
+        j = omap3_get_le16(sector + 0x28);
+        if (j & 0x80)
+            drv.fat += (j & 0x0f) * fatsize * 0x200;
+        cluster = qemu_mallocz(drv.spc * 0x200);
+        for (p = 0; !p && (i = omap3_read_fat_cluster(cluster, &drv, i)); ) {
+            for (j = drv.spc, q=cluster; j-- & !p; q += 0x200)
+                p = omap3_scan_fat_dir_sector(q);
+            if (p) 
+                memcpy(sector, q - 0x200, 0x200); /* save the sector */
+        }
+        free(cluster);
+    } else { /* FAT12/16 */
+        for (i = rootsize, j = 0, p = 0; i-- && !p; j++) {
+            if (bdrv_pread(drv.bs, drv.c0 + j * 0x200, sector, 0x200) != 0x200)
+                break;
+            p = omap3_scan_fat_dir_sector(sector);
+        }
+    }
+    
+    if (p && *p) { /* did we indeed find the file? */
+        i = omap3_get_le16(p + 0x14);
+        i <<= 16;
+        i |= omap3_get_le16(p + 0x1a);
+        j = drv.spc * 0x200;
+        uint8 *data = qemu_mallocz(j);
+        if ((i = omap3_read_fat_cluster(data, &drv, i))) {
+            boot = omap3_boot_init(mpu, mmc1, data, j);
+            boot->state = imagehdr; /* override CH detection */
+            while (omap3_boot_block(data, j, boot))
+                i = omap3_read_fat_cluster(data, &drv, i);
+            result = omap3_boot_finish(boot);
+        } else
+            fprintf(stderr, "%s: unable to read MLO file contents from SD card\n",
+                    __FUNCTION__);
+        free(data);
+    } else
+        fprintf(stderr, "%s: MLO file not found in the root directory\n",
+                __FUNCTION__);
+    
+    return result;
+}
+
+static int omap3_mmc_raw_boot(BlockDriverState *bs,
+                              uint8_t *sector,
+                              struct omap_mpu_state_s *mpu)
+{
+    struct omap3_boot_s *boot;
+    uint32_t i = 0;
+    int result = 0;
+    
+    if (bdrv_pread(bs, 0, sector, 0x200) == 0x200) {
+        boot = omap3_boot_init(mpu, mmc1, sector, 0x200);
+        if (boot->state == confighdr) { /* CH must be present for raw boot */
+            while (omap3_boot_block(sector, 0x200, boot)) {
+                if (bdrv_pread(bs, ++i, sector, 0x200) != 0x200) {
+                    fprintf(stderr, "%s: error trying to read sector %u on boot device\n",
+                            __FUNCTION__, i);
+                    break;
+                }
+            }
+        }
+        result = (boot->state == done);
+        free(boot);
+    }
+    return result;
+}
+
+/* returns non-zero if successful, zero if unsuccessful */
+static int omap3_mmc_boot(struct omap_mpu_state_s *s)
+{
+    BlockDriverState *bs;
+    int sdindex = drive_get_index(IF_SD, 0, 0);
+    uint8_t *sector, *p;
+    uint32_t pstart, i;
+    int result = 0;
+    
+    /* very simple implementation for GP device boot,
+     supports only two modes:
+     1. MBR partition table with an active FAT partition
+     and boot loader file (MLO) in its root directory, or
+     2. CH sector located on first sector, followed by boot loader image */
+    if (sdindex >= 0) {
+        bs = drives_table[sdindex].bdrv;
+        sector = qemu_mallocz(0x200);
+        if (bdrv_pread(bs, 0, sector, 0x200) == 0x200) {
+            for (i = 0, p = sector + 0x1be; i < 4; i++, p += 0x10) 
+                if (p[0] == 0x80) break;
+            if (sector[0x1fe] == 0x55 && sector[0x1ff] == 0xaa /* signature */
+                && i < 4 /* active partition exists */
+                && (p[4] == 1 || p[4] == 4 || p[4] == 6 || p[4] == 11
+                    || p[4] == 12 || p[4] == 14 || p[4] == 15) /* FAT */
+                && bdrv_pread(bs, (pstart = omap3_get_le32(p + 8)) * 0x200,
+                              sector, 0x200) == 0x200
+                && sector[0x1fe] == 0x55 && sector[0x1ff] == 0xaa)
+                result = omap3_mmc_fat_boot(bs, sector, pstart, s);
+            else
+                result = omap3_mmc_raw_boot(bs, sector, s);
+        }
+        free(sector);
+    }
+    return result;
+}
+
+static inline void omap3_nand_sendcmd(struct omap3_nand_boot_desc_s *nd,
+                                      uint8_t cmd)
+{
+    uint8_t x[2] = { cmd, 0 };
+    
+    cpu_physical_memory_write(0x6e00007c, x, nd->bus16 ? 2 : 1);
+}
+
+static inline void omap3_nand_sendaddr_byte(struct omap3_nand_boot_desc_s *nd,
+                                            uint8_t a)
+{
+    uint8_t x[2] = { a, 0 };
+    
+    cpu_physical_memory_write(0x6e000080, x, nd->bus16 ? 2 : 1);
+}
+
+static inline uint8_t omap3_nand_readbyte(struct omap3_nand_boot_desc_s *nd)
+{
+    uint8_t x[2];
+    
+    cpu_physical_memory_read(0x6e000084, x, nd->bus16 ? 2 : 1);
+    return x[0];
+}
+
+static inline void omap3_nand_readpage(struct omap3_nand_boot_desc_s *nd,
+                                       uint32_t pageaddr,
+                                       uint8_t *data)
+{
+    uint32_t i;
+    
+    omap3_nand_sendcmd(nd, 0x00); /* read page */
+    omap3_nand_sendaddr_byte(nd, 0x00);
+    if (nd->pagesize >= 2048) {
+        omap3_nand_sendaddr_byte(nd, 0x00);
+        omap3_nand_sendaddr_byte(nd, (uint8_t)(pageaddr & 0xff));
+        omap3_nand_sendaddr_byte(nd, (uint8_t)((pageaddr >> 8) & 0xff));
+        if (nd->capacity_Mb >= 2048)
+            omap3_nand_sendaddr_byte(nd, (uint8_t)((pageaddr >> 16) & 0xff));
+        omap3_nand_sendcmd(nd, 0x30); /* confirm read */
+    } else {
+        omap3_nand_sendaddr_byte(nd, (uint8_t)(pageaddr & 0xff));
+        omap3_nand_sendaddr_byte(nd, (uint8_t)((pageaddr >> 8) & 0xff));
+    }
+    if (nd->bus16) {
+        for (i = nd->pagesize / 2; i--; data += 2)
+            cpu_physical_memory_read(0x6e000084, data, 2);
+    } else {
+        for (i = nd->pagesize; i--; data++)
+            cpu_physical_memory_read(0x6e000084, data, 1);
+    }
+}
+
+/* returns non-zero if successful, zero if unsuccessful */
+static int omap3_nand_boot(struct omap_mpu_state_s *mpu, int bus16)
+{
+    struct omap3_nand_boot_desc_s *nd;
+    struct omap3_boot_s *boot;
+    uint8_t *data;
+    uint32_t page = 0;
+    int result = 0, i;
+    uint8_t id[4];
+    
+    /* TODO: support bad block marks */
+    nd = qemu_mallocz(sizeof(struct omap3_nand_boot_desc_s));
+    nd->bus16 = bus16;
+    omap3_nand_sendcmd(nd, 0xff); /* reset */
+    omap3_nand_sendcmd(nd, 0x90); /* read id */
+    omap3_nand_sendaddr_byte(nd, 0);
+    id[0] = omap3_nand_readbyte(nd); /* manufacturer id */
+    id[1] = omap3_nand_readbyte(nd); /* device id */
+    id[2] = omap3_nand_readbyte(nd); /* don't care */
+    id[3] = omap3_nand_readbyte(nd); /* attributes */
+    for (i = 0; omap3_boot_nand_devices[i].id; i++) {
+        if (omap3_boot_nand_devices[i].id == id[1]) {
+            nd->capacity_Mb = omap3_boot_nand_devices[i].capacity_Mb;
+            if (nd->capacity_Mb > 1024)
+                nd->pagesize = 1024 * (1 << (id[3] & 3));
+            else
+                nd->pagesize = omap3_boot_nand_devices[i].pagesize;
+            break;
+        }
+    }
+    /* TODO: if device is not recognized at this state, we should
+     * issue READ ID2 command to the device and get device parameters
+     * from there */
+    if (nd->pagesize) {
+        data = qemu_mallocz(nd->pagesize);
+        /* TODO: scan through 4 first blocks for image */
+        omap3_nand_readpage(nd, 0, data);
+        boot = omap3_boot_init(mpu, nand, data, nd->pagesize);
+        while (omap3_boot_block(data, nd->pagesize, boot))
+            omap3_nand_readpage(nd, ++page, data);
+        result = omap3_boot_finish(boot);
+        free(data);
+    }
+    free(nd);
+    return result;
+}
+
+
+void omap3_boot_rom_emu(struct omap_mpu_state_s *s)
+{
+    const uint8_t rom_version[4] = { 0x00, 0x14, 0x00, 0x00 }; /* v. 14.00 */
+    uint8_t x[4] = {0, 0, 0, 0};
+    int result = 0;
+    
+    cpu_physical_memory_write_rom(OMAP3_Q1_BASE,
+                                  omap3_boot_rom_block1,
+                                  sizeof(omap3_boot_rom_block1));
+    cpu_physical_memory_write_rom(OMAP3_Q1_BASE + 0x14000,
+                                  omap3_boot_rom_block2,
+                                  sizeof(omap3_boot_rom_block2));
+    cpu_physical_memory_write_rom(OMAP3_Q1_BASE + 0x1bffc,
+                                  rom_version,
+                                  sizeof(rom_version));
+    cpu_physical_memory_write(OMAP3_SRAM_BASE + 0xffc8,
+                              omap3_sram_vectors,
+                              sizeof(omap3_sram_vectors));
+
+    /* if we have NAND in GPMC CS0, try to read boot loader from there.
+     * here we are relying on all memories to be attached and gpmc_attach
+     * to fill in DEVICETYPE field correctly for CS0 for us */
+    cpu_physical_memory_read(0x6e000060, x, 4); /* GPMC_CONFIG1_0 */
+    if (((x[1] >> 2) & 3) == 2) /* DEVICETYPE == NAND? */
+        result = omap3_nand_boot(s, ((x[1] >> 4) & 3) == 1);
+
+    /* TODO: support OneNAND and XIP */
+    
+    /* if no boot loader found yet, try the MMC/SD card... */
+    if (!result)
+        result = omap3_mmc_boot(s);
+    
+    if (!result) { /* no boot device found */
+        /* move PC to the appropriate ROM dead loop address */
+        s->env->regs[15] = 0x400140a4;
+        /* ...on second thought, let's just call it a day and quit */
+        cpu_abort(s->env, "no boot device found");
+    }
+}