static u_int rk3188_cpu_set_rate(u_int rate) { const struct rk3188_apll_rate *r = NULL; uint32_t apll_con0, apll_con1, apll_con2, clksel0_con, clksel1_con; uint32_t reset_mask, reset, status0_reg, status0_apll_lock; u_int cpu_aclk_div_con; u_int old_rate = rk3188_cpu_get_rate(); u_int new_rate; #if NACT8846PM > 0 device_t pmic; struct act8846_ctrl *dcdc3; pmic = device_find_by_driver_unit("act8846pm", 0); if (pmic == NULL) { printf("%s: no PMIC driver found\n", __func__); return ENXIO; } dcdc3 = act8846_lookup(pmic, "DCDC3"); KASSERT(dcdc3 != NULL); #endif #ifdef ROCKCHIP_CLOCK_DEBUG printf("%s: rate=%u\n", __func__, rate); #endif /* Pick the closest rate (nearest 100MHz increment) */ for (int i = 0; i < __arraycount(rk3188_apll_rates); i++) { u_int arate = ((rk3188_apll_rates[i].rate / 1000000) + 50) / 100 * 100; if (arate <= rate) { r = &rk3188_apll_rates[i]; break; } } if (r == NULL) { #ifdef ROCKCHIP_CLOCK_DEBUG printf("CPU: No matching rate found for %u MHz\n", rate); #endif return EINVAL; } switch (rockchip_chip_id()) { case ROCKCHIP_CHIP_ID_RK3066: case ROCKCHIP_CHIP_ID_RK3188PLUS: reset_mask = CRU_PLL_CON3_RESET_MASK; reset = CRU_PLL_CON3_RESET; apll_con0 = CRU_PLL_CON0_CLKR_MASK | CRU_PLL_CON0_CLKOD_MASK; apll_con0 |= __SHIFTIN(r->nr - 1, CRU_PLL_CON0_CLKR); apll_con0 |= __SHIFTIN(r->no - 1, CRU_PLL_CON0_CLKOD); apll_con1 = CRU_PLL_CON1_CLKF_MASK; apll_con1 |= __SHIFTIN(r->nf - 1, CRU_PLL_CON1_CLKF); apll_con2 = CRU_PLL_CON2_BWADJ_MASK; apll_con2 |= __SHIFTIN(r->nf >> 1, CRU_PLL_CON2_BWADJ); break; case ROCKCHIP_CHIP_ID_RK3188: reset_mask = CRU_PLL_CON3_POWER_DOWN_MASK; reset = CRU_PLL_CON3_POWER_DOWN; apll_con0 = CRU_PLL_CON0_CLKR_MASK | RK3188_CRU_PLL_CON0_CLKOD_MASK; apll_con0 |= __SHIFTIN(r->nr - 1, CRU_PLL_CON0_CLKR); apll_con0 |= __SHIFTIN(r->no - 1, RK3188_CRU_PLL_CON0_CLKOD); apll_con1 = RK3188_CRU_PLL_CON1_CLKF_MASK; apll_con1 |= __SHIFTIN(r->nf - 1, RK3188_CRU_PLL_CON1_CLKF); apll_con2 = 0; break; default: return EINVAL; } switch (r->core_axi_div) { case 1: cpu_aclk_div_con = 0; break; case 2: cpu_aclk_div_con = 1; break; case 3: cpu_aclk_div_con = 2; break; case 4: cpu_aclk_div_con = 3; break; case 8: cpu_aclk_div_con = 4; break; default: panic("bad core_axi_div"); } switch (rockchip_chip_id()) { case ROCKCHIP_CHIP_ID_RK3066: clksel0_con = CRU_CLKSEL_CON0_A9_CORE_DIV_CON_MASK | CRU_CLKSEL_CON0_CORE_PERI_DIV_CON_MASK; clksel0_con |= __SHIFTIN(r->core_div - 1, CRU_CLKSEL_CON0_A9_CORE_DIV_CON); clksel0_con |= __SHIFTIN(ffs(r->core_periph_div) - 2, CRU_CLKSEL_CON0_CORE_PERI_DIV_CON); clksel1_con = CRU_CLKSEL_CON1_AHB2APB_PCLKEN_DIV_CON_MASK | CRU_CLKSEL_CON1_CPU_PCLK_DIV_CON_MASK | CRU_CLKSEL_CON1_CPU_HCLK_DIV_CON_MASK | CRU_CLKSEL_CON1_CPU_ACLK_DIV_CON_MASK; clksel1_con |= __SHIFTIN(ffs(r->ahb2apb_div) - 1, CRU_CLKSEL_CON1_AHB2APB_PCLKEN_DIV_CON); clksel1_con |= __SHIFTIN(ffs(r->hclk_div) - 1, CRU_CLKSEL_CON1_CPU_HCLK_DIV_CON); clksel1_con |= __SHIFTIN(ffs(r->pclk_div) - 1, CRU_CLKSEL_CON1_CPU_PCLK_DIV_CON); clksel1_con |= __SHIFTIN(cpu_aclk_div_con, CRU_CLKSEL_CON1_CPU_ACLK_DIV_CON); status0_reg = GRF_STATUS0_REG; status0_apll_lock = GRF_STATUS0_APLL_LOCK; break; case ROCKCHIP_CHIP_ID_RK3188: case ROCKCHIP_CHIP_ID_RK3188PLUS: clksel0_con = RK3188_CRU_CLKSEL_CON0_A9_CORE_DIV_CON_MASK | CRU_CLKSEL_CON0_CORE_PERI_DIV_CON_MASK; clksel0_con |= __SHIFTIN(r->core_div - 1, RK3188_CRU_CLKSEL_CON0_A9_CORE_DIV_CON); clksel0_con |= __SHIFTIN(ffs(r->core_periph_div) - 2, CRU_CLKSEL_CON0_CORE_PERI_DIV_CON); clksel1_con = CRU_CLKSEL_CON1_AHB2APB_PCLKEN_DIV_CON_MASK | CRU_CLKSEL_CON1_CPU_PCLK_DIV_CON_MASK | CRU_CLKSEL_CON1_CPU_HCLK_DIV_CON_MASK | RK3188_CRU_CLKSEL_CON1_CPU_ACLK_DIV_CON_MASK; clksel1_con |= __SHIFTIN(ffs(r->ahb2apb_div) - 1, CRU_CLKSEL_CON1_AHB2APB_PCLKEN_DIV_CON); clksel1_con |= __SHIFTIN(ffs(r->hclk_div) - 1, CRU_CLKSEL_CON1_CPU_HCLK_DIV_CON); clksel1_con |= __SHIFTIN(ffs(r->pclk_div) - 1, CRU_CLKSEL_CON1_CPU_PCLK_DIV_CON); clksel1_con |= __SHIFTIN(cpu_aclk_div_con, RK3188_CRU_CLKSEL_CON1_CPU_ACLK_DIV_CON); status0_reg = RK3188_GRF_STATUS0_REG; status0_apll_lock = RK3188_GRF_STATUS0_APLL_LOCK; break; default: return EINVAL; } #ifdef ROCKCHIP_CLOCK_DEBUG printf("%s: Set frequency to %u MHz...\n", __func__, r->rate); printf("before: APLL_CON0: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_APLL_CON0_REG)); printf("before: APLL_CON1: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_APLL_CON1_REG)); printf("before: CLKSEL0_CON: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_CLKSEL_CON_REG(0))); printf("before: CLKSEL1_CON: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_CLKSEL_CON_REG(1))); #endif new_rate = r->rate / 1000000; if (new_rate > old_rate) { #if NACT8846PM > 0 act8846_set_voltage(dcdc3, r->voltage, r->voltage); #endif } bus_space_write_4(bst, cru_bsh, CRU_MODE_CON_REG, CRU_MODE_CON_APLL_WORK_MODE_MASK | __SHIFTIN(CRU_MODE_CON_APLL_WORK_MODE_SLOW, CRU_MODE_CON_APLL_WORK_MODE)); /* Power down */ bus_space_write_4(bst, cru_bsh, CRU_APLL_CON3_REG, reset_mask | reset); /* Update APLL regs */ bus_space_write_4(bst, cru_bsh, CRU_APLL_CON0_REG, apll_con0); bus_space_write_4(bst, cru_bsh, CRU_APLL_CON1_REG, apll_con1); if (apll_con2) bus_space_write_4(bst, cru_bsh, CRU_APLL_CON2_REG, apll_con2); for (volatile int i = 5000; i >= 0; i--) ; /* Power up */ bus_space_write_4(bst, cru_bsh, CRU_APLL_CON3_REG, reset_mask); /* Wait for PLL lock */ #ifdef ROCKCHIP_CLOCK_DEBUG printf("%s: Waiting for PLL lock...\n", __func__); #endif for (volatile int i = 50000; i >= 0; i--) ; int retry = ROCKCHIP_REF_FREQ; while (--retry > 0) { uint32_t status = bus_space_read_4(bst, grf_bsh, status0_reg); if (status & status0_apll_lock) break; } if (retry == 0) printf("%s: PLL lock timeout\n", __func__); /* Update CLKSEL regs */ bus_space_write_4(bst, cru_bsh, CRU_CLKSEL_CON_REG(0), clksel0_con); bus_space_write_4(bst, cru_bsh, CRU_CLKSEL_CON_REG(1), clksel1_con); /* Slow -> Normal mode */ bus_space_write_4(bst, cru_bsh, CRU_MODE_CON_REG, CRU_MODE_CON_APLL_WORK_MODE_MASK | __SHIFTIN(CRU_MODE_CON_APLL_WORK_MODE_NORMAL, CRU_MODE_CON_APLL_WORK_MODE)); #ifdef ROCKCHIP_CLOCK_DEBUG printf("after: APLL_CON0: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_APLL_CON0_REG)); printf("after: APLL_CON1: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_APLL_CON1_REG)); printf("after: CLKSEL0_CON: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_CLKSEL_CON_REG(0))); printf("after: CLKSEL1_CON: %#x\n", bus_space_read_4(bst, cru_bsh, CRU_CLKSEL_CON_REG(1))); #endif a9tmr_update_freq(rockchip_a9periph_get_rate()); #if NACT8846PM > 0 if (new_rate < old_rate) { act8846_set_voltage(dcdc3, r->voltage, r->voltage); } #endif return 0; }
static u_int meson8b_cpu_set_rate(u_int rate) { const uint32_t xtal_rate = amlogic_get_rate_xtal(); const u_int old_rate = meson8b_cpu_get_rate(); u_int new_rate = 0; for (int i = 0; i < __arraycount(meson8b_rates); i++) { if (meson8b_rates[i] == rate) { new_rate = meson8b_rates[i] * 1000000; break; } } if (new_rate == 0) { return EINVAL; } if (old_rate == new_rate) { return 0; } uint32_t cntl0 = CBUS_READ(HHI_SYS_CPU_CLK_CNTL0_REG); uint32_t cntl = CBUS_READ(HHI_SYS_PLL_CNTL_REG); u_int new_mul = new_rate / xtal_rate; u_int new_div = 1; u_int new_od = 0; if (new_rate < 600 * 1000000) { new_od = 2; new_mul *= 4; } else if (new_rate < 1200 * 1000000) { new_od = 1; new_mul *= 2; } /* * XXX make some assumptions about the state of cpu clk cntl regs */ if ((cntl0 & HHI_SYS_CPU_CLK_CNTL0_CLKSEL) == 0) return EIO; if (__SHIFTOUT(cntl0, HHI_SYS_CPU_CLK_CNTL0_PLLSEL) != 1) return EIO; if (__SHIFTOUT(cntl0, HHI_SYS_CPU_CLK_CNTL0_SOUTSEL) != 0) return EIO; cntl &= ~HHI_SYS_PLL_CNTL_MUL; cntl |= __SHIFTIN(new_mul, HHI_SYS_PLL_CNTL_MUL); cntl &= ~HHI_SYS_PLL_CNTL_DIV; cntl |= __SHIFTIN(new_div, HHI_SYS_PLL_CNTL_DIV); cntl &= ~HHI_SYS_PLL_CNTL_OD; cntl |= __SHIFTIN(new_od, HHI_SYS_PLL_CNTL_OD); /* Switch CPU to XTAL clock */ CBUS_SET_CLEAR(HHI_SYS_CPU_CLK_CNTL0_REG, 0, HHI_SYS_CPU_CLK_CNTL0_CLKSEL); delay((100 * old_rate) / xtal_rate); /* Update multiplier */ do { CBUS_WRITE(HHI_SYS_PLL_CNTL_REG, cntl); /* Switch CPU to sys pll */ CBUS_SET_CLEAR(HHI_SYS_CPU_CLK_CNTL0_REG, HHI_SYS_CPU_CLK_CNTL0_CLKSEL, 0); delay((500 * old_rate) / new_rate); } while (!(CBUS_READ(HHI_SYS_PLL_CNTL_REG) & HHI_SYS_PLL_CNTL_LOCK)); if (!cold) { a9tmr_update_freq(amlogic_get_rate_a9periph()); } return 0; }