18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * omap_hwmod implementation for OMAP2/3/4 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009-2011 Nokia Corporation 68c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 Texas Instruments, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Paul Walmsley, Benoît Cousson, Kevin Hilman 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Created in collaboration with (alphabetical order): Thara Gopinath, 118c2ecf20Sopenharmony_ci * Tony Lindgren, Rajendra Nayak, Vikram Pandita, Sakari Poussa, Anand 128c2ecf20Sopenharmony_ci * Sawant, Santosh Shilimkar, Richard Woodruff 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Introduction 158c2ecf20Sopenharmony_ci * ------------ 168c2ecf20Sopenharmony_ci * One way to view an OMAP SoC is as a collection of largely unrelated 178c2ecf20Sopenharmony_ci * IP blocks connected by interconnects. The IP blocks include 188c2ecf20Sopenharmony_ci * devices such as ARM processors, audio serial interfaces, UARTs, 198c2ecf20Sopenharmony_ci * etc. Some of these devices, like the DSP, are created by TI; 208c2ecf20Sopenharmony_ci * others, like the SGX, largely originate from external vendors. In 218c2ecf20Sopenharmony_ci * TI's documentation, on-chip devices are referred to as "OMAP 228c2ecf20Sopenharmony_ci * modules." Some of these IP blocks are identical across several 238c2ecf20Sopenharmony_ci * OMAP versions. Others are revised frequently. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * These OMAP modules are tied together by various interconnects. 268c2ecf20Sopenharmony_ci * Most of the address and data flow between modules is via OCP-based 278c2ecf20Sopenharmony_ci * interconnects such as the L3 and L4 buses; but there are other 288c2ecf20Sopenharmony_ci * interconnects that distribute the hardware clock tree, handle idle 298c2ecf20Sopenharmony_ci * and reset signaling, supply power, and connect the modules to 308c2ecf20Sopenharmony_ci * various pads or balls on the OMAP package. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * OMAP hwmod provides a consistent way to describe the on-chip 338c2ecf20Sopenharmony_ci * hardware blocks and their integration into the rest of the chip. 348c2ecf20Sopenharmony_ci * This description can be automatically generated from the TI 358c2ecf20Sopenharmony_ci * hardware database. OMAP hwmod provides a standard, consistent API 368c2ecf20Sopenharmony_ci * to reset, enable, idle, and disable these hardware blocks. And 378c2ecf20Sopenharmony_ci * hwmod provides a way for other core code, such as the Linux device 388c2ecf20Sopenharmony_ci * code or the OMAP power management and address space mapping code, 398c2ecf20Sopenharmony_ci * to query the hardware database. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Using hwmod 428c2ecf20Sopenharmony_ci * ----------- 438c2ecf20Sopenharmony_ci * Drivers won't call hwmod functions directly. That is done by the 448c2ecf20Sopenharmony_ci * omap_device code, and in rare occasions, by custom integration code 458c2ecf20Sopenharmony_ci * in arch/arm/ *omap*. The omap_device code includes functions to 468c2ecf20Sopenharmony_ci * build a struct platform_device using omap_hwmod data, and that is 478c2ecf20Sopenharmony_ci * currently how hwmod data is communicated to drivers and to the 488c2ecf20Sopenharmony_ci * Linux driver model. Most drivers will call omap_hwmod functions only 498c2ecf20Sopenharmony_ci * indirectly, via pm_runtime*() functions. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * From a layering perspective, here is where the OMAP hwmod code 528c2ecf20Sopenharmony_ci * fits into the kernel software stack: 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * +-------------------------------+ 558c2ecf20Sopenharmony_ci * | Device driver code | 568c2ecf20Sopenharmony_ci * | (e.g., drivers/) | 578c2ecf20Sopenharmony_ci * +-------------------------------+ 588c2ecf20Sopenharmony_ci * | Linux driver model | 598c2ecf20Sopenharmony_ci * | (platform_device / | 608c2ecf20Sopenharmony_ci * | platform_driver data/code) | 618c2ecf20Sopenharmony_ci * +-------------------------------+ 628c2ecf20Sopenharmony_ci * | OMAP core-driver integration | 638c2ecf20Sopenharmony_ci * |(arch/arm/mach-omap2/devices.c)| 648c2ecf20Sopenharmony_ci * +-------------------------------+ 658c2ecf20Sopenharmony_ci * | omap_device code | 668c2ecf20Sopenharmony_ci * | (../plat-omap/omap_device.c) | 678c2ecf20Sopenharmony_ci * +-------------------------------+ 688c2ecf20Sopenharmony_ci * ----> | omap_hwmod code/data | <----- 698c2ecf20Sopenharmony_ci * | (../mach-omap2/omap_hwmod*) | 708c2ecf20Sopenharmony_ci * +-------------------------------+ 718c2ecf20Sopenharmony_ci * | OMAP clock/PRCM/register fns | 728c2ecf20Sopenharmony_ci * | ({read,write}l_relaxed, clk*) | 738c2ecf20Sopenharmony_ci * +-------------------------------+ 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Device drivers should not contain any OMAP-specific code or data in 768c2ecf20Sopenharmony_ci * them. They should only contain code to operate the IP block that 778c2ecf20Sopenharmony_ci * the driver is responsible for. This is because these IP blocks can 788c2ecf20Sopenharmony_ci * also appear in other SoCs, either from TI (such as DaVinci) or from 798c2ecf20Sopenharmony_ci * other manufacturers; and drivers should be reusable across other 808c2ecf20Sopenharmony_ci * platforms. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * The OMAP hwmod code also will attempt to reset and idle all on-chip 838c2ecf20Sopenharmony_ci * devices upon boot. The goal here is for the kernel to be 848c2ecf20Sopenharmony_ci * completely self-reliant and independent from bootloaders. This is 858c2ecf20Sopenharmony_ci * to ensure a repeatable configuration, both to ensure consistent 868c2ecf20Sopenharmony_ci * runtime behavior, and to make it easier for others to reproduce 878c2ecf20Sopenharmony_ci * bugs. 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * OMAP module activity states 908c2ecf20Sopenharmony_ci * --------------------------- 918c2ecf20Sopenharmony_ci * The hwmod code considers modules to be in one of several activity 928c2ecf20Sopenharmony_ci * states. IP blocks start out in an UNKNOWN state, then once they 938c2ecf20Sopenharmony_ci * are registered via the hwmod code, proceed to the REGISTERED state. 948c2ecf20Sopenharmony_ci * Once their clock names are resolved to clock pointers, the module 958c2ecf20Sopenharmony_ci * enters the CLKS_INITED state; and finally, once the module has been 968c2ecf20Sopenharmony_ci * reset and the integration registers programmed, the INITIALIZED state 978c2ecf20Sopenharmony_ci * is entered. The hwmod code will then place the module into either 988c2ecf20Sopenharmony_ci * the IDLE state to save power, or in the case of a critical system 998c2ecf20Sopenharmony_ci * module, the ENABLED state. 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * OMAP core integration code can then call omap_hwmod*() functions 1028c2ecf20Sopenharmony_ci * directly to move the module between the IDLE, ENABLED, and DISABLED 1038c2ecf20Sopenharmony_ci * states, as needed. This is done during both the PM idle loop, and 1048c2ecf20Sopenharmony_ci * in the OMAP core integration code's implementation of the PM runtime 1058c2ecf20Sopenharmony_ci * functions. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * References 1088c2ecf20Sopenharmony_ci * ---------- 1098c2ecf20Sopenharmony_ci * This is a partial list. 1108c2ecf20Sopenharmony_ci * - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064) 1118c2ecf20Sopenharmony_ci * - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090) 1128c2ecf20Sopenharmony_ci * - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108) 1138c2ecf20Sopenharmony_ci * - OMAP4430 Multimedia Device Silicon Revision 1.0 (SWPU140) 1148c2ecf20Sopenharmony_ci * - Open Core Protocol Specification 2.2 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * To do: 1178c2ecf20Sopenharmony_ci * - handle IO mapping 1188c2ecf20Sopenharmony_ci * - bus throughput & module latency measurement code 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * XXX add tests at the beginning of each function to ensure the hwmod is 1218c2ecf20Sopenharmony_ci * in the appropriate state 1228c2ecf20Sopenharmony_ci * XXX error return values should be checked to ensure that they are 1238c2ecf20Sopenharmony_ci * appropriate 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci#undef DEBUG 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1288c2ecf20Sopenharmony_ci#include <linux/errno.h> 1298c2ecf20Sopenharmony_ci#include <linux/io.h> 1308c2ecf20Sopenharmony_ci#include <linux/clk.h> 1318c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 1328c2ecf20Sopenharmony_ci#include <linux/delay.h> 1338c2ecf20Sopenharmony_ci#include <linux/err.h> 1348c2ecf20Sopenharmony_ci#include <linux/list.h> 1358c2ecf20Sopenharmony_ci#include <linux/mutex.h> 1368c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 1378c2ecf20Sopenharmony_ci#include <linux/slab.h> 1388c2ecf20Sopenharmony_ci#include <linux/cpu.h> 1398c2ecf20Sopenharmony_ci#include <linux/of.h> 1408c2ecf20Sopenharmony_ci#include <linux/of_address.h> 1418c2ecf20Sopenharmony_ci#include <linux/memblock.h> 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#include <linux/platform_data/ti-sysc.h> 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#include <dt-bindings/bus/ti-sysc.h> 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#include <asm/system_misc.h> 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#include "clock.h" 1508c2ecf20Sopenharmony_ci#include "omap_hwmod.h" 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#include "soc.h" 1538c2ecf20Sopenharmony_ci#include "common.h" 1548c2ecf20Sopenharmony_ci#include "clockdomain.h" 1558c2ecf20Sopenharmony_ci#include "hdq1w.h" 1568c2ecf20Sopenharmony_ci#include "mmc.h" 1578c2ecf20Sopenharmony_ci#include "powerdomain.h" 1588c2ecf20Sopenharmony_ci#include "cm2xxx.h" 1598c2ecf20Sopenharmony_ci#include "cm3xxx.h" 1608c2ecf20Sopenharmony_ci#include "cm33xx.h" 1618c2ecf20Sopenharmony_ci#include "prm.h" 1628c2ecf20Sopenharmony_ci#include "prm3xxx.h" 1638c2ecf20Sopenharmony_ci#include "prm44xx.h" 1648c2ecf20Sopenharmony_ci#include "prm33xx.h" 1658c2ecf20Sopenharmony_ci#include "prminst44xx.h" 1668c2ecf20Sopenharmony_ci#include "pm.h" 1678c2ecf20Sopenharmony_ci#include "wd_timer.h" 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Name of the OMAP hwmod for the MPU */ 1708c2ecf20Sopenharmony_ci#define MPU_INITIATOR_NAME "mpu" 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * Number of struct omap_hwmod_link records per struct 1748c2ecf20Sopenharmony_ci * omap_hwmod_ocp_if record (master->slave and slave->master) 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci#define LINKS_PER_OCP_IF 2 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * Address offset (in bytes) between the reset control and the reset 1808c2ecf20Sopenharmony_ci * status registers: 4 bytes on OMAP4 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci#define OMAP4_RST_CTRL_ST_OFFSET 4 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * Maximum length for module clock handle names 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci#define MOD_CLK_MAX_NAME_LEN 32 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/** 1908c2ecf20Sopenharmony_ci * struct clkctrl_provider - clkctrl provider mapping data 1918c2ecf20Sopenharmony_ci * @num_addrs: number of base address ranges for the provider 1928c2ecf20Sopenharmony_ci * @addr: base address(es) for the provider 1938c2ecf20Sopenharmony_ci * @size: size(s) of the provider address space(s) 1948c2ecf20Sopenharmony_ci * @node: device node associated with the provider 1958c2ecf20Sopenharmony_ci * @link: list link 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistruct clkctrl_provider { 1988c2ecf20Sopenharmony_ci int num_addrs; 1998c2ecf20Sopenharmony_ci u32 *addr; 2008c2ecf20Sopenharmony_ci u32 *size; 2018c2ecf20Sopenharmony_ci struct device_node *node; 2028c2ecf20Sopenharmony_ci struct list_head link; 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic LIST_HEAD(clkctrl_providers); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * struct omap_hwmod_reset - IP specific reset functions 2098c2ecf20Sopenharmony_ci * @match: string to match against the module name 2108c2ecf20Sopenharmony_ci * @len: number of characters to match 2118c2ecf20Sopenharmony_ci * @reset: IP specific reset function 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Used only in cases where struct omap_hwmod is dynamically allocated. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistruct omap_hwmod_reset { 2168c2ecf20Sopenharmony_ci const char *match; 2178c2ecf20Sopenharmony_ci int len; 2188c2ecf20Sopenharmony_ci int (*reset)(struct omap_hwmod *oh); 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * struct omap_hwmod_soc_ops - fn ptrs for some SoC-specific operations 2238c2ecf20Sopenharmony_ci * @enable_module: function to enable a module (via MODULEMODE) 2248c2ecf20Sopenharmony_ci * @disable_module: function to disable a module (via MODULEMODE) 2258c2ecf20Sopenharmony_ci * 2268c2ecf20Sopenharmony_ci * XXX Eventually this functionality will be hidden inside the PRM/CM 2278c2ecf20Sopenharmony_ci * device drivers. Until then, this should avoid huge blocks of cpu_is_*() 2288c2ecf20Sopenharmony_ci * conditionals in this code. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_cistruct omap_hwmod_soc_ops { 2318c2ecf20Sopenharmony_ci void (*enable_module)(struct omap_hwmod *oh); 2328c2ecf20Sopenharmony_ci int (*disable_module)(struct omap_hwmod *oh); 2338c2ecf20Sopenharmony_ci int (*wait_target_ready)(struct omap_hwmod *oh); 2348c2ecf20Sopenharmony_ci int (*assert_hardreset)(struct omap_hwmod *oh, 2358c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri); 2368c2ecf20Sopenharmony_ci int (*deassert_hardreset)(struct omap_hwmod *oh, 2378c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri); 2388c2ecf20Sopenharmony_ci int (*is_hardreset_asserted)(struct omap_hwmod *oh, 2398c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri); 2408c2ecf20Sopenharmony_ci int (*init_clkdm)(struct omap_hwmod *oh); 2418c2ecf20Sopenharmony_ci void (*update_context_lost)(struct omap_hwmod *oh); 2428c2ecf20Sopenharmony_ci int (*get_context_lost)(struct omap_hwmod *oh); 2438c2ecf20Sopenharmony_ci int (*disable_direct_prcm)(struct omap_hwmod *oh); 2448c2ecf20Sopenharmony_ci u32 (*xlate_clkctrl)(struct omap_hwmod *oh); 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* soc_ops: adapts the omap_hwmod code to the currently-booted SoC */ 2488c2ecf20Sopenharmony_cistatic struct omap_hwmod_soc_ops soc_ops; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* omap_hwmod_list contains all registered struct omap_hwmods */ 2518c2ecf20Sopenharmony_cistatic LIST_HEAD(omap_hwmod_list); 2528c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(list_lock); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* mpu_oh: used to add/remove MPU initiator from sleepdep list */ 2558c2ecf20Sopenharmony_cistatic struct omap_hwmod *mpu_oh; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* inited: set to true once the hwmod code is initialized */ 2588c2ecf20Sopenharmony_cistatic bool inited; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/* Private functions */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/** 2638c2ecf20Sopenharmony_ci * _update_sysc_cache - return the module OCP_SYSCONFIG register, keep copy 2648c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * Load the current value of the hwmod OCP_SYSCONFIG register into the 2678c2ecf20Sopenharmony_ci * struct omap_hwmod for later use. Returns -EINVAL if the hwmod has no 2688c2ecf20Sopenharmony_ci * OCP_SYSCONFIG register or 0 upon success. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_cistatic int _update_sysc_cache(struct omap_hwmod *oh) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci if (!oh->class->sysc) { 2738c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: cannot read OCP_SYSCONFIG: not defined on hwmod's class\n", oh->name); 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* XXX ensure module interface clock is up */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci oh->_sysc_cache = omap_hwmod_read(oh, oh->class->sysc->sysc_offs); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!(oh->class->sysc->sysc_flags & SYSC_NO_CACHE)) 2828c2ecf20Sopenharmony_ci oh->_int_flags |= _HWMOD_SYSCONFIG_LOADED; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/** 2888c2ecf20Sopenharmony_ci * _write_sysconfig - write a value to the module's OCP_SYSCONFIG register 2898c2ecf20Sopenharmony_ci * @v: OCP_SYSCONFIG value to write 2908c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * Write @v into the module class' OCP_SYSCONFIG register, if it has 2938c2ecf20Sopenharmony_ci * one. No return value. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_cistatic void _write_sysconfig(u32 v, struct omap_hwmod *oh) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci if (!oh->class->sysc) { 2988c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: cannot write OCP_SYSCONFIG: not defined on hwmod's class\n", oh->name); 2998c2ecf20Sopenharmony_ci return; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* XXX ensure module interface clock is up */ 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Module might have lost context, always update cache and register */ 3058c2ecf20Sopenharmony_ci oh->_sysc_cache = v; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * Some IP blocks (such as RTC) require unlocking of IP before 3098c2ecf20Sopenharmony_ci * accessing its registers. If a function pointer is present 3108c2ecf20Sopenharmony_ci * to unlock, then call it before accessing sysconfig and 3118c2ecf20Sopenharmony_ci * call lock after writing sysconfig. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci if (oh->class->unlock) 3148c2ecf20Sopenharmony_ci oh->class->unlock(oh); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci omap_hwmod_write(v, oh, oh->class->sysc->sysc_offs); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (oh->class->lock) 3198c2ecf20Sopenharmony_ci oh->class->lock(oh); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/** 3238c2ecf20Sopenharmony_ci * _set_master_standbymode: set the OCP_SYSCONFIG MIDLEMODE field in @v 3248c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 3258c2ecf20Sopenharmony_ci * @standbymode: MIDLEMODE field bits 3268c2ecf20Sopenharmony_ci * @v: pointer to register contents to modify 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * Update the master standby mode bits in @v to be @standbymode for 3298c2ecf20Sopenharmony_ci * the @oh hwmod. Does not write to the hardware. Returns -EINVAL 3308c2ecf20Sopenharmony_ci * upon error or 0 upon success. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_cistatic int _set_master_standbymode(struct omap_hwmod *oh, u8 standbymode, 3338c2ecf20Sopenharmony_ci u32 *v) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci u32 mstandby_mask; 3368c2ecf20Sopenharmony_ci u8 mstandby_shift; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!oh->class->sysc || 3398c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_MIDLEMODE)) 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 3438c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mstandby_shift = oh->class->sysc->sysc_fields->midle_shift; 3488c2ecf20Sopenharmony_ci mstandby_mask = (0x3 << mstandby_shift); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci *v &= ~mstandby_mask; 3518c2ecf20Sopenharmony_ci *v |= __ffs(standbymode) << mstandby_shift; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/** 3578c2ecf20Sopenharmony_ci * _set_slave_idlemode: set the OCP_SYSCONFIG SIDLEMODE field in @v 3588c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 3598c2ecf20Sopenharmony_ci * @idlemode: SIDLEMODE field bits 3608c2ecf20Sopenharmony_ci * @v: pointer to register contents to modify 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * Update the slave idle mode bits in @v to be @idlemode for the @oh 3638c2ecf20Sopenharmony_ci * hwmod. Does not write to the hardware. Returns -EINVAL upon error 3648c2ecf20Sopenharmony_ci * or 0 upon success. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_cistatic int _set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode, u32 *v) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci u32 sidle_mask; 3698c2ecf20Sopenharmony_ci u8 sidle_shift; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (!oh->class->sysc || 3728c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_SIDLEMODE)) 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 3768c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci sidle_shift = oh->class->sysc->sysc_fields->sidle_shift; 3818c2ecf20Sopenharmony_ci sidle_mask = (0x3 << sidle_shift); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci *v &= ~sidle_mask; 3848c2ecf20Sopenharmony_ci *v |= __ffs(idlemode) << sidle_shift; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/** 3908c2ecf20Sopenharmony_ci * _set_clockactivity: set OCP_SYSCONFIG.CLOCKACTIVITY bits in @v 3918c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 3928c2ecf20Sopenharmony_ci * @clockact: CLOCKACTIVITY field bits 3938c2ecf20Sopenharmony_ci * @v: pointer to register contents to modify 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * Update the clockactivity mode bits in @v to be @clockact for the 3968c2ecf20Sopenharmony_ci * @oh hwmod. Used for additional powersaving on some modules. Does 3978c2ecf20Sopenharmony_ci * not write to the hardware. Returns -EINVAL upon error or 0 upon 3988c2ecf20Sopenharmony_ci * success. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_cistatic int _set_clockactivity(struct omap_hwmod *oh, u8 clockact, u32 *v) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci u32 clkact_mask; 4038c2ecf20Sopenharmony_ci u8 clkact_shift; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!oh->class->sysc || 4068c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_CLOCKACTIVITY)) 4078c2ecf20Sopenharmony_ci return -EINVAL; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 4108c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); 4118c2ecf20Sopenharmony_ci return -EINVAL; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci clkact_shift = oh->class->sysc->sysc_fields->clkact_shift; 4158c2ecf20Sopenharmony_ci clkact_mask = (0x3 << clkact_shift); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci *v &= ~clkact_mask; 4188c2ecf20Sopenharmony_ci *v |= clockact << clkact_shift; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/** 4248c2ecf20Sopenharmony_ci * _set_softreset: set OCP_SYSCONFIG.SOFTRESET bit in @v 4258c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 4268c2ecf20Sopenharmony_ci * @v: pointer to register contents to modify 4278c2ecf20Sopenharmony_ci * 4288c2ecf20Sopenharmony_ci * Set the SOFTRESET bit in @v for hwmod @oh. Returns -EINVAL upon 4298c2ecf20Sopenharmony_ci * error or 0 upon success. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_cistatic int _set_softreset(struct omap_hwmod *oh, u32 *v) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci u32 softrst_mask; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (!oh->class->sysc || 4368c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) 4378c2ecf20Sopenharmony_ci return -EINVAL; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 4408c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); 4418c2ecf20Sopenharmony_ci return -EINVAL; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci *v |= softrst_mask; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return 0; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/** 4528c2ecf20Sopenharmony_ci * _clear_softreset: clear OCP_SYSCONFIG.SOFTRESET bit in @v 4538c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 4548c2ecf20Sopenharmony_ci * @v: pointer to register contents to modify 4558c2ecf20Sopenharmony_ci * 4568c2ecf20Sopenharmony_ci * Clear the SOFTRESET bit in @v for hwmod @oh. Returns -EINVAL upon 4578c2ecf20Sopenharmony_ci * error or 0 upon success. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_cistatic int _clear_softreset(struct omap_hwmod *oh, u32 *v) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci u32 softrst_mask; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (!oh->class->sysc || 4648c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) 4658c2ecf20Sopenharmony_ci return -EINVAL; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 4688c2ecf20Sopenharmony_ci WARN(1, 4698c2ecf20Sopenharmony_ci "omap_hwmod: %s: sysc_fields absent for sysconfig class\n", 4708c2ecf20Sopenharmony_ci oh->name); 4718c2ecf20Sopenharmony_ci return -EINVAL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci *v &= ~softrst_mask; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/** 4828c2ecf20Sopenharmony_ci * _wait_softreset_complete - wait for an OCP softreset to complete 4838c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to wait on 4848c2ecf20Sopenharmony_ci * 4858c2ecf20Sopenharmony_ci * Wait until the IP block represented by @oh reports that its OCP 4868c2ecf20Sopenharmony_ci * softreset is complete. This can be triggered by software (see 4878c2ecf20Sopenharmony_ci * _ocp_softreset()) or by hardware upon returning from off-mode (one 4888c2ecf20Sopenharmony_ci * example is HSMMC). Waits for up to MAX_MODULE_SOFTRESET_WAIT 4898c2ecf20Sopenharmony_ci * microseconds. Returns the number of microseconds waited. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic int _wait_softreset_complete(struct omap_hwmod *oh) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct omap_hwmod_class_sysconfig *sysc; 4948c2ecf20Sopenharmony_ci u32 softrst_mask; 4958c2ecf20Sopenharmony_ci int c = 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci sysc = oh->class->sysc; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (sysc->sysc_flags & SYSS_HAS_RESET_STATUS && sysc->syss_offs > 0) 5008c2ecf20Sopenharmony_ci omap_test_timeout((omap_hwmod_read(oh, sysc->syss_offs) 5018c2ecf20Sopenharmony_ci & SYSS_RESETDONE_MASK), 5028c2ecf20Sopenharmony_ci MAX_MODULE_SOFTRESET_WAIT, c); 5038c2ecf20Sopenharmony_ci else if (sysc->sysc_flags & SYSC_HAS_RESET_STATUS) { 5048c2ecf20Sopenharmony_ci softrst_mask = (0x1 << sysc->sysc_fields->srst_shift); 5058c2ecf20Sopenharmony_ci omap_test_timeout(!(omap_hwmod_read(oh, sysc->sysc_offs) 5068c2ecf20Sopenharmony_ci & softrst_mask), 5078c2ecf20Sopenharmony_ci MAX_MODULE_SOFTRESET_WAIT, c); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return c; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/** 5148c2ecf20Sopenharmony_ci * _set_dmadisable: set OCP_SYSCONFIG.DMADISABLE bit in @v 5158c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * The DMADISABLE bit is a semi-automatic bit present in sysconfig register 5188c2ecf20Sopenharmony_ci * of some modules. When the DMA must perform read/write accesses, the 5198c2ecf20Sopenharmony_ci * DMADISABLE bit is cleared by the hardware. But when the DMA must stop 5208c2ecf20Sopenharmony_ci * for power management, software must set the DMADISABLE bit back to 1. 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * Set the DMADISABLE bit in @v for hwmod @oh. Returns -EINVAL upon 5238c2ecf20Sopenharmony_ci * error or 0 upon success. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_cistatic int _set_dmadisable(struct omap_hwmod *oh) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci u32 v; 5288c2ecf20Sopenharmony_ci u32 dmadisable_mask; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!oh->class->sysc || 5318c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_DMADISABLE)) 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 5358c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); 5368c2ecf20Sopenharmony_ci return -EINVAL; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* clocks must be on for this operation */ 5408c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_ENABLED) { 5418c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: dma can be disabled only from enabled state\n", oh->name); 5428c2ecf20Sopenharmony_ci return -EINVAL; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: setting DMADISABLE\n", oh->name); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci v = oh->_sysc_cache; 5488c2ecf20Sopenharmony_ci dmadisable_mask = 5498c2ecf20Sopenharmony_ci (0x1 << oh->class->sysc->sysc_fields->dmadisable_shift); 5508c2ecf20Sopenharmony_ci v |= dmadisable_mask; 5518c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/** 5578c2ecf20Sopenharmony_ci * _set_module_autoidle: set the OCP_SYSCONFIG AUTOIDLE field in @v 5588c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 5598c2ecf20Sopenharmony_ci * @autoidle: desired AUTOIDLE bitfield value (0 or 1) 5608c2ecf20Sopenharmony_ci * @v: pointer to register contents to modify 5618c2ecf20Sopenharmony_ci * 5628c2ecf20Sopenharmony_ci * Update the module autoidle bit in @v to be @autoidle for the @oh 5638c2ecf20Sopenharmony_ci * hwmod. The autoidle bit controls whether the module can gate 5648c2ecf20Sopenharmony_ci * internal clocks automatically when it isn't doing anything; the 5658c2ecf20Sopenharmony_ci * exact function of this bit varies on a per-module basis. This 5668c2ecf20Sopenharmony_ci * function does not write to the hardware. Returns -EINVAL upon 5678c2ecf20Sopenharmony_ci * error or 0 upon success. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_cistatic int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle, 5708c2ecf20Sopenharmony_ci u32 *v) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci u32 autoidle_mask; 5738c2ecf20Sopenharmony_ci u8 autoidle_shift; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (!oh->class->sysc || 5768c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_AUTOIDLE)) 5778c2ecf20Sopenharmony_ci return -EINVAL; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 5808c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); 5818c2ecf20Sopenharmony_ci return -EINVAL; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci autoidle_shift = oh->class->sysc->sysc_fields->autoidle_shift; 5858c2ecf20Sopenharmony_ci autoidle_mask = (0x1 << autoidle_shift); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci *v &= ~autoidle_mask; 5888c2ecf20Sopenharmony_ci *v |= autoidle << autoidle_shift; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/** 5948c2ecf20Sopenharmony_ci * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware 5958c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * Allow the hardware module @oh to send wakeups. Returns -EINVAL 5988c2ecf20Sopenharmony_ci * upon error or 0 upon success. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_cistatic int _enable_wakeup(struct omap_hwmod *oh, u32 *v) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci if (!oh->class->sysc || 6038c2ecf20Sopenharmony_ci !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) || 6048c2ecf20Sopenharmony_ci (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) || 6058c2ecf20Sopenharmony_ci (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP))) 6068c2ecf20Sopenharmony_ci return -EINVAL; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) { 6098c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); 6108c2ecf20Sopenharmony_ci return -EINVAL; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) 6148c2ecf20Sopenharmony_ci *v |= 0x1 << oh->class->sysc->sysc_fields->enwkup_shift; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) 6178c2ecf20Sopenharmony_ci _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); 6188c2ecf20Sopenharmony_ci if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) 6198c2ecf20Sopenharmony_ci _set_master_standbymode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* XXX test pwrdm_get_wken for this hwmod's subsystem */ 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic struct clockdomain *_get_clkdm(struct omap_hwmod *oh) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct clk_hw_omap *clk; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (oh->clkdm) { 6318c2ecf20Sopenharmony_ci return oh->clkdm; 6328c2ecf20Sopenharmony_ci } else if (oh->_clk) { 6338c2ecf20Sopenharmony_ci if (!omap2_clk_is_hw_omap(__clk_get_hw(oh->_clk))) 6348c2ecf20Sopenharmony_ci return NULL; 6358c2ecf20Sopenharmony_ci clk = to_clk_hw_omap(__clk_get_hw(oh->_clk)); 6368c2ecf20Sopenharmony_ci return clk->clkdm; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci return NULL; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci/** 6428c2ecf20Sopenharmony_ci * _add_initiator_dep: prevent @oh from smart-idling while @init_oh is active 6438c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * Prevent the hardware module @oh from entering idle while the 6468c2ecf20Sopenharmony_ci * hardare module initiator @init_oh is active. Useful when a module 6478c2ecf20Sopenharmony_ci * will be accessed by a particular initiator (e.g., if a module will 6488c2ecf20Sopenharmony_ci * be accessed by the IVA, there should be a sleepdep between the IVA 6498c2ecf20Sopenharmony_ci * initiator and the module). Only applies to modules in smart-idle 6508c2ecf20Sopenharmony_ci * mode. If the clockdomain is marked as not needing autodeps, return 6518c2ecf20Sopenharmony_ci * 0 without doing anything. Otherwise, returns -EINVAL upon error or 6528c2ecf20Sopenharmony_ci * passes along clkdm_add_sleepdep() value upon success. 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_cistatic int _add_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct clockdomain *clkdm, *init_clkdm; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci clkdm = _get_clkdm(oh); 6598c2ecf20Sopenharmony_ci init_clkdm = _get_clkdm(init_oh); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (!clkdm || !init_clkdm) 6628c2ecf20Sopenharmony_ci return -EINVAL; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (clkdm && clkdm->flags & CLKDM_NO_AUTODEPS) 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return clkdm_add_sleepdep(clkdm, init_clkdm); 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci/** 6718c2ecf20Sopenharmony_ci * _del_initiator_dep: allow @oh to smart-idle even if @init_oh is active 6728c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * Allow the hardware module @oh to enter idle while the hardare 6758c2ecf20Sopenharmony_ci * module initiator @init_oh is active. Useful when a module will not 6768c2ecf20Sopenharmony_ci * be accessed by a particular initiator (e.g., if a module will not 6778c2ecf20Sopenharmony_ci * be accessed by the IVA, there should be no sleepdep between the IVA 6788c2ecf20Sopenharmony_ci * initiator and the module). Only applies to modules in smart-idle 6798c2ecf20Sopenharmony_ci * mode. If the clockdomain is marked as not needing autodeps, return 6808c2ecf20Sopenharmony_ci * 0 without doing anything. Returns -EINVAL upon error or passes 6818c2ecf20Sopenharmony_ci * along clkdm_del_sleepdep() value upon success. 6828c2ecf20Sopenharmony_ci */ 6838c2ecf20Sopenharmony_cistatic int _del_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct clockdomain *clkdm, *init_clkdm; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci clkdm = _get_clkdm(oh); 6888c2ecf20Sopenharmony_ci init_clkdm = _get_clkdm(init_oh); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (!clkdm || !init_clkdm) 6918c2ecf20Sopenharmony_ci return -EINVAL; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (clkdm && clkdm->flags & CLKDM_NO_AUTODEPS) 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return clkdm_del_sleepdep(clkdm, init_clkdm); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic const struct of_device_id ti_clkctrl_match_table[] __initconst = { 7008c2ecf20Sopenharmony_ci { .compatible = "ti,clkctrl" }, 7018c2ecf20Sopenharmony_ci { } 7028c2ecf20Sopenharmony_ci}; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int __init _setup_clkctrl_provider(struct device_node *np) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci const __be32 *addrp; 7078c2ecf20Sopenharmony_ci struct clkctrl_provider *provider; 7088c2ecf20Sopenharmony_ci u64 size; 7098c2ecf20Sopenharmony_ci int i; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci provider = memblock_alloc(sizeof(*provider), SMP_CACHE_BYTES); 7128c2ecf20Sopenharmony_ci if (!provider) 7138c2ecf20Sopenharmony_ci return -ENOMEM; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci provider->node = np; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci provider->num_addrs = 7188c2ecf20Sopenharmony_ci of_property_count_elems_of_size(np, "reg", sizeof(u32)) / 2; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci provider->addr = 7218c2ecf20Sopenharmony_ci memblock_alloc(sizeof(void *) * provider->num_addrs, 7228c2ecf20Sopenharmony_ci SMP_CACHE_BYTES); 7238c2ecf20Sopenharmony_ci if (!provider->addr) 7248c2ecf20Sopenharmony_ci return -ENOMEM; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci provider->size = 7278c2ecf20Sopenharmony_ci memblock_alloc(sizeof(u32) * provider->num_addrs, 7288c2ecf20Sopenharmony_ci SMP_CACHE_BYTES); 7298c2ecf20Sopenharmony_ci if (!provider->size) 7308c2ecf20Sopenharmony_ci return -ENOMEM; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci for (i = 0; i < provider->num_addrs; i++) { 7338c2ecf20Sopenharmony_ci addrp = of_get_address(np, i, &size, NULL); 7348c2ecf20Sopenharmony_ci provider->addr[i] = (u32)of_translate_address(np, addrp); 7358c2ecf20Sopenharmony_ci provider->size[i] = size; 7368c2ecf20Sopenharmony_ci pr_debug("%s: %pOF: %x...%x\n", __func__, np, provider->addr[i], 7378c2ecf20Sopenharmony_ci provider->addr[i] + provider->size[i]); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci list_add(&provider->link, &clkctrl_providers); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return 0; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic int __init _init_clkctrl_providers(void) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci struct device_node *np; 7488c2ecf20Sopenharmony_ci int ret = 0; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci for_each_matching_node(np, ti_clkctrl_match_table) { 7518c2ecf20Sopenharmony_ci ret = _setup_clkctrl_provider(np); 7528c2ecf20Sopenharmony_ci if (ret) { 7538c2ecf20Sopenharmony_ci of_node_put(np); 7548c2ecf20Sopenharmony_ci break; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return ret; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic u32 _omap4_xlate_clkctrl(struct omap_hwmod *oh) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci if (!oh->prcm.omap4.modulemode) 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci return omap_cm_xlate_clkctrl(oh->clkdm->prcm_partition, 7678c2ecf20Sopenharmony_ci oh->clkdm->cm_inst, 7688c2ecf20Sopenharmony_ci oh->prcm.omap4.clkctrl_offs); 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic struct clk *_lookup_clkctrl_clk(struct omap_hwmod *oh) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct clkctrl_provider *provider; 7748c2ecf20Sopenharmony_ci struct clk *clk; 7758c2ecf20Sopenharmony_ci u32 addr; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (!soc_ops.xlate_clkctrl) 7788c2ecf20Sopenharmony_ci return NULL; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci addr = soc_ops.xlate_clkctrl(oh); 7818c2ecf20Sopenharmony_ci if (!addr) 7828c2ecf20Sopenharmony_ci return NULL; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci pr_debug("%s: %s: addr=%x\n", __func__, oh->name, addr); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci list_for_each_entry(provider, &clkctrl_providers, link) { 7878c2ecf20Sopenharmony_ci int i; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci for (i = 0; i < provider->num_addrs; i++) { 7908c2ecf20Sopenharmony_ci if (provider->addr[i] <= addr && 7918c2ecf20Sopenharmony_ci provider->addr[i] + provider->size[i] > addr) { 7928c2ecf20Sopenharmony_ci struct of_phandle_args clkspec; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci clkspec.np = provider->node; 7958c2ecf20Sopenharmony_ci clkspec.args_count = 2; 7968c2ecf20Sopenharmony_ci clkspec.args[0] = addr - provider->addr[0]; 7978c2ecf20Sopenharmony_ci clkspec.args[1] = 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci clk = of_clk_get_from_provider(&clkspec); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci pr_debug("%s: %s got %p (offset=%x, provider=%pOF)\n", 8028c2ecf20Sopenharmony_ci __func__, oh->name, clk, 8038c2ecf20Sopenharmony_ci clkspec.args[0], provider->node); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return clk; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return NULL; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci/** 8148c2ecf20Sopenharmony_ci * _init_main_clk - get a struct clk * for the the hwmod's main functional clk 8158c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * Called from _init_clocks(). Populates the @oh _clk (main 8188c2ecf20Sopenharmony_ci * functional clock pointer) if a clock matching the hwmod name is found, 8198c2ecf20Sopenharmony_ci * or a main_clk is present. Returns 0 on success or -EINVAL on error. 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_cistatic int _init_main_clk(struct omap_hwmod *oh) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci int ret = 0; 8248c2ecf20Sopenharmony_ci struct clk *clk = NULL; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci clk = _lookup_clkctrl_clk(oh); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(clk)) { 8298c2ecf20Sopenharmony_ci pr_debug("%s: mapped main_clk %s for %s\n", __func__, 8308c2ecf20Sopenharmony_ci __clk_get_name(clk), oh->name); 8318c2ecf20Sopenharmony_ci oh->main_clk = __clk_get_name(clk); 8328c2ecf20Sopenharmony_ci oh->_clk = clk; 8338c2ecf20Sopenharmony_ci soc_ops.disable_direct_prcm(oh); 8348c2ecf20Sopenharmony_ci } else { 8358c2ecf20Sopenharmony_ci if (!oh->main_clk) 8368c2ecf20Sopenharmony_ci return 0; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci oh->_clk = clk_get(NULL, oh->main_clk); 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (IS_ERR(oh->_clk)) { 8428c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: cannot clk_get main_clk %s\n", 8438c2ecf20Sopenharmony_ci oh->name, oh->main_clk); 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci /* 8478c2ecf20Sopenharmony_ci * HACK: This needs a re-visit once clk_prepare() is implemented 8488c2ecf20Sopenharmony_ci * to do something meaningful. Today its just a no-op. 8498c2ecf20Sopenharmony_ci * If clk_prepare() is used at some point to do things like 8508c2ecf20Sopenharmony_ci * voltage scaling etc, then this would have to be moved to 8518c2ecf20Sopenharmony_ci * some point where subsystems like i2c and pmic become 8528c2ecf20Sopenharmony_ci * available. 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_ci clk_prepare(oh->_clk); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (!_get_clkdm(oh)) 8578c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: missing clockdomain for %s.\n", 8588c2ecf20Sopenharmony_ci oh->name, oh->main_clk); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return ret; 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci/** 8648c2ecf20Sopenharmony_ci * _init_interface_clks - get a struct clk * for the the hwmod's interface clks 8658c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 8668c2ecf20Sopenharmony_ci * 8678c2ecf20Sopenharmony_ci * Called from _init_clocks(). Populates the @oh OCP slave interface 8688c2ecf20Sopenharmony_ci * clock pointers. Returns 0 on success or -EINVAL on error. 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_cistatic int _init_interface_clks(struct omap_hwmod *oh) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct omap_hwmod_ocp_if *os; 8738c2ecf20Sopenharmony_ci struct clk *c; 8748c2ecf20Sopenharmony_ci int ret = 0; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci list_for_each_entry(os, &oh->slave_ports, node) { 8778c2ecf20Sopenharmony_ci if (!os->clk) 8788c2ecf20Sopenharmony_ci continue; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci c = clk_get(NULL, os->clk); 8818c2ecf20Sopenharmony_ci if (IS_ERR(c)) { 8828c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: cannot clk_get interface_clk %s\n", 8838c2ecf20Sopenharmony_ci oh->name, os->clk); 8848c2ecf20Sopenharmony_ci ret = -EINVAL; 8858c2ecf20Sopenharmony_ci continue; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci os->_clk = c; 8888c2ecf20Sopenharmony_ci /* 8898c2ecf20Sopenharmony_ci * HACK: This needs a re-visit once clk_prepare() is implemented 8908c2ecf20Sopenharmony_ci * to do something meaningful. Today its just a no-op. 8918c2ecf20Sopenharmony_ci * If clk_prepare() is used at some point to do things like 8928c2ecf20Sopenharmony_ci * voltage scaling etc, then this would have to be moved to 8938c2ecf20Sopenharmony_ci * some point where subsystems like i2c and pmic become 8948c2ecf20Sopenharmony_ci * available. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci clk_prepare(os->_clk); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return ret; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * _init_opt_clk - get a struct clk * for the the hwmod's optional clocks 9048c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 9058c2ecf20Sopenharmony_ci * 9068c2ecf20Sopenharmony_ci * Called from _init_clocks(). Populates the @oh omap_hwmod_opt_clk 9078c2ecf20Sopenharmony_ci * clock pointers. Returns 0 on success or -EINVAL on error. 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_cistatic int _init_opt_clks(struct omap_hwmod *oh) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct omap_hwmod_opt_clk *oc; 9128c2ecf20Sopenharmony_ci struct clk *c; 9138c2ecf20Sopenharmony_ci int i; 9148c2ecf20Sopenharmony_ci int ret = 0; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) { 9178c2ecf20Sopenharmony_ci c = clk_get(NULL, oc->clk); 9188c2ecf20Sopenharmony_ci if (IS_ERR(c)) { 9198c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: cannot clk_get opt_clk %s\n", 9208c2ecf20Sopenharmony_ci oh->name, oc->clk); 9218c2ecf20Sopenharmony_ci ret = -EINVAL; 9228c2ecf20Sopenharmony_ci continue; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci oc->_clk = c; 9258c2ecf20Sopenharmony_ci /* 9268c2ecf20Sopenharmony_ci * HACK: This needs a re-visit once clk_prepare() is implemented 9278c2ecf20Sopenharmony_ci * to do something meaningful. Today its just a no-op. 9288c2ecf20Sopenharmony_ci * If clk_prepare() is used at some point to do things like 9298c2ecf20Sopenharmony_ci * voltage scaling etc, then this would have to be moved to 9308c2ecf20Sopenharmony_ci * some point where subsystems like i2c and pmic become 9318c2ecf20Sopenharmony_ci * available. 9328c2ecf20Sopenharmony_ci */ 9338c2ecf20Sopenharmony_ci clk_prepare(oc->_clk); 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci return ret; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic void _enable_optional_clocks(struct omap_hwmod *oh) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct omap_hwmod_opt_clk *oc; 9428c2ecf20Sopenharmony_ci int i; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: enabling optional clocks\n", oh->name); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) 9478c2ecf20Sopenharmony_ci if (oc->_clk) { 9488c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: enable %s:%s\n", oc->role, 9498c2ecf20Sopenharmony_ci __clk_get_name(oc->_clk)); 9508c2ecf20Sopenharmony_ci clk_enable(oc->_clk); 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic void _disable_optional_clocks(struct omap_hwmod *oh) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci struct omap_hwmod_opt_clk *oc; 9578c2ecf20Sopenharmony_ci int i; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: disabling optional clocks\n", oh->name); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) 9628c2ecf20Sopenharmony_ci if (oc->_clk) { 9638c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: disable %s:%s\n", oc->role, 9648c2ecf20Sopenharmony_ci __clk_get_name(oc->_clk)); 9658c2ecf20Sopenharmony_ci clk_disable(oc->_clk); 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci/** 9708c2ecf20Sopenharmony_ci * _enable_clocks - enable hwmod main clock and interface clocks 9718c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 9728c2ecf20Sopenharmony_ci * 9738c2ecf20Sopenharmony_ci * Enables all clocks necessary for register reads and writes to succeed 9748c2ecf20Sopenharmony_ci * on the hwmod @oh. Returns 0. 9758c2ecf20Sopenharmony_ci */ 9768c2ecf20Sopenharmony_cistatic int _enable_clocks(struct omap_hwmod *oh) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct omap_hwmod_ocp_if *os; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: enabling clocks\n", oh->name); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_OPT_CLKS_NEEDED) 9838c2ecf20Sopenharmony_ci _enable_optional_clocks(oh); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (oh->_clk) 9868c2ecf20Sopenharmony_ci clk_enable(oh->_clk); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci list_for_each_entry(os, &oh->slave_ports, node) { 9898c2ecf20Sopenharmony_ci if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { 9908c2ecf20Sopenharmony_ci omap2_clk_deny_idle(os->_clk); 9918c2ecf20Sopenharmony_ci clk_enable(os->_clk); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* The opt clocks are controlled by the device driver. */ 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/** 10018c2ecf20Sopenharmony_ci * _omap4_clkctrl_managed_by_clkfwk - true if clkctrl managed by clock framework 10028c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_cistatic bool _omap4_clkctrl_managed_by_clkfwk(struct omap_hwmod *oh) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci if (oh->prcm.omap4.flags & HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK) 10078c2ecf20Sopenharmony_ci return true; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci return false; 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci/** 10138c2ecf20Sopenharmony_ci * _omap4_has_clkctrl_clock - returns true if a module has clkctrl clock 10148c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_cistatic bool _omap4_has_clkctrl_clock(struct omap_hwmod *oh) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci if (oh->prcm.omap4.clkctrl_offs) 10198c2ecf20Sopenharmony_ci return true; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (!oh->prcm.omap4.clkctrl_offs && 10228c2ecf20Sopenharmony_ci oh->prcm.omap4.flags & HWMOD_OMAP4_ZERO_CLKCTRL_OFFSET) 10238c2ecf20Sopenharmony_ci return true; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci return false; 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci/** 10298c2ecf20Sopenharmony_ci * _disable_clocks - disable hwmod main clock and interface clocks 10308c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 10318c2ecf20Sopenharmony_ci * 10328c2ecf20Sopenharmony_ci * Disables the hwmod @oh main functional and interface clocks. Returns 0. 10338c2ecf20Sopenharmony_ci */ 10348c2ecf20Sopenharmony_cistatic int _disable_clocks(struct omap_hwmod *oh) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci struct omap_hwmod_ocp_if *os; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: disabling clocks\n", oh->name); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (oh->_clk) 10418c2ecf20Sopenharmony_ci clk_disable(oh->_clk); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci list_for_each_entry(os, &oh->slave_ports, node) { 10448c2ecf20Sopenharmony_ci if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { 10458c2ecf20Sopenharmony_ci clk_disable(os->_clk); 10468c2ecf20Sopenharmony_ci omap2_clk_allow_idle(os->_clk); 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_OPT_CLKS_NEEDED) 10518c2ecf20Sopenharmony_ci _disable_optional_clocks(oh); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* The opt clocks are controlled by the device driver. */ 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci return 0; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci/** 10598c2ecf20Sopenharmony_ci * _omap4_enable_module - enable CLKCTRL modulemode on OMAP4 10608c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 10618c2ecf20Sopenharmony_ci * 10628c2ecf20Sopenharmony_ci * Enables the PRCM module mode related to the hwmod @oh. 10638c2ecf20Sopenharmony_ci * No return value. 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_cistatic void _omap4_enable_module(struct omap_hwmod *oh) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci if (!oh->clkdm || !oh->prcm.omap4.modulemode || 10688c2ecf20Sopenharmony_ci _omap4_clkctrl_managed_by_clkfwk(oh)) 10698c2ecf20Sopenharmony_ci return; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: %s: %d\n", 10728c2ecf20Sopenharmony_ci oh->name, __func__, oh->prcm.omap4.modulemode); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci omap_cm_module_enable(oh->prcm.omap4.modulemode, 10758c2ecf20Sopenharmony_ci oh->clkdm->prcm_partition, 10768c2ecf20Sopenharmony_ci oh->clkdm->cm_inst, oh->prcm.omap4.clkctrl_offs); 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci/** 10808c2ecf20Sopenharmony_ci * _omap4_wait_target_disable - wait for a module to be disabled on OMAP4 10818c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 10828c2ecf20Sopenharmony_ci * 10838c2ecf20Sopenharmony_ci * Wait for a module @oh to enter slave idle. Returns 0 if the module 10848c2ecf20Sopenharmony_ci * does not have an IDLEST bit or if the module successfully enters 10858c2ecf20Sopenharmony_ci * slave idle; otherwise, pass along the return value of the 10868c2ecf20Sopenharmony_ci * appropriate *_cm*_wait_module_idle() function. 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_cistatic int _omap4_wait_target_disable(struct omap_hwmod *oh) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci if (!oh) 10918c2ecf20Sopenharmony_ci return -EINVAL; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (oh->_int_flags & _HWMOD_NO_MPU_PORT || !oh->clkdm) 10948c2ecf20Sopenharmony_ci return 0; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_NO_IDLEST) 10978c2ecf20Sopenharmony_ci return 0; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (_omap4_clkctrl_managed_by_clkfwk(oh)) 11008c2ecf20Sopenharmony_ci return 0; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (!_omap4_has_clkctrl_clock(oh)) 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return omap_cm_wait_module_idle(oh->clkdm->prcm_partition, 11068c2ecf20Sopenharmony_ci oh->clkdm->cm_inst, 11078c2ecf20Sopenharmony_ci oh->prcm.omap4.clkctrl_offs, 0); 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci/** 11118c2ecf20Sopenharmony_ci * _save_mpu_port_index - find and save the index to @oh's MPU port 11128c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 11138c2ecf20Sopenharmony_ci * 11148c2ecf20Sopenharmony_ci * Determines the array index of the OCP slave port that the MPU uses 11158c2ecf20Sopenharmony_ci * to address the device, and saves it into the struct omap_hwmod. 11168c2ecf20Sopenharmony_ci * Intended to be called during hwmod registration only. No return 11178c2ecf20Sopenharmony_ci * value. 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_cistatic void __init _save_mpu_port_index(struct omap_hwmod *oh) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct omap_hwmod_ocp_if *os = NULL; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (!oh) 11248c2ecf20Sopenharmony_ci return; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci oh->_int_flags |= _HWMOD_NO_MPU_PORT; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci list_for_each_entry(os, &oh->slave_ports, node) { 11298c2ecf20Sopenharmony_ci if (os->user & OCP_USER_MPU) { 11308c2ecf20Sopenharmony_ci oh->_mpu_port = os; 11318c2ecf20Sopenharmony_ci oh->_int_flags &= ~_HWMOD_NO_MPU_PORT; 11328c2ecf20Sopenharmony_ci break; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci/** 11408c2ecf20Sopenharmony_ci * _find_mpu_rt_port - return omap_hwmod_ocp_if accessible by the MPU 11418c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 11428c2ecf20Sopenharmony_ci * 11438c2ecf20Sopenharmony_ci * Given a pointer to a struct omap_hwmod record @oh, return a pointer 11448c2ecf20Sopenharmony_ci * to the struct omap_hwmod_ocp_if record that is used by the MPU to 11458c2ecf20Sopenharmony_ci * communicate with the IP block. This interface need not be directly 11468c2ecf20Sopenharmony_ci * connected to the MPU (and almost certainly is not), but is directly 11478c2ecf20Sopenharmony_ci * connected to the IP block represented by @oh. Returns a pointer 11488c2ecf20Sopenharmony_ci * to the struct omap_hwmod_ocp_if * upon success, or returns NULL upon 11498c2ecf20Sopenharmony_ci * error or if there does not appear to be a path from the MPU to this 11508c2ecf20Sopenharmony_ci * IP block. 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_cistatic struct omap_hwmod_ocp_if *_find_mpu_rt_port(struct omap_hwmod *oh) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci if (!oh || oh->_int_flags & _HWMOD_NO_MPU_PORT || oh->slaves_cnt == 0) 11558c2ecf20Sopenharmony_ci return NULL; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci return oh->_mpu_port; 11588c2ecf20Sopenharmony_ci}; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci/** 11618c2ecf20Sopenharmony_ci * _enable_sysc - try to bring a module out of idle via OCP_SYSCONFIG 11628c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 11638c2ecf20Sopenharmony_ci * 11648c2ecf20Sopenharmony_ci * Ensure that the OCP_SYSCONFIG register for the IP block represented 11658c2ecf20Sopenharmony_ci * by @oh is set to indicate to the PRCM that the IP block is active. 11668c2ecf20Sopenharmony_ci * Usually this means placing the module into smart-idle mode and 11678c2ecf20Sopenharmony_ci * smart-standby, but if there is a bug in the automatic idle handling 11688c2ecf20Sopenharmony_ci * for the IP block, it may need to be placed into the force-idle or 11698c2ecf20Sopenharmony_ci * no-idle variants of these modes. No return value. 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_cistatic void _enable_sysc(struct omap_hwmod *oh) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci u8 idlemode, sf; 11748c2ecf20Sopenharmony_ci u32 v; 11758c2ecf20Sopenharmony_ci bool clkdm_act; 11768c2ecf20Sopenharmony_ci struct clockdomain *clkdm; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (!oh->class->sysc) 11798c2ecf20Sopenharmony_ci return; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* 11828c2ecf20Sopenharmony_ci * Wait until reset has completed, this is needed as the IP 11838c2ecf20Sopenharmony_ci * block is reset automatically by hardware in some cases 11848c2ecf20Sopenharmony_ci * (off-mode for example), and the drivers require the 11858c2ecf20Sopenharmony_ci * IP to be ready when they access it 11868c2ecf20Sopenharmony_ci */ 11878c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) 11888c2ecf20Sopenharmony_ci _enable_optional_clocks(oh); 11898c2ecf20Sopenharmony_ci _wait_softreset_complete(oh); 11908c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) 11918c2ecf20Sopenharmony_ci _disable_optional_clocks(oh); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci v = oh->_sysc_cache; 11948c2ecf20Sopenharmony_ci sf = oh->class->sysc->sysc_flags; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci clkdm = _get_clkdm(oh); 11978c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_SIDLEMODE) { 11988c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_SWSUP_SIDLE || 11998c2ecf20Sopenharmony_ci oh->flags & HWMOD_SWSUP_SIDLE_ACT) { 12008c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_NO; 12018c2ecf20Sopenharmony_ci } else { 12028c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_ENAWAKEUP) 12038c2ecf20Sopenharmony_ci _enable_wakeup(oh, &v); 12048c2ecf20Sopenharmony_ci if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) 12058c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART_WKUP; 12068c2ecf20Sopenharmony_ci else 12078c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* 12118c2ecf20Sopenharmony_ci * This is special handling for some IPs like 12128c2ecf20Sopenharmony_ci * 32k sync timer. Force them to idle! 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_ci clkdm_act = (clkdm && clkdm->flags & CLKDM_ACTIVE_WITH_MPU); 12158c2ecf20Sopenharmony_ci if (clkdm_act && !(oh->class->sysc->idlemodes & 12168c2ecf20Sopenharmony_ci (SIDLE_SMART | SIDLE_SMART_WKUP))) 12178c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_FORCE; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci _set_slave_idlemode(oh, idlemode, &v); 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_MIDLEMODE) { 12238c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_FORCE_MSTANDBY) { 12248c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_FORCE; 12258c2ecf20Sopenharmony_ci } else if (oh->flags & HWMOD_SWSUP_MSTANDBY) { 12268c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_NO; 12278c2ecf20Sopenharmony_ci } else { 12288c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_ENAWAKEUP) 12298c2ecf20Sopenharmony_ci _enable_wakeup(oh, &v); 12308c2ecf20Sopenharmony_ci if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) 12318c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART_WKUP; 12328c2ecf20Sopenharmony_ci else 12338c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci _set_master_standbymode(oh, idlemode, &v); 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* 12398c2ecf20Sopenharmony_ci * XXX The clock framework should handle this, by 12408c2ecf20Sopenharmony_ci * calling into this code. But this must wait until the 12418c2ecf20Sopenharmony_ci * clock structures are tagged with omap_hwmod entries 12428c2ecf20Sopenharmony_ci */ 12438c2ecf20Sopenharmony_ci if ((oh->flags & HWMOD_SET_DEFAULT_CLOCKACT) && 12448c2ecf20Sopenharmony_ci (sf & SYSC_HAS_CLOCKACTIVITY)) 12458c2ecf20Sopenharmony_ci _set_clockactivity(oh, CLOCKACT_TEST_ICLK, &v); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* 12508c2ecf20Sopenharmony_ci * Set the autoidle bit only after setting the smartidle bit 12518c2ecf20Sopenharmony_ci * Setting this will not have any impact on the other modules. 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_AUTOIDLE) { 12548c2ecf20Sopenharmony_ci idlemode = (oh->flags & HWMOD_NO_OCP_AUTOIDLE) ? 12558c2ecf20Sopenharmony_ci 0 : 1; 12568c2ecf20Sopenharmony_ci _set_module_autoidle(oh, idlemode, &v); 12578c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci/** 12628c2ecf20Sopenharmony_ci * _idle_sysc - try to put a module into idle via OCP_SYSCONFIG 12638c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 12648c2ecf20Sopenharmony_ci * 12658c2ecf20Sopenharmony_ci * If module is marked as SWSUP_SIDLE, force the module into slave 12668c2ecf20Sopenharmony_ci * idle; otherwise, configure it for smart-idle. If module is marked 12678c2ecf20Sopenharmony_ci * as SWSUP_MSUSPEND, force the module into master standby; otherwise, 12688c2ecf20Sopenharmony_ci * configure it for smart-standby. No return value. 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_cistatic void _idle_sysc(struct omap_hwmod *oh) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci u8 idlemode, sf; 12738c2ecf20Sopenharmony_ci u32 v; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if (!oh->class->sysc) 12768c2ecf20Sopenharmony_ci return; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci v = oh->_sysc_cache; 12798c2ecf20Sopenharmony_ci sf = oh->class->sysc->sysc_flags; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_SIDLEMODE) { 12828c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_SWSUP_SIDLE) { 12838c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_FORCE; 12848c2ecf20Sopenharmony_ci } else { 12858c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_ENAWAKEUP) 12868c2ecf20Sopenharmony_ci _enable_wakeup(oh, &v); 12878c2ecf20Sopenharmony_ci if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) 12888c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART_WKUP; 12898c2ecf20Sopenharmony_ci else 12908c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci _set_slave_idlemode(oh, idlemode, &v); 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_MIDLEMODE) { 12968c2ecf20Sopenharmony_ci if ((oh->flags & HWMOD_SWSUP_MSTANDBY) || 12978c2ecf20Sopenharmony_ci (oh->flags & HWMOD_FORCE_MSTANDBY)) { 12988c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_FORCE; 12998c2ecf20Sopenharmony_ci } else { 13008c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_ENAWAKEUP) 13018c2ecf20Sopenharmony_ci _enable_wakeup(oh, &v); 13028c2ecf20Sopenharmony_ci if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) 13038c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART_WKUP; 13048c2ecf20Sopenharmony_ci else 13058c2ecf20Sopenharmony_ci idlemode = HWMOD_IDLEMODE_SMART; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci _set_master_standbymode(oh, idlemode, &v); 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci /* If the cached value is the same as the new value, skip the write */ 13118c2ecf20Sopenharmony_ci if (oh->_sysc_cache != v) 13128c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 13138c2ecf20Sopenharmony_ci} 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci/** 13168c2ecf20Sopenharmony_ci * _shutdown_sysc - force a module into idle via OCP_SYSCONFIG 13178c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 13188c2ecf20Sopenharmony_ci * 13198c2ecf20Sopenharmony_ci * Force the module into slave idle and master suspend. No return 13208c2ecf20Sopenharmony_ci * value. 13218c2ecf20Sopenharmony_ci */ 13228c2ecf20Sopenharmony_cistatic void _shutdown_sysc(struct omap_hwmod *oh) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci u32 v; 13258c2ecf20Sopenharmony_ci u8 sf; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (!oh->class->sysc) 13288c2ecf20Sopenharmony_ci return; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci v = oh->_sysc_cache; 13318c2ecf20Sopenharmony_ci sf = oh->class->sysc->sysc_flags; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_SIDLEMODE) 13348c2ecf20Sopenharmony_ci _set_slave_idlemode(oh, HWMOD_IDLEMODE_FORCE, &v); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_MIDLEMODE) 13378c2ecf20Sopenharmony_ci _set_master_standbymode(oh, HWMOD_IDLEMODE_FORCE, &v); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (sf & SYSC_HAS_AUTOIDLE) 13408c2ecf20Sopenharmony_ci _set_module_autoidle(oh, 1, &v); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci/** 13468c2ecf20Sopenharmony_ci * _lookup - find an omap_hwmod by name 13478c2ecf20Sopenharmony_ci * @name: find an omap_hwmod by name 13488c2ecf20Sopenharmony_ci * 13498c2ecf20Sopenharmony_ci * Return a pointer to an omap_hwmod by name, or NULL if not found. 13508c2ecf20Sopenharmony_ci */ 13518c2ecf20Sopenharmony_cistatic struct omap_hwmod *_lookup(const char *name) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci struct omap_hwmod *oh, *temp_oh; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci oh = NULL; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci list_for_each_entry(temp_oh, &omap_hwmod_list, node) { 13588c2ecf20Sopenharmony_ci if (!strcmp(name, temp_oh->name)) { 13598c2ecf20Sopenharmony_ci oh = temp_oh; 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return oh; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci/** 13688c2ecf20Sopenharmony_ci * _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod 13698c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 13708c2ecf20Sopenharmony_ci * 13718c2ecf20Sopenharmony_ci * Convert a clockdomain name stored in a struct omap_hwmod into a 13728c2ecf20Sopenharmony_ci * clockdomain pointer, and save it into the struct omap_hwmod. 13738c2ecf20Sopenharmony_ci * Return -EINVAL if the clkdm_name lookup failed. 13748c2ecf20Sopenharmony_ci */ 13758c2ecf20Sopenharmony_cistatic int _init_clkdm(struct omap_hwmod *oh) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci if (!oh->clkdm_name) { 13788c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: missing clockdomain\n", oh->name); 13798c2ecf20Sopenharmony_ci return 0; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci oh->clkdm = clkdm_lookup(oh->clkdm_name); 13838c2ecf20Sopenharmony_ci if (!oh->clkdm) { 13848c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: could not associate to clkdm %s\n", 13858c2ecf20Sopenharmony_ci oh->name, oh->clkdm_name); 13868c2ecf20Sopenharmony_ci return 0; 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: associated to clkdm %s\n", 13908c2ecf20Sopenharmony_ci oh->name, oh->clkdm_name); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci return 0; 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci/** 13968c2ecf20Sopenharmony_ci * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as 13978c2ecf20Sopenharmony_ci * well the clockdomain. 13988c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 13998c2ecf20Sopenharmony_ci * @np: device_node mapped to this hwmod 14008c2ecf20Sopenharmony_ci * 14018c2ecf20Sopenharmony_ci * Called by omap_hwmod_setup_*() (after omap2_clk_init()). 14028c2ecf20Sopenharmony_ci * Resolves all clock names embedded in the hwmod. Returns 0 on 14038c2ecf20Sopenharmony_ci * success, or a negative error code on failure. 14048c2ecf20Sopenharmony_ci */ 14058c2ecf20Sopenharmony_cistatic int _init_clocks(struct omap_hwmod *oh, struct device_node *np) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci int ret = 0; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_REGISTERED) 14108c2ecf20Sopenharmony_ci return 0; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: looking up clocks\n", oh->name); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (soc_ops.init_clkdm) 14158c2ecf20Sopenharmony_ci ret |= soc_ops.init_clkdm(oh); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci ret |= _init_main_clk(oh); 14188c2ecf20Sopenharmony_ci ret |= _init_interface_clks(oh); 14198c2ecf20Sopenharmony_ci ret |= _init_opt_clks(oh); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (!ret) 14228c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_CLKS_INITED; 14238c2ecf20Sopenharmony_ci else 14248c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: cannot _init_clocks\n", oh->name); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci return ret; 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci/** 14308c2ecf20Sopenharmony_ci * _lookup_hardreset - fill register bit info for this hwmod/reset line 14318c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 14328c2ecf20Sopenharmony_ci * @name: name of the reset line in the context of this hwmod 14338c2ecf20Sopenharmony_ci * @ohri: struct omap_hwmod_rst_info * that this function will fill in 14348c2ecf20Sopenharmony_ci * 14358c2ecf20Sopenharmony_ci * Return the bit position of the reset line that match the 14368c2ecf20Sopenharmony_ci * input name. Return -ENOENT if not found. 14378c2ecf20Sopenharmony_ci */ 14388c2ecf20Sopenharmony_cistatic int _lookup_hardreset(struct omap_hwmod *oh, const char *name, 14398c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci int i; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci for (i = 0; i < oh->rst_lines_cnt; i++) { 14448c2ecf20Sopenharmony_ci const char *rst_line = oh->rst_lines[i].name; 14458c2ecf20Sopenharmony_ci if (!strcmp(rst_line, name)) { 14468c2ecf20Sopenharmony_ci ohri->rst_shift = oh->rst_lines[i].rst_shift; 14478c2ecf20Sopenharmony_ci ohri->st_shift = oh->rst_lines[i].st_shift; 14488c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: %s: %s: rst %d st %d\n", 14498c2ecf20Sopenharmony_ci oh->name, __func__, rst_line, ohri->rst_shift, 14508c2ecf20Sopenharmony_ci ohri->st_shift); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci return 0; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci return -ENOENT; 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci/** 14608c2ecf20Sopenharmony_ci * _assert_hardreset - assert the HW reset line of submodules 14618c2ecf20Sopenharmony_ci * contained in the hwmod module. 14628c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 14638c2ecf20Sopenharmony_ci * @name: name of the reset line to lookup and assert 14648c2ecf20Sopenharmony_ci * 14658c2ecf20Sopenharmony_ci * Some IP like dsp, ipu or iva contain processor that require an HW 14668c2ecf20Sopenharmony_ci * reset line to be assert / deassert in order to enable fully the IP. 14678c2ecf20Sopenharmony_ci * Returns -EINVAL if @oh is null, -ENOSYS if we have no way of 14688c2ecf20Sopenharmony_ci * asserting the hardreset line on the currently-booted SoC, or passes 14698c2ecf20Sopenharmony_ci * along the return value from _lookup_hardreset() or the SoC's 14708c2ecf20Sopenharmony_ci * assert_hardreset code. 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_cistatic int _assert_hardreset(struct omap_hwmod *oh, const char *name) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info ohri; 14758c2ecf20Sopenharmony_ci int ret = -EINVAL; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (!oh) 14788c2ecf20Sopenharmony_ci return -EINVAL; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (!soc_ops.assert_hardreset) 14818c2ecf20Sopenharmony_ci return -ENOSYS; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci ret = _lookup_hardreset(oh, name, &ohri); 14848c2ecf20Sopenharmony_ci if (ret < 0) 14858c2ecf20Sopenharmony_ci return ret; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci ret = soc_ops.assert_hardreset(oh, &ohri); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci return ret; 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci/** 14938c2ecf20Sopenharmony_ci * _deassert_hardreset - deassert the HW reset line of submodules contained 14948c2ecf20Sopenharmony_ci * in the hwmod module. 14958c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 14968c2ecf20Sopenharmony_ci * @name: name of the reset line to look up and deassert 14978c2ecf20Sopenharmony_ci * 14988c2ecf20Sopenharmony_ci * Some IP like dsp, ipu or iva contain processor that require an HW 14998c2ecf20Sopenharmony_ci * reset line to be assert / deassert in order to enable fully the IP. 15008c2ecf20Sopenharmony_ci * Returns -EINVAL if @oh is null, -ENOSYS if we have no way of 15018c2ecf20Sopenharmony_ci * deasserting the hardreset line on the currently-booted SoC, or passes 15028c2ecf20Sopenharmony_ci * along the return value from _lookup_hardreset() or the SoC's 15038c2ecf20Sopenharmony_ci * deassert_hardreset code. 15048c2ecf20Sopenharmony_ci */ 15058c2ecf20Sopenharmony_cistatic int _deassert_hardreset(struct omap_hwmod *oh, const char *name) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info ohri; 15088c2ecf20Sopenharmony_ci int ret = -EINVAL; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (!oh) 15118c2ecf20Sopenharmony_ci return -EINVAL; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci if (!soc_ops.deassert_hardreset) 15148c2ecf20Sopenharmony_ci return -ENOSYS; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci ret = _lookup_hardreset(oh, name, &ohri); 15178c2ecf20Sopenharmony_ci if (ret < 0) 15188c2ecf20Sopenharmony_ci return ret; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci if (oh->clkdm) { 15218c2ecf20Sopenharmony_ci /* 15228c2ecf20Sopenharmony_ci * A clockdomain must be in SW_SUP otherwise reset 15238c2ecf20Sopenharmony_ci * might not be completed. The clockdomain can be set 15248c2ecf20Sopenharmony_ci * in HW_AUTO only when the module become ready. 15258c2ecf20Sopenharmony_ci */ 15268c2ecf20Sopenharmony_ci clkdm_deny_idle(oh->clkdm); 15278c2ecf20Sopenharmony_ci ret = clkdm_hwmod_enable(oh->clkdm, oh); 15288c2ecf20Sopenharmony_ci if (ret) { 15298c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", 15308c2ecf20Sopenharmony_ci oh->name, oh->clkdm->name, ret); 15318c2ecf20Sopenharmony_ci return ret; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci _enable_clocks(oh); 15368c2ecf20Sopenharmony_ci if (soc_ops.enable_module) 15378c2ecf20Sopenharmony_ci soc_ops.enable_module(oh); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci ret = soc_ops.deassert_hardreset(oh, &ohri); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (soc_ops.disable_module) 15428c2ecf20Sopenharmony_ci soc_ops.disable_module(oh); 15438c2ecf20Sopenharmony_ci _disable_clocks(oh); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (ret == -EBUSY) 15468c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: failed to hardreset\n", oh->name); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (oh->clkdm) { 15498c2ecf20Sopenharmony_ci /* 15508c2ecf20Sopenharmony_ci * Set the clockdomain to HW_AUTO, assuming that the 15518c2ecf20Sopenharmony_ci * previous state was HW_AUTO. 15528c2ecf20Sopenharmony_ci */ 15538c2ecf20Sopenharmony_ci clkdm_allow_idle(oh->clkdm); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci clkdm_hwmod_disable(oh->clkdm, oh); 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci return ret; 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci/** 15628c2ecf20Sopenharmony_ci * _read_hardreset - read the HW reset line state of submodules 15638c2ecf20Sopenharmony_ci * contained in the hwmod module 15648c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 15658c2ecf20Sopenharmony_ci * @name: name of the reset line to look up and read 15668c2ecf20Sopenharmony_ci * 15678c2ecf20Sopenharmony_ci * Return the state of the reset line. Returns -EINVAL if @oh is 15688c2ecf20Sopenharmony_ci * null, -ENOSYS if we have no way of reading the hardreset line 15698c2ecf20Sopenharmony_ci * status on the currently-booted SoC, or passes along the return 15708c2ecf20Sopenharmony_ci * value from _lookup_hardreset() or the SoC's is_hardreset_asserted 15718c2ecf20Sopenharmony_ci * code. 15728c2ecf20Sopenharmony_ci */ 15738c2ecf20Sopenharmony_cistatic int _read_hardreset(struct omap_hwmod *oh, const char *name) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info ohri; 15768c2ecf20Sopenharmony_ci int ret = -EINVAL; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci if (!oh) 15798c2ecf20Sopenharmony_ci return -EINVAL; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (!soc_ops.is_hardreset_asserted) 15828c2ecf20Sopenharmony_ci return -ENOSYS; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci ret = _lookup_hardreset(oh, name, &ohri); 15858c2ecf20Sopenharmony_ci if (ret < 0) 15868c2ecf20Sopenharmony_ci return ret; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci return soc_ops.is_hardreset_asserted(oh, &ohri); 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci/** 15928c2ecf20Sopenharmony_ci * _are_all_hardreset_lines_asserted - return true if the @oh is hard-reset 15938c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 15948c2ecf20Sopenharmony_ci * 15958c2ecf20Sopenharmony_ci * If all hardreset lines associated with @oh are asserted, then return true. 15968c2ecf20Sopenharmony_ci * Otherwise, if part of @oh is out hardreset or if no hardreset lines 15978c2ecf20Sopenharmony_ci * associated with @oh are asserted, then return false. 15988c2ecf20Sopenharmony_ci * This function is used to avoid executing some parts of the IP block 15998c2ecf20Sopenharmony_ci * enable/disable sequence if its hardreset line is set. 16008c2ecf20Sopenharmony_ci */ 16018c2ecf20Sopenharmony_cistatic bool _are_all_hardreset_lines_asserted(struct omap_hwmod *oh) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci int i, rst_cnt = 0; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci if (oh->rst_lines_cnt == 0) 16068c2ecf20Sopenharmony_ci return false; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci for (i = 0; i < oh->rst_lines_cnt; i++) 16098c2ecf20Sopenharmony_ci if (_read_hardreset(oh, oh->rst_lines[i].name) > 0) 16108c2ecf20Sopenharmony_ci rst_cnt++; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (oh->rst_lines_cnt == rst_cnt) 16138c2ecf20Sopenharmony_ci return true; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return false; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci/** 16198c2ecf20Sopenharmony_ci * _are_any_hardreset_lines_asserted - return true if any part of @oh is 16208c2ecf20Sopenharmony_ci * hard-reset 16218c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 16228c2ecf20Sopenharmony_ci * 16238c2ecf20Sopenharmony_ci * If any hardreset lines associated with @oh are asserted, then 16248c2ecf20Sopenharmony_ci * return true. Otherwise, if no hardreset lines associated with @oh 16258c2ecf20Sopenharmony_ci * are asserted, or if @oh has no hardreset lines, then return false. 16268c2ecf20Sopenharmony_ci * This function is used to avoid executing some parts of the IP block 16278c2ecf20Sopenharmony_ci * enable/disable sequence if any hardreset line is set. 16288c2ecf20Sopenharmony_ci */ 16298c2ecf20Sopenharmony_cistatic bool _are_any_hardreset_lines_asserted(struct omap_hwmod *oh) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci int rst_cnt = 0; 16328c2ecf20Sopenharmony_ci int i; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci for (i = 0; i < oh->rst_lines_cnt && rst_cnt == 0; i++) 16358c2ecf20Sopenharmony_ci if (_read_hardreset(oh, oh->rst_lines[i].name) > 0) 16368c2ecf20Sopenharmony_ci rst_cnt++; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci return (rst_cnt) ? true : false; 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci/** 16428c2ecf20Sopenharmony_ci * _omap4_disable_module - enable CLKCTRL modulemode on OMAP4 16438c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 16448c2ecf20Sopenharmony_ci * 16458c2ecf20Sopenharmony_ci * Disable the PRCM module mode related to the hwmod @oh. 16468c2ecf20Sopenharmony_ci * Return EINVAL if the modulemode is not supported and 0 in case of success. 16478c2ecf20Sopenharmony_ci */ 16488c2ecf20Sopenharmony_cistatic int _omap4_disable_module(struct omap_hwmod *oh) 16498c2ecf20Sopenharmony_ci{ 16508c2ecf20Sopenharmony_ci int v; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci if (!oh->clkdm || !oh->prcm.omap4.modulemode || 16538c2ecf20Sopenharmony_ci _omap4_clkctrl_managed_by_clkfwk(oh)) 16548c2ecf20Sopenharmony_ci return -EINVAL; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci /* 16578c2ecf20Sopenharmony_ci * Since integration code might still be doing something, only 16588c2ecf20Sopenharmony_ci * disable if all lines are under hardreset. 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_ci if (_are_any_hardreset_lines_asserted(oh)) 16618c2ecf20Sopenharmony_ci return 0; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci omap_cm_module_disable(oh->clkdm->prcm_partition, oh->clkdm->cm_inst, 16668c2ecf20Sopenharmony_ci oh->prcm.omap4.clkctrl_offs); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci v = _omap4_wait_target_disable(oh); 16698c2ecf20Sopenharmony_ci if (v) 16708c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", 16718c2ecf20Sopenharmony_ci oh->name); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci return 0; 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci/** 16778c2ecf20Sopenharmony_ci * _ocp_softreset - reset an omap_hwmod via the OCP_SYSCONFIG bit 16788c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 16798c2ecf20Sopenharmony_ci * 16808c2ecf20Sopenharmony_ci * Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit. hwmod must be 16818c2ecf20Sopenharmony_ci * enabled for this to work. Returns -ENOENT if the hwmod cannot be 16828c2ecf20Sopenharmony_ci * reset this way, -EINVAL if the hwmod is in the wrong state, 16838c2ecf20Sopenharmony_ci * -ETIMEDOUT if the module did not reset in time, or 0 upon success. 16848c2ecf20Sopenharmony_ci * 16858c2ecf20Sopenharmony_ci * In OMAP3 a specific SYSSTATUS register is used to get the reset status. 16868c2ecf20Sopenharmony_ci * Starting in OMAP4, some IPs do not have SYSSTATUS registers and instead 16878c2ecf20Sopenharmony_ci * use the SYSCONFIG softreset bit to provide the status. 16888c2ecf20Sopenharmony_ci * 16898c2ecf20Sopenharmony_ci * Note that some IP like McBSP do have reset control but don't have 16908c2ecf20Sopenharmony_ci * reset status. 16918c2ecf20Sopenharmony_ci */ 16928c2ecf20Sopenharmony_cistatic int _ocp_softreset(struct omap_hwmod *oh) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci u32 v; 16958c2ecf20Sopenharmony_ci int c = 0; 16968c2ecf20Sopenharmony_ci int ret = 0; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (!oh->class->sysc || 16998c2ecf20Sopenharmony_ci !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) 17008c2ecf20Sopenharmony_ci return -ENOENT; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci /* clocks must be on for this operation */ 17038c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_ENABLED) { 17048c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: reset can only be entered from enabled state\n", 17058c2ecf20Sopenharmony_ci oh->name); 17068c2ecf20Sopenharmony_ci return -EINVAL; 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci /* For some modules, all optionnal clocks need to be enabled as well */ 17108c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) 17118c2ecf20Sopenharmony_ci _enable_optional_clocks(oh); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: resetting via OCP SOFTRESET\n", oh->name); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci v = oh->_sysc_cache; 17168c2ecf20Sopenharmony_ci ret = _set_softreset(oh, &v); 17178c2ecf20Sopenharmony_ci if (ret) 17188c2ecf20Sopenharmony_ci goto dis_opt_clks; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci if (oh->class->sysc->srst_udelay) 17238c2ecf20Sopenharmony_ci udelay(oh->class->sysc->srst_udelay); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci c = _wait_softreset_complete(oh); 17268c2ecf20Sopenharmony_ci if (c == MAX_MODULE_SOFTRESET_WAIT) { 17278c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: softreset failed (waited %d usec)\n", 17288c2ecf20Sopenharmony_ci oh->name, MAX_MODULE_SOFTRESET_WAIT); 17298c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 17308c2ecf20Sopenharmony_ci goto dis_opt_clks; 17318c2ecf20Sopenharmony_ci } else { 17328c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: softreset in %d usec\n", oh->name, c); 17338c2ecf20Sopenharmony_ci } 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci ret = _clear_softreset(oh, &v); 17368c2ecf20Sopenharmony_ci if (ret) 17378c2ecf20Sopenharmony_ci goto dis_opt_clks; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci /* 17428c2ecf20Sopenharmony_ci * XXX add _HWMOD_STATE_WEDGED for modules that don't come back from 17438c2ecf20Sopenharmony_ci * _wait_target_ready() or _reset() 17448c2ecf20Sopenharmony_ci */ 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_cidis_opt_clks: 17478c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) 17488c2ecf20Sopenharmony_ci _disable_optional_clocks(oh); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci return ret; 17518c2ecf20Sopenharmony_ci} 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci/** 17548c2ecf20Sopenharmony_ci * _reset - reset an omap_hwmod 17558c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 17568c2ecf20Sopenharmony_ci * 17578c2ecf20Sopenharmony_ci * Resets an omap_hwmod @oh. If the module has a custom reset 17588c2ecf20Sopenharmony_ci * function pointer defined, then call it to reset the IP block, and 17598c2ecf20Sopenharmony_ci * pass along its return value to the caller. Otherwise, if the IP 17608c2ecf20Sopenharmony_ci * block has an OCP_SYSCONFIG register with a SOFTRESET bitfield 17618c2ecf20Sopenharmony_ci * associated with it, call a function to reset the IP block via that 17628c2ecf20Sopenharmony_ci * method, and pass along the return value to the caller. Finally, if 17638c2ecf20Sopenharmony_ci * the IP block has some hardreset lines associated with it, assert 17648c2ecf20Sopenharmony_ci * all of those, but do _not_ deassert them. (This is because driver 17658c2ecf20Sopenharmony_ci * authors have expressed an apparent requirement to control the 17668c2ecf20Sopenharmony_ci * deassertion of the hardreset lines themselves.) 17678c2ecf20Sopenharmony_ci * 17688c2ecf20Sopenharmony_ci * The default software reset mechanism for most OMAP IP blocks is 17698c2ecf20Sopenharmony_ci * triggered via the OCP_SYSCONFIG.SOFTRESET bit. However, some 17708c2ecf20Sopenharmony_ci * hwmods cannot be reset via this method. Some are not targets and 17718c2ecf20Sopenharmony_ci * therefore have no OCP header registers to access. Others (like the 17728c2ecf20Sopenharmony_ci * IVA) have idiosyncratic reset sequences. So for these relatively 17738c2ecf20Sopenharmony_ci * rare cases, custom reset code can be supplied in the struct 17748c2ecf20Sopenharmony_ci * omap_hwmod_class .reset function pointer. 17758c2ecf20Sopenharmony_ci * 17768c2ecf20Sopenharmony_ci * _set_dmadisable() is called to set the DMADISABLE bit so that it 17778c2ecf20Sopenharmony_ci * does not prevent idling of the system. This is necessary for cases 17788c2ecf20Sopenharmony_ci * where ROMCODE/BOOTLOADER uses dma and transfers control to the 17798c2ecf20Sopenharmony_ci * kernel without disabling dma. 17808c2ecf20Sopenharmony_ci * 17818c2ecf20Sopenharmony_ci * Passes along the return value from either _ocp_softreset() or the 17828c2ecf20Sopenharmony_ci * custom reset function - these must return -EINVAL if the hwmod 17838c2ecf20Sopenharmony_ci * cannot be reset this way or if the hwmod is in the wrong state, 17848c2ecf20Sopenharmony_ci * -ETIMEDOUT if the module did not reset in time, or 0 upon success. 17858c2ecf20Sopenharmony_ci */ 17868c2ecf20Sopenharmony_cistatic int _reset(struct omap_hwmod *oh) 17878c2ecf20Sopenharmony_ci{ 17888c2ecf20Sopenharmony_ci int i, r; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: resetting\n", oh->name); 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci if (oh->class->reset) { 17938c2ecf20Sopenharmony_ci r = oh->class->reset(oh); 17948c2ecf20Sopenharmony_ci } else { 17958c2ecf20Sopenharmony_ci if (oh->rst_lines_cnt > 0) { 17968c2ecf20Sopenharmony_ci for (i = 0; i < oh->rst_lines_cnt; i++) 17978c2ecf20Sopenharmony_ci _assert_hardreset(oh, oh->rst_lines[i].name); 17988c2ecf20Sopenharmony_ci return 0; 17998c2ecf20Sopenharmony_ci } else { 18008c2ecf20Sopenharmony_ci r = _ocp_softreset(oh); 18018c2ecf20Sopenharmony_ci if (r == -ENOENT) 18028c2ecf20Sopenharmony_ci r = 0; 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci _set_dmadisable(oh); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci /* 18098c2ecf20Sopenharmony_ci * OCP_SYSCONFIG bits need to be reprogrammed after a 18108c2ecf20Sopenharmony_ci * softreset. The _enable() function should be split to avoid 18118c2ecf20Sopenharmony_ci * the rewrite of the OCP_SYSCONFIG register. 18128c2ecf20Sopenharmony_ci */ 18138c2ecf20Sopenharmony_ci if (oh->class->sysc) { 18148c2ecf20Sopenharmony_ci _update_sysc_cache(oh); 18158c2ecf20Sopenharmony_ci _enable_sysc(oh); 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci return r; 18198c2ecf20Sopenharmony_ci} 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci/** 18228c2ecf20Sopenharmony_ci * _omap4_update_context_lost - increment hwmod context loss counter if 18238c2ecf20Sopenharmony_ci * hwmod context was lost, and clear hardware context loss reg 18248c2ecf20Sopenharmony_ci * @oh: hwmod to check for context loss 18258c2ecf20Sopenharmony_ci * 18268c2ecf20Sopenharmony_ci * If the PRCM indicates that the hwmod @oh lost context, increment 18278c2ecf20Sopenharmony_ci * our in-memory context loss counter, and clear the RM_*_CONTEXT 18288c2ecf20Sopenharmony_ci * bits. No return value. 18298c2ecf20Sopenharmony_ci */ 18308c2ecf20Sopenharmony_cistatic void _omap4_update_context_lost(struct omap_hwmod *oh) 18318c2ecf20Sopenharmony_ci{ 18328c2ecf20Sopenharmony_ci if (oh->prcm.omap4.flags & HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT) 18338c2ecf20Sopenharmony_ci return; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci if (!prm_was_any_context_lost_old(oh->clkdm->pwrdm.ptr->prcm_partition, 18368c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_offs, 18378c2ecf20Sopenharmony_ci oh->prcm.omap4.context_offs)) 18388c2ecf20Sopenharmony_ci return; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci oh->prcm.omap4.context_lost_counter++; 18418c2ecf20Sopenharmony_ci prm_clear_context_loss_flags_old(oh->clkdm->pwrdm.ptr->prcm_partition, 18428c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_offs, 18438c2ecf20Sopenharmony_ci oh->prcm.omap4.context_offs); 18448c2ecf20Sopenharmony_ci} 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci/** 18478c2ecf20Sopenharmony_ci * _omap4_get_context_lost - get context loss counter for a hwmod 18488c2ecf20Sopenharmony_ci * @oh: hwmod to get context loss counter for 18498c2ecf20Sopenharmony_ci * 18508c2ecf20Sopenharmony_ci * Returns the in-memory context loss counter for a hwmod. 18518c2ecf20Sopenharmony_ci */ 18528c2ecf20Sopenharmony_cistatic int _omap4_get_context_lost(struct omap_hwmod *oh) 18538c2ecf20Sopenharmony_ci{ 18548c2ecf20Sopenharmony_ci return oh->prcm.omap4.context_lost_counter; 18558c2ecf20Sopenharmony_ci} 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci/** 18588c2ecf20Sopenharmony_ci * _enable - enable an omap_hwmod 18598c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 18608c2ecf20Sopenharmony_ci * 18618c2ecf20Sopenharmony_ci * Enables an omap_hwmod @oh such that the MPU can access the hwmod's 18628c2ecf20Sopenharmony_ci * register target. Returns -EINVAL if the hwmod is in the wrong 18638c2ecf20Sopenharmony_ci * state or passes along the return value of _wait_target_ready(). 18648c2ecf20Sopenharmony_ci */ 18658c2ecf20Sopenharmony_cistatic int _enable(struct omap_hwmod *oh) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci int r; 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: enabling\n", oh->name); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci /* 18728c2ecf20Sopenharmony_ci * hwmods with HWMOD_INIT_NO_IDLE flag set are left in enabled 18738c2ecf20Sopenharmony_ci * state at init. 18748c2ecf20Sopenharmony_ci */ 18758c2ecf20Sopenharmony_ci if (oh->_int_flags & _HWMOD_SKIP_ENABLE) { 18768c2ecf20Sopenharmony_ci oh->_int_flags &= ~_HWMOD_SKIP_ENABLE; 18778c2ecf20Sopenharmony_ci return 0; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_INITIALIZED && 18818c2ecf20Sopenharmony_ci oh->_state != _HWMOD_STATE_IDLE && 18828c2ecf20Sopenharmony_ci oh->_state != _HWMOD_STATE_DISABLED) { 18838c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: enabled state can only be entered from initialized, idle, or disabled state\n", 18848c2ecf20Sopenharmony_ci oh->name); 18858c2ecf20Sopenharmony_ci return -EINVAL; 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci /* 18898c2ecf20Sopenharmony_ci * If an IP block contains HW reset lines and all of them are 18908c2ecf20Sopenharmony_ci * asserted, we let integration code associated with that 18918c2ecf20Sopenharmony_ci * block handle the enable. We've received very little 18928c2ecf20Sopenharmony_ci * information on what those driver authors need, and until 18938c2ecf20Sopenharmony_ci * detailed information is provided and the driver code is 18948c2ecf20Sopenharmony_ci * posted to the public lists, this is probably the best we 18958c2ecf20Sopenharmony_ci * can do. 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_ci if (_are_all_hardreset_lines_asserted(oh)) 18988c2ecf20Sopenharmony_ci return 0; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci _add_initiator_dep(oh, mpu_oh); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci if (oh->clkdm) { 19038c2ecf20Sopenharmony_ci /* 19048c2ecf20Sopenharmony_ci * A clockdomain must be in SW_SUP before enabling 19058c2ecf20Sopenharmony_ci * completely the module. The clockdomain can be set 19068c2ecf20Sopenharmony_ci * in HW_AUTO only when the module become ready. 19078c2ecf20Sopenharmony_ci */ 19088c2ecf20Sopenharmony_ci clkdm_deny_idle(oh->clkdm); 19098c2ecf20Sopenharmony_ci r = clkdm_hwmod_enable(oh->clkdm, oh); 19108c2ecf20Sopenharmony_ci if (r) { 19118c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", 19128c2ecf20Sopenharmony_ci oh->name, oh->clkdm->name, r); 19138c2ecf20Sopenharmony_ci return r; 19148c2ecf20Sopenharmony_ci } 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci _enable_clocks(oh); 19188c2ecf20Sopenharmony_ci if (soc_ops.enable_module) 19198c2ecf20Sopenharmony_ci soc_ops.enable_module(oh); 19208c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_BLOCK_WFI) 19218c2ecf20Sopenharmony_ci cpu_idle_poll_ctrl(true); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci if (soc_ops.update_context_lost) 19248c2ecf20Sopenharmony_ci soc_ops.update_context_lost(oh); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci r = (soc_ops.wait_target_ready) ? soc_ops.wait_target_ready(oh) : 19278c2ecf20Sopenharmony_ci -EINVAL; 19288c2ecf20Sopenharmony_ci if (oh->clkdm && !(oh->flags & HWMOD_CLKDM_NOAUTO)) 19298c2ecf20Sopenharmony_ci clkdm_allow_idle(oh->clkdm); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci if (!r) { 19328c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_ENABLED; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci /* Access the sysconfig only if the target is ready */ 19358c2ecf20Sopenharmony_ci if (oh->class->sysc) { 19368c2ecf20Sopenharmony_ci if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) 19378c2ecf20Sopenharmony_ci _update_sysc_cache(oh); 19388c2ecf20Sopenharmony_ci _enable_sysc(oh); 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci } else { 19418c2ecf20Sopenharmony_ci if (soc_ops.disable_module) 19428c2ecf20Sopenharmony_ci soc_ops.disable_module(oh); 19438c2ecf20Sopenharmony_ci _disable_clocks(oh); 19448c2ecf20Sopenharmony_ci pr_err("omap_hwmod: %s: _wait_target_ready failed: %d\n", 19458c2ecf20Sopenharmony_ci oh->name, r); 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci if (oh->clkdm) 19488c2ecf20Sopenharmony_ci clkdm_hwmod_disable(oh->clkdm, oh); 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci return r; 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci/** 19558c2ecf20Sopenharmony_ci * _idle - idle an omap_hwmod 19568c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 19578c2ecf20Sopenharmony_ci * 19588c2ecf20Sopenharmony_ci * Idles an omap_hwmod @oh. This should be called once the hwmod has 19598c2ecf20Sopenharmony_ci * no further work. Returns -EINVAL if the hwmod is in the wrong 19608c2ecf20Sopenharmony_ci * state or returns 0. 19618c2ecf20Sopenharmony_ci */ 19628c2ecf20Sopenharmony_cistatic int _idle(struct omap_hwmod *oh) 19638c2ecf20Sopenharmony_ci{ 19648c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_NO_IDLE) { 19658c2ecf20Sopenharmony_ci oh->_int_flags |= _HWMOD_SKIP_ENABLE; 19668c2ecf20Sopenharmony_ci return 0; 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: idling\n", oh->name); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci if (_are_all_hardreset_lines_asserted(oh)) 19728c2ecf20Sopenharmony_ci return 0; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_ENABLED) { 19758c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: idle state can only be entered from enabled state\n", 19768c2ecf20Sopenharmony_ci oh->name); 19778c2ecf20Sopenharmony_ci return -EINVAL; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci if (oh->class->sysc) 19818c2ecf20Sopenharmony_ci _idle_sysc(oh); 19828c2ecf20Sopenharmony_ci _del_initiator_dep(oh, mpu_oh); 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci /* 19858c2ecf20Sopenharmony_ci * If HWMOD_CLKDM_NOAUTO is set then we don't 19868c2ecf20Sopenharmony_ci * deny idle the clkdm again since idle was already denied 19878c2ecf20Sopenharmony_ci * in _enable() 19888c2ecf20Sopenharmony_ci */ 19898c2ecf20Sopenharmony_ci if (oh->clkdm && !(oh->flags & HWMOD_CLKDM_NOAUTO)) 19908c2ecf20Sopenharmony_ci clkdm_deny_idle(oh->clkdm); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_BLOCK_WFI) 19938c2ecf20Sopenharmony_ci cpu_idle_poll_ctrl(false); 19948c2ecf20Sopenharmony_ci if (soc_ops.disable_module) 19958c2ecf20Sopenharmony_ci soc_ops.disable_module(oh); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* 19988c2ecf20Sopenharmony_ci * The module must be in idle mode before disabling any parents 19998c2ecf20Sopenharmony_ci * clocks. Otherwise, the parent clock might be disabled before 20008c2ecf20Sopenharmony_ci * the module transition is done, and thus will prevent the 20018c2ecf20Sopenharmony_ci * transition to complete properly. 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_ci _disable_clocks(oh); 20048c2ecf20Sopenharmony_ci if (oh->clkdm) { 20058c2ecf20Sopenharmony_ci clkdm_allow_idle(oh->clkdm); 20068c2ecf20Sopenharmony_ci clkdm_hwmod_disable(oh->clkdm, oh); 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_IDLE; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci return 0; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci/** 20158c2ecf20Sopenharmony_ci * _shutdown - shutdown an omap_hwmod 20168c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 20178c2ecf20Sopenharmony_ci * 20188c2ecf20Sopenharmony_ci * Shut down an omap_hwmod @oh. This should be called when the driver 20198c2ecf20Sopenharmony_ci * used for the hwmod is removed or unloaded or if the driver is not 20208c2ecf20Sopenharmony_ci * used by the system. Returns -EINVAL if the hwmod is in the wrong 20218c2ecf20Sopenharmony_ci * state or returns 0. 20228c2ecf20Sopenharmony_ci */ 20238c2ecf20Sopenharmony_cistatic int _shutdown(struct omap_hwmod *oh) 20248c2ecf20Sopenharmony_ci{ 20258c2ecf20Sopenharmony_ci int ret, i; 20268c2ecf20Sopenharmony_ci u8 prev_state; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci if (_are_all_hardreset_lines_asserted(oh)) 20298c2ecf20Sopenharmony_ci return 0; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_IDLE && 20328c2ecf20Sopenharmony_ci oh->_state != _HWMOD_STATE_ENABLED) { 20338c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: disabled state can only be entered from idle, or enabled state\n", 20348c2ecf20Sopenharmony_ci oh->name); 20358c2ecf20Sopenharmony_ci return -EINVAL; 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: disabling\n", oh->name); 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci if (oh->class->pre_shutdown) { 20418c2ecf20Sopenharmony_ci prev_state = oh->_state; 20428c2ecf20Sopenharmony_ci if (oh->_state == _HWMOD_STATE_IDLE) 20438c2ecf20Sopenharmony_ci _enable(oh); 20448c2ecf20Sopenharmony_ci ret = oh->class->pre_shutdown(oh); 20458c2ecf20Sopenharmony_ci if (ret) { 20468c2ecf20Sopenharmony_ci if (prev_state == _HWMOD_STATE_IDLE) 20478c2ecf20Sopenharmony_ci _idle(oh); 20488c2ecf20Sopenharmony_ci return ret; 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci if (oh->class->sysc) { 20538c2ecf20Sopenharmony_ci if (oh->_state == _HWMOD_STATE_IDLE) 20548c2ecf20Sopenharmony_ci _enable(oh); 20558c2ecf20Sopenharmony_ci _shutdown_sysc(oh); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci /* clocks and deps are already disabled in idle */ 20598c2ecf20Sopenharmony_ci if (oh->_state == _HWMOD_STATE_ENABLED) { 20608c2ecf20Sopenharmony_ci _del_initiator_dep(oh, mpu_oh); 20618c2ecf20Sopenharmony_ci /* XXX what about the other system initiators here? dma, dsp */ 20628c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_BLOCK_WFI) 20638c2ecf20Sopenharmony_ci cpu_idle_poll_ctrl(false); 20648c2ecf20Sopenharmony_ci if (soc_ops.disable_module) 20658c2ecf20Sopenharmony_ci soc_ops.disable_module(oh); 20668c2ecf20Sopenharmony_ci _disable_clocks(oh); 20678c2ecf20Sopenharmony_ci if (oh->clkdm) 20688c2ecf20Sopenharmony_ci clkdm_hwmod_disable(oh->clkdm, oh); 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci /* XXX Should this code also force-disable the optional clocks? */ 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci for (i = 0; i < oh->rst_lines_cnt; i++) 20738c2ecf20Sopenharmony_ci _assert_hardreset(oh, oh->rst_lines[i].name); 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_DISABLED; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci return 0; 20788c2ecf20Sopenharmony_ci} 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_cistatic int of_dev_find_hwmod(struct device_node *np, 20818c2ecf20Sopenharmony_ci struct omap_hwmod *oh) 20828c2ecf20Sopenharmony_ci{ 20838c2ecf20Sopenharmony_ci int count, i, res; 20848c2ecf20Sopenharmony_ci const char *p; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci count = of_property_count_strings(np, "ti,hwmods"); 20878c2ecf20Sopenharmony_ci if (count < 1) 20888c2ecf20Sopenharmony_ci return -ENODEV; 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 20918c2ecf20Sopenharmony_ci res = of_property_read_string_index(np, "ti,hwmods", 20928c2ecf20Sopenharmony_ci i, &p); 20938c2ecf20Sopenharmony_ci if (res) 20948c2ecf20Sopenharmony_ci continue; 20958c2ecf20Sopenharmony_ci if (!strcmp(p, oh->name)) { 20968c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: dt %pOFn[%i] uses hwmod %s\n", 20978c2ecf20Sopenharmony_ci np, i, oh->name); 20988c2ecf20Sopenharmony_ci return i; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci return -ENODEV; 21038c2ecf20Sopenharmony_ci} 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci/** 21068c2ecf20Sopenharmony_ci * of_dev_hwmod_lookup - look up needed hwmod from dt blob 21078c2ecf20Sopenharmony_ci * @np: struct device_node * 21088c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 21098c2ecf20Sopenharmony_ci * @index: index of the entry found 21108c2ecf20Sopenharmony_ci * @found: struct device_node * found or NULL 21118c2ecf20Sopenharmony_ci * 21128c2ecf20Sopenharmony_ci * Parse the dt blob and find out needed hwmod. Recursive function is 21138c2ecf20Sopenharmony_ci * implemented to take care hierarchical dt blob parsing. 21148c2ecf20Sopenharmony_ci * Return: Returns 0 on success, -ENODEV when not found. 21158c2ecf20Sopenharmony_ci */ 21168c2ecf20Sopenharmony_cistatic int of_dev_hwmod_lookup(struct device_node *np, 21178c2ecf20Sopenharmony_ci struct omap_hwmod *oh, 21188c2ecf20Sopenharmony_ci int *index, 21198c2ecf20Sopenharmony_ci struct device_node **found) 21208c2ecf20Sopenharmony_ci{ 21218c2ecf20Sopenharmony_ci struct device_node *np0 = NULL; 21228c2ecf20Sopenharmony_ci int res; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci res = of_dev_find_hwmod(np, oh); 21258c2ecf20Sopenharmony_ci if (res >= 0) { 21268c2ecf20Sopenharmony_ci *found = np; 21278c2ecf20Sopenharmony_ci *index = res; 21288c2ecf20Sopenharmony_ci return 0; 21298c2ecf20Sopenharmony_ci } 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci for_each_child_of_node(np, np0) { 21328c2ecf20Sopenharmony_ci struct device_node *fc; 21338c2ecf20Sopenharmony_ci int i; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci res = of_dev_hwmod_lookup(np0, oh, &i, &fc); 21368c2ecf20Sopenharmony_ci if (res == 0) { 21378c2ecf20Sopenharmony_ci *found = fc; 21388c2ecf20Sopenharmony_ci *index = i; 21398c2ecf20Sopenharmony_ci return 0; 21408c2ecf20Sopenharmony_ci } 21418c2ecf20Sopenharmony_ci } 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci *found = NULL; 21448c2ecf20Sopenharmony_ci *index = 0; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci return -ENODEV; 21478c2ecf20Sopenharmony_ci} 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci/** 21508c2ecf20Sopenharmony_ci * omap_hwmod_fix_mpu_rt_idx - fix up mpu_rt_idx register offsets 21518c2ecf20Sopenharmony_ci * 21528c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 21538c2ecf20Sopenharmony_ci * @np: struct device_node * 21548c2ecf20Sopenharmony_ci * 21558c2ecf20Sopenharmony_ci * Fix up module register offsets for modules with mpu_rt_idx. 21568c2ecf20Sopenharmony_ci * Only needed for cpsw with interconnect target module defined 21578c2ecf20Sopenharmony_ci * in device tree while still using legacy hwmod platform data 21588c2ecf20Sopenharmony_ci * for rev, sysc and syss registers. 21598c2ecf20Sopenharmony_ci * 21608c2ecf20Sopenharmony_ci * Can be removed when all cpsw hwmod platform data has been 21618c2ecf20Sopenharmony_ci * dropped. 21628c2ecf20Sopenharmony_ci */ 21638c2ecf20Sopenharmony_cistatic void omap_hwmod_fix_mpu_rt_idx(struct omap_hwmod *oh, 21648c2ecf20Sopenharmony_ci struct device_node *np, 21658c2ecf20Sopenharmony_ci struct resource *res) 21668c2ecf20Sopenharmony_ci{ 21678c2ecf20Sopenharmony_ci struct device_node *child = NULL; 21688c2ecf20Sopenharmony_ci int error; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci child = of_get_next_child(np, child); 21718c2ecf20Sopenharmony_ci if (!child) 21728c2ecf20Sopenharmony_ci return; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci error = of_address_to_resource(child, oh->mpu_rt_idx, res); 21758c2ecf20Sopenharmony_ci if (error) 21768c2ecf20Sopenharmony_ci pr_err("%s: error mapping mpu_rt_idx: %i\n", 21778c2ecf20Sopenharmony_ci __func__, error); 21788c2ecf20Sopenharmony_ci} 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci/** 21818c2ecf20Sopenharmony_ci * omap_hwmod_parse_module_range - map module IO range from device tree 21828c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 21838c2ecf20Sopenharmony_ci * @np: struct device_node * 21848c2ecf20Sopenharmony_ci * 21858c2ecf20Sopenharmony_ci * Parse the device tree range an interconnect target module provides 21868c2ecf20Sopenharmony_ci * for it's child device IP blocks. This way we can support the old 21878c2ecf20Sopenharmony_ci * "ti,hwmods" property with just dts data without a need for platform 21888c2ecf20Sopenharmony_ci * data for IO resources. And we don't need all the child IP device 21898c2ecf20Sopenharmony_ci * nodes available in the dts. 21908c2ecf20Sopenharmony_ci */ 21918c2ecf20Sopenharmony_ciint omap_hwmod_parse_module_range(struct omap_hwmod *oh, 21928c2ecf20Sopenharmony_ci struct device_node *np, 21938c2ecf20Sopenharmony_ci struct resource *res) 21948c2ecf20Sopenharmony_ci{ 21958c2ecf20Sopenharmony_ci struct property *prop; 21968c2ecf20Sopenharmony_ci const __be32 *ranges; 21978c2ecf20Sopenharmony_ci const char *name; 21988c2ecf20Sopenharmony_ci u32 nr_addr, nr_size; 21998c2ecf20Sopenharmony_ci u64 base, size; 22008c2ecf20Sopenharmony_ci int len, error; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci if (!res) 22038c2ecf20Sopenharmony_ci return -EINVAL; 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci ranges = of_get_property(np, "ranges", &len); 22068c2ecf20Sopenharmony_ci if (!ranges) 22078c2ecf20Sopenharmony_ci return -ENOENT; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci len /= sizeof(*ranges); 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci if (len < 3) 22128c2ecf20Sopenharmony_ci return -EINVAL; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci of_property_for_each_string(np, "compatible", prop, name) 22158c2ecf20Sopenharmony_ci if (!strncmp("ti,sysc-", name, 8)) 22168c2ecf20Sopenharmony_ci break; 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci if (!name) 22198c2ecf20Sopenharmony_ci return -ENOENT; 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci error = of_property_read_u32(np, "#address-cells", &nr_addr); 22228c2ecf20Sopenharmony_ci if (error) 22238c2ecf20Sopenharmony_ci return -ENOENT; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci error = of_property_read_u32(np, "#size-cells", &nr_size); 22268c2ecf20Sopenharmony_ci if (error) 22278c2ecf20Sopenharmony_ci return -ENOENT; 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci if (nr_addr != 1 || nr_size != 1) { 22308c2ecf20Sopenharmony_ci pr_err("%s: invalid range for %s->%pOFn\n", __func__, 22318c2ecf20Sopenharmony_ci oh->name, np); 22328c2ecf20Sopenharmony_ci return -EINVAL; 22338c2ecf20Sopenharmony_ci } 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci ranges++; 22368c2ecf20Sopenharmony_ci base = of_translate_address(np, ranges++); 22378c2ecf20Sopenharmony_ci size = be32_to_cpup(ranges); 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s %pOFn at 0x%llx size 0x%llx\n", 22408c2ecf20Sopenharmony_ci oh->name, np, base, size); 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci if (oh && oh->mpu_rt_idx) { 22438c2ecf20Sopenharmony_ci omap_hwmod_fix_mpu_rt_idx(oh, np, res); 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci return 0; 22468c2ecf20Sopenharmony_ci } 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci res->start = base; 22498c2ecf20Sopenharmony_ci res->end = base + size - 1; 22508c2ecf20Sopenharmony_ci res->flags = IORESOURCE_MEM; 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci return 0; 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci/** 22568c2ecf20Sopenharmony_ci * _init_mpu_rt_base - populate the virtual address for a hwmod 22578c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to locate the virtual address 22588c2ecf20Sopenharmony_ci * @data: (unused, caller should pass NULL) 22598c2ecf20Sopenharmony_ci * @index: index of the reg entry iospace in device tree 22608c2ecf20Sopenharmony_ci * @np: struct device_node * of the IP block's device node in the DT data 22618c2ecf20Sopenharmony_ci * 22628c2ecf20Sopenharmony_ci * Cache the virtual address used by the MPU to access this IP block's 22638c2ecf20Sopenharmony_ci * registers. This address is needed early so the OCP registers that 22648c2ecf20Sopenharmony_ci * are part of the device's address space can be ioremapped properly. 22658c2ecf20Sopenharmony_ci * 22668c2ecf20Sopenharmony_ci * If SYSC access is not needed, the registers will not be remapped 22678c2ecf20Sopenharmony_ci * and non-availability of MPU access is not treated as an error. 22688c2ecf20Sopenharmony_ci * 22698c2ecf20Sopenharmony_ci * Returns 0 on success, -EINVAL if an invalid hwmod is passed, and 22708c2ecf20Sopenharmony_ci * -ENXIO on absent or invalid register target address space. 22718c2ecf20Sopenharmony_ci */ 22728c2ecf20Sopenharmony_cistatic int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, 22738c2ecf20Sopenharmony_ci int index, struct device_node *np) 22748c2ecf20Sopenharmony_ci{ 22758c2ecf20Sopenharmony_ci void __iomem *va_start = NULL; 22768c2ecf20Sopenharmony_ci struct resource res; 22778c2ecf20Sopenharmony_ci int error; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci if (!oh) 22808c2ecf20Sopenharmony_ci return -EINVAL; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci _save_mpu_port_index(oh); 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci /* if we don't need sysc access we don't need to ioremap */ 22858c2ecf20Sopenharmony_ci if (!oh->class->sysc) 22868c2ecf20Sopenharmony_ci return 0; 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci /* we can't continue without MPU PORT if we need sysc access */ 22898c2ecf20Sopenharmony_ci if (oh->_int_flags & _HWMOD_NO_MPU_PORT) 22908c2ecf20Sopenharmony_ci return -ENXIO; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (!np) { 22938c2ecf20Sopenharmony_ci pr_err("omap_hwmod: %s: no dt node\n", oh->name); 22948c2ecf20Sopenharmony_ci return -ENXIO; 22958c2ecf20Sopenharmony_ci } 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci /* Do we have a dts range for the interconnect target module? */ 22988c2ecf20Sopenharmony_ci error = omap_hwmod_parse_module_range(oh, np, &res); 22998c2ecf20Sopenharmony_ci if (!error) 23008c2ecf20Sopenharmony_ci va_start = ioremap(res.start, resource_size(&res)); 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci /* No ranges, rely on device reg entry */ 23038c2ecf20Sopenharmony_ci if (!va_start) 23048c2ecf20Sopenharmony_ci va_start = of_iomap(np, index + oh->mpu_rt_idx); 23058c2ecf20Sopenharmony_ci if (!va_start) { 23068c2ecf20Sopenharmony_ci pr_err("omap_hwmod: %s: Missing dt reg%i for %pOF\n", 23078c2ecf20Sopenharmony_ci oh->name, index, np); 23088c2ecf20Sopenharmony_ci return -ENXIO; 23098c2ecf20Sopenharmony_ci } 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: MPU register target at va %p\n", 23128c2ecf20Sopenharmony_ci oh->name, va_start); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci oh->_mpu_rt_va = va_start; 23158c2ecf20Sopenharmony_ci return 0; 23168c2ecf20Sopenharmony_ci} 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_cistatic void __init parse_module_flags(struct omap_hwmod *oh, 23198c2ecf20Sopenharmony_ci struct device_node *np) 23208c2ecf20Sopenharmony_ci{ 23218c2ecf20Sopenharmony_ci if (of_find_property(np, "ti,no-reset-on-init", NULL)) 23228c2ecf20Sopenharmony_ci oh->flags |= HWMOD_INIT_NO_RESET; 23238c2ecf20Sopenharmony_ci if (of_find_property(np, "ti,no-idle-on-init", NULL)) 23248c2ecf20Sopenharmony_ci oh->flags |= HWMOD_INIT_NO_IDLE; 23258c2ecf20Sopenharmony_ci if (of_find_property(np, "ti,no-idle", NULL)) 23268c2ecf20Sopenharmony_ci oh->flags |= HWMOD_NO_IDLE; 23278c2ecf20Sopenharmony_ci} 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci/** 23308c2ecf20Sopenharmony_ci * _init - initialize internal data for the hwmod @oh 23318c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 23328c2ecf20Sopenharmony_ci * @n: (unused) 23338c2ecf20Sopenharmony_ci * 23348c2ecf20Sopenharmony_ci * Look up the clocks and the address space used by the MPU to access 23358c2ecf20Sopenharmony_ci * registers belonging to the hwmod @oh. @oh must already be 23368c2ecf20Sopenharmony_ci * registered at this point. This is the first of two phases for 23378c2ecf20Sopenharmony_ci * hwmod initialization. Code called here does not touch any hardware 23388c2ecf20Sopenharmony_ci * registers, it simply prepares internal data structures. Returns 0 23398c2ecf20Sopenharmony_ci * upon success or if the hwmod isn't registered or if the hwmod's 23408c2ecf20Sopenharmony_ci * address space is not defined, or -EINVAL upon failure. 23418c2ecf20Sopenharmony_ci */ 23428c2ecf20Sopenharmony_cistatic int __init _init(struct omap_hwmod *oh, void *data) 23438c2ecf20Sopenharmony_ci{ 23448c2ecf20Sopenharmony_ci int r, index; 23458c2ecf20Sopenharmony_ci struct device_node *np = NULL; 23468c2ecf20Sopenharmony_ci struct device_node *bus; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_REGISTERED) 23498c2ecf20Sopenharmony_ci return 0; 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci bus = of_find_node_by_name(NULL, "ocp"); 23528c2ecf20Sopenharmony_ci if (!bus) 23538c2ecf20Sopenharmony_ci return -ENODEV; 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci r = of_dev_hwmod_lookup(bus, oh, &index, &np); 23568c2ecf20Sopenharmony_ci if (r) 23578c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s missing dt data\n", oh->name); 23588c2ecf20Sopenharmony_ci else if (np && index) 23598c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s using broken dt data from %pOFn\n", 23608c2ecf20Sopenharmony_ci oh->name, np); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci r = _init_mpu_rt_base(oh, NULL, index, np); 23638c2ecf20Sopenharmony_ci if (r < 0) { 23648c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: doesn't have mpu register target base\n", 23658c2ecf20Sopenharmony_ci oh->name); 23668c2ecf20Sopenharmony_ci return 0; 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci r = _init_clocks(oh, np); 23708c2ecf20Sopenharmony_ci if (r < 0) { 23718c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: couldn't init clocks\n", oh->name); 23728c2ecf20Sopenharmony_ci return -EINVAL; 23738c2ecf20Sopenharmony_ci } 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci if (np) { 23768c2ecf20Sopenharmony_ci struct device_node *child; 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci parse_module_flags(oh, np); 23798c2ecf20Sopenharmony_ci child = of_get_next_child(np, NULL); 23808c2ecf20Sopenharmony_ci if (child) 23818c2ecf20Sopenharmony_ci parse_module_flags(oh, child); 23828c2ecf20Sopenharmony_ci } 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_INITIALIZED; 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci return 0; 23878c2ecf20Sopenharmony_ci} 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci/** 23908c2ecf20Sopenharmony_ci * _setup_iclk_autoidle - configure an IP block's interface clocks 23918c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 23928c2ecf20Sopenharmony_ci * 23938c2ecf20Sopenharmony_ci * Set up the module's interface clocks. XXX This function is still mostly 23948c2ecf20Sopenharmony_ci * a stub; implementing this properly requires iclk autoidle usecounting in 23958c2ecf20Sopenharmony_ci * the clock code. No return value. 23968c2ecf20Sopenharmony_ci */ 23978c2ecf20Sopenharmony_cistatic void _setup_iclk_autoidle(struct omap_hwmod *oh) 23988c2ecf20Sopenharmony_ci{ 23998c2ecf20Sopenharmony_ci struct omap_hwmod_ocp_if *os; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_INITIALIZED) 24028c2ecf20Sopenharmony_ci return; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci list_for_each_entry(os, &oh->slave_ports, node) { 24058c2ecf20Sopenharmony_ci if (!os->_clk) 24068c2ecf20Sopenharmony_ci continue; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci if (os->flags & OCPIF_SWSUP_IDLE) { 24098c2ecf20Sopenharmony_ci /* 24108c2ecf20Sopenharmony_ci * we might have multiple users of one iclk with 24118c2ecf20Sopenharmony_ci * different requirements, disable autoidle when 24128c2ecf20Sopenharmony_ci * the module is enabled, e.g. dss iclk 24138c2ecf20Sopenharmony_ci */ 24148c2ecf20Sopenharmony_ci } else { 24158c2ecf20Sopenharmony_ci /* we are enabling autoidle afterwards anyways */ 24168c2ecf20Sopenharmony_ci clk_enable(os->_clk); 24178c2ecf20Sopenharmony_ci } 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci return; 24218c2ecf20Sopenharmony_ci} 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci/** 24248c2ecf20Sopenharmony_ci * _setup_reset - reset an IP block during the setup process 24258c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 24268c2ecf20Sopenharmony_ci * 24278c2ecf20Sopenharmony_ci * Reset the IP block corresponding to the hwmod @oh during the setup 24288c2ecf20Sopenharmony_ci * process. The IP block is first enabled so it can be successfully 24298c2ecf20Sopenharmony_ci * reset. Returns 0 upon success or a negative error code upon 24308c2ecf20Sopenharmony_ci * failure. 24318c2ecf20Sopenharmony_ci */ 24328c2ecf20Sopenharmony_cistatic int _setup_reset(struct omap_hwmod *oh) 24338c2ecf20Sopenharmony_ci{ 24348c2ecf20Sopenharmony_ci int r = 0; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_INITIALIZED) 24378c2ecf20Sopenharmony_ci return -EINVAL; 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_EXT_OPT_MAIN_CLK) 24408c2ecf20Sopenharmony_ci return -EPERM; 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci if (oh->rst_lines_cnt == 0) { 24438c2ecf20Sopenharmony_ci r = _enable(oh); 24448c2ecf20Sopenharmony_ci if (r) { 24458c2ecf20Sopenharmony_ci pr_warn("omap_hwmod: %s: cannot be enabled for reset (%d)\n", 24468c2ecf20Sopenharmony_ci oh->name, oh->_state); 24478c2ecf20Sopenharmony_ci return -EINVAL; 24488c2ecf20Sopenharmony_ci } 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci if (!(oh->flags & HWMOD_INIT_NO_RESET)) 24528c2ecf20Sopenharmony_ci r = _reset(oh); 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci return r; 24558c2ecf20Sopenharmony_ci} 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci/** 24588c2ecf20Sopenharmony_ci * _setup_postsetup - transition to the appropriate state after _setup 24598c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 24608c2ecf20Sopenharmony_ci * 24618c2ecf20Sopenharmony_ci * Place an IP block represented by @oh into a "post-setup" state -- 24628c2ecf20Sopenharmony_ci * either IDLE, ENABLED, or DISABLED. ("post-setup" simply means that 24638c2ecf20Sopenharmony_ci * this function is called at the end of _setup().) The postsetup 24648c2ecf20Sopenharmony_ci * state for an IP block can be changed by calling 24658c2ecf20Sopenharmony_ci * omap_hwmod_enter_postsetup_state() early in the boot process, 24668c2ecf20Sopenharmony_ci * before one of the omap_hwmod_setup*() functions are called for the 24678c2ecf20Sopenharmony_ci * IP block. 24688c2ecf20Sopenharmony_ci * 24698c2ecf20Sopenharmony_ci * The IP block stays in this state until a PM runtime-based driver is 24708c2ecf20Sopenharmony_ci * loaded for that IP block. A post-setup state of IDLE is 24718c2ecf20Sopenharmony_ci * appropriate for almost all IP blocks with runtime PM-enabled 24728c2ecf20Sopenharmony_ci * drivers, since those drivers are able to enable the IP block. A 24738c2ecf20Sopenharmony_ci * post-setup state of ENABLED is appropriate for kernels with PM 24748c2ecf20Sopenharmony_ci * runtime disabled. The DISABLED state is appropriate for unusual IP 24758c2ecf20Sopenharmony_ci * blocks such as the MPU WDTIMER on kernels without WDTIMER drivers 24768c2ecf20Sopenharmony_ci * included, since the WDTIMER starts running on reset and will reset 24778c2ecf20Sopenharmony_ci * the MPU if left active. 24788c2ecf20Sopenharmony_ci * 24798c2ecf20Sopenharmony_ci * This post-setup mechanism is deprecated. Once all of the OMAP 24808c2ecf20Sopenharmony_ci * drivers have been converted to use PM runtime, and all of the IP 24818c2ecf20Sopenharmony_ci * block data and interconnect data is available to the hwmod code, it 24828c2ecf20Sopenharmony_ci * should be possible to replace this mechanism with a "lazy reset" 24838c2ecf20Sopenharmony_ci * arrangement. In a "lazy reset" setup, each IP block is enabled 24848c2ecf20Sopenharmony_ci * when the driver first probes, then all remaining IP blocks without 24858c2ecf20Sopenharmony_ci * drivers are either shut down or enabled after the drivers have 24868c2ecf20Sopenharmony_ci * loaded. However, this cannot take place until the above 24878c2ecf20Sopenharmony_ci * preconditions have been met, since otherwise the late reset code 24888c2ecf20Sopenharmony_ci * has no way of knowing which IP blocks are in use by drivers, and 24898c2ecf20Sopenharmony_ci * which ones are unused. 24908c2ecf20Sopenharmony_ci * 24918c2ecf20Sopenharmony_ci * No return value. 24928c2ecf20Sopenharmony_ci */ 24938c2ecf20Sopenharmony_cistatic void _setup_postsetup(struct omap_hwmod *oh) 24948c2ecf20Sopenharmony_ci{ 24958c2ecf20Sopenharmony_ci u8 postsetup_state; 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci if (oh->rst_lines_cnt > 0) 24988c2ecf20Sopenharmony_ci return; 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci postsetup_state = oh->_postsetup_state; 25018c2ecf20Sopenharmony_ci if (postsetup_state == _HWMOD_STATE_UNKNOWN) 25028c2ecf20Sopenharmony_ci postsetup_state = _HWMOD_STATE_ENABLED; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci /* 25058c2ecf20Sopenharmony_ci * XXX HWMOD_INIT_NO_IDLE does not belong in hwmod data - 25068c2ecf20Sopenharmony_ci * it should be set by the core code as a runtime flag during startup 25078c2ecf20Sopenharmony_ci */ 25088c2ecf20Sopenharmony_ci if ((oh->flags & (HWMOD_INIT_NO_IDLE | HWMOD_NO_IDLE)) && 25098c2ecf20Sopenharmony_ci (postsetup_state == _HWMOD_STATE_IDLE)) { 25108c2ecf20Sopenharmony_ci oh->_int_flags |= _HWMOD_SKIP_ENABLE; 25118c2ecf20Sopenharmony_ci postsetup_state = _HWMOD_STATE_ENABLED; 25128c2ecf20Sopenharmony_ci } 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci if (postsetup_state == _HWMOD_STATE_IDLE) 25158c2ecf20Sopenharmony_ci _idle(oh); 25168c2ecf20Sopenharmony_ci else if (postsetup_state == _HWMOD_STATE_DISABLED) 25178c2ecf20Sopenharmony_ci _shutdown(oh); 25188c2ecf20Sopenharmony_ci else if (postsetup_state != _HWMOD_STATE_ENABLED) 25198c2ecf20Sopenharmony_ci WARN(1, "hwmod: %s: unknown postsetup state %d! defaulting to enabled\n", 25208c2ecf20Sopenharmony_ci oh->name, postsetup_state); 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci return; 25238c2ecf20Sopenharmony_ci} 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci/** 25268c2ecf20Sopenharmony_ci * _setup - prepare IP block hardware for use 25278c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 25288c2ecf20Sopenharmony_ci * @n: (unused, pass NULL) 25298c2ecf20Sopenharmony_ci * 25308c2ecf20Sopenharmony_ci * Configure the IP block represented by @oh. This may include 25318c2ecf20Sopenharmony_ci * enabling the IP block, resetting it, and placing it into a 25328c2ecf20Sopenharmony_ci * post-setup state, depending on the type of IP block and applicable 25338c2ecf20Sopenharmony_ci * flags. IP blocks are reset to prevent any previous configuration 25348c2ecf20Sopenharmony_ci * by the bootloader or previous operating system from interfering 25358c2ecf20Sopenharmony_ci * with power management or other parts of the system. The reset can 25368c2ecf20Sopenharmony_ci * be avoided; see omap_hwmod_no_setup_reset(). This is the second of 25378c2ecf20Sopenharmony_ci * two phases for hwmod initialization. Code called here generally 25388c2ecf20Sopenharmony_ci * affects the IP block hardware, or system integration hardware 25398c2ecf20Sopenharmony_ci * associated with the IP block. Returns 0. 25408c2ecf20Sopenharmony_ci */ 25418c2ecf20Sopenharmony_cistatic int _setup(struct omap_hwmod *oh, void *data) 25428c2ecf20Sopenharmony_ci{ 25438c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_INITIALIZED) 25448c2ecf20Sopenharmony_ci return 0; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci if (oh->parent_hwmod) { 25478c2ecf20Sopenharmony_ci int r; 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci r = _enable(oh->parent_hwmod); 25508c2ecf20Sopenharmony_ci WARN(r, "hwmod: %s: setup: failed to enable parent hwmod %s\n", 25518c2ecf20Sopenharmony_ci oh->name, oh->parent_hwmod->name); 25528c2ecf20Sopenharmony_ci } 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci _setup_iclk_autoidle(oh); 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci if (!_setup_reset(oh)) 25578c2ecf20Sopenharmony_ci _setup_postsetup(oh); 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci if (oh->parent_hwmod) { 25608c2ecf20Sopenharmony_ci u8 postsetup_state; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci postsetup_state = oh->parent_hwmod->_postsetup_state; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci if (postsetup_state == _HWMOD_STATE_IDLE) 25658c2ecf20Sopenharmony_ci _idle(oh->parent_hwmod); 25668c2ecf20Sopenharmony_ci else if (postsetup_state == _HWMOD_STATE_DISABLED) 25678c2ecf20Sopenharmony_ci _shutdown(oh->parent_hwmod); 25688c2ecf20Sopenharmony_ci else if (postsetup_state != _HWMOD_STATE_ENABLED) 25698c2ecf20Sopenharmony_ci WARN(1, "hwmod: %s: unknown postsetup state %d! defaulting to enabled\n", 25708c2ecf20Sopenharmony_ci oh->parent_hwmod->name, postsetup_state); 25718c2ecf20Sopenharmony_ci } 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci return 0; 25748c2ecf20Sopenharmony_ci} 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci/** 25778c2ecf20Sopenharmony_ci * _register - register a struct omap_hwmod 25788c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 25798c2ecf20Sopenharmony_ci * 25808c2ecf20Sopenharmony_ci * Registers the omap_hwmod @oh. Returns -EEXIST if an omap_hwmod 25818c2ecf20Sopenharmony_ci * already has been registered by the same name; -EINVAL if the 25828c2ecf20Sopenharmony_ci * omap_hwmod is in the wrong state, if @oh is NULL, if the 25838c2ecf20Sopenharmony_ci * omap_hwmod's class field is NULL; if the omap_hwmod is missing a 25848c2ecf20Sopenharmony_ci * name, or if the omap_hwmod's class is missing a name; or 0 upon 25858c2ecf20Sopenharmony_ci * success. 25868c2ecf20Sopenharmony_ci * 25878c2ecf20Sopenharmony_ci * XXX The data should be copied into bootmem, so the original data 25888c2ecf20Sopenharmony_ci * should be marked __initdata and freed after init. This would allow 25898c2ecf20Sopenharmony_ci * unneeded omap_hwmods to be freed on multi-OMAP configurations. Note 25908c2ecf20Sopenharmony_ci * that the copy process would be relatively complex due to the large number 25918c2ecf20Sopenharmony_ci * of substructures. 25928c2ecf20Sopenharmony_ci */ 25938c2ecf20Sopenharmony_cistatic int _register(struct omap_hwmod *oh) 25948c2ecf20Sopenharmony_ci{ 25958c2ecf20Sopenharmony_ci if (!oh || !oh->name || !oh->class || !oh->class->name || 25968c2ecf20Sopenharmony_ci (oh->_state != _HWMOD_STATE_UNKNOWN)) 25978c2ecf20Sopenharmony_ci return -EINVAL; 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: registering\n", oh->name); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci if (_lookup(oh->name)) 26028c2ecf20Sopenharmony_ci return -EEXIST; 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci list_add_tail(&oh->node, &omap_hwmod_list); 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&oh->slave_ports); 26078c2ecf20Sopenharmony_ci spin_lock_init(&oh->_lock); 26088c2ecf20Sopenharmony_ci lockdep_set_class(&oh->_lock, &oh->hwmod_key); 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_REGISTERED; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci /* 26138c2ecf20Sopenharmony_ci * XXX Rather than doing a strcmp(), this should test a flag 26148c2ecf20Sopenharmony_ci * set in the hwmod data, inserted by the autogenerator code. 26158c2ecf20Sopenharmony_ci */ 26168c2ecf20Sopenharmony_ci if (!strcmp(oh->name, MPU_INITIATOR_NAME)) 26178c2ecf20Sopenharmony_ci mpu_oh = oh; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci return 0; 26208c2ecf20Sopenharmony_ci} 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci/** 26238c2ecf20Sopenharmony_ci * _add_link - add an interconnect between two IP blocks 26248c2ecf20Sopenharmony_ci * @oi: pointer to a struct omap_hwmod_ocp_if record 26258c2ecf20Sopenharmony_ci * 26268c2ecf20Sopenharmony_ci * Add struct omap_hwmod_link records connecting the slave IP block 26278c2ecf20Sopenharmony_ci * specified in @oi->slave to @oi. This code is assumed to run before 26288c2ecf20Sopenharmony_ci * preemption or SMP has been enabled, thus avoiding the need for 26298c2ecf20Sopenharmony_ci * locking in this code. Changes to this assumption will require 26308c2ecf20Sopenharmony_ci * additional locking. Returns 0. 26318c2ecf20Sopenharmony_ci */ 26328c2ecf20Sopenharmony_cistatic int _add_link(struct omap_hwmod_ocp_if *oi) 26338c2ecf20Sopenharmony_ci{ 26348c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s -> %s: adding link\n", oi->master->name, 26358c2ecf20Sopenharmony_ci oi->slave->name); 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci list_add(&oi->node, &oi->slave->slave_ports); 26388c2ecf20Sopenharmony_ci oi->slave->slaves_cnt++; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci return 0; 26418c2ecf20Sopenharmony_ci} 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci/** 26448c2ecf20Sopenharmony_ci * _register_link - register a struct omap_hwmod_ocp_if 26458c2ecf20Sopenharmony_ci * @oi: struct omap_hwmod_ocp_if * 26468c2ecf20Sopenharmony_ci * 26478c2ecf20Sopenharmony_ci * Registers the omap_hwmod_ocp_if record @oi. Returns -EEXIST if it 26488c2ecf20Sopenharmony_ci * has already been registered; -EINVAL if @oi is NULL or if the 26498c2ecf20Sopenharmony_ci * record pointed to by @oi is missing required fields; or 0 upon 26508c2ecf20Sopenharmony_ci * success. 26518c2ecf20Sopenharmony_ci * 26528c2ecf20Sopenharmony_ci * XXX The data should be copied into bootmem, so the original data 26538c2ecf20Sopenharmony_ci * should be marked __initdata and freed after init. This would allow 26548c2ecf20Sopenharmony_ci * unneeded omap_hwmods to be freed on multi-OMAP configurations. 26558c2ecf20Sopenharmony_ci */ 26568c2ecf20Sopenharmony_cistatic int __init _register_link(struct omap_hwmod_ocp_if *oi) 26578c2ecf20Sopenharmony_ci{ 26588c2ecf20Sopenharmony_ci if (!oi || !oi->master || !oi->slave || !oi->user) 26598c2ecf20Sopenharmony_ci return -EINVAL; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci if (oi->_int_flags & _OCPIF_INT_FLAGS_REGISTERED) 26628c2ecf20Sopenharmony_ci return -EEXIST; 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: registering link from %s to %s\n", 26658c2ecf20Sopenharmony_ci oi->master->name, oi->slave->name); 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci /* 26688c2ecf20Sopenharmony_ci * Register the connected hwmods, if they haven't been 26698c2ecf20Sopenharmony_ci * registered already 26708c2ecf20Sopenharmony_ci */ 26718c2ecf20Sopenharmony_ci if (oi->master->_state != _HWMOD_STATE_REGISTERED) 26728c2ecf20Sopenharmony_ci _register(oi->master); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci if (oi->slave->_state != _HWMOD_STATE_REGISTERED) 26758c2ecf20Sopenharmony_ci _register(oi->slave); 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci _add_link(oi); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci oi->_int_flags |= _OCPIF_INT_FLAGS_REGISTERED; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci return 0; 26828c2ecf20Sopenharmony_ci} 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci/* Static functions intended only for use in soc_ops field function pointers */ 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci/** 26878c2ecf20Sopenharmony_ci * _omap2xxx_3xxx_wait_target_ready - wait for a module to leave slave idle 26888c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 26898c2ecf20Sopenharmony_ci * 26908c2ecf20Sopenharmony_ci * Wait for a module @oh to leave slave idle. Returns 0 if the module 26918c2ecf20Sopenharmony_ci * does not have an IDLEST bit or if the module successfully leaves 26928c2ecf20Sopenharmony_ci * slave idle; otherwise, pass along the return value of the 26938c2ecf20Sopenharmony_ci * appropriate *_cm*_wait_module_ready() function. 26948c2ecf20Sopenharmony_ci */ 26958c2ecf20Sopenharmony_cistatic int _omap2xxx_3xxx_wait_target_ready(struct omap_hwmod *oh) 26968c2ecf20Sopenharmony_ci{ 26978c2ecf20Sopenharmony_ci if (!oh) 26988c2ecf20Sopenharmony_ci return -EINVAL; 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_NO_IDLEST) 27018c2ecf20Sopenharmony_ci return 0; 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci if (!_find_mpu_rt_port(oh)) 27048c2ecf20Sopenharmony_ci return 0; 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci /* XXX check module SIDLEMODE, hardreset status, enabled clocks */ 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci return omap_cm_wait_module_ready(0, oh->prcm.omap2.module_offs, 27098c2ecf20Sopenharmony_ci oh->prcm.omap2.idlest_reg_id, 27108c2ecf20Sopenharmony_ci oh->prcm.omap2.idlest_idle_bit); 27118c2ecf20Sopenharmony_ci} 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci/** 27148c2ecf20Sopenharmony_ci * _omap4_wait_target_ready - wait for a module to leave slave idle 27158c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 27168c2ecf20Sopenharmony_ci * 27178c2ecf20Sopenharmony_ci * Wait for a module @oh to leave slave idle. Returns 0 if the module 27188c2ecf20Sopenharmony_ci * does not have an IDLEST bit or if the module successfully leaves 27198c2ecf20Sopenharmony_ci * slave idle; otherwise, pass along the return value of the 27208c2ecf20Sopenharmony_ci * appropriate *_cm*_wait_module_ready() function. 27218c2ecf20Sopenharmony_ci */ 27228c2ecf20Sopenharmony_cistatic int _omap4_wait_target_ready(struct omap_hwmod *oh) 27238c2ecf20Sopenharmony_ci{ 27248c2ecf20Sopenharmony_ci if (!oh) 27258c2ecf20Sopenharmony_ci return -EINVAL; 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_NO_IDLEST || !oh->clkdm) 27288c2ecf20Sopenharmony_ci return 0; 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci if (!_find_mpu_rt_port(oh)) 27318c2ecf20Sopenharmony_ci return 0; 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci if (_omap4_clkctrl_managed_by_clkfwk(oh)) 27348c2ecf20Sopenharmony_ci return 0; 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci if (!_omap4_has_clkctrl_clock(oh)) 27378c2ecf20Sopenharmony_ci return 0; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci /* XXX check module SIDLEMODE, hardreset status */ 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci return omap_cm_wait_module_ready(oh->clkdm->prcm_partition, 27428c2ecf20Sopenharmony_ci oh->clkdm->cm_inst, 27438c2ecf20Sopenharmony_ci oh->prcm.omap4.clkctrl_offs, 0); 27448c2ecf20Sopenharmony_ci} 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci/** 27478c2ecf20Sopenharmony_ci * _omap2_assert_hardreset - call OMAP2 PRM hardreset fn with hwmod args 27488c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to assert hardreset 27498c2ecf20Sopenharmony_ci * @ohri: hardreset line data 27508c2ecf20Sopenharmony_ci * 27518c2ecf20Sopenharmony_ci * Call omap2_prm_assert_hardreset() with parameters extracted from 27528c2ecf20Sopenharmony_ci * the hwmod @oh and the hardreset line data @ohri. Only intended for 27538c2ecf20Sopenharmony_ci * use as an soc_ops function pointer. Passes along the return value 27548c2ecf20Sopenharmony_ci * from omap2_prm_assert_hardreset(). XXX This function is scheduled 27558c2ecf20Sopenharmony_ci * for removal when the PRM code is moved into drivers/. 27568c2ecf20Sopenharmony_ci */ 27578c2ecf20Sopenharmony_cistatic int _omap2_assert_hardreset(struct omap_hwmod *oh, 27588c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 27598c2ecf20Sopenharmony_ci{ 27608c2ecf20Sopenharmony_ci return omap_prm_assert_hardreset(ohri->rst_shift, 0, 27618c2ecf20Sopenharmony_ci oh->prcm.omap2.module_offs, 0); 27628c2ecf20Sopenharmony_ci} 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci/** 27658c2ecf20Sopenharmony_ci * _omap2_deassert_hardreset - call OMAP2 PRM hardreset fn with hwmod args 27668c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to deassert hardreset 27678c2ecf20Sopenharmony_ci * @ohri: hardreset line data 27688c2ecf20Sopenharmony_ci * 27698c2ecf20Sopenharmony_ci * Call omap2_prm_deassert_hardreset() with parameters extracted from 27708c2ecf20Sopenharmony_ci * the hwmod @oh and the hardreset line data @ohri. Only intended for 27718c2ecf20Sopenharmony_ci * use as an soc_ops function pointer. Passes along the return value 27728c2ecf20Sopenharmony_ci * from omap2_prm_deassert_hardreset(). XXX This function is 27738c2ecf20Sopenharmony_ci * scheduled for removal when the PRM code is moved into drivers/. 27748c2ecf20Sopenharmony_ci */ 27758c2ecf20Sopenharmony_cistatic int _omap2_deassert_hardreset(struct omap_hwmod *oh, 27768c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 27778c2ecf20Sopenharmony_ci{ 27788c2ecf20Sopenharmony_ci return omap_prm_deassert_hardreset(ohri->rst_shift, ohri->st_shift, 0, 27798c2ecf20Sopenharmony_ci oh->prcm.omap2.module_offs, 0, 0); 27808c2ecf20Sopenharmony_ci} 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci/** 27838c2ecf20Sopenharmony_ci * _omap2_is_hardreset_asserted - call OMAP2 PRM hardreset fn with hwmod args 27848c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to test hardreset 27858c2ecf20Sopenharmony_ci * @ohri: hardreset line data 27868c2ecf20Sopenharmony_ci * 27878c2ecf20Sopenharmony_ci * Call omap2_prm_is_hardreset_asserted() with parameters extracted 27888c2ecf20Sopenharmony_ci * from the hwmod @oh and the hardreset line data @ohri. Only 27898c2ecf20Sopenharmony_ci * intended for use as an soc_ops function pointer. Passes along the 27908c2ecf20Sopenharmony_ci * return value from omap2_prm_is_hardreset_asserted(). XXX This 27918c2ecf20Sopenharmony_ci * function is scheduled for removal when the PRM code is moved into 27928c2ecf20Sopenharmony_ci * drivers/. 27938c2ecf20Sopenharmony_ci */ 27948c2ecf20Sopenharmony_cistatic int _omap2_is_hardreset_asserted(struct omap_hwmod *oh, 27958c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 27968c2ecf20Sopenharmony_ci{ 27978c2ecf20Sopenharmony_ci return omap_prm_is_hardreset_asserted(ohri->st_shift, 0, 27988c2ecf20Sopenharmony_ci oh->prcm.omap2.module_offs, 0); 27998c2ecf20Sopenharmony_ci} 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci/** 28028c2ecf20Sopenharmony_ci * _omap4_assert_hardreset - call OMAP4 PRM hardreset fn with hwmod args 28038c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to assert hardreset 28048c2ecf20Sopenharmony_ci * @ohri: hardreset line data 28058c2ecf20Sopenharmony_ci * 28068c2ecf20Sopenharmony_ci * Call omap4_prminst_assert_hardreset() with parameters extracted 28078c2ecf20Sopenharmony_ci * from the hwmod @oh and the hardreset line data @ohri. Only 28088c2ecf20Sopenharmony_ci * intended for use as an soc_ops function pointer. Passes along the 28098c2ecf20Sopenharmony_ci * return value from omap4_prminst_assert_hardreset(). XXX This 28108c2ecf20Sopenharmony_ci * function is scheduled for removal when the PRM code is moved into 28118c2ecf20Sopenharmony_ci * drivers/. 28128c2ecf20Sopenharmony_ci */ 28138c2ecf20Sopenharmony_cistatic int _omap4_assert_hardreset(struct omap_hwmod *oh, 28148c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 28158c2ecf20Sopenharmony_ci{ 28168c2ecf20Sopenharmony_ci if (!oh->clkdm) 28178c2ecf20Sopenharmony_ci return -EINVAL; 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci return omap_prm_assert_hardreset(ohri->rst_shift, 28208c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_partition, 28218c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_offs, 28228c2ecf20Sopenharmony_ci oh->prcm.omap4.rstctrl_offs); 28238c2ecf20Sopenharmony_ci} 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci/** 28268c2ecf20Sopenharmony_ci * _omap4_deassert_hardreset - call OMAP4 PRM hardreset fn with hwmod args 28278c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to deassert hardreset 28288c2ecf20Sopenharmony_ci * @ohri: hardreset line data 28298c2ecf20Sopenharmony_ci * 28308c2ecf20Sopenharmony_ci * Call omap4_prminst_deassert_hardreset() with parameters extracted 28318c2ecf20Sopenharmony_ci * from the hwmod @oh and the hardreset line data @ohri. Only 28328c2ecf20Sopenharmony_ci * intended for use as an soc_ops function pointer. Passes along the 28338c2ecf20Sopenharmony_ci * return value from omap4_prminst_deassert_hardreset(). XXX This 28348c2ecf20Sopenharmony_ci * function is scheduled for removal when the PRM code is moved into 28358c2ecf20Sopenharmony_ci * drivers/. 28368c2ecf20Sopenharmony_ci */ 28378c2ecf20Sopenharmony_cistatic int _omap4_deassert_hardreset(struct omap_hwmod *oh, 28388c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 28398c2ecf20Sopenharmony_ci{ 28408c2ecf20Sopenharmony_ci if (!oh->clkdm) 28418c2ecf20Sopenharmony_ci return -EINVAL; 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci if (ohri->st_shift) 28448c2ecf20Sopenharmony_ci pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", 28458c2ecf20Sopenharmony_ci oh->name, ohri->name); 28468c2ecf20Sopenharmony_ci return omap_prm_deassert_hardreset(ohri->rst_shift, ohri->rst_shift, 28478c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_partition, 28488c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_offs, 28498c2ecf20Sopenharmony_ci oh->prcm.omap4.rstctrl_offs, 28508c2ecf20Sopenharmony_ci oh->prcm.omap4.rstctrl_offs + 28518c2ecf20Sopenharmony_ci OMAP4_RST_CTRL_ST_OFFSET); 28528c2ecf20Sopenharmony_ci} 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci/** 28558c2ecf20Sopenharmony_ci * _omap4_is_hardreset_asserted - call OMAP4 PRM hardreset fn with hwmod args 28568c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to test hardreset 28578c2ecf20Sopenharmony_ci * @ohri: hardreset line data 28588c2ecf20Sopenharmony_ci * 28598c2ecf20Sopenharmony_ci * Call omap4_prminst_is_hardreset_asserted() with parameters 28608c2ecf20Sopenharmony_ci * extracted from the hwmod @oh and the hardreset line data @ohri. 28618c2ecf20Sopenharmony_ci * Only intended for use as an soc_ops function pointer. Passes along 28628c2ecf20Sopenharmony_ci * the return value from omap4_prminst_is_hardreset_asserted(). XXX 28638c2ecf20Sopenharmony_ci * This function is scheduled for removal when the PRM code is moved 28648c2ecf20Sopenharmony_ci * into drivers/. 28658c2ecf20Sopenharmony_ci */ 28668c2ecf20Sopenharmony_cistatic int _omap4_is_hardreset_asserted(struct omap_hwmod *oh, 28678c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 28688c2ecf20Sopenharmony_ci{ 28698c2ecf20Sopenharmony_ci if (!oh->clkdm) 28708c2ecf20Sopenharmony_ci return -EINVAL; 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci return omap_prm_is_hardreset_asserted(ohri->rst_shift, 28738c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr-> 28748c2ecf20Sopenharmony_ci prcm_partition, 28758c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_offs, 28768c2ecf20Sopenharmony_ci oh->prcm.omap4.rstctrl_offs); 28778c2ecf20Sopenharmony_ci} 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci/** 28808c2ecf20Sopenharmony_ci * _omap4_disable_direct_prcm - disable direct PRCM control for hwmod 28818c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to disable control for 28828c2ecf20Sopenharmony_ci * 28838c2ecf20Sopenharmony_ci * Disables direct PRCM clkctrl done by hwmod core. Instead, the hwmod 28848c2ecf20Sopenharmony_ci * will be using its main_clk to enable/disable the module. Returns 28858c2ecf20Sopenharmony_ci * 0 if successful. 28868c2ecf20Sopenharmony_ci */ 28878c2ecf20Sopenharmony_cistatic int _omap4_disable_direct_prcm(struct omap_hwmod *oh) 28888c2ecf20Sopenharmony_ci{ 28898c2ecf20Sopenharmony_ci if (!oh) 28908c2ecf20Sopenharmony_ci return -EINVAL; 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci oh->prcm.omap4.flags |= HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK; 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci return 0; 28958c2ecf20Sopenharmony_ci} 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci/** 28988c2ecf20Sopenharmony_ci * _am33xx_deassert_hardreset - call AM33XX PRM hardreset fn with hwmod args 28998c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * to deassert hardreset 29008c2ecf20Sopenharmony_ci * @ohri: hardreset line data 29018c2ecf20Sopenharmony_ci * 29028c2ecf20Sopenharmony_ci * Call am33xx_prminst_deassert_hardreset() with parameters extracted 29038c2ecf20Sopenharmony_ci * from the hwmod @oh and the hardreset line data @ohri. Only 29048c2ecf20Sopenharmony_ci * intended for use as an soc_ops function pointer. Passes along the 29058c2ecf20Sopenharmony_ci * return value from am33xx_prminst_deassert_hardreset(). XXX This 29068c2ecf20Sopenharmony_ci * function is scheduled for removal when the PRM code is moved into 29078c2ecf20Sopenharmony_ci * drivers/. 29088c2ecf20Sopenharmony_ci */ 29098c2ecf20Sopenharmony_cistatic int _am33xx_deassert_hardreset(struct omap_hwmod *oh, 29108c2ecf20Sopenharmony_ci struct omap_hwmod_rst_info *ohri) 29118c2ecf20Sopenharmony_ci{ 29128c2ecf20Sopenharmony_ci return omap_prm_deassert_hardreset(ohri->rst_shift, ohri->st_shift, 29138c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_partition, 29148c2ecf20Sopenharmony_ci oh->clkdm->pwrdm.ptr->prcm_offs, 29158c2ecf20Sopenharmony_ci oh->prcm.omap4.rstctrl_offs, 29168c2ecf20Sopenharmony_ci oh->prcm.omap4.rstst_offs); 29178c2ecf20Sopenharmony_ci} 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_ci/* Public functions */ 29208c2ecf20Sopenharmony_ci 29218c2ecf20Sopenharmony_ciu32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs) 29228c2ecf20Sopenharmony_ci{ 29238c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_16BIT_REG) 29248c2ecf20Sopenharmony_ci return readw_relaxed(oh->_mpu_rt_va + reg_offs); 29258c2ecf20Sopenharmony_ci else 29268c2ecf20Sopenharmony_ci return readl_relaxed(oh->_mpu_rt_va + reg_offs); 29278c2ecf20Sopenharmony_ci} 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_civoid omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs) 29308c2ecf20Sopenharmony_ci{ 29318c2ecf20Sopenharmony_ci if (oh->flags & HWMOD_16BIT_REG) 29328c2ecf20Sopenharmony_ci writew_relaxed(v, oh->_mpu_rt_va + reg_offs); 29338c2ecf20Sopenharmony_ci else 29348c2ecf20Sopenharmony_ci writel_relaxed(v, oh->_mpu_rt_va + reg_offs); 29358c2ecf20Sopenharmony_ci} 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci/** 29388c2ecf20Sopenharmony_ci * omap_hwmod_softreset - reset a module via SYSCONFIG.SOFTRESET bit 29398c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 29408c2ecf20Sopenharmony_ci * 29418c2ecf20Sopenharmony_ci * This is a public function exposed to drivers. Some drivers may need to do 29428c2ecf20Sopenharmony_ci * some settings before and after resetting the device. Those drivers after 29438c2ecf20Sopenharmony_ci * doing the necessary settings could use this function to start a reset by 29448c2ecf20Sopenharmony_ci * setting the SYSCONFIG.SOFTRESET bit. 29458c2ecf20Sopenharmony_ci */ 29468c2ecf20Sopenharmony_ciint omap_hwmod_softreset(struct omap_hwmod *oh) 29478c2ecf20Sopenharmony_ci{ 29488c2ecf20Sopenharmony_ci u32 v; 29498c2ecf20Sopenharmony_ci int ret; 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci if (!oh || !(oh->_sysc_cache)) 29528c2ecf20Sopenharmony_ci return -EINVAL; 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci v = oh->_sysc_cache; 29558c2ecf20Sopenharmony_ci ret = _set_softreset(oh, &v); 29568c2ecf20Sopenharmony_ci if (ret) 29578c2ecf20Sopenharmony_ci goto error; 29588c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci ret = _clear_softreset(oh, &v); 29618c2ecf20Sopenharmony_ci if (ret) 29628c2ecf20Sopenharmony_ci goto error; 29638c2ecf20Sopenharmony_ci _write_sysconfig(v, oh); 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_cierror: 29668c2ecf20Sopenharmony_ci return ret; 29678c2ecf20Sopenharmony_ci} 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci/** 29708c2ecf20Sopenharmony_ci * omap_hwmod_lookup - look up a registered omap_hwmod by name 29718c2ecf20Sopenharmony_ci * @name: name of the omap_hwmod to look up 29728c2ecf20Sopenharmony_ci * 29738c2ecf20Sopenharmony_ci * Given a @name of an omap_hwmod, return a pointer to the registered 29748c2ecf20Sopenharmony_ci * struct omap_hwmod *, or NULL upon error. 29758c2ecf20Sopenharmony_ci */ 29768c2ecf20Sopenharmony_cistruct omap_hwmod *omap_hwmod_lookup(const char *name) 29778c2ecf20Sopenharmony_ci{ 29788c2ecf20Sopenharmony_ci struct omap_hwmod *oh; 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_ci if (!name) 29818c2ecf20Sopenharmony_ci return NULL; 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci oh = _lookup(name); 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ci return oh; 29868c2ecf20Sopenharmony_ci} 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci/** 29898c2ecf20Sopenharmony_ci * omap_hwmod_for_each - call function for each registered omap_hwmod 29908c2ecf20Sopenharmony_ci * @fn: pointer to a callback function 29918c2ecf20Sopenharmony_ci * @data: void * data to pass to callback function 29928c2ecf20Sopenharmony_ci * 29938c2ecf20Sopenharmony_ci * Call @fn for each registered omap_hwmod, passing @data to each 29948c2ecf20Sopenharmony_ci * function. @fn must return 0 for success or any other value for 29958c2ecf20Sopenharmony_ci * failure. If @fn returns non-zero, the iteration across omap_hwmods 29968c2ecf20Sopenharmony_ci * will stop and the non-zero return value will be passed to the 29978c2ecf20Sopenharmony_ci * caller of omap_hwmod_for_each(). @fn is called with 29988c2ecf20Sopenharmony_ci * omap_hwmod_for_each() held. 29998c2ecf20Sopenharmony_ci */ 30008c2ecf20Sopenharmony_ciint omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data), 30018c2ecf20Sopenharmony_ci void *data) 30028c2ecf20Sopenharmony_ci{ 30038c2ecf20Sopenharmony_ci struct omap_hwmod *temp_oh; 30048c2ecf20Sopenharmony_ci int ret = 0; 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci if (!fn) 30078c2ecf20Sopenharmony_ci return -EINVAL; 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci list_for_each_entry(temp_oh, &omap_hwmod_list, node) { 30108c2ecf20Sopenharmony_ci ret = (*fn)(temp_oh, data); 30118c2ecf20Sopenharmony_ci if (ret) 30128c2ecf20Sopenharmony_ci break; 30138c2ecf20Sopenharmony_ci } 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci return ret; 30168c2ecf20Sopenharmony_ci} 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci/** 30198c2ecf20Sopenharmony_ci * omap_hwmod_register_links - register an array of hwmod links 30208c2ecf20Sopenharmony_ci * @ois: pointer to an array of omap_hwmod_ocp_if to register 30218c2ecf20Sopenharmony_ci * 30228c2ecf20Sopenharmony_ci * Intended to be called early in boot before the clock framework is 30238c2ecf20Sopenharmony_ci * initialized. If @ois is not null, will register all omap_hwmods 30248c2ecf20Sopenharmony_ci * listed in @ois that are valid for this chip. Returns -EINVAL if 30258c2ecf20Sopenharmony_ci * omap_hwmod_init() hasn't been called before calling this function, 30268c2ecf20Sopenharmony_ci * -ENOMEM if the link memory area can't be allocated, or 0 upon 30278c2ecf20Sopenharmony_ci * success. 30288c2ecf20Sopenharmony_ci */ 30298c2ecf20Sopenharmony_ciint __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois) 30308c2ecf20Sopenharmony_ci{ 30318c2ecf20Sopenharmony_ci int r, i; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci if (!inited) 30348c2ecf20Sopenharmony_ci return -EINVAL; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci if (!ois) 30378c2ecf20Sopenharmony_ci return 0; 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci if (ois[0] == NULL) /* Empty list */ 30408c2ecf20Sopenharmony_ci return 0; 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci i = 0; 30438c2ecf20Sopenharmony_ci do { 30448c2ecf20Sopenharmony_ci r = _register_link(ois[i]); 30458c2ecf20Sopenharmony_ci WARN(r && r != -EEXIST, 30468c2ecf20Sopenharmony_ci "omap_hwmod: _register_link(%s -> %s) returned %d\n", 30478c2ecf20Sopenharmony_ci ois[i]->master->name, ois[i]->slave->name, r); 30488c2ecf20Sopenharmony_ci } while (ois[++i]); 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci return 0; 30518c2ecf20Sopenharmony_ci} 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci/** 30548c2ecf20Sopenharmony_ci * _ensure_mpu_hwmod_is_setup - ensure the MPU SS hwmod is init'ed and set up 30558c2ecf20Sopenharmony_ci * @oh: pointer to the hwmod currently being set up (usually not the MPU) 30568c2ecf20Sopenharmony_ci * 30578c2ecf20Sopenharmony_ci * If the hwmod data corresponding to the MPU subsystem IP block 30588c2ecf20Sopenharmony_ci * hasn't been initialized and set up yet, do so now. This must be 30598c2ecf20Sopenharmony_ci * done first since sleep dependencies may be added from other hwmods 30608c2ecf20Sopenharmony_ci * to the MPU. Intended to be called only by omap_hwmod_setup*(). No 30618c2ecf20Sopenharmony_ci * return value. 30628c2ecf20Sopenharmony_ci */ 30638c2ecf20Sopenharmony_cistatic void __init _ensure_mpu_hwmod_is_setup(struct omap_hwmod *oh) 30648c2ecf20Sopenharmony_ci{ 30658c2ecf20Sopenharmony_ci if (!mpu_oh || mpu_oh->_state == _HWMOD_STATE_UNKNOWN) 30668c2ecf20Sopenharmony_ci pr_err("omap_hwmod: %s: MPU initiator hwmod %s not yet registered\n", 30678c2ecf20Sopenharmony_ci __func__, MPU_INITIATOR_NAME); 30688c2ecf20Sopenharmony_ci else if (mpu_oh->_state == _HWMOD_STATE_REGISTERED && oh != mpu_oh) 30698c2ecf20Sopenharmony_ci omap_hwmod_setup_one(MPU_INITIATOR_NAME); 30708c2ecf20Sopenharmony_ci} 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci/** 30738c2ecf20Sopenharmony_ci * omap_hwmod_setup_one - set up a single hwmod 30748c2ecf20Sopenharmony_ci * @oh_name: const char * name of the already-registered hwmod to set up 30758c2ecf20Sopenharmony_ci * 30768c2ecf20Sopenharmony_ci * Initialize and set up a single hwmod. Intended to be used for a 30778c2ecf20Sopenharmony_ci * small number of early devices, such as the timer IP blocks used for 30788c2ecf20Sopenharmony_ci * the scheduler clock. Must be called after omap2_clk_init(). 30798c2ecf20Sopenharmony_ci * Resolves the struct clk names to struct clk pointers for each 30808c2ecf20Sopenharmony_ci * registered omap_hwmod. Also calls _setup() on each hwmod. Returns 30818c2ecf20Sopenharmony_ci * -EINVAL upon error or 0 upon success. 30828c2ecf20Sopenharmony_ci */ 30838c2ecf20Sopenharmony_ciint __init omap_hwmod_setup_one(const char *oh_name) 30848c2ecf20Sopenharmony_ci{ 30858c2ecf20Sopenharmony_ci struct omap_hwmod *oh; 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: %s\n", oh_name, __func__); 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci oh = _lookup(oh_name); 30908c2ecf20Sopenharmony_ci if (!oh) { 30918c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: %s: hwmod not yet registered\n", oh_name); 30928c2ecf20Sopenharmony_ci return -EINVAL; 30938c2ecf20Sopenharmony_ci } 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci _ensure_mpu_hwmod_is_setup(oh); 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci _init(oh, NULL); 30988c2ecf20Sopenharmony_ci _setup(oh, NULL); 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci return 0; 31018c2ecf20Sopenharmony_ci} 31028c2ecf20Sopenharmony_ci 31038c2ecf20Sopenharmony_cistatic void omap_hwmod_check_one(struct device *dev, 31048c2ecf20Sopenharmony_ci const char *name, s8 v1, u8 v2) 31058c2ecf20Sopenharmony_ci{ 31068c2ecf20Sopenharmony_ci if (v1 < 0) 31078c2ecf20Sopenharmony_ci return; 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci if (v1 != v2) 31108c2ecf20Sopenharmony_ci dev_warn(dev, "%s %d != %d\n", name, v1, v2); 31118c2ecf20Sopenharmony_ci} 31128c2ecf20Sopenharmony_ci 31138c2ecf20Sopenharmony_ci/** 31148c2ecf20Sopenharmony_ci * omap_hwmod_check_sysc - check sysc against platform sysc 31158c2ecf20Sopenharmony_ci * @dev: struct device 31168c2ecf20Sopenharmony_ci * @data: module data 31178c2ecf20Sopenharmony_ci * @sysc_fields: new sysc configuration 31188c2ecf20Sopenharmony_ci */ 31198c2ecf20Sopenharmony_cistatic int omap_hwmod_check_sysc(struct device *dev, 31208c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 31218c2ecf20Sopenharmony_ci struct sysc_regbits *sysc_fields) 31228c2ecf20Sopenharmony_ci{ 31238c2ecf20Sopenharmony_ci const struct sysc_regbits *regbits = data->cap->regbits; 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ci omap_hwmod_check_one(dev, "dmadisable_shift", 31268c2ecf20Sopenharmony_ci regbits->dmadisable_shift, 31278c2ecf20Sopenharmony_ci sysc_fields->dmadisable_shift); 31288c2ecf20Sopenharmony_ci omap_hwmod_check_one(dev, "midle_shift", 31298c2ecf20Sopenharmony_ci regbits->midle_shift, 31308c2ecf20Sopenharmony_ci sysc_fields->midle_shift); 31318c2ecf20Sopenharmony_ci omap_hwmod_check_one(dev, "sidle_shift", 31328c2ecf20Sopenharmony_ci regbits->sidle_shift, 31338c2ecf20Sopenharmony_ci sysc_fields->sidle_shift); 31348c2ecf20Sopenharmony_ci omap_hwmod_check_one(dev, "clkact_shift", 31358c2ecf20Sopenharmony_ci regbits->clkact_shift, 31368c2ecf20Sopenharmony_ci sysc_fields->clkact_shift); 31378c2ecf20Sopenharmony_ci omap_hwmod_check_one(dev, "enwkup_shift", 31388c2ecf20Sopenharmony_ci regbits->enwkup_shift, 31398c2ecf20Sopenharmony_ci sysc_fields->enwkup_shift); 31408c2ecf20Sopenharmony_ci omap_hwmod_check_one(dev, "srst_shift", 31418c2ecf20Sopenharmony_ci regbits->srst_shift, 31428c2ecf20Sopenharmony_ci sysc_fields->srst_shift); 31438c2ecf20Sopenharmony_ci omap_hwmod_check_one(dev, "autoidle_shift", 31448c2ecf20Sopenharmony_ci regbits->autoidle_shift, 31458c2ecf20Sopenharmony_ci sysc_fields->autoidle_shift); 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci return 0; 31488c2ecf20Sopenharmony_ci} 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci/** 31518c2ecf20Sopenharmony_ci * omap_hwmod_init_regbits - init sysconfig specific register bits 31528c2ecf20Sopenharmony_ci * @dev: struct device 31538c2ecf20Sopenharmony_ci * @oh: module 31548c2ecf20Sopenharmony_ci * @data: module data 31558c2ecf20Sopenharmony_ci * @sysc_fields: new sysc configuration 31568c2ecf20Sopenharmony_ci */ 31578c2ecf20Sopenharmony_cistatic int omap_hwmod_init_regbits(struct device *dev, struct omap_hwmod *oh, 31588c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 31598c2ecf20Sopenharmony_ci struct sysc_regbits **sysc_fields) 31608c2ecf20Sopenharmony_ci{ 31618c2ecf20Sopenharmony_ci switch (data->cap->type) { 31628c2ecf20Sopenharmony_ci case TI_SYSC_OMAP2: 31638c2ecf20Sopenharmony_ci case TI_SYSC_OMAP2_TIMER: 31648c2ecf20Sopenharmony_ci *sysc_fields = &omap_hwmod_sysc_type1; 31658c2ecf20Sopenharmony_ci break; 31668c2ecf20Sopenharmony_ci case TI_SYSC_OMAP3_SHAM: 31678c2ecf20Sopenharmony_ci *sysc_fields = &omap3_sham_sysc_fields; 31688c2ecf20Sopenharmony_ci break; 31698c2ecf20Sopenharmony_ci case TI_SYSC_OMAP3_AES: 31708c2ecf20Sopenharmony_ci *sysc_fields = &omap3xxx_aes_sysc_fields; 31718c2ecf20Sopenharmony_ci break; 31728c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4: 31738c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4_TIMER: 31748c2ecf20Sopenharmony_ci *sysc_fields = &omap_hwmod_sysc_type2; 31758c2ecf20Sopenharmony_ci break; 31768c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4_SIMPLE: 31778c2ecf20Sopenharmony_ci *sysc_fields = &omap_hwmod_sysc_type3; 31788c2ecf20Sopenharmony_ci break; 31798c2ecf20Sopenharmony_ci case TI_SYSC_OMAP34XX_SR: 31808c2ecf20Sopenharmony_ci *sysc_fields = &omap34xx_sr_sysc_fields; 31818c2ecf20Sopenharmony_ci break; 31828c2ecf20Sopenharmony_ci case TI_SYSC_OMAP36XX_SR: 31838c2ecf20Sopenharmony_ci *sysc_fields = &omap36xx_sr_sysc_fields; 31848c2ecf20Sopenharmony_ci break; 31858c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4_SR: 31868c2ecf20Sopenharmony_ci *sysc_fields = &omap36xx_sr_sysc_fields; 31878c2ecf20Sopenharmony_ci break; 31888c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4_MCASP: 31898c2ecf20Sopenharmony_ci *sysc_fields = &omap_hwmod_sysc_type_mcasp; 31908c2ecf20Sopenharmony_ci break; 31918c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4_USB_HOST_FS: 31928c2ecf20Sopenharmony_ci *sysc_fields = &omap_hwmod_sysc_type_usb_host_fs; 31938c2ecf20Sopenharmony_ci break; 31948c2ecf20Sopenharmony_ci default: 31958c2ecf20Sopenharmony_ci *sysc_fields = NULL; 31968c2ecf20Sopenharmony_ci if (!oh->class->sysc->sysc_fields) 31978c2ecf20Sopenharmony_ci return 0; 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci dev_err(dev, "sysc_fields not found\n"); 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci return -EINVAL; 32028c2ecf20Sopenharmony_ci } 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci return omap_hwmod_check_sysc(dev, data, *sysc_fields); 32058c2ecf20Sopenharmony_ci} 32068c2ecf20Sopenharmony_ci 32078c2ecf20Sopenharmony_ci/** 32088c2ecf20Sopenharmony_ci * omap_hwmod_init_reg_offs - initialize sysconfig register offsets 32098c2ecf20Sopenharmony_ci * @dev: struct device 32108c2ecf20Sopenharmony_ci * @data: module data 32118c2ecf20Sopenharmony_ci * @rev_offs: revision register offset 32128c2ecf20Sopenharmony_ci * @sysc_offs: sysc register offset 32138c2ecf20Sopenharmony_ci * @syss_offs: syss register offset 32148c2ecf20Sopenharmony_ci */ 32158c2ecf20Sopenharmony_cistatic int omap_hwmod_init_reg_offs(struct device *dev, 32168c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 32178c2ecf20Sopenharmony_ci s32 *rev_offs, s32 *sysc_offs, 32188c2ecf20Sopenharmony_ci s32 *syss_offs) 32198c2ecf20Sopenharmony_ci{ 32208c2ecf20Sopenharmony_ci *rev_offs = -ENODEV; 32218c2ecf20Sopenharmony_ci *sysc_offs = 0; 32228c2ecf20Sopenharmony_ci *syss_offs = 0; 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci if (data->offsets[SYSC_REVISION] >= 0) 32258c2ecf20Sopenharmony_ci *rev_offs = data->offsets[SYSC_REVISION]; 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci if (data->offsets[SYSC_SYSCONFIG] >= 0) 32288c2ecf20Sopenharmony_ci *sysc_offs = data->offsets[SYSC_SYSCONFIG]; 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci if (data->offsets[SYSC_SYSSTATUS] >= 0) 32318c2ecf20Sopenharmony_ci *syss_offs = data->offsets[SYSC_SYSSTATUS]; 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci return 0; 32348c2ecf20Sopenharmony_ci} 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci/** 32378c2ecf20Sopenharmony_ci * omap_hwmod_init_sysc_flags - initialize sysconfig features 32388c2ecf20Sopenharmony_ci * @dev: struct device 32398c2ecf20Sopenharmony_ci * @data: module data 32408c2ecf20Sopenharmony_ci * @sysc_flags: module configuration 32418c2ecf20Sopenharmony_ci */ 32428c2ecf20Sopenharmony_cistatic int omap_hwmod_init_sysc_flags(struct device *dev, 32438c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 32448c2ecf20Sopenharmony_ci u32 *sysc_flags) 32458c2ecf20Sopenharmony_ci{ 32468c2ecf20Sopenharmony_ci *sysc_flags = 0; 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci switch (data->cap->type) { 32498c2ecf20Sopenharmony_ci case TI_SYSC_OMAP2: 32508c2ecf20Sopenharmony_ci case TI_SYSC_OMAP2_TIMER: 32518c2ecf20Sopenharmony_ci /* See SYSC_OMAP2_* in include/dt-bindings/bus/ti-sysc.h */ 32528c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP2_CLOCKACTIVITY) 32538c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_CLOCKACTIVITY; 32548c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP2_EMUFREE) 32558c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_EMUFREE; 32568c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP2_ENAWAKEUP) 32578c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_ENAWAKEUP; 32588c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP2_SOFTRESET) 32598c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_SOFTRESET; 32608c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP2_AUTOIDLE) 32618c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_AUTOIDLE; 32628c2ecf20Sopenharmony_ci break; 32638c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4: 32648c2ecf20Sopenharmony_ci case TI_SYSC_OMAP4_TIMER: 32658c2ecf20Sopenharmony_ci /* See SYSC_OMAP4_* in include/dt-bindings/bus/ti-sysc.h */ 32668c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP4_DMADISABLE) 32678c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_DMADISABLE; 32688c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP4_FREEEMU) 32698c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_EMUFREE; 32708c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP4_SOFTRESET) 32718c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_SOFTRESET; 32728c2ecf20Sopenharmony_ci break; 32738c2ecf20Sopenharmony_ci case TI_SYSC_OMAP34XX_SR: 32748c2ecf20Sopenharmony_ci case TI_SYSC_OMAP36XX_SR: 32758c2ecf20Sopenharmony_ci /* See SYSC_OMAP3_SR_* in include/dt-bindings/bus/ti-sysc.h */ 32768c2ecf20Sopenharmony_ci if (data->cfg->sysc_val & SYSC_OMAP3_SR_ENAWAKEUP) 32778c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_ENAWAKEUP; 32788c2ecf20Sopenharmony_ci break; 32798c2ecf20Sopenharmony_ci default: 32808c2ecf20Sopenharmony_ci if (data->cap->regbits->emufree_shift >= 0) 32818c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_EMUFREE; 32828c2ecf20Sopenharmony_ci if (data->cap->regbits->enwkup_shift >= 0) 32838c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_ENAWAKEUP; 32848c2ecf20Sopenharmony_ci if (data->cap->regbits->srst_shift >= 0) 32858c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_SOFTRESET; 32868c2ecf20Sopenharmony_ci if (data->cap->regbits->autoidle_shift >= 0) 32878c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_AUTOIDLE; 32888c2ecf20Sopenharmony_ci break; 32898c2ecf20Sopenharmony_ci } 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci if (data->cap->regbits->midle_shift >= 0 && 32928c2ecf20Sopenharmony_ci data->cfg->midlemodes) 32938c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_MIDLEMODE; 32948c2ecf20Sopenharmony_ci 32958c2ecf20Sopenharmony_ci if (data->cap->regbits->sidle_shift >= 0 && 32968c2ecf20Sopenharmony_ci data->cfg->sidlemodes) 32978c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_SIDLEMODE; 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_UNCACHED) 33008c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_NO_CACHE; 33018c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_RESET_STATUS) 33028c2ecf20Sopenharmony_ci *sysc_flags |= SYSC_HAS_RESET_STATUS; 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_ci if (data->cfg->syss_mask & 1) 33058c2ecf20Sopenharmony_ci *sysc_flags |= SYSS_HAS_RESET_STATUS; 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci return 0; 33088c2ecf20Sopenharmony_ci} 33098c2ecf20Sopenharmony_ci 33108c2ecf20Sopenharmony_ci/** 33118c2ecf20Sopenharmony_ci * omap_hwmod_init_idlemodes - initialize module idle modes 33128c2ecf20Sopenharmony_ci * @dev: struct device 33138c2ecf20Sopenharmony_ci * @data: module data 33148c2ecf20Sopenharmony_ci * @idlemodes: module supported idle modes 33158c2ecf20Sopenharmony_ci */ 33168c2ecf20Sopenharmony_cistatic int omap_hwmod_init_idlemodes(struct device *dev, 33178c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 33188c2ecf20Sopenharmony_ci u32 *idlemodes) 33198c2ecf20Sopenharmony_ci{ 33208c2ecf20Sopenharmony_ci *idlemodes = 0; 33218c2ecf20Sopenharmony_ci 33228c2ecf20Sopenharmony_ci if (data->cfg->midlemodes & BIT(SYSC_IDLE_FORCE)) 33238c2ecf20Sopenharmony_ci *idlemodes |= MSTANDBY_FORCE; 33248c2ecf20Sopenharmony_ci if (data->cfg->midlemodes & BIT(SYSC_IDLE_NO)) 33258c2ecf20Sopenharmony_ci *idlemodes |= MSTANDBY_NO; 33268c2ecf20Sopenharmony_ci if (data->cfg->midlemodes & BIT(SYSC_IDLE_SMART)) 33278c2ecf20Sopenharmony_ci *idlemodes |= MSTANDBY_SMART; 33288c2ecf20Sopenharmony_ci if (data->cfg->midlemodes & BIT(SYSC_IDLE_SMART_WKUP)) 33298c2ecf20Sopenharmony_ci *idlemodes |= MSTANDBY_SMART_WKUP; 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci if (data->cfg->sidlemodes & BIT(SYSC_IDLE_FORCE)) 33328c2ecf20Sopenharmony_ci *idlemodes |= SIDLE_FORCE; 33338c2ecf20Sopenharmony_ci if (data->cfg->sidlemodes & BIT(SYSC_IDLE_NO)) 33348c2ecf20Sopenharmony_ci *idlemodes |= SIDLE_NO; 33358c2ecf20Sopenharmony_ci if (data->cfg->sidlemodes & BIT(SYSC_IDLE_SMART)) 33368c2ecf20Sopenharmony_ci *idlemodes |= SIDLE_SMART; 33378c2ecf20Sopenharmony_ci if (data->cfg->sidlemodes & BIT(SYSC_IDLE_SMART_WKUP)) 33388c2ecf20Sopenharmony_ci *idlemodes |= SIDLE_SMART_WKUP; 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci return 0; 33418c2ecf20Sopenharmony_ci} 33428c2ecf20Sopenharmony_ci 33438c2ecf20Sopenharmony_ci/** 33448c2ecf20Sopenharmony_ci * omap_hwmod_check_module - check new module against platform data 33458c2ecf20Sopenharmony_ci * @dev: struct device 33468c2ecf20Sopenharmony_ci * @oh: module 33478c2ecf20Sopenharmony_ci * @data: new module data 33488c2ecf20Sopenharmony_ci * @sysc_fields: sysc register bits 33498c2ecf20Sopenharmony_ci * @rev_offs: revision register offset 33508c2ecf20Sopenharmony_ci * @sysc_offs: sysconfig register offset 33518c2ecf20Sopenharmony_ci * @syss_offs: sysstatus register offset 33528c2ecf20Sopenharmony_ci * @sysc_flags: sysc specific flags 33538c2ecf20Sopenharmony_ci * @idlemodes: sysc supported idlemodes 33548c2ecf20Sopenharmony_ci */ 33558c2ecf20Sopenharmony_cistatic int omap_hwmod_check_module(struct device *dev, 33568c2ecf20Sopenharmony_ci struct omap_hwmod *oh, 33578c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 33588c2ecf20Sopenharmony_ci struct sysc_regbits *sysc_fields, 33598c2ecf20Sopenharmony_ci s32 rev_offs, s32 sysc_offs, 33608c2ecf20Sopenharmony_ci s32 syss_offs, u32 sysc_flags, 33618c2ecf20Sopenharmony_ci u32 idlemodes) 33628c2ecf20Sopenharmony_ci{ 33638c2ecf20Sopenharmony_ci if (!oh->class->sysc) 33648c2ecf20Sopenharmony_ci return -ENODEV; 33658c2ecf20Sopenharmony_ci 33668c2ecf20Sopenharmony_ci if (oh->class->sysc->sysc_fields && 33678c2ecf20Sopenharmony_ci sysc_fields != oh->class->sysc->sysc_fields) 33688c2ecf20Sopenharmony_ci dev_warn(dev, "sysc_fields mismatch\n"); 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_ci if (rev_offs != oh->class->sysc->rev_offs) 33718c2ecf20Sopenharmony_ci dev_warn(dev, "rev_offs %08x != %08x\n", rev_offs, 33728c2ecf20Sopenharmony_ci oh->class->sysc->rev_offs); 33738c2ecf20Sopenharmony_ci if (sysc_offs != oh->class->sysc->sysc_offs) 33748c2ecf20Sopenharmony_ci dev_warn(dev, "sysc_offs %08x != %08x\n", sysc_offs, 33758c2ecf20Sopenharmony_ci oh->class->sysc->sysc_offs); 33768c2ecf20Sopenharmony_ci if (syss_offs != oh->class->sysc->syss_offs) 33778c2ecf20Sopenharmony_ci dev_warn(dev, "syss_offs %08x != %08x\n", syss_offs, 33788c2ecf20Sopenharmony_ci oh->class->sysc->syss_offs); 33798c2ecf20Sopenharmony_ci 33808c2ecf20Sopenharmony_ci if (sysc_flags != oh->class->sysc->sysc_flags) 33818c2ecf20Sopenharmony_ci dev_warn(dev, "sysc_flags %08x != %08x\n", sysc_flags, 33828c2ecf20Sopenharmony_ci oh->class->sysc->sysc_flags); 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci if (idlemodes != oh->class->sysc->idlemodes) 33858c2ecf20Sopenharmony_ci dev_warn(dev, "idlemodes %08x != %08x\n", idlemodes, 33868c2ecf20Sopenharmony_ci oh->class->sysc->idlemodes); 33878c2ecf20Sopenharmony_ci 33888c2ecf20Sopenharmony_ci if (data->cfg->srst_udelay != oh->class->sysc->srst_udelay) 33898c2ecf20Sopenharmony_ci dev_warn(dev, "srst_udelay %i != %i\n", 33908c2ecf20Sopenharmony_ci data->cfg->srst_udelay, 33918c2ecf20Sopenharmony_ci oh->class->sysc->srst_udelay); 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_ci return 0; 33948c2ecf20Sopenharmony_ci} 33958c2ecf20Sopenharmony_ci 33968c2ecf20Sopenharmony_ci/** 33978c2ecf20Sopenharmony_ci * omap_hwmod_allocate_module - allocate new module 33988c2ecf20Sopenharmony_ci * @dev: struct device 33998c2ecf20Sopenharmony_ci * @oh: module 34008c2ecf20Sopenharmony_ci * @sysc_fields: sysc register bits 34018c2ecf20Sopenharmony_ci * @clockdomain: clockdomain 34028c2ecf20Sopenharmony_ci * @rev_offs: revision register offset 34038c2ecf20Sopenharmony_ci * @sysc_offs: sysconfig register offset 34048c2ecf20Sopenharmony_ci * @syss_offs: sysstatus register offset 34058c2ecf20Sopenharmony_ci * @sysc_flags: sysc specific flags 34068c2ecf20Sopenharmony_ci * @idlemodes: sysc supported idlemodes 34078c2ecf20Sopenharmony_ci * 34088c2ecf20Sopenharmony_ci * Note that the allocations here cannot use devm as ti-sysc can rebind. 34098c2ecf20Sopenharmony_ci */ 34108c2ecf20Sopenharmony_cistatic int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, 34118c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 34128c2ecf20Sopenharmony_ci struct sysc_regbits *sysc_fields, 34138c2ecf20Sopenharmony_ci struct clockdomain *clkdm, 34148c2ecf20Sopenharmony_ci s32 rev_offs, s32 sysc_offs, 34158c2ecf20Sopenharmony_ci s32 syss_offs, u32 sysc_flags, 34168c2ecf20Sopenharmony_ci u32 idlemodes) 34178c2ecf20Sopenharmony_ci{ 34188c2ecf20Sopenharmony_ci struct omap_hwmod_class_sysconfig *sysc; 34198c2ecf20Sopenharmony_ci struct omap_hwmod_class *class = NULL; 34208c2ecf20Sopenharmony_ci struct omap_hwmod_ocp_if *oi = NULL; 34218c2ecf20Sopenharmony_ci void __iomem *regs = NULL; 34228c2ecf20Sopenharmony_ci unsigned long flags; 34238c2ecf20Sopenharmony_ci 34248c2ecf20Sopenharmony_ci sysc = kzalloc(sizeof(*sysc), GFP_KERNEL); 34258c2ecf20Sopenharmony_ci if (!sysc) 34268c2ecf20Sopenharmony_ci return -ENOMEM; 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci sysc->sysc_fields = sysc_fields; 34298c2ecf20Sopenharmony_ci sysc->rev_offs = rev_offs; 34308c2ecf20Sopenharmony_ci sysc->sysc_offs = sysc_offs; 34318c2ecf20Sopenharmony_ci sysc->syss_offs = syss_offs; 34328c2ecf20Sopenharmony_ci sysc->sysc_flags = sysc_flags; 34338c2ecf20Sopenharmony_ci sysc->idlemodes = idlemodes; 34348c2ecf20Sopenharmony_ci sysc->srst_udelay = data->cfg->srst_udelay; 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci if (!oh->_mpu_rt_va) { 34378c2ecf20Sopenharmony_ci regs = ioremap(data->module_pa, 34388c2ecf20Sopenharmony_ci data->module_size); 34398c2ecf20Sopenharmony_ci if (!regs) 34408c2ecf20Sopenharmony_ci goto out_free_sysc; 34418c2ecf20Sopenharmony_ci } 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci /* 34448c2ecf20Sopenharmony_ci * We may need a new oh->class as the other devices in the same class 34458c2ecf20Sopenharmony_ci * may not yet have ioremapped their registers. 34468c2ecf20Sopenharmony_ci */ 34478c2ecf20Sopenharmony_ci if (oh->class->name && strcmp(oh->class->name, data->name)) { 34488c2ecf20Sopenharmony_ci class = kmemdup(oh->class, sizeof(*oh->class), GFP_KERNEL); 34498c2ecf20Sopenharmony_ci if (!class) 34508c2ecf20Sopenharmony_ci goto out_unmap; 34518c2ecf20Sopenharmony_ci } 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_ci if (list_empty(&oh->slave_ports)) { 34548c2ecf20Sopenharmony_ci oi = kcalloc(1, sizeof(*oi), GFP_KERNEL); 34558c2ecf20Sopenharmony_ci if (!oi) 34568c2ecf20Sopenharmony_ci goto out_free_class; 34578c2ecf20Sopenharmony_ci 34588c2ecf20Sopenharmony_ci /* 34598c2ecf20Sopenharmony_ci * Note that we assume interconnect interface clocks will be 34608c2ecf20Sopenharmony_ci * managed by the interconnect driver for OCPIF_SWSUP_IDLE case 34618c2ecf20Sopenharmony_ci * on omap24xx and omap3. 34628c2ecf20Sopenharmony_ci */ 34638c2ecf20Sopenharmony_ci oi->slave = oh; 34648c2ecf20Sopenharmony_ci oi->user = OCP_USER_MPU | OCP_USER_SDMA; 34658c2ecf20Sopenharmony_ci } 34668c2ecf20Sopenharmony_ci 34678c2ecf20Sopenharmony_ci spin_lock_irqsave(&oh->_lock, flags); 34688c2ecf20Sopenharmony_ci if (regs) 34698c2ecf20Sopenharmony_ci oh->_mpu_rt_va = regs; 34708c2ecf20Sopenharmony_ci if (class) 34718c2ecf20Sopenharmony_ci oh->class = class; 34728c2ecf20Sopenharmony_ci oh->class->sysc = sysc; 34738c2ecf20Sopenharmony_ci if (oi) 34748c2ecf20Sopenharmony_ci _add_link(oi); 34758c2ecf20Sopenharmony_ci if (clkdm) 34768c2ecf20Sopenharmony_ci oh->clkdm = clkdm; 34778c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_INITIALIZED; 34788c2ecf20Sopenharmony_ci oh->_postsetup_state = _HWMOD_STATE_DEFAULT; 34798c2ecf20Sopenharmony_ci _setup(oh, NULL); 34808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&oh->_lock, flags); 34818c2ecf20Sopenharmony_ci 34828c2ecf20Sopenharmony_ci return 0; 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_ciout_free_class: 34858c2ecf20Sopenharmony_ci kfree(class); 34868c2ecf20Sopenharmony_ciout_unmap: 34878c2ecf20Sopenharmony_ci iounmap(regs); 34888c2ecf20Sopenharmony_ciout_free_sysc: 34898c2ecf20Sopenharmony_ci kfree(sysc); 34908c2ecf20Sopenharmony_ci return -ENOMEM; 34918c2ecf20Sopenharmony_ci} 34928c2ecf20Sopenharmony_ci 34938c2ecf20Sopenharmony_cistatic const struct omap_hwmod_reset omap24xx_reset_quirks[] = { 34948c2ecf20Sopenharmony_ci { .match = "msdi", .len = 4, .reset = omap_msdi_reset, }, 34958c2ecf20Sopenharmony_ci}; 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_cistatic const struct omap_hwmod_reset dra7_reset_quirks[] = { 34988c2ecf20Sopenharmony_ci { .match = "pcie", .len = 4, .reset = dra7xx_pciess_reset, }, 34998c2ecf20Sopenharmony_ci}; 35008c2ecf20Sopenharmony_ci 35018c2ecf20Sopenharmony_cistatic const struct omap_hwmod_reset omap_reset_quirks[] = { 35028c2ecf20Sopenharmony_ci { .match = "dss_core", .len = 8, .reset = omap_dss_reset, }, 35038c2ecf20Sopenharmony_ci { .match = "hdq1w", .len = 5, .reset = omap_hdq1w_reset, }, 35048c2ecf20Sopenharmony_ci { .match = "i2c", .len = 3, .reset = omap_i2c_reset, }, 35058c2ecf20Sopenharmony_ci { .match = "wd_timer", .len = 8, .reset = omap2_wd_timer_reset, }, 35068c2ecf20Sopenharmony_ci}; 35078c2ecf20Sopenharmony_ci 35088c2ecf20Sopenharmony_cistatic void 35098c2ecf20Sopenharmony_ciomap_hwmod_init_reset_quirk(struct device *dev, struct omap_hwmod *oh, 35108c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 35118c2ecf20Sopenharmony_ci const struct omap_hwmod_reset *quirks, 35128c2ecf20Sopenharmony_ci int quirks_sz) 35138c2ecf20Sopenharmony_ci{ 35148c2ecf20Sopenharmony_ci const struct omap_hwmod_reset *quirk; 35158c2ecf20Sopenharmony_ci int i; 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_ci for (i = 0; i < quirks_sz; i++) { 35188c2ecf20Sopenharmony_ci quirk = &quirks[i]; 35198c2ecf20Sopenharmony_ci if (!strncmp(data->name, quirk->match, quirk->len)) { 35208c2ecf20Sopenharmony_ci oh->class->reset = quirk->reset; 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_ci return; 35238c2ecf20Sopenharmony_ci } 35248c2ecf20Sopenharmony_ci } 35258c2ecf20Sopenharmony_ci} 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_cistatic void 35288c2ecf20Sopenharmony_ciomap_hwmod_init_reset_quirks(struct device *dev, struct omap_hwmod *oh, 35298c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data) 35308c2ecf20Sopenharmony_ci{ 35318c2ecf20Sopenharmony_ci if (soc_is_omap24xx()) 35328c2ecf20Sopenharmony_ci omap_hwmod_init_reset_quirk(dev, oh, data, 35338c2ecf20Sopenharmony_ci omap24xx_reset_quirks, 35348c2ecf20Sopenharmony_ci ARRAY_SIZE(omap24xx_reset_quirks)); 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_ci if (soc_is_dra7xx()) 35378c2ecf20Sopenharmony_ci omap_hwmod_init_reset_quirk(dev, oh, data, dra7_reset_quirks, 35388c2ecf20Sopenharmony_ci ARRAY_SIZE(dra7_reset_quirks)); 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci omap_hwmod_init_reset_quirk(dev, oh, data, omap_reset_quirks, 35418c2ecf20Sopenharmony_ci ARRAY_SIZE(omap_reset_quirks)); 35428c2ecf20Sopenharmony_ci} 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci/** 35458c2ecf20Sopenharmony_ci * omap_hwmod_init_module - initialize new module 35468c2ecf20Sopenharmony_ci * @dev: struct device 35478c2ecf20Sopenharmony_ci * @data: module data 35488c2ecf20Sopenharmony_ci * @cookie: cookie for the caller to use for later calls 35498c2ecf20Sopenharmony_ci */ 35508c2ecf20Sopenharmony_ciint omap_hwmod_init_module(struct device *dev, 35518c2ecf20Sopenharmony_ci const struct ti_sysc_module_data *data, 35528c2ecf20Sopenharmony_ci struct ti_sysc_cookie *cookie) 35538c2ecf20Sopenharmony_ci{ 35548c2ecf20Sopenharmony_ci struct omap_hwmod *oh; 35558c2ecf20Sopenharmony_ci struct sysc_regbits *sysc_fields; 35568c2ecf20Sopenharmony_ci s32 rev_offs, sysc_offs, syss_offs; 35578c2ecf20Sopenharmony_ci u32 sysc_flags, idlemodes; 35588c2ecf20Sopenharmony_ci int error; 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_ci if (!dev || !data || !data->name || !cookie) 35618c2ecf20Sopenharmony_ci return -EINVAL; 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_ci oh = _lookup(data->name); 35648c2ecf20Sopenharmony_ci if (!oh) { 35658c2ecf20Sopenharmony_ci oh = kzalloc(sizeof(*oh), GFP_KERNEL); 35668c2ecf20Sopenharmony_ci if (!oh) 35678c2ecf20Sopenharmony_ci return -ENOMEM; 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci oh->name = data->name; 35708c2ecf20Sopenharmony_ci oh->_state = _HWMOD_STATE_UNKNOWN; 35718c2ecf20Sopenharmony_ci lockdep_register_key(&oh->hwmod_key); 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci /* Unused, can be handled by PRM driver handling resets */ 35748c2ecf20Sopenharmony_ci oh->prcm.omap4.flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT; 35758c2ecf20Sopenharmony_ci 35768c2ecf20Sopenharmony_ci oh->class = kzalloc(sizeof(*oh->class), GFP_KERNEL); 35778c2ecf20Sopenharmony_ci if (!oh->class) { 35788c2ecf20Sopenharmony_ci kfree(oh); 35798c2ecf20Sopenharmony_ci return -ENOMEM; 35808c2ecf20Sopenharmony_ci } 35818c2ecf20Sopenharmony_ci 35828c2ecf20Sopenharmony_ci omap_hwmod_init_reset_quirks(dev, oh, data); 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci oh->class->name = data->name; 35858c2ecf20Sopenharmony_ci mutex_lock(&list_lock); 35868c2ecf20Sopenharmony_ci error = _register(oh); 35878c2ecf20Sopenharmony_ci mutex_unlock(&list_lock); 35888c2ecf20Sopenharmony_ci } 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci cookie->data = oh; 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_ci error = omap_hwmod_init_regbits(dev, oh, data, &sysc_fields); 35938c2ecf20Sopenharmony_ci if (error) 35948c2ecf20Sopenharmony_ci return error; 35958c2ecf20Sopenharmony_ci 35968c2ecf20Sopenharmony_ci error = omap_hwmod_init_reg_offs(dev, data, &rev_offs, 35978c2ecf20Sopenharmony_ci &sysc_offs, &syss_offs); 35988c2ecf20Sopenharmony_ci if (error) 35998c2ecf20Sopenharmony_ci return error; 36008c2ecf20Sopenharmony_ci 36018c2ecf20Sopenharmony_ci error = omap_hwmod_init_sysc_flags(dev, data, &sysc_flags); 36028c2ecf20Sopenharmony_ci if (error) 36038c2ecf20Sopenharmony_ci return error; 36048c2ecf20Sopenharmony_ci 36058c2ecf20Sopenharmony_ci error = omap_hwmod_init_idlemodes(dev, data, &idlemodes); 36068c2ecf20Sopenharmony_ci if (error) 36078c2ecf20Sopenharmony_ci return error; 36088c2ecf20Sopenharmony_ci 36098c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_NO_IDLE) 36108c2ecf20Sopenharmony_ci oh->flags |= HWMOD_NO_IDLE; 36118c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_NO_IDLE_ON_INIT) 36128c2ecf20Sopenharmony_ci oh->flags |= HWMOD_INIT_NO_IDLE; 36138c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_NO_RESET_ON_INIT) 36148c2ecf20Sopenharmony_ci oh->flags |= HWMOD_INIT_NO_RESET; 36158c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_USE_CLOCKACT) 36168c2ecf20Sopenharmony_ci oh->flags |= HWMOD_SET_DEFAULT_CLOCKACT; 36178c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_SWSUP_SIDLE) 36188c2ecf20Sopenharmony_ci oh->flags |= HWMOD_SWSUP_SIDLE; 36198c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_SWSUP_SIDLE_ACT) 36208c2ecf20Sopenharmony_ci oh->flags |= HWMOD_SWSUP_SIDLE_ACT; 36218c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_SWSUP_MSTANDBY) 36228c2ecf20Sopenharmony_ci oh->flags |= HWMOD_SWSUP_MSTANDBY; 36238c2ecf20Sopenharmony_ci if (data->cfg->quirks & SYSC_QUIRK_CLKDM_NOAUTO) 36248c2ecf20Sopenharmony_ci oh->flags |= HWMOD_CLKDM_NOAUTO; 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci error = omap_hwmod_check_module(dev, oh, data, sysc_fields, 36278c2ecf20Sopenharmony_ci rev_offs, sysc_offs, syss_offs, 36288c2ecf20Sopenharmony_ci sysc_flags, idlemodes); 36298c2ecf20Sopenharmony_ci if (!error) 36308c2ecf20Sopenharmony_ci return error; 36318c2ecf20Sopenharmony_ci 36328c2ecf20Sopenharmony_ci return omap_hwmod_allocate_module(dev, oh, data, sysc_fields, 36338c2ecf20Sopenharmony_ci cookie->clkdm, rev_offs, 36348c2ecf20Sopenharmony_ci sysc_offs, syss_offs, 36358c2ecf20Sopenharmony_ci sysc_flags, idlemodes); 36368c2ecf20Sopenharmony_ci} 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci/** 36398c2ecf20Sopenharmony_ci * omap_hwmod_setup_earlycon_flags - set up flags for early console 36408c2ecf20Sopenharmony_ci * 36418c2ecf20Sopenharmony_ci * Enable DEBUG_OMAPUART_FLAGS for uart hwmod that is being used as 36428c2ecf20Sopenharmony_ci * early concole so that hwmod core doesn't reset and keep it in idle 36438c2ecf20Sopenharmony_ci * that specific uart. 36448c2ecf20Sopenharmony_ci */ 36458c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIAL_EARLYCON 36468c2ecf20Sopenharmony_cistatic void __init omap_hwmod_setup_earlycon_flags(void) 36478c2ecf20Sopenharmony_ci{ 36488c2ecf20Sopenharmony_ci struct device_node *np; 36498c2ecf20Sopenharmony_ci struct omap_hwmod *oh; 36508c2ecf20Sopenharmony_ci const char *uart; 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci np = of_find_node_by_path("/chosen"); 36538c2ecf20Sopenharmony_ci if (np) { 36548c2ecf20Sopenharmony_ci uart = of_get_property(np, "stdout-path", NULL); 36558c2ecf20Sopenharmony_ci if (uart) { 36568c2ecf20Sopenharmony_ci np = of_find_node_by_path(uart); 36578c2ecf20Sopenharmony_ci if (np) { 36588c2ecf20Sopenharmony_ci uart = of_get_property(np, "ti,hwmods", NULL); 36598c2ecf20Sopenharmony_ci oh = omap_hwmod_lookup(uart); 36608c2ecf20Sopenharmony_ci if (!oh) { 36618c2ecf20Sopenharmony_ci uart = of_get_property(np->parent, 36628c2ecf20Sopenharmony_ci "ti,hwmods", 36638c2ecf20Sopenharmony_ci NULL); 36648c2ecf20Sopenharmony_ci oh = omap_hwmod_lookup(uart); 36658c2ecf20Sopenharmony_ci } 36668c2ecf20Sopenharmony_ci if (oh) 36678c2ecf20Sopenharmony_ci oh->flags |= DEBUG_OMAPUART_FLAGS; 36688c2ecf20Sopenharmony_ci } 36698c2ecf20Sopenharmony_ci } 36708c2ecf20Sopenharmony_ci } 36718c2ecf20Sopenharmony_ci} 36728c2ecf20Sopenharmony_ci#endif 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_ci/** 36758c2ecf20Sopenharmony_ci * omap_hwmod_setup_all - set up all registered IP blocks 36768c2ecf20Sopenharmony_ci * 36778c2ecf20Sopenharmony_ci * Initialize and set up all IP blocks registered with the hwmod code. 36788c2ecf20Sopenharmony_ci * Must be called after omap2_clk_init(). Resolves the struct clk 36798c2ecf20Sopenharmony_ci * names to struct clk pointers for each registered omap_hwmod. Also 36808c2ecf20Sopenharmony_ci * calls _setup() on each hwmod. Returns 0 upon success. 36818c2ecf20Sopenharmony_ci */ 36828c2ecf20Sopenharmony_cistatic int __init omap_hwmod_setup_all(void) 36838c2ecf20Sopenharmony_ci{ 36848c2ecf20Sopenharmony_ci _ensure_mpu_hwmod_is_setup(NULL); 36858c2ecf20Sopenharmony_ci 36868c2ecf20Sopenharmony_ci omap_hwmod_for_each(_init, NULL); 36878c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIAL_EARLYCON 36888c2ecf20Sopenharmony_ci omap_hwmod_setup_earlycon_flags(); 36898c2ecf20Sopenharmony_ci#endif 36908c2ecf20Sopenharmony_ci omap_hwmod_for_each(_setup, NULL); 36918c2ecf20Sopenharmony_ci 36928c2ecf20Sopenharmony_ci return 0; 36938c2ecf20Sopenharmony_ci} 36948c2ecf20Sopenharmony_ciomap_postcore_initcall(omap_hwmod_setup_all); 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci/** 36978c2ecf20Sopenharmony_ci * omap_hwmod_enable - enable an omap_hwmod 36988c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 36998c2ecf20Sopenharmony_ci * 37008c2ecf20Sopenharmony_ci * Enable an omap_hwmod @oh. Intended to be called by omap_device_enable(). 37018c2ecf20Sopenharmony_ci * Returns -EINVAL on error or passes along the return value from _enable(). 37028c2ecf20Sopenharmony_ci */ 37038c2ecf20Sopenharmony_ciint omap_hwmod_enable(struct omap_hwmod *oh) 37048c2ecf20Sopenharmony_ci{ 37058c2ecf20Sopenharmony_ci int r; 37068c2ecf20Sopenharmony_ci unsigned long flags; 37078c2ecf20Sopenharmony_ci 37088c2ecf20Sopenharmony_ci if (!oh) 37098c2ecf20Sopenharmony_ci return -EINVAL; 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci spin_lock_irqsave(&oh->_lock, flags); 37128c2ecf20Sopenharmony_ci r = _enable(oh); 37138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&oh->_lock, flags); 37148c2ecf20Sopenharmony_ci 37158c2ecf20Sopenharmony_ci return r; 37168c2ecf20Sopenharmony_ci} 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_ci/** 37198c2ecf20Sopenharmony_ci * omap_hwmod_idle - idle an omap_hwmod 37208c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 37218c2ecf20Sopenharmony_ci * 37228c2ecf20Sopenharmony_ci * Idle an omap_hwmod @oh. Intended to be called by omap_device_idle(). 37238c2ecf20Sopenharmony_ci * Returns -EINVAL on error or passes along the return value from _idle(). 37248c2ecf20Sopenharmony_ci */ 37258c2ecf20Sopenharmony_ciint omap_hwmod_idle(struct omap_hwmod *oh) 37268c2ecf20Sopenharmony_ci{ 37278c2ecf20Sopenharmony_ci int r; 37288c2ecf20Sopenharmony_ci unsigned long flags; 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci if (!oh) 37318c2ecf20Sopenharmony_ci return -EINVAL; 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci spin_lock_irqsave(&oh->_lock, flags); 37348c2ecf20Sopenharmony_ci r = _idle(oh); 37358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&oh->_lock, flags); 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci return r; 37388c2ecf20Sopenharmony_ci} 37398c2ecf20Sopenharmony_ci 37408c2ecf20Sopenharmony_ci/** 37418c2ecf20Sopenharmony_ci * omap_hwmod_shutdown - shutdown an omap_hwmod 37428c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 37438c2ecf20Sopenharmony_ci * 37448c2ecf20Sopenharmony_ci * Shutdown an omap_hwmod @oh. Intended to be called by 37458c2ecf20Sopenharmony_ci * omap_device_shutdown(). Returns -EINVAL on error or passes along 37468c2ecf20Sopenharmony_ci * the return value from _shutdown(). 37478c2ecf20Sopenharmony_ci */ 37488c2ecf20Sopenharmony_ciint omap_hwmod_shutdown(struct omap_hwmod *oh) 37498c2ecf20Sopenharmony_ci{ 37508c2ecf20Sopenharmony_ci int r; 37518c2ecf20Sopenharmony_ci unsigned long flags; 37528c2ecf20Sopenharmony_ci 37538c2ecf20Sopenharmony_ci if (!oh) 37548c2ecf20Sopenharmony_ci return -EINVAL; 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci spin_lock_irqsave(&oh->_lock, flags); 37578c2ecf20Sopenharmony_ci r = _shutdown(oh); 37588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&oh->_lock, flags); 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci return r; 37618c2ecf20Sopenharmony_ci} 37628c2ecf20Sopenharmony_ci 37638c2ecf20Sopenharmony_ci/* 37648c2ecf20Sopenharmony_ci * IP block data retrieval functions 37658c2ecf20Sopenharmony_ci */ 37668c2ecf20Sopenharmony_ci 37678c2ecf20Sopenharmony_ci/** 37688c2ecf20Sopenharmony_ci * omap_hwmod_get_pwrdm - return pointer to this module's main powerdomain 37698c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 37708c2ecf20Sopenharmony_ci * 37718c2ecf20Sopenharmony_ci * Return the powerdomain pointer associated with the OMAP module 37728c2ecf20Sopenharmony_ci * @oh's main clock. If @oh does not have a main clk, return the 37738c2ecf20Sopenharmony_ci * powerdomain associated with the interface clock associated with the 37748c2ecf20Sopenharmony_ci * module's MPU port. (XXX Perhaps this should use the SDMA port 37758c2ecf20Sopenharmony_ci * instead?) Returns NULL on error, or a struct powerdomain * on 37768c2ecf20Sopenharmony_ci * success. 37778c2ecf20Sopenharmony_ci */ 37788c2ecf20Sopenharmony_cistruct powerdomain *omap_hwmod_get_pwrdm(struct omap_hwmod *oh) 37798c2ecf20Sopenharmony_ci{ 37808c2ecf20Sopenharmony_ci struct clk *c; 37818c2ecf20Sopenharmony_ci struct omap_hwmod_ocp_if *oi; 37828c2ecf20Sopenharmony_ci struct clockdomain *clkdm; 37838c2ecf20Sopenharmony_ci struct clk_hw_omap *clk; 37848c2ecf20Sopenharmony_ci struct clk_hw *hw; 37858c2ecf20Sopenharmony_ci 37868c2ecf20Sopenharmony_ci if (!oh) 37878c2ecf20Sopenharmony_ci return NULL; 37888c2ecf20Sopenharmony_ci 37898c2ecf20Sopenharmony_ci if (oh->clkdm) 37908c2ecf20Sopenharmony_ci return oh->clkdm->pwrdm.ptr; 37918c2ecf20Sopenharmony_ci 37928c2ecf20Sopenharmony_ci if (oh->_clk) { 37938c2ecf20Sopenharmony_ci c = oh->_clk; 37948c2ecf20Sopenharmony_ci } else { 37958c2ecf20Sopenharmony_ci oi = _find_mpu_rt_port(oh); 37968c2ecf20Sopenharmony_ci if (!oi) 37978c2ecf20Sopenharmony_ci return NULL; 37988c2ecf20Sopenharmony_ci c = oi->_clk; 37998c2ecf20Sopenharmony_ci } 38008c2ecf20Sopenharmony_ci 38018c2ecf20Sopenharmony_ci hw = __clk_get_hw(c); 38028c2ecf20Sopenharmony_ci if (!hw) 38038c2ecf20Sopenharmony_ci return NULL; 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_ci clk = to_clk_hw_omap(hw); 38068c2ecf20Sopenharmony_ci if (!clk) 38078c2ecf20Sopenharmony_ci return NULL; 38088c2ecf20Sopenharmony_ci 38098c2ecf20Sopenharmony_ci clkdm = clk->clkdm; 38108c2ecf20Sopenharmony_ci if (!clkdm) 38118c2ecf20Sopenharmony_ci return NULL; 38128c2ecf20Sopenharmony_ci 38138c2ecf20Sopenharmony_ci return clkdm->pwrdm.ptr; 38148c2ecf20Sopenharmony_ci} 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_ci/** 38178c2ecf20Sopenharmony_ci * omap_hwmod_get_mpu_rt_va - return the module's base address (for the MPU) 38188c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 38198c2ecf20Sopenharmony_ci * 38208c2ecf20Sopenharmony_ci * Returns the virtual address corresponding to the beginning of the 38218c2ecf20Sopenharmony_ci * module's register target, in the address range that is intended to 38228c2ecf20Sopenharmony_ci * be used by the MPU. Returns the virtual address upon success or NULL 38238c2ecf20Sopenharmony_ci * upon error. 38248c2ecf20Sopenharmony_ci */ 38258c2ecf20Sopenharmony_civoid __iomem *omap_hwmod_get_mpu_rt_va(struct omap_hwmod *oh) 38268c2ecf20Sopenharmony_ci{ 38278c2ecf20Sopenharmony_ci if (!oh) 38288c2ecf20Sopenharmony_ci return NULL; 38298c2ecf20Sopenharmony_ci 38308c2ecf20Sopenharmony_ci if (oh->_int_flags & _HWMOD_NO_MPU_PORT) 38318c2ecf20Sopenharmony_ci return NULL; 38328c2ecf20Sopenharmony_ci 38338c2ecf20Sopenharmony_ci if (oh->_state == _HWMOD_STATE_UNKNOWN) 38348c2ecf20Sopenharmony_ci return NULL; 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci return oh->_mpu_rt_va; 38378c2ecf20Sopenharmony_ci} 38388c2ecf20Sopenharmony_ci 38398c2ecf20Sopenharmony_ci/* 38408c2ecf20Sopenharmony_ci * XXX what about functions for drivers to save/restore ocp_sysconfig 38418c2ecf20Sopenharmony_ci * for context save/restore operations? 38428c2ecf20Sopenharmony_ci */ 38438c2ecf20Sopenharmony_ci 38448c2ecf20Sopenharmony_ci/** 38458c2ecf20Sopenharmony_ci * omap_hwmod_assert_hardreset - assert the HW reset line of submodules 38468c2ecf20Sopenharmony_ci * contained in the hwmod module. 38478c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 38488c2ecf20Sopenharmony_ci * @name: name of the reset line to lookup and assert 38498c2ecf20Sopenharmony_ci * 38508c2ecf20Sopenharmony_ci * Some IP like dsp, ipu or iva contain processor that require 38518c2ecf20Sopenharmony_ci * an HW reset line to be assert / deassert in order to enable fully 38528c2ecf20Sopenharmony_ci * the IP. Returns -EINVAL if @oh is null or if the operation is not 38538c2ecf20Sopenharmony_ci * yet supported on this OMAP; otherwise, passes along the return value 38548c2ecf20Sopenharmony_ci * from _assert_hardreset(). 38558c2ecf20Sopenharmony_ci */ 38568c2ecf20Sopenharmony_ciint omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name) 38578c2ecf20Sopenharmony_ci{ 38588c2ecf20Sopenharmony_ci int ret; 38598c2ecf20Sopenharmony_ci unsigned long flags; 38608c2ecf20Sopenharmony_ci 38618c2ecf20Sopenharmony_ci if (!oh) 38628c2ecf20Sopenharmony_ci return -EINVAL; 38638c2ecf20Sopenharmony_ci 38648c2ecf20Sopenharmony_ci spin_lock_irqsave(&oh->_lock, flags); 38658c2ecf20Sopenharmony_ci ret = _assert_hardreset(oh, name); 38668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&oh->_lock, flags); 38678c2ecf20Sopenharmony_ci 38688c2ecf20Sopenharmony_ci return ret; 38698c2ecf20Sopenharmony_ci} 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci/** 38728c2ecf20Sopenharmony_ci * omap_hwmod_deassert_hardreset - deassert the HW reset line of submodules 38738c2ecf20Sopenharmony_ci * contained in the hwmod module. 38748c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 38758c2ecf20Sopenharmony_ci * @name: name of the reset line to look up and deassert 38768c2ecf20Sopenharmony_ci * 38778c2ecf20Sopenharmony_ci * Some IP like dsp, ipu or iva contain processor that require 38788c2ecf20Sopenharmony_ci * an HW reset line to be assert / deassert in order to enable fully 38798c2ecf20Sopenharmony_ci * the IP. Returns -EINVAL if @oh is null or if the operation is not 38808c2ecf20Sopenharmony_ci * yet supported on this OMAP; otherwise, passes along the return value 38818c2ecf20Sopenharmony_ci * from _deassert_hardreset(). 38828c2ecf20Sopenharmony_ci */ 38838c2ecf20Sopenharmony_ciint omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name) 38848c2ecf20Sopenharmony_ci{ 38858c2ecf20Sopenharmony_ci int ret; 38868c2ecf20Sopenharmony_ci unsigned long flags; 38878c2ecf20Sopenharmony_ci 38888c2ecf20Sopenharmony_ci if (!oh) 38898c2ecf20Sopenharmony_ci return -EINVAL; 38908c2ecf20Sopenharmony_ci 38918c2ecf20Sopenharmony_ci spin_lock_irqsave(&oh->_lock, flags); 38928c2ecf20Sopenharmony_ci ret = _deassert_hardreset(oh, name); 38938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&oh->_lock, flags); 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_ci return ret; 38968c2ecf20Sopenharmony_ci} 38978c2ecf20Sopenharmony_ci 38988c2ecf20Sopenharmony_ci/** 38998c2ecf20Sopenharmony_ci * omap_hwmod_for_each_by_class - call @fn for each hwmod of class @classname 39008c2ecf20Sopenharmony_ci * @classname: struct omap_hwmod_class name to search for 39018c2ecf20Sopenharmony_ci * @fn: callback function pointer to call for each hwmod in class @classname 39028c2ecf20Sopenharmony_ci * @user: arbitrary context data to pass to the callback function 39038c2ecf20Sopenharmony_ci * 39048c2ecf20Sopenharmony_ci * For each omap_hwmod of class @classname, call @fn. 39058c2ecf20Sopenharmony_ci * If the callback function returns something other than 39068c2ecf20Sopenharmony_ci * zero, the iterator is terminated, and the callback function's return 39078c2ecf20Sopenharmony_ci * value is passed back to the caller. Returns 0 upon success, -EINVAL 39088c2ecf20Sopenharmony_ci * if @classname or @fn are NULL, or passes back the error code from @fn. 39098c2ecf20Sopenharmony_ci */ 39108c2ecf20Sopenharmony_ciint omap_hwmod_for_each_by_class(const char *classname, 39118c2ecf20Sopenharmony_ci int (*fn)(struct omap_hwmod *oh, 39128c2ecf20Sopenharmony_ci void *user), 39138c2ecf20Sopenharmony_ci void *user) 39148c2ecf20Sopenharmony_ci{ 39158c2ecf20Sopenharmony_ci struct omap_hwmod *temp_oh; 39168c2ecf20Sopenharmony_ci int ret = 0; 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci if (!classname || !fn) 39198c2ecf20Sopenharmony_ci return -EINVAL; 39208c2ecf20Sopenharmony_ci 39218c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: looking for modules of class %s\n", 39228c2ecf20Sopenharmony_ci __func__, classname); 39238c2ecf20Sopenharmony_ci 39248c2ecf20Sopenharmony_ci list_for_each_entry(temp_oh, &omap_hwmod_list, node) { 39258c2ecf20Sopenharmony_ci if (!strcmp(temp_oh->class->name, classname)) { 39268c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: %s: calling callback fn\n", 39278c2ecf20Sopenharmony_ci __func__, temp_oh->name); 39288c2ecf20Sopenharmony_ci ret = (*fn)(temp_oh, user); 39298c2ecf20Sopenharmony_ci if (ret) 39308c2ecf20Sopenharmony_ci break; 39318c2ecf20Sopenharmony_ci } 39328c2ecf20Sopenharmony_ci } 39338c2ecf20Sopenharmony_ci 39348c2ecf20Sopenharmony_ci if (ret) 39358c2ecf20Sopenharmony_ci pr_debug("omap_hwmod: %s: iterator terminated early: %d\n", 39368c2ecf20Sopenharmony_ci __func__, ret); 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ci return ret; 39398c2ecf20Sopenharmony_ci} 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_ci/** 39428c2ecf20Sopenharmony_ci * omap_hwmod_set_postsetup_state - set the post-_setup() state for this hwmod 39438c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 39448c2ecf20Sopenharmony_ci * @state: state that _setup() should leave the hwmod in 39458c2ecf20Sopenharmony_ci * 39468c2ecf20Sopenharmony_ci * Sets the hwmod state that @oh will enter at the end of _setup() 39478c2ecf20Sopenharmony_ci * (called by omap_hwmod_setup_*()). See also the documentation 39488c2ecf20Sopenharmony_ci * for _setup_postsetup(), above. Returns 0 upon success or 39498c2ecf20Sopenharmony_ci * -EINVAL if there is a problem with the arguments or if the hwmod is 39508c2ecf20Sopenharmony_ci * in the wrong state. 39518c2ecf20Sopenharmony_ci */ 39528c2ecf20Sopenharmony_ciint omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state) 39538c2ecf20Sopenharmony_ci{ 39548c2ecf20Sopenharmony_ci int ret; 39558c2ecf20Sopenharmony_ci unsigned long flags; 39568c2ecf20Sopenharmony_ci 39578c2ecf20Sopenharmony_ci if (!oh) 39588c2ecf20Sopenharmony_ci return -EINVAL; 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ci if (state != _HWMOD_STATE_DISABLED && 39618c2ecf20Sopenharmony_ci state != _HWMOD_STATE_ENABLED && 39628c2ecf20Sopenharmony_ci state != _HWMOD_STATE_IDLE) 39638c2ecf20Sopenharmony_ci return -EINVAL; 39648c2ecf20Sopenharmony_ci 39658c2ecf20Sopenharmony_ci spin_lock_irqsave(&oh->_lock, flags); 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci if (oh->_state != _HWMOD_STATE_REGISTERED) { 39688c2ecf20Sopenharmony_ci ret = -EINVAL; 39698c2ecf20Sopenharmony_ci goto ohsps_unlock; 39708c2ecf20Sopenharmony_ci } 39718c2ecf20Sopenharmony_ci 39728c2ecf20Sopenharmony_ci oh->_postsetup_state = state; 39738c2ecf20Sopenharmony_ci ret = 0; 39748c2ecf20Sopenharmony_ci 39758c2ecf20Sopenharmony_ciohsps_unlock: 39768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&oh->_lock, flags); 39778c2ecf20Sopenharmony_ci 39788c2ecf20Sopenharmony_ci return ret; 39798c2ecf20Sopenharmony_ci} 39808c2ecf20Sopenharmony_ci 39818c2ecf20Sopenharmony_ci/** 39828c2ecf20Sopenharmony_ci * omap_hwmod_get_context_loss_count - get lost context count 39838c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 39848c2ecf20Sopenharmony_ci * 39858c2ecf20Sopenharmony_ci * Returns the context loss count of associated @oh 39868c2ecf20Sopenharmony_ci * upon success, or zero if no context loss data is available. 39878c2ecf20Sopenharmony_ci * 39888c2ecf20Sopenharmony_ci * On OMAP4, this queries the per-hwmod context loss register, 39898c2ecf20Sopenharmony_ci * assuming one exists. If not, or on OMAP2/3, this queries the 39908c2ecf20Sopenharmony_ci * enclosing powerdomain context loss count. 39918c2ecf20Sopenharmony_ci */ 39928c2ecf20Sopenharmony_ciint omap_hwmod_get_context_loss_count(struct omap_hwmod *oh) 39938c2ecf20Sopenharmony_ci{ 39948c2ecf20Sopenharmony_ci struct powerdomain *pwrdm; 39958c2ecf20Sopenharmony_ci int ret = 0; 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_ci if (soc_ops.get_context_lost) 39988c2ecf20Sopenharmony_ci return soc_ops.get_context_lost(oh); 39998c2ecf20Sopenharmony_ci 40008c2ecf20Sopenharmony_ci pwrdm = omap_hwmod_get_pwrdm(oh); 40018c2ecf20Sopenharmony_ci if (pwrdm) 40028c2ecf20Sopenharmony_ci ret = pwrdm_get_context_loss_count(pwrdm); 40038c2ecf20Sopenharmony_ci 40048c2ecf20Sopenharmony_ci return ret; 40058c2ecf20Sopenharmony_ci} 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci/** 40088c2ecf20Sopenharmony_ci * omap_hwmod_init - initialize the hwmod code 40098c2ecf20Sopenharmony_ci * 40108c2ecf20Sopenharmony_ci * Sets up some function pointers needed by the hwmod code to operate on the 40118c2ecf20Sopenharmony_ci * currently-booted SoC. Intended to be called once during kernel init 40128c2ecf20Sopenharmony_ci * before any hwmods are registered. No return value. 40138c2ecf20Sopenharmony_ci */ 40148c2ecf20Sopenharmony_civoid __init omap_hwmod_init(void) 40158c2ecf20Sopenharmony_ci{ 40168c2ecf20Sopenharmony_ci if (cpu_is_omap24xx()) { 40178c2ecf20Sopenharmony_ci soc_ops.wait_target_ready = _omap2xxx_3xxx_wait_target_ready; 40188c2ecf20Sopenharmony_ci soc_ops.assert_hardreset = _omap2_assert_hardreset; 40198c2ecf20Sopenharmony_ci soc_ops.deassert_hardreset = _omap2_deassert_hardreset; 40208c2ecf20Sopenharmony_ci soc_ops.is_hardreset_asserted = _omap2_is_hardreset_asserted; 40218c2ecf20Sopenharmony_ci } else if (cpu_is_omap34xx()) { 40228c2ecf20Sopenharmony_ci soc_ops.wait_target_ready = _omap2xxx_3xxx_wait_target_ready; 40238c2ecf20Sopenharmony_ci soc_ops.assert_hardreset = _omap2_assert_hardreset; 40248c2ecf20Sopenharmony_ci soc_ops.deassert_hardreset = _omap2_deassert_hardreset; 40258c2ecf20Sopenharmony_ci soc_ops.is_hardreset_asserted = _omap2_is_hardreset_asserted; 40268c2ecf20Sopenharmony_ci soc_ops.init_clkdm = _init_clkdm; 40278c2ecf20Sopenharmony_ci } else if (cpu_is_omap44xx() || soc_is_omap54xx() || soc_is_dra7xx()) { 40288c2ecf20Sopenharmony_ci soc_ops.enable_module = _omap4_enable_module; 40298c2ecf20Sopenharmony_ci soc_ops.disable_module = _omap4_disable_module; 40308c2ecf20Sopenharmony_ci soc_ops.wait_target_ready = _omap4_wait_target_ready; 40318c2ecf20Sopenharmony_ci soc_ops.assert_hardreset = _omap4_assert_hardreset; 40328c2ecf20Sopenharmony_ci soc_ops.deassert_hardreset = _omap4_deassert_hardreset; 40338c2ecf20Sopenharmony_ci soc_ops.is_hardreset_asserted = _omap4_is_hardreset_asserted; 40348c2ecf20Sopenharmony_ci soc_ops.init_clkdm = _init_clkdm; 40358c2ecf20Sopenharmony_ci soc_ops.update_context_lost = _omap4_update_context_lost; 40368c2ecf20Sopenharmony_ci soc_ops.get_context_lost = _omap4_get_context_lost; 40378c2ecf20Sopenharmony_ci soc_ops.disable_direct_prcm = _omap4_disable_direct_prcm; 40388c2ecf20Sopenharmony_ci soc_ops.xlate_clkctrl = _omap4_xlate_clkctrl; 40398c2ecf20Sopenharmony_ci } else if (cpu_is_ti814x() || cpu_is_ti816x() || soc_is_am33xx() || 40408c2ecf20Sopenharmony_ci soc_is_am43xx()) { 40418c2ecf20Sopenharmony_ci soc_ops.enable_module = _omap4_enable_module; 40428c2ecf20Sopenharmony_ci soc_ops.disable_module = _omap4_disable_module; 40438c2ecf20Sopenharmony_ci soc_ops.wait_target_ready = _omap4_wait_target_ready; 40448c2ecf20Sopenharmony_ci soc_ops.assert_hardreset = _omap4_assert_hardreset; 40458c2ecf20Sopenharmony_ci soc_ops.deassert_hardreset = _am33xx_deassert_hardreset; 40468c2ecf20Sopenharmony_ci soc_ops.is_hardreset_asserted = _omap4_is_hardreset_asserted; 40478c2ecf20Sopenharmony_ci soc_ops.init_clkdm = _init_clkdm; 40488c2ecf20Sopenharmony_ci soc_ops.disable_direct_prcm = _omap4_disable_direct_prcm; 40498c2ecf20Sopenharmony_ci soc_ops.xlate_clkctrl = _omap4_xlate_clkctrl; 40508c2ecf20Sopenharmony_ci } else { 40518c2ecf20Sopenharmony_ci WARN(1, "omap_hwmod: unknown SoC type\n"); 40528c2ecf20Sopenharmony_ci } 40538c2ecf20Sopenharmony_ci 40548c2ecf20Sopenharmony_ci _init_clkctrl_providers(); 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci inited = true; 40578c2ecf20Sopenharmony_ci} 40588c2ecf20Sopenharmony_ci 40598c2ecf20Sopenharmony_ci/** 40608c2ecf20Sopenharmony_ci * omap_hwmod_get_main_clk - get pointer to main clock name 40618c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod * 40628c2ecf20Sopenharmony_ci * 40638c2ecf20Sopenharmony_ci * Returns the main clock name assocated with @oh upon success, 40648c2ecf20Sopenharmony_ci * or NULL if @oh is NULL. 40658c2ecf20Sopenharmony_ci */ 40668c2ecf20Sopenharmony_ciconst char *omap_hwmod_get_main_clk(struct omap_hwmod *oh) 40678c2ecf20Sopenharmony_ci{ 40688c2ecf20Sopenharmony_ci if (!oh) 40698c2ecf20Sopenharmony_ci return NULL; 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci return oh->main_clk; 40728c2ecf20Sopenharmony_ci} 4073