13a580e9eSGhennadi Procopciuc /* 23a580e9eSGhennadi Procopciuc * Copyright 2024 NXP 33a580e9eSGhennadi Procopciuc * 43a580e9eSGhennadi Procopciuc * SPDX-License-Identifier: BSD-3-Clause 53a580e9eSGhennadi Procopciuc */ 63a580e9eSGhennadi Procopciuc #include <errno.h> 73a580e9eSGhennadi Procopciuc 88ab34357SGhennadi Procopciuc #include <s32cc-clk-regs.h> 98ab34357SGhennadi Procopciuc 10d9373519SGhennadi Procopciuc #include <common/debug.h> 113a580e9eSGhennadi Procopciuc #include <drivers/clk.h> 128ab34357SGhennadi Procopciuc #include <lib/mmio.h> 13b5101c45SGhennadi Procopciuc #include <s32cc-clk-ids.h> 14d9373519SGhennadi Procopciuc #include <s32cc-clk-modules.h> 15d9373519SGhennadi Procopciuc #include <s32cc-clk-utils.h> 16d9373519SGhennadi Procopciuc 17d9373519SGhennadi Procopciuc #define MAX_STACK_DEPTH (15U) 18d9373519SGhennadi Procopciuc 19b5101c45SGhennadi Procopciuc /* This is used for floating-point precision calculations. */ 20b5101c45SGhennadi Procopciuc #define FP_PRECISION (100000000UL) 21b5101c45SGhennadi Procopciuc 228ab34357SGhennadi Procopciuc struct s32cc_clk_drv { 238ab34357SGhennadi Procopciuc uintptr_t fxosc_base; 24b5101c45SGhennadi Procopciuc uintptr_t armpll_base; 25*7004f678SGhennadi Procopciuc uintptr_t cgm1_base; 268ab34357SGhennadi Procopciuc }; 278ab34357SGhennadi Procopciuc 28d9373519SGhennadi Procopciuc static int update_stack_depth(unsigned int *depth) 29d9373519SGhennadi Procopciuc { 30d9373519SGhennadi Procopciuc if (*depth == 0U) { 31d9373519SGhennadi Procopciuc return -ENOMEM; 32d9373519SGhennadi Procopciuc } 33d9373519SGhennadi Procopciuc 34d9373519SGhennadi Procopciuc (*depth)--; 35d9373519SGhennadi Procopciuc return 0; 36d9373519SGhennadi Procopciuc } 373a580e9eSGhennadi Procopciuc 388ab34357SGhennadi Procopciuc static struct s32cc_clk_drv *get_drv(void) 398ab34357SGhennadi Procopciuc { 408ab34357SGhennadi Procopciuc static struct s32cc_clk_drv driver = { 418ab34357SGhennadi Procopciuc .fxosc_base = FXOSC_BASE_ADDR, 42b5101c45SGhennadi Procopciuc .armpll_base = ARMPLL_BASE_ADDR, 43*7004f678SGhennadi Procopciuc .cgm1_base = CGM1_BASE_ADDR, 448ab34357SGhennadi Procopciuc }; 458ab34357SGhennadi Procopciuc 468ab34357SGhennadi Procopciuc return &driver; 478ab34357SGhennadi Procopciuc } 488ab34357SGhennadi Procopciuc 498ab34357SGhennadi Procopciuc static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth); 508ab34357SGhennadi Procopciuc 518ab34357SGhennadi Procopciuc static int enable_clk_module(const struct s32cc_clk_obj *module, 528ab34357SGhennadi Procopciuc const struct s32cc_clk_drv *drv, 538ab34357SGhennadi Procopciuc unsigned int *depth) 548ab34357SGhennadi Procopciuc { 558ab34357SGhennadi Procopciuc const struct s32cc_clk *clk = s32cc_obj2clk(module); 568ab34357SGhennadi Procopciuc int ret; 578ab34357SGhennadi Procopciuc 588ab34357SGhennadi Procopciuc ret = update_stack_depth(depth); 598ab34357SGhennadi Procopciuc if (ret != 0) { 608ab34357SGhennadi Procopciuc return ret; 618ab34357SGhennadi Procopciuc } 628ab34357SGhennadi Procopciuc 638ab34357SGhennadi Procopciuc if (clk == NULL) { 648ab34357SGhennadi Procopciuc return -EINVAL; 658ab34357SGhennadi Procopciuc } 668ab34357SGhennadi Procopciuc 678ab34357SGhennadi Procopciuc if (clk->module != NULL) { 688ab34357SGhennadi Procopciuc return enable_module(clk->module, depth); 698ab34357SGhennadi Procopciuc } 708ab34357SGhennadi Procopciuc 718ab34357SGhennadi Procopciuc if (clk->pclock != NULL) { 728ab34357SGhennadi Procopciuc return enable_clk_module(&clk->pclock->desc, drv, depth); 738ab34357SGhennadi Procopciuc } 748ab34357SGhennadi Procopciuc 758ab34357SGhennadi Procopciuc return -EINVAL; 768ab34357SGhennadi Procopciuc } 778ab34357SGhennadi Procopciuc 78b5101c45SGhennadi Procopciuc static int get_base_addr(enum s32cc_clk_source id, const struct s32cc_clk_drv *drv, 79b5101c45SGhennadi Procopciuc uintptr_t *base) 80b5101c45SGhennadi Procopciuc { 81b5101c45SGhennadi Procopciuc int ret = 0; 82b5101c45SGhennadi Procopciuc 83b5101c45SGhennadi Procopciuc switch (id) { 84b5101c45SGhennadi Procopciuc case S32CC_FXOSC: 85b5101c45SGhennadi Procopciuc *base = drv->fxosc_base; 86b5101c45SGhennadi Procopciuc break; 87b5101c45SGhennadi Procopciuc case S32CC_ARM_PLL: 88b5101c45SGhennadi Procopciuc *base = drv->armpll_base; 89b5101c45SGhennadi Procopciuc break; 90b5101c45SGhennadi Procopciuc case S32CC_CGM1: 91*7004f678SGhennadi Procopciuc *base = drv->cgm1_base; 92b5101c45SGhennadi Procopciuc break; 93b5101c45SGhennadi Procopciuc case S32CC_FIRC: 94b5101c45SGhennadi Procopciuc break; 95b5101c45SGhennadi Procopciuc case S32CC_SIRC: 96b5101c45SGhennadi Procopciuc break; 97b5101c45SGhennadi Procopciuc default: 98b5101c45SGhennadi Procopciuc ret = -EINVAL; 99b5101c45SGhennadi Procopciuc break; 100b5101c45SGhennadi Procopciuc } 101b5101c45SGhennadi Procopciuc 102b5101c45SGhennadi Procopciuc if (ret != 0) { 103b5101c45SGhennadi Procopciuc ERROR("Unknown clock source id: %u\n", id); 104b5101c45SGhennadi Procopciuc } 105b5101c45SGhennadi Procopciuc 106b5101c45SGhennadi Procopciuc return ret; 107b5101c45SGhennadi Procopciuc } 108b5101c45SGhennadi Procopciuc 1098ab34357SGhennadi Procopciuc static void enable_fxosc(const struct s32cc_clk_drv *drv) 1108ab34357SGhennadi Procopciuc { 1118ab34357SGhennadi Procopciuc uintptr_t fxosc_base = drv->fxosc_base; 1128ab34357SGhennadi Procopciuc uint32_t ctrl; 1138ab34357SGhennadi Procopciuc 1148ab34357SGhennadi Procopciuc ctrl = mmio_read_32(FXOSC_CTRL(fxosc_base)); 1158ab34357SGhennadi Procopciuc if ((ctrl & FXOSC_CTRL_OSCON) != U(0)) { 1168ab34357SGhennadi Procopciuc return; 1178ab34357SGhennadi Procopciuc } 1188ab34357SGhennadi Procopciuc 1198ab34357SGhennadi Procopciuc ctrl = FXOSC_CTRL_COMP_EN; 1208ab34357SGhennadi Procopciuc ctrl &= ~FXOSC_CTRL_OSC_BYP; 1218ab34357SGhennadi Procopciuc ctrl |= FXOSC_CTRL_EOCV(0x1); 1228ab34357SGhennadi Procopciuc ctrl |= FXOSC_CTRL_GM_SEL(0x7); 1238ab34357SGhennadi Procopciuc mmio_write_32(FXOSC_CTRL(fxosc_base), ctrl); 1248ab34357SGhennadi Procopciuc 1258ab34357SGhennadi Procopciuc /* Switch ON the crystal oscillator. */ 1268ab34357SGhennadi Procopciuc mmio_setbits_32(FXOSC_CTRL(fxosc_base), FXOSC_CTRL_OSCON); 1278ab34357SGhennadi Procopciuc 1288ab34357SGhennadi Procopciuc /* Wait until the clock is stable. */ 1298ab34357SGhennadi Procopciuc while ((mmio_read_32(FXOSC_STAT(fxosc_base)) & FXOSC_STAT_OSC_STAT) == U(0)) { 1308ab34357SGhennadi Procopciuc } 1318ab34357SGhennadi Procopciuc } 1328ab34357SGhennadi Procopciuc 1338ab34357SGhennadi Procopciuc static int enable_osc(const struct s32cc_clk_obj *module, 1348ab34357SGhennadi Procopciuc const struct s32cc_clk_drv *drv, 1358ab34357SGhennadi Procopciuc unsigned int *depth) 1368ab34357SGhennadi Procopciuc { 1378ab34357SGhennadi Procopciuc const struct s32cc_osc *osc = s32cc_obj2osc(module); 1388ab34357SGhennadi Procopciuc int ret = 0; 1398ab34357SGhennadi Procopciuc 1408ab34357SGhennadi Procopciuc ret = update_stack_depth(depth); 1418ab34357SGhennadi Procopciuc if (ret != 0) { 1428ab34357SGhennadi Procopciuc return ret; 1438ab34357SGhennadi Procopciuc } 1448ab34357SGhennadi Procopciuc 1458ab34357SGhennadi Procopciuc switch (osc->source) { 1468ab34357SGhennadi Procopciuc case S32CC_FXOSC: 1478ab34357SGhennadi Procopciuc enable_fxosc(drv); 1488ab34357SGhennadi Procopciuc break; 1498ab34357SGhennadi Procopciuc /* FIRC and SIRC oscillators are enabled by default */ 1508ab34357SGhennadi Procopciuc case S32CC_FIRC: 1518ab34357SGhennadi Procopciuc break; 1528ab34357SGhennadi Procopciuc case S32CC_SIRC: 1538ab34357SGhennadi Procopciuc break; 1548ab34357SGhennadi Procopciuc default: 1558ab34357SGhennadi Procopciuc ERROR("Invalid oscillator %d\n", osc->source); 1568ab34357SGhennadi Procopciuc ret = -EINVAL; 1578ab34357SGhennadi Procopciuc break; 1588ab34357SGhennadi Procopciuc }; 1598ab34357SGhennadi Procopciuc 1608ab34357SGhennadi Procopciuc return ret; 1618ab34357SGhennadi Procopciuc } 1628ab34357SGhennadi Procopciuc 163b5101c45SGhennadi Procopciuc static int get_pll_mfi_mfn(unsigned long pll_vco, unsigned long ref_freq, 164b5101c45SGhennadi Procopciuc uint32_t *mfi, uint32_t *mfn) 165b5101c45SGhennadi Procopciuc 166b5101c45SGhennadi Procopciuc { 167b5101c45SGhennadi Procopciuc unsigned long vco; 168b5101c45SGhennadi Procopciuc unsigned long mfn64; 169b5101c45SGhennadi Procopciuc 170b5101c45SGhennadi Procopciuc /* FRAC-N mode */ 171b5101c45SGhennadi Procopciuc *mfi = (uint32_t)(pll_vco / ref_freq); 172b5101c45SGhennadi Procopciuc 173b5101c45SGhennadi Procopciuc /* MFN formula : (double)(pll_vco % ref_freq) / ref_freq * 18432.0 */ 174b5101c45SGhennadi Procopciuc mfn64 = pll_vco % ref_freq; 175b5101c45SGhennadi Procopciuc mfn64 *= FP_PRECISION; 176b5101c45SGhennadi Procopciuc mfn64 /= ref_freq; 177b5101c45SGhennadi Procopciuc mfn64 *= 18432UL; 178b5101c45SGhennadi Procopciuc mfn64 /= FP_PRECISION; 179b5101c45SGhennadi Procopciuc 180b5101c45SGhennadi Procopciuc if (mfn64 > UINT32_MAX) { 181b5101c45SGhennadi Procopciuc return -EINVAL; 182b5101c45SGhennadi Procopciuc } 183b5101c45SGhennadi Procopciuc 184b5101c45SGhennadi Procopciuc *mfn = (uint32_t)mfn64; 185b5101c45SGhennadi Procopciuc 186b5101c45SGhennadi Procopciuc vco = ((unsigned long)*mfn * FP_PRECISION) / 18432UL; 187b5101c45SGhennadi Procopciuc vco += (unsigned long)*mfi * FP_PRECISION; 188b5101c45SGhennadi Procopciuc vco *= ref_freq; 189b5101c45SGhennadi Procopciuc vco /= FP_PRECISION; 190b5101c45SGhennadi Procopciuc 191b5101c45SGhennadi Procopciuc if (vco != pll_vco) { 192b5101c45SGhennadi Procopciuc ERROR("Failed to find MFI and MFN settings for PLL freq %lu. Nearest freq = %lu\n", 193b5101c45SGhennadi Procopciuc pll_vco, vco); 194b5101c45SGhennadi Procopciuc return -EINVAL; 195b5101c45SGhennadi Procopciuc } 196b5101c45SGhennadi Procopciuc 197b5101c45SGhennadi Procopciuc return 0; 198b5101c45SGhennadi Procopciuc } 199b5101c45SGhennadi Procopciuc 200b5101c45SGhennadi Procopciuc static struct s32cc_clkmux *get_pll_mux(const struct s32cc_pll *pll) 201b5101c45SGhennadi Procopciuc { 202b5101c45SGhennadi Procopciuc const struct s32cc_clk_obj *source = pll->source; 203b5101c45SGhennadi Procopciuc const struct s32cc_clk *clk; 204b5101c45SGhennadi Procopciuc 205b5101c45SGhennadi Procopciuc if (source == NULL) { 206b5101c45SGhennadi Procopciuc ERROR("Failed to identify PLL's parent\n"); 207b5101c45SGhennadi Procopciuc return NULL; 208b5101c45SGhennadi Procopciuc } 209b5101c45SGhennadi Procopciuc 210b5101c45SGhennadi Procopciuc if (source->type != s32cc_clk_t) { 211b5101c45SGhennadi Procopciuc ERROR("The parent of the PLL isn't a clock\n"); 212b5101c45SGhennadi Procopciuc return NULL; 213b5101c45SGhennadi Procopciuc } 214b5101c45SGhennadi Procopciuc 215b5101c45SGhennadi Procopciuc clk = s32cc_obj2clk(source); 216b5101c45SGhennadi Procopciuc 217b5101c45SGhennadi Procopciuc if (clk->module == NULL) { 218b5101c45SGhennadi Procopciuc ERROR("The clock isn't connected to a module\n"); 219b5101c45SGhennadi Procopciuc return NULL; 220b5101c45SGhennadi Procopciuc } 221b5101c45SGhennadi Procopciuc 222b5101c45SGhennadi Procopciuc source = clk->module; 223b5101c45SGhennadi Procopciuc 224b5101c45SGhennadi Procopciuc if ((source->type != s32cc_clkmux_t) && 225b5101c45SGhennadi Procopciuc (source->type != s32cc_shared_clkmux_t)) { 226b5101c45SGhennadi Procopciuc ERROR("The parent of the PLL isn't a MUX\n"); 227b5101c45SGhennadi Procopciuc return NULL; 228b5101c45SGhennadi Procopciuc } 229b5101c45SGhennadi Procopciuc 230b5101c45SGhennadi Procopciuc return s32cc_obj2clkmux(source); 231b5101c45SGhennadi Procopciuc } 232b5101c45SGhennadi Procopciuc 233b5101c45SGhennadi Procopciuc static void disable_odiv(uintptr_t pll_addr, uint32_t div_index) 234b5101c45SGhennadi Procopciuc { 235b5101c45SGhennadi Procopciuc mmio_clrbits_32(PLLDIG_PLLODIV(pll_addr, div_index), PLLDIG_PLLODIV_DE); 236b5101c45SGhennadi Procopciuc } 237b5101c45SGhennadi Procopciuc 23884e82085SGhennadi Procopciuc static void enable_odiv(uintptr_t pll_addr, uint32_t div_index) 23984e82085SGhennadi Procopciuc { 24084e82085SGhennadi Procopciuc mmio_setbits_32(PLLDIG_PLLODIV(pll_addr, div_index), PLLDIG_PLLODIV_DE); 24184e82085SGhennadi Procopciuc } 24284e82085SGhennadi Procopciuc 243b5101c45SGhennadi Procopciuc static void disable_odivs(uintptr_t pll_addr, uint32_t ndivs) 244b5101c45SGhennadi Procopciuc { 245b5101c45SGhennadi Procopciuc uint32_t i; 246b5101c45SGhennadi Procopciuc 247b5101c45SGhennadi Procopciuc for (i = 0; i < ndivs; i++) { 248b5101c45SGhennadi Procopciuc disable_odiv(pll_addr, i); 249b5101c45SGhennadi Procopciuc } 250b5101c45SGhennadi Procopciuc } 251b5101c45SGhennadi Procopciuc 252b5101c45SGhennadi Procopciuc static void enable_pll_hw(uintptr_t pll_addr) 253b5101c45SGhennadi Procopciuc { 254b5101c45SGhennadi Procopciuc /* Enable the PLL. */ 255b5101c45SGhennadi Procopciuc mmio_write_32(PLLDIG_PLLCR(pll_addr), 0x0); 256b5101c45SGhennadi Procopciuc 257b5101c45SGhennadi Procopciuc /* Poll until PLL acquires lock. */ 258b5101c45SGhennadi Procopciuc while ((mmio_read_32(PLLDIG_PLLSR(pll_addr)) & PLLDIG_PLLSR_LOCK) == 0U) { 259b5101c45SGhennadi Procopciuc } 260b5101c45SGhennadi Procopciuc } 261b5101c45SGhennadi Procopciuc 262b5101c45SGhennadi Procopciuc static void disable_pll_hw(uintptr_t pll_addr) 263b5101c45SGhennadi Procopciuc { 264b5101c45SGhennadi Procopciuc mmio_write_32(PLLDIG_PLLCR(pll_addr), PLLDIG_PLLCR_PLLPD); 265b5101c45SGhennadi Procopciuc } 266b5101c45SGhennadi Procopciuc 267b5101c45SGhennadi Procopciuc static int program_pll(const struct s32cc_pll *pll, uintptr_t pll_addr, 268b5101c45SGhennadi Procopciuc const struct s32cc_clk_drv *drv, uint32_t sclk_id, 269b5101c45SGhennadi Procopciuc unsigned long sclk_freq) 270b5101c45SGhennadi Procopciuc { 271b5101c45SGhennadi Procopciuc uint32_t rdiv = 1, mfi, mfn; 272b5101c45SGhennadi Procopciuc int ret; 273b5101c45SGhennadi Procopciuc 274b5101c45SGhennadi Procopciuc ret = get_pll_mfi_mfn(pll->vco_freq, sclk_freq, &mfi, &mfn); 275b5101c45SGhennadi Procopciuc if (ret != 0) { 276b5101c45SGhennadi Procopciuc return -EINVAL; 277b5101c45SGhennadi Procopciuc } 278b5101c45SGhennadi Procopciuc 279b5101c45SGhennadi Procopciuc /* Disable ODIVs*/ 280b5101c45SGhennadi Procopciuc disable_odivs(pll_addr, pll->ndividers); 281b5101c45SGhennadi Procopciuc 282b5101c45SGhennadi Procopciuc /* Disable PLL */ 283b5101c45SGhennadi Procopciuc disable_pll_hw(pll_addr); 284b5101c45SGhennadi Procopciuc 285b5101c45SGhennadi Procopciuc /* Program PLLCLKMUX */ 286b5101c45SGhennadi Procopciuc mmio_write_32(PLLDIG_PLLCLKMUX(pll_addr), sclk_id); 287b5101c45SGhennadi Procopciuc 288b5101c45SGhennadi Procopciuc /* Program VCO */ 289b5101c45SGhennadi Procopciuc mmio_clrsetbits_32(PLLDIG_PLLDV(pll_addr), 290b5101c45SGhennadi Procopciuc PLLDIG_PLLDV_RDIV_MASK | PLLDIG_PLLDV_MFI_MASK, 291b5101c45SGhennadi Procopciuc PLLDIG_PLLDV_RDIV_SET(rdiv) | PLLDIG_PLLDV_MFI(mfi)); 292b5101c45SGhennadi Procopciuc 293b5101c45SGhennadi Procopciuc mmio_write_32(PLLDIG_PLLFD(pll_addr), 294b5101c45SGhennadi Procopciuc PLLDIG_PLLFD_MFN_SET(mfn) | PLLDIG_PLLFD_SMDEN); 295b5101c45SGhennadi Procopciuc 296b5101c45SGhennadi Procopciuc enable_pll_hw(pll_addr); 297b5101c45SGhennadi Procopciuc 298b5101c45SGhennadi Procopciuc return ret; 299b5101c45SGhennadi Procopciuc } 300b5101c45SGhennadi Procopciuc 301b5101c45SGhennadi Procopciuc static int enable_pll(const struct s32cc_clk_obj *module, 302b5101c45SGhennadi Procopciuc const struct s32cc_clk_drv *drv, 303b5101c45SGhennadi Procopciuc unsigned int *depth) 304b5101c45SGhennadi Procopciuc { 305b5101c45SGhennadi Procopciuc const struct s32cc_pll *pll = s32cc_obj2pll(module); 306b5101c45SGhennadi Procopciuc const struct s32cc_clkmux *mux; 307b5101c45SGhennadi Procopciuc uintptr_t pll_addr = UL(0x0); 308b5101c45SGhennadi Procopciuc unsigned long sclk_freq; 309b5101c45SGhennadi Procopciuc uint32_t sclk_id; 310b5101c45SGhennadi Procopciuc int ret; 311b5101c45SGhennadi Procopciuc 312b5101c45SGhennadi Procopciuc ret = update_stack_depth(depth); 313b5101c45SGhennadi Procopciuc if (ret != 0) { 314b5101c45SGhennadi Procopciuc return ret; 315b5101c45SGhennadi Procopciuc } 316b5101c45SGhennadi Procopciuc 317b5101c45SGhennadi Procopciuc mux = get_pll_mux(pll); 318b5101c45SGhennadi Procopciuc if (mux == NULL) { 319b5101c45SGhennadi Procopciuc return -EINVAL; 320b5101c45SGhennadi Procopciuc } 321b5101c45SGhennadi Procopciuc 322b5101c45SGhennadi Procopciuc if (pll->instance != mux->module) { 323b5101c45SGhennadi Procopciuc ERROR("MUX type is not in sync with PLL ID\n"); 324b5101c45SGhennadi Procopciuc return -EINVAL; 325b5101c45SGhennadi Procopciuc } 326b5101c45SGhennadi Procopciuc 327b5101c45SGhennadi Procopciuc ret = get_base_addr(pll->instance, drv, &pll_addr); 328b5101c45SGhennadi Procopciuc if (ret != 0) { 329b5101c45SGhennadi Procopciuc ERROR("Failed to detect PLL instance\n"); 330b5101c45SGhennadi Procopciuc return ret; 331b5101c45SGhennadi Procopciuc } 332b5101c45SGhennadi Procopciuc 333b5101c45SGhennadi Procopciuc switch (mux->source_id) { 334b5101c45SGhennadi Procopciuc case S32CC_CLK_FIRC: 335b5101c45SGhennadi Procopciuc sclk_freq = 48U * MHZ; 336b5101c45SGhennadi Procopciuc sclk_id = 0; 337b5101c45SGhennadi Procopciuc break; 338b5101c45SGhennadi Procopciuc case S32CC_CLK_FXOSC: 339b5101c45SGhennadi Procopciuc sclk_freq = 40U * MHZ; 340b5101c45SGhennadi Procopciuc sclk_id = 1; 341b5101c45SGhennadi Procopciuc break; 342b5101c45SGhennadi Procopciuc default: 343b5101c45SGhennadi Procopciuc ERROR("Invalid source selection for PLL 0x%lx\n", 344b5101c45SGhennadi Procopciuc pll_addr); 345b5101c45SGhennadi Procopciuc return -EINVAL; 346b5101c45SGhennadi Procopciuc }; 347b5101c45SGhennadi Procopciuc 348b5101c45SGhennadi Procopciuc return program_pll(pll, pll_addr, drv, sclk_id, sclk_freq); 349b5101c45SGhennadi Procopciuc } 350b5101c45SGhennadi Procopciuc 35184e82085SGhennadi Procopciuc static inline struct s32cc_pll *get_div_pll(const struct s32cc_pll_out_div *pdiv) 35284e82085SGhennadi Procopciuc { 35384e82085SGhennadi Procopciuc const struct s32cc_clk_obj *parent; 35484e82085SGhennadi Procopciuc 35584e82085SGhennadi Procopciuc parent = pdiv->parent; 35684e82085SGhennadi Procopciuc if (parent == NULL) { 35784e82085SGhennadi Procopciuc ERROR("Failed to identify PLL divider's parent\n"); 35884e82085SGhennadi Procopciuc return NULL; 35984e82085SGhennadi Procopciuc } 36084e82085SGhennadi Procopciuc 36184e82085SGhennadi Procopciuc if (parent->type != s32cc_pll_t) { 36284e82085SGhennadi Procopciuc ERROR("The parent of the divider is not a PLL instance\n"); 36384e82085SGhennadi Procopciuc return NULL; 36484e82085SGhennadi Procopciuc } 36584e82085SGhennadi Procopciuc 36684e82085SGhennadi Procopciuc return s32cc_obj2pll(parent); 36784e82085SGhennadi Procopciuc } 36884e82085SGhennadi Procopciuc 36984e82085SGhennadi Procopciuc static void config_pll_out_div(uintptr_t pll_addr, uint32_t div_index, uint32_t dc) 37084e82085SGhennadi Procopciuc { 37184e82085SGhennadi Procopciuc uint32_t pllodiv; 37284e82085SGhennadi Procopciuc uint32_t pdiv; 37384e82085SGhennadi Procopciuc 37484e82085SGhennadi Procopciuc pllodiv = mmio_read_32(PLLDIG_PLLODIV(pll_addr, div_index)); 37584e82085SGhennadi Procopciuc pdiv = PLLDIG_PLLODIV_DIV(pllodiv); 37684e82085SGhennadi Procopciuc 37784e82085SGhennadi Procopciuc if (((pdiv + 1U) == dc) && ((pllodiv & PLLDIG_PLLODIV_DE) != 0U)) { 37884e82085SGhennadi Procopciuc return; 37984e82085SGhennadi Procopciuc } 38084e82085SGhennadi Procopciuc 38184e82085SGhennadi Procopciuc if ((pllodiv & PLLDIG_PLLODIV_DE) != 0U) { 38284e82085SGhennadi Procopciuc disable_odiv(pll_addr, div_index); 38384e82085SGhennadi Procopciuc } 38484e82085SGhennadi Procopciuc 38584e82085SGhennadi Procopciuc pllodiv = PLLDIG_PLLODIV_DIV_SET(dc - 1U); 38684e82085SGhennadi Procopciuc mmio_write_32(PLLDIG_PLLODIV(pll_addr, div_index), pllodiv); 38784e82085SGhennadi Procopciuc 38884e82085SGhennadi Procopciuc enable_odiv(pll_addr, div_index); 38984e82085SGhennadi Procopciuc } 39084e82085SGhennadi Procopciuc 39184e82085SGhennadi Procopciuc static int enable_pll_div(const struct s32cc_clk_obj *module, 39284e82085SGhennadi Procopciuc const struct s32cc_clk_drv *drv, 39384e82085SGhennadi Procopciuc unsigned int *depth) 39484e82085SGhennadi Procopciuc { 39584e82085SGhennadi Procopciuc const struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module); 39684e82085SGhennadi Procopciuc uintptr_t pll_addr = 0x0ULL; 39784e82085SGhennadi Procopciuc const struct s32cc_pll *pll; 39884e82085SGhennadi Procopciuc uint32_t dc; 39984e82085SGhennadi Procopciuc int ret; 40084e82085SGhennadi Procopciuc 40184e82085SGhennadi Procopciuc ret = update_stack_depth(depth); 40284e82085SGhennadi Procopciuc if (ret != 0) { 40384e82085SGhennadi Procopciuc return ret; 40484e82085SGhennadi Procopciuc } 40584e82085SGhennadi Procopciuc 40684e82085SGhennadi Procopciuc pll = get_div_pll(pdiv); 40784e82085SGhennadi Procopciuc if (pll == NULL) { 40884e82085SGhennadi Procopciuc ERROR("The parent of the PLL DIV is invalid\n"); 40984e82085SGhennadi Procopciuc return 0; 41084e82085SGhennadi Procopciuc } 41184e82085SGhennadi Procopciuc 41284e82085SGhennadi Procopciuc ret = get_base_addr(pll->instance, drv, &pll_addr); 41384e82085SGhennadi Procopciuc if (ret != 0) { 41484e82085SGhennadi Procopciuc ERROR("Failed to detect PLL instance\n"); 41584e82085SGhennadi Procopciuc return -EINVAL; 41684e82085SGhennadi Procopciuc } 41784e82085SGhennadi Procopciuc 41884e82085SGhennadi Procopciuc dc = (uint32_t)(pll->vco_freq / pdiv->freq); 41984e82085SGhennadi Procopciuc 42084e82085SGhennadi Procopciuc config_pll_out_div(pll_addr, pdiv->index, dc); 42184e82085SGhennadi Procopciuc 42284e82085SGhennadi Procopciuc return 0; 42384e82085SGhennadi Procopciuc } 42484e82085SGhennadi Procopciuc 425*7004f678SGhennadi Procopciuc static int cgm_mux_clk_config(uintptr_t cgm_addr, uint32_t mux, uint32_t source, 426*7004f678SGhennadi Procopciuc bool safe_clk) 427*7004f678SGhennadi Procopciuc { 428*7004f678SGhennadi Procopciuc uint32_t css, csc; 429*7004f678SGhennadi Procopciuc 430*7004f678SGhennadi Procopciuc css = mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)); 431*7004f678SGhennadi Procopciuc 432*7004f678SGhennadi Procopciuc /* Already configured */ 433*7004f678SGhennadi Procopciuc if ((MC_CGM_MUXn_CSS_SELSTAT(css) == source) && 434*7004f678SGhennadi Procopciuc (MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SUCCESS) && 435*7004f678SGhennadi Procopciuc ((css & MC_CGM_MUXn_CSS_SWIP) == 0U) && !safe_clk) { 436*7004f678SGhennadi Procopciuc return 0; 437*7004f678SGhennadi Procopciuc } 438*7004f678SGhennadi Procopciuc 439*7004f678SGhennadi Procopciuc /* Ongoing clock switch? */ 440*7004f678SGhennadi Procopciuc while ((mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)) & 441*7004f678SGhennadi Procopciuc MC_CGM_MUXn_CSS_SWIP) != 0U) { 442*7004f678SGhennadi Procopciuc } 443*7004f678SGhennadi Procopciuc 444*7004f678SGhennadi Procopciuc csc = mmio_read_32(CGM_MUXn_CSC(cgm_addr, mux)); 445*7004f678SGhennadi Procopciuc 446*7004f678SGhennadi Procopciuc /* Clear previous source. */ 447*7004f678SGhennadi Procopciuc csc &= ~(MC_CGM_MUXn_CSC_SELCTL_MASK); 448*7004f678SGhennadi Procopciuc 449*7004f678SGhennadi Procopciuc if (!safe_clk) { 450*7004f678SGhennadi Procopciuc /* Select the clock source and trigger the clock switch. */ 451*7004f678SGhennadi Procopciuc csc |= MC_CGM_MUXn_CSC_SELCTL(source) | MC_CGM_MUXn_CSC_CLK_SW; 452*7004f678SGhennadi Procopciuc } else { 453*7004f678SGhennadi Procopciuc /* Switch to safe clock */ 454*7004f678SGhennadi Procopciuc csc |= MC_CGM_MUXn_CSC_SAFE_SW; 455*7004f678SGhennadi Procopciuc } 456*7004f678SGhennadi Procopciuc 457*7004f678SGhennadi Procopciuc mmio_write_32(CGM_MUXn_CSC(cgm_addr, mux), csc); 458*7004f678SGhennadi Procopciuc 459*7004f678SGhennadi Procopciuc /* Wait for configuration bit to auto-clear. */ 460*7004f678SGhennadi Procopciuc while ((mmio_read_32(CGM_MUXn_CSC(cgm_addr, mux)) & 461*7004f678SGhennadi Procopciuc MC_CGM_MUXn_CSC_CLK_SW) != 0U) { 462*7004f678SGhennadi Procopciuc } 463*7004f678SGhennadi Procopciuc 464*7004f678SGhennadi Procopciuc /* Is the clock switch completed? */ 465*7004f678SGhennadi Procopciuc while ((mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)) & 466*7004f678SGhennadi Procopciuc MC_CGM_MUXn_CSS_SWIP) != 0U) { 467*7004f678SGhennadi Procopciuc } 468*7004f678SGhennadi Procopciuc 469*7004f678SGhennadi Procopciuc /* 470*7004f678SGhennadi Procopciuc * Check if the switch succeeded. 471*7004f678SGhennadi Procopciuc * Check switch trigger cause and the source. 472*7004f678SGhennadi Procopciuc */ 473*7004f678SGhennadi Procopciuc css = mmio_read_32(CGM_MUXn_CSS(cgm_addr, mux)); 474*7004f678SGhennadi Procopciuc if (!safe_clk) { 475*7004f678SGhennadi Procopciuc if ((MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SUCCESS) && 476*7004f678SGhennadi Procopciuc (MC_CGM_MUXn_CSS_SELSTAT(css) == source)) { 477*7004f678SGhennadi Procopciuc return 0; 478*7004f678SGhennadi Procopciuc } 479*7004f678SGhennadi Procopciuc 480*7004f678SGhennadi Procopciuc ERROR("Failed to change the source of mux %" PRIu32 " to %" PRIu32 " (CGM=%lu)\n", 481*7004f678SGhennadi Procopciuc mux, source, cgm_addr); 482*7004f678SGhennadi Procopciuc } else { 483*7004f678SGhennadi Procopciuc if (((MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SAFE_CLK) || 484*7004f678SGhennadi Procopciuc (MC_CGM_MUXn_CSS_SWTRG(css) == MC_CGM_MUXn_CSS_SWTRG_SAFE_CLK_INACTIVE)) && 485*7004f678SGhennadi Procopciuc ((MC_CGM_MUXn_CSS_SAFE_SW & css) != 0U)) { 486*7004f678SGhennadi Procopciuc return 0; 487*7004f678SGhennadi Procopciuc } 488*7004f678SGhennadi Procopciuc 489*7004f678SGhennadi Procopciuc ERROR("The switch of mux %" PRIu32 " (CGM=%lu) to safe clock failed\n", 490*7004f678SGhennadi Procopciuc mux, cgm_addr); 491*7004f678SGhennadi Procopciuc } 492*7004f678SGhennadi Procopciuc 493*7004f678SGhennadi Procopciuc return -EINVAL; 494*7004f678SGhennadi Procopciuc } 495*7004f678SGhennadi Procopciuc 496*7004f678SGhennadi Procopciuc static int enable_cgm_mux(const struct s32cc_clkmux *mux, 497*7004f678SGhennadi Procopciuc const struct s32cc_clk_drv *drv) 498*7004f678SGhennadi Procopciuc { 499*7004f678SGhennadi Procopciuc uintptr_t cgm_addr = UL(0x0); 500*7004f678SGhennadi Procopciuc uint32_t mux_hw_clk; 501*7004f678SGhennadi Procopciuc int ret; 502*7004f678SGhennadi Procopciuc 503*7004f678SGhennadi Procopciuc ret = get_base_addr(mux->module, drv, &cgm_addr); 504*7004f678SGhennadi Procopciuc if (ret != 0) { 505*7004f678SGhennadi Procopciuc return ret; 506*7004f678SGhennadi Procopciuc } 507*7004f678SGhennadi Procopciuc 508*7004f678SGhennadi Procopciuc mux_hw_clk = (uint32_t)S32CC_CLK_ID(mux->source_id); 509*7004f678SGhennadi Procopciuc 510*7004f678SGhennadi Procopciuc return cgm_mux_clk_config(cgm_addr, mux->index, 511*7004f678SGhennadi Procopciuc mux_hw_clk, false); 512*7004f678SGhennadi Procopciuc } 513*7004f678SGhennadi Procopciuc 514*7004f678SGhennadi Procopciuc static int enable_mux(const struct s32cc_clk_obj *module, 515*7004f678SGhennadi Procopciuc const struct s32cc_clk_drv *drv, 516*7004f678SGhennadi Procopciuc unsigned int *depth) 517*7004f678SGhennadi Procopciuc { 518*7004f678SGhennadi Procopciuc const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module); 519*7004f678SGhennadi Procopciuc const struct s32cc_clk *clk; 520*7004f678SGhennadi Procopciuc int ret = 0; 521*7004f678SGhennadi Procopciuc 522*7004f678SGhennadi Procopciuc ret = update_stack_depth(depth); 523*7004f678SGhennadi Procopciuc if (ret != 0) { 524*7004f678SGhennadi Procopciuc return ret; 525*7004f678SGhennadi Procopciuc } 526*7004f678SGhennadi Procopciuc 527*7004f678SGhennadi Procopciuc if (mux == NULL) { 528*7004f678SGhennadi Procopciuc return -EINVAL; 529*7004f678SGhennadi Procopciuc } 530*7004f678SGhennadi Procopciuc 531*7004f678SGhennadi Procopciuc clk = s32cc_get_arch_clk(mux->source_id); 532*7004f678SGhennadi Procopciuc if (clk == NULL) { 533*7004f678SGhennadi Procopciuc ERROR("Invalid parent (%lu) for mux %" PRIu8 "\n", 534*7004f678SGhennadi Procopciuc mux->source_id, mux->index); 535*7004f678SGhennadi Procopciuc return -EINVAL; 536*7004f678SGhennadi Procopciuc } 537*7004f678SGhennadi Procopciuc 538*7004f678SGhennadi Procopciuc switch (mux->module) { 539*7004f678SGhennadi Procopciuc /* PLL mux will be enabled by PLL setup */ 540*7004f678SGhennadi Procopciuc case S32CC_ARM_PLL: 541*7004f678SGhennadi Procopciuc break; 542*7004f678SGhennadi Procopciuc case S32CC_CGM1: 543*7004f678SGhennadi Procopciuc ret = enable_cgm_mux(mux, drv); 544*7004f678SGhennadi Procopciuc break; 545*7004f678SGhennadi Procopciuc default: 546*7004f678SGhennadi Procopciuc ERROR("Unknown mux parent type: %d\n", mux->module); 547*7004f678SGhennadi Procopciuc ret = -EINVAL; 548*7004f678SGhennadi Procopciuc break; 549*7004f678SGhennadi Procopciuc }; 550*7004f678SGhennadi Procopciuc 551*7004f678SGhennadi Procopciuc return ret; 552*7004f678SGhennadi Procopciuc } 553*7004f678SGhennadi Procopciuc 5548ab34357SGhennadi Procopciuc static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth) 5558ab34357SGhennadi Procopciuc { 5568ab34357SGhennadi Procopciuc const struct s32cc_clk_drv *drv = get_drv(); 5578ab34357SGhennadi Procopciuc int ret = 0; 5588ab34357SGhennadi Procopciuc 5598ab34357SGhennadi Procopciuc ret = update_stack_depth(depth); 5608ab34357SGhennadi Procopciuc if (ret != 0) { 5618ab34357SGhennadi Procopciuc return ret; 5628ab34357SGhennadi Procopciuc } 5638ab34357SGhennadi Procopciuc 5648ab34357SGhennadi Procopciuc if (drv == NULL) { 5658ab34357SGhennadi Procopciuc return -EINVAL; 5668ab34357SGhennadi Procopciuc } 5678ab34357SGhennadi Procopciuc 5688ab34357SGhennadi Procopciuc switch (module->type) { 5698ab34357SGhennadi Procopciuc case s32cc_osc_t: 5708ab34357SGhennadi Procopciuc ret = enable_osc(module, drv, depth); 5718ab34357SGhennadi Procopciuc break; 5728ab34357SGhennadi Procopciuc case s32cc_clk_t: 5738ab34357SGhennadi Procopciuc ret = enable_clk_module(module, drv, depth); 5748ab34357SGhennadi Procopciuc break; 575b5101c45SGhennadi Procopciuc case s32cc_pll_t: 576b5101c45SGhennadi Procopciuc ret = enable_pll(module, drv, depth); 577b5101c45SGhennadi Procopciuc break; 57884e82085SGhennadi Procopciuc case s32cc_pll_out_div_t: 57984e82085SGhennadi Procopciuc ret = enable_pll_div(module, drv, depth); 58084e82085SGhennadi Procopciuc break; 581a8be748aSGhennadi Procopciuc case s32cc_clkmux_t: 582*7004f678SGhennadi Procopciuc ret = enable_mux(module, drv, depth); 583a8be748aSGhennadi Procopciuc break; 5843fa91a94SGhennadi Procopciuc case s32cc_shared_clkmux_t: 585*7004f678SGhennadi Procopciuc ret = enable_mux(module, drv, depth); 5863fa91a94SGhennadi Procopciuc break; 58744e2130aSGhennadi Procopciuc case s32cc_fixed_div_t: 588a8be748aSGhennadi Procopciuc ret = -ENOTSUP; 589a8be748aSGhennadi Procopciuc break; 5908ab34357SGhennadi Procopciuc default: 5918ab34357SGhennadi Procopciuc ret = -EINVAL; 5928ab34357SGhennadi Procopciuc break; 5938ab34357SGhennadi Procopciuc } 5948ab34357SGhennadi Procopciuc 5958ab34357SGhennadi Procopciuc return ret; 5968ab34357SGhennadi Procopciuc } 5978ab34357SGhennadi Procopciuc 5983a580e9eSGhennadi Procopciuc static int s32cc_clk_enable(unsigned long id) 5993a580e9eSGhennadi Procopciuc { 6008ab34357SGhennadi Procopciuc unsigned int depth = MAX_STACK_DEPTH; 6018ab34357SGhennadi Procopciuc const struct s32cc_clk *clk; 6028ab34357SGhennadi Procopciuc 6038ab34357SGhennadi Procopciuc clk = s32cc_get_arch_clk(id); 6048ab34357SGhennadi Procopciuc if (clk == NULL) { 6058ab34357SGhennadi Procopciuc return -EINVAL; 6068ab34357SGhennadi Procopciuc } 6078ab34357SGhennadi Procopciuc 6088ab34357SGhennadi Procopciuc return enable_module(&clk->desc, &depth); 6093a580e9eSGhennadi Procopciuc } 6103a580e9eSGhennadi Procopciuc 6113a580e9eSGhennadi Procopciuc static void s32cc_clk_disable(unsigned long id) 6123a580e9eSGhennadi Procopciuc { 6133a580e9eSGhennadi Procopciuc } 6143a580e9eSGhennadi Procopciuc 6153a580e9eSGhennadi Procopciuc static bool s32cc_clk_is_enabled(unsigned long id) 6163a580e9eSGhennadi Procopciuc { 6173a580e9eSGhennadi Procopciuc return false; 6183a580e9eSGhennadi Procopciuc } 6193a580e9eSGhennadi Procopciuc 6203a580e9eSGhennadi Procopciuc static unsigned long s32cc_clk_get_rate(unsigned long id) 6213a580e9eSGhennadi Procopciuc { 6223a580e9eSGhennadi Procopciuc return 0; 6233a580e9eSGhennadi Procopciuc } 6243a580e9eSGhennadi Procopciuc 625d9373519SGhennadi Procopciuc static int set_module_rate(const struct s32cc_clk_obj *module, 626d9373519SGhennadi Procopciuc unsigned long rate, unsigned long *orate, 627d9373519SGhennadi Procopciuc unsigned int *depth); 628d9373519SGhennadi Procopciuc 629d9373519SGhennadi Procopciuc static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate, 630d9373519SGhennadi Procopciuc unsigned long *orate, unsigned int *depth) 631d9373519SGhennadi Procopciuc { 632d9373519SGhennadi Procopciuc struct s32cc_osc *osc = s32cc_obj2osc(module); 633d9373519SGhennadi Procopciuc int ret; 634d9373519SGhennadi Procopciuc 635d9373519SGhennadi Procopciuc ret = update_stack_depth(depth); 636d9373519SGhennadi Procopciuc if (ret != 0) { 637d9373519SGhennadi Procopciuc return ret; 638d9373519SGhennadi Procopciuc } 639d9373519SGhennadi Procopciuc 640d9373519SGhennadi Procopciuc if ((osc->freq != 0UL) && (rate != osc->freq)) { 641d9373519SGhennadi Procopciuc ERROR("Already initialized oscillator. freq = %lu\n", 642d9373519SGhennadi Procopciuc osc->freq); 643d9373519SGhennadi Procopciuc return -EINVAL; 644d9373519SGhennadi Procopciuc } 645d9373519SGhennadi Procopciuc 646d9373519SGhennadi Procopciuc osc->freq = rate; 647d9373519SGhennadi Procopciuc *orate = osc->freq; 648d9373519SGhennadi Procopciuc 649d9373519SGhennadi Procopciuc return 0; 650d9373519SGhennadi Procopciuc } 651d9373519SGhennadi Procopciuc 652d9373519SGhennadi Procopciuc static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate, 653d9373519SGhennadi Procopciuc unsigned long *orate, unsigned int *depth) 654d9373519SGhennadi Procopciuc { 655d9373519SGhennadi Procopciuc const struct s32cc_clk *clk = s32cc_obj2clk(module); 656d9373519SGhennadi Procopciuc int ret; 657d9373519SGhennadi Procopciuc 658d9373519SGhennadi Procopciuc ret = update_stack_depth(depth); 659d9373519SGhennadi Procopciuc if (ret != 0) { 660d9373519SGhennadi Procopciuc return ret; 661d9373519SGhennadi Procopciuc } 662d9373519SGhennadi Procopciuc 663d9373519SGhennadi Procopciuc if ((clk->min_freq != 0UL) && (clk->max_freq != 0UL) && 664d9373519SGhennadi Procopciuc ((rate < clk->min_freq) || (rate > clk->max_freq))) { 665d9373519SGhennadi Procopciuc ERROR("%lu frequency is out of the allowed range: [%lu:%lu]\n", 666d9373519SGhennadi Procopciuc rate, clk->min_freq, clk->max_freq); 667d9373519SGhennadi Procopciuc return -EINVAL; 668d9373519SGhennadi Procopciuc } 669d9373519SGhennadi Procopciuc 670d9373519SGhennadi Procopciuc if (clk->module != NULL) { 671d9373519SGhennadi Procopciuc return set_module_rate(clk->module, rate, orate, depth); 672d9373519SGhennadi Procopciuc } 673d9373519SGhennadi Procopciuc 674d9373519SGhennadi Procopciuc if (clk->pclock != NULL) { 675d9373519SGhennadi Procopciuc return set_clk_freq(&clk->pclock->desc, rate, orate, depth); 676d9373519SGhennadi Procopciuc } 677d9373519SGhennadi Procopciuc 678d9373519SGhennadi Procopciuc return -EINVAL; 679d9373519SGhennadi Procopciuc } 680d9373519SGhennadi Procopciuc 6817ad4e231SGhennadi Procopciuc static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate, 6827ad4e231SGhennadi Procopciuc unsigned long *orate, unsigned int *depth) 6837ad4e231SGhennadi Procopciuc { 6847ad4e231SGhennadi Procopciuc struct s32cc_pll *pll = s32cc_obj2pll(module); 6857ad4e231SGhennadi Procopciuc int ret; 6867ad4e231SGhennadi Procopciuc 6877ad4e231SGhennadi Procopciuc ret = update_stack_depth(depth); 6887ad4e231SGhennadi Procopciuc if (ret != 0) { 6897ad4e231SGhennadi Procopciuc return ret; 6907ad4e231SGhennadi Procopciuc } 6917ad4e231SGhennadi Procopciuc 6927ad4e231SGhennadi Procopciuc if ((pll->vco_freq != 0UL) && (pll->vco_freq != rate)) { 6937ad4e231SGhennadi Procopciuc ERROR("PLL frequency was already set\n"); 6947ad4e231SGhennadi Procopciuc return -EINVAL; 6957ad4e231SGhennadi Procopciuc } 6967ad4e231SGhennadi Procopciuc 6977ad4e231SGhennadi Procopciuc pll->vco_freq = rate; 6987ad4e231SGhennadi Procopciuc *orate = pll->vco_freq; 6997ad4e231SGhennadi Procopciuc 7007ad4e231SGhennadi Procopciuc return 0; 7017ad4e231SGhennadi Procopciuc } 7027ad4e231SGhennadi Procopciuc 703de950ef0SGhennadi Procopciuc static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, 704de950ef0SGhennadi Procopciuc unsigned long *orate, unsigned int *depth) 705de950ef0SGhennadi Procopciuc { 706de950ef0SGhennadi Procopciuc struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module); 707de950ef0SGhennadi Procopciuc const struct s32cc_pll *pll; 708de950ef0SGhennadi Procopciuc unsigned long prate, dc; 709de950ef0SGhennadi Procopciuc int ret; 710de950ef0SGhennadi Procopciuc 711de950ef0SGhennadi Procopciuc ret = update_stack_depth(depth); 712de950ef0SGhennadi Procopciuc if (ret != 0) { 713de950ef0SGhennadi Procopciuc return ret; 714de950ef0SGhennadi Procopciuc } 715de950ef0SGhennadi Procopciuc 716de950ef0SGhennadi Procopciuc if (pdiv->parent == NULL) { 717de950ef0SGhennadi Procopciuc ERROR("Failed to identify PLL divider's parent\n"); 718de950ef0SGhennadi Procopciuc return -EINVAL; 719de950ef0SGhennadi Procopciuc } 720de950ef0SGhennadi Procopciuc 721de950ef0SGhennadi Procopciuc pll = s32cc_obj2pll(pdiv->parent); 722de950ef0SGhennadi Procopciuc if (pll == NULL) { 723de950ef0SGhennadi Procopciuc ERROR("The parent of the PLL DIV is invalid\n"); 724de950ef0SGhennadi Procopciuc return -EINVAL; 725de950ef0SGhennadi Procopciuc } 726de950ef0SGhennadi Procopciuc 727de950ef0SGhennadi Procopciuc prate = pll->vco_freq; 728de950ef0SGhennadi Procopciuc 729de950ef0SGhennadi Procopciuc /** 730de950ef0SGhennadi Procopciuc * The PLL is not initialized yet, so let's take a risk 731de950ef0SGhennadi Procopciuc * and accept the proposed rate. 732de950ef0SGhennadi Procopciuc */ 733de950ef0SGhennadi Procopciuc if (prate == 0UL) { 734de950ef0SGhennadi Procopciuc pdiv->freq = rate; 735de950ef0SGhennadi Procopciuc *orate = rate; 736de950ef0SGhennadi Procopciuc return 0; 737de950ef0SGhennadi Procopciuc } 738de950ef0SGhennadi Procopciuc 739de950ef0SGhennadi Procopciuc /* Decline in case the rate cannot fit PLL's requirements. */ 740de950ef0SGhennadi Procopciuc dc = prate / rate; 741de950ef0SGhennadi Procopciuc if ((prate / dc) != rate) { 742de950ef0SGhennadi Procopciuc return -EINVAL; 743de950ef0SGhennadi Procopciuc } 744de950ef0SGhennadi Procopciuc 745de950ef0SGhennadi Procopciuc pdiv->freq = rate; 746de950ef0SGhennadi Procopciuc *orate = pdiv->freq; 747de950ef0SGhennadi Procopciuc 748de950ef0SGhennadi Procopciuc return 0; 749de950ef0SGhennadi Procopciuc } 750de950ef0SGhennadi Procopciuc 75165739db2SGhennadi Procopciuc static int set_fixed_div_freq(const struct s32cc_clk_obj *module, unsigned long rate, 75265739db2SGhennadi Procopciuc unsigned long *orate, unsigned int *depth) 75365739db2SGhennadi Procopciuc { 75465739db2SGhennadi Procopciuc const struct s32cc_fixed_div *fdiv = s32cc_obj2fixeddiv(module); 75565739db2SGhennadi Procopciuc int ret; 75665739db2SGhennadi Procopciuc 75765739db2SGhennadi Procopciuc ret = update_stack_depth(depth); 75865739db2SGhennadi Procopciuc if (ret != 0) { 75965739db2SGhennadi Procopciuc return ret; 76065739db2SGhennadi Procopciuc } 76165739db2SGhennadi Procopciuc 76265739db2SGhennadi Procopciuc if (fdiv->parent == NULL) { 76365739db2SGhennadi Procopciuc ERROR("The divider doesn't have a valid parent\b"); 76465739db2SGhennadi Procopciuc return -EINVAL; 76565739db2SGhennadi Procopciuc } 76665739db2SGhennadi Procopciuc 76765739db2SGhennadi Procopciuc ret = set_module_rate(fdiv->parent, rate * fdiv->rate_div, orate, depth); 76865739db2SGhennadi Procopciuc 76965739db2SGhennadi Procopciuc /* Update the output rate based on the parent's rate */ 77065739db2SGhennadi Procopciuc *orate /= fdiv->rate_div; 77165739db2SGhennadi Procopciuc 77265739db2SGhennadi Procopciuc return ret; 77365739db2SGhennadi Procopciuc } 77465739db2SGhennadi Procopciuc 77564e0c226SGhennadi Procopciuc static int set_mux_freq(const struct s32cc_clk_obj *module, unsigned long rate, 77664e0c226SGhennadi Procopciuc unsigned long *orate, unsigned int *depth) 77764e0c226SGhennadi Procopciuc { 77864e0c226SGhennadi Procopciuc const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module); 77964e0c226SGhennadi Procopciuc const struct s32cc_clk *clk = s32cc_get_arch_clk(mux->source_id); 78064e0c226SGhennadi Procopciuc int ret; 78164e0c226SGhennadi Procopciuc 78264e0c226SGhennadi Procopciuc ret = update_stack_depth(depth); 78364e0c226SGhennadi Procopciuc if (ret != 0) { 78464e0c226SGhennadi Procopciuc return ret; 78564e0c226SGhennadi Procopciuc } 78664e0c226SGhennadi Procopciuc 78764e0c226SGhennadi Procopciuc if (clk == NULL) { 78864e0c226SGhennadi Procopciuc ERROR("Mux (id:%" PRIu8 ") without a valid source (%lu)\n", 78964e0c226SGhennadi Procopciuc mux->index, mux->source_id); 79064e0c226SGhennadi Procopciuc return -EINVAL; 79164e0c226SGhennadi Procopciuc } 79264e0c226SGhennadi Procopciuc 79364e0c226SGhennadi Procopciuc return set_module_rate(&clk->desc, rate, orate, depth); 79464e0c226SGhennadi Procopciuc } 79564e0c226SGhennadi Procopciuc 796d9373519SGhennadi Procopciuc static int set_module_rate(const struct s32cc_clk_obj *module, 797d9373519SGhennadi Procopciuc unsigned long rate, unsigned long *orate, 798d9373519SGhennadi Procopciuc unsigned int *depth) 799d9373519SGhennadi Procopciuc { 800d9373519SGhennadi Procopciuc int ret = 0; 801d9373519SGhennadi Procopciuc 802d9373519SGhennadi Procopciuc ret = update_stack_depth(depth); 803d9373519SGhennadi Procopciuc if (ret != 0) { 804d9373519SGhennadi Procopciuc return ret; 805d9373519SGhennadi Procopciuc } 806d9373519SGhennadi Procopciuc 807d9373519SGhennadi Procopciuc switch (module->type) { 808d9373519SGhennadi Procopciuc case s32cc_clk_t: 809d9373519SGhennadi Procopciuc ret = set_clk_freq(module, rate, orate, depth); 810d9373519SGhennadi Procopciuc break; 811d9373519SGhennadi Procopciuc case s32cc_osc_t: 812d9373519SGhennadi Procopciuc ret = set_osc_freq(module, rate, orate, depth); 813d9373519SGhennadi Procopciuc break; 8147ad4e231SGhennadi Procopciuc case s32cc_pll_t: 8157ad4e231SGhennadi Procopciuc ret = set_pll_freq(module, rate, orate, depth); 8167ad4e231SGhennadi Procopciuc break; 817de950ef0SGhennadi Procopciuc case s32cc_pll_out_div_t: 818de950ef0SGhennadi Procopciuc ret = set_pll_div_freq(module, rate, orate, depth); 819de950ef0SGhennadi Procopciuc break; 82065739db2SGhennadi Procopciuc case s32cc_fixed_div_t: 82165739db2SGhennadi Procopciuc ret = set_fixed_div_freq(module, rate, orate, depth); 82265739db2SGhennadi Procopciuc break; 823a8be748aSGhennadi Procopciuc case s32cc_clkmux_t: 82464e0c226SGhennadi Procopciuc ret = set_mux_freq(module, rate, orate, depth); 82564e0c226SGhennadi Procopciuc break; 8263fa91a94SGhennadi Procopciuc case s32cc_shared_clkmux_t: 82764e0c226SGhennadi Procopciuc ret = set_mux_freq(module, rate, orate, depth); 828a8be748aSGhennadi Procopciuc break; 829d9373519SGhennadi Procopciuc default: 830d9373519SGhennadi Procopciuc ret = -EINVAL; 831d9373519SGhennadi Procopciuc break; 832d9373519SGhennadi Procopciuc } 833d9373519SGhennadi Procopciuc 834d9373519SGhennadi Procopciuc return ret; 835d9373519SGhennadi Procopciuc } 836d9373519SGhennadi Procopciuc 8373a580e9eSGhennadi Procopciuc static int s32cc_clk_set_rate(unsigned long id, unsigned long rate, 8383a580e9eSGhennadi Procopciuc unsigned long *orate) 8393a580e9eSGhennadi Procopciuc { 840d9373519SGhennadi Procopciuc unsigned int depth = MAX_STACK_DEPTH; 841d9373519SGhennadi Procopciuc const struct s32cc_clk *clk; 842d9373519SGhennadi Procopciuc int ret; 843d9373519SGhennadi Procopciuc 844d9373519SGhennadi Procopciuc clk = s32cc_get_arch_clk(id); 845d9373519SGhennadi Procopciuc if (clk == NULL) { 846d9373519SGhennadi Procopciuc return -EINVAL; 847d9373519SGhennadi Procopciuc } 848d9373519SGhennadi Procopciuc 849d9373519SGhennadi Procopciuc ret = set_module_rate(&clk->desc, rate, orate, &depth); 850d9373519SGhennadi Procopciuc if (ret != 0) { 851d9373519SGhennadi Procopciuc ERROR("Failed to set frequency (%lu MHz) for clock %lu\n", 852d9373519SGhennadi Procopciuc rate, id); 853d9373519SGhennadi Procopciuc } 854d9373519SGhennadi Procopciuc 855d9373519SGhennadi Procopciuc return ret; 8563a580e9eSGhennadi Procopciuc } 8573a580e9eSGhennadi Procopciuc 8583a580e9eSGhennadi Procopciuc static int s32cc_clk_get_parent(unsigned long id) 8593a580e9eSGhennadi Procopciuc { 8603a580e9eSGhennadi Procopciuc return -ENOTSUP; 8613a580e9eSGhennadi Procopciuc } 8623a580e9eSGhennadi Procopciuc 8633a580e9eSGhennadi Procopciuc static int s32cc_clk_set_parent(unsigned long id, unsigned long parent_id) 8643a580e9eSGhennadi Procopciuc { 86512e7a2cdSGhennadi Procopciuc const struct s32cc_clk *parent; 86612e7a2cdSGhennadi Procopciuc const struct s32cc_clk *clk; 86712e7a2cdSGhennadi Procopciuc bool valid_source = false; 86812e7a2cdSGhennadi Procopciuc struct s32cc_clkmux *mux; 86912e7a2cdSGhennadi Procopciuc uint8_t i; 87012e7a2cdSGhennadi Procopciuc 87112e7a2cdSGhennadi Procopciuc clk = s32cc_get_arch_clk(id); 87212e7a2cdSGhennadi Procopciuc if (clk == NULL) { 87312e7a2cdSGhennadi Procopciuc return -EINVAL; 87412e7a2cdSGhennadi Procopciuc } 87512e7a2cdSGhennadi Procopciuc 87612e7a2cdSGhennadi Procopciuc parent = s32cc_get_arch_clk(parent_id); 87712e7a2cdSGhennadi Procopciuc if (parent == NULL) { 87812e7a2cdSGhennadi Procopciuc return -EINVAL; 87912e7a2cdSGhennadi Procopciuc } 88012e7a2cdSGhennadi Procopciuc 88112e7a2cdSGhennadi Procopciuc if (!is_s32cc_clk_mux(clk)) { 88212e7a2cdSGhennadi Procopciuc ERROR("Clock %lu is not a mux\n", id); 88312e7a2cdSGhennadi Procopciuc return -EINVAL; 88412e7a2cdSGhennadi Procopciuc } 88512e7a2cdSGhennadi Procopciuc 88612e7a2cdSGhennadi Procopciuc mux = s32cc_clk2mux(clk); 88712e7a2cdSGhennadi Procopciuc if (mux == NULL) { 88812e7a2cdSGhennadi Procopciuc ERROR("Failed to cast clock %lu to clock mux\n", id); 88912e7a2cdSGhennadi Procopciuc return -EINVAL; 89012e7a2cdSGhennadi Procopciuc } 89112e7a2cdSGhennadi Procopciuc 89212e7a2cdSGhennadi Procopciuc for (i = 0; i < mux->nclks; i++) { 89312e7a2cdSGhennadi Procopciuc if (mux->clkids[i] == parent_id) { 89412e7a2cdSGhennadi Procopciuc valid_source = true; 89512e7a2cdSGhennadi Procopciuc break; 89612e7a2cdSGhennadi Procopciuc } 89712e7a2cdSGhennadi Procopciuc } 89812e7a2cdSGhennadi Procopciuc 89912e7a2cdSGhennadi Procopciuc if (!valid_source) { 90012e7a2cdSGhennadi Procopciuc ERROR("Clock %lu is not a valid clock for mux %lu\n", 90112e7a2cdSGhennadi Procopciuc parent_id, id); 90212e7a2cdSGhennadi Procopciuc return -EINVAL; 90312e7a2cdSGhennadi Procopciuc } 90412e7a2cdSGhennadi Procopciuc 90512e7a2cdSGhennadi Procopciuc mux->source_id = parent_id; 90612e7a2cdSGhennadi Procopciuc 90712e7a2cdSGhennadi Procopciuc return 0; 9083a580e9eSGhennadi Procopciuc } 9093a580e9eSGhennadi Procopciuc 9103a580e9eSGhennadi Procopciuc void s32cc_clk_register_drv(void) 9113a580e9eSGhennadi Procopciuc { 9123a580e9eSGhennadi Procopciuc static const struct clk_ops s32cc_clk_ops = { 9133a580e9eSGhennadi Procopciuc .enable = s32cc_clk_enable, 9143a580e9eSGhennadi Procopciuc .disable = s32cc_clk_disable, 9153a580e9eSGhennadi Procopciuc .is_enabled = s32cc_clk_is_enabled, 9163a580e9eSGhennadi Procopciuc .get_rate = s32cc_clk_get_rate, 9173a580e9eSGhennadi Procopciuc .set_rate = s32cc_clk_set_rate, 9183a580e9eSGhennadi Procopciuc .get_parent = s32cc_clk_get_parent, 9193a580e9eSGhennadi Procopciuc .set_parent = s32cc_clk_set_parent, 9203a580e9eSGhennadi Procopciuc }; 9213a580e9eSGhennadi Procopciuc 9223a580e9eSGhennadi Procopciuc clk_register(&s32cc_clk_ops); 9233a580e9eSGhennadi Procopciuc } 9243a580e9eSGhennadi Procopciuc 925