summaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2/cm_common.c
blob: c6237df288d859f170fa87ff33ca3ae034e95ca5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
 * OMAP2+ common Clock Management (CM) IP block functions
 *
 * Copyright (C) 2012 Texas Instruments, Inc.
 * Paul Walmsley
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * XXX This code should eventually be moved to a CM driver.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/bug.h>

#include "cm2xxx.h"
#include "cm3xxx.h"
#include "cm44xx.h"

/*
 * cm_ll_data: function pointers to SoC-specific implementations of
 * common CM functions
 */
static struct cm_ll_data null_cm_ll_data;
static struct cm_ll_data *cm_ll_data = &null_cm_ll_data;

/* cm_base: base virtual address of the CM IP block */
void __iomem *cm_base;

/* cm2_base: base virtual address of the CM2 IP block (OMAP44xx only) */
void __iomem *cm2_base;

/**
 * omap2_set_globals_cm - set the CM/CM2 base addresses (for early use)
 * @cm: CM base virtual address
 * @cm2: CM2 base virtual address (if present on the booted SoC)
 *
 * XXX Will be replaced when the PRM/CM drivers are completed.
 */
void __init omap2_set_globals_cm(void __iomem *cm, void __iomem *cm2)
{
	cm_base = cm;
	cm2_base = cm2;
}

/**
 * cm_split_idlest_reg - split CM_IDLEST reg addr into its components
 * @idlest_reg: CM_IDLEST* virtual address
 * @prcm_inst: pointer to an s16 to return the PRCM instance offset
 * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID
 *
 * Given an absolute CM_IDLEST register address @idlest_reg, passes
 * the PRCM instance offset and IDLEST register ID back to the caller
 * via the @prcm_inst and @idlest_reg_id.  Returns -EINVAL upon error,
 * or 0 upon success.  XXX This function is only needed until absolute
 * register addresses are removed from the OMAP struct clk records.
 */
int cm_split_idlest_reg(void __iomem *idlest_reg, s16 *prcm_inst,
			u8 *idlest_reg_id)
{
	if (!cm_ll_data->split_idlest_reg) {
		WARN_ONCE(1, "cm: %s: no low-level function defined\n",
			  __func__);
		return -EINVAL;
	}

	return cm_ll_data->split_idlest_reg(idlest_reg, prcm_inst,
					   idlest_reg_id);
}

/**
 * omap_cm_wait_module_ready - wait for a module to leave idle or standby
 * @part: PRCM partition
 * @prcm_mod: PRCM module offset
 * @idlest_reg: CM_IDLESTx register
 * @idlest_shift: shift of the bit in the CM_IDLEST* register to check
 *
 * Wait for the PRCM to indicate that the module identified by
 * (@prcm_mod, @idlest_id, @idlest_shift) is clocked.  Return 0 upon
 * success, -EBUSY if the module doesn't enable in time, or -EINVAL if
 * no per-SoC wait_module_ready() function pointer has been registered
 * or if the idlest register is unknown on the SoC.
 */
int omap_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_reg,
			      u8 idlest_shift)
{
	if (!cm_ll_data->wait_module_ready) {
		WARN_ONCE(1, "cm: %s: no low-level function defined\n",
			  __func__);
		return -EINVAL;
	}

	return cm_ll_data->wait_module_ready(part, prcm_mod, idlest_reg,
					     idlest_shift);
}

/**
 * omap_cm_wait_module_idle - wait for a module to enter idle or standby
 * @part: PRCM partition
 * @prcm_mod: PRCM module offset
 * @idlest_reg: CM_IDLESTx register
 * @idlest_shift: shift of the bit in the CM_IDLEST* register to check
 *
 * Wait for the PRCM to indicate that the module identified by
 * (@prcm_mod, @idlest_id, @idlest_shift) is no longer clocked.  Return
 * 0 upon success, -EBUSY if the module doesn't enable in time, or
 * -EINVAL if no per-SoC wait_module_idle() function pointer has been
 * registered or if the idlest register is unknown on the SoC.
 */
int omap_cm_wait_module_idle(u8 part, s16 prcm_mod, u16 idlest_reg,
			     u8 idlest_shift)
{
	if (!cm_ll_data->wait_module_idle) {
		WARN_ONCE(1, "cm: %s: no low-level function defined\n",
			  __func__);
		return -EINVAL;
	}

	return cm_ll_data->wait_module_idle(part, prcm_mod, idlest_reg,
					    idlest_shift);
}

/**
 * cm_register - register per-SoC low-level data with the CM
 * @cld: low-level per-SoC OMAP CM data & function pointers to register
 *
 * Register per-SoC low-level OMAP CM data and function pointers with
 * the OMAP CM common interface.  The caller must keep the data
 * pointed to by @cld valid until it calls cm_unregister() and
 * it returns successfully.  Returns 0 upon success, -EINVAL if @cld
 * is NULL, or -EEXIST if cm_register() has already been called
 * without an intervening cm_unregister().
 */
int cm_register(struct cm_ll_data *cld)
{
	if (!cld)
		return -EINVAL;

	if (cm_ll_data != &null_cm_ll_data)
		return -EEXIST;

	cm_ll_data = cld;

	return 0;
}

/**
 * cm_unregister - unregister per-SoC low-level data & function pointers
 * @cld: low-level per-SoC OMAP CM data & function pointers to unregister
 *
 * Unregister per-SoC low-level OMAP CM data and function pointers
 * that were previously registered with cm_register().  The
 * caller may not destroy any of the data pointed to by @cld until
 * this function returns successfully.  Returns 0 upon success, or
 * -EINVAL if @cld is NULL or if @cld does not match the struct
 * cm_ll_data * previously registered by cm_register().
 */
int cm_unregister(struct cm_ll_data *cld)
{
	if (!cld || cm_ll_data != cld)
		return -EINVAL;

	cm_ll_data = &null_cm_ll_data;

	return 0;
}