cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cm33xx.c (11925B)


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