// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include zx_status_t s905d2_hiu_init(zx_handle_t bti, aml_hiu_dev_t* device) { zx_handle_t resource = get_root_resource(); zx_status_t status; status = io_buffer_init_physical(&device->regs_iobuff, bti, S905D2_HIU_BASE, S905D2_HIU_LENGTH, resource, ZX_CACHE_POLICY_UNCACHED_DEVICE); if (status != ZX_OK) { zxlogf(ERROR, "%s: io_buffer_init_physical failed %d\n", __func__, status); return status; } device->virt_regs = (zx_vaddr_t)(io_buffer_virt(&device->regs_iobuff)); return ZX_OK; } static zx_status_t s905d2_pll_init_regs(aml_pll_dev_t* pll_dev) { aml_hiu_dev_t* device = pll_dev->hiu; if (pll_dev->pll_num == HIFI_PLL) { //write config values hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL1, G12A_HIFI_PLL_CNTL1); hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL2, G12A_HIFI_PLL_CNTL2); hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL3, G12A_HIFI_PLL_CNTL3); hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL4, G12A_HIFI_PLL_CNTL4); hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL5, G12A_HIFI_PLL_CNTL5); hiu_clk_set_reg(device, HHI_HIFI_PLL_CNTL6, G12A_HIFI_PLL_CNTL6); zx_nanosleep(zx_deadline_after(ZX_USEC(10))); return ZX_OK; } else if (pll_dev->pll_num == SYS_PLL) { //write config values hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL1, G12A_SYS_PLL_CNTL1); hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL2, G12A_SYS_PLL_CNTL2); hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL3, G12A_SYS_PLL_CNTL3); hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL4, G12A_SYS_PLL_CNTL4); hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL5, G12A_SYS_PLL_CNTL5); hiu_clk_set_reg(device, HHI_SYS_PLL_CNTL6, G12A_SYS_PLL_CNTL6); zx_nanosleep(zx_deadline_after(ZX_USEC(10))); return ZX_OK; } else if (pll_dev->pll_num == GP0_PLL) { //write config values hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL1, G12A_GP0_PLL_CNTL1); hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL2, G12A_GP0_PLL_CNTL2); hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL3, G12A_GP0_PLL_CNTL3); hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL4, G12A_GP0_PLL_CNTL4); hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL5, G12A_GP0_PLL_CNTL5); hiu_clk_set_reg(device, HHI_GP0_PLL_CNTL6, G12A_GP0_PLL_CNTL6); zx_nanosleep(zx_deadline_after(ZX_USEC(10))); return ZX_OK; } return ZX_OK; } zx_status_t s905d2_pll_init(aml_hiu_dev_t* device, aml_pll_dev_t* pll_dev, hhi_plls_t pll_num) { ZX_DEBUG_ASSERT(device); ZX_DEBUG_ASSERT(pll_dev); pll_dev->hiu = device; pll_dev->rate_table = s905d2_pll_get_rate_table(pll_num); pll_dev->rate_idx = 0; pll_dev->frequency = 0; pll_dev->pll_num = pll_num; pll_dev->rate_count = s905d2_get_rate_table_count(pll_num); ZX_DEBUG_ASSERT(pll_dev->rate_table); ZX_DEBUG_ASSERT(pll_dev->rate_count); //Disable and reset the pll s905d2_pll_disable(pll_dev); //Write configuration registers return s905d2_pll_init_regs(pll_dev); } bool s905d2_pll_disable(aml_pll_dev_t* pll_dev) { uint32_t offs = hiu_get_pll_offs(pll_dev); uint32_t ctl0 = hiu_clk_get_reg(pll_dev->hiu, offs); bool retval = ctl0 & HHI_PLL_CNTL0_EN; ctl0 = (ctl0 & ~HHI_PLL_CNTL0_EN) | HHI_PLL_CNTL0_RESET; hiu_clk_set_reg(pll_dev->hiu, offs, ctl0); return retval; } zx_status_t s905d2_pll_ena(aml_pll_dev_t* pll_dev) { ZX_DEBUG_ASSERT(pll_dev); uint32_t offs = hiu_get_pll_offs(pll_dev); uint32_t reg_val = hiu_clk_get_reg(pll_dev->hiu, offs); // Set Enable bit reg_val |= HHI_PLL_CNTL0_EN; hiu_clk_set_reg(pll_dev->hiu, offs, reg_val); zx_nanosleep(zx_deadline_after(ZX_USEC(50))); // Clear Reset bit reg_val &= ~HHI_PLL_CNTL0_RESET; hiu_clk_set_reg(pll_dev->hiu, offs, reg_val); uint32_t wait_count = 100; while (wait_count) { if (hiu_clk_get_reg(pll_dev->hiu, offs) & HHI_PLL_LOCK) { return ZX_OK; } zx_nanosleep(zx_deadline_after(ZX_USEC(10))); wait_count--; } return ZX_ERR_TIMED_OUT; } /* Notes: -VCO needs to be between 3-6GHz per the datasheet. It appears that if you provide values which would result in a VCO outside of this range, it will still oscillate, but at unknown (but likely close to target) frequency. */ zx_status_t s905d2_pll_set_rate(aml_pll_dev_t* pll_dev, uint64_t freq) { ZX_DEBUG_ASSERT(pll_dev); const hhi_pll_rate_t* pll_rate; zx_status_t status = s905d2_pll_fetch_rate(pll_dev, freq, &pll_rate); if (status != ZX_OK) { return status; } //Disable/reset the pll, save previous state bool ena = s905d2_pll_disable(pll_dev); //Initialize the registers to defaults (may not be retained after reset) s905d2_pll_init_regs(pll_dev); uint32_t offs = hiu_get_pll_offs(pll_dev); uint32_t ctl0 = hiu_clk_get_reg(pll_dev->hiu, offs); ctl0 &= ~HHI_PLL_CNTL0_M; ctl0 |= pll_rate->m << HHI_PLL_CNTL0_M_SHIFT; ctl0 &= ~HHI_PLL_CNTL0_N; ctl0 |= pll_rate->n << HHI_PLL_CNTL0_N_SHIFT; ctl0 &= ~HHI_PLL_CNTL0_OD; ctl0 |= pll_rate->od << HHI_PLL_CNTL0_OD_SHIFT; hiu_clk_set_reg(pll_dev->hiu, offs, ctl0); hiu_clk_set_reg(pll_dev->hiu, offs + 4, pll_rate->frac); if (ena) { return s905d2_pll_ena(pll_dev); } return ZX_OK; }