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