/* SPDX-License-Identifier: BSD-3-Clause */ #include <stdint.h> #include <delay.h> #include <console/console.h> #include <soc/clock.h> #include <soc/lcc-reg.h> #include <device/mmio.h> typedef struct { void *gcc_apcs_regs; void *lcc_pll0_regs; void *lcc_ahbix_regs; void *lcc_mi2s_regs; void *lcc_pll_regs; } Ipq806xLccClocks; typedef struct __packed { uint32_t apcs; } Ipq806xLccGccRegs; typedef struct __packed { uint32_t mode; uint32_t l_val; uint32_t m_val; uint32_t n_val; uint32_t UNUSED; uint32_t config; uint32_t status; } Ipq806xLccPll0Regs; typedef struct __packed { uint32_t ns; uint32_t md; uint32_t UNUSED; uint32_t status; } Ipq806xLccAhbixRegs; typedef struct __packed { uint32_t ns; uint32_t md; uint32_t status; } Ipq806xLccMi2sRegs; typedef struct __packed { uint32_t pri; uint32_t sec; } Ipq806xLccPllRegs; struct lcc_freq_tbl { unsigned int freq; unsigned int pd; unsigned int m; unsigned int n; unsigned int d; }; static const struct lcc_freq_tbl lcc_mi2s_freq_tbl[] = { { 1024000, 4, 1, 96, 8 }, { 1411200, 4, 2, 139, 8 }, { 1536000, 4, 1, 64, 8 }, { 2048000, 4, 1, 48, 8 }, { 2116800, 4, 2, 93, 8 }, { 2304000, 4, 2, 85, 8 }, { 2822400, 4, 6, 209, 8 }, { 3072000, 4, 1, 32, 8 }, { 3175200, 4, 1, 31, 8 }, { 4096000, 4, 1, 24, 8 }, { 4233600, 4, 9, 209, 8 }, { 4608000, 4, 3, 64, 8 }, { 5644800, 4, 12, 209, 8 }, { 6144000, 4, 1, 16, 8 }, { 6350400, 4, 2, 31, 8 }, { 8192000, 4, 1, 12, 8 }, { 8467200, 4, 18, 209, 8 }, { 9216000, 4, 3, 32, 8 }, { 11289600, 4, 24, 209, 8 }, { 12288000, 4, 1, 8, 8 }, { 12700800, 4, 27, 209, 8 }, { 13824000, 4, 9, 64, 8 }, { 16384000, 4, 1, 6, 8 }, { 16934400, 4, 41, 238, 8 }, { 18432000, 4, 3, 16, 8 }, { 22579200, 2, 24, 209, 8 }, { 24576000, 4, 1, 4, 8 }, { 27648000, 4, 9, 32, 8 }, { 33868800, 4, 41, 119, 8 }, { 36864000, 4, 3, 8, 8 }, { 45158400, 1, 24, 209, 8 }, { 49152000, 4, 1, 2, 8 }, { 50803200, 1, 27, 209, 8 }, { } }; static int lcc_init_enable_pll0(Ipq806xLccClocks *bus) { Ipq806xLccGccRegs *gcc_regs = bus->gcc_apcs_regs; Ipq806xLccPll0Regs *pll0_regs = bus->lcc_pll0_regs; Ipq806xLccPllRegs *pll_regs = bus->lcc_pll_regs; uint32_t regval; regval = 0; regval = 15 << LCC_PLL0_L_SHIFT & LCC_PLL0_L_MASK; write32(&pll0_regs->l_val, regval); regval = 0; regval = 145 << LCC_PLL0_M_SHIFT & LCC_PLL0_M_MASK; write32(&pll0_regs->m_val, regval); regval = 0; regval = 199 << LCC_PLL0_N_SHIFT & LCC_PLL0_N_MASK; write32(&pll0_regs->n_val, regval); regval = 0; regval |= LCC_PLL0_CFG_LV_MAIN_ENABLE; regval |= LCC_PLL0_CFG_FRAC_ENABLE; write32(&pll0_regs->config, regval); regval = 0; regval |= LCC_PLL_PCLK_SRC_PRI; write32(&pll_regs->pri, regval); regval = 0; regval |= 1 << LCC_PLL0_MODE_BIAS_CNT_SHIFT & LCC_PLL0_MODE_BIAS_CNT_MASK; regval |= 8 << LCC_PLL0_MODE_LOCK_CNT_SHIFT & LCC_PLL0_MODE_LOCK_CNT_MASK; write32(&pll0_regs->mode, regval); regval = read32(&gcc_regs->apcs); regval |= GCC_PLL_APCS_PLL4_ENABLE; write32(&gcc_regs->apcs, regval); regval = read32(&pll0_regs->mode); regval |= LCC_PLL0_MODE_FSM_VOTE_ENABLE; write32(&pll0_regs->mode, regval); mdelay(1); regval = read32(&pll0_regs->status); if (regval & LCC_PLL0_STAT_ACTIVE_MASK) return 0; printk(BIOS_ERR, "%s: error enabling PLL4 clock\n", __func__); return 1; } static int lcc_init_enable_ahbix(Ipq806xLccClocks *bus) { Ipq806xLccAhbixRegs *ahbix_regs = bus->lcc_ahbix_regs; uint32_t regval; regval = 0; regval |= 1 << LCC_AHBIX_MD_M_VAL_SHIFT & LCC_AHBIX_MD_M_VAL_MASK; regval |= 252 << LCC_AHBIX_MD_NOT_2D_VAL_SHIFT & LCC_AHBIX_MD_NOT_2D_VAL_MASK; write32(&ahbix_regs->md, regval); regval = 0; regval |= 253 << LCC_AHBIX_NS_N_VAL_SHIFT & LCC_AHBIX_NS_N_VAL_MASK; regval |= LCC_AHBIX_NS_CRC_ENABLE; regval |= LCC_AHBIX_NS_GFM_SEL_MNC; regval |= LCC_AHBIX_NS_MNC_CLK_ENABLE; regval |= LCC_AHBIX_NS_MNC_ENABLE; regval |= LCC_AHBIX_NS_MNC_MODE_DUAL; regval |= LCC_AHBIX_NS_PREDIV_BYPASS; regval |= LCC_AHBIX_NS_MN_SRC_LPA; write32(&ahbix_regs->ns, regval); mdelay(1); regval = read32(&ahbix_regs->status); if (regval & LCC_AHBIX_STAT_AIF_CLK_MASK) return 0; printk(BIOS_ERR, "%s: error enabling AHBIX clock\n", __func__); return 1; } static int lcc_init_mi2s(Ipq806xLccClocks *bus, unsigned int freq) { Ipq806xLccMi2sRegs *mi2s_regs = bus->lcc_mi2s_regs; uint32_t regval; uint8_t pd, m, n, d; unsigned int i; i = 0; while (lcc_mi2s_freq_tbl[i].freq != 0) { if (lcc_mi2s_freq_tbl[i].freq == freq) break; ++i; } if (lcc_mi2s_freq_tbl[i].freq == 0) { printk(BIOS_ERR, "%s: invalid frequency given: %u\n", __func__, freq); return 1; } switch (lcc_mi2s_freq_tbl[i].pd) { case 1: pd = LCC_MI2S_NS_PREDIV_BYPASS; break; case 2: pd = LCC_MI2S_NS_PREDIV_DIV2; break; case 4: pd = LCC_MI2S_NS_PREDIV_DIV4; break; default: printk(BIOS_ERR, "%s: invalid prediv found: %u\n", __func__, lcc_mi2s_freq_tbl[i].pd); return 1; } m = lcc_mi2s_freq_tbl[i].m; n = ~(lcc_mi2s_freq_tbl[i].n - m); d = ~(lcc_mi2s_freq_tbl[i].d * 2); regval = 0; regval |= m << LCC_MI2S_MD_M_VAL_SHIFT & LCC_MI2S_MD_M_VAL_MASK; regval |= d << LCC_MI2S_MD_NOT_2D_VAL_SHIFT & LCC_MI2S_MD_NOT_2D_VAL_MASK; write32(&mi2s_regs->md, regval); regval = 0; regval |= n << LCC_MI2S_NS_N_VAL_SHIFT & LCC_MI2S_NS_N_VAL_MASK; regval |= LCC_MI2S_NS_BIT_DIV_DIV4; regval |= LCC_MI2S_NS_MNC_CLK_ENABLE; regval |= LCC_MI2S_NS_MNC_ENABLE; regval |= LCC_MI2S_NS_MNC_MODE_DUAL; regval |= pd; regval |= LCC_MI2S_NS_MN_SRC_LPA; write32(&mi2s_regs->ns, regval); return 0; } static int lcc_enable_mi2s(Ipq806xLccClocks *bus) { Ipq806xLccMi2sRegs *mi2s_regs = bus->lcc_mi2s_regs; uint32_t regval; regval = read32(&mi2s_regs->ns); regval |= LCC_MI2S_NS_OSR_CXC_ENABLE; regval |= LCC_MI2S_NS_BIT_CXC_ENABLE; write32(&mi2s_regs->ns, regval); udelay(10); regval = read32(&mi2s_regs->status); if (regval & LCC_MI2S_STAT_OSR_CLK_MASK) if (regval & LCC_MI2S_STAT_BIT_CLK_MASK) return 0; printk(BIOS_ERR, "%s: error enabling MI2S clocks: %u\n", __func__, regval); return 1; } int audio_clock_config(unsigned int frequency) { Ipq806xLccClocks bus = { .gcc_apcs_regs = (void *)(MSM_GCC_BASE + GCC_PLL_APCS_REG), .lcc_pll0_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_PLL0_MODE_REG), .lcc_ahbix_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_AHBIX_NS_REG), .lcc_mi2s_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_MI2S_NS_REG), .lcc_pll_regs = (void *)(MSM_LPASS_LCC_BASE + LCC_PLL_PCLK_REG), }; if (lcc_init_enable_pll0(&bus)) return 1; if (lcc_init_enable_ahbix(&bus)) return 1; if (lcc_init_mi2s(&bus, frequency)) return 1; if (lcc_enable_mi2s(&bus)) return 1; return 0; }