From: Juha Riihimäki Date: Fri, 13 Mar 2009 14:20:42 +0000 (+0200) Subject: OMAP3 DPLL5 clock fix and added the missing omap3_boot.c. X-Git-Tag: 0.10.0-0maemo4~46 X-Git-Url: http://vcs.maemo.org/git/?a=commitdiff_plain;h=ad67a5a42085008e5fe780be3f4801cfd70ac194;p=qemu OMAP3 DPLL5 clock fix and added the missing omap3_boot.c. --- diff --git a/hw/omap3.c b/hw/omap3.c index 9abceaf..403e584 100644 --- a/hw/omap3.c +++ b/hw/omap3.c @@ -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 index 0000000..2626075 --- /dev/null +++ b/hw/omap3_boot.c @@ -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"); + } +}