162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP2+ common Clock Management (CM) IP block functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Paul Walmsley 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * XXX This code should eventually be moved to a CM driver. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/bug.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "cm2xxx.h" 1962306a36Sopenharmony_ci#include "cm3xxx.h" 2062306a36Sopenharmony_ci#include "cm33xx.h" 2162306a36Sopenharmony_ci#include "cm44xx.h" 2262306a36Sopenharmony_ci#include "clock.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * cm_ll_data: function pointers to SoC-specific implementations of 2662306a36Sopenharmony_ci * common CM functions 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic struct cm_ll_data null_cm_ll_data; 2962306a36Sopenharmony_cistatic const struct cm_ll_data *cm_ll_data = &null_cm_ll_data; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* cm_base: base virtual address of the CM IP block */ 3262306a36Sopenharmony_cistruct omap_domain_base cm_base; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* cm2_base: base virtual address of the CM2 IP block (OMAP44xx only) */ 3562306a36Sopenharmony_cistruct omap_domain_base cm2_base; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define CM_NO_CLOCKS 0x1 3862306a36Sopenharmony_ci#define CM_SINGLE_INSTANCE 0x2 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * cm_split_idlest_reg - split CM_IDLEST reg addr into its components 4262306a36Sopenharmony_ci * @idlest_reg: CM_IDLEST* virtual address 4362306a36Sopenharmony_ci * @prcm_inst: pointer to an s16 to return the PRCM instance offset 4462306a36Sopenharmony_ci * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Given an absolute CM_IDLEST register address @idlest_reg, passes 4762306a36Sopenharmony_ci * the PRCM instance offset and IDLEST register ID back to the caller 4862306a36Sopenharmony_ci * via the @prcm_inst and @idlest_reg_id. Returns -EINVAL upon error, 4962306a36Sopenharmony_ci * or 0 upon success. XXX This function is only needed until absolute 5062306a36Sopenharmony_ci * register addresses are removed from the OMAP struct clk records. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ciint cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, s16 *prcm_inst, 5362306a36Sopenharmony_ci u8 *idlest_reg_id) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci int ret; 5662306a36Sopenharmony_ci if (!cm_ll_data->split_idlest_reg) { 5762306a36Sopenharmony_ci WARN_ONCE(1, "cm: %s: no low-level function defined\n", 5862306a36Sopenharmony_ci __func__); 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ret = cm_ll_data->split_idlest_reg(idlest_reg, prcm_inst, 6362306a36Sopenharmony_ci idlest_reg_id); 6462306a36Sopenharmony_ci *prcm_inst -= cm_base.offset; 6562306a36Sopenharmony_ci return ret; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/** 6962306a36Sopenharmony_ci * omap_cm_wait_module_ready - wait for a module to leave idle or standby 7062306a36Sopenharmony_ci * @part: PRCM partition 7162306a36Sopenharmony_ci * @prcm_mod: PRCM module offset 7262306a36Sopenharmony_ci * @idlest_reg: CM_IDLESTx register 7362306a36Sopenharmony_ci * @idlest_shift: shift of the bit in the CM_IDLEST* register to check 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * Wait for the PRCM to indicate that the module identified by 7662306a36Sopenharmony_ci * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon 7762306a36Sopenharmony_ci * success, -EBUSY if the module doesn't enable in time, or -EINVAL if 7862306a36Sopenharmony_ci * no per-SoC wait_module_ready() function pointer has been registered 7962306a36Sopenharmony_ci * or if the idlest register is unknown on the SoC. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ciint omap_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_reg, 8262306a36Sopenharmony_ci u8 idlest_shift) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci if (!cm_ll_data->wait_module_ready) { 8562306a36Sopenharmony_ci WARN_ONCE(1, "cm: %s: no low-level function defined\n", 8662306a36Sopenharmony_ci __func__); 8762306a36Sopenharmony_ci return -EINVAL; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return cm_ll_data->wait_module_ready(part, prcm_mod, idlest_reg, 9162306a36Sopenharmony_ci idlest_shift); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * omap_cm_wait_module_idle - wait for a module to enter idle or standby 9662306a36Sopenharmony_ci * @part: PRCM partition 9762306a36Sopenharmony_ci * @prcm_mod: PRCM module offset 9862306a36Sopenharmony_ci * @idlest_reg: CM_IDLESTx register 9962306a36Sopenharmony_ci * @idlest_shift: shift of the bit in the CM_IDLEST* register to check 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Wait for the PRCM to indicate that the module identified by 10262306a36Sopenharmony_ci * (@prcm_mod, @idlest_id, @idlest_shift) is no longer clocked. Return 10362306a36Sopenharmony_ci * 0 upon success, -EBUSY if the module doesn't enable in time, or 10462306a36Sopenharmony_ci * -EINVAL if no per-SoC wait_module_idle() function pointer has been 10562306a36Sopenharmony_ci * registered or if the idlest register is unknown on the SoC. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ciint omap_cm_wait_module_idle(u8 part, s16 prcm_mod, u16 idlest_reg, 10862306a36Sopenharmony_ci u8 idlest_shift) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci if (!cm_ll_data->wait_module_idle) { 11162306a36Sopenharmony_ci WARN_ONCE(1, "cm: %s: no low-level function defined\n", 11262306a36Sopenharmony_ci __func__); 11362306a36Sopenharmony_ci return -EINVAL; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return cm_ll_data->wait_module_idle(part, prcm_mod, idlest_reg, 11762306a36Sopenharmony_ci idlest_shift); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * omap_cm_module_enable - enable a module 12262306a36Sopenharmony_ci * @mode: target mode for the module 12362306a36Sopenharmony_ci * @part: PRCM partition 12462306a36Sopenharmony_ci * @inst: PRCM instance 12562306a36Sopenharmony_ci * @clkctrl_offs: CM_CLKCTRL register offset for the module 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Enables clocks for a module identified by (@part, @inst, @clkctrl_offs) 12862306a36Sopenharmony_ci * making its IO space accessible. Return 0 upon success, -EINVAL if no 12962306a36Sopenharmony_ci * per-SoC module_enable() function pointer has been registered. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ciint omap_cm_module_enable(u8 mode, u8 part, u16 inst, u16 clkctrl_offs) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci if (!cm_ll_data->module_enable) { 13462306a36Sopenharmony_ci WARN_ONCE(1, "cm: %s: no low-level function defined\n", 13562306a36Sopenharmony_ci __func__); 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci cm_ll_data->module_enable(mode, part, inst, clkctrl_offs); 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/** 14462306a36Sopenharmony_ci * omap_cm_module_disable - disable a module 14562306a36Sopenharmony_ci * @part: PRCM partition 14662306a36Sopenharmony_ci * @inst: PRCM instance 14762306a36Sopenharmony_ci * @clkctrl_offs: CM_CLKCTRL register offset for the module 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * Disables clocks for a module identified by (@part, @inst, @clkctrl_offs) 15062306a36Sopenharmony_ci * makings its IO space inaccessible. Return 0 upon success, -EINVAL if 15162306a36Sopenharmony_ci * no per-SoC module_disable() function pointer has been registered. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ciint omap_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci if (!cm_ll_data->module_disable) { 15662306a36Sopenharmony_ci WARN_ONCE(1, "cm: %s: no low-level function defined\n", 15762306a36Sopenharmony_ci __func__); 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci cm_ll_data->module_disable(part, inst, clkctrl_offs); 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciu32 omap_cm_xlate_clkctrl(u8 part, u16 inst, u16 clkctrl_offs) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (!cm_ll_data->xlate_clkctrl) { 16862306a36Sopenharmony_ci WARN_ONCE(1, "cm: %s: no low-level function defined\n", 16962306a36Sopenharmony_ci __func__); 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci return cm_ll_data->xlate_clkctrl(part, inst, clkctrl_offs); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/** 17662306a36Sopenharmony_ci * cm_register - register per-SoC low-level data with the CM 17762306a36Sopenharmony_ci * @cld: low-level per-SoC OMAP CM data & function pointers to register 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * Register per-SoC low-level OMAP CM data and function pointers with 18062306a36Sopenharmony_ci * the OMAP CM common interface. The caller must keep the data 18162306a36Sopenharmony_ci * pointed to by @cld valid until it calls cm_unregister() and 18262306a36Sopenharmony_ci * it returns successfully. Returns 0 upon success, -EINVAL if @cld 18362306a36Sopenharmony_ci * is NULL, or -EEXIST if cm_register() has already been called 18462306a36Sopenharmony_ci * without an intervening cm_unregister(). 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ciint cm_register(const struct cm_ll_data *cld) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (!cld) 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (cm_ll_data != &null_cm_ll_data) 19262306a36Sopenharmony_ci return -EEXIST; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci cm_ll_data = cld; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/** 20062306a36Sopenharmony_ci * cm_unregister - unregister per-SoC low-level data & function pointers 20162306a36Sopenharmony_ci * @cld: low-level per-SoC OMAP CM data & function pointers to unregister 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Unregister per-SoC low-level OMAP CM data and function pointers 20462306a36Sopenharmony_ci * that were previously registered with cm_register(). The 20562306a36Sopenharmony_ci * caller may not destroy any of the data pointed to by @cld until 20662306a36Sopenharmony_ci * this function returns successfully. Returns 0 upon success, or 20762306a36Sopenharmony_ci * -EINVAL if @cld is NULL or if @cld does not match the struct 20862306a36Sopenharmony_ci * cm_ll_data * previously registered by cm_register(). 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ciint cm_unregister(const struct cm_ll_data *cld) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci if (!cld || cm_ll_data != cld) 21362306a36Sopenharmony_ci return -EINVAL; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci cm_ll_data = &null_cm_ll_data; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \ 22162306a36Sopenharmony_ci defined(CONFIG_SOC_DRA7XX) 22262306a36Sopenharmony_cistatic struct omap_prcm_init_data cm_data __initdata = { 22362306a36Sopenharmony_ci .index = TI_CLKM_CM, 22462306a36Sopenharmony_ci .init = omap4_cm_init, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic struct omap_prcm_init_data cm2_data __initdata = { 22862306a36Sopenharmony_ci .index = TI_CLKM_CM2, 22962306a36Sopenharmony_ci .init = omap4_cm_init, 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci#endif 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP2 23462306a36Sopenharmony_cistatic struct omap_prcm_init_data omap2_prcm_data __initdata = { 23562306a36Sopenharmony_ci .index = TI_CLKM_CM, 23662306a36Sopenharmony_ci .init = omap2xxx_cm_init, 23762306a36Sopenharmony_ci .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci#endif 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP3 24262306a36Sopenharmony_cistatic struct omap_prcm_init_data omap3_cm_data __initdata = { 24362306a36Sopenharmony_ci .index = TI_CLKM_CM, 24462306a36Sopenharmony_ci .init = omap3xxx_cm_init, 24562306a36Sopenharmony_ci .flags = CM_SINGLE_INSTANCE, 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * IVA2 offset is a negative value, must offset the cm_base address 24962306a36Sopenharmony_ci * by this to get it to positive side on the iomap 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci .offset = -OMAP3430_IVA2_MOD, 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci#endif 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_TI81XX) 25662306a36Sopenharmony_cistatic struct omap_prcm_init_data am3_prcm_data __initdata = { 25762306a36Sopenharmony_ci .index = TI_CLKM_CM, 25862306a36Sopenharmony_ci .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, 25962306a36Sopenharmony_ci .init = am33xx_cm_init, 26062306a36Sopenharmony_ci}; 26162306a36Sopenharmony_ci#endif 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci#ifdef CONFIG_SOC_AM43XX 26462306a36Sopenharmony_cistatic struct omap_prcm_init_data am4_prcm_data __initdata = { 26562306a36Sopenharmony_ci .index = TI_CLKM_CM, 26662306a36Sopenharmony_ci .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, 26762306a36Sopenharmony_ci .init = omap4_cm_init, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci#endif 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct of_device_id omap_cm_dt_match_table[] __initconst = { 27262306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP2 27362306a36Sopenharmony_ci { .compatible = "ti,omap2-prcm", .data = &omap2_prcm_data }, 27462306a36Sopenharmony_ci#endif 27562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP3 27662306a36Sopenharmony_ci { .compatible = "ti,omap3-cm", .data = &omap3_cm_data }, 27762306a36Sopenharmony_ci#endif 27862306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP4 27962306a36Sopenharmony_ci { .compatible = "ti,omap4-cm1", .data = &cm_data }, 28062306a36Sopenharmony_ci { .compatible = "ti,omap4-cm2", .data = &cm2_data }, 28162306a36Sopenharmony_ci#endif 28262306a36Sopenharmony_ci#ifdef CONFIG_SOC_OMAP5 28362306a36Sopenharmony_ci { .compatible = "ti,omap5-cm-core-aon", .data = &cm_data }, 28462306a36Sopenharmony_ci { .compatible = "ti,omap5-cm-core", .data = &cm2_data }, 28562306a36Sopenharmony_ci#endif 28662306a36Sopenharmony_ci#ifdef CONFIG_SOC_DRA7XX 28762306a36Sopenharmony_ci { .compatible = "ti,dra7-cm-core-aon", .data = &cm_data }, 28862306a36Sopenharmony_ci { .compatible = "ti,dra7-cm-core", .data = &cm2_data }, 28962306a36Sopenharmony_ci#endif 29062306a36Sopenharmony_ci#ifdef CONFIG_SOC_AM33XX 29162306a36Sopenharmony_ci { .compatible = "ti,am3-prcm", .data = &am3_prcm_data }, 29262306a36Sopenharmony_ci#endif 29362306a36Sopenharmony_ci#ifdef CONFIG_SOC_AM43XX 29462306a36Sopenharmony_ci { .compatible = "ti,am4-prcm", .data = &am4_prcm_data }, 29562306a36Sopenharmony_ci#endif 29662306a36Sopenharmony_ci#ifdef CONFIG_SOC_TI81XX 29762306a36Sopenharmony_ci { .compatible = "ti,dm814-prcm", .data = &am3_prcm_data }, 29862306a36Sopenharmony_ci { .compatible = "ti,dm816-prcm", .data = &am3_prcm_data }, 29962306a36Sopenharmony_ci#endif 30062306a36Sopenharmony_ci { } 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/** 30462306a36Sopenharmony_ci * omap2_cm_base_init - initialize iomappings for the CM drivers 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * Detects and initializes the iomappings for the CM driver, based 30762306a36Sopenharmony_ci * on the DT data. Returns 0 in success, negative error value 30862306a36Sopenharmony_ci * otherwise. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ciint __init omap2_cm_base_init(void) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct device_node *np; 31362306a36Sopenharmony_ci const struct of_device_id *match; 31462306a36Sopenharmony_ci struct omap_prcm_init_data *data; 31562306a36Sopenharmony_ci struct resource res; 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci struct omap_domain_base *mem = NULL; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { 32062306a36Sopenharmony_ci data = (struct omap_prcm_init_data *)match->data; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 32362306a36Sopenharmony_ci if (ret) { 32462306a36Sopenharmony_ci of_node_put(np); 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (data->index == TI_CLKM_CM) 32962306a36Sopenharmony_ci mem = &cm_base; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (data->index == TI_CLKM_CM2) 33262306a36Sopenharmony_ci mem = &cm2_base; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci data->mem = ioremap(res.start, resource_size(&res)); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (mem) { 33762306a36Sopenharmony_ci mem->pa = res.start + data->offset; 33862306a36Sopenharmony_ci mem->va = data->mem + data->offset; 33962306a36Sopenharmony_ci mem->offset = data->offset; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci data->np = np; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (data->init && (data->flags & CM_SINGLE_INSTANCE || 34562306a36Sopenharmony_ci (cm_base.va && cm2_base.va))) 34662306a36Sopenharmony_ci data->init(data); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/** 35362306a36Sopenharmony_ci * omap_cm_init - low level init for the CM drivers 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * Initializes the low level clock infrastructure for CM drivers. 35662306a36Sopenharmony_ci * Returns 0 in success, negative error value in failure. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ciint __init omap_cm_init(void) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct device_node *np; 36162306a36Sopenharmony_ci const struct of_device_id *match; 36262306a36Sopenharmony_ci const struct omap_prcm_init_data *data; 36362306a36Sopenharmony_ci int ret; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { 36662306a36Sopenharmony_ci data = match->data; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (data->flags & CM_NO_CLOCKS) 36962306a36Sopenharmony_ci continue; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = omap2_clk_provider_init(np, data->index, NULL, data->mem); 37262306a36Sopenharmony_ci if (ret) { 37362306a36Sopenharmony_ci of_node_put(np); 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 380