Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/mach-omap2/cm33xx.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* AM33XX CM functions
4
*
5
* Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/
6
* Vaibhav Hiremath <[email protected]>
7
*
8
* Reference taken from OMAP4 cminst44xx.c
9
*/
10
11
#include <linux/kernel.h>
12
#include <linux/types.h>
13
#include <linux/errno.h>
14
#include <linux/err.h>
15
#include <linux/io.h>
16
17
#include "clockdomain.h"
18
#include "cm.h"
19
#include "cm33xx.h"
20
#include "cm-regbits-34xx.h"
21
#include "cm-regbits-33xx.h"
22
#include "prm33xx.h"
23
#if IS_ENABLED(CONFIG_SUSPEND)
24
#include <linux/suspend.h>
25
#endif
26
27
/*
28
* CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield:
29
*
30
* 0x0 func: Module is fully functional, including OCP
31
* 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep
32
* abortion
33
* 0x2 idle: Module is in Idle mode (only OCP part). It is functional if
34
* using separate functional clock
35
* 0x3 disabled: Module is disabled and cannot be accessed
36
*
37
*/
38
#define CLKCTRL_IDLEST_FUNCTIONAL 0x0
39
#define CLKCTRL_IDLEST_INTRANSITION 0x1
40
#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2
41
#define CLKCTRL_IDLEST_DISABLED 0x3
42
43
/* Private functions */
44
45
/* Read a register in a CM instance */
46
static inline u32 am33xx_cm_read_reg(u16 inst, u16 idx)
47
{
48
return readl_relaxed(cm_base.va + inst + idx);
49
}
50
51
/* Write into a register in a CM */
52
static inline void am33xx_cm_write_reg(u32 val, u16 inst, u16 idx)
53
{
54
writel_relaxed(val, cm_base.va + inst + idx);
55
}
56
57
/* Read-modify-write a register in CM */
58
static inline u32 am33xx_cm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx)
59
{
60
u32 v;
61
62
v = am33xx_cm_read_reg(inst, idx);
63
v &= ~mask;
64
v |= bits;
65
am33xx_cm_write_reg(v, inst, idx);
66
67
return v;
68
}
69
70
static inline u32 am33xx_cm_read_reg_bits(u16 inst, s16 idx, u32 mask)
71
{
72
u32 v;
73
74
v = am33xx_cm_read_reg(inst, idx);
75
v &= mask;
76
v >>= __ffs(mask);
77
78
return v;
79
}
80
81
/**
82
* _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
83
* @inst: CM instance register offset (*_INST macro)
84
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
85
*
86
* Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
87
* bit 0.
88
*/
89
static u32 _clkctrl_idlest(u16 inst, u16 clkctrl_offs)
90
{
91
u32 v = am33xx_cm_read_reg(inst, clkctrl_offs);
92
v &= AM33XX_IDLEST_MASK;
93
v >>= AM33XX_IDLEST_SHIFT;
94
return v;
95
}
96
97
/**
98
* _is_module_ready - can module registers be accessed without causing an abort?
99
* @inst: CM instance register offset (*_INST macro)
100
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
101
*
102
* Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
103
* *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
104
*/
105
static bool _is_module_ready(u16 inst, u16 clkctrl_offs)
106
{
107
u32 v;
108
109
v = _clkctrl_idlest(inst, clkctrl_offs);
110
111
return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
112
v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
113
}
114
115
/**
116
* _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield
117
* @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted)
118
* @inst: CM instance register offset (*_INST macro)
119
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
120
*
121
* @c must be the unshifted value for CLKTRCTRL - i.e., this function
122
* will handle the shift itself.
123
*/
124
static void _clktrctrl_write(u8 c, u16 inst, u16 cdoffs)
125
{
126
u32 v;
127
128
v = am33xx_cm_read_reg(inst, cdoffs);
129
v &= ~AM33XX_CLKTRCTRL_MASK;
130
v |= c << AM33XX_CLKTRCTRL_SHIFT;
131
am33xx_cm_write_reg(v, inst, cdoffs);
132
}
133
134
/* Public functions */
135
136
/**
137
* am33xx_cm_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode?
138
* @inst: CM instance register offset (*_INST macro)
139
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
140
*
141
* Returns true if the clockdomain referred to by (@inst, @cdoffs)
142
* is in hardware-supervised idle mode, or 0 otherwise.
143
*/
144
static bool am33xx_cm_is_clkdm_in_hwsup(u16 inst, u16 cdoffs)
145
{
146
u32 v;
147
148
v = am33xx_cm_read_reg(inst, cdoffs);
149
v &= AM33XX_CLKTRCTRL_MASK;
150
v >>= AM33XX_CLKTRCTRL_SHIFT;
151
152
return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false;
153
}
154
155
/**
156
* am33xx_cm_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode
157
* @inst: CM instance register offset (*_INST macro)
158
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
159
*
160
* Put a clockdomain referred to by (@inst, @cdoffs) into
161
* hardware-supervised idle mode. No return value.
162
*/
163
static void am33xx_cm_clkdm_enable_hwsup(u16 inst, u16 cdoffs)
164
{
165
_clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, inst, cdoffs);
166
}
167
168
/**
169
* am33xx_cm_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode
170
* @inst: CM instance register offset (*_INST macro)
171
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
172
*
173
* Put a clockdomain referred to by (@inst, @cdoffs) into
174
* software-supervised idle mode, i.e., controlled manually by the
175
* Linux OMAP clockdomain code. No return value.
176
*/
177
static void am33xx_cm_clkdm_disable_hwsup(u16 inst, u16 cdoffs)
178
{
179
_clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, inst, cdoffs);
180
}
181
182
/**
183
* am33xx_cm_clkdm_force_sleep - try to put a clockdomain into idle
184
* @inst: CM instance register offset (*_INST macro)
185
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
186
*
187
* Put a clockdomain referred to by (@inst, @cdoffs) into idle
188
* No return value.
189
*/
190
static void am33xx_cm_clkdm_force_sleep(u16 inst, u16 cdoffs)
191
{
192
_clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, inst, cdoffs);
193
}
194
195
/**
196
* am33xx_cm_clkdm_force_wakeup - try to take a clockdomain out of idle
197
* @inst: CM instance register offset (*_INST macro)
198
* @cdoffs: Clockdomain register offset (*_CDOFFS macro)
199
*
200
* Take a clockdomain referred to by (@inst, @cdoffs) out of idle,
201
* waking it up. No return value.
202
*/
203
static void am33xx_cm_clkdm_force_wakeup(u16 inst, u16 cdoffs)
204
{
205
_clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, inst, cdoffs);
206
}
207
208
/*
209
*
210
*/
211
212
/**
213
* am33xx_cm_wait_module_ready - wait for a module to be in 'func' state
214
* @part: PRCM partition, ignored for AM33xx
215
* @inst: CM instance register offset (*_INST macro)
216
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
217
* @bit_shift: bit shift for the register, ignored for AM33xx
218
*
219
* Wait for the module IDLEST to be functional. If the idle state is in any
220
* the non functional state (trans, idle or disabled), module and thus the
221
* sysconfig cannot be accessed and will probably lead to an "imprecise
222
* external abort"
223
*/
224
static int am33xx_cm_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs,
225
u8 bit_shift)
226
{
227
int i = 0;
228
229
omap_test_timeout(_is_module_ready(inst, clkctrl_offs),
230
MAX_MODULE_READY_TIME, i);
231
232
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
233
}
234
235
/**
236
* am33xx_cm_wait_module_idle - wait for a module to be in 'disabled'
237
* state
238
* @part: CM partition, ignored for AM33xx
239
* @inst: CM instance register offset (*_INST macro)
240
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
241
* @bit_shift: bit shift for the register, ignored for AM33xx
242
*
243
* Wait for the module IDLEST to be disabled. Some PRCM transition,
244
* like reset assertion or parent clock de-activation must wait the
245
* module to be fully disabled.
246
*/
247
static int am33xx_cm_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs,
248
u8 bit_shift)
249
{
250
int i = 0;
251
252
omap_test_timeout((_clkctrl_idlest(inst, clkctrl_offs) ==
253
CLKCTRL_IDLEST_DISABLED),
254
MAX_MODULE_READY_TIME, i);
255
256
return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
257
}
258
259
/**
260
* am33xx_cm_module_enable - Enable the modulemode inside CLKCTRL
261
* @mode: Module mode (SW or HW)
262
* @part: CM partition, ignored for AM33xx
263
* @inst: CM instance register offset (*_INST macro)
264
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
265
*
266
* No return value.
267
*/
268
static void am33xx_cm_module_enable(u8 mode, u8 part, u16 inst,
269
u16 clkctrl_offs)
270
{
271
u32 v;
272
273
v = am33xx_cm_read_reg(inst, clkctrl_offs);
274
v &= ~AM33XX_MODULEMODE_MASK;
275
v |= mode << AM33XX_MODULEMODE_SHIFT;
276
am33xx_cm_write_reg(v, inst, clkctrl_offs);
277
}
278
279
/**
280
* am33xx_cm_module_disable - Disable the module inside CLKCTRL
281
* @part: CM partition, ignored for AM33xx
282
* @inst: CM instance register offset (*_INST macro)
283
* @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
284
*
285
* No return value.
286
*/
287
static void am33xx_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs)
288
{
289
u32 v;
290
291
v = am33xx_cm_read_reg(inst, clkctrl_offs);
292
v &= ~AM33XX_MODULEMODE_MASK;
293
am33xx_cm_write_reg(v, inst, clkctrl_offs);
294
}
295
296
/*
297
* Clockdomain low-level functions
298
*/
299
300
static int am33xx_clkdm_sleep(struct clockdomain *clkdm)
301
{
302
am33xx_cm_clkdm_force_sleep(clkdm->cm_inst, clkdm->clkdm_offs);
303
return 0;
304
}
305
306
static int am33xx_clkdm_wakeup(struct clockdomain *clkdm)
307
{
308
am33xx_cm_clkdm_force_wakeup(clkdm->cm_inst, clkdm->clkdm_offs);
309
return 0;
310
}
311
312
static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm)
313
{
314
am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
315
}
316
317
static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm)
318
{
319
am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
320
}
321
322
static int am33xx_clkdm_clk_enable(struct clockdomain *clkdm)
323
{
324
if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
325
return am33xx_clkdm_wakeup(clkdm);
326
327
return 0;
328
}
329
330
static int am33xx_clkdm_clk_disable(struct clockdomain *clkdm)
331
{
332
bool hwsup = false;
333
334
#if IS_ENABLED(CONFIG_SUSPEND)
335
/*
336
* In case of standby, Don't put the l4ls clk domain to sleep.
337
* Since CM3 PM FW doesn't wake-up/enable the l4ls clk domain
338
* upon wake-up, CM3 PM FW fails to wake-up th MPU.
339
*/
340
if (pm_suspend_target_state == PM_SUSPEND_STANDBY &&
341
(clkdm->flags & CLKDM_STANDBY_FORCE_WAKEUP))
342
return 0;
343
#endif
344
hwsup = am33xx_cm_is_clkdm_in_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
345
if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
346
am33xx_clkdm_sleep(clkdm);
347
348
return 0;
349
}
350
351
static u32 am33xx_cm_xlate_clkctrl(u8 part, u16 inst, u16 offset)
352
{
353
return cm_base.pa + inst + offset;
354
}
355
356
/**
357
* am33xx_clkdm_save_context - Save the clockdomain transition context
358
* @clkdm: The clockdomain pointer whose context needs to be saved
359
*
360
* Save the clockdomain transition context.
361
*/
362
static int am33xx_clkdm_save_context(struct clockdomain *clkdm)
363
{
364
clkdm->context = am33xx_cm_read_reg_bits(clkdm->cm_inst,
365
clkdm->clkdm_offs,
366
AM33XX_CLKTRCTRL_MASK);
367
368
return 0;
369
}
370
371
/**
372
* am33xx_clkdm_restore_context - Restore the clockdomain transition context
373
* @clkdm: The clockdomain pointer whose context needs to be restored
374
*
375
* Restore the clockdomain transition context.
376
*/
377
static int am33xx_clkdm_restore_context(struct clockdomain *clkdm)
378
{
379
switch (clkdm->context) {
380
case OMAP34XX_CLKSTCTRL_DISABLE_AUTO:
381
am33xx_clkdm_deny_idle(clkdm);
382
break;
383
case OMAP34XX_CLKSTCTRL_FORCE_SLEEP:
384
am33xx_clkdm_sleep(clkdm);
385
break;
386
case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP:
387
am33xx_clkdm_wakeup(clkdm);
388
break;
389
case OMAP34XX_CLKSTCTRL_ENABLE_AUTO:
390
am33xx_clkdm_allow_idle(clkdm);
391
break;
392
}
393
return 0;
394
}
395
396
struct clkdm_ops am33xx_clkdm_operations = {
397
.clkdm_sleep = am33xx_clkdm_sleep,
398
.clkdm_wakeup = am33xx_clkdm_wakeup,
399
.clkdm_allow_idle = am33xx_clkdm_allow_idle,
400
.clkdm_deny_idle = am33xx_clkdm_deny_idle,
401
.clkdm_clk_enable = am33xx_clkdm_clk_enable,
402
.clkdm_clk_disable = am33xx_clkdm_clk_disable,
403
.clkdm_save_context = am33xx_clkdm_save_context,
404
.clkdm_restore_context = am33xx_clkdm_restore_context,
405
};
406
407
static const struct cm_ll_data am33xx_cm_ll_data = {
408
.wait_module_ready = &am33xx_cm_wait_module_ready,
409
.wait_module_idle = &am33xx_cm_wait_module_idle,
410
.module_enable = &am33xx_cm_module_enable,
411
.module_disable = &am33xx_cm_module_disable,
412
.xlate_clkctrl = &am33xx_cm_xlate_clkctrl,
413
};
414
415
int __init am33xx_cm_init(const struct omap_prcm_init_data *data)
416
{
417
return cm_register(&am33xx_cm_ll_data);
418
}
419
420
static void __exit am33xx_cm_exit(void)
421
{
422
cm_unregister(&am33xx_cm_ll_data);
423
}
424
__exitcall(am33xx_cm_exit);
425
426