12ace831fSPaul Walmsley /* 22ace831fSPaul Walmsley * OMAP4 CM instance functions 32ace831fSPaul Walmsley * 42ace831fSPaul Walmsley * Copyright (C) 2009 Nokia Corporation 5d0f0631dSBenoit Cousson * Copyright (C) 2011 Texas Instruments, Inc. 62ace831fSPaul Walmsley * Paul Walmsley 72ace831fSPaul Walmsley * 82ace831fSPaul Walmsley * This program is free software; you can redistribute it and/or modify 92ace831fSPaul Walmsley * it under the terms of the GNU General Public License version 2 as 102ace831fSPaul Walmsley * published by the Free Software Foundation. 112ace831fSPaul Walmsley * 122ace831fSPaul Walmsley * This is needed since CM instances can be in the PRM, PRCM_MPU, CM1, 132ace831fSPaul Walmsley * or CM2 hardware modules. For example, the EMU_CM CM instance is in 142ace831fSPaul Walmsley * the PRM hardware module. What a mess... 152ace831fSPaul Walmsley */ 162ace831fSPaul Walmsley 172ace831fSPaul Walmsley #include <linux/kernel.h> 182ace831fSPaul Walmsley #include <linux/types.h> 192ace831fSPaul Walmsley #include <linux/errno.h> 202ace831fSPaul Walmsley #include <linux/err.h> 212ace831fSPaul Walmsley #include <linux/io.h> 222ace831fSPaul Walmsley 232ace831fSPaul Walmsley #include <plat/common.h> 242ace831fSPaul Walmsley 252ace831fSPaul Walmsley #include "cm.h" 262ace831fSPaul Walmsley #include "cm1_44xx.h" 272ace831fSPaul Walmsley #include "cm2_44xx.h" 282ace831fSPaul Walmsley #include "cm44xx.h" 292ace831fSPaul Walmsley #include "cminst44xx.h" 30bd2122caSPaul Walmsley #include "cm-regbits-34xx.h" 312ace831fSPaul Walmsley #include "cm-regbits-44xx.h" 322ace831fSPaul Walmsley #include "prcm44xx.h" 332ace831fSPaul Walmsley #include "prm44xx.h" 342ace831fSPaul Walmsley #include "prcm_mpu44xx.h" 352ace831fSPaul Walmsley 36d0f0631dSBenoit Cousson /* 37d0f0631dSBenoit Cousson * CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield: 38d0f0631dSBenoit Cousson * 39d0f0631dSBenoit Cousson * 0x0 func: Module is fully functional, including OCP 40d0f0631dSBenoit Cousson * 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep 41d0f0631dSBenoit Cousson * abortion 42d0f0631dSBenoit Cousson * 0x2 idle: Module is in Idle mode (only OCP part). It is functional if 43d0f0631dSBenoit Cousson * using separate functional clock 44d0f0631dSBenoit Cousson * 0x3 disabled: Module is disabled and cannot be accessed 45d0f0631dSBenoit Cousson * 46d0f0631dSBenoit Cousson */ 47d0f0631dSBenoit Cousson #define CLKCTRL_IDLEST_FUNCTIONAL 0x0 48d0f0631dSBenoit Cousson #define CLKCTRL_IDLEST_INTRANSITION 0x1 49d0f0631dSBenoit Cousson #define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 50d0f0631dSBenoit Cousson #define CLKCTRL_IDLEST_DISABLED 0x3 51d0f0631dSBenoit Cousson 522ace831fSPaul Walmsley static u32 _cm_bases[OMAP4_MAX_PRCM_PARTITIONS] = { 532ace831fSPaul Walmsley [OMAP4430_INVALID_PRCM_PARTITION] = 0, 542ace831fSPaul Walmsley [OMAP4430_PRM_PARTITION] = OMAP4430_PRM_BASE, 552ace831fSPaul Walmsley [OMAP4430_CM1_PARTITION] = OMAP4430_CM1_BASE, 562ace831fSPaul Walmsley [OMAP4430_CM2_PARTITION] = OMAP4430_CM2_BASE, 572ace831fSPaul Walmsley [OMAP4430_SCRM_PARTITION] = 0, 582ace831fSPaul Walmsley [OMAP4430_PRCM_MPU_PARTITION] = OMAP4430_PRCM_MPU_BASE, 592ace831fSPaul Walmsley }; 602ace831fSPaul Walmsley 61d0f0631dSBenoit Cousson /* Private functions */ 62d0f0631dSBenoit Cousson 63d0f0631dSBenoit Cousson /** 64d0f0631dSBenoit Cousson * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield 65d0f0631dSBenoit Cousson * @part: PRCM partition ID that the CM_CLKCTRL register exists in 66d0f0631dSBenoit Cousson * @inst: CM instance register offset (*_INST macro) 67d0f0631dSBenoit Cousson * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 68d0f0631dSBenoit Cousson * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) 69d0f0631dSBenoit Cousson * 70d0f0631dSBenoit Cousson * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to 71d0f0631dSBenoit Cousson * bit 0. 72d0f0631dSBenoit Cousson */ 73d0f0631dSBenoit Cousson static u32 _clkctrl_idlest(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs) 74d0f0631dSBenoit Cousson { 75d0f0631dSBenoit Cousson u32 v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs); 76d0f0631dSBenoit Cousson v &= OMAP4430_IDLEST_MASK; 77d0f0631dSBenoit Cousson v >>= OMAP4430_IDLEST_SHIFT; 78d0f0631dSBenoit Cousson return v; 79d0f0631dSBenoit Cousson } 80d0f0631dSBenoit Cousson 81d0f0631dSBenoit Cousson /** 82d0f0631dSBenoit Cousson * _is_module_ready - can module registers be accessed without causing an abort? 83d0f0631dSBenoit Cousson * @part: PRCM partition ID that the CM_CLKCTRL register exists in 84d0f0631dSBenoit Cousson * @inst: CM instance register offset (*_INST macro) 85d0f0631dSBenoit Cousson * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 86d0f0631dSBenoit Cousson * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) 87d0f0631dSBenoit Cousson * 88d0f0631dSBenoit Cousson * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either 89d0f0631dSBenoit Cousson * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise. 90d0f0631dSBenoit Cousson */ 91d0f0631dSBenoit Cousson static bool _is_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs) 92d0f0631dSBenoit Cousson { 93d0f0631dSBenoit Cousson u32 v; 94d0f0631dSBenoit Cousson 95d0f0631dSBenoit Cousson v = _clkctrl_idlest(part, inst, cdoffs, clkctrl_offs); 96d0f0631dSBenoit Cousson 97d0f0631dSBenoit Cousson return (v == CLKCTRL_IDLEST_FUNCTIONAL || 98d0f0631dSBenoit Cousson v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false; 99d0f0631dSBenoit Cousson } 100d0f0631dSBenoit Cousson 101d0f0631dSBenoit Cousson /* Public functions */ 102d0f0631dSBenoit Cousson 1032ace831fSPaul Walmsley /* Read a register in a CM instance */ 1042ace831fSPaul Walmsley u32 omap4_cminst_read_inst_reg(u8 part, s16 inst, u16 idx) 1052ace831fSPaul Walmsley { 1062ace831fSPaul Walmsley BUG_ON(part >= OMAP4_MAX_PRCM_PARTITIONS || 1072ace831fSPaul Walmsley part == OMAP4430_INVALID_PRCM_PARTITION || 1082ace831fSPaul Walmsley !_cm_bases[part]); 1092ace831fSPaul Walmsley return __raw_readl(OMAP2_L4_IO_ADDRESS(_cm_bases[part] + inst + idx)); 1102ace831fSPaul Walmsley } 1112ace831fSPaul Walmsley 1122ace831fSPaul Walmsley /* Write into a register in a CM instance */ 1132ace831fSPaul Walmsley void omap4_cminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx) 1142ace831fSPaul Walmsley { 1152ace831fSPaul Walmsley BUG_ON(part >= OMAP4_MAX_PRCM_PARTITIONS || 1162ace831fSPaul Walmsley part == OMAP4430_INVALID_PRCM_PARTITION || 1172ace831fSPaul Walmsley !_cm_bases[part]); 1182ace831fSPaul Walmsley __raw_writel(val, OMAP2_L4_IO_ADDRESS(_cm_bases[part] + inst + idx)); 1192ace831fSPaul Walmsley } 1202ace831fSPaul Walmsley 1212ace831fSPaul Walmsley /* Read-modify-write a register in CM1. Caller must lock */ 1222ace831fSPaul Walmsley u32 omap4_cminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst, 1232ace831fSPaul Walmsley s16 idx) 1242ace831fSPaul Walmsley { 1252ace831fSPaul Walmsley u32 v; 1262ace831fSPaul Walmsley 1272ace831fSPaul Walmsley v = omap4_cminst_read_inst_reg(part, inst, idx); 1282ace831fSPaul Walmsley v &= ~mask; 1292ace831fSPaul Walmsley v |= bits; 1302ace831fSPaul Walmsley omap4_cminst_write_inst_reg(v, part, inst, idx); 1312ace831fSPaul Walmsley 1322ace831fSPaul Walmsley return v; 1332ace831fSPaul Walmsley } 1342ace831fSPaul Walmsley 13504eb7773SRajendra Nayak u32 omap4_cminst_set_inst_reg_bits(u32 bits, u8 part, s16 inst, s16 idx) 13604eb7773SRajendra Nayak { 13704eb7773SRajendra Nayak return omap4_cminst_rmw_inst_reg_bits(bits, bits, part, inst, idx); 13804eb7773SRajendra Nayak } 13904eb7773SRajendra Nayak 14004eb7773SRajendra Nayak u32 omap4_cminst_clear_inst_reg_bits(u32 bits, u8 part, s16 inst, s16 idx) 14104eb7773SRajendra Nayak { 14204eb7773SRajendra Nayak return omap4_cminst_rmw_inst_reg_bits(bits, 0x0, part, inst, idx); 14304eb7773SRajendra Nayak } 14404eb7773SRajendra Nayak 14504eb7773SRajendra Nayak u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx, u32 mask) 14604eb7773SRajendra Nayak { 14704eb7773SRajendra Nayak u32 v; 14804eb7773SRajendra Nayak 14904eb7773SRajendra Nayak v = omap4_cminst_read_inst_reg(part, inst, idx); 15004eb7773SRajendra Nayak v &= mask; 15104eb7773SRajendra Nayak v >>= __ffs(mask); 15204eb7773SRajendra Nayak 15304eb7773SRajendra Nayak return v; 15404eb7773SRajendra Nayak } 15504eb7773SRajendra Nayak 156bd2122caSPaul Walmsley /* 157bd2122caSPaul Walmsley * 158bd2122caSPaul Walmsley */ 159bd2122caSPaul Walmsley 160bd2122caSPaul Walmsley /** 161bd2122caSPaul Walmsley * _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield 162bd2122caSPaul Walmsley * @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted) 163bd2122caSPaul Walmsley * @part: PRCM partition ID that the CM_CLKSTCTRL register exists in 164bd2122caSPaul Walmsley * @inst: CM instance register offset (*_INST macro) 165bd2122caSPaul Walmsley * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 166bd2122caSPaul Walmsley * 167bd2122caSPaul Walmsley * @c must be the unshifted value for CLKTRCTRL - i.e., this function 168bd2122caSPaul Walmsley * will handle the shift itself. 169bd2122caSPaul Walmsley */ 170bd2122caSPaul Walmsley static void _clktrctrl_write(u8 c, u8 part, s16 inst, u16 cdoffs) 171bd2122caSPaul Walmsley { 172bd2122caSPaul Walmsley u32 v; 173bd2122caSPaul Walmsley 174bd2122caSPaul Walmsley v = omap4_cminst_read_inst_reg(part, inst, cdoffs + OMAP4_CM_CLKSTCTRL); 175bd2122caSPaul Walmsley v &= ~OMAP4430_CLKTRCTRL_MASK; 176bd2122caSPaul Walmsley v |= c << OMAP4430_CLKTRCTRL_SHIFT; 177bd2122caSPaul Walmsley omap4_cminst_write_inst_reg(v, part, inst, cdoffs + OMAP4_CM_CLKSTCTRL); 178bd2122caSPaul Walmsley } 179bd2122caSPaul Walmsley 180bd2122caSPaul Walmsley /** 181bd2122caSPaul Walmsley * omap4_cminst_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode? 182bd2122caSPaul Walmsley * @part: PRCM partition ID that the CM_CLKSTCTRL register exists in 183bd2122caSPaul Walmsley * @inst: CM instance register offset (*_INST macro) 184bd2122caSPaul Walmsley * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 185bd2122caSPaul Walmsley * 186bd2122caSPaul Walmsley * Returns true if the clockdomain referred to by (@part, @inst, @cdoffs) 187bd2122caSPaul Walmsley * is in hardware-supervised idle mode, or 0 otherwise. 188bd2122caSPaul Walmsley */ 189bd2122caSPaul Walmsley bool omap4_cminst_is_clkdm_in_hwsup(u8 part, s16 inst, u16 cdoffs) 190bd2122caSPaul Walmsley { 191bd2122caSPaul Walmsley u32 v; 192bd2122caSPaul Walmsley 193bd2122caSPaul Walmsley v = omap4_cminst_read_inst_reg(part, inst, cdoffs + OMAP4_CM_CLKSTCTRL); 194bd2122caSPaul Walmsley v &= OMAP4430_CLKTRCTRL_MASK; 195bd2122caSPaul Walmsley v >>= OMAP4430_CLKTRCTRL_SHIFT; 196bd2122caSPaul Walmsley 197bd2122caSPaul Walmsley return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false; 198bd2122caSPaul Walmsley } 199bd2122caSPaul Walmsley 200bd2122caSPaul Walmsley /** 201bd2122caSPaul Walmsley * omap4_cminst_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode 202bd2122caSPaul Walmsley * @part: PRCM partition ID that the clockdomain registers exist in 203bd2122caSPaul Walmsley * @inst: CM instance register offset (*_INST macro) 204bd2122caSPaul Walmsley * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 205bd2122caSPaul Walmsley * 206bd2122caSPaul Walmsley * Put a clockdomain referred to by (@part, @inst, @cdoffs) into 207bd2122caSPaul Walmsley * hardware-supervised idle mode. No return value. 208bd2122caSPaul Walmsley */ 209bd2122caSPaul Walmsley void omap4_cminst_clkdm_enable_hwsup(u8 part, s16 inst, u16 cdoffs) 210bd2122caSPaul Walmsley { 211bd2122caSPaul Walmsley _clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, part, inst, cdoffs); 212bd2122caSPaul Walmsley } 213bd2122caSPaul Walmsley 214bd2122caSPaul Walmsley /** 215bd2122caSPaul Walmsley * omap4_cminst_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode 216bd2122caSPaul Walmsley * @part: PRCM partition ID that the clockdomain registers exist in 217bd2122caSPaul Walmsley * @inst: CM instance register offset (*_INST macro) 218bd2122caSPaul Walmsley * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 219bd2122caSPaul Walmsley * 220bd2122caSPaul Walmsley * Put a clockdomain referred to by (@part, @inst, @cdoffs) into 221bd2122caSPaul Walmsley * software-supervised idle mode, i.e., controlled manually by the 222bd2122caSPaul Walmsley * Linux OMAP clockdomain code. No return value. 223bd2122caSPaul Walmsley */ 224bd2122caSPaul Walmsley void omap4_cminst_clkdm_disable_hwsup(u8 part, s16 inst, u16 cdoffs) 225bd2122caSPaul Walmsley { 226bd2122caSPaul Walmsley _clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, part, inst, cdoffs); 227bd2122caSPaul Walmsley } 228bd2122caSPaul Walmsley 229bd2122caSPaul Walmsley /** 230bd2122caSPaul Walmsley * omap4_cminst_clkdm_force_sleep - try to put a clockdomain into idle 231bd2122caSPaul Walmsley * @part: PRCM partition ID that the clockdomain registers exist in 232bd2122caSPaul Walmsley * @inst: CM instance register offset (*_INST macro) 233bd2122caSPaul Walmsley * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 234bd2122caSPaul Walmsley * 235bd2122caSPaul Walmsley * Put a clockdomain referred to by (@part, @inst, @cdoffs) into idle 236bd2122caSPaul Walmsley * No return value. 237bd2122caSPaul Walmsley */ 238bd2122caSPaul Walmsley void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs) 239bd2122caSPaul Walmsley { 240bd2122caSPaul Walmsley _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, part, inst, cdoffs); 241bd2122caSPaul Walmsley } 242bd2122caSPaul Walmsley 243bd2122caSPaul Walmsley /** 244bd2122caSPaul Walmsley * omap4_cminst_clkdm_force_sleep - try to take a clockdomain out of idle 245bd2122caSPaul Walmsley * @part: PRCM partition ID that the clockdomain registers exist in 246bd2122caSPaul Walmsley * @inst: CM instance register offset (*_INST macro) 247bd2122caSPaul Walmsley * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 248bd2122caSPaul Walmsley * 249bd2122caSPaul Walmsley * Take a clockdomain referred to by (@part, @inst, @cdoffs) out of idle, 250bd2122caSPaul Walmsley * waking it up. No return value. 251bd2122caSPaul Walmsley */ 252bd2122caSPaul Walmsley void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs) 253bd2122caSPaul Walmsley { 254bd2122caSPaul Walmsley _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, part, inst, cdoffs); 255bd2122caSPaul Walmsley } 256bd2122caSPaul Walmsley 257bd2122caSPaul Walmsley /* 258bd2122caSPaul Walmsley * 259bd2122caSPaul Walmsley */ 2602ace831fSPaul Walmsley 2612ace831fSPaul Walmsley /** 262d0f0631dSBenoit Cousson * omap4_cminst_wait_module_ready - wait for a module to be in 'func' state 263d0f0631dSBenoit Cousson * @part: PRCM partition ID that the CM_CLKCTRL register exists in 264d0f0631dSBenoit Cousson * @inst: CM instance register offset (*_INST macro) 265d0f0631dSBenoit Cousson * @cdoffs: Clockdomain register offset (*_CDOFFS macro) 266d0f0631dSBenoit Cousson * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) 2672ace831fSPaul Walmsley * 2682ace831fSPaul Walmsley * Wait for the module IDLEST to be functional. If the idle state is in any 2692ace831fSPaul Walmsley * the non functional state (trans, idle or disabled), module and thus the 2702ace831fSPaul Walmsley * sysconfig cannot be accessed and will probably lead to an "imprecise 2712ace831fSPaul Walmsley * external abort" 2722ace831fSPaul Walmsley */ 273d0f0631dSBenoit Cousson int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs, 274d0f0631dSBenoit Cousson u16 clkctrl_offs) 2752ace831fSPaul Walmsley { 2762ace831fSPaul Walmsley int i = 0; 2772ace831fSPaul Walmsley 278d0f0631dSBenoit Cousson if (!clkctrl_offs) 2792ace831fSPaul Walmsley return 0; 2802ace831fSPaul Walmsley 281d0f0631dSBenoit Cousson omap_test_timeout(_is_module_ready(part, inst, cdoffs, clkctrl_offs), 2822ace831fSPaul Walmsley MAX_MODULE_READY_TIME, i); 2832ace831fSPaul Walmsley 2842ace831fSPaul Walmsley return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; 2852ace831fSPaul Walmsley } 2862ace831fSPaul Walmsley 287