Commit 51fec3cc7eb7a9c8e1be2f2bb971db303d17ea61
1 parent
74b9decc
Omap DPLL & APLL locking logic.
Reset I2C fifo on new transfers. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4919 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
121 additions
and
17 deletions
hw/omap2.c
| ... | ... | @@ -2727,6 +2727,8 @@ struct omap_prcm_s { |
| 2727 | 2727 | |
| 2728 | 2728 | uint32_t ev; |
| 2729 | 2729 | uint32_t evtime[2]; |
| 2730 | + | |
| 2731 | + int dpll_lock, apll_lock[2]; | |
| 2730 | 2732 | }; |
| 2731 | 2733 | |
| 2732 | 2734 | static void omap_prcm_int_update(struct omap_prcm_s *s, int dom) |
| ... | ... | @@ -2739,6 +2741,7 @@ static uint32_t omap_prcm_read(void *opaque, target_phys_addr_t addr) |
| 2739 | 2741 | { |
| 2740 | 2742 | struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; |
| 2741 | 2743 | int offset = addr - s->base; |
| 2744 | + uint32_t ret; | |
| 2742 | 2745 | |
| 2743 | 2746 | switch (offset) { |
| 2744 | 2747 | case 0x000: /* PRCM_REVISION */ |
| ... | ... | @@ -2922,14 +2925,17 @@ static uint32_t omap_prcm_read(void *opaque, target_phys_addr_t addr) |
| 2922 | 2925 | case 0x500: /* CM_CLKEN_PLL */ |
| 2923 | 2926 | return s->clken[9]; |
| 2924 | 2927 | case 0x520: /* CM_IDLEST_CKGEN */ |
| 2925 | - /* Core uses 32-kHz clock */ | |
| 2928 | + ret = 0x0000070 | (s->apll_lock[0] << 9) | (s->apll_lock[1] << 8); | |
| 2926 | 2929 | if (!(s->clksel[6] & 3)) |
| 2927 | - return 0x00000377; | |
| 2928 | - /* DPLL not in lock mode, core uses ref_clk */ | |
| 2929 | - if ((s->clken[9] & 3) != 3) | |
| 2930 | - return 0x00000375; | |
| 2931 | - /* Core uses DPLL */ | |
| 2932 | - return 0x00000376; | |
| 2930 | + /* Core uses 32-kHz clock */ | |
| 2931 | + ret |= 3 << 0; | |
| 2932 | + else if (!s->dpll_lock) | |
| 2933 | + /* DPLL not locked, core uses ref_clk */ | |
| 2934 | + ret |= 1 << 0; | |
| 2935 | + else | |
| 2936 | + /* Core uses DPLL */ | |
| 2937 | + ret |= 2 << 0; | |
| 2938 | + return ret; | |
| 2933 | 2939 | case 0x530: /* CM_AUTOIDLE_PLL */ |
| 2934 | 2940 | return s->clkidle[5]; |
| 2935 | 2941 | case 0x540: /* CM_CLKSEL1_PLL */ |
| ... | ... | @@ -2976,6 +2982,69 @@ static uint32_t omap_prcm_read(void *opaque, target_phys_addr_t addr) |
| 2976 | 2982 | return 0; |
| 2977 | 2983 | } |
| 2978 | 2984 | |
| 2985 | +static void omap_prcm_apll_update(struct omap_prcm_s *s) | |
| 2986 | +{ | |
| 2987 | + int mode[2]; | |
| 2988 | + | |
| 2989 | + mode[0] = (s->clken[9] >> 6) & 3; | |
| 2990 | + s->apll_lock[0] = (mode[0] == 3); | |
| 2991 | + mode[1] = (s->clken[9] >> 2) & 3; | |
| 2992 | + s->apll_lock[1] = (mode[1] == 3); | |
| 2993 | + /* TODO: update clocks */ | |
| 2994 | + | |
| 2995 | + if (mode[0] == 1 || mode[0] == 2 || mode[1] == 1 || mode[2] == 2) | |
| 2996 | + fprintf(stderr, "%s: bad EN_54M_PLL or bad EN_96M_PLL\n", | |
| 2997 | + __FUNCTION__); | |
| 2998 | +} | |
| 2999 | + | |
| 3000 | +static void omap_prcm_dpll_update(struct omap_prcm_s *s) | |
| 3001 | +{ | |
| 3002 | + omap_clk dpll = omap_findclk(s->mpu, "dpll"); | |
| 3003 | + omap_clk dpll_x2 = omap_findclk(s->mpu, "dpll"); | |
| 3004 | + omap_clk core = omap_findclk(s->mpu, "core_clk"); | |
| 3005 | + int mode = (s->clken[9] >> 0) & 3; | |
| 3006 | + int mult, div; | |
| 3007 | + | |
| 3008 | + mult = (s->clksel[5] >> 12) & 0x3ff; | |
| 3009 | + div = (s->clksel[5] >> 8) & 0xf; | |
| 3010 | + if (mult == 0 || mult == 1) | |
| 3011 | + mode = 1; /* Bypass */ | |
| 3012 | + | |
| 3013 | + s->dpll_lock = 0; | |
| 3014 | + switch (mode) { | |
| 3015 | + case 0: | |
| 3016 | + fprintf(stderr, "%s: bad EN_DPLL\n", __FUNCTION__); | |
| 3017 | + break; | |
| 3018 | + case 1: /* Low-power bypass mode (Default) */ | |
| 3019 | + case 2: /* Fast-relock bypass mode */ | |
| 3020 | + omap_clk_setrate(dpll, 1, 1); | |
| 3021 | + omap_clk_setrate(dpll_x2, 1, 1); | |
| 3022 | + break; | |
| 3023 | + case 3: /* Lock mode */ | |
| 3024 | + s->dpll_lock = 1; /* After 20 FINT cycles (ref_clk / (div + 1)). */ | |
| 3025 | + | |
| 3026 | + omap_clk_setrate(dpll, div + 1, mult); | |
| 3027 | + omap_clk_setrate(dpll_x2, div + 1, mult * 2); | |
| 3028 | + break; | |
| 3029 | + } | |
| 3030 | + | |
| 3031 | + switch ((s->clksel[6] >> 0) & 3) { | |
| 3032 | + case 0: | |
| 3033 | + omap_clk_reparent(core, omap_findclk(s->mpu, "clk32-kHz")); | |
| 3034 | + break; | |
| 3035 | + case 1: | |
| 3036 | + omap_clk_reparent(core, dpll); | |
| 3037 | + break; | |
| 3038 | + case 2: | |
| 3039 | + /* Default */ | |
| 3040 | + omap_clk_reparent(core, dpll_x2); | |
| 3041 | + break; | |
| 3042 | + case 3: | |
| 3043 | + fprintf(stderr, "%s: bad CORE_CLK_SRC\n", __FUNCTION__); | |
| 3044 | + break; | |
| 3045 | + } | |
| 3046 | +} | |
| 3047 | + | |
| 2979 | 3048 | static void omap_prcm_write(void *opaque, target_phys_addr_t addr, |
| 2980 | 3049 | uint32_t value) |
| 2981 | 3050 | { |
| ... | ... | @@ -3235,20 +3304,44 @@ static void omap_prcm_write(void *opaque, target_phys_addr_t addr, |
| 3235 | 3304 | break; |
| 3236 | 3305 | |
| 3237 | 3306 | case 0x500: /* CM_CLKEN_PLL */ |
| 3238 | - s->clken[9] = value & 0xcf; | |
| 3239 | - /* TODO update clocks */ | |
| 3307 | + if (value & 0xffffff30) | |
| 3308 | + fprintf(stderr, "%s: write 0s in CM_CLKEN_PLL for " | |
| 3309 | + "future compatiblity\n", __FUNCTION__); | |
| 3310 | + if ((s->clken[9] ^ value) & 0xcc) { | |
| 3311 | + s->clken[9] &= ~0xcc; | |
| 3312 | + s->clken[9] |= value & 0xcc; | |
| 3313 | + omap_prcm_apll_update(s); | |
| 3314 | + } | |
| 3315 | + if ((s->clken[9] ^ value) & 3) { | |
| 3316 | + s->clken[9] &= ~3; | |
| 3317 | + s->clken[9] |= value & 3; | |
| 3318 | + omap_prcm_dpll_update(s); | |
| 3319 | + } | |
| 3240 | 3320 | break; |
| 3241 | 3321 | case 0x530: /* CM_AUTOIDLE_PLL */ |
| 3242 | 3322 | s->clkidle[5] = value & 0x000000cf; |
| 3243 | 3323 | /* TODO update clocks */ |
| 3244 | 3324 | break; |
| 3245 | 3325 | case 0x540: /* CM_CLKSEL1_PLL */ |
| 3326 | + if (value & 0xfc4000d7) | |
| 3327 | + fprintf(stderr, "%s: write 0s in CM_CLKSEL1_PLL for " | |
| 3328 | + "future compatiblity\n", __FUNCTION__); | |
| 3329 | + if ((s->clksel[5] ^ value) & 0x003fff00) { | |
| 3330 | + s->clksel[5] = value & 0x03bfff28; | |
| 3331 | + omap_prcm_dpll_update(s); | |
| 3332 | + } | |
| 3333 | + /* TODO update the other clocks */ | |
| 3334 | + | |
| 3246 | 3335 | s->clksel[5] = value & 0x03bfff28; |
| 3247 | - /* TODO update clocks */ | |
| 3248 | 3336 | break; |
| 3249 | 3337 | case 0x544: /* CM_CLKSEL2_PLL */ |
| 3250 | - s->clksel[6] = value & 3; | |
| 3251 | - /* TODO update clocks */ | |
| 3338 | + if (value & ~3) | |
| 3339 | + fprintf(stderr, "%s: write 0s in CM_CLKSEL2_PLL[31:2] for " | |
| 3340 | + "future compatiblity\n", __FUNCTION__); | |
| 3341 | + if (s->clksel[6] != (value & 3)) { | |
| 3342 | + s->clksel[6] = value & 3; | |
| 3343 | + omap_prcm_dpll_update(s); | |
| 3344 | + } | |
| 3252 | 3345 | break; |
| 3253 | 3346 | |
| 3254 | 3347 | case 0x800: /* CM_FCLKEN_DSP */ |
| ... | ... | @@ -3373,6 +3466,8 @@ static void omap_prcm_reset(struct omap_prcm_s *s) |
| 3373 | 3466 | s->power[3] = 0x14; |
| 3374 | 3467 | s->rstctrl[0] = 1; |
| 3375 | 3468 | s->rst[3] = 1; |
| 3469 | + omap_prcm_apll_update(s); | |
| 3470 | + omap_prcm_dpll_update(s); | |
| 3376 | 3471 | } |
| 3377 | 3472 | |
| 3378 | 3473 | static void omap_prcm_coldreset(struct omap_prcm_s *s) | ... | ... |
hw/omap_clk.c
| ... | ... | @@ -510,18 +510,25 @@ static struct clk clk32k = { |
| 510 | 510 | .parent = &xtal_osc32k, |
| 511 | 511 | }; |
| 512 | 512 | |
| 513 | +static struct clk ref_clk = { | |
| 514 | + .name = "ref_clk", | |
| 515 | + .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, | |
| 516 | + .rate = 12000000, /* 12 MHz or 13 MHz or 19.2 MHz */ | |
| 517 | + /*.parent = sys.xtalin */ | |
| 518 | +}; | |
| 519 | + | |
| 513 | 520 | static struct clk apll_96m = { |
| 514 | 521 | .name = "apll_96m", |
| 515 | 522 | .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, |
| 516 | 523 | .rate = 96000000, |
| 517 | - /*.parent = sys.xtalin */ | |
| 524 | + /*.parent = ref_clk */ | |
| 518 | 525 | }; |
| 519 | 526 | |
| 520 | 527 | static struct clk apll_54m = { |
| 521 | 528 | .name = "apll_54m", |
| 522 | 529 | .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, |
| 523 | 530 | .rate = 54000000, |
| 524 | - /*.parent = sys.xtalin */ | |
| 531 | + /*.parent = ref_clk */ | |
| 525 | 532 | }; |
| 526 | 533 | |
| 527 | 534 | static struct clk sys_clk = { |
| ... | ... | @@ -541,13 +548,13 @@ static struct clk sleep_clk = { |
| 541 | 548 | static struct clk dpll_ck = { |
| 542 | 549 | .name = "dpll", |
| 543 | 550 | .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, |
| 544 | - /*.parent = sys.xtalin */ | |
| 551 | + .parent = &ref_clk, | |
| 545 | 552 | }; |
| 546 | 553 | |
| 547 | 554 | static struct clk dpll_x2_ck = { |
| 548 | 555 | .name = "dpll_x2", |
| 549 | 556 | .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, |
| 550 | - /*.parent = sys.xtalin */ | |
| 557 | + .parent = &ref_clk, | |
| 551 | 558 | }; |
| 552 | 559 | |
| 553 | 560 | static struct clk wdt1_sys_clk = { |
| ... | ... | @@ -600,7 +607,7 @@ static struct clk sys_clkout2 = { |
| 600 | 607 | static struct clk core_clk = { |
| 601 | 608 | .name = "core_clk", |
| 602 | 609 | .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, |
| 603 | - .parent = &dpll_ck, | |
| 610 | + .parent = &dpll_x2_ck, /* Switchable between dpll_ck and clk32k */ | |
| 604 | 611 | }; |
| 605 | 612 | |
| 606 | 613 | static struct clk l3_clk = { |
| ... | ... | @@ -1009,6 +1016,7 @@ static struct clk *onchip_clks[] = { |
| 1009 | 1016 | |
| 1010 | 1017 | /* OMAP 2 */ |
| 1011 | 1018 | |
| 1019 | + &ref_clk, | |
| 1012 | 1020 | &apll_96m, |
| 1013 | 1021 | &apll_54m, |
| 1014 | 1022 | &sys_clk, | ... | ... |
hw/omap_i2c.c
| ... | ... | @@ -395,6 +395,7 @@ static void omap_i2c_write(void *opaque, target_phys_addr_t addr, |
| 395 | 395 | (~value >> 9) & 1); /* TRX */ |
| 396 | 396 | s->stat |= nack << 1; /* NACK */ |
| 397 | 397 | s->control &= ~(1 << 0); /* STT */ |
| 398 | + s->fifo = 0; | |
| 398 | 399 | if (nack) |
| 399 | 400 | s->control &= ~(1 << 1); /* STP */ |
| 400 | 401 | else { | ... | ... |