cm33xx.c 10.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * AM33XX CM functions
 *
 * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
 * Vaibhav Hiremath <hvaibhav@ti.com>
 *
 * Reference taken from from OMAP4 cminst44xx.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation version 2.
 *
 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/io.h>

25
#include "clockdomain.h"
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
#include "cm.h"
#include "cm33xx.h"
#include "cm-regbits-34xx.h"
#include "cm-regbits-33xx.h"
#include "prm33xx.h"

/*
 * CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield:
 *
 *   0x0 func:     Module is fully functional, including OCP
 *   0x1 trans:    Module is performing transition: wakeup, or sleep, or sleep
 *                 abortion
 *   0x2 idle:     Module is in Idle mode (only OCP part). It is functional if
 *                 using separate functional clock
 *   0x3 disabled: Module is disabled and cannot be accessed
 *
 */
#define CLKCTRL_IDLEST_FUNCTIONAL		0x0
#define CLKCTRL_IDLEST_INTRANSITION		0x1
#define CLKCTRL_IDLEST_INTERFACE_IDLE		0x2
#define CLKCTRL_IDLEST_DISABLED			0x3

/* Private functions */

/* Read a register in a CM instance */
51
static inline u32 am33xx_cm_read_reg(u16 inst, u16 idx)
52
{
53
	return readl_relaxed(cm_base + inst + idx);
54 55 56
}

/* Write into a register in a CM */
57
static inline void am33xx_cm_write_reg(u32 val, u16 inst, u16 idx)
58
{
59
	writel_relaxed(val, cm_base + inst + idx);
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
}

/* Read-modify-write a register in CM */
static inline u32 am33xx_cm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx)
{
	u32 v;

	v = am33xx_cm_read_reg(inst, idx);
	v &= ~mask;
	v |= bits;
	am33xx_cm_write_reg(v, inst, idx);

	return v;
}

static inline u32 am33xx_cm_set_reg_bits(u32 bits, s16 inst, s16 idx)
{
	return am33xx_cm_rmw_reg_bits(bits, bits, inst, idx);
}

static inline u32 am33xx_cm_clear_reg_bits(u32 bits, s16 inst, s16 idx)
{
	return am33xx_cm_rmw_reg_bits(bits, 0x0, inst, idx);
}

static inline u32 am33xx_cm_read_reg_bits(u16 inst, s16 idx, u32 mask)
{
	u32 v;

	v = am33xx_cm_read_reg(inst, idx);
	v &= mask;
	v >>= __ffs(mask);

	return v;
}

/**
 * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield
 * @inst: CM instance register offset (*_INST macro)
 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 *
 * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to
 * bit 0.
 */
104
static u32 _clkctrl_idlest(u16 inst, u16 clkctrl_offs)
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
{
	u32 v = am33xx_cm_read_reg(inst, clkctrl_offs);
	v &= AM33XX_IDLEST_MASK;
	v >>= AM33XX_IDLEST_SHIFT;
	return v;
}

/**
 * _is_module_ready - can module registers be accessed without causing an abort?
 * @inst: CM instance register offset (*_INST macro)
 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 *
 * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either
 * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise.
 */
120
static bool _is_module_ready(u16 inst, u16 clkctrl_offs)
121 122 123
{
	u32 v;

124
	v = _clkctrl_idlest(inst, clkctrl_offs);
125 126 127 128 129 130 131 132 133 134 135 136 137 138

	return (v == CLKCTRL_IDLEST_FUNCTIONAL ||
		v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false;
}

/**
 * _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield
 * @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted)
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 *
 * @c must be the unshifted value for CLKTRCTRL - i.e., this function
 * will handle the shift itself.
 */
139
static void _clktrctrl_write(u8 c, u16 inst, u16 cdoffs)
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
{
	u32 v;

	v = am33xx_cm_read_reg(inst, cdoffs);
	v &= ~AM33XX_CLKTRCTRL_MASK;
	v |= c << AM33XX_CLKTRCTRL_SHIFT;
	am33xx_cm_write_reg(v, inst, cdoffs);
}

/* Public functions */

/**
 * am33xx_cm_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode?
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 *
 * Returns true if the clockdomain referred to by (@inst, @cdoffs)
 * is in hardware-supervised idle mode, or 0 otherwise.
 */
159
bool am33xx_cm_is_clkdm_in_hwsup(u16 inst, u16 cdoffs)
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
{
	u32 v;

	v = am33xx_cm_read_reg(inst, cdoffs);
	v &= AM33XX_CLKTRCTRL_MASK;
	v >>= AM33XX_CLKTRCTRL_SHIFT;

	return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false;
}

/**
 * am33xx_cm_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 *
 * Put a clockdomain referred to by (@inst, @cdoffs) into
 * hardware-supervised idle mode.  No return value.
 */
178
void am33xx_cm_clkdm_enable_hwsup(u16 inst, u16 cdoffs)
179 180 181 182 183 184 185 186 187 188 189 190 191
{
	_clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, inst, cdoffs);
}

/**
 * am33xx_cm_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 *
 * Put a clockdomain referred to by (@inst, @cdoffs) into
 * software-supervised idle mode, i.e., controlled manually by the
 * Linux OMAP clockdomain code.  No return value.
 */
192
void am33xx_cm_clkdm_disable_hwsup(u16 inst, u16 cdoffs)
193 194 195 196 197 198 199 200 201 202 203 204
{
	_clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, inst, cdoffs);
}

/**
 * am33xx_cm_clkdm_force_sleep - try to put a clockdomain into idle
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 *
 * Put a clockdomain referred to by (@inst, @cdoffs) into idle
 * No return value.
 */
205
void am33xx_cm_clkdm_force_sleep(u16 inst, u16 cdoffs)
206 207 208 209 210 211 212 213 214 215 216 217
{
	_clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, inst, cdoffs);
}

/**
 * am33xx_cm_clkdm_force_wakeup - try to take a clockdomain out of idle
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 *
 * Take a clockdomain referred to by (@inst, @cdoffs) out of idle,
 * waking it up.  No return value.
 */
218
void am33xx_cm_clkdm_force_wakeup(u16 inst, u16 cdoffs)
219 220 221 222 223 224 225 226 227 228
{
	_clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, inst, cdoffs);
}

/*
 *
 */

/**
 * am33xx_cm_wait_module_ready - wait for a module to be in 'func' state
229
 * @part: PRCM partition, ignored for AM33xx
230 231
 * @inst: CM instance register offset (*_INST macro)
 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
232
 * @bit_shift: bit shift for the register, ignored for AM33xx
233 234 235 236 237 238
 *
 * Wait for the module IDLEST to be functional. If the idle state is in any
 * the non functional state (trans, idle or disabled), module and thus the
 * sysconfig cannot be accessed and will probably lead to an "imprecise
 * external abort"
 */
239 240
static int am33xx_cm_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs,
				       u8 bit_shift)
241 242 243
{
	int i = 0;

244
	omap_test_timeout(_is_module_ready(inst, clkctrl_offs),
245 246 247 248 249 250 251 252
			  MAX_MODULE_READY_TIME, i);

	return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
}

/**
 * am33xx_cm_wait_module_idle - wait for a module to be in 'disabled'
 * state
253
 * @part: CM partition, ignored for AM33xx
254 255
 * @inst: CM instance register offset (*_INST macro)
 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
256
 * @bit_shift: bit shift for the register, ignored for AM33xx
257 258 259 260 261
 *
 * Wait for the module IDLEST to be disabled. Some PRCM transition,
 * like reset assertion or parent clock de-activation must wait the
 * module to be fully disabled.
 */
262 263
static int am33xx_cm_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs,
				      u8 bit_shift)
264 265 266 267 268 269
{
	int i = 0;

	if (!clkctrl_offs)
		return 0;

270
	omap_test_timeout((_clkctrl_idlest(inst, clkctrl_offs) ==
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
				CLKCTRL_IDLEST_DISABLED),
				MAX_MODULE_READY_TIME, i);

	return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
}

/**
 * am33xx_cm_module_enable - Enable the modulemode inside CLKCTRL
 * @mode: Module mode (SW or HW)
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 *
 * No return value.
 */
void am33xx_cm_module_enable(u8 mode, u16 inst, s16 cdoffs, u16 clkctrl_offs)
{
	u32 v;

	v = am33xx_cm_read_reg(inst, clkctrl_offs);
	v &= ~AM33XX_MODULEMODE_MASK;
	v |= mode << AM33XX_MODULEMODE_SHIFT;
	am33xx_cm_write_reg(v, inst, clkctrl_offs);
}

/**
 * am33xx_cm_module_disable - Disable the module inside CLKCTRL
 * @inst: CM instance register offset (*_INST macro)
 * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
 * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
 *
 * No return value.
 */
void am33xx_cm_module_disable(u16 inst, s16 cdoffs, u16 clkctrl_offs)
{
	u32 v;

	v = am33xx_cm_read_reg(inst, clkctrl_offs);
	v &= ~AM33XX_MODULEMODE_MASK;
	am33xx_cm_write_reg(v, inst, clkctrl_offs);
}
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

/*
 * Clockdomain low-level functions
 */

static int am33xx_clkdm_sleep(struct clockdomain *clkdm)
{
	am33xx_cm_clkdm_force_sleep(clkdm->cm_inst, clkdm->clkdm_offs);
	return 0;
}

static int am33xx_clkdm_wakeup(struct clockdomain *clkdm)
{
	am33xx_cm_clkdm_force_wakeup(clkdm->cm_inst, clkdm->clkdm_offs);
	return 0;
}

static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm)
{
	am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
}

static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm)
{
	am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);
}

static int am33xx_clkdm_clk_enable(struct clockdomain *clkdm)
{
	if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
		return am33xx_clkdm_wakeup(clkdm);

	return 0;
}

static int am33xx_clkdm_clk_disable(struct clockdomain *clkdm)
{
	bool hwsup = false;

	hwsup = am33xx_cm_is_clkdm_in_hwsup(clkdm->cm_inst, clkdm->clkdm_offs);

	if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
		am33xx_clkdm_sleep(clkdm);

	return 0;
}

struct clkdm_ops am33xx_clkdm_operations = {
	.clkdm_sleep		= am33xx_clkdm_sleep,
	.clkdm_wakeup		= am33xx_clkdm_wakeup,
	.clkdm_allow_idle	= am33xx_clkdm_allow_idle,
	.clkdm_deny_idle	= am33xx_clkdm_deny_idle,
	.clkdm_clk_enable	= am33xx_clkdm_clk_enable,
	.clkdm_clk_disable	= am33xx_clkdm_clk_disable,
};
367

368 369
static struct cm_ll_data am33xx_cm_ll_data = {
	.wait_module_ready	= &am33xx_cm_wait_module_ready,
370
	.wait_module_idle	= &am33xx_cm_wait_module_idle,
371
};
372 373 374 375 376 377 378 379 380 381 382

int __init am33xx_cm_init(void)
{
	return cm_register(&am33xx_cm_ll_data);
}

static void __exit am33xx_cm_exit(void)
{
	cm_unregister(&am33xx_cm_ll_data);
}
__exitcall(am33xx_cm_exit);