diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /arch/arm/mach-omap2 | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
210 files changed, 53218 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/.gitignore b/arch/arm/mach-omap2/.gitignore new file mode 100644 index 000000000..dc7be7556 --- /dev/null +++ b/arch/arm/mach-omap2/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +pm-asm-offsets.h diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig new file mode 100644 index 000000000..3b53dda9e --- /dev/null +++ b/arch/arm/mach-omap2/Kconfig @@ -0,0 +1,281 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "TI OMAP/AM/DM/DRA Family" + depends on ARCH_MULTI_V6 || ARCH_MULTI_V7 + +config OMAP_HWMOD + bool + +config ARCH_OMAP2 + bool "TI OMAP2" + depends on ARCH_MULTI_V6 + select ARCH_OMAP2PLUS + select CPU_V6 + select OMAP_HWMOD + select SOC_HAS_OMAP2_SDRC + +config ARCH_OMAP3 + bool "TI OMAP3" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS + select ARM_CPU_SUSPEND + select OMAP_HWMOD + select OMAP_INTERCONNECT + select PM_OPP + select SOC_HAS_OMAP2_SDRC + select ARM_ERRATA_430973 + +config ARCH_OMAP4 + bool "TI OMAP4" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS + select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP + select ARM_CPU_SUSPEND + select ARM_ERRATA_720789 + select ARM_GIC + select HAVE_ARM_SCU if SMP + select HAVE_ARM_TWD if SMP + select OMAP_INTERCONNECT + select OMAP_INTERCONNECT_BARRIER + select PL310_ERRATA_588369 if CACHE_L2X0 + select PL310_ERRATA_727915 if CACHE_L2X0 + select PM_OPP + select PM if CPU_IDLE + select ARM_ERRATA_754322 + select ARM_ERRATA_775420 + select OMAP_INTERCONNECT + +config SOC_OMAP5 + bool "TI OMAP5" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS + select ARM_CPU_SUSPEND + select ARM_GIC + select HAVE_ARM_SCU if SMP + select HAVE_ARM_ARCH_TIMER + select ARM_ERRATA_798181 if SMP + select OMAP_INTERCONNECT + select OMAP_INTERCONNECT_BARRIER + select PM_OPP + select ZONE_DMA if ARM_LPAE + +config SOC_AM33XX + bool "TI AM33XX" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS + select ARM_CPU_SUSPEND + +config SOC_AM43XX + bool "TI AM43x" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS + select ARM_GIC + select MACH_OMAP_GENERIC + select HAVE_ARM_SCU + select GENERIC_CLOCKEVENTS_BROADCAST + select HAVE_ARM_TWD + select ARM_ERRATA_754322 + select ARM_ERRATA_775420 + select OMAP_INTERCONNECT + select ARM_CPU_SUSPEND + +config SOC_DRA7XX + bool "TI DRA7XX" + depends on ARCH_MULTI_V7 + select ARCH_OMAP2PLUS + select ARM_CPU_SUSPEND + select ARM_GIC + select HAVE_ARM_SCU if SMP + select HAVE_ARM_ARCH_TIMER + select IRQ_CROSSBAR + select ARM_ERRATA_798181 if SMP + select OMAP_INTERCONNECT + select OMAP_INTERCONNECT_BARRIER + select PM_OPP + select ZONE_DMA if ARM_LPAE + select PINCTRL_TI_IODELAY if OF && PINCTRL + +config ARCH_OMAP2PLUS + bool + select ARCH_HAS_BANDGAP + select ARCH_HAS_RESET_CONTROLLER + select ARCH_OMAP + select CLKSRC_MMIO + select GENERIC_IRQ_CHIP + select GPIOLIB + select MACH_OMAP_GENERIC + select MEMORY + select MFD_SYSCON + select OMAP_DM_SYSTIMER + select OMAP_DM_TIMER + select OMAP_GPMC + select PINCTRL + select PM + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF + select RESET_CONTROLLER + select SOC_BUS + select TI_SYSC + select OMAP_IRQCHIP + select CLKSRC_TI_32K + help + Systems based on OMAP2, OMAP3, OMAP4 or OMAP5 + +config OMAP_INTERCONNECT_BARRIER + bool + select ARM_HEAVY_MB + +config ARCH_OMAP + bool + +if ARCH_OMAP2PLUS + +menu "TI OMAP2/3/4 Specific Features" + +config ARCH_OMAP2PLUS_TYPICAL + bool "Typical OMAP configuration" + default y + select AEABI + select HIGHMEM + select I2C + select I2C_OMAP + select MENELAUS if ARCH_OMAP2 + select NEON if CPU_V7 + select REGULATOR + select REGULATOR_FIXED_VOLTAGE + select TWL4030_CORE if ARCH_OMAP3 || ARCH_OMAP4 + select TWL4030_POWER if ARCH_OMAP3 || ARCH_OMAP4 + select VFP + help + Compile a kernel suitable for booting most boards + +config SOC_HAS_OMAP2_SDRC + bool "OMAP2 SDRAM Controller support" + +config SOC_HAS_REALTIME_COUNTER + bool "Real time free running counter" + depends on SOC_OMAP5 || SOC_DRA7XX + default y + +config POWER_AVS_OMAP + bool "AVS(Adaptive Voltage Scaling) support for OMAP IP versions 1&2" + depends on (ARCH_OMAP3 || ARCH_OMAP4) && PM + select POWER_SUPPLY + help + Say Y to enable AVS(Adaptive Voltage Scaling) + support on OMAP containing the version 1 or + version 2 of the SmartReflex IP. + V1 is the 65nm version used in OMAP3430. + V2 is the update for the 45nm version of the IP used in OMAP3630 + and OMAP4430 + + Please note, that by default SmartReflex is only + initialized and not enabled. To enable the automatic voltage + compensation for vdd mpu and vdd core from user space, + user must write 1 to + /debug/smartreflex/sr_<X>/autocomp, + where X is mpu_iva or core for OMAP3. + Optionally autocompensation can be enabled in the kernel + by default during system init via the enable_on_init flag + which an be passed as platform data to the smartreflex driver. + +config POWER_AVS_OMAP_CLASS3 + bool "Class 3 mode of Smartreflex Implementation" + depends on POWER_AVS_OMAP && TWL4030_CORE + help + Say Y to enable Class 3 implementation of Smartreflex + + Class 3 implementation of Smartreflex employs continuous hardware + voltage calibration. + +config OMAP3_L2_AUX_SECURE_SAVE_RESTORE + bool "OMAP3 HS/EMU save and restore for L2 AUX control register" + depends on ARCH_OMAP3 && PM + help + Without this option, L2 Auxiliary control register contents are + lost during off-mode entry on HS/EMU devices. This feature + requires support from PPA / boot-loader in HS/EMU devices, which + currently does not exist by default. + +config OMAP3_L2_AUX_SECURE_SERVICE_SET_ID + int "Service ID for the support routine to set L2 AUX control" + depends on OMAP3_L2_AUX_SECURE_SAVE_RESTORE + default 43 + help + PPA routine service ID for setting L2 auxiliary control register. + +comment "OMAP Core Type" + depends on ARCH_OMAP2 + +config SOC_OMAP2420 + bool "OMAP2420 support" + depends on ARCH_OMAP2 + default y + select OMAP_DM_SYSTIMER + select OMAP_DM_TIMER + select SOC_HAS_OMAP2_SDRC + +config SOC_OMAP2430 + bool "OMAP2430 support" + depends on ARCH_OMAP2 + default y + select SOC_HAS_OMAP2_SDRC + +config SOC_OMAP3430 + bool "OMAP3430 support" + depends on ARCH_OMAP3 + default y + select SOC_HAS_OMAP2_SDRC + +config SOC_TI81XX + bool "TI81XX support" + depends on ARCH_OMAP3 + default y + +comment "OMAP Legacy Platform Data Board Type" + depends on ARCH_OMAP2PLUS + +config MACH_OMAP_GENERIC + bool + +config MACH_OMAP2_TUSB6010 + bool + depends on ARCH_OMAP2 && SOC_OMAP2420 + default y if MACH_NOKIA_N8X0 + +config MACH_NOKIA_N810 + bool + +config MACH_NOKIA_N810_WIMAX + bool + +config MACH_NOKIA_N8X0 + bool "Nokia N800/N810" + depends on SOC_OMAP2420 + default y + select MACH_NOKIA_N810 + select MACH_NOKIA_N810_WIMAX + +config OMAP3_SDRC_AC_TIMING + bool "Enable SDRC AC timing register changes" + depends on ARCH_OMAP3 + help + If you know that none of your system initiators will attempt to + access SDRAM during CORE DVFS, select Y here. This should boost + SDRAM performance at lower CORE OPPs. There are relatively few + users who will wish to say yes at this point - almost everyone will + wish to say no. Selecting yes without understanding what is + going on could result in system crashes; + +endmenu + +endif + +config OMAP5_ERRATA_801819 + bool "Errata 801819: An eviction from L1 data cache might stall indefinitely" + depends on SOC_OMAP5 || SOC_DRA7XX + help + A livelock can occur in the L2 cache arbitration that might prevent + a snoop from completing. Under certain conditions this can cause the + system to deadlock. + +endmenu diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile new file mode 100644 index 000000000..2feb9f663 --- /dev/null +++ b/arch/arm/mach-omap2/Makefile @@ -0,0 +1,229 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux kernel. +# + +# Common support +obj-y := id.o io.o control.o devices.o fb.o pm.o \ + common.o dma.o omap-headsmp.o sram.o + +hwmod-common = omap_hwmod.o omap_hwmod_reset.o \ + omap_hwmod_common_data.o \ + omap_hwmod_common_ipblock_data.o \ + omap_device.o display.o hdq1w.o \ + i2c.o wd_timer.o +clock-common = clock.o +secure-common = omap-smc.o omap-secure.o + +obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common) +obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) $(secure-common) +obj-$(CONFIG_ARCH_OMAP4) += $(secure-common) +obj-$(CONFIG_SOC_AM33XX) += $(secure-common) +obj-$(CONFIG_SOC_OMAP5) += $(secure-common) +obj-$(CONFIG_SOC_AM43XX) += $(secure-common) +obj-$(CONFIG_SOC_DRA7XX) += $(secure-common) + +ifneq ($(CONFIG_SND_SOC_OMAP_MCBSP),) +obj-$(CONFIG_OMAP_HWMOD) += mcbsp.o +endif + +obj-$(CONFIG_TWL4030_CORE) += omap_twl.o + +ifneq ($(CONFIG_MFD_CPCAP),) +obj-y += pmic-cpcap.o +endif + +obj-$(CONFIG_SOC_HAS_OMAP2_SDRC) += sdrc.o + +# SMP support ONLY available for OMAP4 + +smp-$(CONFIG_SMP) += omap-smp.o +smp-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o +omap-4-5-common = omap4-common.o omap-wakeupgen.o +obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-common) $(smp-y) sleep44xx.o +obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-common) $(smp-y) sleep44xx.o +obj-$(CONFIG_SOC_AM43XX) += $(omap-4-5-common) +obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-common) $(smp-y) sleep44xx.o + +omap5-dra7-common-$(CONFIG_SOC_HAS_REALTIME_COUNTER) = timer.o +obj-$(CONFIG_SOC_OMAP5) += $(omap5-dra7-common-y) +obj-$(CONFIG_SOC_DRA7XX) += $(omap5-dra7-common-y) + +# Functions loaded to SRAM +obj-$(CONFIG_SOC_OMAP2420) += sram242x.o +obj-$(CONFIG_SOC_OMAP2430) += sram243x.o + +# Restart code (OMAP4/5 currently in omap4-common.c) +obj-$(CONFIG_SOC_OMAP2420) += omap2-restart.o +obj-$(CONFIG_SOC_OMAP2430) += omap2-restart.o +obj-$(CONFIG_SOC_TI81XX) += ti81xx-restart.o +obj-$(CONFIG_SOC_AM33XX) += am33xx-restart.o +obj-$(CONFIG_SOC_AM43XX) += omap4-restart.o +obj-$(CONFIG_ARCH_OMAP3) += omap3-restart.o +obj-$(CONFIG_ARCH_OMAP4) += omap4-restart.o +obj-$(CONFIG_SOC_OMAP5) += omap4-restart.o +obj-$(CONFIG_SOC_DRA7XX) += omap4-restart.o + +# SMS/SDRC +obj-$(CONFIG_ARCH_OMAP2) += sdrc2xxx.o +# obj-$(CONFIG_ARCH_OMAP3) += sdrc3xxx.o + +# OPP table initialization +ifeq ($(CONFIG_PM_OPP),y) +obj-$(CONFIG_ARCH_OMAP3) += opp3xxx_data.o +obj-$(CONFIG_ARCH_OMAP4) += opp4xxx_data.o +endif + +# Power Management +omap-4-5-pm-common = omap-mpuss-lowpower.o +obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common) +obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common) + +ifeq ($(CONFIG_PM),y) +obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o +obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o +obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o +omap-4-5-pm-common += pm44xx.o +obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common) +obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common) +obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-pm-common) +obj-$(CONFIG_SOC_AM33XX) += pm33xx-core.o sleep33xx.o +obj-$(CONFIG_SOC_AM43XX) += pm33xx-core.o sleep43xx.o +obj-$(CONFIG_PM_DEBUG) += pm-debug.o + +obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o +obj-$(CONFIG_POWER_AVS_OMAP_CLASS3) += smartreflex-class3.o + +endif + +ifeq ($(CONFIG_CPU_IDLE),y) +obj-$(CONFIG_ARCH_OMAP3) += cpuidle34xx.o +omap-4-5-idle-common = cpuidle44xx.o +obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-idle-common) +obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-idle-common) +obj-$(CONFIG_SOC_DRA7XX) += $(omap-4-5-idle-common) +endif + +# PRCM +obj-y += prm_common.o cm_common.o +obj-$(CONFIG_ARCH_OMAP2) += prm2xxx_3xxx.o prm2xxx.o cm2xxx.o +obj-$(CONFIG_ARCH_OMAP3) += prm2xxx_3xxx.o prm3xxx.o cm3xxx.o +obj-$(CONFIG_ARCH_OMAP3) += vc3xxx_data.o vp3xxx_data.o +omap-prcm-4-5-common = cminst44xx.o prm44xx.o \ + prcm_mpu44xx.o prminst44xx.o \ + vc44xx_data.o vp44xx_data.o +obj-$(CONFIG_ARCH_OMAP4) += $(omap-prcm-4-5-common) +obj-$(CONFIG_SOC_OMAP5) += $(omap-prcm-4-5-common) +obj-$(CONFIG_SOC_DRA7XX) += $(omap-prcm-4-5-common) +am33xx-43xx-prcm-common += prm33xx.o cm33xx.o +obj-$(CONFIG_SOC_TI81XX) += $(am33xx-43xx-prcm-common) +obj-$(CONFIG_SOC_AM33XX) += $(am33xx-43xx-prcm-common) +obj-$(CONFIG_SOC_AM43XX) += $(omap-prcm-4-5-common) \ + $(am33xx-43xx-prcm-common) + +# OMAP voltage domains +voltagedomain-common := voltage.o vc.o vp.o +obj-$(CONFIG_ARCH_OMAP2) += $(voltagedomain-common) +obj-$(CONFIG_ARCH_OMAP2) += voltagedomains2xxx_data.o +obj-$(CONFIG_ARCH_OMAP3) += $(voltagedomain-common) +obj-$(CONFIG_ARCH_OMAP3) += voltagedomains3xxx_data.o +obj-$(CONFIG_ARCH_OMAP4) += $(voltagedomain-common) +obj-$(CONFIG_ARCH_OMAP4) += voltagedomains44xx_data.o +obj-$(CONFIG_SOC_AM33XX) += $(voltagedomain-common) +obj-$(CONFIG_SOC_AM43XX) += $(voltagedomain-common) +obj-$(CONFIG_SOC_OMAP5) += $(voltagedomain-common) +obj-$(CONFIG_SOC_OMAP5) += voltagedomains54xx_data.o +obj-$(CONFIG_SOC_DRA7XX) += $(voltagedomain-common) + +# OMAP powerdomain framework +powerdomain-common += powerdomain.o powerdomain-common.o +obj-$(CONFIG_ARCH_OMAP2) += $(powerdomain-common) +obj-$(CONFIG_ARCH_OMAP2) += powerdomains2xxx_data.o +obj-$(CONFIG_ARCH_OMAP2) += powerdomains2xxx_3xxx_data.o +obj-$(CONFIG_ARCH_OMAP3) += $(powerdomain-common) +obj-$(CONFIG_ARCH_OMAP3) += powerdomains3xxx_data.o +obj-$(CONFIG_ARCH_OMAP3) += powerdomains2xxx_3xxx_data.o +obj-$(CONFIG_ARCH_OMAP4) += $(powerdomain-common) +obj-$(CONFIG_ARCH_OMAP4) += powerdomains44xx_data.o +obj-$(CONFIG_SOC_AM33XX) += $(powerdomain-common) +obj-$(CONFIG_SOC_AM33XX) += powerdomains33xx_data.o +obj-$(CONFIG_SOC_AM43XX) += $(powerdomain-common) +obj-$(CONFIG_SOC_AM43XX) += powerdomains43xx_data.o +obj-$(CONFIG_SOC_OMAP5) += $(powerdomain-common) +obj-$(CONFIG_SOC_OMAP5) += powerdomains54xx_data.o +obj-$(CONFIG_SOC_DRA7XX) += $(powerdomain-common) +obj-$(CONFIG_SOC_DRA7XX) += powerdomains7xx_data.o + +# PRCM clockdomain control +clockdomain-common += clockdomain.o +obj-$(CONFIG_ARCH_OMAP2) += $(clockdomain-common) +obj-$(CONFIG_ARCH_OMAP2) += clockdomains2xxx_3xxx_data.o +obj-$(CONFIG_SOC_OMAP2420) += clockdomains2420_data.o +obj-$(CONFIG_SOC_OMAP2430) += clockdomains2430_data.o +obj-$(CONFIG_ARCH_OMAP3) += $(clockdomain-common) +obj-$(CONFIG_ARCH_OMAP3) += clockdomains2xxx_3xxx_data.o +obj-$(CONFIG_ARCH_OMAP3) += clockdomains3xxx_data.o +obj-$(CONFIG_ARCH_OMAP4) += $(clockdomain-common) +obj-$(CONFIG_ARCH_OMAP4) += clockdomains44xx_data.o +obj-$(CONFIG_SOC_AM33XX) += $(clockdomain-common) +obj-$(CONFIG_SOC_AM33XX) += clockdomains33xx_data.o +obj-$(CONFIG_SOC_TI81XX) += $(clockdomain-common) +obj-$(CONFIG_SOC_TI81XX) += clockdomains81xx_data.o +obj-$(CONFIG_SOC_AM43XX) += $(clockdomain-common) +obj-$(CONFIG_SOC_AM43XX) += clockdomains43xx_data.o +obj-$(CONFIG_SOC_OMAP5) += $(clockdomain-common) +obj-$(CONFIG_SOC_OMAP5) += clockdomains54xx_data.o +obj-$(CONFIG_SOC_DRA7XX) += $(clockdomain-common) +obj-$(CONFIG_SOC_DRA7XX) += clockdomains7xx_data.o + +# Clock framework +obj-$(CONFIG_ARCH_OMAP2) += $(clock-common) +obj-$(CONFIG_ARCH_OMAP2) += clkt2xxx_dpllcore.o +obj-$(CONFIG_ARCH_OMAP2) += clkt2xxx_virt_prcm_set.o +obj-$(CONFIG_ARCH_OMAP2) += clkt2xxx_dpll.o +obj-$(CONFIG_ARCH_OMAP3) += $(clock-common) +obj-$(CONFIG_ARCH_OMAP4) += $(clock-common) +obj-$(CONFIG_SOC_AM33XX) += $(clock-common) +obj-$(CONFIG_SOC_OMAP5) += $(clock-common) +obj-$(CONFIG_SOC_DRA7XX) += $(clock-common) +obj-$(CONFIG_SOC_AM43XX) += $(clock-common) + +# OMAP2 clock rate set data (old "OPP" data) +obj-$(CONFIG_SOC_OMAP2420) += opp2420_data.o +obj-$(CONFIG_SOC_OMAP2430) += opp2430_data.o + +# hwmod data +obj-$(CONFIG_SOC_OMAP2420) += omap_hwmod_2xxx_ipblock_data.o +obj-$(CONFIG_SOC_OMAP2420) += omap_hwmod_2xxx_3xxx_ipblock_data.o +obj-$(CONFIG_SOC_OMAP2420) += omap_hwmod_2xxx_interconnect_data.o +obj-$(CONFIG_SOC_OMAP2420) += omap_hwmod_2420_data.o +obj-$(CONFIG_SOC_OMAP2430) += omap_hwmod_2xxx_ipblock_data.o +obj-$(CONFIG_SOC_OMAP2430) += omap_hwmod_2xxx_3xxx_ipblock_data.o +obj-$(CONFIG_SOC_OMAP2430) += omap_hwmod_2xxx_interconnect_data.o +obj-$(CONFIG_SOC_OMAP2430) += omap_hwmod_2430_data.o +obj-$(CONFIG_ARCH_OMAP3) += omap_hwmod_2xxx_3xxx_ipblock_data.o +obj-$(CONFIG_ARCH_OMAP3) += omap_hwmod_3xxx_data.o +obj-$(CONFIG_SOC_TI81XX) += omap_hwmod_81xx_data.o + +# OMAP2420 MSDI controller integration support ("MMC") +obj-$(CONFIG_SOC_OMAP2420) += msdi.o + +# Specific board support +obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o pdata-quirks.o +obj-$(CONFIG_MACH_NOKIA_N8X0) += board-n8x0.o + +# Platform specific device init code + +obj-y += omap_phy_internal.o + +obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o + +$(obj)/pm-asm-offsets.h: $(obj)/pm-asm-offsets.s FORCE + $(call filechk,offsets,__TI_PM_ASM_OFFSETS_H__) + +$(obj)/sleep33xx.o $(obj)/sleep43xx.o: $(obj)/pm-asm-offsets.h + +targets += pm-asm-offsets.s +clean-files += pm-asm-offsets.h + +obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o diff --git a/arch/arm/mach-omap2/am33xx-restart.c b/arch/arm/mach-omap2/am33xx-restart.c new file mode 100644 index 000000000..ef2f18a56 --- /dev/null +++ b/arch/arm/mach-omap2/am33xx-restart.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * am33xx-restart.c - Code common to all AM33xx machines. + */ +#include <linux/kernel.h> +#include <linux/reboot.h> + +#include "common.h" +#include "prm.h" + +/** + * am3xx_restart - trigger a software restart of the SoC + * @mode: the "reboot mode", see arch/arm/kernel/{setup,process}.c + * @cmd: passed from the userspace program rebooting the system (if provided) + * + * Resets the SoC. For @cmd, see the 'reboot' syscall in + * kernel/sys.c. No return value. + */ +void am33xx_restart(enum reboot_mode mode, const char *cmd) +{ + /* TODO: Handle mode and cmd if necessary */ + + omap_prm_reset_system(); +} diff --git a/arch/arm/mach-omap2/am33xx.h b/arch/arm/mach-omap2/am33xx.h new file mode 100644 index 000000000..32bcfcf34 --- /dev/null +++ b/arch/arm/mach-omap2/am33xx.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * This file contains the address info for various AM33XX modules. + * + * Copyright (C) 2011 Texas Instruments, Inc. - https://www.ti.com/ + */ + +#ifndef __ASM_ARCH_AM33XX_H +#define __ASM_ARCH_AM33XX_H + +#define L4_SLOW_AM33XX_BASE 0x48000000 + +#define AM33XX_SCM_BASE 0x44E10000 +#define AM33XX_CTRL_BASE AM33XX_SCM_BASE +#define AM33XX_PRCM_BASE 0x44E00000 +#define AM43XX_PRCM_BASE 0x44DF0000 +#define AM33XX_TAP_BASE (AM33XX_CTRL_BASE + 0x3FC) + +#endif /* __ASM_ARCH_AM33XX_H */ diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c new file mode 100644 index 000000000..10d2f078e --- /dev/null +++ b/arch/arm/mach-omap2/board-generic.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2005 Nokia Corporation + * Author: Paul Mundt <paul.mundt@nokia.com> + * + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ + * + * Modified from the original mach-omap/omap2/board-generic.c did by Paul + * to support the OMAP2+ device tree boards with an unique board file. + */ +#include <linux/io.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/irqdomain.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> + +#include <asm/setup.h> +#include <asm/mach/arch.h> +#include <asm/system_info.h> + +#include "common.h" + +static const struct of_device_id omap_dt_match_table[] __initconst = { + { .compatible = "simple-bus", }, + { .compatible = "ti,omap-infra", }, + { } +}; + +static void __init __maybe_unused omap_generic_init(void) +{ + pdata_quirks_init(omap_dt_match_table); + omap_soc_device_init(); +} + +/* Clocks are needed early, see drivers/clocksource for the rest */ +static void __init __maybe_unused omap_init_time_of(void) +{ + omap_clk_init(); + timer_probe(); +} + +/* Used by am437x for ARM timer in non-SMP configurations */ +#if !defined(CONFIG_SMP) && defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) +void tick_broadcast(const struct cpumask *mask) +{ +} +#endif + +#ifdef CONFIG_SOC_OMAP2420 +static const char *const omap242x_boards_compat[] __initconst = { + "ti,omap2420", + NULL, +}; + +DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = omap242x_map_io, + .init_early = omap2420_init_early, + .init_machine = omap_generic_init, + .init_time = omap_init_time_of, + .dt_compat = omap242x_boards_compat, + .restart = omap2xxx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_SOC_OMAP2430 +static const char *const omap243x_boards_compat[] __initconst = { + "ti,omap2430", + NULL, +}; + +DT_MACHINE_START(OMAP243X_DT, "Generic OMAP2430 (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = omap243x_map_io, + .init_early = omap2430_init_early, + .init_machine = omap_generic_init, + .init_time = omap_init_time_of, + .dt_compat = omap243x_boards_compat, + .restart = omap2xxx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_ARCH_OMAP3 +/* Some boards need board name for legacy userspace in /proc/cpuinfo */ +static const char *const n900_boards_compat[] __initconst = { + "nokia,omap3-n900", + NULL, +}; + +/* Set system_rev from atags */ +static void __init rx51_set_system_rev(const struct tag *tags) +{ + const struct tag *tag; + + if (tags->hdr.tag != ATAG_CORE) + return; + + for_each_tag(tag, tags) { + if (tag->hdr.tag == ATAG_REVISION) { + system_rev = tag->u.revision.rev; + break; + } + } +} + +/* Legacy userspace on Nokia N900 needs ATAGS exported in /proc/atags, + * save them while the data is still not overwritten + */ +static void __init rx51_reserve(void) +{ + const struct tag *tags = (const struct tag *)(PAGE_OFFSET + 0x100); + + save_atags(tags); + rx51_set_system_rev(tags); + omap_reserve(); +} + +DT_MACHINE_START(OMAP3_N900_DT, "Nokia RX-51 board") + .reserve = rx51_reserve, + .map_io = omap3_map_io, + .init_early = omap3430_init_early, + .init_machine = omap_generic_init, + .init_late = omap3_init_late, + .init_time = omap_init_time_of, + .dt_compat = n900_boards_compat, + .restart = omap3xxx_restart, +MACHINE_END + +/* Generic omap3 boards, most boards can use these */ +static const char *const omap3_boards_compat[] __initconst = { + "ti,omap3430", + "ti,omap3", + NULL, +}; + +DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = omap3_map_io, + .init_early = omap3430_init_early, + .init_machine = omap_generic_init, + .init_late = omap3_init_late, + .init_time = omap_init_time_of, + .dt_compat = omap3_boards_compat, + .restart = omap3xxx_restart, +MACHINE_END + +static const char *const omap36xx_boards_compat[] __initconst = { + "ti,omap3630", + "ti,omap36xx", + NULL, +}; + +DT_MACHINE_START(OMAP36XX_DT, "Generic OMAP36xx (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = omap3_map_io, + .init_early = omap3630_init_early, + .init_machine = omap_generic_init, + .init_late = omap3_init_late, + .init_time = omap_init_time_of, + .dt_compat = omap36xx_boards_compat, + .restart = omap3xxx_restart, +MACHINE_END + +static const char *const omap3_gp_boards_compat[] __initconst = { + "ti,omap3-beagle", + "timll,omap3-devkit8000", + NULL, +}; + +DT_MACHINE_START(OMAP3_GP_DT, "Generic OMAP3-GP (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = omap3_map_io, + .init_early = omap3430_init_early, + .init_machine = omap_generic_init, + .init_late = omap3_init_late, + .init_time = omap_init_time_of, + .dt_compat = omap3_gp_boards_compat, + .restart = omap3xxx_restart, +MACHINE_END + +static const char *const am3517_boards_compat[] __initconst = { + "ti,am3517", + NULL, +}; + +DT_MACHINE_START(AM3517_DT, "Generic AM3517 (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = omap3_map_io, + .init_early = am35xx_init_early, + .init_machine = omap_generic_init, + .init_late = omap3_init_late, + .init_time = omap_init_time_of, + .dt_compat = am3517_boards_compat, + .restart = omap3xxx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_SOC_TI81XX +static const char *const ti814x_boards_compat[] __initconst = { + "ti,dm8148", + "ti,dm814", + NULL, +}; + +DT_MACHINE_START(TI814X_DT, "Generic ti814x (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = ti81xx_map_io, + .init_early = ti814x_init_early, + .init_machine = omap_generic_init, + .init_late = ti81xx_init_late, + .init_time = omap_init_time_of, + .dt_compat = ti814x_boards_compat, + .restart = ti81xx_restart, +MACHINE_END + +static const char *const ti816x_boards_compat[] __initconst = { + "ti,dm8168", + "ti,dm816", + NULL, +}; + +DT_MACHINE_START(TI816X_DT, "Generic ti816x (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = ti81xx_map_io, + .init_early = ti816x_init_early, + .init_machine = omap_generic_init, + .init_late = ti81xx_init_late, + .init_time = omap_init_time_of, + .dt_compat = ti816x_boards_compat, + .restart = ti81xx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_SOC_AM33XX +static const char *const am33xx_boards_compat[] __initconst = { + "ti,am33xx", + NULL, +}; + +DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)") + .reserve = omap_reserve, + .map_io = am33xx_map_io, + .init_early = am33xx_init_early, + .init_machine = omap_generic_init, + .init_late = am33xx_init_late, + .init_time = omap_init_time_of, + .dt_compat = am33xx_boards_compat, + .restart = am33xx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_ARCH_OMAP4 +static const char *const omap4_boards_compat[] __initconst = { + "ti,omap4460", + "ti,omap4430", + "ti,omap4", + NULL, +}; + +DT_MACHINE_START(OMAP4_DT, "Generic OMAP4 (Flattened Device Tree)") + .l2c_aux_val = OMAP_L2C_AUX_CTRL, + .l2c_aux_mask = 0xcf9fffff, + .l2c_write_sec = omap4_l2c310_write_sec, + .reserve = omap_reserve, + .smp = smp_ops(omap4_smp_ops), + .map_io = omap4_map_io, + .init_early = omap4430_init_early, + .init_irq = omap_gic_of_init, + .init_machine = omap_generic_init, + .init_late = omap4430_init_late, + .init_time = omap_init_time_of, + .dt_compat = omap4_boards_compat, + .restart = omap44xx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_SOC_OMAP5 +static const char *const omap5_boards_compat[] __initconst = { + "ti,omap5432", + "ti,omap5430", + "ti,omap5", + NULL, +}; + +DT_MACHINE_START(OMAP5_DT, "Generic OMAP5 (Flattened Device Tree)") +#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE) + .dma_zone_size = SZ_2G, +#endif + .reserve = omap_reserve, + .smp = smp_ops(omap4_smp_ops), + .map_io = omap5_map_io, + .init_early = omap5_init_early, + .init_irq = omap_gic_of_init, + .init_machine = omap_generic_init, + .init_late = omap5_init_late, + .init_time = omap5_realtime_timer_init, + .dt_compat = omap5_boards_compat, + .restart = omap44xx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_SOC_AM43XX +static const char *const am43_boards_compat[] __initconst = { + "ti,am4372", + "ti,am43", + NULL, +}; + +DT_MACHINE_START(AM43_DT, "Generic AM43 (Flattened Device Tree)") + .l2c_aux_val = OMAP_L2C_AUX_CTRL, + .l2c_aux_mask = 0xcf9fffff, + .l2c_write_sec = omap4_l2c310_write_sec, + .map_io = am33xx_map_io, + .init_early = am43xx_init_early, + .init_late = am43xx_init_late, + .init_irq = omap_gic_of_init, + .init_machine = omap_generic_init, + .init_time = omap_init_time_of, + .dt_compat = am43_boards_compat, + .restart = omap44xx_restart, +MACHINE_END +#endif + +#ifdef CONFIG_SOC_DRA7XX +static const char *const dra74x_boards_compat[] __initconst = { + "ti,dra762", + "ti,am5728", + "ti,am5726", + "ti,dra742", + "ti,dra7", + NULL, +}; + +DT_MACHINE_START(DRA74X_DT, "Generic DRA74X (Flattened Device Tree)") +#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE) + .dma_zone_size = SZ_2G, +#endif + .reserve = omap_reserve, + .smp = smp_ops(omap4_smp_ops), + .map_io = dra7xx_map_io, + .init_early = dra7xx_init_early, + .init_late = dra7xx_init_late, + .init_irq = omap_gic_of_init, + .init_machine = omap_generic_init, + .init_time = omap5_realtime_timer_init, + .dt_compat = dra74x_boards_compat, + .restart = omap44xx_restart, +MACHINE_END + +static const char *const dra72x_boards_compat[] __initconst = { + "ti,am5718", + "ti,am5716", + "ti,dra722", + "ti,dra718", + NULL, +}; + +DT_MACHINE_START(DRA72X_DT, "Generic DRA72X (Flattened Device Tree)") +#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE) + .dma_zone_size = SZ_2G, +#endif + .reserve = omap_reserve, + .map_io = dra7xx_map_io, + .init_early = dra7xx_init_early, + .init_late = dra7xx_init_late, + .init_irq = omap_gic_of_init, + .init_machine = omap_generic_init, + .init_time = omap5_realtime_timer_init, + .dt_compat = dra72x_boards_compat, + .restart = omap44xx_restart, +MACHINE_END +#endif diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c new file mode 100644 index 000000000..5e86145db --- /dev/null +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/board-n8x0.c + * + * Copyright (C) 2005-2009 Nokia Corporation + * Author: Juha Yrjola <juha.yrjola@nokia.com> + * + * Modified from mach-omap2/board-generic.c + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/stddef.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/usb/musb.h> +#include <linux/mmc/host.h> +#include <linux/platform_data/spi-omap2-mcspi.h> +#include <linux/platform_data/mmc-omap.h> +#include <linux/mfd/menelaus.h> +#include <sound/tlv320aic3x.h> + +#include <asm/mach/arch.h> +#include <asm/mach-types.h> + +#include "common.h" +#include "mmc.h" +#include "soc.h" +#include "common-board-devices.h" + +#define TUSB6010_ASYNC_CS 1 +#define TUSB6010_SYNC_CS 4 +#define TUSB6010_GPIO_INT 58 +#define TUSB6010_GPIO_ENABLE 0 +#define TUSB6010_DMACHAN 0x3f + +#define NOKIA_N810_WIMAX (1 << 2) +#define NOKIA_N810 (1 << 1) +#define NOKIA_N800 (1 << 0) + +static u32 board_caps; + +#define board_is_n800() (board_caps & NOKIA_N800) +#define board_is_n810() (board_caps & NOKIA_N810) +#define board_is_n810_wimax() (board_caps & NOKIA_N810_WIMAX) + +static void board_check_revision(void) +{ + if (of_machine_is_compatible("nokia,n800")) + board_caps = NOKIA_N800; + else if (of_machine_is_compatible("nokia,n810")) + board_caps = NOKIA_N810; + else if (of_machine_is_compatible("nokia,n810-wimax")) + board_caps = NOKIA_N810_WIMAX; + + if (!board_caps) + pr_err("Unknown board\n"); +} + +#if IS_ENABLED(CONFIG_USB_MUSB_TUSB6010) +/* + * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and + * 1.5 V voltage regulators of PM companion chip. Companion chip will then + * provide then PGOOD signal to TUSB6010 which will release it from reset. + */ +static int tusb_set_power(int state) +{ + int i, retval = 0; + + if (state) { + gpio_set_value(TUSB6010_GPIO_ENABLE, 1); + msleep(1); + + /* Wait until TUSB6010 pulls INT pin down */ + i = 100; + while (i && gpio_get_value(TUSB6010_GPIO_INT)) { + msleep(1); + i--; + } + + if (!i) { + printk(KERN_ERR "tusb: powerup failed\n"); + retval = -ENODEV; + } + } else { + gpio_set_value(TUSB6010_GPIO_ENABLE, 0); + msleep(10); + } + + return retval; +} + +static struct musb_hdrc_config musb_config = { + .multipoint = 1, + .dyn_fifo = 1, + .num_eps = 16, + .ram_bits = 12, +}; + +static struct musb_hdrc_platform_data tusb_data = { + .mode = MUSB_OTG, + .set_power = tusb_set_power, + .min_power = 25, /* x2 = 50 mA drawn from VBUS as peripheral */ + .power = 100, /* Max 100 mA VBUS for host mode */ + .config = &musb_config, +}; + +static void __init n8x0_usb_init(void) +{ + int ret = 0; + static const char announce[] __initconst = KERN_INFO "TUSB 6010\n"; + + /* PM companion chip power control pin */ + ret = gpio_request_one(TUSB6010_GPIO_ENABLE, GPIOF_OUT_INIT_LOW, + "TUSB6010 enable"); + if (ret != 0) { + printk(KERN_ERR "Could not get TUSB power GPIO%i\n", + TUSB6010_GPIO_ENABLE); + return; + } + tusb_set_power(0); + + ret = tusb6010_setup_interface(&tusb_data, TUSB6010_REFCLK_19, 2, + TUSB6010_ASYNC_CS, TUSB6010_SYNC_CS, + TUSB6010_GPIO_INT, TUSB6010_DMACHAN); + if (ret != 0) + goto err; + + printk(announce); + + return; + +err: + gpio_free(TUSB6010_GPIO_ENABLE); +} +#else + +static void __init n8x0_usb_init(void) {} + +#endif /*CONFIG_USB_MUSB_TUSB6010 */ + + +static struct omap2_mcspi_device_config p54spi_mcspi_config = { + .turbo_mode = 0, +}; + +static struct spi_board_info n800_spi_board_info[] __initdata = { + { + .modalias = "p54spi", + .bus_num = 2, + .chip_select = 0, + .max_speed_hz = 48000000, + .controller_data = &p54spi_mcspi_config, + }, +}; + +#if defined(CONFIG_MENELAUS) && IS_ENABLED(CONFIG_MMC_OMAP) + +/* + * On both N800 and N810, only the first of the two MMC controllers is in use. + * The two MMC slots are multiplexed via Menelaus companion chip over I2C. + * On N800, both slots are powered via Menelaus. On N810, only one of the + * slots is powered via Menelaus. The N810 EMMC is powered via GPIO. + * + * VMMC slot 1 on both N800 and N810 + * VDCDC3_APE and VMCS2_APE slot 2 on N800 + * GPIO23 and GPIO9 slot 2 EMMC on N810 + * + */ +#define N8X0_SLOT_SWITCH_GPIO 96 +#define N810_EMMC_VSD_GPIO 23 +#define N810_EMMC_VIO_GPIO 9 + +static int slot1_cover_open; +static int slot2_cover_open; +static struct device *mmc_device; + +static int n8x0_mmc_switch_slot(struct device *dev, int slot) +{ +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Choose slot %d\n", slot + 1); +#endif + gpio_set_value(N8X0_SLOT_SWITCH_GPIO, slot); + return 0; +} + +static int n8x0_mmc_set_power_menelaus(struct device *dev, int slot, + int power_on, int vdd) +{ + int mV; + +#ifdef CONFIG_MMC_DEBUG + dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1, + power_on ? "on" : "off", vdd); +#endif + if (slot == 0) { + if (!power_on) + return menelaus_set_vmmc(0); + switch (1 << vdd) { + case MMC_VDD_33_34: + case MMC_VDD_32_33: + case MMC_VDD_31_32: + mV = 3100; + break; + case MMC_VDD_30_31: + mV = 3000; + break; + case MMC_VDD_28_29: + mV = 2800; + break; + case MMC_VDD_165_195: + mV = 1850; + break; + default: + BUG(); + } + return menelaus_set_vmmc(mV); + } else { + if (!power_on) + return menelaus_set_vdcdc(3, 0); + switch (1 << vdd) { + case MMC_VDD_33_34: + case MMC_VDD_32_33: + mV = 3300; + break; + case MMC_VDD_30_31: + case MMC_VDD_29_30: + mV = 3000; + break; + case MMC_VDD_28_29: + case MMC_VDD_27_28: + mV = 2800; + break; + case MMC_VDD_24_25: + case MMC_VDD_23_24: + mV = 2400; + break; + case MMC_VDD_22_23: + case MMC_VDD_21_22: + mV = 2200; + break; + case MMC_VDD_20_21: + mV = 2000; + break; + case MMC_VDD_165_195: + mV = 1800; + break; + default: + BUG(); + } + return menelaus_set_vdcdc(3, mV); + } + return 0; +} + +static void n810_set_power_emmc(struct device *dev, + int power_on) +{ + dev_dbg(dev, "Set EMMC power %s\n", power_on ? "on" : "off"); + + if (power_on) { + gpio_set_value(N810_EMMC_VSD_GPIO, 1); + msleep(1); + gpio_set_value(N810_EMMC_VIO_GPIO, 1); + msleep(1); + } else { + gpio_set_value(N810_EMMC_VIO_GPIO, 0); + msleep(50); + gpio_set_value(N810_EMMC_VSD_GPIO, 0); + msleep(50); + } +} + +static int n8x0_mmc_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + if (board_is_n800() || slot == 0) + return n8x0_mmc_set_power_menelaus(dev, slot, power_on, vdd); + + n810_set_power_emmc(dev, power_on); + + return 0; +} + +static int n8x0_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode) +{ + int r; + + dev_dbg(dev, "Set slot %d bus mode %s\n", slot + 1, + bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull"); + BUG_ON(slot != 0 && slot != 1); + slot++; + switch (bus_mode) { + case MMC_BUSMODE_OPENDRAIN: + r = menelaus_set_mmc_opendrain(slot, 1); + break; + case MMC_BUSMODE_PUSHPULL: + r = menelaus_set_mmc_opendrain(slot, 0); + break; + default: + BUG(); + } + if (r != 0 && printk_ratelimit()) + dev_err(dev, "MMC: unable to set bus mode for slot %d\n", + slot); + return r; +} + +static int n8x0_mmc_get_cover_state(struct device *dev, int slot) +{ + slot++; + BUG_ON(slot != 1 && slot != 2); + if (slot == 1) + return slot1_cover_open; + else + return slot2_cover_open; +} + +static void n8x0_mmc_callback(void *data, u8 card_mask) +{ +#ifdef CONFIG_MMC_OMAP + int bit, *openp, index; + + if (board_is_n800()) { + bit = 1 << 1; + openp = &slot2_cover_open; + index = 1; + } else { + bit = 1; + openp = &slot1_cover_open; + index = 0; + } + + if (card_mask & bit) + *openp = 1; + else + *openp = 0; + + omap_mmc_notify_cover_event(mmc_device, index, *openp); +#else + pr_warn("MMC: notify cover event not available\n"); +#endif +} + +static int n8x0_mmc_late_init(struct device *dev) +{ + int r, bit, *openp; + int vs2sel; + + mmc_device = dev; + + r = menelaus_set_slot_sel(1); + if (r < 0) + return r; + + if (board_is_n800()) + vs2sel = 0; + else + vs2sel = 2; + + r = menelaus_set_mmc_slot(2, 0, vs2sel, 1); + if (r < 0) + return r; + + n8x0_mmc_set_power(dev, 0, MMC_POWER_ON, 16); /* MMC_VDD_28_29 */ + n8x0_mmc_set_power(dev, 1, MMC_POWER_ON, 16); + + r = menelaus_set_mmc_slot(1, 1, 0, 1); + if (r < 0) + return r; + r = menelaus_set_mmc_slot(2, 1, vs2sel, 1); + if (r < 0) + return r; + + r = menelaus_get_slot_pin_states(); + if (r < 0) + return r; + + if (board_is_n800()) { + bit = 1 << 1; + openp = &slot2_cover_open; + } else { + bit = 1; + openp = &slot1_cover_open; + slot2_cover_open = 0; + } + + /* All slot pin bits seem to be inversed until first switch change */ + if (r == 0xf || r == (0xf & ~bit)) + r = ~r; + + if (r & bit) + *openp = 1; + else + *openp = 0; + + r = menelaus_register_mmc_callback(n8x0_mmc_callback, NULL); + + return r; +} + +static void n8x0_mmc_shutdown(struct device *dev) +{ + int vs2sel; + + if (board_is_n800()) + vs2sel = 0; + else + vs2sel = 2; + + menelaus_set_mmc_slot(1, 0, 0, 0); + menelaus_set_mmc_slot(2, 0, vs2sel, 0); +} + +static void n8x0_mmc_cleanup(struct device *dev) +{ + menelaus_unregister_mmc_callback(); + + gpio_free(N8X0_SLOT_SWITCH_GPIO); + + if (board_is_n810()) { + gpio_free(N810_EMMC_VSD_GPIO); + gpio_free(N810_EMMC_VIO_GPIO); + } +} + +/* + * MMC controller1 has two slots that are multiplexed via I2C. + * MMC controller2 is not in use. + */ +static struct omap_mmc_platform_data mmc1_data = { + .nr_slots = 0, + .switch_slot = n8x0_mmc_switch_slot, + .init = n8x0_mmc_late_init, + .cleanup = n8x0_mmc_cleanup, + .shutdown = n8x0_mmc_shutdown, + .max_freq = 24000000, + .slots[0] = { + .wires = 4, + .set_power = n8x0_mmc_set_power, + .set_bus_mode = n8x0_mmc_set_bus_mode, + .get_cover_state = n8x0_mmc_get_cover_state, + .ocr_mask = MMC_VDD_165_195 | MMC_VDD_30_31 | + MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "internal", + }, + .slots[1] = { + .set_power = n8x0_mmc_set_power, + .set_bus_mode = n8x0_mmc_set_bus_mode, + .get_cover_state = n8x0_mmc_get_cover_state, + .ocr_mask = MMC_VDD_165_195 | MMC_VDD_20_21 | + MMC_VDD_21_22 | MMC_VDD_22_23 | + MMC_VDD_23_24 | MMC_VDD_24_25 | + MMC_VDD_27_28 | MMC_VDD_28_29 | + MMC_VDD_29_30 | MMC_VDD_30_31 | + MMC_VDD_32_33 | MMC_VDD_33_34, + .name = "external", + }, +}; + +static struct omap_mmc_platform_data *mmc_data[OMAP24XX_NR_MMC]; + +static struct gpio n810_emmc_gpios[] __initdata = { + { N810_EMMC_VSD_GPIO, GPIOF_OUT_INIT_LOW, "MMC slot 2 Vddf" }, + { N810_EMMC_VIO_GPIO, GPIOF_OUT_INIT_LOW, "MMC slot 2 Vdd" }, +}; + +static void __init n8x0_mmc_init(void) +{ + int err; + + if (board_is_n810()) { + mmc1_data.slots[0].name = "external"; + + /* + * Some Samsung Movinand chips do not like open-ended + * multi-block reads and fall to braind-dead state + * while doing so. Reducing the number of blocks in + * the transfer or delays in clock disable do not help + */ + mmc1_data.slots[1].name = "internal"; + mmc1_data.slots[1].ban_openended = 1; + } + + err = gpio_request_one(N8X0_SLOT_SWITCH_GPIO, GPIOF_OUT_INIT_LOW, + "MMC slot switch"); + if (err) + return; + + if (board_is_n810()) { + err = gpio_request_array(n810_emmc_gpios, + ARRAY_SIZE(n810_emmc_gpios)); + if (err) { + gpio_free(N8X0_SLOT_SWITCH_GPIO); + return; + } + } + + mmc1_data.nr_slots = 2; + mmc_data[0] = &mmc1_data; +} +#else +static struct omap_mmc_platform_data mmc1_data; +void __init n8x0_mmc_init(void) +{ +} +#endif /* CONFIG_MMC_OMAP */ + +#ifdef CONFIG_MENELAUS + +static int n8x0_auto_sleep_regulators(void) +{ + u32 val; + int ret; + + val = EN_VPLL_SLEEP | EN_VMMC_SLEEP \ + | EN_VAUX_SLEEP | EN_VIO_SLEEP \ + | EN_VMEM_SLEEP | EN_DC3_SLEEP \ + | EN_VC_SLEEP | EN_DC2_SLEEP; + + ret = menelaus_set_regulator_sleep(1, val); + if (ret < 0) { + pr_err("Could not set regulators to sleep on menelaus: %u\n", + ret); + return ret; + } + return 0; +} + +static int n8x0_auto_voltage_scale(void) +{ + int ret; + + ret = menelaus_set_vcore_hw(1400, 1050); + if (ret < 0) { + pr_err("Could not set VCORE voltage on menelaus: %u\n", ret); + return ret; + } + return 0; +} + +static int n8x0_menelaus_late_init(struct device *dev) +{ + int ret; + + ret = n8x0_auto_voltage_scale(); + if (ret < 0) + return ret; + ret = n8x0_auto_sleep_regulators(); + if (ret < 0) + return ret; + return 0; +} + +#else +static int n8x0_menelaus_late_init(struct device *dev) +{ + return 0; +} +#endif + +struct menelaus_platform_data n8x0_menelaus_platform_data = { + .late_init = n8x0_menelaus_late_init, +}; + +struct aic3x_pdata n810_aic33_data = { + .gpio_reset = 118, +}; + +static int __init n8x0_late_initcall(void) +{ + if (!board_caps) + return -ENODEV; + + n8x0_mmc_init(); + n8x0_usb_init(); + + return 0; +} +omap_late_initcall(n8x0_late_initcall); + +/* + * Legacy init pdata init for n8x0. Note that we want to follow the + * I2C bus numbering starting at 0 for device tree like other omaps. + */ +void * __init n8x0_legacy_init(void) +{ + board_check_revision(); + spi_register_board_info(n800_spi_board_info, + ARRAY_SIZE(n800_spi_board_info)); + return &mmc1_data; +} diff --git a/arch/arm/mach-omap2/clkt2xxx_dpll.c b/arch/arm/mach-omap2/clkt2xxx_dpll.c new file mode 100644 index 000000000..0edebf335 --- /dev/null +++ b/arch/arm/mach-omap2/clkt2xxx_dpll.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2-specific DPLL control functions + * + * Copyright (C) 2011 Nokia Corporation + * Paul Walmsley + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include "clock.h" +#include "cm2xxx.h" +#include "cm-regbits-24xx.h" + +/* Private functions */ + +/** + * _allow_idle - enable DPLL autoidle bits + * @clk: struct clk * of the DPLL to operate on + * + * Enable DPLL automatic idle control. The DPLL will enter low-power + * stop when its downstream clocks are gated. No return value. + * REVISIT: DPLL can optionally enter low-power bypass by writing 0x1 + * instead. Add some mechanism to optionally enter this mode. + */ +static void _allow_idle(struct clk_hw_omap *clk) +{ + if (!clk || !clk->dpll_data) + return; + + omap2xxx_cm_set_dpll_auto_low_power_stop(); +} + +/** + * _deny_idle - prevent DPLL from automatically idling + * @clk: struct clk * of the DPLL to operate on + * + * Disable DPLL automatic idle control. No return value. + */ +static void _deny_idle(struct clk_hw_omap *clk) +{ + if (!clk || !clk->dpll_data) + return; + + omap2xxx_cm_set_dpll_disable_autoidle(); +} + + +/* Public data */ +const struct clk_hw_omap_ops clkhwops_omap2xxx_dpll = { + .allow_idle = _allow_idle, + .deny_idle = _deny_idle, +}; diff --git a/arch/arm/mach-omap2/clkt2xxx_dpllcore.c b/arch/arm/mach-omap2/clkt2xxx_dpllcore.c new file mode 100644 index 000000000..8a9983cb4 --- /dev/null +++ b/arch/arm/mach-omap2/clkt2xxx_dpllcore.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DPLL + CORE_CLK composite clock functions + * + * Copyright (C) 2005-2008 Texas Instruments, Inc. + * Copyright (C) 2004-2010 Nokia Corporation + * + * Contacts: + * Richard Woodruff <r-woodruff2@ti.com> + * Paul Walmsley + * + * Based on earlier work by Tuukka Tikkanen, Tony Lindgren, + * Gordon McNutt and RidgeRun, Inc. + * + * XXX The DPLL and CORE clocks should be split into two separate clock + * types. + */ +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include "clock.h" +#include "clock2xxx.h" +#include "opp2xxx.h" +#include "cm2xxx.h" +#include "cm-regbits-24xx.h" +#include "sdrc.h" +#include "sram.h" + +/* #define DOWN_VARIABLE_DPLL 1 */ /* Experimental */ + +/* + * dpll_core_ck: pointer to the combined dpll_ck + core_ck on OMAP2xxx + * (currently defined as "dpll_ck" in the OMAP2xxx clock tree). Set + * during dpll_ck init and used later by omap2xxx_clk_get_core_rate(). + */ +static struct clk_hw_omap *dpll_core_ck; + +/** + * omap2xxx_clk_get_core_rate - return the CORE_CLK rate + * + * Returns the CORE_CLK rate. CORE_CLK can have one of three rate + * sources on OMAP2xxx: the DPLL CLKOUT rate, DPLL CLKOUTX2, or 32KHz + * (the latter is unusual). This currently should be called with + * struct clk *dpll_ck, which is a composite clock of dpll_ck and + * core_ck. + */ +unsigned long omap2xxx_clk_get_core_rate(void) +{ + long long core_clk; + u32 v; + + WARN_ON(!dpll_core_ck); + + core_clk = omap2_get_dpll_rate(dpll_core_ck); + + v = omap2xxx_cm_get_core_clk_src(); + + if (v == CORE_CLK_SRC_32K) + core_clk = 32768; + else + core_clk *= v; + + return core_clk; +} + +/* + * Uses the current prcm set to tell if a rate is valid. + * You can go slower, but not faster within a given rate set. + */ +static long omap2_dpllcore_round_rate(unsigned long target_rate) +{ + u32 high, low, core_clk_src; + + core_clk_src = omap2xxx_cm_get_core_clk_src(); + + if (core_clk_src == CORE_CLK_SRC_DPLL) { /* DPLL clockout */ + high = curr_prcm_set->dpll_speed * 2; + low = curr_prcm_set->dpll_speed; + } else { /* DPLL clockout x 2 */ + high = curr_prcm_set->dpll_speed; + low = curr_prcm_set->dpll_speed / 2; + } + +#ifdef DOWN_VARIABLE_DPLL + if (target_rate > high) + return high; + else + return target_rate; +#else + if (target_rate > low) + return high; + else + return low; +#endif + +} + +unsigned long omap2_dpllcore_recalc(struct clk_hw *hw, + unsigned long parent_rate) +{ + return omap2xxx_clk_get_core_rate(); +} + +int omap2_reprogram_dpllcore(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + u32 cur_rate, low, mult, div, valid_rate, done_rate; + u32 bypass = 0; + struct prcm_config tmpset; + const struct dpll_data *dd; + + cur_rate = omap2xxx_clk_get_core_rate(); + mult = omap2xxx_cm_get_core_clk_src(); + + if ((rate == (cur_rate / 2)) && (mult == 2)) { + omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1); + } else if ((rate == (cur_rate * 2)) && (mult == 1)) { + omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1); + } else if (rate != cur_rate) { + valid_rate = omap2_dpllcore_round_rate(rate); + if (valid_rate != rate) + return -EINVAL; + + if (mult == 1) + low = curr_prcm_set->dpll_speed; + else + low = curr_prcm_set->dpll_speed / 2; + + dd = clk->dpll_data; + if (!dd) + return -EINVAL; + + tmpset.cm_clksel1_pll = + omap_clk_ll_ops.clk_readl(&dd->mult_div1_reg); + tmpset.cm_clksel1_pll &= ~(dd->mult_mask | + dd->div1_mask); + div = ((curr_prcm_set->xtal_speed / 1000000) - 1); + tmpset.cm_clksel2_pll = omap2xxx_cm_get_core_pll_config(); + tmpset.cm_clksel2_pll &= ~OMAP24XX_CORE_CLK_SRC_MASK; + if (rate > low) { + tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL_X2; + mult = ((rate / 2) / 1000000); + done_rate = CORE_CLK_SRC_DPLL_X2; + } else { + tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL; + mult = (rate / 1000000); + done_rate = CORE_CLK_SRC_DPLL; + } + tmpset.cm_clksel1_pll |= (div << __ffs(dd->mult_mask)); + tmpset.cm_clksel1_pll |= (mult << __ffs(dd->div1_mask)); + + /* Worst case */ + tmpset.base_sdrc_rfr = SDRC_RFR_CTRL_BYPASS; + + if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */ + bypass = 1; + + /* For omap2xxx_sdrc_init_params() */ + omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1); + + /* Force dll lock mode */ + omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr, + bypass); + + /* Errata: ret dll entry state */ + omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked()); + omap2xxx_sdrc_reprogram(done_rate, 0); + } + + return 0; +} + +/** + * omap2xxx_clkt_dpllcore_init - clk init function for dpll_ck + * @clk: struct clk *dpll_ck + * + * Store a local copy of @clk in dpll_core_ck so other code can query + * the core rate without having to clk_get(), which can sleep. Must + * only be called once. No return value. XXX If the clock + * registration process is ever changed such that dpll_ck is no longer + * statically defined, this code may need to change to increment some + * kind of use count on dpll_ck. + */ +void omap2xxx_clkt_dpllcore_init(struct clk_hw *hw) +{ + WARN(dpll_core_ck, "dpll_core_ck already set - should never happen"); + dpll_core_ck = to_clk_hw_omap(hw); +} diff --git a/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c new file mode 100644 index 000000000..edf046b47 --- /dev/null +++ b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2xxx DVFS virtual clock functions + * + * Copyright (C) 2005-2008, 2012 Texas Instruments, Inc. + * Copyright (C) 2004-2010 Nokia Corporation + * + * Contacts: + * Richard Woodruff <r-woodruff2@ti.com> + * Paul Walmsley + * + * Based on earlier work by Tuukka Tikkanen, Tony Lindgren, + * Gordon McNutt and RidgeRun, Inc. + * + * XXX Some of this code should be replaceable by the upcoming OPP layer + * code. However, some notion of "rate set" is probably still necessary + * for OMAP2xxx at least. Rate sets should be generalized so they can be + * used for any OMAP chip, not just OMAP2xxx. In particular, Richard Woodruff + * has in the past expressed a preference to use rate sets for OPP changes, + * rather than dynamically recalculating the clock tree, so if someone wants + * this badly enough to write the code to handle it, we should support it + * as an option. + */ +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/cpufreq.h> +#include <linux/slab.h> + +#include "soc.h" +#include "clock.h" +#include "clock2xxx.h" +#include "opp2xxx.h" +#include "cm2xxx.h" +#include "cm-regbits-24xx.h" +#include "sdrc.h" +#include "sram.h" + +const struct prcm_config *curr_prcm_set; +const struct prcm_config *rate_table; + +/* + * sys_ck_rate: the rate of the external high-frequency clock + * oscillator on the board. Set by the SoC-specific clock init code. + * Once set during a boot, will not change. + */ +static unsigned long sys_ck_rate; + +/** + * omap2_table_mpu_recalc - just return the MPU speed + * @clk: virt_prcm_set struct clk + * + * Set virt_prcm_set's rate to the mpu_speed field of the current PRCM set. + */ +unsigned long omap2_table_mpu_recalc(struct clk_hw *clk, + unsigned long parent_rate) +{ + return curr_prcm_set->mpu_speed; +} + +/* + * Look for a rate equal or less than the target rate given a configuration set. + * + * What's not entirely clear is "which" field represents the key field. + * Some might argue L3-DDR, others ARM, others IVA. This code is simple and + * just uses the ARM rates. + */ +long omap2_round_to_table_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + const struct prcm_config *ptr; + long highest_rate; + + highest_rate = -EINVAL; + + for (ptr = rate_table; ptr->mpu_speed; ptr++) { + if (!(ptr->flags & cpu_mask)) + continue; + if (ptr->xtal_speed != sys_ck_rate) + continue; + + highest_rate = ptr->mpu_speed; + + /* Can check only after xtal frequency check */ + if (ptr->mpu_speed <= rate) + break; + } + return highest_rate; +} + +/* Sets basic clocks based on the specified rate */ +int omap2_select_table_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 cur_rate, done_rate, bypass = 0; + const struct prcm_config *prcm; + unsigned long found_speed = 0; + unsigned long flags; + + for (prcm = rate_table; prcm->mpu_speed; prcm++) { + if (!(prcm->flags & cpu_mask)) + continue; + + if (prcm->xtal_speed != sys_ck_rate) + continue; + + if (prcm->mpu_speed <= rate) { + found_speed = prcm->mpu_speed; + break; + } + } + + if (!found_speed) { + printk(KERN_INFO "Could not set MPU rate to %luMHz\n", + rate / 1000000); + return -EINVAL; + } + + curr_prcm_set = prcm; + cur_rate = omap2xxx_clk_get_core_rate(); + + if (prcm->dpll_speed == cur_rate / 2) { + omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1); + } else if (prcm->dpll_speed == cur_rate * 2) { + omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1); + } else if (prcm->dpll_speed != cur_rate) { + local_irq_save(flags); + + if (prcm->dpll_speed == prcm->xtal_speed) + bypass = 1; + + if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) == + CORE_CLK_SRC_DPLL_X2) + done_rate = CORE_CLK_SRC_DPLL_X2; + else + done_rate = CORE_CLK_SRC_DPLL; + + omap2xxx_cm_set_mod_dividers(prcm->cm_clksel_mpu, + prcm->cm_clksel_dsp, + prcm->cm_clksel_gfx, + prcm->cm_clksel1_core, + prcm->cm_clksel_mdm); + + /* x2 to enter omap2xxx_sdrc_init_params() */ + omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1); + + omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr, + bypass); + + omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked()); + omap2xxx_sdrc_reprogram(done_rate, 0); + + local_irq_restore(flags); + } + + return 0; +} + +/** + * omap2xxx_clkt_vps_check_bootloader_rate - determine which of the rate + * table sets matches the current CORE DPLL hardware rate + * + * Check the MPU rate set by bootloader. Sets the 'curr_prcm_set' + * global to point to the active rate set when found; otherwise, sets + * it to NULL. No return value; + */ +void omap2xxx_clkt_vps_check_bootloader_rates(void) +{ + const struct prcm_config *prcm = NULL; + unsigned long rate; + + rate = omap2xxx_clk_get_core_rate(); + for (prcm = rate_table; prcm->mpu_speed; prcm++) { + if (!(prcm->flags & cpu_mask)) + continue; + if (prcm->xtal_speed != sys_ck_rate) + continue; + if (prcm->dpll_speed <= rate) + break; + } + curr_prcm_set = prcm; +} + +/** + * omap2xxx_clkt_vps_late_init - store a copy of the sys_ck rate + * + * Store a copy of the sys_ck rate for later use by the OMAP2xxx DVFS + * code. (The sys_ck rate does not -- or rather, must not -- change + * during kernel runtime.) Must be called after we have a valid + * sys_ck rate, but before the virt_prcm_set clock rate is + * recalculated. No return value. + */ +void omap2xxx_clkt_vps_late_init(void) +{ + struct clk *c; + + c = clk_get(NULL, "sys_ck"); + if (IS_ERR(c)) { + WARN(1, "could not locate sys_ck\n"); + } else { + sys_ck_rate = clk_get_rate(c); + clk_put(c); + } +} + +#ifdef CONFIG_OF +#include <linux/clk-provider.h> +#include <linux/clkdev.h> + +static const struct clk_ops virt_prcm_set_ops = { + .recalc_rate = &omap2_table_mpu_recalc, + .set_rate = &omap2_select_table_rate, + .round_rate = &omap2_round_to_table_rate, +}; + +/** + * omap2xxx_clkt_vps_init - initialize virt_prcm_set clock + * + * Does a manual init for the virtual prcm DVFS clock for OMAP2. This + * function is called only from omap2 DT clock init, as the virtual + * node is not modelled in the DT clock data. + */ +void omap2xxx_clkt_vps_init(void) +{ + struct clk_init_data init = { NULL }; + struct clk_hw_omap *hw = NULL; + struct clk *clk; + const char *parent_name = "mpu_ck"; + + omap2xxx_clkt_vps_late_init(); + omap2xxx_clkt_vps_check_bootloader_rates(); + + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) + return; + init.name = "virt_prcm_set"; + init.ops = &virt_prcm_set_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + hw->hw.init = &init; + + clk = clk_register(NULL, &hw->hw); + if (IS_ERR(clk)) { + printk(KERN_ERR "Failed to register clock\n"); + kfree(hw); + return; + } + + clkdev_create(clk, "cpufreq_ck", NULL); +} +#endif diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c new file mode 100644 index 000000000..3c1d12dc8 --- /dev/null +++ b/arch/arm/mach-omap2/clock.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/clock.c + * + * Copyright (C) 2005-2008 Texas Instruments, Inc. + * Copyright (C) 2004-2010 Nokia Corporation + * + * Contacts: + * Richard Woodruff <r-woodruff2@ti.com> + * Paul Walmsley + */ +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/of_address.h> +#include <asm/cpu.h> + +#include <trace/events/power.h> + +#include "soc.h" +#include "clockdomain.h" +#include "clock.h" +#include "cm.h" +#include "cm2xxx.h" +#include "cm3xxx.h" +#include "cm-regbits-24xx.h" +#include "cm-regbits-34xx.h" +#include "common.h" + +u16 cpu_mask; + +/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */ +#define OMAP3430_DPLL_FINT_BAND1_MIN 750000 +#define OMAP3430_DPLL_FINT_BAND1_MAX 2100000 +#define OMAP3430_DPLL_FINT_BAND2_MIN 7500000 +#define OMAP3430_DPLL_FINT_BAND2_MAX 21000000 + +/* + * DPLL valid Fint frequency range for OMAP36xx and OMAP4xxx. + * From device data manual section 4.3 "DPLL and DLL Specifications". + */ +#define OMAP3PLUS_DPLL_FINT_MIN 32000 +#define OMAP3PLUS_DPLL_FINT_MAX 52000000 + +struct ti_clk_ll_ops omap_clk_ll_ops = { + .clkdm_clk_enable = clkdm_clk_enable, + .clkdm_clk_disable = clkdm_clk_disable, + .clkdm_lookup = clkdm_lookup, + .cm_wait_module_ready = omap_cm_wait_module_ready, + .cm_split_idlest_reg = cm_split_idlest_reg, +}; + +/** + * omap2_clk_setup_ll_ops - setup clock driver low-level ops + * + * Sets up clock driver low-level platform ops. These are needed + * for register accesses and various other misc platform operations. + * Returns 0 on success, -EBUSY if low level ops have been registered + * already. + */ +int __init omap2_clk_setup_ll_ops(void) +{ + return ti_clk_setup_ll_ops(&omap_clk_ll_ops); +} + +/* + * OMAP2+ specific clock functions + */ + +/** + * ti_clk_init_features - init clock features struct for the SoC + * + * Initializes the clock features struct based on the SoC type. + */ +void __init ti_clk_init_features(void) +{ + struct ti_clk_features features = { 0 }; + /* Fint setup for DPLLs */ + if (cpu_is_omap3430()) { + features.fint_min = OMAP3430_DPLL_FINT_BAND1_MIN; + features.fint_max = OMAP3430_DPLL_FINT_BAND2_MAX; + features.fint_band1_max = OMAP3430_DPLL_FINT_BAND1_MAX; + features.fint_band2_min = OMAP3430_DPLL_FINT_BAND2_MIN; + } else { + features.fint_min = OMAP3PLUS_DPLL_FINT_MIN; + features.fint_max = OMAP3PLUS_DPLL_FINT_MAX; + } + + /* Bypass value setup for DPLLs */ + if (cpu_is_omap24xx()) { + features.dpll_bypass_vals |= + (1 << OMAP2XXX_EN_DPLL_LPBYPASS) | + (1 << OMAP2XXX_EN_DPLL_FRBYPASS); + } else if (cpu_is_omap34xx()) { + features.dpll_bypass_vals |= + (1 << OMAP3XXX_EN_DPLL_LPBYPASS) | + (1 << OMAP3XXX_EN_DPLL_FRBYPASS); + } else if (soc_is_am33xx() || cpu_is_omap44xx() || soc_is_am43xx() || + soc_is_omap54xx() || soc_is_dra7xx()) { + features.dpll_bypass_vals |= + (1 << OMAP4XXX_EN_DPLL_LPBYPASS) | + (1 << OMAP4XXX_EN_DPLL_FRBYPASS) | + (1 << OMAP4XXX_EN_DPLL_MNBYPASS); + } + + /* Jitter correction only available on OMAP343X */ + if (cpu_is_omap343x()) + features.flags |= TI_CLK_DPLL_HAS_FREQSEL; + + if (omap_type() == OMAP2_DEVICE_TYPE_GP) + features.flags |= TI_CLK_DEVICE_TYPE_GP; + + /* Idlest value for interface clocks. + * 24xx uses 0 to indicate not ready, and 1 to indicate ready. + * 34xx reverses this, just to keep us on our toes + * AM35xx uses both, depending on the module. + */ + if (cpu_is_omap24xx()) + features.cm_idlest_val = OMAP24XX_CM_IDLEST_VAL; + else if (cpu_is_omap34xx()) + features.cm_idlest_val = OMAP34XX_CM_IDLEST_VAL; + + /* On OMAP3430 ES1.0, DPLL4 can't be re-programmed */ + if (omap_rev() == OMAP3430_REV_ES1_0) + features.flags |= TI_CLK_DPLL4_DENY_REPROGRAM; + + /* Errata I810 for omap5 / dra7 */ + if (soc_is_omap54xx() || soc_is_dra7xx()) + features.flags |= TI_CLK_ERRATA_I810; + + ti_clk_setup_features(&features); +} diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h new file mode 100644 index 000000000..bbe4b3289 --- /dev/null +++ b/arch/arm/mach-omap2/clock.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/arch/arm/mach-omap2/clock.h + * + * Copyright (C) 2005-2009 Texas Instruments, Inc. + * Copyright (C) 2004-2011 Nokia Corporation + * + * Contacts: + * Richard Woodruff <r-woodruff2@ti.com> + * Paul Walmsley + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK_H +#define __ARCH_ARM_MACH_OMAP2_CLOCK_H + +#include <linux/kernel.h> +#include <linux/list.h> + +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/clk/ti.h> + +/* struct clksel_rate.flags possibilities */ +#define RATE_IN_242X (1 << 0) +#define RATE_IN_243X (1 << 1) +#define RATE_IN_3430ES1 (1 << 2) /* 3430ES1 rates only */ +#define RATE_IN_3430ES2PLUS (1 << 3) /* 3430 ES >= 2 rates only */ +#define RATE_IN_36XX (1 << 4) +#define RATE_IN_4430 (1 << 5) +#define RATE_IN_TI816X (1 << 6) +#define RATE_IN_4460 (1 << 7) +#define RATE_IN_AM33XX (1 << 8) +#define RATE_IN_TI814X (1 << 9) + +#define RATE_IN_24XX (RATE_IN_242X | RATE_IN_243X) +#define RATE_IN_34XX (RATE_IN_3430ES1 | RATE_IN_3430ES2PLUS) +#define RATE_IN_3XXX (RATE_IN_34XX | RATE_IN_36XX) +#define RATE_IN_44XX (RATE_IN_4430 | RATE_IN_4460) + +/* RATE_IN_3430ES2PLUS_36XX includes 34xx/35xx with ES >=2, and all 36xx/37xx */ +#define RATE_IN_3430ES2PLUS_36XX (RATE_IN_3430ES2PLUS | RATE_IN_36XX) + +/* CM_CLKSEL2_PLL.CORE_CLK_SRC bits (2XXX) */ +#define CORE_CLK_SRC_32K 0x0 +#define CORE_CLK_SRC_DPLL 0x1 +#define CORE_CLK_SRC_DPLL_X2 0x2 + +/* OMAP2xxx CM_CLKEN_PLL.EN_DPLL bits - for omap2_get_dpll_rate() */ +#define OMAP2XXX_EN_DPLL_LPBYPASS 0x1 +#define OMAP2XXX_EN_DPLL_FRBYPASS 0x2 +#define OMAP2XXX_EN_DPLL_LOCKED 0x3 + +/* OMAP3xxx CM_CLKEN_PLL*.EN_*_DPLL bits - for omap2_get_dpll_rate() */ +#define OMAP3XXX_EN_DPLL_LPBYPASS 0x5 +#define OMAP3XXX_EN_DPLL_FRBYPASS 0x6 +#define OMAP3XXX_EN_DPLL_LOCKED 0x7 + +/* OMAP4xxx CM_CLKMODE_DPLL*.EN_*_DPLL bits - for omap2_get_dpll_rate() */ +#define OMAP4XXX_EN_DPLL_MNBYPASS 0x4 +#define OMAP4XXX_EN_DPLL_LPBYPASS 0x5 +#define OMAP4XXX_EN_DPLL_FRBYPASS 0x6 +#define OMAP4XXX_EN_DPLL_LOCKED 0x7 + +extern struct ti_clk_ll_ops omap_clk_ll_ops; + +extern u16 cpu_mask; + +extern const struct clkops clkops_omap2_dflt_wait; +extern const struct clkops clkops_omap2_dflt; + +extern struct clk_functions omap2_clk_functions; + +int __init omap2_clk_setup_ll_ops(void); + +void __init ti_clk_init_features(void); +#endif diff --git a/arch/arm/mach-omap2/clock2xxx.h b/arch/arm/mach-omap2/clock2xxx.h new file mode 100644 index 000000000..a8408f9d0 --- /dev/null +++ b/arch/arm/mach-omap2/clock2xxx.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * OMAP2 clock function prototypes and macros + * + * Copyright (C) 2005-2010 Texas Instruments, Inc. + * Copyright (C) 2004-2010 Nokia Corporation + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK2XXX_H +#define __ARCH_ARM_MACH_OMAP2_CLOCK2XXX_H + +#include <linux/clk-provider.h> +#include "clock.h" + +unsigned long omap2_table_mpu_recalc(struct clk_hw *clk, + unsigned long parent_rate); +int omap2_select_table_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +long omap2_round_to_table_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate); +unsigned long omap2xxx_sys_clk_recalc(struct clk_hw *clk, + unsigned long parent_rate); +unsigned long omap2_osc_clk_recalc(struct clk_hw *clk, + unsigned long parent_rate); +void omap2xxx_clkt_dpllcore_init(struct clk_hw *hw); +unsigned long omap2xxx_clk_get_core_rate(void); +u32 omap2xxx_get_sysclkdiv(void); +void omap2xxx_clk_prepare_for_reboot(void); +void omap2xxx_clkt_vps_check_bootloader_rates(void); +void omap2xxx_clkt_vps_late_init(void); + +#ifdef CONFIG_SOC_OMAP2420 +int omap2420_clk_init(void); +#else +#define omap2420_clk_init() do { } while(0) +#endif + +#ifdef CONFIG_SOC_OMAP2430 +int omap2430_clk_init(void); +#else +#define omap2430_clk_init() do { } while(0) +#endif + +extern struct clk_hw *dclk_hw; + +#endif diff --git a/arch/arm/mach-omap2/clock3xxx.h b/arch/arm/mach-omap2/clock3xxx.h new file mode 100644 index 000000000..10a9f577d --- /dev/null +++ b/arch/arm/mach-omap2/clock3xxx.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * OMAP3-common clock function prototypes and macros + * + * Copyright (C) 2007-2010 Texas Instruments, Inc. + * Copyright (C) 2007-2010 Nokia Corporation + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK3XXX_H +#define __ARCH_ARM_MACH_OMAP2_CLOCK3XXX_H + +int omap3xxx_clk_init(void); +int omap3_core_dpll_m2_set_rate(struct clk_hw *clk, unsigned long rate, + unsigned long parent_rate); + +extern struct clk *sdrc_ick_p; +extern struct clk *arm_fck_p; + +extern const struct clkops clkops_noncore_dpll_ops; + +#endif diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c new file mode 100644 index 000000000..1feb00987 --- /dev/null +++ b/arch/arm/mach-omap2/clockdomain.c @@ -0,0 +1,1341 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2/3/4 clockdomain framework functions + * + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Copyright (C) 2008-2011 Nokia Corporation + * + * Written by Paul Walmsley and Jouni Högander + * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> + */ +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/limits.h> +#include <linux/err.h> +#include <linux/clk-provider.h> +#include <linux/cpu_pm.h> + +#include <linux/io.h> + +#include <linux/bitops.h> + +#include "soc.h" +#include "clock.h" +#include "clockdomain.h" +#include "pm.h" + +/* clkdm_list contains all registered struct clockdomains */ +static LIST_HEAD(clkdm_list); + +/* array of clockdomain deps to be added/removed when clkdm in hwsup mode */ +static struct clkdm_autodep *autodeps; + +static struct clkdm_ops *arch_clkdm; +void clkdm_save_context(void); +void clkdm_restore_context(void); + +/* Private functions */ + +static struct clockdomain *_clkdm_lookup(const char *name) +{ + struct clockdomain *clkdm, *temp_clkdm; + + if (!name) + return NULL; + + clkdm = NULL; + + list_for_each_entry(temp_clkdm, &clkdm_list, node) { + if (!strcmp(name, temp_clkdm->name)) { + clkdm = temp_clkdm; + break; + } + } + + return clkdm; +} + +/** + * _clkdm_register - register a clockdomain + * @clkdm: struct clockdomain * to register + * + * Adds a clockdomain to the internal clockdomain list. + * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is + * already registered by the provided name, or 0 upon success. + */ +static int _clkdm_register(struct clockdomain *clkdm) +{ + struct powerdomain *pwrdm; + + if (!clkdm || !clkdm->name) + return -EINVAL; + + pwrdm = pwrdm_lookup(clkdm->pwrdm.name); + if (!pwrdm) { + pr_err("clockdomain: %s: powerdomain %s does not exist\n", + clkdm->name, clkdm->pwrdm.name); + return -EINVAL; + } + clkdm->pwrdm.ptr = pwrdm; + + /* Verify that the clockdomain is not already registered */ + if (_clkdm_lookup(clkdm->name)) + return -EEXIST; + + list_add(&clkdm->node, &clkdm_list); + + pwrdm_add_clkdm(pwrdm, clkdm); + + pr_debug("clockdomain: registered %s\n", clkdm->name); + + return 0; +} + +/* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */ +static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm, + struct clkdm_dep *deps) +{ + struct clkdm_dep *cd; + + if (!clkdm || !deps) + return ERR_PTR(-EINVAL); + + for (cd = deps; cd->clkdm_name; cd++) { + if (!cd->clkdm && cd->clkdm_name) + cd->clkdm = _clkdm_lookup(cd->clkdm_name); + + if (cd->clkdm == clkdm) + break; + } + + if (!cd->clkdm_name) + return ERR_PTR(-ENOENT); + + return cd; +} + +/** + * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store + * @autodep: struct clkdm_autodep * to resolve + * + * Resolve autodep clockdomain names to clockdomain pointers via + * clkdm_lookup() and store the pointers in the autodep structure. An + * "autodep" is a clockdomain sleep/wakeup dependency that is + * automatically added and removed whenever clocks in the associated + * clockdomain are enabled or disabled (respectively) when the + * clockdomain is in hardware-supervised mode. Meant to be called + * once at clockdomain layer initialization, since these should remain + * fixed for a particular architecture. No return value. + * + * XXX autodeps are deprecated and should be removed at the earliest + * opportunity + */ +static void _autodep_lookup(struct clkdm_autodep *autodep) +{ + struct clockdomain *clkdm; + + if (!autodep) + return; + + clkdm = clkdm_lookup(autodep->clkdm.name); + if (!clkdm) { + pr_err("clockdomain: autodeps: clockdomain %s does not exist\n", + autodep->clkdm.name); + clkdm = ERR_PTR(-ENOENT); + } + autodep->clkdm.ptr = clkdm; +} + +/** + * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms + * @clkdm: clockdomain that we are resolving dependencies for + * @clkdm_deps: ptr to array of struct clkdm_deps to resolve + * + * Iterates through @clkdm_deps, looking up the struct clockdomain named by + * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep. + * No return value. + */ +static void _resolve_clkdm_deps(struct clockdomain *clkdm, + struct clkdm_dep *clkdm_deps) +{ + struct clkdm_dep *cd; + + for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) { + if (cd->clkdm) + continue; + cd->clkdm = _clkdm_lookup(cd->clkdm_name); + + WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen", + clkdm->name, cd->clkdm_name); + } +} + +/** + * _clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 (lockless) + * @clkdm1: wake this struct clockdomain * up (dependent) + * @clkdm2: when this struct clockdomain * wakes up (source) + * + * When the clockdomain represented by @clkdm2 wakes up, wake up + * @clkdm1. Implemented in hardware on the OMAP, this feature is + * designed to reduce wakeup latency of the dependent clockdomain @clkdm1. + * Returns -EINVAL if presented with invalid clockdomain pointers, + * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon + * success. + */ +static int _clkdm_add_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret = 0; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); + if (IS_ERR(cd)) + ret = PTR_ERR(cd); + + if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep) + ret = -EINVAL; + + if (ret) { + pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n", + clkdm1->name, clkdm2->name); + return ret; + } + + cd->wkdep_usecount++; + if (cd->wkdep_usecount == 1) { + pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n", + clkdm1->name, clkdm2->name); + + ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2); + } + + return ret; +} + +/** + * _clkdm_del_wkdep - remove a wakeup dep from clkdm2 to clkdm1 (lockless) + * @clkdm1: wake this struct clockdomain * up (dependent) + * @clkdm2: when this struct clockdomain * wakes up (source) + * + * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2 + * wakes up. Returns -EINVAL if presented with invalid clockdomain + * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or + * 0 upon success. + */ +static int _clkdm_del_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret = 0; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); + if (IS_ERR(cd)) + ret = PTR_ERR(cd); + + if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep) + ret = -EINVAL; + + if (ret) { + pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n", + clkdm1->name, clkdm2->name); + return ret; + } + + cd->wkdep_usecount--; + if (cd->wkdep_usecount == 0) { + pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n", + clkdm1->name, clkdm2->name); + + ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2); + } + + return ret; +} + +/** + * _clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 (lockless) + * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) + * @clkdm2: when this struct clockdomain * is active (source) + * + * Prevent @clkdm1 from automatically going inactive (and then to + * retention or off) if @clkdm2 is active. Returns -EINVAL if + * presented with invalid clockdomain pointers or called on a machine + * that does not support software-configurable hardware sleep + * dependencies, -ENOENT if the specified dependency cannot be set in + * hardware, or 0 upon success. + */ +static int _clkdm_add_sleepdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret = 0; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); + if (IS_ERR(cd)) + ret = PTR_ERR(cd); + + if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep) + ret = -EINVAL; + + if (ret) { + pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n", + clkdm1->name, clkdm2->name); + return ret; + } + + cd->sleepdep_usecount++; + if (cd->sleepdep_usecount == 1) { + pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n", + clkdm1->name, clkdm2->name); + + ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2); + } + + return ret; +} + +/** + * _clkdm_del_sleepdep - remove a sleep dep from clkdm2 to clkdm1 (lockless) + * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) + * @clkdm2: when this struct clockdomain * is active (source) + * + * Allow @clkdm1 to automatically go inactive (and then to retention or + * off), independent of the activity state of @clkdm2. Returns -EINVAL + * if presented with invalid clockdomain pointers or called on a machine + * that does not support software-configurable hardware sleep dependencies, + * -ENOENT if the specified dependency cannot be cleared in hardware, or + * 0 upon success. + */ +static int _clkdm_del_sleepdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret = 0; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); + if (IS_ERR(cd)) + ret = PTR_ERR(cd); + + if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep) + ret = -EINVAL; + + if (ret) { + pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n", + clkdm1->name, clkdm2->name); + return ret; + } + + cd->sleepdep_usecount--; + if (cd->sleepdep_usecount == 0) { + pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n", + clkdm1->name, clkdm2->name); + + ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2); + } + + return ret; +} + +/* Public functions */ + +/** + * clkdm_register_platform_funcs - register clockdomain implementation fns + * @co: func pointers for arch specific implementations + * + * Register the list of function pointers used to implement the + * clockdomain functions on different OMAP SoCs. Should be called + * before any other clkdm_register*() function. Returns -EINVAL if + * @co is null, -EEXIST if platform functions have already been + * registered, or 0 upon success. + */ +int clkdm_register_platform_funcs(struct clkdm_ops *co) +{ + if (!co) + return -EINVAL; + + if (arch_clkdm) + return -EEXIST; + + arch_clkdm = co; + + return 0; +}; + +/** + * clkdm_register_clkdms - register SoC clockdomains + * @cs: pointer to an array of struct clockdomain to register + * + * Register the clockdomains available on a particular OMAP SoC. Must + * be called after clkdm_register_platform_funcs(). May be called + * multiple times. Returns -EACCES if called before + * clkdm_register_platform_funcs(); -EINVAL if the argument @cs is + * null; or 0 upon success. + */ +int clkdm_register_clkdms(struct clockdomain **cs) +{ + struct clockdomain **c = NULL; + + if (!arch_clkdm) + return -EACCES; + + if (!cs) + return -EINVAL; + + for (c = cs; *c; c++) + _clkdm_register(*c); + + return 0; +} + +/** + * clkdm_register_autodeps - register autodeps (if required) + * @ia: pointer to a static array of struct clkdm_autodep to register + * + * Register clockdomain "automatic dependencies." These are + * clockdomain wakeup and sleep dependencies that are automatically + * added whenever the first clock inside a clockdomain is enabled, and + * removed whenever the last clock inside a clockdomain is disabled. + * These are currently only used on OMAP3 devices, and are deprecated, + * since they waste energy. However, until the OMAP2/3 IP block + * enable/disable sequence can be converted to match the OMAP4 + * sequence, they are needed. + * + * Must be called only after all of the SoC clockdomains are + * registered, since the function will resolve autodep clockdomain + * names into clockdomain pointers. + * + * The struct clkdm_autodep @ia array must be static, as this function + * does not copy the array elements. + * + * Returns -EACCES if called before any clockdomains have been + * registered, -EINVAL if called with a null @ia argument, -EEXIST if + * autodeps have already been registered, or 0 upon success. + */ +int clkdm_register_autodeps(struct clkdm_autodep *ia) +{ + struct clkdm_autodep *a = NULL; + + if (list_empty(&clkdm_list)) + return -EACCES; + + if (!ia) + return -EINVAL; + + if (autodeps) + return -EEXIST; + + autodeps = ia; + for (a = autodeps; a->clkdm.ptr; a++) + _autodep_lookup(a); + + return 0; +} + +static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + if (enable_off_mode) + clkdm_save_context(); + break; + case CPU_CLUSTER_PM_EXIT: + if (enable_off_mode) + clkdm_restore_context(); + break; + } + + return NOTIFY_OK; +} + +/** + * clkdm_complete_init - set up the clockdomain layer + * + * Put all clockdomains into software-supervised mode; PM code should + * later enable hardware-supervised mode as appropriate. Must be + * called after clkdm_register_clkdms(). Returns -EACCES if called + * before clkdm_register_clkdms(), or 0 upon success. + */ +int clkdm_complete_init(void) +{ + struct clockdomain *clkdm; + static struct notifier_block nb; + + if (list_empty(&clkdm_list)) + return -EACCES; + + list_for_each_entry(clkdm, &clkdm_list, node) { + clkdm_deny_idle(clkdm); + + _resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs); + clkdm_clear_all_wkdeps(clkdm); + + _resolve_clkdm_deps(clkdm, clkdm->sleepdep_srcs); + clkdm_clear_all_sleepdeps(clkdm); + } + + /* Only AM43XX can lose clkdm context during rtc-ddr suspend */ + if (soc_is_am43xx()) { + nb.notifier_call = cpu_notifier; + cpu_pm_register_notifier(&nb); + } + + return 0; +} + +/** + * clkdm_lookup - look up a clockdomain by name, return a pointer + * @name: name of clockdomain + * + * Find a registered clockdomain by its name @name. Returns a pointer + * to the struct clockdomain if found, or NULL otherwise. + */ +struct clockdomain *clkdm_lookup(const char *name) +{ + struct clockdomain *clkdm, *temp_clkdm; + + if (!name) + return NULL; + + clkdm = NULL; + + list_for_each_entry(temp_clkdm, &clkdm_list, node) { + if (!strcmp(name, temp_clkdm->name)) { + clkdm = temp_clkdm; + break; + } + } + + return clkdm; +} + +/** + * clkdm_for_each - call function on each registered clockdomain + * @fn: callback function * + * + * Call the supplied function @fn for each registered clockdomain. + * The callback function @fn can return anything but 0 to bail + * out early from the iterator. The callback function is called with + * the clkdm_mutex held, so no clockdomain structure manipulation + * functions should be called from the callback, although hardware + * clockdomain control functions are fine. Returns the last return + * value of the callback function, which should be 0 for success or + * anything else to indicate failure; or -EINVAL if the function pointer + * is null. + */ +int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), + void *user) +{ + struct clockdomain *clkdm; + int ret = 0; + + if (!fn) + return -EINVAL; + + list_for_each_entry(clkdm, &clkdm_list, node) { + ret = (*fn)(clkdm, user); + if (ret) + break; + } + + return ret; +} + + +/** + * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in + * @clkdm: struct clockdomain * + * + * Return a pointer to the struct powerdomain that the specified clockdomain + * @clkdm exists in, or returns NULL if @clkdm is NULL. + */ +struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) +{ + if (!clkdm) + return NULL; + + return clkdm->pwrdm.ptr; +} + + +/* Hardware clockdomain control */ + +/** + * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 + * @clkdm1: wake this struct clockdomain * up (dependent) + * @clkdm2: when this struct clockdomain * wakes up (source) + * + * When the clockdomain represented by @clkdm2 wakes up, wake up + * @clkdm1. Implemented in hardware on the OMAP, this feature is + * designed to reduce wakeup latency of the dependent clockdomain @clkdm1. + * Returns -EINVAL if presented with invalid clockdomain pointers, + * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon + * success. + */ +int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); + if (IS_ERR(cd)) + return PTR_ERR(cd); + + pwrdm_lock(cd->clkdm->pwrdm.ptr); + ret = _clkdm_add_wkdep(clkdm1, clkdm2); + pwrdm_unlock(cd->clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1 + * @clkdm1: wake this struct clockdomain * up (dependent) + * @clkdm2: when this struct clockdomain * wakes up (source) + * + * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2 + * wakes up. Returns -EINVAL if presented with invalid clockdomain + * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or + * 0 upon success. + */ +int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); + if (IS_ERR(cd)) + return PTR_ERR(cd); + + pwrdm_lock(cd->clkdm->pwrdm.ptr); + ret = _clkdm_del_wkdep(clkdm1, clkdm2); + pwrdm_unlock(cd->clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1 + * @clkdm1: wake this struct clockdomain * up (dependent) + * @clkdm2: when this struct clockdomain * wakes up (source) + * + * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be + * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL + * if either clockdomain pointer is invalid; or -ENOENT if the hardware + * is incapable. + * + * REVISIT: Currently this function only represents software-controllable + * wakeup dependencies. Wakeup dependencies fixed in hardware are not + * yet handled here. + */ +int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret = 0; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); + if (IS_ERR(cd)) + ret = PTR_ERR(cd); + + if (!arch_clkdm || !arch_clkdm->clkdm_read_wkdep) + ret = -EINVAL; + + if (ret) { + pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n", + clkdm1->name, clkdm2->name); + return ret; + } + + /* XXX It's faster to return the wkdep_usecount */ + return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2); +} + +/** + * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm + * @clkdm: struct clockdomain * to remove all wakeup dependencies from + * + * Remove all inter-clockdomain wakeup dependencies that could cause + * @clkdm to wake. Intended to be used during boot to initialize the + * PRCM to a known state, after all clockdomains are put into swsup idle + * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or + * 0 upon success. + */ +int clkdm_clear_all_wkdeps(struct clockdomain *clkdm) +{ + if (!clkdm) + return -EINVAL; + + if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_wkdeps) + return -EINVAL; + + return arch_clkdm->clkdm_clear_all_wkdeps(clkdm); +} + +/** + * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 + * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) + * @clkdm2: when this struct clockdomain * is active (source) + * + * Prevent @clkdm1 from automatically going inactive (and then to + * retention or off) if @clkdm2 is active. Returns -EINVAL if + * presented with invalid clockdomain pointers or called on a machine + * that does not support software-configurable hardware sleep + * dependencies, -ENOENT if the specified dependency cannot be set in + * hardware, or 0 upon success. + */ +int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); + if (IS_ERR(cd)) + return PTR_ERR(cd); + + pwrdm_lock(cd->clkdm->pwrdm.ptr); + ret = _clkdm_add_sleepdep(clkdm1, clkdm2); + pwrdm_unlock(cd->clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1 + * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) + * @clkdm2: when this struct clockdomain * is active (source) + * + * Allow @clkdm1 to automatically go inactive (and then to retention or + * off), independent of the activity state of @clkdm2. Returns -EINVAL + * if presented with invalid clockdomain pointers or called on a machine + * that does not support software-configurable hardware sleep dependencies, + * -ENOENT if the specified dependency cannot be cleared in hardware, or + * 0 upon success. + */ +int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); + if (IS_ERR(cd)) + return PTR_ERR(cd); + + pwrdm_lock(cd->clkdm->pwrdm.ptr); + ret = _clkdm_del_sleepdep(clkdm1, clkdm2); + pwrdm_unlock(cd->clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1 + * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) + * @clkdm2: when this struct clockdomain * is active (source) + * + * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will + * not be allowed to automatically go inactive if @clkdm2 is active; + * 0 if @clkdm1's automatic power state inactivity transition is independent + * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called + * on a machine that does not support software-configurable hardware sleep + * dependencies; or -ENOENT if the hardware is incapable. + * + * REVISIT: Currently this function only represents software-controllable + * sleep dependencies. Sleep dependencies fixed in hardware are not + * yet handled here. + */ +int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) +{ + struct clkdm_dep *cd; + int ret = 0; + + if (!clkdm1 || !clkdm2) + return -EINVAL; + + cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); + if (IS_ERR(cd)) + ret = PTR_ERR(cd); + + if (!arch_clkdm || !arch_clkdm->clkdm_read_sleepdep) + ret = -EINVAL; + + if (ret) { + pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n", + clkdm1->name, clkdm2->name); + return ret; + } + + /* XXX It's faster to return the sleepdep_usecount */ + return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2); +} + +/** + * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm + * @clkdm: struct clockdomain * to remove all sleep dependencies from + * + * Remove all inter-clockdomain sleep dependencies that could prevent + * @clkdm from idling. Intended to be used during boot to initialize the + * PRCM to a known state, after all clockdomains are put into swsup idle + * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or + * 0 upon success. + */ +int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) +{ + if (!clkdm) + return -EINVAL; + + if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_sleepdeps) + return -EINVAL; + + return arch_clkdm->clkdm_clear_all_sleepdeps(clkdm); +} + +/** + * clkdm_sleep_nolock - force clockdomain sleep transition (lockless) + * @clkdm: struct clockdomain * + * + * Instruct the CM to force a sleep transition on the specified + * clockdomain @clkdm. Only for use by the powerdomain code. Returns + * -EINVAL if @clkdm is NULL or if clockdomain does not support + * software-initiated sleep; 0 upon success. + */ +int clkdm_sleep_nolock(struct clockdomain *clkdm) +{ + int ret; + + if (!clkdm) + return -EINVAL; + + if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) { + pr_debug("clockdomain: %s does not support forcing sleep via software\n", + clkdm->name); + return -EINVAL; + } + + if (!arch_clkdm || !arch_clkdm->clkdm_sleep) + return -EINVAL; + + pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); + + clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; + ret = arch_clkdm->clkdm_sleep(clkdm); + ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_sleep - force clockdomain sleep transition + * @clkdm: struct clockdomain * + * + * Instruct the CM to force a sleep transition on the specified + * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if + * clockdomain does not support software-initiated sleep; 0 upon + * success. + */ +int clkdm_sleep(struct clockdomain *clkdm) +{ + int ret; + + pwrdm_lock(clkdm->pwrdm.ptr); + ret = clkdm_sleep_nolock(clkdm); + pwrdm_unlock(clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless) + * @clkdm: struct clockdomain * + * + * Instruct the CM to force a wakeup transition on the specified + * clockdomain @clkdm. Only for use by the powerdomain code. Returns + * -EINVAL if @clkdm is NULL or if the clockdomain does not support + * software-controlled wakeup; 0 upon success. + */ +int clkdm_wakeup_nolock(struct clockdomain *clkdm) +{ + int ret; + + if (!clkdm) + return -EINVAL; + + if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) { + pr_debug("clockdomain: %s does not support forcing wakeup via software\n", + clkdm->name); + return -EINVAL; + } + + if (!arch_clkdm || !arch_clkdm->clkdm_wakeup) + return -EINVAL; + + pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); + + clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; + ret = arch_clkdm->clkdm_wakeup(clkdm); + ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_wakeup - force clockdomain wakeup transition + * @clkdm: struct clockdomain * + * + * Instruct the CM to force a wakeup transition on the specified + * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the + * clockdomain does not support software-controlled wakeup; 0 upon + * success. + */ +int clkdm_wakeup(struct clockdomain *clkdm) +{ + int ret; + + pwrdm_lock(clkdm->pwrdm.ptr); + ret = clkdm_wakeup_nolock(clkdm); + pwrdm_unlock(clkdm->pwrdm.ptr); + + return ret; +} + +/** + * clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm + * @clkdm: struct clockdomain * + * + * Allow the hardware to automatically switch the clockdomain @clkdm + * into active or idle states, as needed by downstream clocks. If the + * clockdomain has any downstream clocks enabled in the clock + * framework, wkdep/sleepdep autodependencies are added; this is so + * device drivers can read and write to the device. Only for use by + * the powerdomain code. No return value. + */ +void clkdm_allow_idle_nolock(struct clockdomain *clkdm) +{ + if (!clkdm) + return; + + if (!WARN_ON(!clkdm->forcewake_count)) + clkdm->forcewake_count--; + + if (clkdm->forcewake_count) + return; + + if (!clkdm->usecount && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) + clkdm_sleep_nolock(clkdm); + + if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) + return; + + if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) + return; + + if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle) + return; + + pr_debug("clockdomain: enabling automatic idle transitions for %s\n", + clkdm->name); + + clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED; + arch_clkdm->clkdm_allow_idle(clkdm); + pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); +} + +/** + * clkdm_allow_idle - enable hwsup idle transitions for clkdm + * @clkdm: struct clockdomain * + * + * Allow the hardware to automatically switch the clockdomain @clkdm into + * active or idle states, as needed by downstream clocks. If the + * clockdomain has any downstream clocks enabled in the clock + * framework, wkdep/sleepdep autodependencies are added; this is so + * device drivers can read and write to the device. No return value. + */ +void clkdm_allow_idle(struct clockdomain *clkdm) +{ + pwrdm_lock(clkdm->pwrdm.ptr); + clkdm_allow_idle_nolock(clkdm); + pwrdm_unlock(clkdm->pwrdm.ptr); +} + +/** + * clkdm_deny_idle - disable hwsup idle transitions for clkdm + * @clkdm: struct clockdomain * + * + * Prevent the hardware from automatically switching the clockdomain + * @clkdm into inactive or idle states. If the clockdomain has + * downstream clocks enabled in the clock framework, wkdep/sleepdep + * autodependencies are removed. Only for use by the powerdomain + * code. No return value. + */ +void clkdm_deny_idle_nolock(struct clockdomain *clkdm) +{ + if (!clkdm) + return; + + if (clkdm->forcewake_count++) + return; + + if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + clkdm_wakeup_nolock(clkdm); + + if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) + return; + + if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) + return; + + if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle) + return; + + pr_debug("clockdomain: disabling automatic idle transitions for %s\n", + clkdm->name); + + clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; + arch_clkdm->clkdm_deny_idle(clkdm); + pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); +} + +/** + * clkdm_deny_idle - disable hwsup idle transitions for clkdm + * @clkdm: struct clockdomain * + * + * Prevent the hardware from automatically switching the clockdomain + * @clkdm into inactive or idle states. If the clockdomain has + * downstream clocks enabled in the clock framework, wkdep/sleepdep + * autodependencies are removed. No return value. + */ +void clkdm_deny_idle(struct clockdomain *clkdm) +{ + pwrdm_lock(clkdm->pwrdm.ptr); + clkdm_deny_idle_nolock(clkdm); + pwrdm_unlock(clkdm->pwrdm.ptr); +} + +/** + * clkdm_in_hwsup - is clockdomain @clkdm have hardware-supervised idle enabled? + * @clkdm: struct clockdomain * + * + * Returns true if clockdomain @clkdm currently has + * hardware-supervised idle enabled, or false if it does not or if + * @clkdm is NULL. It is only valid to call this function after + * clkdm_init() has been called. This function does not actually read + * bits from the hardware; it instead tests an in-memory flag that is + * changed whenever the clockdomain code changes the auto-idle mode. + */ +bool clkdm_in_hwsup(struct clockdomain *clkdm) +{ + bool ret; + + if (!clkdm) + return false; + + ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false; + + return ret; +} + +/** + * clkdm_missing_idle_reporting - can @clkdm enter autoidle even if in use? + * @clkdm: struct clockdomain * + * + * Returns true if clockdomain @clkdm has the + * CLKDM_MISSING_IDLE_REPORTING flag set, or false if not or @clkdm is + * null. More information is available in the documentation for the + * CLKDM_MISSING_IDLE_REPORTING macro. + */ +bool clkdm_missing_idle_reporting(struct clockdomain *clkdm) +{ + if (!clkdm) + return false; + + return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false; +} + +/* Public autodep handling functions (deprecated) */ + +/** + * clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable + * @clkdm: struct clockdomain * + * + * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm' + * in hardware-supervised mode. Meant to be called from clock framework + * when a clock inside clockdomain 'clkdm' is enabled. No return value. + * + * XXX autodeps are deprecated and should be removed at the earliest + * opportunity + */ +void clkdm_add_autodeps(struct clockdomain *clkdm) +{ + struct clkdm_autodep *autodep; + + if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) + return; + + for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { + if (IS_ERR(autodep->clkdm.ptr)) + continue; + + pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n", + clkdm->name, autodep->clkdm.ptr->name); + + _clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); + _clkdm_add_wkdep(clkdm, autodep->clkdm.ptr); + } +} + +/** + * clkdm_del_autodeps - remove auto sleepdeps/wkdeps from clkdm + * @clkdm: struct clockdomain * + * + * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm' + * in hardware-supervised mode. Meant to be called from clock framework + * when a clock inside clockdomain 'clkdm' is disabled. No return value. + * + * XXX autodeps are deprecated and should be removed at the earliest + * opportunity + */ +void clkdm_del_autodeps(struct clockdomain *clkdm) +{ + struct clkdm_autodep *autodep; + + if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) + return; + + for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { + if (IS_ERR(autodep->clkdm.ptr)) + continue; + + pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n", + clkdm->name, autodep->clkdm.ptr->name); + + _clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); + _clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); + } +} + +/* Clockdomain-to-clock/hwmod framework interface code */ + +/** + * clkdm_clk_enable - add an enabled downstream clock to this clkdm + * @clkdm: struct clockdomain * + * @clk: struct clk * of the enabled downstream clock + * + * Increment the usecount of the clockdomain @clkdm and ensure that it + * is awake before @clk is enabled. Intended to be called by + * clk_enable() code. If the clockdomain is in software-supervised + * idle mode, force the clockdomain to wake. If the clockdomain is in + * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to + * ensure that devices in the clockdomain can be read from/written to + * by on-chip processors. Returns -EINVAL if passed null pointers; + * returns 0 upon success or if the clockdomain is in hwsup idle mode. + */ +int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *unused) +{ + if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable) + return -EINVAL; + + pwrdm_lock(clkdm->pwrdm.ptr); + + /* + * For arch's with no autodeps, clkcm_clk_enable + * should be called for every clock instance or hwmod that is + * enabled, so the clkdm can be force woken up. + */ + clkdm->usecount++; + if (clkdm->usecount > 1 && autodeps) { + pwrdm_unlock(clkdm->pwrdm.ptr); + return 0; + } + + arch_clkdm->clkdm_clk_enable(clkdm); + pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); + pwrdm_unlock(clkdm->pwrdm.ptr); + + pr_debug("clockdomain: %s: enabled\n", clkdm->name); + + return 0; +} + +/** + * clkdm_clk_disable - remove an enabled downstream clock from this clkdm + * @clkdm: struct clockdomain * + * @clk: struct clk * of the disabled downstream clock + * + * Decrement the usecount of this clockdomain @clkdm when @clk is + * disabled. Intended to be called by clk_disable() code. If the + * clockdomain usecount goes to 0, put the clockdomain to sleep + * (software-supervised mode) or remove the clkdm autodependencies + * (hardware-supervised mode). Returns -EINVAL if passed null + * pointers; -ERANGE if the @clkdm usecount underflows; or returns 0 + * upon success or if the clockdomain is in hwsup idle mode. + */ +int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) +{ + if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) + return -EINVAL; + + pwrdm_lock(clkdm->pwrdm.ptr); + + /* corner case: disabling unused clocks */ + if (clk && (__clk_get_enable_count(clk) == 0) && clkdm->usecount == 0) + goto ccd_exit; + + if (clkdm->usecount == 0) { + pwrdm_unlock(clkdm->pwrdm.ptr); + WARN_ON(1); /* underflow */ + return -ERANGE; + } + + clkdm->usecount--; + if (clkdm->usecount > 0) { + pwrdm_unlock(clkdm->pwrdm.ptr); + return 0; + } + + arch_clkdm->clkdm_clk_disable(clkdm); + pwrdm_state_switch_nolock(clkdm->pwrdm.ptr); + + pr_debug("clockdomain: %s: disabled\n", clkdm->name); + +ccd_exit: + pwrdm_unlock(clkdm->pwrdm.ptr); + + return 0; +} + +/** + * clkdm_hwmod_enable - add an enabled downstream hwmod to this clkdm + * @clkdm: struct clockdomain * + * @oh: struct omap_hwmod * of the enabled downstream hwmod + * + * Increment the usecount of the clockdomain @clkdm and ensure that it + * is awake before @oh is enabled. Intended to be called by + * module_enable() code. + * If the clockdomain is in software-supervised idle mode, force the + * clockdomain to wake. If the clockdomain is in hardware-supervised idle + * mode, add clkdm-pwrdm autodependencies, to ensure that devices in the + * clockdomain can be read from/written to by on-chip processors. + * Returns -EINVAL if passed null pointers; + * returns 0 upon success or if the clockdomain is in hwsup idle mode. + */ +int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh) +{ + /* The clkdm attribute does not exist yet prior OMAP4 */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + return 0; + + /* + * XXX Rewrite this code to maintain a list of enabled + * downstream hwmods for debugging purposes? + */ + + if (!oh) + return -EINVAL; + + return clkdm_clk_enable(clkdm, NULL); +} + +/** + * clkdm_hwmod_disable - remove an enabled downstream hwmod from this clkdm + * @clkdm: struct clockdomain * + * @oh: struct omap_hwmod * of the disabled downstream hwmod + * + * Decrement the usecount of this clockdomain @clkdm when @oh is + * disabled. Intended to be called by module_disable() code. + * If the clockdomain usecount goes to 0, put the clockdomain to sleep + * (software-supervised mode) or remove the clkdm autodependencies + * (hardware-supervised mode). + * Returns -EINVAL if passed null pointers; -ERANGE if the @clkdm usecount + * underflows; or returns 0 upon success or if the clockdomain is in hwsup + * idle mode. + */ +int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) +{ + /* The clkdm attribute does not exist yet prior OMAP4 */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + return 0; + + if (!oh) + return -EINVAL; + + return clkdm_clk_disable(clkdm, NULL); +} + +/** + * _clkdm_save_context - save the context for the control of this clkdm + * + * Due to a suspend or hibernation operation, the state of the registers + * controlling this clkdm will be lost, save their context. + */ +static int _clkdm_save_context(struct clockdomain *clkdm, void *unused) +{ + if (!arch_clkdm || !arch_clkdm->clkdm_save_context) + return -EINVAL; + + return arch_clkdm->clkdm_save_context(clkdm); +} + +/** + * _clkdm_restore_context - restore context for control of this clkdm + * + * Restore the register values for this clockdomain. + */ +static int _clkdm_restore_context(struct clockdomain *clkdm, void *unused) +{ + if (!arch_clkdm || !arch_clkdm->clkdm_restore_context) + return -EINVAL; + + return arch_clkdm->clkdm_restore_context(clkdm); +} + +/** + * clkdm_save_context - Saves the context for each registered clkdm + * + * Save the context for each registered clockdomain. + */ +void clkdm_save_context(void) +{ + clkdm_for_each(_clkdm_save_context, NULL); +} + +/** + * clkdm_restore_context - Restores the context for each registered clkdm + * + * Restore the context for each registered clockdomain. + */ +void clkdm_restore_context(void) +{ + clkdm_for_each(_clkdm_restore_context, NULL); +} diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h new file mode 100644 index 000000000..68550b23c --- /dev/null +++ b/arch/arm/mach-omap2/clockdomain.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2/3 clockdomain framework functions + * + * Copyright (C) 2008, 2012 Texas Instruments, Inc. + * Copyright (C) 2008-2011 Nokia Corporation + * + * Paul Walmsley + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H +#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H + +#include <linux/init.h> + +#include "powerdomain.h" +#include "clock.h" + +/* + * Clockdomain flags + * + * XXX Document CLKDM_CAN_* flags + * + * CLKDM_NO_AUTODEPS: Prevent "autodeps" from being added/removed from this + * clockdomain. (Currently, this applies to OMAP3 clockdomains only.) + * CLKDM_ACTIVE_WITH_MPU: The PRCM guarantees that this clockdomain is + * active whenever the MPU is active. True for interconnects and + * the WKUP clockdomains. + * CLKDM_MISSING_IDLE_REPORTING: The idle status of the IP blocks and + * clocks inside this clockdomain are not taken into account by + * the PRCM when determining whether the clockdomain is idle. + * Without this flag, if the clockdomain is set to + * hardware-supervised idle mode, the PRCM may transition the + * enclosing powerdomain to a low power state, even when devices + * inside the clockdomain and powerdomain are in use. (An example + * of such a clockdomain is the EMU clockdomain on OMAP3/4.) If + * this flag is set, and the clockdomain does not support the + * force-sleep mode, then the HW_AUTO mode will be used to put the + * clockdomain to sleep. Similarly, if the clockdomain supports + * the force-wakeup mode, then it will be used whenever a clock or + * IP block inside the clockdomain is active, rather than the + * HW_AUTO mode. + */ +#define CLKDM_CAN_FORCE_SLEEP (1 << 0) +#define CLKDM_CAN_FORCE_WAKEUP (1 << 1) +#define CLKDM_CAN_ENABLE_AUTO (1 << 2) +#define CLKDM_CAN_DISABLE_AUTO (1 << 3) +#define CLKDM_NO_AUTODEPS (1 << 4) +#define CLKDM_ACTIVE_WITH_MPU (1 << 5) +#define CLKDM_MISSING_IDLE_REPORTING (1 << 6) + +#define CLKDM_CAN_HWSUP (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_DISABLE_AUTO) +#define CLKDM_CAN_SWSUP (CLKDM_CAN_FORCE_SLEEP | CLKDM_CAN_FORCE_WAKEUP) +#define CLKDM_CAN_HWSUP_SWSUP (CLKDM_CAN_SWSUP | CLKDM_CAN_HWSUP) + +/** + * struct clkdm_autodep - clkdm deps to add when entering/exiting hwsup mode + * @clkdm: clockdomain to add wkdep+sleepdep on - set name member only + * + * A clockdomain that should have wkdeps and sleepdeps added when a + * clockdomain should stay active in hwsup mode; and conversely, + * removed when the clockdomain should be allowed to go inactive in + * hwsup mode. + * + * Autodeps are deprecated and should be removed after + * omap_hwmod-based fine-grained module idle control is added. + */ +struct clkdm_autodep { + union { + const char *name; + struct clockdomain *ptr; + } clkdm; +}; + +/** + * struct clkdm_dep - encode dependencies between clockdomains + * @clkdm_name: clockdomain name + * @clkdm: pointer to the struct clockdomain of @clkdm_name + * @wkdep_usecount: Number of wakeup dependencies causing this clkdm to wake + * @sleepdep_usecount: Number of sleep deps that could prevent clkdm from idle + * + * Statically defined. @clkdm is resolved from @clkdm_name at runtime and + * should not be pre-initialized. + * + * XXX Should also include hardware (fixed) dependencies. + */ +struct clkdm_dep { + const char *clkdm_name; + struct clockdomain *clkdm; + s16 wkdep_usecount; + s16 sleepdep_usecount; +}; + +/* Possible flags for struct clockdomain._flags */ +#define _CLKDM_FLAG_HWSUP_ENABLED BIT(0) + +struct omap_hwmod; + +/** + * struct clockdomain - OMAP clockdomain + * @name: clockdomain name + * @pwrdm: powerdomain containing this clockdomain + * @clktrctrl_reg: CLKSTCTRL reg for the given clock domain + * @clktrctrl_mask: CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg + * @flags: Clockdomain capability flags + * @_flags: Flags for use only by internal clockdomain code + * @dep_bit: Bit shift of this clockdomain's PM_WKDEP/CM_SLEEPDEP bit + * @prcm_partition: (OMAP4 only) PRCM partition ID for this clkdm's registers + * @cm_inst: (OMAP4 only) CM instance register offset + * @clkdm_offs: (OMAP4 only) CM clockdomain register offset + * @wkdep_srcs: Clockdomains that can be told to wake this powerdomain up + * @sleepdep_srcs: Clockdomains that can be told to keep this clkdm from inact + * @usecount: Usecount tracking + * @forcewake_count: Usecount for forcing the domain active + * @node: list_head to link all clockdomains together + * + * @prcm_partition should be a macro from mach-omap2/prcm44xx.h (OMAP4 only) + * @cm_inst should be a macro ending in _INST from the OMAP4 CM instance + * definitions (OMAP4 only) + * @clkdm_offs should be a macro ending in _CDOFFS from the OMAP4 CM instance + * definitions (OMAP4 only) + */ +struct clockdomain { + const char *name; + union { + const char *name; + struct powerdomain *ptr; + } pwrdm; + const u16 clktrctrl_mask; + const u8 flags; + u8 _flags; + const u8 dep_bit; + const u8 prcm_partition; + const u16 cm_inst; + const u16 clkdm_offs; + struct clkdm_dep *wkdep_srcs; + struct clkdm_dep *sleepdep_srcs; + int usecount; + int forcewake_count; + struct list_head node; + u32 context; +}; + +/** + * struct clkdm_ops - Arch specific function implementations + * @clkdm_add_wkdep: Add a wakeup dependency between clk domains + * @clkdm_del_wkdep: Delete a wakeup dependency between clk domains + * @clkdm_read_wkdep: Read wakeup dependency state between clk domains + * @clkdm_clear_all_wkdeps: Remove all wakeup dependencies from the clk domain + * @clkdm_add_sleepdep: Add a sleep dependency between clk domains + * @clkdm_del_sleepdep: Delete a sleep dependency between clk domains + * @clkdm_read_sleepdep: Read sleep dependency state between clk domains + * @clkdm_clear_all_sleepdeps: Remove all sleep dependencies from the clk domain + * @clkdm_sleep: Force a clockdomain to sleep + * @clkdm_wakeup: Force a clockdomain to wakeup + * @clkdm_allow_idle: Enable hw supervised idle transitions for clock domain + * @clkdm_deny_idle: Disable hw supervised idle transitions for clock domain + * @clkdm_clk_enable: Put the clkdm in right state for a clock enable + * @clkdm_clk_disable: Put the clkdm in right state for a clock disable + * @clkdm_save_context: Save the current clkdm context + * @clkdm_restore_context: Restore the clkdm context + */ +struct clkdm_ops { + int (*clkdm_add_wkdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); + int (*clkdm_del_wkdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); + int (*clkdm_read_wkdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); + int (*clkdm_clear_all_wkdeps)(struct clockdomain *clkdm); + int (*clkdm_add_sleepdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); + int (*clkdm_del_sleepdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); + int (*clkdm_read_sleepdep)(struct clockdomain *clkdm1, struct clockdomain *clkdm2); + int (*clkdm_clear_all_sleepdeps)(struct clockdomain *clkdm); + int (*clkdm_sleep)(struct clockdomain *clkdm); + int (*clkdm_wakeup)(struct clockdomain *clkdm); + void (*clkdm_allow_idle)(struct clockdomain *clkdm); + void (*clkdm_deny_idle)(struct clockdomain *clkdm); + int (*clkdm_clk_enable)(struct clockdomain *clkdm); + int (*clkdm_clk_disable)(struct clockdomain *clkdm); + int (*clkdm_save_context)(struct clockdomain *clkdm); + int (*clkdm_restore_context)(struct clockdomain *clkdm); +}; + +int clkdm_register_platform_funcs(struct clkdm_ops *co); +int clkdm_register_autodeps(struct clkdm_autodep *ia); +int clkdm_register_clkdms(struct clockdomain **c); +int clkdm_complete_init(void); + +struct clockdomain *clkdm_lookup(const char *name); + +int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), + void *user); +struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm); + +int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); +int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); +int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); +int clkdm_clear_all_wkdeps(struct clockdomain *clkdm); +int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); +int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); +int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); +int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm); + +void clkdm_allow_idle_nolock(struct clockdomain *clkdm); +void clkdm_allow_idle(struct clockdomain *clkdm); +void clkdm_deny_idle_nolock(struct clockdomain *clkdm); +void clkdm_deny_idle(struct clockdomain *clkdm); +bool clkdm_in_hwsup(struct clockdomain *clkdm); +bool clkdm_missing_idle_reporting(struct clockdomain *clkdm); + +int clkdm_wakeup_nolock(struct clockdomain *clkdm); +int clkdm_wakeup(struct clockdomain *clkdm); +int clkdm_sleep_nolock(struct clockdomain *clkdm); +int clkdm_sleep(struct clockdomain *clkdm); + +int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk); +int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk); +int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh); +int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh); + +void clkdm_save_context(void); +void clkdm_restore_context(void); + +extern void __init omap242x_clockdomains_init(void); +extern void __init omap243x_clockdomains_init(void); +extern void __init omap3xxx_clockdomains_init(void); +extern void __init am33xx_clockdomains_init(void); +extern void __init ti814x_clockdomains_init(void); +extern void __init ti816x_clockdomains_init(void); +extern void __init omap44xx_clockdomains_init(void); +extern void __init omap54xx_clockdomains_init(void); +extern void __init dra7xx_clockdomains_init(void); +void am43xx_clockdomains_init(void); + +extern void clkdm_add_autodeps(struct clockdomain *clkdm); +extern void clkdm_del_autodeps(struct clockdomain *clkdm); + +extern struct clkdm_ops omap2_clkdm_operations; +extern struct clkdm_ops omap3_clkdm_operations; +extern struct clkdm_ops omap4_clkdm_operations; +extern struct clkdm_ops am33xx_clkdm_operations; +extern struct clkdm_ops am43xx_clkdm_operations; + +extern struct clkdm_dep gfx_24xx_wkdeps[]; +extern struct clkdm_dep dsp_24xx_wkdeps[]; +extern struct clockdomain wkup_common_clkdm; + +#endif diff --git a/arch/arm/mach-omap2/clockdomains2420_data.c b/arch/arm/mach-omap2/clockdomains2420_data.c new file mode 100644 index 000000000..ac4ffb5d2 --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains2420_data.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP2420 clockdomains + * + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * + * Paul Walmsley, Jouni Högander + * + * This file contains clockdomains and clockdomain wakeup dependencies + * for OMAP2420 chips. Some notes: + * + * A useful validation rule for struct clockdomain: Any clockdomain + * referenced by a wkdep_srcs must have a dep_bit assigned. So + * wkdep_srcs are really just software-controllable dependencies. + * Non-software-controllable dependencies do exist, but they are not + * encoded below (yet). + * + * 24xx does not support programmable sleep dependencies (SLEEPDEP) + * + * The overly-specific dep_bit names are due to a bit name collision + * with CM_FCLKEN_{DSP,IVA2}. The DSP/IVA2 PM_WKDEP and CM_SLEEPDEP shift + * value are the same for all powerdomains: 2 + * + * XXX should dep_bit be a mask, so we can test to see if it is 0 as a + * sanity check? + * XXX encode hardware fixed wakeup dependencies -- esp. for 3430 CORE + */ + +/* + * To-Do List + * -> Port the Sleep/Wakeup dependencies for the domains + * from the Power domain framework + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "soc.h" +#include "clockdomain.h" +#include "prm2xxx_3xxx.h" +#include "cm2xxx_3xxx.h" +#include "cm-regbits-24xx.h" +#include "prm-regbits-24xx.h" + +/* + * Clockdomain dependencies for wkdeps + * + * XXX Hardware dependencies (e.g., dependencies that cannot be + * changed in software) are not included here yet, but should be. + */ + +/* Wakeup dependency source arrays */ + +/* 2420-specific possible wakeup dependencies */ + +/* 2420 PM_WKDEP_MPU: CORE, DSP, WKUP */ +static struct clkdm_dep mpu_2420_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "dsp_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* 2420 PM_WKDEP_CORE: DSP, GFX, MPU, WKUP */ +static struct clkdm_dep core_2420_wkdeps[] = { + { .clkdm_name = "dsp_clkdm" }, + { .clkdm_name = "gfx_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* + * 2420-only clockdomains + */ + +static struct clockdomain mpu_2420_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .wkdep_srcs = mpu_2420_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_MPU_MASK, +}; + +static struct clockdomain iva1_2420_clkdm = { + .name = "iva1_clkdm", + .pwrdm = { .name = "dsp_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .dep_bit = OMAP24XX_PM_WKDEP_MPU_EN_DSP_SHIFT, + .wkdep_srcs = dsp_24xx_wkdeps, + .clktrctrl_mask = OMAP2420_AUTOSTATE_IVA_MASK, +}; + +static struct clockdomain dsp_2420_clkdm = { + .name = "dsp_clkdm", + .pwrdm = { .name = "dsp_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_DSP_MASK, +}; + +static struct clockdomain gfx_2420_clkdm = { + .name = "gfx_clkdm", + .pwrdm = { .name = "gfx_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = gfx_24xx_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_GFX_MASK, +}; + +static struct clockdomain core_l3_2420_clkdm = { + .name = "core_l3_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .wkdep_srcs = core_2420_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_L3_MASK, +}; + +static struct clockdomain core_l4_2420_clkdm = { + .name = "core_l4_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .wkdep_srcs = core_2420_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_L4_MASK, +}; + +static struct clockdomain dss_2420_clkdm = { + .name = "dss_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_DSS_MASK, +}; + +static struct clockdomain *clockdomains_omap242x[] __initdata = { + &wkup_common_clkdm, + &mpu_2420_clkdm, + &iva1_2420_clkdm, + &dsp_2420_clkdm, + &gfx_2420_clkdm, + &core_l3_2420_clkdm, + &core_l4_2420_clkdm, + &dss_2420_clkdm, + NULL, +}; + +void __init omap242x_clockdomains_init(void) +{ + if (!cpu_is_omap242x()) + return; + + clkdm_register_platform_funcs(&omap2_clkdm_operations); + clkdm_register_clkdms(clockdomains_omap242x); + clkdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/clockdomains2430_data.c b/arch/arm/mach-omap2/clockdomains2430_data.c new file mode 100644 index 000000000..0e97e178a --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains2430_data.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP2xxx clockdomains + * + * Copyright (C) 2008-2009 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * + * Paul Walmsley, Jouni Högander + * + * This file contains clockdomains and clockdomain wakeup dependencies + * for OMAP2xxx chips. Some notes: + * + * A useful validation rule for struct clockdomain: Any clockdomain + * referenced by a wkdep_srcs must have a dep_bit assigned. So + * wkdep_srcs are really just software-controllable dependencies. + * Non-software-controllable dependencies do exist, but they are not + * encoded below (yet). + * + * 24xx does not support programmable sleep dependencies (SLEEPDEP) + * + * The overly-specific dep_bit names are due to a bit name collision + * with CM_FCLKEN_{DSP,IVA2}. The DSP/IVA2 PM_WKDEP and CM_SLEEPDEP shift + * value are the same for all powerdomains: 2 + * + * XXX should dep_bit be a mask, so we can test to see if it is 0 as a + * sanity check? + * XXX encode hardware fixed wakeup dependencies -- esp. for 3430 CORE + */ + +/* + * To-Do List + * -> Port the Sleep/Wakeup dependencies for the domains + * from the Power domain framework + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "soc.h" +#include "clockdomain.h" +#include "prm2xxx_3xxx.h" +#include "cm2xxx_3xxx.h" +#include "cm-regbits-24xx.h" +#include "prm-regbits-24xx.h" + +/* + * Clockdomain dependencies for wkdeps + * + * XXX Hardware dependencies (e.g., dependencies that cannot be + * changed in software) are not included here yet, but should be. + */ + +/* Wakeup dependency source arrays */ + +/* 2430-specific possible wakeup dependencies */ + +/* 2430 PM_WKDEP_CORE: DSP, GFX, MPU, WKUP, MDM */ +static struct clkdm_dep core_2430_wkdeps[] = { + { .clkdm_name = "dsp_clkdm" }, + { .clkdm_name = "gfx_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { .clkdm_name = "mdm_clkdm" }, + { NULL }, +}; + +/* 2430 PM_WKDEP_MPU: CORE, DSP, WKUP, MDM */ +static struct clkdm_dep mpu_2430_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "dsp_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { .clkdm_name = "mdm_clkdm" }, + { NULL }, +}; + +/* 2430 PM_WKDEP_MDM: CORE, MPU, WKUP */ +static struct clkdm_dep mdm_2430_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* + * 2430-only clockdomains + */ + +static struct clockdomain mpu_2430_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = mpu_2430_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_MPU_MASK, +}; + +/* Another case of bit name collisions between several registers: EN_MDM */ +static struct clockdomain mdm_clkdm = { + .name = "mdm_clkdm", + .pwrdm = { .name = "mdm_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .dep_bit = OMAP2430_PM_WKDEP_MPU_EN_MDM_SHIFT, + .wkdep_srcs = mdm_2430_wkdeps, + .clktrctrl_mask = OMAP2430_AUTOSTATE_MDM_MASK, +}; + +static struct clockdomain dsp_2430_clkdm = { + .name = "dsp_clkdm", + .pwrdm = { .name = "dsp_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .dep_bit = OMAP24XX_PM_WKDEP_MPU_EN_DSP_SHIFT, + .wkdep_srcs = dsp_24xx_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_DSP_MASK, +}; + +static struct clockdomain gfx_2430_clkdm = { + .name = "gfx_clkdm", + .pwrdm = { .name = "gfx_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = gfx_24xx_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_GFX_MASK, +}; + +/* + * XXX add usecounting for clkdm dependencies, otherwise the presence + * of a single dep bit for core_l3_24xx_clkdm and core_l4_24xx_clkdm + * could cause trouble + */ +static struct clockdomain core_l3_2430_clkdm = { + .name = "core_l3_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .dep_bit = OMAP24XX_EN_CORE_SHIFT, + .wkdep_srcs = core_2430_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_L3_MASK, +}; + +/* + * XXX add usecounting for clkdm dependencies, otherwise the presence + * of a single dep bit for core_l3_24xx_clkdm and core_l4_24xx_clkdm + * could cause trouble + */ +static struct clockdomain core_l4_2430_clkdm = { + .name = "core_l4_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .dep_bit = OMAP24XX_EN_CORE_SHIFT, + .wkdep_srcs = core_2430_wkdeps, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_L4_MASK, +}; + +static struct clockdomain dss_2430_clkdm = { + .name = "dss_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .clktrctrl_mask = OMAP24XX_AUTOSTATE_DSS_MASK, +}; + +static struct clockdomain *clockdomains_omap243x[] __initdata = { + &wkup_common_clkdm, + &mpu_2430_clkdm, + &mdm_clkdm, + &dsp_2430_clkdm, + &gfx_2430_clkdm, + &core_l3_2430_clkdm, + &core_l4_2430_clkdm, + &dss_2430_clkdm, + NULL, +}; + +void __init omap243x_clockdomains_init(void) +{ + if (!cpu_is_omap243x()) + return; + + clkdm_register_platform_funcs(&omap2_clkdm_operations); + clkdm_register_clkdms(clockdomains_omap243x); + clkdm_complete_init(); +} + diff --git a/arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c b/arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c new file mode 100644 index 000000000..220c22f5e --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains2xxx_3xxx_data.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP2/3 clockdomain common data + * + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * + * Paul Walmsley, Jouni Högander + * + * This file contains clockdomains and clockdomain wakeup/sleep + * dependencies for the OMAP2/3 chips. Some notes: + * + * A useful validation rule for struct clockdomain: Any clockdomain + * referenced by a wkdep_srcs or sleepdep_srcs array must have a + * dep_bit assigned. So wkdep_srcs/sleepdep_srcs are really just + * software-controllable dependencies. Non-software-controllable + * dependencies do exist, but they are not encoded below (yet). + * + * 24xx does not support programmable sleep dependencies (SLEEPDEP) + * + * The overly-specific dep_bit names are due to a bit name collision + * with CM_FCLKEN_{DSP,IVA2}. The DSP/IVA2 PM_WKDEP and CM_SLEEPDEP shift + * value are the same for all powerdomains: 2 + * + * XXX should dep_bit be a mask, so we can test to see if it is 0 as a + * sanity check? + * XXX encode hardware fixed wakeup dependencies -- esp. for 3430 CORE + */ + +/* + * To-Do List + * -> Port the Sleep/Wakeup dependencies for the domains + * from the Power domain framework + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "prm2xxx_3xxx.h" +#include "cm2xxx_3xxx.h" +#include "cm-regbits-24xx.h" +#include "cm-regbits-34xx.h" +#include "cm-regbits-44xx.h" +#include "prm-regbits-24xx.h" +#include "prm-regbits-34xx.h" + +/* + * Clockdomain dependencies for wkdeps/sleepdeps + * + * XXX Hardware dependencies (e.g., dependencies that cannot be + * changed in software) are not included here yet, but should be. + */ + +/* Wakeup dependency source arrays */ + +/* 2xxx-specific possible dependencies */ + +/* 2xxx PM_WKDEP_GFX: CORE, MPU, WKUP */ +struct clkdm_dep gfx_24xx_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* 2xxx PM_WKDEP_DSP: CORE, MPU, WKUP */ +struct clkdm_dep dsp_24xx_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + + +/* + * OMAP2/3-common clockdomains + * + * Even though the 2420 has a single PRCM module from the + * interconnect's perspective, internally it does appear to have + * separate PRM and CM clockdomains. The usual test case is + * sys_clkout/sys_clkout2. + */ + +/* This is an implicit clockdomain - it is never defined as such in TRM */ +struct clockdomain wkup_common_clkdm = { + .name = "wkup_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .dep_bit = OMAP_EN_WKUP_SHIFT, + .flags = CLKDM_ACTIVE_WITH_MPU, +}; diff --git a/arch/arm/mach-omap2/clockdomains33xx_data.c b/arch/arm/mach-omap2/clockdomains33xx_data.c new file mode 100644 index 000000000..87f4e927e --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains33xx_data.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AM33XX Clock Domain data. + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + * Vaibhav Hiremath <hvaibhav@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "cm.h" +#include "cm33xx.h" +#include "cm-regbits-33xx.h" + +static struct clockdomain l4ls_am33xx_clkdm = { + .name = "l4ls_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_L4LS_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3s_am33xx_clkdm = { + .name = "l3s_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_L3S_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4fw_am33xx_clkdm = { + .name = "l4fw_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_L4FW_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3_am33xx_clkdm = { + .name = "l3_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_L3_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4hs_am33xx_clkdm = { + .name = "l4hs_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_L4HS_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain ocpwp_l3_am33xx_clkdm = { + .name = "ocpwp_l3_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_OCPWP_L3_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain pruss_ocp_am33xx_clkdm = { + .name = "pruss_ocp_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_PRUSS_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain cpsw_125mhz_am33xx_clkdm = { + .name = "cpsw_125mhz_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_CPSW_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain lcdc_am33xx_clkdm = { + .name = "lcdc_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_LCDC_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain clk_24mhz_am33xx_clkdm = { + .name = "clk_24mhz_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .cm_inst = AM33XX_CM_PER_MOD, + .clkdm_offs = AM33XX_CM_PER_CLK_24MHZ_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_wkup_am33xx_clkdm = { + .name = "l4_wkup_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .cm_inst = AM33XX_CM_WKUP_MOD, + .clkdm_offs = AM33XX_CM_WKUP_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3_aon_am33xx_clkdm = { + .name = "l3_aon_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .cm_inst = AM33XX_CM_WKUP_MOD, + .clkdm_offs = AM33XX_CM_L3_AON_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_wkup_aon_am33xx_clkdm = { + .name = "l4_wkup_aon_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .cm_inst = AM33XX_CM_WKUP_MOD, + .clkdm_offs = AM33XX_CM_L4_WKUP_AON_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain mpu_am33xx_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .cm_inst = AM33XX_CM_MPU_MOD, + .clkdm_offs = AM33XX_CM_MPU_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_rtc_am33xx_clkdm = { + .name = "l4_rtc_clkdm", + .pwrdm = { .name = "rtc_pwrdm" }, + .cm_inst = AM33XX_CM_RTC_MOD, + .clkdm_offs = AM33XX_CM_RTC_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain gfx_l3_am33xx_clkdm = { + .name = "gfx_l3_clkdm", + .pwrdm = { .name = "gfx_pwrdm" }, + .cm_inst = AM33XX_CM_GFX_MOD, + .clkdm_offs = AM33XX_CM_GFX_L3_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain gfx_l4ls_gfx_am33xx_clkdm = { + .name = "gfx_l4ls_gfx_clkdm", + .pwrdm = { .name = "gfx_pwrdm" }, + .cm_inst = AM33XX_CM_GFX_MOD, + .clkdm_offs = AM33XX_CM_GFX_L4LS_GFX_CLKSTCTRL__1_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_cefuse_am33xx_clkdm = { + .name = "l4_cefuse_clkdm", + .pwrdm = { .name = "cefuse_pwrdm" }, + .cm_inst = AM33XX_CM_CEFUSE_MOD, + .clkdm_offs = AM33XX_CM_CEFUSE_CLKSTCTRL_OFFSET, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain *clockdomains_am33xx[] __initdata = { + &l4ls_am33xx_clkdm, + &l3s_am33xx_clkdm, + &l4fw_am33xx_clkdm, + &l3_am33xx_clkdm, + &l4hs_am33xx_clkdm, + &ocpwp_l3_am33xx_clkdm, + &pruss_ocp_am33xx_clkdm, + &cpsw_125mhz_am33xx_clkdm, + &lcdc_am33xx_clkdm, + &clk_24mhz_am33xx_clkdm, + &l4_wkup_am33xx_clkdm, + &l3_aon_am33xx_clkdm, + &l4_wkup_aon_am33xx_clkdm, + &mpu_am33xx_clkdm, + &l4_rtc_am33xx_clkdm, + &gfx_l3_am33xx_clkdm, + &gfx_l4ls_gfx_am33xx_clkdm, + &l4_cefuse_am33xx_clkdm, + NULL, +}; + +void __init am33xx_clockdomains_init(void) +{ + clkdm_register_platform_funcs(&am33xx_clkdm_operations); + clkdm_register_clkdms(clockdomains_am33xx); + clkdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/clockdomains3xxx_data.c b/arch/arm/mach-omap2/clockdomains3xxx_data.c new file mode 100644 index 000000000..94bbd3831 --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains3xxx_data.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP3xxx clockdomains + * + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * + * Paul Walmsley, Jouni Högander + * + * This file contains clockdomains and clockdomain wakeup/sleep + * dependencies for the OMAP3xxx chips. Some notes: + * + * A useful validation rule for struct clockdomain: Any clockdomain + * referenced by a wkdep_srcs or sleepdep_srcs array must have a + * dep_bit assigned. So wkdep_srcs/sleepdep_srcs are really just + * software-controllable dependencies. Non-software-controllable + * dependencies do exist, but they are not encoded below (yet). + * + * The overly-specific dep_bit names are due to a bit name collision + * with CM_FCLKEN_{DSP,IVA2}. The DSP/IVA2 PM_WKDEP and CM_SLEEPDEP shift + * value are the same for all powerdomains: 2 + * + * XXX should dep_bit be a mask, so we can test to see if it is 0 as a + * sanity check? + * XXX encode hardware fixed wakeup dependencies -- esp. for 3430 CORE + */ + +/* + * To-Do List + * -> Port the Sleep/Wakeup dependencies for the domains + * from the Power domain framework + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "soc.h" +#include "clockdomain.h" +#include "prm2xxx_3xxx.h" +#include "cm2xxx_3xxx.h" +#include "cm-regbits-34xx.h" +#include "prm-regbits-34xx.h" + +/* + * Clockdomain dependencies for wkdeps/sleepdeps + * + * XXX Hardware dependencies (e.g., dependencies that cannot be + * changed in software) are not included here yet, but should be. + */ + +/* OMAP3-specific possible dependencies */ + +/* + * 3430ES1 PM_WKDEP_GFX: adds IVA2, removes CORE + * 3430ES2 PM_WKDEP_SGX: adds IVA2, removes CORE + */ +static struct clkdm_dep gfx_sgx_3xxx_wkdeps[] = { + { .clkdm_name = "iva2_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep gfx_sgx_am35x_wkdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* 3430: PM_WKDEP_PER: CORE, IVA2, MPU, WKUP */ +static struct clkdm_dep per_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "iva2_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep per_am35x_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* 3430ES2: PM_WKDEP_USBHOST: CORE, IVA2, MPU, WKUP */ +static struct clkdm_dep usbhost_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "iva2_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep usbhost_am35x_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* 3430 PM_WKDEP_MPU: CORE, IVA2, DSS, PER */ +static struct clkdm_dep mpu_3xxx_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "iva2_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "per_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep mpu_am35x_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "per_clkdm" }, + { NULL }, +}; + +/* 3430 PM_WKDEP_IVA2: CORE, MPU, WKUP, DSS, PER */ +static struct clkdm_dep iva2_wkdeps[] = { + { .clkdm_name = "core_l3_clkdm" }, + { .clkdm_name = "core_l4_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "per_clkdm" }, + { NULL }, +}; + +/* 3430 PM_WKDEP_CAM: IVA2, MPU, WKUP */ +static struct clkdm_dep cam_wkdeps[] = { + { .clkdm_name = "iva2_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* 3430 PM_WKDEP_DSS: IVA2, MPU, WKUP */ +static struct clkdm_dep dss_wkdeps[] = { + { .clkdm_name = "iva2_clkdm" }, + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dss_am35x_wkdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "wkup_clkdm" }, + { NULL }, +}; + +/* 3430: PM_WKDEP_NEON: MPU */ +static struct clkdm_dep neon_wkdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { NULL }, +}; + +/* Sleep dependency source arrays for OMAP3-specific clkdms */ + +/* 3430: CM_SLEEPDEP_DSS: MPU, IVA */ +static struct clkdm_dep dss_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "iva2_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dss_am35x_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { NULL }, +}; + +/* 3430: CM_SLEEPDEP_PER: MPU, IVA */ +static struct clkdm_dep per_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "iva2_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep per_am35x_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { NULL }, +}; + +/* 3430ES2: CM_SLEEPDEP_USBHOST: MPU, IVA */ +static struct clkdm_dep usbhost_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { .clkdm_name = "iva2_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep usbhost_am35x_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { NULL }, +}; + +/* 3430: CM_SLEEPDEP_CAM: MPU */ +static struct clkdm_dep cam_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { NULL }, +}; + +/* + * 3430ES1: CM_SLEEPDEP_GFX: MPU + * 3430ES2: CM_SLEEPDEP_SGX: MPU + * These can share data since they will never be present simultaneously + * on the same device. + */ +static struct clkdm_dep gfx_sgx_sleepdeps[] = { + { .clkdm_name = "mpu_clkdm" }, + { NULL }, +}; + +/* + * OMAP3 clockdomains + */ + +static struct clockdomain mpu_3xxx_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .flags = CLKDM_CAN_HWSUP | CLKDM_CAN_FORCE_WAKEUP, + .dep_bit = OMAP3430_EN_MPU_SHIFT, + .wkdep_srcs = mpu_3xxx_wkdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_MPU_MASK, +}; + +static struct clockdomain mpu_am35x_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .flags = CLKDM_CAN_HWSUP | CLKDM_CAN_FORCE_WAKEUP, + .dep_bit = OMAP3430_EN_MPU_SHIFT, + .wkdep_srcs = mpu_am35x_wkdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_MPU_MASK, +}; + +static struct clockdomain neon_clkdm = { + .name = "neon_clkdm", + .pwrdm = { .name = "neon_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = neon_wkdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_NEON_MASK, +}; + +static struct clockdomain iva2_clkdm = { + .name = "iva2_clkdm", + .pwrdm = { .name = "iva2_pwrdm" }, + .flags = CLKDM_CAN_SWSUP, + .dep_bit = OMAP3430_PM_WKDEP_MPU_EN_IVA2_SHIFT, + .wkdep_srcs = iva2_wkdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_IVA2_MASK, +}; + +static struct clockdomain gfx_3430es1_clkdm = { + .name = "gfx_clkdm", + .pwrdm = { .name = "gfx_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = gfx_sgx_3xxx_wkdeps, + .sleepdep_srcs = gfx_sgx_sleepdeps, + .clktrctrl_mask = OMAP3430ES1_CLKTRCTRL_GFX_MASK, +}; + +static struct clockdomain sgx_clkdm = { + .name = "sgx_clkdm", + .pwrdm = { .name = "sgx_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = gfx_sgx_3xxx_wkdeps, + .sleepdep_srcs = gfx_sgx_sleepdeps, + .clktrctrl_mask = OMAP3430ES2_CLKTRCTRL_SGX_MASK, +}; + +static struct clockdomain sgx_am35x_clkdm = { + .name = "sgx_clkdm", + .pwrdm = { .name = "sgx_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = gfx_sgx_am35x_wkdeps, + .sleepdep_srcs = gfx_sgx_sleepdeps, + .clktrctrl_mask = OMAP3430ES2_CLKTRCTRL_SGX_MASK, +}; + +/* + * The die-to-die clockdomain was documented in the 34xx ES1 TRM, but + * then that information was removed from the 34xx ES2+ TRM. It is + * unclear whether the core is still there, but the clockdomain logic + * is there, and must be programmed to an appropriate state if the + * CORE clockdomain is to become inactive. + */ +static struct clockdomain d2d_clkdm = { + .name = "d2d_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .clktrctrl_mask = OMAP3430ES1_CLKTRCTRL_D2D_MASK, +}; + +/* + * XXX add usecounting for clkdm dependencies, otherwise the presence + * of a single dep bit for core_l3_3xxx_clkdm and core_l4_3xxx_clkdm + * could cause trouble + */ +static struct clockdomain core_l3_3xxx_clkdm = { + .name = "core_l3_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .dep_bit = OMAP3430_EN_CORE_SHIFT, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_L3_MASK, +}; + +/* + * XXX add usecounting for clkdm dependencies, otherwise the presence + * of a single dep bit for core_l3_3xxx_clkdm and core_l4_3xxx_clkdm + * could cause trouble + */ +static struct clockdomain core_l4_3xxx_clkdm = { + .name = "core_l4_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP, + .dep_bit = OMAP3430_EN_CORE_SHIFT, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_L4_MASK, +}; + +/* Another case of bit name collisions between several registers: EN_DSS */ +static struct clockdomain dss_3xxx_clkdm = { + .name = "dss_clkdm", + .pwrdm = { .name = "dss_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .dep_bit = OMAP3430_PM_WKDEP_MPU_EN_DSS_SHIFT, + .wkdep_srcs = dss_wkdeps, + .sleepdep_srcs = dss_sleepdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_DSS_MASK, +}; + +static struct clockdomain dss_am35x_clkdm = { + .name = "dss_clkdm", + .pwrdm = { .name = "dss_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .dep_bit = OMAP3430_PM_WKDEP_MPU_EN_DSS_SHIFT, + .wkdep_srcs = dss_am35x_wkdeps, + .sleepdep_srcs = dss_am35x_sleepdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_DSS_MASK, +}; + +static struct clockdomain cam_clkdm = { + .name = "cam_clkdm", + .pwrdm = { .name = "cam_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = cam_wkdeps, + .sleepdep_srcs = cam_sleepdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_CAM_MASK, +}; + +static struct clockdomain usbhost_clkdm = { + .name = "usbhost_clkdm", + .pwrdm = { .name = "usbhost_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = usbhost_wkdeps, + .sleepdep_srcs = usbhost_sleepdeps, + .clktrctrl_mask = OMAP3430ES2_CLKTRCTRL_USBHOST_MASK, +}; + +static struct clockdomain usbhost_am35x_clkdm = { + .name = "usbhost_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .wkdep_srcs = usbhost_am35x_wkdeps, + .sleepdep_srcs = usbhost_am35x_sleepdeps, + .clktrctrl_mask = OMAP3430ES2_CLKTRCTRL_USBHOST_MASK, +}; + +static struct clockdomain per_clkdm = { + .name = "per_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .dep_bit = OMAP3430_EN_PER_SHIFT, + .wkdep_srcs = per_wkdeps, + .sleepdep_srcs = per_sleepdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_PER_MASK, +}; + +static struct clockdomain per_am35x_clkdm = { + .name = "per_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .flags = CLKDM_CAN_HWSUP_SWSUP, + .dep_bit = OMAP3430_EN_PER_SHIFT, + .wkdep_srcs = per_am35x_wkdeps, + .sleepdep_srcs = per_am35x_sleepdeps, + .clktrctrl_mask = OMAP3430_CLKTRCTRL_PER_MASK, +}; + +static struct clockdomain emu_clkdm = { + .name = "emu_clkdm", + .pwrdm = { .name = "emu_pwrdm" }, + .flags = (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_SWSUP | + CLKDM_MISSING_IDLE_REPORTING), + .clktrctrl_mask = OMAP3430_CLKTRCTRL_EMU_MASK, +}; + +static struct clockdomain dpll1_clkdm = { + .name = "dpll1_clkdm", + .pwrdm = { .name = "dpll1_pwrdm" }, +}; + +static struct clockdomain dpll2_clkdm = { + .name = "dpll2_clkdm", + .pwrdm = { .name = "dpll2_pwrdm" }, +}; + +static struct clockdomain dpll3_clkdm = { + .name = "dpll3_clkdm", + .pwrdm = { .name = "dpll3_pwrdm" }, +}; + +static struct clockdomain dpll4_clkdm = { + .name = "dpll4_clkdm", + .pwrdm = { .name = "dpll4_pwrdm" }, +}; + +static struct clockdomain dpll5_clkdm = { + .name = "dpll5_clkdm", + .pwrdm = { .name = "dpll5_pwrdm" }, +}; + +/* + * Clockdomain hwsup dependencies + */ + +static struct clkdm_autodep clkdm_autodeps[] = { + { + .clkdm = { .name = "mpu_clkdm" }, + }, + { + .clkdm = { .name = "iva2_clkdm" }, + }, + { + .clkdm = { .name = NULL }, + } +}; + +static struct clkdm_autodep clkdm_am35x_autodeps[] = { + { + .clkdm = { .name = "mpu_clkdm" }, + }, + { + .clkdm = { .name = NULL }, + } +}; + +/* + * + */ + +static struct clockdomain *clockdomains_common[] __initdata = { + &wkup_common_clkdm, + &neon_clkdm, + &core_l3_3xxx_clkdm, + &core_l4_3xxx_clkdm, + &emu_clkdm, + &dpll1_clkdm, + &dpll3_clkdm, + &dpll4_clkdm, + NULL +}; + +static struct clockdomain *clockdomains_omap3430[] __initdata = { + &mpu_3xxx_clkdm, + &iva2_clkdm, + &d2d_clkdm, + &dss_3xxx_clkdm, + &cam_clkdm, + &per_clkdm, + &dpll2_clkdm, + NULL +}; + +static struct clockdomain *clockdomains_omap3430es1[] __initdata = { + &gfx_3430es1_clkdm, + NULL, +}; + +static struct clockdomain *clockdomains_omap3430es2plus[] __initdata = { + &sgx_clkdm, + &dpll5_clkdm, + &usbhost_clkdm, + NULL, +}; + +static struct clockdomain *clockdomains_am35x[] __initdata = { + &mpu_am35x_clkdm, + &sgx_am35x_clkdm, + &dss_am35x_clkdm, + &per_am35x_clkdm, + &usbhost_am35x_clkdm, + &dpll5_clkdm, + NULL +}; + +void __init omap3xxx_clockdomains_init(void) +{ + struct clockdomain **sc; + unsigned int rev; + + if (!cpu_is_omap34xx()) + return; + + clkdm_register_platform_funcs(&omap3_clkdm_operations); + clkdm_register_clkdms(clockdomains_common); + + rev = omap_rev(); + + if (rev == AM35XX_REV_ES1_0 || rev == AM35XX_REV_ES1_1) { + clkdm_register_clkdms(clockdomains_am35x); + clkdm_register_autodeps(clkdm_am35x_autodeps); + } else { + clkdm_register_clkdms(clockdomains_omap3430); + + sc = (rev == OMAP3430_REV_ES1_0) ? + clockdomains_omap3430es1 : clockdomains_omap3430es2plus; + + clkdm_register_clkdms(sc); + clkdm_register_autodeps(clkdm_autodeps); + } + + clkdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/clockdomains43xx_data.c b/arch/arm/mach-omap2/clockdomains43xx_data.c new file mode 100644 index 000000000..c96a2b1ef --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains43xx_data.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AM43xx Clock domains framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "prcm44xx.h" +#include "prcm43xx.h" + +static struct clockdomain l4_cefuse_43xx_clkdm = { + .name = "l4_cefuse_clkdm", + .pwrdm = { .name = "cefuse_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_CEFUSE_INST, + .clkdm_offs = AM43XX_CM_CEFUSE_CEFUSE_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain mpu_43xx_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_MPU_INST, + .clkdm_offs = AM43XX_CM_MPU_MPU_CDOFFS, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l4ls_43xx_clkdm = { + .name = "l4ls_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_L4LS_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain tamper_43xx_clkdm = { + .name = "tamper_clkdm", + .pwrdm = { .name = "tamper_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_TAMPER_INST, + .clkdm_offs = AM43XX_CM_TAMPER_TAMPER_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_rtc_43xx_clkdm = { + .name = "l4_rtc_clkdm", + .pwrdm = { .name = "rtc_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_RTC_INST, + .clkdm_offs = AM43XX_CM_RTC_RTC_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain pruss_ocp_43xx_clkdm = { + .name = "pruss_ocp_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_ICSS_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain ocpwp_l3_43xx_clkdm = { + .name = "ocpwp_l3_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_OCPWP_L3_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3s_tsc_43xx_clkdm = { + .name = "l3s_tsc_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_WKUP_INST, + .clkdm_offs = AM43XX_CM_WKUP_L3S_TSC_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain lcdc_43xx_clkdm = { + .name = "lcdc_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_LCDC_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain dss_43xx_clkdm = { + .name = "dss_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_DSS_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3_aon_43xx_clkdm = { + .name = "l3_aon_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_WKUP_INST, + .clkdm_offs = AM43XX_CM_WKUP_L3_AON_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain emif_43xx_clkdm = { + .name = "emif_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_EMIF_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_wkup_aon_43xx_clkdm = { + .name = "l4_wkup_aon_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_WKUP_INST, + .clkdm_offs = AM43XX_CM_WKUP_L4_WKUP_AON_CDOFFS, +}; + +static struct clockdomain l3_43xx_clkdm = { + .name = "l3_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_L3_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_wkup_43xx_clkdm = { + .name = "l4_wkup_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_WKUP_INST, + .clkdm_offs = AM43XX_CM_WKUP_WKUP_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain cpsw_125mhz_43xx_clkdm = { + .name = "cpsw_125mhz_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_CPSW_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain gfx_l3_43xx_clkdm = { + .name = "gfx_l3_clkdm", + .pwrdm = { .name = "gfx_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_GFX_INST, + .clkdm_offs = AM43XX_CM_GFX_GFX_L3_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3s_43xx_clkdm = { + .name = "l3s_clkdm", + .pwrdm = { .name = "per_pwrdm" }, + .prcm_partition = AM43XX_CM_PARTITION, + .cm_inst = AM43XX_CM_PER_INST, + .clkdm_offs = AM43XX_CM_PER_L3S_CDOFFS, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain *clockdomains_am43xx[] __initdata = { + &l4_cefuse_43xx_clkdm, + &mpu_43xx_clkdm, + &l4ls_43xx_clkdm, + &tamper_43xx_clkdm, + &l4_rtc_43xx_clkdm, + &pruss_ocp_43xx_clkdm, + &ocpwp_l3_43xx_clkdm, + &l3s_tsc_43xx_clkdm, + &lcdc_43xx_clkdm, + &dss_43xx_clkdm, + &l3_aon_43xx_clkdm, + &emif_43xx_clkdm, + &l4_wkup_aon_43xx_clkdm, + &l3_43xx_clkdm, + &l4_wkup_43xx_clkdm, + &cpsw_125mhz_43xx_clkdm, + &gfx_l3_43xx_clkdm, + &l3s_43xx_clkdm, + NULL +}; + +void __init am43xx_clockdomains_init(void) +{ + clkdm_register_platform_funcs(&am43xx_clkdm_operations); + clkdm_register_clkdms(clockdomains_am43xx); + clkdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/clockdomains44xx_data.c b/arch/arm/mach-omap2/clockdomains44xx_data.c new file mode 100644 index 000000000..8285be7c1 --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains44xx_data.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 Clock domains framework + * + * Copyright (C) 2009-2011 Texas Instruments, Inc. + * Copyright (C) 2009-2011 Nokia Corporation + * + * Abhijit Pagare (abhijitpagare@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * Paul Walmsley (paul@pwsan.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "cm1_44xx.h" +#include "cm2_44xx.h" + +#include "cm-regbits-44xx.h" +#include "prm44xx.h" +#include "prcm44xx.h" +#include "prcm_mpu44xx.h" + +/* Static Dependencies for OMAP4 Clock Domains */ + +static struct clkdm_dep d2d_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_2_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { .clkdm_name = "l3_init_clkdm" }, + { .clkdm_name = "l4_cfg_clkdm" }, + { .clkdm_name = "l4_per_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep ducati_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_2_clkdm" }, + { .clkdm_name = "l3_dss_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { .clkdm_name = "l3_gfx_clkdm" }, + { .clkdm_name = "l3_init_clkdm" }, + { .clkdm_name = "l4_cfg_clkdm" }, + { .clkdm_name = "l4_per_clkdm" }, + { .clkdm_name = "l4_secure_clkdm" }, + { .clkdm_name = "l4_wkup_clkdm" }, + { .clkdm_name = "tesla_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep iss_wkup_sleep_deps[] = { + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep ivahd_wkup_sleep_deps[] = { + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l3_dma_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "ducati_clkdm" }, + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_dss_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { .clkdm_name = "l3_init_clkdm" }, + { .clkdm_name = "l4_cfg_clkdm" }, + { .clkdm_name = "l4_per_clkdm" }, + { .clkdm_name = "l4_secure_clkdm" }, + { .clkdm_name = "l4_wkup_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l3_dss_wkup_sleep_deps[] = { + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_2_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l3_gfx_wkup_sleep_deps[] = { + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l3_init_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { .clkdm_name = "l4_cfg_clkdm" }, + { .clkdm_name = "l4_per_clkdm" }, + { .clkdm_name = "l4_secure_clkdm" }, + { .clkdm_name = "l4_wkup_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l4_secure_wkup_sleep_deps[] = { + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { .clkdm_name = "l4_per_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep mpu_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "ducati_clkdm" }, + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_2_clkdm" }, + { .clkdm_name = "l3_dss_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { .clkdm_name = "l3_gfx_clkdm" }, + { .clkdm_name = "l3_init_clkdm" }, + { .clkdm_name = "l4_cfg_clkdm" }, + { .clkdm_name = "l4_per_clkdm" }, + { .clkdm_name = "l4_secure_clkdm" }, + { .clkdm_name = "l4_wkup_clkdm" }, + { .clkdm_name = "tesla_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep tesla_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "ivahd_clkdm" }, + { .clkdm_name = "l3_1_clkdm" }, + { .clkdm_name = "l3_2_clkdm" }, + { .clkdm_name = "l3_emif_clkdm" }, + { .clkdm_name = "l3_init_clkdm" }, + { .clkdm_name = "l4_cfg_clkdm" }, + { .clkdm_name = "l4_per_clkdm" }, + { .clkdm_name = "l4_wkup_clkdm" }, + { NULL }, +}; + +static struct clockdomain l4_cefuse_44xx_clkdm = { + .name = "l4_cefuse_clkdm", + .pwrdm = { .name = "cefuse_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CEFUSE_INST, + .clkdm_offs = OMAP4430_CM2_CEFUSE_CEFUSE_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l4_cfg_44xx_clkdm = { + .name = "l4_cfg_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_L4CFG_CDOFFS, + .dep_bit = OMAP4430_L4CFG_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain tesla_44xx_clkdm = { + .name = "tesla_clkdm", + .pwrdm = { .name = "tesla_pwrdm" }, + .prcm_partition = OMAP4430_CM1_PARTITION, + .cm_inst = OMAP4430_CM1_TESLA_INST, + .clkdm_offs = OMAP4430_CM1_TESLA_TESLA_CDOFFS, + .dep_bit = OMAP4430_TESLA_STATDEP_SHIFT, + .wkdep_srcs = tesla_wkup_sleep_deps, + .sleepdep_srcs = tesla_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l3_gfx_44xx_clkdm = { + .name = "l3_gfx_clkdm", + .pwrdm = { .name = "gfx_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_GFX_INST, + .clkdm_offs = OMAP4430_CM2_GFX_GFX_CDOFFS, + .dep_bit = OMAP4430_GFX_STATDEP_SHIFT, + .wkdep_srcs = l3_gfx_wkup_sleep_deps, + .sleepdep_srcs = l3_gfx_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain ivahd_44xx_clkdm = { + .name = "ivahd_clkdm", + .pwrdm = { .name = "ivahd_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_IVAHD_INST, + .clkdm_offs = OMAP4430_CM2_IVAHD_IVAHD_CDOFFS, + .dep_bit = OMAP4430_IVAHD_STATDEP_SHIFT, + .wkdep_srcs = ivahd_wkup_sleep_deps, + .sleepdep_srcs = ivahd_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l4_secure_44xx_clkdm = { + .name = "l4_secure_clkdm", + .pwrdm = { .name = "l4per_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_L4PER_INST, + .clkdm_offs = OMAP4430_CM2_L4PER_L4SEC_CDOFFS, + .dep_bit = OMAP4430_L4SEC_STATDEP_SHIFT, + .wkdep_srcs = l4_secure_wkup_sleep_deps, + .sleepdep_srcs = l4_secure_wkup_sleep_deps, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4_per_44xx_clkdm = { + .name = "l4_per_clkdm", + .pwrdm = { .name = "l4per_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_L4PER_INST, + .clkdm_offs = OMAP4430_CM2_L4PER_L4PER_CDOFFS, + .dep_bit = OMAP4430_L4PER_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain abe_44xx_clkdm = { + .name = "abe_clkdm", + .pwrdm = { .name = "abe_pwrdm" }, + .prcm_partition = OMAP4430_CM1_PARTITION, + .cm_inst = OMAP4430_CM1_ABE_INST, + .clkdm_offs = OMAP4430_CM1_ABE_ABE_CDOFFS, + .dep_bit = OMAP4430_ABE_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l3_instr_44xx_clkdm = { + .name = "l3_instr_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_L3INSTR_CDOFFS, +}; + +static struct clockdomain l3_init_44xx_clkdm = { + .name = "l3_init_clkdm", + .pwrdm = { .name = "l3init_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_L3INIT_INST, + .clkdm_offs = OMAP4430_CM2_L3INIT_L3INIT_CDOFFS, + .dep_bit = OMAP4430_L3INIT_STATDEP_SHIFT, + .wkdep_srcs = l3_init_wkup_sleep_deps, + .sleepdep_srcs = l3_init_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain d2d_44xx_clkdm = { + .name = "d2d_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_D2D_CDOFFS, + .wkdep_srcs = d2d_wkup_sleep_deps, + .sleepdep_srcs = d2d_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain mpu0_44xx_clkdm = { + .name = "mpu0_clkdm", + .pwrdm = { .name = "cpu0_pwrdm" }, + .prcm_partition = OMAP4430_PRCM_MPU_PARTITION, + .cm_inst = OMAP4430_PRCM_MPU_CPU0_INST, + .clkdm_offs = OMAP4430_PRCM_MPU_CPU0_CPU0_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain mpu1_44xx_clkdm = { + .name = "mpu1_clkdm", + .pwrdm = { .name = "cpu1_pwrdm" }, + .prcm_partition = OMAP4430_PRCM_MPU_PARTITION, + .cm_inst = OMAP4430_PRCM_MPU_CPU1_INST, + .clkdm_offs = OMAP4430_PRCM_MPU_CPU1_CPU1_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3_emif_44xx_clkdm = { + .name = "l3_emif_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_MEMIF_CDOFFS, + .dep_bit = OMAP4430_MEMIF_STATDEP_SHIFT, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l4_ao_44xx_clkdm = { + .name = "l4_ao_clkdm", + .pwrdm = { .name = "always_on_core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_ALWAYS_ON_INST, + .clkdm_offs = OMAP4430_CM2_ALWAYS_ON_ALWON_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain ducati_44xx_clkdm = { + .name = "ducati_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_DUCATI_CDOFFS, + .dep_bit = OMAP4430_DUCATI_STATDEP_SHIFT, + .wkdep_srcs = ducati_wkup_sleep_deps, + .sleepdep_srcs = ducati_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain mpu_44xx_clkdm = { + .name = "mpuss_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .prcm_partition = OMAP4430_CM1_PARTITION, + .cm_inst = OMAP4430_CM1_MPU_INST, + .clkdm_offs = OMAP4430_CM1_MPU_MPU_CDOFFS, + .wkdep_srcs = mpu_wkup_sleep_deps, + .sleepdep_srcs = mpu_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3_2_44xx_clkdm = { + .name = "l3_2_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_L3_2_CDOFFS, + .dep_bit = OMAP4430_L3_2_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3_1_44xx_clkdm = { + .name = "l3_1_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_L3_1_CDOFFS, + .dep_bit = OMAP4430_L3_1_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain iss_44xx_clkdm = { + .name = "iss_clkdm", + .pwrdm = { .name = "cam_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CAM_INST, + .clkdm_offs = OMAP4430_CM2_CAM_CAM_CDOFFS, + .wkdep_srcs = iss_wkup_sleep_deps, + .sleepdep_srcs = iss_wkup_sleep_deps, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3_dss_44xx_clkdm = { + .name = "l3_dss_clkdm", + .pwrdm = { .name = "dss_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_DSS_INST, + .clkdm_offs = OMAP4430_CM2_DSS_DSS_CDOFFS, + .dep_bit = OMAP4430_DSS_STATDEP_SHIFT, + .wkdep_srcs = l3_dss_wkup_sleep_deps, + .sleepdep_srcs = l3_dss_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l4_wkup_44xx_clkdm = { + .name = "l4_wkup_clkdm", + .pwrdm = { .name = "wkup_pwrdm" }, + .prcm_partition = OMAP4430_PRM_PARTITION, + .cm_inst = OMAP4430_PRM_WKUP_CM_INST, + .clkdm_offs = OMAP4430_PRM_WKUP_CM_WKUP_CDOFFS, + .dep_bit = OMAP4430_L4WKUP_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP | CLKDM_ACTIVE_WITH_MPU, +}; + +static struct clockdomain emu_sys_44xx_clkdm = { + .name = "emu_sys_clkdm", + .pwrdm = { .name = "emu_pwrdm" }, + .prcm_partition = OMAP4430_PRM_PARTITION, + .cm_inst = OMAP4430_PRM_EMU_CM_INST, + .clkdm_offs = OMAP4430_PRM_EMU_CM_EMU_CDOFFS, + .flags = (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_FORCE_WAKEUP | + CLKDM_MISSING_IDLE_REPORTING), +}; + +static struct clockdomain l3_dma_44xx_clkdm = { + .name = "l3_dma_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP4430_CM2_PARTITION, + .cm_inst = OMAP4430_CM2_CORE_INST, + .clkdm_offs = OMAP4430_CM2_CORE_SDMA_CDOFFS, + .wkdep_srcs = l3_dma_wkup_sleep_deps, + .sleepdep_srcs = l3_dma_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +/* As clockdomains are added or removed above, this list must also be changed */ +static struct clockdomain *clockdomains_omap44xx[] __initdata = { + &l4_cefuse_44xx_clkdm, + &l4_cfg_44xx_clkdm, + &tesla_44xx_clkdm, + &l3_gfx_44xx_clkdm, + &ivahd_44xx_clkdm, + &l4_secure_44xx_clkdm, + &l4_per_44xx_clkdm, + &abe_44xx_clkdm, + &l3_instr_44xx_clkdm, + &l3_init_44xx_clkdm, + &d2d_44xx_clkdm, + &mpu0_44xx_clkdm, + &mpu1_44xx_clkdm, + &l3_emif_44xx_clkdm, + &l4_ao_44xx_clkdm, + &ducati_44xx_clkdm, + &mpu_44xx_clkdm, + &l3_2_44xx_clkdm, + &l3_1_44xx_clkdm, + &iss_44xx_clkdm, + &l3_dss_44xx_clkdm, + &l4_wkup_44xx_clkdm, + &emu_sys_44xx_clkdm, + &l3_dma_44xx_clkdm, + NULL +}; + + +void __init omap44xx_clockdomains_init(void) +{ + clkdm_register_platform_funcs(&omap4_clkdm_operations); + clkdm_register_clkdms(clockdomains_omap44xx); + clkdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/clockdomains54xx_data.c b/arch/arm/mach-omap2/clockdomains54xx_data.c new file mode 100644 index 000000000..5611e0801 --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains54xx_data.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP54XX Clock domains framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + * + * Abhijit Pagare (abhijitpagare@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * Paul Walmsley (paul@pwsan.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "cm1_54xx.h" +#include "cm2_54xx.h" + +#include "cm-regbits-54xx.h" +#include "prm54xx.h" +#include "prcm44xx.h" +#include "prcm_mpu54xx.h" + +/* Static Dependencies for OMAP4 Clock Domains */ + +static struct clkdm_dep c2c_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l3main2_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep cam_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dma_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dsp_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l3main2_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dss_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3main2_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep gpu_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep ipu_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "dsp_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l3main2_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep iva_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l3init_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l4sec_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep mipiext_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l3main2_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep mpu_wkup_sleep_deps[] = { + { .clkdm_name = "abe_clkdm" }, + { .clkdm_name = "dsp_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l3main2_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clockdomain l4sec_54xx_clkdm = { + .name = "l4sec_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_L4SEC_CDOFFS, + .dep_bit = OMAP54XX_L4SEC_STATDEP_SHIFT, + .wkdep_srcs = l4sec_wkup_sleep_deps, + .sleepdep_srcs = l4sec_wkup_sleep_deps, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain iva_54xx_clkdm = { + .name = "iva_clkdm", + .pwrdm = { .name = "iva_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_IVA_INST, + .clkdm_offs = OMAP54XX_CM_CORE_IVA_IVA_CDOFFS, + .dep_bit = OMAP54XX_IVA_STATDEP_SHIFT, + .wkdep_srcs = iva_wkup_sleep_deps, + .sleepdep_srcs = iva_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain mipiext_54xx_clkdm = { + .name = "mipiext_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_MIPIEXT_CDOFFS, + .wkdep_srcs = mipiext_wkup_sleep_deps, + .sleepdep_srcs = mipiext_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3main2_54xx_clkdm = { + .name = "l3main2_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_L3MAIN2_CDOFFS, + .dep_bit = OMAP54XX_L3MAIN2_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3main1_54xx_clkdm = { + .name = "l3main1_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_L3MAIN1_CDOFFS, + .dep_bit = OMAP54XX_L3MAIN1_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain custefuse_54xx_clkdm = { + .name = "custefuse_clkdm", + .pwrdm = { .name = "custefuse_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CUSTEFUSE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CUSTEFUSE_CUSTEFUSE_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain ipu_54xx_clkdm = { + .name = "ipu_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_IPU_CDOFFS, + .dep_bit = OMAP54XX_IPU_STATDEP_SHIFT, + .wkdep_srcs = ipu_wkup_sleep_deps, + .sleepdep_srcs = ipu_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l4cfg_54xx_clkdm = { + .name = "l4cfg_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_L4CFG_CDOFFS, + .dep_bit = OMAP54XX_L4CFG_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain abe_54xx_clkdm = { + .name = "abe_clkdm", + .pwrdm = { .name = "abe_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_AON_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_AON_ABE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_AON_ABE_ABE_CDOFFS, + .dep_bit = OMAP54XX_ABE_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain dss_54xx_clkdm = { + .name = "dss_clkdm", + .pwrdm = { .name = "dss_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_DSS_INST, + .clkdm_offs = OMAP54XX_CM_CORE_DSS_DSS_CDOFFS, + .dep_bit = OMAP54XX_DSS_STATDEP_SHIFT, + .wkdep_srcs = dss_wkup_sleep_deps, + .sleepdep_srcs = dss_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain dsp_54xx_clkdm = { + .name = "dsp_clkdm", + .pwrdm = { .name = "dsp_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_AON_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_AON_DSP_INST, + .clkdm_offs = OMAP54XX_CM_CORE_AON_DSP_DSP_CDOFFS, + .dep_bit = OMAP54XX_DSP_STATDEP_SHIFT, + .wkdep_srcs = dsp_wkup_sleep_deps, + .sleepdep_srcs = dsp_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain c2c_54xx_clkdm = { + .name = "c2c_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_C2C_CDOFFS, + .wkdep_srcs = c2c_wkup_sleep_deps, + .sleepdep_srcs = c2c_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l4per_54xx_clkdm = { + .name = "l4per_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_L4PER_CDOFFS, + .dep_bit = OMAP54XX_L4PER_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain gpu_54xx_clkdm = { + .name = "gpu_clkdm", + .pwrdm = { .name = "gpu_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_GPU_INST, + .clkdm_offs = OMAP54XX_CM_CORE_GPU_GPU_CDOFFS, + .dep_bit = OMAP54XX_GPU_STATDEP_SHIFT, + .wkdep_srcs = gpu_wkup_sleep_deps, + .sleepdep_srcs = gpu_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain wkupaon_54xx_clkdm = { + .name = "wkupaon_clkdm", + .pwrdm = { .name = "wkupaon_pwrdm" }, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .cm_inst = OMAP54XX_PRM_WKUPAON_CM_INST, + .clkdm_offs = OMAP54XX_PRM_WKUPAON_CM_WKUPAON_CDOFFS, + .dep_bit = OMAP54XX_WKUPAON_STATDEP_SHIFT, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain mpu0_54xx_clkdm = { + .name = "mpu0_clkdm", + .pwrdm = { .name = "cpu0_pwrdm" }, + .prcm_partition = OMAP54XX_PRCM_MPU_PARTITION, + .cm_inst = OMAP54XX_PRCM_MPU_CM_C0_INST, + .clkdm_offs = OMAP54XX_PRCM_MPU_CM_C0_CPU0_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain mpu1_54xx_clkdm = { + .name = "mpu1_clkdm", + .pwrdm = { .name = "cpu1_pwrdm" }, + .prcm_partition = OMAP54XX_PRCM_MPU_PARTITION, + .cm_inst = OMAP54XX_PRCM_MPU_CM_C1_INST, + .clkdm_offs = OMAP54XX_PRCM_MPU_CM_C1_CPU1_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain coreaon_54xx_clkdm = { + .name = "coreaon_clkdm", + .pwrdm = { .name = "coreaon_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_COREAON_INST, + .clkdm_offs = OMAP54XX_CM_CORE_COREAON_COREAON_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain mpu_54xx_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_AON_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_AON_MPU_INST, + .clkdm_offs = OMAP54XX_CM_CORE_AON_MPU_MPU_CDOFFS, + .wkdep_srcs = mpu_wkup_sleep_deps, + .sleepdep_srcs = mpu_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3init_54xx_clkdm = { + .name = "l3init_clkdm", + .pwrdm = { .name = "l3init_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_L3INIT_INST, + .clkdm_offs = OMAP54XX_CM_CORE_L3INIT_L3INIT_CDOFFS, + .dep_bit = OMAP54XX_L3INIT_STATDEP_SHIFT, + .wkdep_srcs = l3init_wkup_sleep_deps, + .sleepdep_srcs = l3init_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain dma_54xx_clkdm = { + .name = "dma_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_DMA_CDOFFS, + .wkdep_srcs = dma_wkup_sleep_deps, + .sleepdep_srcs = dma_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3instr_54xx_clkdm = { + .name = "l3instr_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_L3INSTR_CDOFFS, +}; + +static struct clockdomain emif_54xx_clkdm = { + .name = "emif_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CORE_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CORE_EMIF_CDOFFS, + .dep_bit = OMAP54XX_EMIF_STATDEP_SHIFT, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain emu_54xx_clkdm = { + .name = "emu_clkdm", + .pwrdm = { .name = "emu_pwrdm" }, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .cm_inst = OMAP54XX_PRM_EMU_CM_INST, + .clkdm_offs = OMAP54XX_PRM_EMU_CM_EMU_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain cam_54xx_clkdm = { + .name = "cam_clkdm", + .pwrdm = { .name = "cam_pwrdm" }, + .prcm_partition = OMAP54XX_CM_CORE_PARTITION, + .cm_inst = OMAP54XX_CM_CORE_CAM_INST, + .clkdm_offs = OMAP54XX_CM_CORE_CAM_CAM_CDOFFS, + .wkdep_srcs = cam_wkup_sleep_deps, + .sleepdep_srcs = cam_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +/* As clockdomains are added or removed above, this list must also be changed */ +static struct clockdomain *clockdomains_omap54xx[] __initdata = { + &l4sec_54xx_clkdm, + &iva_54xx_clkdm, + &mipiext_54xx_clkdm, + &l3main2_54xx_clkdm, + &l3main1_54xx_clkdm, + &custefuse_54xx_clkdm, + &ipu_54xx_clkdm, + &l4cfg_54xx_clkdm, + &abe_54xx_clkdm, + &dss_54xx_clkdm, + &dsp_54xx_clkdm, + &c2c_54xx_clkdm, + &l4per_54xx_clkdm, + &gpu_54xx_clkdm, + &wkupaon_54xx_clkdm, + &mpu0_54xx_clkdm, + &mpu1_54xx_clkdm, + &coreaon_54xx_clkdm, + &mpu_54xx_clkdm, + &l3init_54xx_clkdm, + &dma_54xx_clkdm, + &l3instr_54xx_clkdm, + &emif_54xx_clkdm, + &emu_54xx_clkdm, + &cam_54xx_clkdm, + NULL +}; + +void __init omap54xx_clockdomains_init(void) +{ + clkdm_register_platform_funcs(&omap4_clkdm_operations); + clkdm_register_clkdms(clockdomains_omap54xx); + clkdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/clockdomains7xx_data.c b/arch/arm/mach-omap2/clockdomains7xx_data.c new file mode 100644 index 000000000..27835c4d1 --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains7xx_data.c @@ -0,0 +1,737 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRA7xx Clock domains framework + * + * Copyright (C) 2009-2013 Texas Instruments, Inc. + * Copyright (C) 2009-2011 Nokia Corporation + * + * Generated by code originally written by: + * Abhijit Pagare (abhijitpagare@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * Paul Walmsley (paul@pwsan.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "cm1_7xx.h" +#include "cm2_7xx.h" + +#include "cm-regbits-7xx.h" +#include "prm7xx.h" +#include "prcm44xx.h" +#include "prcm_mpu7xx.h" + +/* Static Dependencies for DRA7xx Clock Domains */ + +static struct clkdm_dep cam_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dma_wkup_sleep_deps[] = { + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "ipu1_clkdm" }, + { .clkdm_name = "ipu2_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "pcie_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dsp1_wkup_sleep_deps[] = { + { .clkdm_name = "atl_clkdm" }, + { .clkdm_name = "cam_clkdm" }, + { .clkdm_name = "dsp2_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "gmac_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "ipu1_clkdm" }, + { .clkdm_name = "ipu2_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "pcie_clkdm" }, + { .clkdm_name = "vpe_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dsp2_wkup_sleep_deps[] = { + { .clkdm_name = "atl_clkdm" }, + { .clkdm_name = "cam_clkdm" }, + { .clkdm_name = "dsp1_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "gmac_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "ipu1_clkdm" }, + { .clkdm_name = "ipu2_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "pcie_clkdm" }, + { .clkdm_name = "vpe_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep dss_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep eve1_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep eve2_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep eve3_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep eve4_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep gmac_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep gpu_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep ipu1_wkup_sleep_deps[] = { + { .clkdm_name = "atl_clkdm" }, + { .clkdm_name = "dsp1_clkdm" }, + { .clkdm_name = "dsp2_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "gmac_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "ipu2_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "pcie_clkdm" }, + { .clkdm_name = "vpe_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep ipu2_wkup_sleep_deps[] = { + { .clkdm_name = "atl_clkdm" }, + { .clkdm_name = "dsp1_clkdm" }, + { .clkdm_name = "dsp2_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "gmac_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "ipu1_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "pcie_clkdm" }, + { .clkdm_name = "vpe_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep iva_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l3init_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l4per2_wkup_sleep_deps[] = { + { .clkdm_name = "dsp1_clkdm" }, + { .clkdm_name = "dsp2_clkdm" }, + { .clkdm_name = "ipu1_clkdm" }, + { .clkdm_name = "ipu2_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep l4sec_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep mpu_wkup_sleep_deps[] = { + { .clkdm_name = "cam_clkdm" }, + { .clkdm_name = "dsp1_clkdm" }, + { .clkdm_name = "dsp2_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "gmac_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "ipu1_clkdm" }, + { .clkdm_name = "ipu2_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l3main1_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "pcie_clkdm" }, + { .clkdm_name = "vpe_clkdm" }, + { .clkdm_name = "wkupaon_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep pcie_wkup_sleep_deps[] = { + { .clkdm_name = "atl_clkdm" }, + { .clkdm_name = "cam_clkdm" }, + { .clkdm_name = "dsp1_clkdm" }, + { .clkdm_name = "dsp2_clkdm" }, + { .clkdm_name = "dss_clkdm" }, + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "eve1_clkdm" }, + { .clkdm_name = "eve2_clkdm" }, + { .clkdm_name = "eve3_clkdm" }, + { .clkdm_name = "eve4_clkdm" }, + { .clkdm_name = "gmac_clkdm" }, + { .clkdm_name = "gpu_clkdm" }, + { .clkdm_name = "ipu_clkdm" }, + { .clkdm_name = "ipu1_clkdm" }, + { .clkdm_name = "iva_clkdm" }, + { .clkdm_name = "l3init_clkdm" }, + { .clkdm_name = "l4cfg_clkdm" }, + { .clkdm_name = "l4per_clkdm" }, + { .clkdm_name = "l4per2_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { .clkdm_name = "l4sec_clkdm" }, + { .clkdm_name = "vpe_clkdm" }, + { NULL }, +}; + +static struct clkdm_dep vpe_wkup_sleep_deps[] = { + { .clkdm_name = "emif_clkdm" }, + { .clkdm_name = "l4per3_clkdm" }, + { NULL }, +}; + +static struct clockdomain l4per3_7xx_clkdm = { + .name = "l4per3_clkdm", + .pwrdm = { .name = "l4per_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_L4PER_INST, + .clkdm_offs = DRA7XX_CM_CORE_L4PER_L4PER3_CDOFFS, + .dep_bit = DRA7XX_L4PER3_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l4per2_7xx_clkdm = { + .name = "l4per2_clkdm", + .pwrdm = { .name = "l4per_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_L4PER_INST, + .clkdm_offs = DRA7XX_CM_CORE_L4PER_L4PER2_CDOFFS, + .dep_bit = DRA7XX_L4PER2_STATDEP_SHIFT, + .wkdep_srcs = l4per2_wkup_sleep_deps, + .sleepdep_srcs = l4per2_wkup_sleep_deps, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain mpu0_7xx_clkdm = { + .name = "mpu0_clkdm", + .pwrdm = { .name = "cpu0_pwrdm" }, + .prcm_partition = DRA7XX_MPU_PRCM_PARTITION, + .cm_inst = DRA7XX_MPU_PRCM_CM_C0_INST, + .clkdm_offs = DRA7XX_MPU_PRCM_CM_C0_CPU0_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain iva_7xx_clkdm = { + .name = "iva_clkdm", + .pwrdm = { .name = "iva_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_IVA_INST, + .clkdm_offs = DRA7XX_CM_CORE_IVA_IVA_CDOFFS, + .dep_bit = DRA7XX_IVA_STATDEP_SHIFT, + .wkdep_srcs = iva_wkup_sleep_deps, + .sleepdep_srcs = iva_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain coreaon_7xx_clkdm = { + .name = "coreaon_clkdm", + .pwrdm = { .name = "coreaon_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_COREAON_INST, + .clkdm_offs = DRA7XX_CM_CORE_COREAON_COREAON_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain ipu1_7xx_clkdm = { + .name = "ipu1_clkdm", + .pwrdm = { .name = "ipu_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_IPU_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_IPU_IPU1_CDOFFS, + .dep_bit = DRA7XX_IPU1_STATDEP_SHIFT, + .wkdep_srcs = ipu1_wkup_sleep_deps, + .sleepdep_srcs = ipu1_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain ipu2_7xx_clkdm = { + .name = "ipu2_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CORE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CORE_IPU2_CDOFFS, + .dep_bit = DRA7XX_IPU2_STATDEP_SHIFT, + .wkdep_srcs = ipu2_wkup_sleep_deps, + .sleepdep_srcs = ipu2_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l3init_7xx_clkdm = { + .name = "l3init_clkdm", + .pwrdm = { .name = "l3init_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_L3INIT_INST, + .clkdm_offs = DRA7XX_CM_CORE_L3INIT_L3INIT_CDOFFS, + .dep_bit = DRA7XX_L3INIT_STATDEP_SHIFT, + .wkdep_srcs = l3init_wkup_sleep_deps, + .sleepdep_srcs = l3init_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l4sec_7xx_clkdm = { + .name = "l4sec_clkdm", + .pwrdm = { .name = "l4per_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_L4PER_INST, + .clkdm_offs = DRA7XX_CM_CORE_L4PER_L4SEC_CDOFFS, + .dep_bit = DRA7XX_L4SEC_STATDEP_SHIFT, + .wkdep_srcs = l4sec_wkup_sleep_deps, + .sleepdep_srcs = l4sec_wkup_sleep_deps, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l3main1_7xx_clkdm = { + .name = "l3main1_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CORE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CORE_L3MAIN1_CDOFFS, + .dep_bit = DRA7XX_L3MAIN1_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain vpe_7xx_clkdm = { + .name = "vpe_clkdm", + .pwrdm = { .name = "vpe_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_VPE_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_VPE_VPE_CDOFFS, + .dep_bit = DRA7XX_VPE_STATDEP_SHIFT, + .wkdep_srcs = vpe_wkup_sleep_deps, + .sleepdep_srcs = vpe_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain mpu_7xx_clkdm = { + .name = "mpu_clkdm", + .pwrdm = { .name = "mpu_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_MPU_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_MPU_MPU_CDOFFS, + .wkdep_srcs = mpu_wkup_sleep_deps, + .sleepdep_srcs = mpu_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain custefuse_7xx_clkdm = { + .name = "custefuse_clkdm", + .pwrdm = { .name = "custefuse_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CUSTEFUSE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CUSTEFUSE_CUSTEFUSE_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain ipu_7xx_clkdm = { + .name = "ipu_clkdm", + .pwrdm = { .name = "ipu_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_IPU_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_IPU_IPU_CDOFFS, + .dep_bit = DRA7XX_IPU_STATDEP_SHIFT, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain mpu1_7xx_clkdm = { + .name = "mpu1_clkdm", + .pwrdm = { .name = "cpu1_pwrdm" }, + .prcm_partition = DRA7XX_MPU_PRCM_PARTITION, + .cm_inst = DRA7XX_MPU_PRCM_CM_C1_INST, + .clkdm_offs = DRA7XX_MPU_PRCM_CM_C1_CPU1_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain gmac_7xx_clkdm = { + .name = "gmac_clkdm", + .pwrdm = { .name = "l3init_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_L3INIT_INST, + .clkdm_offs = DRA7XX_CM_CORE_L3INIT_GMAC_CDOFFS, + .dep_bit = DRA7XX_GMAC_STATDEP_SHIFT, + .wkdep_srcs = gmac_wkup_sleep_deps, + .sleepdep_srcs = gmac_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain l4cfg_7xx_clkdm = { + .name = "l4cfg_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CORE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CORE_L4CFG_CDOFFS, + .dep_bit = DRA7XX_L4CFG_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP, +}; + +static struct clockdomain dma_7xx_clkdm = { + .name = "dma_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CORE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CORE_DMA_CDOFFS, + .wkdep_srcs = dma_wkup_sleep_deps, + .sleepdep_srcs = dma_wkup_sleep_deps, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain rtc_7xx_clkdm = { + .name = "rtc_clkdm", + .pwrdm = { .name = "rtc_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_RTC_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_RTC_RTC_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain pcie_7xx_clkdm = { + .name = "pcie_clkdm", + .pwrdm = { .name = "l3init_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_L3INIT_INST, + .clkdm_offs = DRA7XX_CM_CORE_L3INIT_PCIE_CDOFFS, + .dep_bit = DRA7XX_PCIE_STATDEP_SHIFT, + .wkdep_srcs = pcie_wkup_sleep_deps, + .sleepdep_srcs = pcie_wkup_sleep_deps, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain atl_7xx_clkdm = { + .name = "atl_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CORE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CORE_ATL_CDOFFS, + .dep_bit = DRA7XX_ATL_STATDEP_SHIFT, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain l3instr_7xx_clkdm = { + .name = "l3instr_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CORE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CORE_L3INSTR_CDOFFS, +}; + +static struct clockdomain dss_7xx_clkdm = { + .name = "dss_clkdm", + .pwrdm = { .name = "dss_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_DSS_INST, + .clkdm_offs = DRA7XX_CM_CORE_DSS_DSS_CDOFFS, + .dep_bit = DRA7XX_DSS_STATDEP_SHIFT, + .wkdep_srcs = dss_wkup_sleep_deps, + .sleepdep_srcs = dss_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain emif_7xx_clkdm = { + .name = "emif_clkdm", + .pwrdm = { .name = "core_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CORE_INST, + .clkdm_offs = DRA7XX_CM_CORE_CORE_EMIF_CDOFFS, + .dep_bit = DRA7XX_EMIF_STATDEP_SHIFT, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain emu_7xx_clkdm = { + .name = "emu_clkdm", + .pwrdm = { .name = "emu_pwrdm" }, + .prcm_partition = DRA7XX_PRM_PARTITION, + .cm_inst = DRA7XX_PRM_EMU_CM_INST, + .clkdm_offs = DRA7XX_PRM_EMU_CM_EMU_CDOFFS, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain dsp2_7xx_clkdm = { + .name = "dsp2_clkdm", + .pwrdm = { .name = "dsp2_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_DSP2_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_DSP2_DSP2_CDOFFS, + .dep_bit = DRA7XX_DSP2_STATDEP_SHIFT, + .wkdep_srcs = dsp2_wkup_sleep_deps, + .sleepdep_srcs = dsp2_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain dsp1_7xx_clkdm = { + .name = "dsp1_clkdm", + .pwrdm = { .name = "dsp1_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_DSP1_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_DSP1_DSP1_CDOFFS, + .dep_bit = DRA7XX_DSP1_STATDEP_SHIFT, + .wkdep_srcs = dsp1_wkup_sleep_deps, + .sleepdep_srcs = dsp1_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain cam_7xx_clkdm = { + .name = "cam_clkdm", + .pwrdm = { .name = "cam_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_CAM_INST, + .clkdm_offs = DRA7XX_CM_CORE_CAM_CAM_CDOFFS, + .dep_bit = DRA7XX_CAM_STATDEP_SHIFT, + .wkdep_srcs = cam_wkup_sleep_deps, + .sleepdep_srcs = cam_wkup_sleep_deps, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain l4per_7xx_clkdm = { + .name = "l4per_clkdm", + .pwrdm = { .name = "l4per_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_L4PER_INST, + .clkdm_offs = DRA7XX_CM_CORE_L4PER_L4PER_CDOFFS, + .dep_bit = DRA7XX_L4PER_STATDEP_SHIFT, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain gpu_7xx_clkdm = { + .name = "gpu_clkdm", + .pwrdm = { .name = "gpu_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_PARTITION, + .cm_inst = DRA7XX_CM_CORE_GPU_INST, + .clkdm_offs = DRA7XX_CM_CORE_GPU_GPU_CDOFFS, + .dep_bit = DRA7XX_GPU_STATDEP_SHIFT, + .wkdep_srcs = gpu_wkup_sleep_deps, + .sleepdep_srcs = gpu_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain eve4_7xx_clkdm = { + .name = "eve4_clkdm", + .pwrdm = { .name = "eve4_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_EVE4_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_EVE4_EVE4_CDOFFS, + .dep_bit = DRA7XX_EVE4_STATDEP_SHIFT, + .wkdep_srcs = eve4_wkup_sleep_deps, + .sleepdep_srcs = eve4_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain eve2_7xx_clkdm = { + .name = "eve2_clkdm", + .pwrdm = { .name = "eve2_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_EVE2_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_EVE2_EVE2_CDOFFS, + .dep_bit = DRA7XX_EVE2_STATDEP_SHIFT, + .wkdep_srcs = eve2_wkup_sleep_deps, + .sleepdep_srcs = eve2_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain eve3_7xx_clkdm = { + .name = "eve3_clkdm", + .pwrdm = { .name = "eve3_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_EVE3_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_EVE3_EVE3_CDOFFS, + .dep_bit = DRA7XX_EVE3_STATDEP_SHIFT, + .wkdep_srcs = eve3_wkup_sleep_deps, + .sleepdep_srcs = eve3_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain wkupaon_7xx_clkdm = { + .name = "wkupaon_clkdm", + .pwrdm = { .name = "wkupaon_pwrdm" }, + .prcm_partition = DRA7XX_PRM_PARTITION, + .cm_inst = DRA7XX_PRM_WKUPAON_CM_INST, + .clkdm_offs = DRA7XX_PRM_WKUPAON_CM_WKUPAON_CDOFFS, + .dep_bit = DRA7XX_WKUPAON_STATDEP_SHIFT, + .flags = CLKDM_CAN_FORCE_WAKEUP | CLKDM_CAN_HWSUP, +}; + +static struct clockdomain eve1_7xx_clkdm = { + .name = "eve1_clkdm", + .pwrdm = { .name = "eve1_pwrdm" }, + .prcm_partition = DRA7XX_CM_CORE_AON_PARTITION, + .cm_inst = DRA7XX_CM_CORE_AON_EVE1_INST, + .clkdm_offs = DRA7XX_CM_CORE_AON_EVE1_EVE1_CDOFFS, + .dep_bit = DRA7XX_EVE1_STATDEP_SHIFT, + .wkdep_srcs = eve1_wkup_sleep_deps, + .sleepdep_srcs = eve1_wkup_sleep_deps, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +/* As clockdomains are added or removed above, this list must also be changed */ +static struct clockdomain *clockdomains_dra7xx[] __initdata = { + &l4per3_7xx_clkdm, + &l4per2_7xx_clkdm, + &mpu0_7xx_clkdm, + &iva_7xx_clkdm, + &coreaon_7xx_clkdm, + &ipu1_7xx_clkdm, + &ipu2_7xx_clkdm, + &l3init_7xx_clkdm, + &l4sec_7xx_clkdm, + &l3main1_7xx_clkdm, + &vpe_7xx_clkdm, + &mpu_7xx_clkdm, + &custefuse_7xx_clkdm, + &ipu_7xx_clkdm, + &mpu1_7xx_clkdm, + &gmac_7xx_clkdm, + &l4cfg_7xx_clkdm, + &dma_7xx_clkdm, + &rtc_7xx_clkdm, + &pcie_7xx_clkdm, + &atl_7xx_clkdm, + &l3instr_7xx_clkdm, + &dss_7xx_clkdm, + &emif_7xx_clkdm, + &emu_7xx_clkdm, + &dsp2_7xx_clkdm, + &dsp1_7xx_clkdm, + &cam_7xx_clkdm, + &l4per_7xx_clkdm, + &gpu_7xx_clkdm, + &eve4_7xx_clkdm, + &eve2_7xx_clkdm, + &eve3_7xx_clkdm, + &wkupaon_7xx_clkdm, + &eve1_7xx_clkdm, + NULL +}; + +void __init dra7xx_clockdomains_init(void) +{ + clkdm_register_platform_funcs(&omap4_clkdm_operations); + clkdm_register_clkdms(clockdomains_dra7xx); + clkdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/clockdomains81xx_data.c b/arch/arm/mach-omap2/clockdomains81xx_data.c new file mode 100644 index 000000000..549cf6148 --- /dev/null +++ b/arch/arm/mach-omap2/clockdomains81xx_data.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI81XX Clock Domain data. + * + * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ + * Copyright (C) 2013 SKTB SKiT, http://www.skitlab.ru/ + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CLOCKDOMAINS_81XX_H +#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAINS_81XX_H + +#include <linux/kernel.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "cm81xx.h" + +/* + * Note that 814x seems to have HWSUP_SWSUP for many clockdomains + * while 816x does not. According to the TRM, 816x only has HWSUP + * for ALWON_L3_FAST. Also note that the TI tree clockdomains81xx.h + * seems to have the related ifdef the wrong way around claiming + * 816x supports HWSUP while 814x does not. For now, we only set + * HWSUP for ALWON_L3_FAST as that seems to be supported for both + * dm814x and dm816x. + */ + +/* Common for 81xx */ + +static struct clockdomain alwon_l3_slow_81xx_clkdm = { + .name = "alwon_l3s_clkdm", + .pwrdm = { .name = "alwon_pwrdm" }, + .cm_inst = TI81XX_CM_ALWON_MOD, + .clkdm_offs = TI81XX_CM_ALWON_L3_SLOW_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain alwon_l3_med_81xx_clkdm = { + .name = "alwon_l3_med_clkdm", + .pwrdm = { .name = "alwon_pwrdm" }, + .cm_inst = TI81XX_CM_ALWON_MOD, + .clkdm_offs = TI81XX_CM_ALWON_L3_MED_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain alwon_l3_fast_81xx_clkdm = { + .name = "alwon_l3_fast_clkdm", + .pwrdm = { .name = "alwon_pwrdm" }, + .cm_inst = TI81XX_CM_ALWON_MOD, + .clkdm_offs = TI81XX_CM_ALWON_L3_FAST_CLKDM, + .flags = CLKDM_CAN_HWSUP_SWSUP, +}; + +static struct clockdomain alwon_ethernet_81xx_clkdm = { + .name = "alwon_ethernet_clkdm", + .pwrdm = { .name = "alwon_pwrdm" }, + .cm_inst = TI81XX_CM_ALWON_MOD, + .clkdm_offs = TI81XX_CM_ETHERNET_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain mmu_81xx_clkdm = { + .name = "mmu_clkdm", + .pwrdm = { .name = "alwon_pwrdm" }, + .cm_inst = TI81XX_CM_ALWON_MOD, + .clkdm_offs = TI81XX_CM_MMU_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain mmu_cfg_81xx_clkdm = { + .name = "mmu_cfg_clkdm", + .pwrdm = { .name = "alwon_pwrdm" }, + .cm_inst = TI81XX_CM_ALWON_MOD, + .clkdm_offs = TI81XX_CM_MMUCFG_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain default_l3_slow_81xx_clkdm = { + .name = "default_l3_slow_clkdm", + .pwrdm = { .name = "default_pwrdm" }, + .cm_inst = TI81XX_CM_DEFAULT_MOD, + .clkdm_offs = TI816X_CM_DEFAULT_L3_SLOW_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain default_sata_81xx_clkdm = { + .name = "default_clkdm", + .pwrdm = { .name = "default_pwrdm" }, + .cm_inst = TI81XX_CM_DEFAULT_MOD, + .clkdm_offs = TI816X_CM_DEFAULT_SATA_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +/* 816x only */ + +static struct clockdomain alwon_mpu_816x_clkdm = { + .name = "alwon_mpu_clkdm", + .pwrdm = { .name = "alwon_pwrdm" }, + .cm_inst = TI81XX_CM_ALWON_MOD, + .clkdm_offs = TI81XX_CM_ALWON_MPU_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain active_gem_816x_clkdm = { + .name = "active_gem_clkdm", + .pwrdm = { .name = "active_pwrdm" }, + .cm_inst = TI81XX_CM_ACTIVE_MOD, + .clkdm_offs = TI816X_CM_ACTIVE_GEM_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain ivahd0_816x_clkdm = { + .name = "ivahd0_clkdm", + .pwrdm = { .name = "ivahd0_pwrdm" }, + .cm_inst = TI816X_CM_IVAHD0_MOD, + .clkdm_offs = TI816X_CM_IVAHD0_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain ivahd1_816x_clkdm = { + .name = "ivahd1_clkdm", + .pwrdm = { .name = "ivahd1_pwrdm" }, + .cm_inst = TI816X_CM_IVAHD1_MOD, + .clkdm_offs = TI816X_CM_IVAHD1_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain ivahd2_816x_clkdm = { + .name = "ivahd2_clkdm", + .pwrdm = { .name = "ivahd2_pwrdm" }, + .cm_inst = TI816X_CM_IVAHD2_MOD, + .clkdm_offs = TI816X_CM_IVAHD2_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain sgx_816x_clkdm = { + .name = "sgx_clkdm", + .pwrdm = { .name = "sgx_pwrdm" }, + .cm_inst = TI81XX_CM_SGX_MOD, + .clkdm_offs = TI816X_CM_SGX_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain default_l3_med_816x_clkdm = { + .name = "default_l3_med_clkdm", + .pwrdm = { .name = "default_pwrdm" }, + .cm_inst = TI81XX_CM_DEFAULT_MOD, + .clkdm_offs = TI816X_CM_DEFAULT_L3_MED_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain default_ducati_816x_clkdm = { + .name = "default_ducati_clkdm", + .pwrdm = { .name = "default_pwrdm" }, + .cm_inst = TI81XX_CM_DEFAULT_MOD, + .clkdm_offs = TI816X_CM_DEFAULT_DUCATI_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain default_pci_816x_clkdm = { + .name = "default_pci_clkdm", + .pwrdm = { .name = "default_pwrdm" }, + .cm_inst = TI81XX_CM_DEFAULT_MOD, + .clkdm_offs = TI816X_CM_DEFAULT_PCI_CLKDM, + .flags = CLKDM_CAN_SWSUP, +}; + +static struct clockdomain *clockdomains_ti814x[] __initdata = { + &alwon_l3_slow_81xx_clkdm, + &alwon_l3_med_81xx_clkdm, + &alwon_l3_fast_81xx_clkdm, + &alwon_ethernet_81xx_clkdm, + &mmu_81xx_clkdm, + &mmu_cfg_81xx_clkdm, + &default_l3_slow_81xx_clkdm, + &default_sata_81xx_clkdm, + NULL, +}; + +void __init ti814x_clockdomains_init(void) +{ + clkdm_register_platform_funcs(&am33xx_clkdm_operations); + clkdm_register_clkdms(clockdomains_ti814x); + clkdm_complete_init(); +} + +static struct clockdomain *clockdomains_ti816x[] __initdata = { + &alwon_mpu_816x_clkdm, + &alwon_l3_slow_81xx_clkdm, + &alwon_l3_med_81xx_clkdm, + &alwon_l3_fast_81xx_clkdm, + &alwon_ethernet_81xx_clkdm, + &mmu_81xx_clkdm, + &mmu_cfg_81xx_clkdm, + &active_gem_816x_clkdm, + &ivahd0_816x_clkdm, + &ivahd1_816x_clkdm, + &ivahd2_816x_clkdm, + &sgx_816x_clkdm, + &default_l3_med_816x_clkdm, + &default_ducati_816x_clkdm, + &default_pci_816x_clkdm, + &default_l3_slow_81xx_clkdm, + &default_sata_81xx_clkdm, + NULL, +}; + +void __init ti816x_clockdomains_init(void) +{ + clkdm_register_platform_funcs(&am33xx_clkdm_operations); + clkdm_register_clkdms(clockdomains_ti816x); + clkdm_complete_init(); +} +#endif diff --git a/arch/arm/mach-omap2/cm-regbits-24xx.h b/arch/arm/mach-omap2/cm-regbits-24xx.h new file mode 100644 index 000000000..59c14dbb6 --- /dev/null +++ b/arch/arm/mach-omap2/cm-regbits-24xx.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARCH_ARM_MACH_OMAP2_CM_REGBITS_24XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_REGBITS_24XX_H + +/* + * OMAP24XX Clock Management register bits + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Copyright (C) 2007 Nokia Corporation + * + * Written by Paul Walmsley + */ + +#define OMAP24XX_AUTOSTATE_MPU_MASK (1 << 0) +#define OMAP24XX_EN_DSS1_MASK (1 << 0) +#define OMAP24XX_ST_MAILBOXES_SHIFT 30 +#define OMAP24XX_ST_HDQ_SHIFT 23 +#define OMAP2420_ST_I2C2_SHIFT 20 +#define OMAP2430_ST_I2CHS1_SHIFT 19 +#define OMAP2420_ST_I2C1_SHIFT 19 +#define OMAP2430_ST_I2CHS2_SHIFT 20 +#define OMAP24XX_ST_MCBSP2_SHIFT 16 +#define OMAP24XX_ST_MCBSP1_SHIFT 15 +#define OMAP2430_ST_MCBSP5_SHIFT 5 +#define OMAP2430_ST_MCBSP4_SHIFT 4 +#define OMAP2430_ST_MCBSP3_SHIFT 3 +#define OMAP24XX_ST_AES_SHIFT 3 +#define OMAP24XX_ST_RNG_SHIFT 2 +#define OMAP24XX_ST_SHA_SHIFT 1 +#define OMAP24XX_CLKSEL_DSS2_MASK (0x1 << 13) +#define OMAP24XX_AUTOSTATE_DSS_MASK (1 << 2) +#define OMAP24XX_AUTOSTATE_L4_MASK (1 << 1) +#define OMAP24XX_AUTOSTATE_L3_MASK (1 << 0) +#define OMAP24XX_AUTOSTATE_GFX_MASK (1 << 0) +#define OMAP24XX_ST_MPU_WDT_SHIFT 3 +#define OMAP24XX_ST_32KSYNC_SHIFT 1 +#define OMAP24XX_EN_54M_PLL_SHIFT 6 +#define OMAP24XX_EN_96M_PLL_SHIFT 2 +#define OMAP24XX_ST_54M_APLL_SHIFT 9 +#define OMAP24XX_ST_96M_APLL_SHIFT 8 +#define OMAP24XX_AUTO_54M_MASK (0x3 << 6) +#define OMAP24XX_AUTO_96M_MASK (0x3 << 2) +#define OMAP24XX_AUTO_DPLL_SHIFT 0 +#define OMAP24XX_AUTO_DPLL_MASK (0x3 << 0) +#define OMAP24XX_CORE_CLK_SRC_MASK (0x3 << 0) +#define OMAP2420_AUTOSTATE_IVA_MASK (1 << 8) +#define OMAP24XX_AUTOSTATE_DSP_MASK (1 << 0) +#define OMAP2430_AUTOSTATE_MDM_MASK (1 << 0) +#define OMAP24XX_CLKSTCTRL_DISABLE_AUTO 0x0 +#define OMAP24XX_CLKSTCTRL_ENABLE_AUTO 0x1 +#endif diff --git a/arch/arm/mach-omap2/cm-regbits-33xx.h b/arch/arm/mach-omap2/cm-regbits-33xx.h new file mode 100644 index 000000000..1b97219aa --- /dev/null +++ b/arch/arm/mach-omap2/cm-regbits-33xx.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AM33XX Power Management register bits + * + * This file is automatically generated from the AM33XX hardware databases. + * Vaibhav Hiremath <hvaibhav@ti.com> + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + + +#ifndef __ARCH_ARM_MACH_OMAP2_CM_REGBITS_33XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_REGBITS_33XX_H + +#define AM33XX_CLKOUT2DIV_SHIFT 3 +#define AM33XX_CLKOUT2DIV_WIDTH 3 +#define AM33XX_CLKOUT2EN_SHIFT 7 +#define AM33XX_CLKOUT2SOURCE_MASK (0x7 << 0) +#define AM33XX_CLKSEL_0_0_SHIFT 0 +#define AM33XX_CLKSEL_0_0_WIDTH 1 +#define AM33XX_CLKSEL_0_0_MASK (1 << 0) +#define AM33XX_CLKSEL_0_1_MASK (3 << 0) +#define AM33XX_CLKSEL_0_2_MASK (7 << 0) +#define AM33XX_CLKSEL_GFX_FCLK_MASK (1 << 1) +#define AM33XX_CLKTRCTRL_SHIFT 0 +#define AM33XX_CLKTRCTRL_MASK (0x3 << 0) +#define AM33XX_DPLL_CLKOUT_DIV_SHIFT 0 +#define AM33XX_DPLL_CLKOUT_DIV_WIDTH 5 +#define AM33XX_DPLL_DIV_MASK (0x7f << 0) +#define AM33XX_DPLL_PER_DIV_MASK (0xff << 0) +#define AM33XX_DPLL_EN_MASK (0x7 << 0) +#define AM33XX_DPLL_MULT_MASK (0x7ff << 8) +#define AM33XX_DPLL_MULT_PERIPH_MASK (0xfff << 8) +#define AM33XX_HSDIVIDER_CLKOUT1_DIV_SHIFT 0 +#define AM33XX_HSDIVIDER_CLKOUT1_DIV_WIDTH 5 +#define AM33XX_HSDIVIDER_CLKOUT2_DIV_SHIFT 0 +#define AM33XX_HSDIVIDER_CLKOUT2_DIV_WIDTH 5 +#define AM33XX_HSDIVIDER_CLKOUT3_DIV_SHIFT 0 +#define AM33XX_HSDIVIDER_CLKOUT3_DIV_WIDTH 5 +#define AM33XX_IDLEST_SHIFT 16 +#define AM33XX_IDLEST_MASK (0x3 << 16) +#define AM33XX_MODULEMODE_SHIFT 0 +#define AM33XX_MODULEMODE_MASK (0x3 << 0) +#define AM33XX_OPTCLK_DEBUG_CLKA_SHIFT 30 +#define AM33XX_OPTFCLKEN_DBGSYSCLK_SHIFT 19 +#define AM33XX_OPTFCLKEN_GPIO0_GDBCLK_SHIFT 18 +#define AM33XX_OPTFCLKEN_GPIO_1_GDBCLK_SHIFT 18 +#define AM33XX_OPTFCLKEN_GPIO_2_GDBCLK_SHIFT 18 +#define AM33XX_OPTFCLKEN_GPIO_3_GDBCLK_SHIFT 18 +#define AM33XX_STM_PMD_CLKDIVSEL_SHIFT 27 +#define AM33XX_STM_PMD_CLKDIVSEL_WIDTH 3 +#define AM33XX_STM_PMD_CLKSEL_SHIFT 22 +#define AM33XX_STM_PMD_CLKSEL_WIDTH 2 +#define AM33XX_ST_DPLL_CLK_MASK (1 << 0) +#define AM33XX_ST_DPLL_CLKDCOLDO_SHIFT 8 +#define AM33XX_TRC_PMD_CLKDIVSEL_SHIFT 24 +#define AM33XX_TRC_PMD_CLKDIVSEL_WIDTH 3 +#define AM33XX_TRC_PMD_CLKSEL_SHIFT 20 +#define AM33XX_TRC_PMD_CLKSEL_WIDTH 2 +#endif diff --git a/arch/arm/mach-omap2/cm-regbits-34xx.h b/arch/arm/mach-omap2/cm-regbits-34xx.h new file mode 100644 index 000000000..037529a9e --- /dev/null +++ b/arch/arm/mach-omap2/cm-regbits-34xx.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARCH_ARM_MACH_OMAP2_CM_REGBITS_34XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_REGBITS_34XX_H + +/* + * OMAP3430 Clock Management register bits + * + * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Copyright (C) 2007-2008 Nokia Corporation + * + * Written by Paul Walmsley + */ + +#define OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_MASK (1 << 0) +#define OMAP3430_ST_IVA2_SHIFT 0 +#define OMAP3430_CLKTRCTRL_IVA2_MASK (0x3 << 0) +#define OMAP3430_CLKACTIVITY_IVA2_MASK (1 << 0) +#define OMAP3430_CLKTRCTRL_MPU_MASK (0x3 << 0) +#define OMAP3430_ST_AES2_SHIFT 28 +#define OMAP3430_ST_SHA12_SHIFT 27 +#define AM35XX_ST_UART4_SHIFT 23 +#define OMAP3430_ST_HDQ_SHIFT 22 +#define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 +#define OMAP3430_ST_MAILBOXES_SHIFT 7 +#define OMAP3430_ST_SAD2D_SHIFT 3 +#define OMAP3430_ST_SDMA_SHIFT 2 +#define OMAP3430ES2_ST_USBTLL_SHIFT 2 +#define OMAP3430ES1_CLKTRCTRL_D2D_MASK (0x3 << 4) +#define OMAP3430_CLKTRCTRL_L4_MASK (0x3 << 2) +#define OMAP3430_CLKTRCTRL_L3_MASK (0x3 << 0) +#define OMAP3430ES1_CLKTRCTRL_GFX_MASK (0x3 << 0) +#define OMAP3430ES2_CLKTRCTRL_SGX_MASK (0x3 << 0) +#define OMAP3430_ST_WDT2_SHIFT 5 +#define OMAP3430_ST_32KSYNC_SHIFT 2 +#define OMAP3430_AUTO_PERIPH_DPLL_MASK (0x7 << 3) +#define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 +#define OMAP3430_CLKTRCTRL_DSS_MASK (0x3 << 0) +#define OMAP3430_CLKTRCTRL_CAM_MASK (0x3 << 0) +#define OMAP3430_ST_MCBSP4_SHIFT 2 +#define OMAP3430_ST_MCBSP3_SHIFT 1 +#define OMAP3430_ST_MCBSP2_SHIFT 0 +#define OMAP3430_CLKTRCTRL_PER_MASK (0x3 << 0) +#define OMAP3430_CLKTRCTRL_EMU_MASK (0x3 << 0) +#define OMAP3430_CLKTRCTRL_NEON_MASK (0x3 << 0) +#define OMAP3430ES2_EN_USBHOST2_SHIFT 1 +#define OMAP3430ES2_ST_USBHOST_IDLE_SHIFT 1 +#define OMAP3430ES2_CLKTRCTRL_USBHOST_MASK (3 << 0) +#define OMAP34XX_CLKSTCTRL_DISABLE_AUTO 0x0 +#define OMAP34XX_CLKSTCTRL_FORCE_SLEEP 0x1 +#define OMAP34XX_CLKSTCTRL_FORCE_WAKEUP 0x2 +#define OMAP34XX_CLKSTCTRL_ENABLE_AUTO 0x3 +#endif diff --git a/arch/arm/mach-omap2/cm-regbits-44xx.h b/arch/arm/mach-omap2/cm-regbits-44xx.h new file mode 100644 index 000000000..553a6267e --- /dev/null +++ b/arch/arm/mach-omap2/cm-regbits-44xx.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx Clock Management register bits + * + * Copyright (C) 2009-2012 Texas Instruments, Inc. + * Copyright (C) 2009-2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM_REGBITS_44XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_REGBITS_44XX_H + +#define OMAP4430_ABE_STATDEP_SHIFT 3 +#define OMAP4430_CLKTRCTRL_SHIFT 0 +#define OMAP4430_CLKTRCTRL_MASK (0x3 << 0) +#define OMAP4430_DSS_STATDEP_SHIFT 8 +#define OMAP4430_DUCATI_STATDEP_SHIFT 0 +#define OMAP4430_GFX_STATDEP_SHIFT 10 +#define OMAP4430_IDLEST_SHIFT 16 +#define OMAP4430_IDLEST_MASK (0x3 << 16) +#define OMAP4430_IVAHD_STATDEP_SHIFT 2 +#define OMAP4430_L3INIT_STATDEP_SHIFT 7 +#define OMAP4430_L3_1_STATDEP_SHIFT 5 +#define OMAP4430_L3_2_STATDEP_SHIFT 6 +#define OMAP4430_L4CFG_STATDEP_SHIFT 12 +#define OMAP4430_L4PER_STATDEP_SHIFT 13 +#define OMAP4430_L4SEC_STATDEP_SHIFT 14 +#define OMAP4430_L4WKUP_STATDEP_SHIFT 15 +#define OMAP4430_MEMIF_STATDEP_SHIFT 4 +#define OMAP4430_MODULEMODE_SHIFT 0 +#define OMAP4430_MODULEMODE_MASK (0x3 << 0) +#define OMAP4430_TESLA_STATDEP_SHIFT 1 +#endif diff --git a/arch/arm/mach-omap2/cm-regbits-54xx.h b/arch/arm/mach-omap2/cm-regbits-54xx.h new file mode 100644 index 000000000..fc8868838 --- /dev/null +++ b/arch/arm/mach-omap2/cm-regbits-54xx.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP54xx Clock Management register bits + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM_REGBITS_54XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_REGBITS_54XX_H + +#define OMAP54XX_ABE_STATDEP_SHIFT 3 +#define OMAP54XX_AUTO_DPLL_MODE_MASK (0x7 << 0) +#define OMAP54XX_CLKSEL_SHIFT 24 +#define OMAP54XX_CLKSEL_WIDTH 0x1 +#define OMAP54XX_CLKSEL_0_0_SHIFT 0 +#define OMAP54XX_CLKSEL_0_0_WIDTH 0x1 +#define OMAP54XX_CLKSEL_AESS_FCLK_SHIFT 24 +#define OMAP54XX_CLKSEL_AESS_FCLK_WIDTH 0x1 +#define OMAP54XX_CLKSEL_DIV_SHIFT 25 +#define OMAP54XX_CLKSEL_DIV_WIDTH 0x1 +#define OMAP54XX_CLKSEL_FCLK_SHIFT 24 +#define OMAP54XX_CLKSEL_FCLK_WIDTH 0x1 +#define OMAP54XX_CLKSEL_GPU_CORE_GCLK_SHIFT 24 +#define OMAP54XX_CLKSEL_GPU_CORE_GCLK_WIDTH 0x1 +#define OMAP54XX_CLKSEL_GPU_HYD_GCLK_SHIFT 25 +#define OMAP54XX_CLKSEL_GPU_HYD_GCLK_WIDTH 0x1 +#define OMAP54XX_CLKSEL_INTERNAL_SOURCE_SHIFT 26 +#define OMAP54XX_CLKSEL_INTERNAL_SOURCE_WIDTH 0x2 +#define OMAP54XX_CLKSEL_OPP_SHIFT 0 +#define OMAP54XX_CLKSEL_OPP_WIDTH 0x2 +#define OMAP54XX_CLKSEL_SOURCE_SHIFT 24 +#define OMAP54XX_CLKSEL_SOURCE_WIDTH 0x2 +#define OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_SHIFT 24 +#define OMAP54XX_CLKSEL_SOURCE_L3INIT_MMC1_WIDTH 0x1 +#define OMAP54XX_CLKSEL_UTMI_P1_SHIFT 24 +#define OMAP54XX_CLKSEL_UTMI_P1_WIDTH 0x1 +#define OMAP54XX_CLKSEL_UTMI_P2_SHIFT 25 +#define OMAP54XX_CLKSEL_UTMI_P2_WIDTH 0x1 +#define OMAP54XX_DIVHS_MASK (0x3f << 0) +#define OMAP54XX_DIVHS_0_4_MASK (0x1f << 0) +#define OMAP54XX_DIVHS_0_6_MASK (0x7f << 0) +#define OMAP54XX_DPLL_DIV_MASK (0x7f << 0) +#define OMAP54XX_DPLL_EN_MASK (0x7 << 0) +#define OMAP54XX_DPLL_LPMODE_EN_MASK (1 << 10) +#define OMAP54XX_DPLL_MULT_MASK (0x7ff << 8) +#define OMAP54XX_DPLL_REGM4XEN_MASK (1 << 11) +#define OMAP54XX_DPLL_SD_DIV_MASK (0xff << 24) +#define OMAP54XX_DSP_STATDEP_SHIFT 1 +#define OMAP54XX_DSS_STATDEP_SHIFT 8 +#define OMAP54XX_EMIF_STATDEP_SHIFT 4 +#define OMAP54XX_GPU_STATDEP_SHIFT 10 +#define OMAP54XX_IPU_STATDEP_SHIFT 0 +#define OMAP54XX_IVA_STATDEP_SHIFT 2 +#define OMAP54XX_L3INIT_STATDEP_SHIFT 7 +#define OMAP54XX_L3MAIN1_STATDEP_SHIFT 5 +#define OMAP54XX_L3MAIN2_STATDEP_SHIFT 6 +#define OMAP54XX_L4CFG_STATDEP_SHIFT 12 +#define OMAP54XX_L4PER_STATDEP_SHIFT 13 +#define OMAP54XX_L4SEC_STATDEP_SHIFT 14 +#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_SHIFT 11 +#define OMAP54XX_OPTFCLKEN_32KHZ_CLK_8_8_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_48MHZ_CLK_SHIFT 9 +#define OMAP54XX_OPTFCLKEN_CLK32K_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_CTRLCLK_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_DBCLK_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_DSSCLK_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_HSIC480M_P1_CLK_SHIFT 13 +#define OMAP54XX_OPTFCLKEN_HSIC480M_P2_CLK_SHIFT 14 +#define OMAP54XX_OPTFCLKEN_HSIC480M_P3_CLK_SHIFT 7 +#define OMAP54XX_OPTFCLKEN_HSIC60M_P1_CLK_SHIFT 11 +#define OMAP54XX_OPTFCLKEN_HSIC60M_P2_CLK_SHIFT 12 +#define OMAP54XX_OPTFCLKEN_HSIC60M_P3_CLK_SHIFT 6 +#define OMAP54XX_OPTFCLKEN_REFCLK960M_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_REF_CLK_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_SLIMBUS_CLK_SHIFT 11 +#define OMAP54XX_OPTFCLKEN_SYS_CLK_SHIFT 10 +#define OMAP54XX_OPTFCLKEN_TXPHY_CLK_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_TXPHY_LS_CLK_SHIFT 9 +#define OMAP54XX_OPTFCLKEN_USB_CH0_CLK_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_USB_CH1_CLK_SHIFT 9 +#define OMAP54XX_OPTFCLKEN_USB_CH2_CLK_SHIFT 10 +#define OMAP54XX_OPTFCLKEN_UTMI_P1_CLK_SHIFT 8 +#define OMAP54XX_OPTFCLKEN_UTMI_P2_CLK_SHIFT 9 +#define OMAP54XX_OPTFCLKEN_UTMI_P3_CLK_SHIFT 10 +#define OMAP54XX_PAD_CLKS_GATE_SHIFT 8 +#define OMAP54XX_SLIMBUS1_CLK_GATE_SHIFT 10 +#define OMAP54XX_ST_DPLL_CLK_MASK (1 << 0) +#define OMAP54XX_SYS_CLKSEL_SHIFT 0 +#define OMAP54XX_SYS_CLKSEL_WIDTH 0x3 +#define OMAP54XX_WKUPAON_STATDEP_SHIFT 15 +#endif diff --git a/arch/arm/mach-omap2/cm-regbits-7xx.h b/arch/arm/mach-omap2/cm-regbits-7xx.h new file mode 100644 index 000000000..2725af4d1 --- /dev/null +++ b/arch/arm/mach-omap2/cm-regbits-7xx.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * DRA7xx Clock Management register bits + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Generated by code originally written by: + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM_REGBITS_7XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_REGBITS_7XX_H + +#define DRA7XX_ATL_STATDEP_SHIFT 30 +#define DRA7XX_CAM_STATDEP_SHIFT 9 +#define DRA7XX_DSP1_STATDEP_SHIFT 1 +#define DRA7XX_DSP2_STATDEP_SHIFT 18 +#define DRA7XX_DSS_STATDEP_SHIFT 8 +#define DRA7XX_EMIF_STATDEP_SHIFT 4 +#define DRA7XX_EVE1_STATDEP_SHIFT 19 +#define DRA7XX_EVE2_STATDEP_SHIFT 20 +#define DRA7XX_EVE3_STATDEP_SHIFT 21 +#define DRA7XX_EVE4_STATDEP_SHIFT 22 +#define DRA7XX_GMAC_STATDEP_SHIFT 25 +#define DRA7XX_GPU_STATDEP_SHIFT 10 +#define DRA7XX_IPU1_STATDEP_SHIFT 23 +#define DRA7XX_IPU2_STATDEP_SHIFT 0 +#define DRA7XX_IPU_STATDEP_SHIFT 24 +#define DRA7XX_IVA_STATDEP_SHIFT 2 +#define DRA7XX_L3INIT_STATDEP_SHIFT 7 +#define DRA7XX_L3MAIN1_STATDEP_SHIFT 5 +#define DRA7XX_L4CFG_STATDEP_SHIFT 12 +#define DRA7XX_L4PER2_STATDEP_SHIFT 26 +#define DRA7XX_L4PER3_STATDEP_SHIFT 27 +#define DRA7XX_L4PER_STATDEP_SHIFT 13 +#define DRA7XX_L4SEC_STATDEP_SHIFT 14 +#define DRA7XX_PCIE_STATDEP_SHIFT 29 +#define DRA7XX_VPE_STATDEP_SHIFT 28 +#define DRA7XX_WKUPAON_STATDEP_SHIFT 15 +#endif diff --git a/arch/arm/mach-omap2/cm.h b/arch/arm/mach-omap2/cm.h new file mode 100644 index 000000000..14beb59e5 --- /dev/null +++ b/arch/arm/mach-omap2/cm.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2+ Clock Management prototypes + * + * Copyright (C) 2007-2009, 2012 Texas Instruments, Inc. + * Copyright (C) 2007-2009 Nokia Corporation + * + * Written by Paul Walmsley + */ +#ifndef __ARCH_ASM_MACH_OMAP2_CM_H +#define __ARCH_ASM_MACH_OMAP2_CM_H + +/* + * MAX_MODULE_READY_TIME: max duration in microseconds to wait for the + * PRCM to request that a module exit the inactive state in the case of + * OMAP2 & 3. + * In the case of OMAP4 this is the max duration in microseconds for the + * module to reach the functionnal state from an inactive state. + */ +#define MAX_MODULE_READY_TIME 2000 + +# ifndef __ASSEMBLER__ +#include <linux/clk/ti.h> + +#include "prcm-common.h" + +extern struct omap_domain_base cm_base; +extern struct omap_domain_base cm2_base; +# endif + +/* + * MAX_MODULE_DISABLE_TIME: max duration in microseconds to wait for + * the PRCM to request that a module enter the inactive state in the + * case of OMAP2 & 3. In the case of OMAP4 this is the max duration + * in microseconds for the module to reach the inactive state from + * a functional state. + * XXX FSUSB on OMAP4430 takes ~4ms to idle after reset during + * kernel init. + */ +#define MAX_MODULE_DISABLE_TIME 5000 + +# ifndef __ASSEMBLER__ + +/** + * struct cm_ll_data - fn ptrs to per-SoC CM function implementations + * @split_idlest_reg: ptr to the SoC CM-specific split_idlest_reg impl + * @wait_module_ready: ptr to the SoC CM-specific wait_module_ready impl + * @wait_module_idle: ptr to the SoC CM-specific wait_module_idle impl + * @module_enable: ptr to the SoC CM-specific module_enable impl + * @module_disable: ptr to the SoC CM-specific module_disable impl + * @xlate_clkctrl: ptr to the SoC CM-specific clkctrl xlate addr impl + */ +struct cm_ll_data { + int (*split_idlest_reg)(struct clk_omap_reg *idlest_reg, s16 *prcm_inst, + u8 *idlest_reg_id); + int (*wait_module_ready)(u8 part, s16 prcm_mod, u16 idlest_reg, + u8 idlest_shift); + int (*wait_module_idle)(u8 part, s16 prcm_mod, u16 idlest_reg, + u8 idlest_shift); + void (*module_enable)(u8 mode, u8 part, u16 inst, u16 clkctrl_offs); + void (*module_disable)(u8 part, u16 inst, u16 clkctrl_offs); + u32 (*xlate_clkctrl)(u8 part, u16 inst, u16 clkctrl_offs); +}; + +extern int cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, s16 *prcm_inst, + u8 *idlest_reg_id); +int omap_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_reg, + u8 idlest_shift); +int omap_cm_wait_module_idle(u8 part, s16 prcm_mod, u16 idlest_reg, + u8 idlest_shift); +int omap_cm_module_enable(u8 mode, u8 part, u16 inst, u16 clkctrl_offs); +int omap_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs); +u32 omap_cm_xlate_clkctrl(u8 part, u16 inst, u16 clkctrl_offs); +extern int cm_register(const struct cm_ll_data *cld); +extern int cm_unregister(const struct cm_ll_data *cld); +int omap_cm_init(void); +int omap2_cm_base_init(void); + +# endif + +#endif diff --git a/arch/arm/mach-omap2/cm1_44xx.h b/arch/arm/mach-omap2/cm1_44xx.h new file mode 100644 index 000000000..13710cefa --- /dev/null +++ b/arch/arm/mach-omap2/cm1_44xx.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx CM1 instance offset macros + * + * Copyright (C) 2009-2011 Texas Instruments, Inc. + * Copyright (C) 2009-2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + * + * XXX This file needs to be updated to align on one of "OMAP4", "OMAP44XX", + * or "OMAP4430". + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM1_44XX_H +#define __ARCH_ARM_MACH_OMAP2_CM1_44XX_H + +/* CM1 base address */ +#define OMAP4430_CM1_BASE 0x4a004000 + +#define OMAP44XX_CM1_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP4430_CM1_BASE + (inst) + (reg)) + +/* CM1 instances */ +#define OMAP4430_CM1_OCP_SOCKET_INST 0x0000 +#define OMAP4430_CM1_CKGEN_INST 0x0100 +#define OMAP4430_CM1_MPU_INST 0x0300 +#define OMAP4430_CM1_TESLA_INST 0x0400 +#define OMAP4430_CM1_ABE_INST 0x0500 + +/* CM1 clockdomain register offsets (from instance start) */ +#define OMAP4430_CM1_MPU_MPU_CDOFFS 0x0000 +#define OMAP4430_CM1_TESLA_TESLA_CDOFFS 0x0000 +#define OMAP4430_CM1_ABE_ABE_CDOFFS 0x0000 + +#endif diff --git a/arch/arm/mach-omap2/cm1_54xx.h b/arch/arm/mach-omap2/cm1_54xx.h new file mode 100644 index 000000000..fdca20aa4 --- /dev/null +++ b/arch/arm/mach-omap2/cm1_54xx.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP54xx CM1 instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM1_54XX_H +#define __ARCH_ARM_MACH_OMAP2_CM1_54XX_H + +/* CM1 base address */ +#define OMAP54XX_CM_CORE_AON_BASE 0x4a004000 + +#define OMAP54XX_CM_CORE_AON_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP54XX_CM_CORE_AON_BASE + (inst) + (reg)) + +/* CM_CORE_AON instances */ +#define OMAP54XX_CM_CORE_AON_OCP_SOCKET_INST 0x0000 +#define OMAP54XX_CM_CORE_AON_CKGEN_INST 0x0100 +#define OMAP54XX_CM_CORE_AON_MPU_INST 0x0300 +#define OMAP54XX_CM_CORE_AON_DSP_INST 0x0400 +#define OMAP54XX_CM_CORE_AON_ABE_INST 0x0500 + +/* CM_CORE_AON clockdomain register offsets (from instance start) */ +#define OMAP54XX_CM_CORE_AON_MPU_MPU_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_AON_DSP_DSP_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_AON_ABE_ABE_CDOFFS 0x0000 + +#endif diff --git a/arch/arm/mach-omap2/cm1_7xx.h b/arch/arm/mach-omap2/cm1_7xx.h new file mode 100644 index 000000000..a543eb3db --- /dev/null +++ b/arch/arm/mach-omap2/cm1_7xx.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * DRA7xx CM1 instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Generated by code originally written by: + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM1_7XX_H +#define __ARCH_ARM_MACH_OMAP2_CM1_7XX_H + +/* CM1 base address */ +#define DRA7XX_CM_CORE_AON_BASE 0x4a005000 + +#define DRA7XX_CM_CORE_AON_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(DRA7XX_CM_CORE_AON_BASE + (inst) + (reg)) + +/* CM_CORE_AON instances */ +#define DRA7XX_CM_CORE_AON_OCP_SOCKET_INST 0x0000 +#define DRA7XX_CM_CORE_AON_CKGEN_INST 0x0100 +#define DRA7XX_CM_CORE_AON_MPU_INST 0x0300 +#define DRA7XX_CM_CORE_AON_DSP1_INST 0x0400 +#define DRA7XX_CM_CORE_AON_IPU_INST 0x0500 +#define DRA7XX_CM_CORE_AON_DSP2_INST 0x0600 +#define DRA7XX_CM_CORE_AON_EVE1_INST 0x0640 +#define DRA7XX_CM_CORE_AON_EVE2_INST 0x0680 +#define DRA7XX_CM_CORE_AON_EVE3_INST 0x06c0 +#define DRA7XX_CM_CORE_AON_EVE4_INST 0x0700 +#define DRA7XX_CM_CORE_AON_RTC_INST 0x0740 +#define DRA7XX_CM_CORE_AON_VPE_INST 0x0760 + +/* CM_CORE_AON clockdomain register offsets (from instance start) */ +#define DRA7XX_CM_CORE_AON_MPU_MPU_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_DSP1_DSP1_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_IPU_IPU1_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_IPU_IPU_CDOFFS 0x0040 +#define DRA7XX_CM_CORE_AON_DSP2_DSP2_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_EVE1_EVE1_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_EVE2_EVE2_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_EVE3_EVE3_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_EVE4_EVE4_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_RTC_RTC_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_AON_VPE_VPE_CDOFFS 0x0000 + +#endif diff --git a/arch/arm/mach-omap2/cm2_44xx.h b/arch/arm/mach-omap2/cm2_44xx.h new file mode 100644 index 000000000..7f9b7a81f --- /dev/null +++ b/arch/arm/mach-omap2/cm2_44xx.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx CM2 instance offset macros + * + * Copyright (C) 2009-2011 Texas Instruments, Inc. + * Copyright (C) 2009-2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + * + * XXX This file needs to be updated to align on one of "OMAP4", "OMAP44XX", + * or "OMAP4430". + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM2_44XX_H +#define __ARCH_ARM_MACH_OMAP2_CM2_44XX_H + +/* CM2 base address */ +#define OMAP4430_CM2_BASE 0x4a008000 + +#define OMAP44XX_CM2_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP4430_CM2_BASE + (inst) + (reg)) + +/* CM2 instances */ +#define OMAP4430_CM2_OCP_SOCKET_INST 0x0000 +#define OMAP4430_CM2_CKGEN_INST 0x0100 +#define OMAP4430_CM2_ALWAYS_ON_INST 0x0600 +#define OMAP4430_CM2_CORE_INST 0x0700 +#define OMAP4430_CM2_IVAHD_INST 0x0f00 +#define OMAP4430_CM2_CAM_INST 0x1000 +#define OMAP4430_CM2_DSS_INST 0x1100 +#define OMAP4430_CM2_GFX_INST 0x1200 +#define OMAP4430_CM2_L3INIT_INST 0x1300 +#define OMAP4430_CM2_L4PER_INST 0x1400 +#define OMAP4430_CM2_CEFUSE_INST 0x1600 + +/* CM2 clockdomain register offsets (from instance start) */ +#define OMAP4430_CM2_ALWAYS_ON_ALWON_CDOFFS 0x0000 +#define OMAP4430_CM2_CORE_L3_1_CDOFFS 0x0000 +#define OMAP4430_CM2_CORE_L3_2_CDOFFS 0x0100 +#define OMAP4430_CM2_CORE_DUCATI_CDOFFS 0x0200 +#define OMAP4430_CM2_CORE_SDMA_CDOFFS 0x0300 +#define OMAP4430_CM2_CORE_MEMIF_CDOFFS 0x0400 +#define OMAP4430_CM2_CORE_D2D_CDOFFS 0x0500 +#define OMAP4430_CM2_CORE_L4CFG_CDOFFS 0x0600 +#define OMAP4430_CM2_CORE_L3INSTR_CDOFFS 0x0700 +#define OMAP4430_CM2_IVAHD_IVAHD_CDOFFS 0x0000 +#define OMAP4430_CM2_CAM_CAM_CDOFFS 0x0000 +#define OMAP4430_CM2_DSS_DSS_CDOFFS 0x0000 +#define OMAP4430_CM2_GFX_GFX_CDOFFS 0x0000 +#define OMAP4430_CM2_L3INIT_L3INIT_CDOFFS 0x0000 +#define OMAP4430_CM2_L4PER_L4PER_CDOFFS 0x0000 +#define OMAP4430_CM2_L4PER_L4SEC_CDOFFS 0x0180 +#define OMAP4430_CM2_CEFUSE_CEFUSE_CDOFFS 0x0000 + +#endif diff --git a/arch/arm/mach-omap2/cm2_54xx.h b/arch/arm/mach-omap2/cm2_54xx.h new file mode 100644 index 000000000..7e5860578 --- /dev/null +++ b/arch/arm/mach-omap2/cm2_54xx.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP54xx CM2 instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM2_54XX_H +#define __ARCH_ARM_MACH_OMAP2_CM2_54XX_H + +/* CM2 base address */ +#define OMAP54XX_CM_CORE_BASE 0x4a008000 + +#define OMAP54XX_CM_CORE_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP54XX_CM_CORE_BASE + (inst) + (reg)) + +/* CM_CORE instances */ +#define OMAP54XX_CM_CORE_OCP_SOCKET_INST 0x0000 +#define OMAP54XX_CM_CORE_CKGEN_INST 0x0100 +#define OMAP54XX_CM_CORE_COREAON_INST 0x0600 +#define OMAP54XX_CM_CORE_CORE_INST 0x0700 +#define OMAP54XX_CM_CORE_IVA_INST 0x1200 +#define OMAP54XX_CM_CORE_CAM_INST 0x1300 +#define OMAP54XX_CM_CORE_DSS_INST 0x1400 +#define OMAP54XX_CM_CORE_GPU_INST 0x1500 +#define OMAP54XX_CM_CORE_L3INIT_INST 0x1600 +#define OMAP54XX_CM_CORE_CUSTEFUSE_INST 0x1700 + +/* CM_CORE clockdomain register offsets (from instance start) */ +#define OMAP54XX_CM_CORE_COREAON_COREAON_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_CORE_L3MAIN1_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_CORE_L3MAIN2_CDOFFS 0x0100 +#define OMAP54XX_CM_CORE_CORE_IPU_CDOFFS 0x0200 +#define OMAP54XX_CM_CORE_CORE_DMA_CDOFFS 0x0300 +#define OMAP54XX_CM_CORE_CORE_EMIF_CDOFFS 0x0400 +#define OMAP54XX_CM_CORE_CORE_C2C_CDOFFS 0x0500 +#define OMAP54XX_CM_CORE_CORE_L4CFG_CDOFFS 0x0600 +#define OMAP54XX_CM_CORE_CORE_L3INSTR_CDOFFS 0x0700 +#define OMAP54XX_CM_CORE_CORE_MIPIEXT_CDOFFS 0x0800 +#define OMAP54XX_CM_CORE_CORE_L4PER_CDOFFS 0x0900 +#define OMAP54XX_CM_CORE_CORE_L4SEC_CDOFFS 0x0a80 +#define OMAP54XX_CM_CORE_IVA_IVA_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_CAM_CAM_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_DSS_DSS_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_GPU_GPU_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_L3INIT_L3INIT_CDOFFS 0x0000 +#define OMAP54XX_CM_CORE_CUSTEFUSE_CUSTEFUSE_CDOFFS 0x0000 + +#endif diff --git a/arch/arm/mach-omap2/cm2_7xx.h b/arch/arm/mach-omap2/cm2_7xx.h new file mode 100644 index 000000000..af63b4b87 --- /dev/null +++ b/arch/arm/mach-omap2/cm2_7xx.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * DRA7xx CM2 instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Generated by code originally written by: + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM2_7XX_H +#define __ARCH_ARM_MACH_OMAP2_CM2_7XX_H + +/* CM2 base address */ +#define DRA7XX_CM_CORE_BASE 0x4a008000 + +#define DRA7XX_CM_CORE_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(DRA7XX_CM_CORE_BASE + (inst) + (reg)) + +/* CM_CORE instances */ +#define DRA7XX_CM_CORE_OCP_SOCKET_INST 0x0000 +#define DRA7XX_CM_CORE_CKGEN_INST 0x0104 +#define DRA7XX_CM_CORE_COREAON_INST 0x0600 +#define DRA7XX_CM_CORE_CORE_INST 0x0700 +#define DRA7XX_CM_CORE_IVA_INST 0x0f00 +#define DRA7XX_CM_CORE_CAM_INST 0x1000 +#define DRA7XX_CM_CORE_DSS_INST 0x1100 +#define DRA7XX_CM_CORE_GPU_INST 0x1200 +#define DRA7XX_CM_CORE_L3INIT_INST 0x1300 +#define DRA7XX_CM_CORE_CUSTEFUSE_INST 0x1600 +#define DRA7XX_CM_CORE_L4PER_INST 0x1700 + +/* CM_CORE clockdomain register offsets (from instance start) */ +#define DRA7XX_CM_CORE_COREAON_COREAON_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_CORE_L3MAIN1_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_CORE_IPU2_CDOFFS 0x0200 +#define DRA7XX_CM_CORE_CORE_DMA_CDOFFS 0x0300 +#define DRA7XX_CM_CORE_CORE_EMIF_CDOFFS 0x0400 +#define DRA7XX_CM_CORE_CORE_ATL_CDOFFS 0x0520 +#define DRA7XX_CM_CORE_CORE_L4CFG_CDOFFS 0x0600 +#define DRA7XX_CM_CORE_CORE_L3INSTR_CDOFFS 0x0700 +#define DRA7XX_CM_CORE_IVA_IVA_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_CAM_CAM_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_DSS_DSS_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_GPU_GPU_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_L3INIT_L3INIT_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_L3INIT_PCIE_CDOFFS 0x00a0 +#define DRA7XX_CM_CORE_L3INIT_GMAC_CDOFFS 0x00c0 +#define DRA7XX_CM_CORE_CUSTEFUSE_CUSTEFUSE_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_L4PER_L4PER_CDOFFS 0x0000 +#define DRA7XX_CM_CORE_L4PER_L4SEC_CDOFFS 0x0180 +#define DRA7XX_CM_CORE_L4PER_L4PER2_CDOFFS 0x01fc +#define DRA7XX_CM_CORE_L4PER_L4PER3_CDOFFS 0x0210 + +#endif diff --git a/arch/arm/mach-omap2/cm2xxx.c b/arch/arm/mach-omap2/cm2xxx.c new file mode 100644 index 000000000..0827acb60 --- /dev/null +++ b/arch/arm/mach-omap2/cm2xxx.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2xxx CM module functions + * + * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2008-2010, 2012 Texas Instruments, Inc. + * Paul Walmsley + * Rajendra Nayak <rnayak@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "prm2xxx.h" +#include "cm.h" +#include "cm2xxx.h" +#include "cm-regbits-24xx.h" +#include "clockdomain.h" + +/* CM_AUTOIDLE_PLL.AUTO_* bit values for DPLLs */ +#define DPLL_AUTOIDLE_DISABLE 0x0 +#define OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP 0x3 + +/* CM_AUTOIDLE_PLL.AUTO_* bit values for APLLs (OMAP2xxx only) */ +#define OMAP2XXX_APLL_AUTOIDLE_DISABLE 0x0 +#define OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP 0x3 + +/* CM_IDLEST_PLL bit value offset for APLLs (OMAP2xxx only) */ +#define EN_APLL_LOCKED 3 + +static const u8 omap2xxx_cm_idlest_offs[] = { + CM_IDLEST1, CM_IDLEST2, OMAP2430_CM_IDLEST3, OMAP24XX_CM_IDLEST4 +}; + +/* + * + */ + +static void _write_clktrctrl(u8 c, s16 module, u32 mask) +{ + u32 v; + + v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); + v &= ~mask; + v |= c << __ffs(mask); + omap2_cm_write_mod_reg(v, module, OMAP2_CM_CLKSTCTRL); +} + +static bool omap2xxx_cm_is_clkdm_in_hwsup(s16 module, u32 mask) +{ + u32 v; + + v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); + v &= mask; + v >>= __ffs(mask); + + return (v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO) ? 1 : 0; +} + +static void omap2xxx_cm_clkdm_enable_hwsup(s16 module, u32 mask) +{ + _write_clktrctrl(OMAP24XX_CLKSTCTRL_ENABLE_AUTO, module, mask); +} + +static void omap2xxx_cm_clkdm_disable_hwsup(s16 module, u32 mask) +{ + _write_clktrctrl(OMAP24XX_CLKSTCTRL_DISABLE_AUTO, module, mask); +} + +/* + * DPLL autoidle control + */ + +static void _omap2xxx_set_dpll_autoidle(u8 m) +{ + u32 v; + + v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); + v &= ~OMAP24XX_AUTO_DPLL_MASK; + v |= m << OMAP24XX_AUTO_DPLL_SHIFT; + omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); +} + +void omap2xxx_cm_set_dpll_disable_autoidle(void) +{ + _omap2xxx_set_dpll_autoidle(OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP); +} + +void omap2xxx_cm_set_dpll_auto_low_power_stop(void) +{ + _omap2xxx_set_dpll_autoidle(DPLL_AUTOIDLE_DISABLE); +} + +/* + * APLL control + */ + +static void _omap2xxx_set_apll_autoidle(u8 m, u32 mask) +{ + u32 v; + + v = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); + v &= ~mask; + v |= m << __ffs(mask); + omap2_cm_write_mod_reg(v, PLL_MOD, CM_AUTOIDLE); +} + +void omap2xxx_cm_set_apll54_disable_autoidle(void) +{ + _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP, + OMAP24XX_AUTO_54M_MASK); +} + +void omap2xxx_cm_set_apll54_auto_low_power_stop(void) +{ + _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_DISABLE, + OMAP24XX_AUTO_54M_MASK); +} + +void omap2xxx_cm_set_apll96_disable_autoidle(void) +{ + _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP, + OMAP24XX_AUTO_96M_MASK); +} + +void omap2xxx_cm_set_apll96_auto_low_power_stop(void) +{ + _omap2xxx_set_apll_autoidle(OMAP2XXX_APLL_AUTOIDLE_DISABLE, + OMAP24XX_AUTO_96M_MASK); +} + +/* Enable an APLL if off */ +static int _omap2xxx_apll_enable(u8 enable_bit, u8 status_bit) +{ + u32 v, m; + + m = EN_APLL_LOCKED << enable_bit; + + v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); + if (v & m) + return 0; /* apll already enabled */ + + v |= m; + omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN); + + omap2xxx_cm_wait_module_ready(0, PLL_MOD, 1, status_bit); + + /* + * REVISIT: Should we return an error code if + * omap2xxx_cm_wait_module_ready() fails? + */ + return 0; +} + +/* Stop APLL */ +static void _omap2xxx_apll_disable(u8 enable_bit) +{ + u32 v; + + v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); + v &= ~(EN_APLL_LOCKED << enable_bit); + omap2_cm_write_mod_reg(v, PLL_MOD, CM_CLKEN); +} + +/* Enable an APLL if off */ +int omap2xxx_cm_apll54_enable(void) +{ + return _omap2xxx_apll_enable(OMAP24XX_EN_54M_PLL_SHIFT, + OMAP24XX_ST_54M_APLL_SHIFT); +} + +/* Enable an APLL if off */ +int omap2xxx_cm_apll96_enable(void) +{ + return _omap2xxx_apll_enable(OMAP24XX_EN_96M_PLL_SHIFT, + OMAP24XX_ST_96M_APLL_SHIFT); +} + +/* Stop APLL */ +void omap2xxx_cm_apll54_disable(void) +{ + _omap2xxx_apll_disable(OMAP24XX_EN_54M_PLL_SHIFT); +} + +/* Stop APLL */ +void omap2xxx_cm_apll96_disable(void) +{ + _omap2xxx_apll_disable(OMAP24XX_EN_96M_PLL_SHIFT); +} + +/** + * omap2xxx_cm_split_idlest_reg - split CM_IDLEST reg addr into its components + * @idlest_reg: CM_IDLEST* virtual address + * @prcm_inst: pointer to an s16 to return the PRCM instance offset + * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID + * + * XXX This function is only needed until absolute register addresses are + * removed from the OMAP struct clk records. + */ +static int omap2xxx_cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, + s16 *prcm_inst, + u8 *idlest_reg_id) +{ + unsigned long offs; + u8 idlest_offs; + int i; + + idlest_offs = idlest_reg->offset & 0xff; + for (i = 0; i < ARRAY_SIZE(omap2xxx_cm_idlest_offs); i++) { + if (idlest_offs == omap2xxx_cm_idlest_offs[i]) { + *idlest_reg_id = i + 1; + break; + } + } + + if (i == ARRAY_SIZE(omap2xxx_cm_idlest_offs)) + return -EINVAL; + + offs = idlest_reg->offset; + offs &= 0xff00; + *prcm_inst = offs; + + return 0; +} + +/* + * + */ + +/** + * omap2xxx_cm_wait_module_ready - wait for a module to leave idle or standby + * @part: PRCM partition, ignored for OMAP2 + * @prcm_mod: PRCM module offset + * @idlest_id: CM_IDLESTx register ID (i.e., x = 1, 2, 3) + * @idlest_shift: shift of the bit in the CM_IDLEST* register to check + * + * Wait for the PRCM to indicate that the module identified by + * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon + * success or -EBUSY if the module doesn't enable in time. + */ +int omap2xxx_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_id, + u8 idlest_shift) +{ + int ena = 0, i = 0; + u8 cm_idlest_reg; + u32 mask; + + if (!idlest_id || (idlest_id > ARRAY_SIZE(omap2xxx_cm_idlest_offs))) + return -EINVAL; + + cm_idlest_reg = omap2xxx_cm_idlest_offs[idlest_id - 1]; + + mask = 1 << idlest_shift; + ena = mask; + + omap_test_timeout(((omap2_cm_read_mod_reg(prcm_mod, cm_idlest_reg) & + mask) == ena), MAX_MODULE_READY_TIME, i); + + return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; +} + +/* Clockdomain low-level functions */ + +static void omap2xxx_clkdm_allow_idle(struct clockdomain *clkdm) +{ + omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); +} + +static void omap2xxx_clkdm_deny_idle(struct clockdomain *clkdm) +{ + omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); +} + +static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm) +{ + bool hwsup = false; + + if (!clkdm->clktrctrl_mask) + return 0; + + hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + omap2xxx_clkdm_wakeup(clkdm); + + return 0; +} + +static int omap2xxx_clkdm_clk_disable(struct clockdomain *clkdm) +{ + bool hwsup = false; + + if (!clkdm->clktrctrl_mask) + return 0; + + hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + + if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_SLEEP) + omap2xxx_clkdm_sleep(clkdm); + + return 0; +} + +struct clkdm_ops omap2_clkdm_operations = { + .clkdm_add_wkdep = omap2_clkdm_add_wkdep, + .clkdm_del_wkdep = omap2_clkdm_del_wkdep, + .clkdm_read_wkdep = omap2_clkdm_read_wkdep, + .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps, + .clkdm_sleep = omap2xxx_clkdm_sleep, + .clkdm_wakeup = omap2xxx_clkdm_wakeup, + .clkdm_allow_idle = omap2xxx_clkdm_allow_idle, + .clkdm_deny_idle = omap2xxx_clkdm_deny_idle, + .clkdm_clk_enable = omap2xxx_clkdm_clk_enable, + .clkdm_clk_disable = omap2xxx_clkdm_clk_disable, +}; + +int omap2xxx_cm_fclks_active(void) +{ + u32 f1, f2; + + f1 = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); + f2 = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); + + return (f1 | f2) ? 1 : 0; +} + +int omap2xxx_cm_mpu_retention_allowed(void) +{ + u32 l; + + /* Check for MMC, UART2, UART1, McSPI2, McSPI1 and DSS1. */ + l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); + if (l & (OMAP2420_EN_MMC_MASK | OMAP24XX_EN_UART2_MASK | + OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_MCSPI2_MASK | + OMAP24XX_EN_MCSPI1_MASK | OMAP24XX_EN_DSS1_MASK)) + return 0; + /* Check for UART3. */ + l = omap2_cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2); + if (l & OMAP24XX_EN_UART3_MASK) + return 0; + + return 1; +} + +u32 omap2xxx_cm_get_core_clk_src(void) +{ + u32 v; + + v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); + v &= OMAP24XX_CORE_CLK_SRC_MASK; + + return v; +} + +u32 omap2xxx_cm_get_core_pll_config(void) +{ + return omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); +} + +void omap2xxx_cm_set_mod_dividers(u32 mpu, u32 dsp, u32 gfx, u32 core, u32 mdm) +{ + u32 tmp; + + omap2_cm_write_mod_reg(mpu, MPU_MOD, CM_CLKSEL); + omap2_cm_write_mod_reg(dsp, OMAP24XX_DSP_MOD, CM_CLKSEL); + omap2_cm_write_mod_reg(gfx, GFX_MOD, CM_CLKSEL); + tmp = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & + OMAP24XX_CLKSEL_DSS2_MASK; + omap2_cm_write_mod_reg(core | tmp, CORE_MOD, CM_CLKSEL1); + if (mdm) + omap2_cm_write_mod_reg(mdm, OMAP2430_MDM_MOD, CM_CLKSEL); +} + +/* + * + */ + +static const struct cm_ll_data omap2xxx_cm_ll_data = { + .split_idlest_reg = &omap2xxx_cm_split_idlest_reg, + .wait_module_ready = &omap2xxx_cm_wait_module_ready, +}; + +int __init omap2xxx_cm_init(const struct omap_prcm_init_data *data) +{ + return cm_register(&omap2xxx_cm_ll_data); +} + +static void __exit omap2xxx_cm_exit(void) +{ + cm_unregister(&omap2xxx_cm_ll_data); +} +__exitcall(omap2xxx_cm_exit); diff --git a/arch/arm/mach-omap2/cm2xxx.h b/arch/arm/mach-omap2/cm2xxx.h new file mode 100644 index 000000000..004016d74 --- /dev/null +++ b/arch/arm/mach-omap2/cm2xxx.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2xxx Clock Management (CM) register definitions + * + * Copyright (C) 2007-2009, 2012 Texas Instruments, Inc. + * Copyright (C) 2007-2010 Nokia Corporation + * Paul Walmsley + * + * The CM hardware modules on the OMAP2/3 are quite similar to each + * other. The CM modules/instances on OMAP4 are quite different, so + * they are handled in a separate file. + */ +#ifndef __ARCH_ASM_MACH_OMAP2_CM2XXX_H +#define __ARCH_ASM_MACH_OMAP2_CM2XXX_H + +#include "prcm-common.h" +#include "cm2xxx_3xxx.h" + +#define OMAP2420_CM_REGADDR(module, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP2420_CM_BASE + (module) + (reg)) +#define OMAP2430_CM_REGADDR(module, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP2430_CM_BASE + (module) + (reg)) + +/* + * Module specific CM register offsets from CM_BASE + domain offset + * Use cm_{read,write}_mod_reg() with these registers. + * These register offsets generally appear in more than one PRCM submodule. + */ + +/* OMAP2-specific register offsets */ + +#define OMAP24XX_CM_FCLKEN2 0x0004 +#define OMAP24XX_CM_ICLKEN4 0x001c +#define OMAP24XX_CM_AUTOIDLE4 0x003c +#define OMAP24XX_CM_IDLEST4 0x002c + +/* CM_IDLEST bit field values to indicate deasserted IdleReq */ + +#define OMAP24XX_CM_IDLEST_VAL 0 + + +/* Clock management domain register get/set */ + +#ifndef __ASSEMBLER__ + +extern void omap2xxx_cm_set_dpll_disable_autoidle(void); +extern void omap2xxx_cm_set_dpll_auto_low_power_stop(void); + +extern void omap2xxx_cm_set_apll54_disable_autoidle(void); +extern void omap2xxx_cm_set_apll54_auto_low_power_stop(void); +extern void omap2xxx_cm_set_apll96_disable_autoidle(void); +extern void omap2xxx_cm_set_apll96_auto_low_power_stop(void); + +int omap2xxx_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_id, + u8 idlest_shift); +extern int omap2xxx_cm_fclks_active(void); +extern int omap2xxx_cm_mpu_retention_allowed(void); +extern u32 omap2xxx_cm_get_core_clk_src(void); +extern u32 omap2xxx_cm_get_core_pll_config(void); +extern void omap2xxx_cm_set_mod_dividers(u32 mpu, u32 dsp, u32 gfx, u32 core, + u32 mdm); + +int __init omap2xxx_cm_init(const struct omap_prcm_init_data *data); + +#endif + +#endif diff --git a/arch/arm/mach-omap2/cm2xxx_3xxx.h b/arch/arm/mach-omap2/cm2xxx_3xxx.h new file mode 100644 index 000000000..70944b94c --- /dev/null +++ b/arch/arm/mach-omap2/cm2xxx_3xxx.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2/3 Clock Management (CM) register definitions + * + * Copyright (C) 2007-2009 Texas Instruments, Inc. + * Copyright (C) 2007-2010 Nokia Corporation + * Paul Walmsley + * + * The CM hardware modules on the OMAP2/3 are quite similar to each + * other. The CM modules/instances on OMAP4 are quite different, so + * they are handled in a separate file. + */ +#ifndef __ARCH_ASM_MACH_OMAP2_CM2XXX_3XXX_H +#define __ARCH_ASM_MACH_OMAP2_CM2XXX_3XXX_H + +#include "cm.h" + +/* + * Module specific CM register offsets from CM_BASE + domain offset + * Use cm_{read,write}_mod_reg() with these registers. + * These register offsets generally appear in more than one PRCM submodule. + */ + +/* Common between OMAP2 and OMAP3 */ + +#define CM_FCLKEN 0x0000 +#define CM_FCLKEN1 CM_FCLKEN +#define CM_CLKEN CM_FCLKEN +#define CM_ICLKEN 0x0010 +#define CM_ICLKEN1 CM_ICLKEN +#define CM_ICLKEN2 0x0014 +#define CM_ICLKEN3 0x0018 +#define CM_IDLEST 0x0020 +#define CM_IDLEST1 CM_IDLEST +#define CM_IDLEST2 0x0024 +#define OMAP2430_CM_IDLEST3 0x0028 +#define CM_AUTOIDLE 0x0030 +#define CM_AUTOIDLE1 CM_AUTOIDLE +#define CM_AUTOIDLE2 0x0034 +#define CM_AUTOIDLE3 0x0038 +#define CM_CLKSEL 0x0040 +#define CM_CLKSEL1 CM_CLKSEL +#define CM_CLKSEL2 0x0044 +#define OMAP2_CM_CLKSTCTRL 0x0048 + +#ifndef __ASSEMBLER__ + +#include <linux/io.h> + +static inline u32 omap2_cm_read_mod_reg(s16 module, u16 idx) +{ + return readl_relaxed(cm_base.va + module + idx); +} + +static inline void omap2_cm_write_mod_reg(u32 val, s16 module, u16 idx) +{ + writel_relaxed(val, cm_base.va + module + idx); +} + +/* Read-modify-write a register in a CM module. Caller must lock */ +static inline u32 omap2_cm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, + s16 idx) +{ + u32 v; + + v = omap2_cm_read_mod_reg(module, idx); + v &= ~mask; + v |= bits; + omap2_cm_write_mod_reg(v, module, idx); + + return v; +} + +/* Read a CM register, AND it, and shift the result down to bit 0 */ +static inline u32 omap2_cm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask) +{ + u32 v; + + v = omap2_cm_read_mod_reg(domain, idx); + v &= mask; + v >>= __ffs(mask); + + return v; +} + +static inline u32 omap2_cm_set_mod_reg_bits(u32 bits, s16 module, s16 idx) +{ + return omap2_cm_rmw_mod_reg_bits(bits, bits, module, idx); +} + +static inline u32 omap2_cm_clear_mod_reg_bits(u32 bits, s16 module, s16 idx) +{ + return omap2_cm_rmw_mod_reg_bits(bits, 0x0, module, idx); +} + +extern int omap2xxx_cm_apll54_enable(void); +extern void omap2xxx_cm_apll54_disable(void); +extern int omap2xxx_cm_apll96_enable(void); +extern void omap2xxx_cm_apll96_disable(void); + +#endif + +/* CM register bits shared between 24XX and 3430 */ + +/* CM_CLKSEL_GFX */ +#define OMAP_CLKSEL_GFX_SHIFT 0 +#define OMAP_CLKSEL_GFX_MASK (0x7 << 0) +#define OMAP_CLKSEL_GFX_WIDTH 3 + +/* CM_ICLKEN_GFX */ +#define OMAP_EN_GFX_SHIFT 0 +#define OMAP_EN_GFX_MASK (1 << 0) + +/* CM_IDLEST_GFX */ +#define OMAP_ST_GFX_MASK (1 << 0) + +#endif diff --git a/arch/arm/mach-omap2/cm33xx.c b/arch/arm/mach-omap2/cm33xx.c new file mode 100644 index 000000000..d61fa0611 --- /dev/null +++ b/arch/arm/mach-omap2/cm33xx.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AM33XX CM functions + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + * Vaibhav Hiremath <hvaibhav@ti.com> + * + * Reference taken from from OMAP4 cminst44xx.c + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "cm.h" +#include "cm33xx.h" +#include "cm-regbits-34xx.h" +#include "cm-regbits-33xx.h" +#include "prm33xx.h" + +/* + * CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield: + * + * 0x0 func: Module is fully functional, including OCP + * 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep + * abortion + * 0x2 idle: Module is in Idle mode (only OCP part). It is functional if + * using separate functional clock + * 0x3 disabled: Module is disabled and cannot be accessed + * + */ +#define CLKCTRL_IDLEST_FUNCTIONAL 0x0 +#define CLKCTRL_IDLEST_INTRANSITION 0x1 +#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 +#define CLKCTRL_IDLEST_DISABLED 0x3 + +/* Private functions */ + +/* Read a register in a CM instance */ +static inline u32 am33xx_cm_read_reg(u16 inst, u16 idx) +{ + return readl_relaxed(cm_base.va + inst + idx); +} + +/* Write into a register in a CM */ +static inline void am33xx_cm_write_reg(u32 val, u16 inst, u16 idx) +{ + writel_relaxed(val, cm_base.va + inst + idx); +} + +/* Read-modify-write a register in CM */ +static inline u32 am33xx_cm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx) +{ + u32 v; + + v = am33xx_cm_read_reg(inst, idx); + v &= ~mask; + v |= bits; + am33xx_cm_write_reg(v, inst, idx); + + return v; +} + +static inline u32 am33xx_cm_read_reg_bits(u16 inst, s16 idx, u32 mask) +{ + u32 v; + + v = am33xx_cm_read_reg(inst, idx); + v &= mask; + v >>= __ffs(mask); + + return v; +} + +/** + * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to + * bit 0. + */ +static u32 _clkctrl_idlest(u16 inst, u16 clkctrl_offs) +{ + u32 v = am33xx_cm_read_reg(inst, clkctrl_offs); + v &= AM33XX_IDLEST_MASK; + v >>= AM33XX_IDLEST_SHIFT; + return v; +} + +/** + * _is_module_ready - can module registers be accessed without causing an abort? + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either + * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise. + */ +static bool _is_module_ready(u16 inst, u16 clkctrl_offs) +{ + u32 v; + + v = _clkctrl_idlest(inst, clkctrl_offs); + + return (v == CLKCTRL_IDLEST_FUNCTIONAL || + v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false; +} + +/** + * _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield + * @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted) + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * @c must be the unshifted value for CLKTRCTRL - i.e., this function + * will handle the shift itself. + */ +static void _clktrctrl_write(u8 c, u16 inst, u16 cdoffs) +{ + u32 v; + + v = am33xx_cm_read_reg(inst, cdoffs); + v &= ~AM33XX_CLKTRCTRL_MASK; + v |= c << AM33XX_CLKTRCTRL_SHIFT; + am33xx_cm_write_reg(v, inst, cdoffs); +} + +/* Public functions */ + +/** + * am33xx_cm_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode? + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Returns true if the clockdomain referred to by (@inst, @cdoffs) + * is in hardware-supervised idle mode, or 0 otherwise. + */ +static bool am33xx_cm_is_clkdm_in_hwsup(u16 inst, u16 cdoffs) +{ + u32 v; + + v = am33xx_cm_read_reg(inst, cdoffs); + v &= AM33XX_CLKTRCTRL_MASK; + v >>= AM33XX_CLKTRCTRL_SHIFT; + + return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false; +} + +/** + * am33xx_cm_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Put a clockdomain referred to by (@inst, @cdoffs) into + * hardware-supervised idle mode. No return value. + */ +static void am33xx_cm_clkdm_enable_hwsup(u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, inst, cdoffs); +} + +/** + * am33xx_cm_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Put a clockdomain referred to by (@inst, @cdoffs) into + * software-supervised idle mode, i.e., controlled manually by the + * Linux OMAP clockdomain code. No return value. + */ +static void am33xx_cm_clkdm_disable_hwsup(u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, inst, cdoffs); +} + +/** + * am33xx_cm_clkdm_force_sleep - try to put a clockdomain into idle + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Put a clockdomain referred to by (@inst, @cdoffs) into idle + * No return value. + */ +static void am33xx_cm_clkdm_force_sleep(u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, inst, cdoffs); +} + +/** + * am33xx_cm_clkdm_force_wakeup - try to take a clockdomain out of idle + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Take a clockdomain referred to by (@inst, @cdoffs) out of idle, + * waking it up. No return value. + */ +static void am33xx_cm_clkdm_force_wakeup(u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, inst, cdoffs); +} + +/* + * + */ + +/** + * am33xx_cm_wait_module_ready - wait for a module to be in 'func' state + * @part: PRCM partition, ignored for AM33xx + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * @bit_shift: bit shift for the register, ignored for AM33xx + * + * Wait for the module IDLEST to be functional. If the idle state is in any + * the non functional state (trans, idle or disabled), module and thus the + * sysconfig cannot be accessed and will probably lead to an "imprecise + * external abort" + */ +static int am33xx_cm_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs, + u8 bit_shift) +{ + int i = 0; + + omap_test_timeout(_is_module_ready(inst, clkctrl_offs), + MAX_MODULE_READY_TIME, i); + + return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; +} + +/** + * am33xx_cm_wait_module_idle - wait for a module to be in 'disabled' + * state + * @part: CM partition, ignored for AM33xx + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * @bit_shift: bit shift for the register, ignored for AM33xx + * + * Wait for the module IDLEST to be disabled. Some PRCM transition, + * like reset assertion or parent clock de-activation must wait the + * module to be fully disabled. + */ +static int am33xx_cm_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs, + u8 bit_shift) +{ + int i = 0; + + omap_test_timeout((_clkctrl_idlest(inst, clkctrl_offs) == + CLKCTRL_IDLEST_DISABLED), + MAX_MODULE_READY_TIME, i); + + return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; +} + +/** + * am33xx_cm_module_enable - Enable the modulemode inside CLKCTRL + * @mode: Module mode (SW or HW) + * @part: CM partition, ignored for AM33xx + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * No return value. + */ +static void am33xx_cm_module_enable(u8 mode, u8 part, u16 inst, + u16 clkctrl_offs) +{ + u32 v; + + v = am33xx_cm_read_reg(inst, clkctrl_offs); + v &= ~AM33XX_MODULEMODE_MASK; + v |= mode << AM33XX_MODULEMODE_SHIFT; + am33xx_cm_write_reg(v, inst, clkctrl_offs); +} + +/** + * am33xx_cm_module_disable - Disable the module inside CLKCTRL + * @part: CM partition, ignored for AM33xx + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * No return value. + */ +static void am33xx_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs) +{ + u32 v; + + v = am33xx_cm_read_reg(inst, clkctrl_offs); + v &= ~AM33XX_MODULEMODE_MASK; + am33xx_cm_write_reg(v, inst, clkctrl_offs); +} + +/* + * Clockdomain low-level functions + */ + +static int am33xx_clkdm_sleep(struct clockdomain *clkdm) +{ + am33xx_cm_clkdm_force_sleep(clkdm->cm_inst, clkdm->clkdm_offs); + return 0; +} + +static int am33xx_clkdm_wakeup(struct clockdomain *clkdm) +{ + am33xx_cm_clkdm_force_wakeup(clkdm->cm_inst, clkdm->clkdm_offs); + return 0; +} + +static void am33xx_clkdm_allow_idle(struct clockdomain *clkdm) +{ + am33xx_cm_clkdm_enable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs); +} + +static void am33xx_clkdm_deny_idle(struct clockdomain *clkdm) +{ + am33xx_cm_clkdm_disable_hwsup(clkdm->cm_inst, clkdm->clkdm_offs); +} + +static int am33xx_clkdm_clk_enable(struct clockdomain *clkdm) +{ + if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + return am33xx_clkdm_wakeup(clkdm); + + return 0; +} + +static int am33xx_clkdm_clk_disable(struct clockdomain *clkdm) +{ + bool hwsup = false; + + hwsup = am33xx_cm_is_clkdm_in_hwsup(clkdm->cm_inst, clkdm->clkdm_offs); + + if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) + am33xx_clkdm_sleep(clkdm); + + return 0; +} + +static u32 am33xx_cm_xlate_clkctrl(u8 part, u16 inst, u16 offset) +{ + return cm_base.pa + inst + offset; +} + +/** + * am33xx_clkdm_save_context - Save the clockdomain transition context + * @clkdm: The clockdomain pointer whose context needs to be saved + * + * Save the clockdomain transition context. + */ +static int am33xx_clkdm_save_context(struct clockdomain *clkdm) +{ + clkdm->context = am33xx_cm_read_reg_bits(clkdm->cm_inst, + clkdm->clkdm_offs, + AM33XX_CLKTRCTRL_MASK); + + return 0; +} + +/** + * am33xx_restore_save_context - Restore the clockdomain transition context + * @clkdm: The clockdomain pointer whose context needs to be restored + * + * Restore the clockdomain transition context. + */ +static int am33xx_clkdm_restore_context(struct clockdomain *clkdm) +{ + switch (clkdm->context) { + case OMAP34XX_CLKSTCTRL_DISABLE_AUTO: + am33xx_clkdm_deny_idle(clkdm); + break; + case OMAP34XX_CLKSTCTRL_FORCE_SLEEP: + am33xx_clkdm_sleep(clkdm); + break; + case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP: + am33xx_clkdm_wakeup(clkdm); + break; + case OMAP34XX_CLKSTCTRL_ENABLE_AUTO: + am33xx_clkdm_allow_idle(clkdm); + break; + } + return 0; +} + +struct clkdm_ops am33xx_clkdm_operations = { + .clkdm_sleep = am33xx_clkdm_sleep, + .clkdm_wakeup = am33xx_clkdm_wakeup, + .clkdm_allow_idle = am33xx_clkdm_allow_idle, + .clkdm_deny_idle = am33xx_clkdm_deny_idle, + .clkdm_clk_enable = am33xx_clkdm_clk_enable, + .clkdm_clk_disable = am33xx_clkdm_clk_disable, + .clkdm_save_context = am33xx_clkdm_save_context, + .clkdm_restore_context = am33xx_clkdm_restore_context, +}; + +static const struct cm_ll_data am33xx_cm_ll_data = { + .wait_module_ready = &am33xx_cm_wait_module_ready, + .wait_module_idle = &am33xx_cm_wait_module_idle, + .module_enable = &am33xx_cm_module_enable, + .module_disable = &am33xx_cm_module_disable, + .xlate_clkctrl = &am33xx_cm_xlate_clkctrl, +}; + +int __init am33xx_cm_init(const struct omap_prcm_init_data *data) +{ + return cm_register(&am33xx_cm_ll_data); +} + +static void __exit am33xx_cm_exit(void) +{ + cm_unregister(&am33xx_cm_ll_data); +} +__exitcall(am33xx_cm_exit); diff --git a/arch/arm/mach-omap2/cm33xx.h b/arch/arm/mach-omap2/cm33xx.h new file mode 100644 index 000000000..456267a7a --- /dev/null +++ b/arch/arm/mach-omap2/cm33xx.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AM33XX CM offset macros + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + * Vaibhav Hiremath <hvaibhav@ti.com> + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM_33XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_33XX_H + +#include "cm.h" +#include "cm-regbits-33xx.h" +#include "prcm-common.h" + +/* CM base address */ +#define AM33XX_CM_BASE 0x44e00000 + +#define AM33XX_CM_REGADDR(inst, reg) \ + AM33XX_L4_WK_IO_ADDRESS(AM33XX_CM_BASE + (inst) + (reg)) + +/* CM instances */ +#define AM33XX_CM_PER_MOD 0x0000 +#define AM33XX_CM_WKUP_MOD 0x0400 +#define AM33XX_CM_DPLL_MOD 0x0500 +#define AM33XX_CM_MPU_MOD 0x0600 +#define AM33XX_CM_DEVICE_MOD 0x0700 +#define AM33XX_CM_RTC_MOD 0x0800 +#define AM33XX_CM_GFX_MOD 0x0900 +#define AM33XX_CM_CEFUSE_MOD 0x0A00 + +/* CM.PER_CM register offsets */ +#define AM33XX_CM_PER_L4LS_CLKSTCTRL_OFFSET 0x0000 +#define AM33XX_CM_PER_L4LS_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0000) +#define AM33XX_CM_PER_L3S_CLKSTCTRL_OFFSET 0x0004 +#define AM33XX_CM_PER_L3S_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0004) +#define AM33XX_CM_PER_L4FW_CLKSTCTRL_OFFSET 0x0008 +#define AM33XX_CM_PER_L4FW_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0008) +#define AM33XX_CM_PER_L3_CLKSTCTRL_OFFSET 0x000c +#define AM33XX_CM_PER_L3_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x000c) +#define AM33XX_CM_PER_EMIF_CLKCTRL_OFFSET 0x0028 +#define AM33XX_CM_PER_EMIF_CLKCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0028) +#define AM33XX_CM_PER_L4HS_CLKSTCTRL_OFFSET 0x011c +#define AM33XX_CM_PER_L4HS_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x011c) +#define AM33XX_CM_PER_OCPWP_L3_CLKSTCTRL_OFFSET 0x012c +#define AM33XX_CM_PER_OCPWP_L3_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x012c) +#define AM33XX_CM_PER_PRUSS_CLKSTCTRL_OFFSET 0x0140 +#define AM33XX_CM_PER_PRUSS_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0140) +#define AM33XX_CM_PER_CPSW_CLKSTCTRL_OFFSET 0x0144 +#define AM33XX_CM_PER_CPSW_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0144) +#define AM33XX_CM_PER_LCDC_CLKSTCTRL_OFFSET 0x0148 +#define AM33XX_CM_PER_LCDC_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0148) +#define AM33XX_CM_PER_CLK_24MHZ_CLKSTCTRL_OFFSET 0x0150 +#define AM33XX_CM_PER_CLK_24MHZ_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_PER_MOD, 0x0150) + +/* CM.WKUP_CM register offsets */ +#define AM33XX_CM_WKUP_CLKSTCTRL_OFFSET 0x0000 +#define AM33XX_CM_WKUP_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_WKUP_MOD, 0x0000) +#define AM33XX_CM_L3_AON_CLKSTCTRL_OFFSET 0x0018 +#define AM33XX_CM_L3_AON_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_WKUP_MOD, 0x0018) +#define AM33XX_CM_L4_WKUP_AON_CLKSTCTRL_OFFSET 0x00cc +#define AM33XX_CM_L4_WKUP_AON_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_WKUP_MOD, 0x00cc) + +/* CM.DPLL_CM register offsets */ +#define AM33XX_CLKSEL_GFX_FCLK AM33XX_CM_REGADDR(AM33XX_CM_DPLL_MOD, 0x002c) + +/* CM.MPU_CM register offsets */ +#define AM33XX_CM_MPU_CLKSTCTRL_OFFSET 0x0000 +#define AM33XX_CM_MPU_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_MPU_MOD, 0x0000) +#define AM33XX_CM_MPU_MPU_CLKCTRL AM33XX_CM_REGADDR(AM33XX_CM_MPU_MOD, 0x0004) + +/* CM.DEVICE_CM register offsets */ + +/* CM.RTC_CM register offsets */ +#define AM33XX_CM_RTC_CLKSTCTRL_OFFSET 0x0004 +#define AM33XX_CM_RTC_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_RTC_MOD, 0x0004) + +/* CM.GFX_CM register offsets */ +#define AM33XX_CM_GFX_L3_CLKSTCTRL_OFFSET 0x0000 +#define AM33XX_CM_GFX_L3_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_GFX_MOD, 0x0000) +#define AM33XX_CM_GFX_L4LS_GFX_CLKSTCTRL__1_OFFSET 0x000c +#define AM33XX_CM_GFX_L4LS_GFX_CLKSTCTRL__1 AM33XX_CM_REGADDR(AM33XX_CM_GFX_MOD, 0x000c) + +/* CM.CEFUSE_CM register offsets */ +#define AM33XX_CM_CEFUSE_CLKSTCTRL_OFFSET 0x0000 +#define AM33XX_CM_CEFUSE_CLKSTCTRL AM33XX_CM_REGADDR(AM33XX_CM_CEFUSE_MOD, 0x0000) + + +#ifndef __ASSEMBLER__ +int am33xx_cm_init(const struct omap_prcm_init_data *data); +#endif /* ASSEMBLER */ +#endif diff --git a/arch/arm/mach-omap2/cm3xxx.c b/arch/arm/mach-omap2/cm3xxx.c new file mode 100644 index 000000000..b03b6123b --- /dev/null +++ b/arch/arm/mach-omap2/cm3xxx.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3xxx CM module functions + * + * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2008-2010, 2012 Texas Instruments, Inc. + * Paul Walmsley + * Rajendra Nayak <rnayak@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "prm2xxx_3xxx.h" +#include "cm.h" +#include "cm3xxx.h" +#include "cm-regbits-34xx.h" +#include "clockdomain.h" + +static const u8 omap3xxx_cm_idlest_offs[] = { + CM_IDLEST1, CM_IDLEST2, OMAP2430_CM_IDLEST3 +}; + +/* + * + */ + +static void _write_clktrctrl(u8 c, s16 module, u32 mask) +{ + u32 v; + + v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); + v &= ~mask; + v |= c << __ffs(mask); + omap2_cm_write_mod_reg(v, module, OMAP2_CM_CLKSTCTRL); +} + +static bool omap3xxx_cm_is_clkdm_in_hwsup(s16 module, u32 mask) +{ + u32 v; + + v = omap2_cm_read_mod_reg(module, OMAP2_CM_CLKSTCTRL); + v &= mask; + v >>= __ffs(mask); + + return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? 1 : 0; +} + +static void omap3xxx_cm_clkdm_enable_hwsup(s16 module, u32 mask) +{ + _write_clktrctrl(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, module, mask); +} + +static void omap3xxx_cm_clkdm_disable_hwsup(s16 module, u32 mask) +{ + _write_clktrctrl(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, module, mask); +} + +static void omap3xxx_cm_clkdm_force_sleep(s16 module, u32 mask) +{ + _write_clktrctrl(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, module, mask); +} + +static void omap3xxx_cm_clkdm_force_wakeup(s16 module, u32 mask) +{ + _write_clktrctrl(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, module, mask); +} + +/* + * + */ + +/** + * omap3xxx_cm_wait_module_ready - wait for a module to leave idle or standby + * @part: PRCM partition, ignored for OMAP3 + * @prcm_mod: PRCM module offset + * @idlest_id: CM_IDLESTx register ID (i.e., x = 1, 2, 3) + * @idlest_shift: shift of the bit in the CM_IDLEST* register to check + * + * Wait for the PRCM to indicate that the module identified by + * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon + * success or -EBUSY if the module doesn't enable in time. + */ +static int omap3xxx_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_id, + u8 idlest_shift) +{ + int ena = 0, i = 0; + u8 cm_idlest_reg; + u32 mask; + + if (!idlest_id || (idlest_id > ARRAY_SIZE(omap3xxx_cm_idlest_offs))) + return -EINVAL; + + cm_idlest_reg = omap3xxx_cm_idlest_offs[idlest_id - 1]; + + mask = 1 << idlest_shift; + ena = 0; + + omap_test_timeout(((omap2_cm_read_mod_reg(prcm_mod, cm_idlest_reg) & + mask) == ena), MAX_MODULE_READY_TIME, i); + + return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; +} + +/** + * omap3xxx_cm_split_idlest_reg - split CM_IDLEST reg addr into its components + * @idlest_reg: CM_IDLEST* virtual address + * @prcm_inst: pointer to an s16 to return the PRCM instance offset + * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID + * + * XXX This function is only needed until absolute register addresses are + * removed from the OMAP struct clk records. + */ +static int omap3xxx_cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, + s16 *prcm_inst, + u8 *idlest_reg_id) +{ + unsigned long offs; + u8 idlest_offs; + int i; + + idlest_offs = idlest_reg->offset & 0xff; + for (i = 0; i < ARRAY_SIZE(omap3xxx_cm_idlest_offs); i++) { + if (idlest_offs == omap3xxx_cm_idlest_offs[i]) { + *idlest_reg_id = i + 1; + break; + } + } + + if (i == ARRAY_SIZE(omap3xxx_cm_idlest_offs)) + return -EINVAL; + + offs = idlest_reg->offset; + offs &= 0xff00; + *prcm_inst = offs; + + return 0; +} + +/* Clockdomain low-level operations */ + +static int omap3xxx_clkdm_add_sleepdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + omap2_cm_set_mod_reg_bits((1 << clkdm2->dep_bit), + clkdm1->pwrdm.ptr->prcm_offs, + OMAP3430_CM_SLEEPDEP); + return 0; +} + +static int omap3xxx_clkdm_del_sleepdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + omap2_cm_clear_mod_reg_bits((1 << clkdm2->dep_bit), + clkdm1->pwrdm.ptr->prcm_offs, + OMAP3430_CM_SLEEPDEP); + return 0; +} + +static int omap3xxx_clkdm_read_sleepdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + return omap2_cm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, + OMAP3430_CM_SLEEPDEP, + (1 << clkdm2->dep_bit)); +} + +static int omap3xxx_clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) +{ + struct clkdm_dep *cd; + u32 mask = 0; + + for (cd = clkdm->sleepdep_srcs; cd && cd->clkdm_name; cd++) { + if (!cd->clkdm) + continue; /* only happens if data is erroneous */ + + mask |= 1 << cd->clkdm->dep_bit; + cd->sleepdep_usecount = 0; + } + omap2_cm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, + OMAP3430_CM_SLEEPDEP); + return 0; +} + +static int omap3xxx_clkdm_sleep(struct clockdomain *clkdm) +{ + omap3xxx_cm_clkdm_force_sleep(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + return 0; +} + +static int omap3xxx_clkdm_wakeup(struct clockdomain *clkdm) +{ + omap3xxx_cm_clkdm_force_wakeup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + return 0; +} + +static void omap3xxx_clkdm_allow_idle(struct clockdomain *clkdm) +{ + if (clkdm->usecount > 0) + clkdm_add_autodeps(clkdm); + + omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); +} + +static void omap3xxx_clkdm_deny_idle(struct clockdomain *clkdm) +{ + omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + + if (clkdm->usecount > 0) + clkdm_del_autodeps(clkdm); +} + +static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm) +{ + bool hwsup = false; + + if (!clkdm->clktrctrl_mask) + return 0; + + /* + * The CLKDM_MISSING_IDLE_REPORTING flag documentation has + * more details on the unpleasant problem this is working + * around + */ + if ((clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) && + (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) { + omap3xxx_clkdm_wakeup(clkdm); + return 0; + } + + hwsup = omap3xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + + if (hwsup) { + /* Disable HW transitions when we are changing deps */ + omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + clkdm_add_autodeps(clkdm); + omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + } else { + if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + omap3xxx_clkdm_wakeup(clkdm); + } + + return 0; +} + +static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm) +{ + bool hwsup = false; + + if (!clkdm->clktrctrl_mask) + return 0; + + /* + * The CLKDM_MISSING_IDLE_REPORTING flag documentation has + * more details on the unpleasant problem this is working + * around + */ + if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING && + !(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) { + omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + return 0; + } + + hwsup = omap3xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + + if (hwsup) { + /* Disable HW transitions when we are changing deps */ + omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + clkdm_del_autodeps(clkdm); + omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); + } else { + if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP) + omap3xxx_clkdm_sleep(clkdm); + } + + return 0; +} + +struct clkdm_ops omap3_clkdm_operations = { + .clkdm_add_wkdep = omap2_clkdm_add_wkdep, + .clkdm_del_wkdep = omap2_clkdm_del_wkdep, + .clkdm_read_wkdep = omap2_clkdm_read_wkdep, + .clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps, + .clkdm_add_sleepdep = omap3xxx_clkdm_add_sleepdep, + .clkdm_del_sleepdep = omap3xxx_clkdm_del_sleepdep, + .clkdm_read_sleepdep = omap3xxx_clkdm_read_sleepdep, + .clkdm_clear_all_sleepdeps = omap3xxx_clkdm_clear_all_sleepdeps, + .clkdm_sleep = omap3xxx_clkdm_sleep, + .clkdm_wakeup = omap3xxx_clkdm_wakeup, + .clkdm_allow_idle = omap3xxx_clkdm_allow_idle, + .clkdm_deny_idle = omap3xxx_clkdm_deny_idle, + .clkdm_clk_enable = omap3xxx_clkdm_clk_enable, + .clkdm_clk_disable = omap3xxx_clkdm_clk_disable, +}; + +/* + * Context save/restore code - OMAP3 only + */ +struct omap3_cm_regs { + u32 iva2_cm_clksel1; + u32 iva2_cm_clksel2; + u32 cm_sysconfig; + u32 sgx_cm_clksel; + u32 dss_cm_clksel; + u32 cam_cm_clksel; + u32 per_cm_clksel; + u32 emu_cm_clksel; + u32 emu_cm_clkstctrl; + u32 pll_cm_autoidle; + u32 pll_cm_autoidle2; + u32 pll_cm_clksel4; + u32 pll_cm_clksel5; + u32 pll_cm_clken2; + u32 cm_polctrl; + u32 iva2_cm_fclken; + u32 iva2_cm_clken_pll; + u32 core_cm_fclken1; + u32 core_cm_fclken3; + u32 sgx_cm_fclken; + u32 wkup_cm_fclken; + u32 dss_cm_fclken; + u32 cam_cm_fclken; + u32 per_cm_fclken; + u32 usbhost_cm_fclken; + u32 core_cm_iclken1; + u32 core_cm_iclken2; + u32 core_cm_iclken3; + u32 sgx_cm_iclken; + u32 wkup_cm_iclken; + u32 dss_cm_iclken; + u32 cam_cm_iclken; + u32 per_cm_iclken; + u32 usbhost_cm_iclken; + u32 iva2_cm_autoidle2; + u32 mpu_cm_autoidle2; + u32 iva2_cm_clkstctrl; + u32 mpu_cm_clkstctrl; + u32 core_cm_clkstctrl; + u32 sgx_cm_clkstctrl; + u32 dss_cm_clkstctrl; + u32 cam_cm_clkstctrl; + u32 per_cm_clkstctrl; + u32 neon_cm_clkstctrl; + u32 usbhost_cm_clkstctrl; + u32 core_cm_autoidle1; + u32 core_cm_autoidle2; + u32 core_cm_autoidle3; + u32 wkup_cm_autoidle; + u32 dss_cm_autoidle; + u32 cam_cm_autoidle; + u32 per_cm_autoidle; + u32 usbhost_cm_autoidle; + u32 sgx_cm_sleepdep; + u32 dss_cm_sleepdep; + u32 cam_cm_sleepdep; + u32 per_cm_sleepdep; + u32 usbhost_cm_sleepdep; + u32 cm_clkout_ctrl; +}; + +static struct omap3_cm_regs cm_context; + +void omap3_cm_save_context(void) +{ + cm_context.iva2_cm_clksel1 = + omap2_cm_read_mod_reg(OMAP3430_IVA2_MOD, CM_CLKSEL1); + cm_context.iva2_cm_clksel2 = + omap2_cm_read_mod_reg(OMAP3430_IVA2_MOD, CM_CLKSEL2); + cm_context.cm_sysconfig = + omap2_cm_read_mod_reg(OCP_MOD, OMAP3430_CM_SYSCONFIG); + cm_context.sgx_cm_clksel = + omap2_cm_read_mod_reg(OMAP3430ES2_SGX_MOD, CM_CLKSEL); + cm_context.dss_cm_clksel = + omap2_cm_read_mod_reg(OMAP3430_DSS_MOD, CM_CLKSEL); + cm_context.cam_cm_clksel = + omap2_cm_read_mod_reg(OMAP3430_CAM_MOD, CM_CLKSEL); + cm_context.per_cm_clksel = + omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_CLKSEL); + cm_context.emu_cm_clksel = + omap2_cm_read_mod_reg(OMAP3430_EMU_MOD, CM_CLKSEL1); + cm_context.emu_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430_EMU_MOD, OMAP2_CM_CLKSTCTRL); + /* + * As per erratum i671, ROM code does not respect the PER DPLL + * programming scheme if CM_AUTOIDLE_PLL.AUTO_PERIPH_DPLL == 1. + * In this case, even though this register has been saved in + * scratchpad contents, we need to restore AUTO_PERIPH_DPLL + * by ourselves. So, we need to save it anyway. + */ + cm_context.pll_cm_autoidle = + omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE); + cm_context.pll_cm_autoidle2 = + omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE2); + cm_context.pll_cm_clksel4 = + omap2_cm_read_mod_reg(PLL_MOD, OMAP3430ES2_CM_CLKSEL4); + cm_context.pll_cm_clksel5 = + omap2_cm_read_mod_reg(PLL_MOD, OMAP3430ES2_CM_CLKSEL5); + cm_context.pll_cm_clken2 = + omap2_cm_read_mod_reg(PLL_MOD, OMAP3430ES2_CM_CLKEN2); + cm_context.cm_polctrl = + omap2_cm_read_mod_reg(OCP_MOD, OMAP3430_CM_POLCTRL); + cm_context.iva2_cm_fclken = + omap2_cm_read_mod_reg(OMAP3430_IVA2_MOD, CM_FCLKEN); + cm_context.iva2_cm_clken_pll = + omap2_cm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP3430_CM_CLKEN_PLL); + cm_context.core_cm_fclken1 = + omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); + cm_context.core_cm_fclken3 = + omap2_cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3); + cm_context.sgx_cm_fclken = + omap2_cm_read_mod_reg(OMAP3430ES2_SGX_MOD, CM_FCLKEN); + cm_context.wkup_cm_fclken = + omap2_cm_read_mod_reg(WKUP_MOD, CM_FCLKEN); + cm_context.dss_cm_fclken = + omap2_cm_read_mod_reg(OMAP3430_DSS_MOD, CM_FCLKEN); + cm_context.cam_cm_fclken = + omap2_cm_read_mod_reg(OMAP3430_CAM_MOD, CM_FCLKEN); + cm_context.per_cm_fclken = + omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN); + cm_context.usbhost_cm_fclken = + omap2_cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, CM_FCLKEN); + cm_context.core_cm_iclken1 = + omap2_cm_read_mod_reg(CORE_MOD, CM_ICLKEN1); + cm_context.core_cm_iclken2 = + omap2_cm_read_mod_reg(CORE_MOD, CM_ICLKEN2); + cm_context.core_cm_iclken3 = + omap2_cm_read_mod_reg(CORE_MOD, CM_ICLKEN3); + cm_context.sgx_cm_iclken = + omap2_cm_read_mod_reg(OMAP3430ES2_SGX_MOD, CM_ICLKEN); + cm_context.wkup_cm_iclken = + omap2_cm_read_mod_reg(WKUP_MOD, CM_ICLKEN); + cm_context.dss_cm_iclken = + omap2_cm_read_mod_reg(OMAP3430_DSS_MOD, CM_ICLKEN); + cm_context.cam_cm_iclken = + omap2_cm_read_mod_reg(OMAP3430_CAM_MOD, CM_ICLKEN); + cm_context.per_cm_iclken = + omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN); + cm_context.usbhost_cm_iclken = + omap2_cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, CM_ICLKEN); + cm_context.iva2_cm_autoidle2 = + omap2_cm_read_mod_reg(OMAP3430_IVA2_MOD, CM_AUTOIDLE2); + cm_context.mpu_cm_autoidle2 = + omap2_cm_read_mod_reg(MPU_MOD, CM_AUTOIDLE2); + cm_context.iva2_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.mpu_cm_clkstctrl = + omap2_cm_read_mod_reg(MPU_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.core_cm_clkstctrl = + omap2_cm_read_mod_reg(CORE_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.sgx_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430ES2_SGX_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.dss_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430_DSS_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.cam_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430_CAM_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.per_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430_PER_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.neon_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430_NEON_MOD, OMAP2_CM_CLKSTCTRL); + cm_context.usbhost_cm_clkstctrl = + omap2_cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, + OMAP2_CM_CLKSTCTRL); + cm_context.core_cm_autoidle1 = + omap2_cm_read_mod_reg(CORE_MOD, CM_AUTOIDLE1); + cm_context.core_cm_autoidle2 = + omap2_cm_read_mod_reg(CORE_MOD, CM_AUTOIDLE2); + cm_context.core_cm_autoidle3 = + omap2_cm_read_mod_reg(CORE_MOD, CM_AUTOIDLE3); + cm_context.wkup_cm_autoidle = + omap2_cm_read_mod_reg(WKUP_MOD, CM_AUTOIDLE); + cm_context.dss_cm_autoidle = + omap2_cm_read_mod_reg(OMAP3430_DSS_MOD, CM_AUTOIDLE); + cm_context.cam_cm_autoidle = + omap2_cm_read_mod_reg(OMAP3430_CAM_MOD, CM_AUTOIDLE); + cm_context.per_cm_autoidle = + omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE); + cm_context.usbhost_cm_autoidle = + omap2_cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, CM_AUTOIDLE); + cm_context.sgx_cm_sleepdep = + omap2_cm_read_mod_reg(OMAP3430ES2_SGX_MOD, + OMAP3430_CM_SLEEPDEP); + cm_context.dss_cm_sleepdep = + omap2_cm_read_mod_reg(OMAP3430_DSS_MOD, OMAP3430_CM_SLEEPDEP); + cm_context.cam_cm_sleepdep = + omap2_cm_read_mod_reg(OMAP3430_CAM_MOD, OMAP3430_CM_SLEEPDEP); + cm_context.per_cm_sleepdep = + omap2_cm_read_mod_reg(OMAP3430_PER_MOD, OMAP3430_CM_SLEEPDEP); + cm_context.usbhost_cm_sleepdep = + omap2_cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, + OMAP3430_CM_SLEEPDEP); + cm_context.cm_clkout_ctrl = + omap2_cm_read_mod_reg(OMAP3430_CCR_MOD, + OMAP3_CM_CLKOUT_CTRL_OFFSET); +} + +void omap3_cm_restore_context(void) +{ + omap2_cm_write_mod_reg(cm_context.iva2_cm_clksel1, OMAP3430_IVA2_MOD, + CM_CLKSEL1); + omap2_cm_write_mod_reg(cm_context.iva2_cm_clksel2, OMAP3430_IVA2_MOD, + CM_CLKSEL2); + omap2_cm_write_mod_reg(cm_context.cm_sysconfig, OCP_MOD, + OMAP3430_CM_SYSCONFIG); + omap2_cm_write_mod_reg(cm_context.sgx_cm_clksel, OMAP3430ES2_SGX_MOD, + CM_CLKSEL); + omap2_cm_write_mod_reg(cm_context.dss_cm_clksel, OMAP3430_DSS_MOD, + CM_CLKSEL); + omap2_cm_write_mod_reg(cm_context.cam_cm_clksel, OMAP3430_CAM_MOD, + CM_CLKSEL); + omap2_cm_write_mod_reg(cm_context.per_cm_clksel, OMAP3430_PER_MOD, + CM_CLKSEL); + omap2_cm_write_mod_reg(cm_context.emu_cm_clksel, OMAP3430_EMU_MOD, + CM_CLKSEL1); + omap2_cm_write_mod_reg(cm_context.emu_cm_clkstctrl, OMAP3430_EMU_MOD, + OMAP2_CM_CLKSTCTRL); + /* + * As per erratum i671, ROM code does not respect the PER DPLL + * programming scheme if CM_AUTOIDLE_PLL.AUTO_PERIPH_DPLL == 1. + * In this case, we need to restore AUTO_PERIPH_DPLL by ourselves. + */ + omap2_cm_write_mod_reg(cm_context.pll_cm_autoidle, PLL_MOD, + CM_AUTOIDLE); + omap2_cm_write_mod_reg(cm_context.pll_cm_autoidle2, PLL_MOD, + CM_AUTOIDLE2); + omap2_cm_write_mod_reg(cm_context.pll_cm_clksel4, PLL_MOD, + OMAP3430ES2_CM_CLKSEL4); + omap2_cm_write_mod_reg(cm_context.pll_cm_clksel5, PLL_MOD, + OMAP3430ES2_CM_CLKSEL5); + omap2_cm_write_mod_reg(cm_context.pll_cm_clken2, PLL_MOD, + OMAP3430ES2_CM_CLKEN2); + omap2_cm_write_mod_reg(cm_context.cm_polctrl, OCP_MOD, + OMAP3430_CM_POLCTRL); + omap2_cm_write_mod_reg(cm_context.iva2_cm_fclken, OMAP3430_IVA2_MOD, + CM_FCLKEN); + omap2_cm_write_mod_reg(cm_context.iva2_cm_clken_pll, OMAP3430_IVA2_MOD, + OMAP3430_CM_CLKEN_PLL); + omap2_cm_write_mod_reg(cm_context.core_cm_fclken1, CORE_MOD, + CM_FCLKEN1); + omap2_cm_write_mod_reg(cm_context.core_cm_fclken3, CORE_MOD, + OMAP3430ES2_CM_FCLKEN3); + omap2_cm_write_mod_reg(cm_context.sgx_cm_fclken, OMAP3430ES2_SGX_MOD, + CM_FCLKEN); + omap2_cm_write_mod_reg(cm_context.wkup_cm_fclken, WKUP_MOD, CM_FCLKEN); + omap2_cm_write_mod_reg(cm_context.dss_cm_fclken, OMAP3430_DSS_MOD, + CM_FCLKEN); + omap2_cm_write_mod_reg(cm_context.cam_cm_fclken, OMAP3430_CAM_MOD, + CM_FCLKEN); + omap2_cm_write_mod_reg(cm_context.per_cm_fclken, OMAP3430_PER_MOD, + CM_FCLKEN); + omap2_cm_write_mod_reg(cm_context.usbhost_cm_fclken, + OMAP3430ES2_USBHOST_MOD, CM_FCLKEN); + omap2_cm_write_mod_reg(cm_context.core_cm_iclken1, CORE_MOD, + CM_ICLKEN1); + omap2_cm_write_mod_reg(cm_context.core_cm_iclken2, CORE_MOD, + CM_ICLKEN2); + omap2_cm_write_mod_reg(cm_context.core_cm_iclken3, CORE_MOD, + CM_ICLKEN3); + omap2_cm_write_mod_reg(cm_context.sgx_cm_iclken, OMAP3430ES2_SGX_MOD, + CM_ICLKEN); + omap2_cm_write_mod_reg(cm_context.wkup_cm_iclken, WKUP_MOD, CM_ICLKEN); + omap2_cm_write_mod_reg(cm_context.dss_cm_iclken, OMAP3430_DSS_MOD, + CM_ICLKEN); + omap2_cm_write_mod_reg(cm_context.cam_cm_iclken, OMAP3430_CAM_MOD, + CM_ICLKEN); + omap2_cm_write_mod_reg(cm_context.per_cm_iclken, OMAP3430_PER_MOD, + CM_ICLKEN); + omap2_cm_write_mod_reg(cm_context.usbhost_cm_iclken, + OMAP3430ES2_USBHOST_MOD, CM_ICLKEN); + omap2_cm_write_mod_reg(cm_context.iva2_cm_autoidle2, OMAP3430_IVA2_MOD, + CM_AUTOIDLE2); + omap2_cm_write_mod_reg(cm_context.mpu_cm_autoidle2, MPU_MOD, + CM_AUTOIDLE2); + omap2_cm_write_mod_reg(cm_context.iva2_cm_clkstctrl, OMAP3430_IVA2_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.mpu_cm_clkstctrl, MPU_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.core_cm_clkstctrl, CORE_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.sgx_cm_clkstctrl, OMAP3430ES2_SGX_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.dss_cm_clkstctrl, OMAP3430_DSS_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.cam_cm_clkstctrl, OMAP3430_CAM_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.per_cm_clkstctrl, OMAP3430_PER_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.neon_cm_clkstctrl, OMAP3430_NEON_MOD, + OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.usbhost_cm_clkstctrl, + OMAP3430ES2_USBHOST_MOD, OMAP2_CM_CLKSTCTRL); + omap2_cm_write_mod_reg(cm_context.core_cm_autoidle1, CORE_MOD, + CM_AUTOIDLE1); + omap2_cm_write_mod_reg(cm_context.core_cm_autoidle2, CORE_MOD, + CM_AUTOIDLE2); + omap2_cm_write_mod_reg(cm_context.core_cm_autoidle3, CORE_MOD, + CM_AUTOIDLE3); + omap2_cm_write_mod_reg(cm_context.wkup_cm_autoidle, WKUP_MOD, + CM_AUTOIDLE); + omap2_cm_write_mod_reg(cm_context.dss_cm_autoidle, OMAP3430_DSS_MOD, + CM_AUTOIDLE); + omap2_cm_write_mod_reg(cm_context.cam_cm_autoidle, OMAP3430_CAM_MOD, + CM_AUTOIDLE); + omap2_cm_write_mod_reg(cm_context.per_cm_autoidle, OMAP3430_PER_MOD, + CM_AUTOIDLE); + omap2_cm_write_mod_reg(cm_context.usbhost_cm_autoidle, + OMAP3430ES2_USBHOST_MOD, CM_AUTOIDLE); + omap2_cm_write_mod_reg(cm_context.sgx_cm_sleepdep, OMAP3430ES2_SGX_MOD, + OMAP3430_CM_SLEEPDEP); + omap2_cm_write_mod_reg(cm_context.dss_cm_sleepdep, OMAP3430_DSS_MOD, + OMAP3430_CM_SLEEPDEP); + omap2_cm_write_mod_reg(cm_context.cam_cm_sleepdep, OMAP3430_CAM_MOD, + OMAP3430_CM_SLEEPDEP); + omap2_cm_write_mod_reg(cm_context.per_cm_sleepdep, OMAP3430_PER_MOD, + OMAP3430_CM_SLEEPDEP); + omap2_cm_write_mod_reg(cm_context.usbhost_cm_sleepdep, + OMAP3430ES2_USBHOST_MOD, OMAP3430_CM_SLEEPDEP); + omap2_cm_write_mod_reg(cm_context.cm_clkout_ctrl, OMAP3430_CCR_MOD, + OMAP3_CM_CLKOUT_CTRL_OFFSET); +} + +void omap3_cm_save_scratchpad_contents(u32 *ptr) +{ + *ptr++ = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL); + *ptr++ = omap2_cm_read_mod_reg(WKUP_MOD, CM_CLKSEL); + *ptr++ = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKEN); + + /* + * As per erratum i671, ROM code does not respect the PER DPLL + * programming scheme if CM_AUTOIDLE_PLL..AUTO_PERIPH_DPLL == 1. + * Then, in any case, clear these bits to avoid extra latencies. + */ + *ptr++ = omap2_cm_read_mod_reg(PLL_MOD, CM_AUTOIDLE) & + ~OMAP3430_AUTO_PERIPH_DPLL_MASK; + *ptr++ = omap2_cm_read_mod_reg(PLL_MOD, OMAP3430_CM_CLKSEL1_PLL); + *ptr++ = omap2_cm_read_mod_reg(PLL_MOD, OMAP3430_CM_CLKSEL2_PLL); + *ptr++ = omap2_cm_read_mod_reg(PLL_MOD, OMAP3430_CM_CLKSEL3); + *ptr++ = omap2_cm_read_mod_reg(MPU_MOD, OMAP3430_CM_CLKEN_PLL); + *ptr++ = omap2_cm_read_mod_reg(MPU_MOD, OMAP3430_CM_AUTOIDLE_PLL); + *ptr++ = omap2_cm_read_mod_reg(MPU_MOD, OMAP3430_CM_CLKSEL1_PLL); + *ptr++ = omap2_cm_read_mod_reg(MPU_MOD, OMAP3430_CM_CLKSEL2_PLL); +} + +/* + * + */ + +static const struct cm_ll_data omap3xxx_cm_ll_data = { + .split_idlest_reg = &omap3xxx_cm_split_idlest_reg, + .wait_module_ready = &omap3xxx_cm_wait_module_ready, +}; + +int __init omap3xxx_cm_init(const struct omap_prcm_init_data *data) +{ + omap2_clk_legacy_provider_init(TI_CLKM_CM, cm_base.va + + OMAP3430_IVA2_MOD); + return cm_register(&omap3xxx_cm_ll_data); +} + +static void __exit omap3xxx_cm_exit(void) +{ + cm_unregister(&omap3xxx_cm_ll_data); +} +__exitcall(omap3xxx_cm_exit); diff --git a/arch/arm/mach-omap2/cm3xxx.h b/arch/arm/mach-omap2/cm3xxx.h new file mode 100644 index 000000000..ab0dc206d --- /dev/null +++ b/arch/arm/mach-omap2/cm3xxx.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2/3 Clock Management (CM) register definitions + * + * Copyright (C) 2007-2009 Texas Instruments, Inc. + * Copyright (C) 2007-2010 Nokia Corporation + * Paul Walmsley + * + * The CM hardware modules on the OMAP2/3 are quite similar to each + * other. The CM modules/instances on OMAP4 are quite different, so + * they are handled in a separate file. + */ +#ifndef __ARCH_ASM_MACH_OMAP2_CM3XXX_H +#define __ARCH_ASM_MACH_OMAP2_CM3XXX_H + +#include "prcm-common.h" +#include "cm2xxx_3xxx.h" + +#define OMAP34XX_CM_REGADDR(module, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP3430_CM_BASE + (module) + (reg)) + + +/* + * OMAP3-specific global CM registers + * Use cm_{read,write}_reg() with these registers. + * These registers appear once per CM module. + */ + +#define OMAP3430_CM_SYSCONFIG 0x0010 +#define OMAP3430_CM_POLCTRL 0x009c + +#define OMAP3_CM_CLKOUT_CTRL_OFFSET 0x0070 +#define OMAP3430_CM_CLKOUT_CTRL OMAP_CM_REGADDR(OMAP3430_CCR_MOD, 0x0070) + +/* + * Module specific CM register offsets from CM_BASE + domain offset + * Use cm_{read,write}_mod_reg() with these registers. + * These register offsets generally appear in more than one PRCM submodule. + */ + +/* OMAP3-specific register offsets */ + +#define OMAP3430_CM_CLKEN_PLL 0x0004 +#define OMAP3430ES2_CM_CLKEN2 0x0004 +#define OMAP3430ES2_CM_FCLKEN3 0x0008 +#define OMAP3430_CM_IDLEST_PLL CM_IDLEST2 +#define OMAP3430_CM_AUTOIDLE_PLL CM_AUTOIDLE2 +#define OMAP3430ES2_CM_AUTOIDLE2_PLL CM_AUTOIDLE2 +#define OMAP3430_CM_CLKSEL1 CM_CLKSEL +#define OMAP3430_CM_CLKSEL1_PLL CM_CLKSEL +#define OMAP3430_CM_CLKSEL2_PLL CM_CLKSEL2 +#define OMAP3430_CM_SLEEPDEP CM_CLKSEL2 +#define OMAP3430_CM_CLKSEL3 OMAP2_CM_CLKSTCTRL +#define OMAP3430_CM_CLKSTST 0x004c +#define OMAP3430ES2_CM_CLKSEL4 0x004c +#define OMAP3430ES2_CM_CLKSEL5 0x0050 +#define OMAP3430_CM_CLKSEL2_EMU 0x0050 +#define OMAP3430_CM_CLKSEL3_EMU 0x0054 + + +/* CM_IDLEST bit field values to indicate deasserted IdleReq */ + +#define OMAP34XX_CM_IDLEST_VAL 1 + + +#ifndef __ASSEMBLER__ + +extern void omap3_cm_save_context(void); +extern void omap3_cm_restore_context(void); +extern void omap3_cm_save_scratchpad_contents(u32 *ptr); + +int __init omap3xxx_cm_init(const struct omap_prcm_init_data *data); + +#endif + +#endif diff --git a/arch/arm/mach-omap2/cm44xx.h b/arch/arm/mach-omap2/cm44xx.h new file mode 100644 index 000000000..826fdda54 --- /dev/null +++ b/arch/arm/mach-omap2/cm44xx.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP4 Clock Management (CM) definitions + * + * Copyright (C) 2007-2011 Texas Instruments, Inc. + * Copyright (C) 2007-2009 Nokia Corporation + * + * Written by Paul Walmsley + * + * OMAP4 has two separate CM blocks, CM1 and CM2. This file contains + * macros and function prototypes that are applicable to both. + */ +#ifndef __ARCH_ASM_MACH_OMAP2_CM44XX_H +#define __ARCH_ASM_MACH_OMAP2_CM44XX_H + + +#include "prcm-common.h" +#include "cm.h" + +#define OMAP4_CM_CLKSTCTRL 0x0000 +#define OMAP4_CM_STATICDEP 0x0004 + +int omap4_cm_init(const struct omap_prcm_init_data *data); + +#endif diff --git a/arch/arm/mach-omap2/cm81xx.h b/arch/arm/mach-omap2/cm81xx.h new file mode 100644 index 000000000..ffcde1812 --- /dev/null +++ b/arch/arm/mach-omap2/cm81xx.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Clock domain register offsets for TI81XX. + * + * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ + * Copyright (C) 2013 SKTB SKiT, http://www.skitlab.ru/ + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CM_TI81XX_H +#define __ARCH_ARM_MACH_OMAP2_CM_TI81XX_H + +/* TI81XX common CM module offsets */ +#define TI81XX_CM_ACTIVE_MOD 0x0400 /* 256B */ +#define TI81XX_CM_DEFAULT_MOD 0x0500 /* 256B */ +#define TI81XX_CM_ALWON_MOD 0x1400 /* 1KB */ +#define TI81XX_CM_SGX_MOD 0x0900 /* 256B */ + +/* TI816X CM module offsets */ +#define TI816X_CM_IVAHD0_MOD 0x0600 /* 256B */ +#define TI816X_CM_IVAHD1_MOD 0x0700 /* 256B */ +#define TI816X_CM_IVAHD2_MOD 0x0800 /* 256B */ + +/* ALWON */ +#define TI81XX_CM_ALWON_L3_SLOW_CLKDM 0x0000 +#define TI81XX_CM_ALWON_L3_MED_CLKDM 0x0004 +#define TI81XX_CM_ETHERNET_CLKDM 0x0004 +#define TI81XX_CM_MMU_CLKDM 0x000C +#define TI81XX_CM_MMUCFG_CLKDM 0x0010 +#define TI81XX_CM_ALWON_MPU_CLKDM 0x001C +#define TI81XX_CM_ALWON_L3_FAST_CLKDM 0x0030 + +/* ACTIVE */ +#define TI816X_CM_ACTIVE_GEM_CLKDM 0x0000 + +/* IVAHD0 */ +#define TI816X_CM_IVAHD0_CLKDM 0x0000 + +/* IVAHD1 */ +#define TI816X_CM_IVAHD1_CLKDM 0x0000 + +/* IVAHD2 */ +#define TI816X_CM_IVAHD2_CLKDM 0x0000 + +/* SGX */ +#define TI816X_CM_SGX_CLKDM 0x0000 + +/* DEFAULT */ +#define TI816X_CM_DEFAULT_L3_MED_CLKDM 0x0004 +#define TI816X_CM_DEFAULT_PCI_CLKDM 0x0010 +#define TI816X_CM_DEFAULT_L3_SLOW_CLKDM 0x0014 +#define TI816X_CM_DEFAULT_DUCATI_CLKDM 0x0018 +#define TI816X_CM_DEFAULT_SATA_CLKDM 0x0060 + +#endif diff --git a/arch/arm/mach-omap2/cm_common.c b/arch/arm/mach-omap2/cm_common.c new file mode 100644 index 000000000..87f2c2d2d --- /dev/null +++ b/arch/arm/mach-omap2/cm_common.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2+ common Clock Management (CM) IP block functions + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * XXX This code should eventually be moved to a CM driver. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/bug.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "cm2xxx.h" +#include "cm3xxx.h" +#include "cm33xx.h" +#include "cm44xx.h" +#include "clock.h" + +/* + * cm_ll_data: function pointers to SoC-specific implementations of + * common CM functions + */ +static struct cm_ll_data null_cm_ll_data; +static const struct cm_ll_data *cm_ll_data = &null_cm_ll_data; + +/* cm_base: base virtual address of the CM IP block */ +struct omap_domain_base cm_base; + +/* cm2_base: base virtual address of the CM2 IP block (OMAP44xx only) */ +struct omap_domain_base cm2_base; + +#define CM_NO_CLOCKS 0x1 +#define CM_SINGLE_INSTANCE 0x2 + +/** + * cm_split_idlest_reg - split CM_IDLEST reg addr into its components + * @idlest_reg: CM_IDLEST* virtual address + * @prcm_inst: pointer to an s16 to return the PRCM instance offset + * @idlest_reg_id: pointer to a u8 to return the CM_IDLESTx register ID + * + * Given an absolute CM_IDLEST register address @idlest_reg, passes + * the PRCM instance offset and IDLEST register ID back to the caller + * via the @prcm_inst and @idlest_reg_id. Returns -EINVAL upon error, + * or 0 upon success. XXX This function is only needed until absolute + * register addresses are removed from the OMAP struct clk records. + */ +int cm_split_idlest_reg(struct clk_omap_reg *idlest_reg, s16 *prcm_inst, + u8 *idlest_reg_id) +{ + int ret; + if (!cm_ll_data->split_idlest_reg) { + WARN_ONCE(1, "cm: %s: no low-level function defined\n", + __func__); + return -EINVAL; + } + + ret = cm_ll_data->split_idlest_reg(idlest_reg, prcm_inst, + idlest_reg_id); + *prcm_inst -= cm_base.offset; + return ret; +} + +/** + * omap_cm_wait_module_ready - wait for a module to leave idle or standby + * @part: PRCM partition + * @prcm_mod: PRCM module offset + * @idlest_reg: CM_IDLESTx register + * @idlest_shift: shift of the bit in the CM_IDLEST* register to check + * + * Wait for the PRCM to indicate that the module identified by + * (@prcm_mod, @idlest_id, @idlest_shift) is clocked. Return 0 upon + * success, -EBUSY if the module doesn't enable in time, or -EINVAL if + * no per-SoC wait_module_ready() function pointer has been registered + * or if the idlest register is unknown on the SoC. + */ +int omap_cm_wait_module_ready(u8 part, s16 prcm_mod, u16 idlest_reg, + u8 idlest_shift) +{ + if (!cm_ll_data->wait_module_ready) { + WARN_ONCE(1, "cm: %s: no low-level function defined\n", + __func__); + return -EINVAL; + } + + return cm_ll_data->wait_module_ready(part, prcm_mod, idlest_reg, + idlest_shift); +} + +/** + * omap_cm_wait_module_idle - wait for a module to enter idle or standby + * @part: PRCM partition + * @prcm_mod: PRCM module offset + * @idlest_reg: CM_IDLESTx register + * @idlest_shift: shift of the bit in the CM_IDLEST* register to check + * + * Wait for the PRCM to indicate that the module identified by + * (@prcm_mod, @idlest_id, @idlest_shift) is no longer clocked. Return + * 0 upon success, -EBUSY if the module doesn't enable in time, or + * -EINVAL if no per-SoC wait_module_idle() function pointer has been + * registered or if the idlest register is unknown on the SoC. + */ +int omap_cm_wait_module_idle(u8 part, s16 prcm_mod, u16 idlest_reg, + u8 idlest_shift) +{ + if (!cm_ll_data->wait_module_idle) { + WARN_ONCE(1, "cm: %s: no low-level function defined\n", + __func__); + return -EINVAL; + } + + return cm_ll_data->wait_module_idle(part, prcm_mod, idlest_reg, + idlest_shift); +} + +/** + * omap_cm_module_enable - enable a module + * @mode: target mode for the module + * @part: PRCM partition + * @inst: PRCM instance + * @clkctrl_offs: CM_CLKCTRL register offset for the module + * + * Enables clocks for a module identified by (@part, @inst, @clkctrl_offs) + * making its IO space accessible. Return 0 upon success, -EINVAL if no + * per-SoC module_enable() function pointer has been registered. + */ +int omap_cm_module_enable(u8 mode, u8 part, u16 inst, u16 clkctrl_offs) +{ + if (!cm_ll_data->module_enable) { + WARN_ONCE(1, "cm: %s: no low-level function defined\n", + __func__); + return -EINVAL; + } + + cm_ll_data->module_enable(mode, part, inst, clkctrl_offs); + return 0; +} + +/** + * omap_cm_module_disable - disable a module + * @part: PRCM partition + * @inst: PRCM instance + * @clkctrl_offs: CM_CLKCTRL register offset for the module + * + * Disables clocks for a module identified by (@part, @inst, @clkctrl_offs) + * makings its IO space inaccessible. Return 0 upon success, -EINVAL if + * no per-SoC module_disable() function pointer has been registered. + */ +int omap_cm_module_disable(u8 part, u16 inst, u16 clkctrl_offs) +{ + if (!cm_ll_data->module_disable) { + WARN_ONCE(1, "cm: %s: no low-level function defined\n", + __func__); + return -EINVAL; + } + + cm_ll_data->module_disable(part, inst, clkctrl_offs); + return 0; +} + +u32 omap_cm_xlate_clkctrl(u8 part, u16 inst, u16 clkctrl_offs) +{ + if (!cm_ll_data->xlate_clkctrl) { + WARN_ONCE(1, "cm: %s: no low-level function defined\n", + __func__); + return 0; + } + return cm_ll_data->xlate_clkctrl(part, inst, clkctrl_offs); +} + +/** + * cm_register - register per-SoC low-level data with the CM + * @cld: low-level per-SoC OMAP CM data & function pointers to register + * + * Register per-SoC low-level OMAP CM data and function pointers with + * the OMAP CM common interface. The caller must keep the data + * pointed to by @cld valid until it calls cm_unregister() and + * it returns successfully. Returns 0 upon success, -EINVAL if @cld + * is NULL, or -EEXIST if cm_register() has already been called + * without an intervening cm_unregister(). + */ +int cm_register(const struct cm_ll_data *cld) +{ + if (!cld) + return -EINVAL; + + if (cm_ll_data != &null_cm_ll_data) + return -EEXIST; + + cm_ll_data = cld; + + return 0; +} + +/** + * cm_unregister - unregister per-SoC low-level data & function pointers + * @cld: low-level per-SoC OMAP CM data & function pointers to unregister + * + * Unregister per-SoC low-level OMAP CM data and function pointers + * that were previously registered with cm_register(). The + * caller may not destroy any of the data pointed to by @cld until + * this function returns successfully. Returns 0 upon success, or + * -EINVAL if @cld is NULL or if @cld does not match the struct + * cm_ll_data * previously registered by cm_register(). + */ +int cm_unregister(const struct cm_ll_data *cld) +{ + if (!cld || cm_ll_data != cld) + return -EINVAL; + + cm_ll_data = &null_cm_ll_data; + + return 0; +} + +#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \ + defined(CONFIG_SOC_DRA7XX) +static struct omap_prcm_init_data cm_data __initdata = { + .index = TI_CLKM_CM, + .init = omap4_cm_init, +}; + +static struct omap_prcm_init_data cm2_data __initdata = { + .index = TI_CLKM_CM2, + .init = omap4_cm_init, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP2 +static struct omap_prcm_init_data omap2_prcm_data __initdata = { + .index = TI_CLKM_CM, + .init = omap2xxx_cm_init, + .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP3 +static struct omap_prcm_init_data omap3_cm_data __initdata = { + .index = TI_CLKM_CM, + .init = omap3xxx_cm_init, + .flags = CM_SINGLE_INSTANCE, + + /* + * IVA2 offset is a negative value, must offset the cm_base address + * by this to get it to positive side on the iomap + */ + .offset = -OMAP3430_IVA2_MOD, +}; +#endif + +#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_TI81XX) +static struct omap_prcm_init_data am3_prcm_data __initdata = { + .index = TI_CLKM_CM, + .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, + .init = am33xx_cm_init, +}; +#endif + +#ifdef CONFIG_SOC_AM43XX +static struct omap_prcm_init_data am4_prcm_data __initdata = { + .index = TI_CLKM_CM, + .flags = CM_NO_CLOCKS | CM_SINGLE_INSTANCE, + .init = omap4_cm_init, +}; +#endif + +static const struct of_device_id omap_cm_dt_match_table[] __initconst = { +#ifdef CONFIG_ARCH_OMAP2 + { .compatible = "ti,omap2-prcm", .data = &omap2_prcm_data }, +#endif +#ifdef CONFIG_ARCH_OMAP3 + { .compatible = "ti,omap3-cm", .data = &omap3_cm_data }, +#endif +#ifdef CONFIG_ARCH_OMAP4 + { .compatible = "ti,omap4-cm1", .data = &cm_data }, + { .compatible = "ti,omap4-cm2", .data = &cm2_data }, +#endif +#ifdef CONFIG_SOC_OMAP5 + { .compatible = "ti,omap5-cm-core-aon", .data = &cm_data }, + { .compatible = "ti,omap5-cm-core", .data = &cm2_data }, +#endif +#ifdef CONFIG_SOC_DRA7XX + { .compatible = "ti,dra7-cm-core-aon", .data = &cm_data }, + { .compatible = "ti,dra7-cm-core", .data = &cm2_data }, +#endif +#ifdef CONFIG_SOC_AM33XX + { .compatible = "ti,am3-prcm", .data = &am3_prcm_data }, +#endif +#ifdef CONFIG_SOC_AM43XX + { .compatible = "ti,am4-prcm", .data = &am4_prcm_data }, +#endif +#ifdef CONFIG_SOC_TI81XX + { .compatible = "ti,dm814-prcm", .data = &am3_prcm_data }, + { .compatible = "ti,dm816-prcm", .data = &am3_prcm_data }, +#endif + { } +}; + +/** + * omap2_cm_base_init - initialize iomappings for the CM drivers + * + * Detects and initializes the iomappings for the CM driver, based + * on the DT data. Returns 0 in success, negative error value + * otherwise. + */ +int __init omap2_cm_base_init(void) +{ + struct device_node *np; + const struct of_device_id *match; + struct omap_prcm_init_data *data; + struct resource res; + int ret; + struct omap_domain_base *mem = NULL; + + for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { + data = (struct omap_prcm_init_data *)match->data; + + ret = of_address_to_resource(np, 0, &res); + if (ret) { + of_node_put(np); + return ret; + } + + if (data->index == TI_CLKM_CM) + mem = &cm_base; + + if (data->index == TI_CLKM_CM2) + mem = &cm2_base; + + data->mem = ioremap(res.start, resource_size(&res)); + + if (mem) { + mem->pa = res.start + data->offset; + mem->va = data->mem + data->offset; + mem->offset = data->offset; + } + + data->np = np; + + if (data->init && (data->flags & CM_SINGLE_INSTANCE || + (cm_base.va && cm2_base.va))) + data->init(data); + } + + return 0; +} + +/** + * omap_cm_init - low level init for the CM drivers + * + * Initializes the low level clock infrastructure for CM drivers. + * Returns 0 in success, negative error value in failure. + */ +int __init omap_cm_init(void) +{ + struct device_node *np; + const struct of_device_id *match; + const struct omap_prcm_init_data *data; + int ret; + + for_each_matching_node_and_match(np, omap_cm_dt_match_table, &match) { + data = match->data; + + if (data->flags & CM_NO_CLOCKS) + continue; + + ret = omap2_clk_provider_init(np, data->index, NULL, data->mem); + if (ret) { + of_node_put(np); + return ret; + } + } + + return 0; +} diff --git a/arch/arm/mach-omap2/cminst44xx.c b/arch/arm/mach-omap2/cminst44xx.c new file mode 100644 index 000000000..46670521b --- /dev/null +++ b/arch/arm/mach-omap2/cminst44xx.c @@ -0,0 +1,569 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 CM instance functions + * + * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2008-2011 Texas Instruments, Inc. + * Paul Walmsley + * Rajendra Nayak <rnayak@ti.com> + * + * This is needed since CM instances can be in the PRM, PRCM_MPU, CM1, + * or CM2 hardware modules. For example, the EMU_CM CM instance is in + * the PRM hardware module. What a mess... + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "clockdomain.h" +#include "cm.h" +#include "cm1_44xx.h" +#include "cm2_44xx.h" +#include "cm44xx.h" +#include "cm-regbits-34xx.h" +#include "prcm44xx.h" +#include "prm44xx.h" +#include "prcm_mpu44xx.h" +#include "prcm-common.h" + +#define OMAP4430_IDLEST_SHIFT 16 +#define OMAP4430_IDLEST_MASK (0x3 << 16) +#define OMAP4430_CLKTRCTRL_SHIFT 0 +#define OMAP4430_CLKTRCTRL_MASK (0x3 << 0) +#define OMAP4430_MODULEMODE_SHIFT 0 +#define OMAP4430_MODULEMODE_MASK (0x3 << 0) + +/* + * CLKCTRL_IDLEST_*: possible values for the CM_*_CLKCTRL.IDLEST bitfield: + * + * 0x0 func: Module is fully functional, including OCP + * 0x1 trans: Module is performing transition: wakeup, or sleep, or sleep + * abortion + * 0x2 idle: Module is in Idle mode (only OCP part). It is functional if + * using separate functional clock + * 0x3 disabled: Module is disabled and cannot be accessed + * + */ +#define CLKCTRL_IDLEST_FUNCTIONAL 0x0 +#define CLKCTRL_IDLEST_INTRANSITION 0x1 +#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 +#define CLKCTRL_IDLEST_DISABLED 0x3 + +static struct omap_domain_base _cm_bases[OMAP4_MAX_PRCM_PARTITIONS]; + +/** + * omap_cm_base_init - Populates the cm partitions + * + * Populates the base addresses of the _cm_bases + * array used for read/write of cm module registers. + */ +static void omap_cm_base_init(void) +{ + memcpy(&_cm_bases[OMAP4430_PRM_PARTITION], &prm_base, sizeof(prm_base)); + memcpy(&_cm_bases[OMAP4430_CM1_PARTITION], &cm_base, sizeof(cm_base)); + memcpy(&_cm_bases[OMAP4430_CM2_PARTITION], &cm2_base, sizeof(cm2_base)); + memcpy(&_cm_bases[OMAP4430_PRCM_MPU_PARTITION], &prcm_mpu_base, + sizeof(prcm_mpu_base)); +} + +/* Private functions */ + +static u32 omap4_cminst_read_inst_reg(u8 part, u16 inst, u16 idx); + +/** + * _clkctrl_idlest - read a CM_*_CLKCTRL register; mask & shift IDLEST bitfield + * @part: PRCM partition ID that the CM_CLKCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * Return the IDLEST bitfield of a CM_*_CLKCTRL register, shifted down to + * bit 0. + */ +static u32 _clkctrl_idlest(u8 part, u16 inst, u16 clkctrl_offs) +{ + u32 v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs); + v &= OMAP4430_IDLEST_MASK; + v >>= OMAP4430_IDLEST_SHIFT; + return v; +} + +/** + * _is_module_ready - can module registers be accessed without causing an abort? + * @part: PRCM partition ID that the CM_CLKCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * Returns true if the module's CM_*_CLKCTRL.IDLEST bitfield is either + * *FUNCTIONAL or *INTERFACE_IDLE; false otherwise. + */ +static bool _is_module_ready(u8 part, u16 inst, u16 clkctrl_offs) +{ + u32 v; + + v = _clkctrl_idlest(part, inst, clkctrl_offs); + + return (v == CLKCTRL_IDLEST_FUNCTIONAL || + v == CLKCTRL_IDLEST_INTERFACE_IDLE) ? true : false; +} + +/* Read a register in a CM instance */ +static u32 omap4_cminst_read_inst_reg(u8 part, u16 inst, u16 idx) +{ + BUG_ON(part >= OMAP4_MAX_PRCM_PARTITIONS || + part == OMAP4430_INVALID_PRCM_PARTITION || + !_cm_bases[part].va); + return readl_relaxed(_cm_bases[part].va + inst + idx); +} + +/* Write into a register in a CM instance */ +static void omap4_cminst_write_inst_reg(u32 val, u8 part, u16 inst, u16 idx) +{ + BUG_ON(part >= OMAP4_MAX_PRCM_PARTITIONS || + part == OMAP4430_INVALID_PRCM_PARTITION || + !_cm_bases[part].va); + writel_relaxed(val, _cm_bases[part].va + inst + idx); +} + +/* Read-modify-write a register in CM1. Caller must lock */ +static u32 omap4_cminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, u16 inst, + s16 idx) +{ + u32 v; + + v = omap4_cminst_read_inst_reg(part, inst, idx); + v &= ~mask; + v |= bits; + omap4_cminst_write_inst_reg(v, part, inst, idx); + + return v; +} + +static u32 omap4_cminst_set_inst_reg_bits(u32 bits, u8 part, u16 inst, s16 idx) +{ + return omap4_cminst_rmw_inst_reg_bits(bits, bits, part, inst, idx); +} + +static u32 omap4_cminst_clear_inst_reg_bits(u32 bits, u8 part, u16 inst, + s16 idx) +{ + return omap4_cminst_rmw_inst_reg_bits(bits, 0x0, part, inst, idx); +} + +static u32 omap4_cminst_read_inst_reg_bits(u8 part, u16 inst, s16 idx, u32 mask) +{ + u32 v; + + v = omap4_cminst_read_inst_reg(part, inst, idx); + v &= mask; + v >>= __ffs(mask); + + return v; +} + +/* + * + */ + +/** + * _clktrctrl_write - write @c to a CM_CLKSTCTRL.CLKTRCTRL register bitfield + * @c: CLKTRCTRL register bitfield (LSB = bit 0, i.e., unshifted) + * @part: PRCM partition ID that the CM_CLKSTCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * @c must be the unshifted value for CLKTRCTRL - i.e., this function + * will handle the shift itself. + */ +static void _clktrctrl_write(u8 c, u8 part, u16 inst, u16 cdoffs) +{ + u32 v; + + v = omap4_cminst_read_inst_reg(part, inst, cdoffs + OMAP4_CM_CLKSTCTRL); + v &= ~OMAP4430_CLKTRCTRL_MASK; + v |= c << OMAP4430_CLKTRCTRL_SHIFT; + omap4_cminst_write_inst_reg(v, part, inst, cdoffs + OMAP4_CM_CLKSTCTRL); +} + +/** + * omap4_cminst_is_clkdm_in_hwsup - is a clockdomain in hwsup idle mode? + * @part: PRCM partition ID that the CM_CLKSTCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Returns true if the clockdomain referred to by (@part, @inst, @cdoffs) + * is in hardware-supervised idle mode, or 0 otherwise. + */ +static bool omap4_cminst_is_clkdm_in_hwsup(u8 part, u16 inst, u16 cdoffs) +{ + u32 v; + + v = omap4_cminst_read_inst_reg(part, inst, cdoffs + OMAP4_CM_CLKSTCTRL); + v &= OMAP4430_CLKTRCTRL_MASK; + v >>= OMAP4430_CLKTRCTRL_SHIFT; + + return (v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ? true : false; +} + +/** + * omap4_cminst_clkdm_enable_hwsup - put a clockdomain in hwsup-idle mode + * @part: PRCM partition ID that the clockdomain registers exist in + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Put a clockdomain referred to by (@part, @inst, @cdoffs) into + * hardware-supervised idle mode. No return value. + */ +static void omap4_cminst_clkdm_enable_hwsup(u8 part, u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, part, inst, cdoffs); +} + +/** + * omap4_cminst_clkdm_disable_hwsup - put a clockdomain in swsup-idle mode + * @part: PRCM partition ID that the clockdomain registers exist in + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Put a clockdomain referred to by (@part, @inst, @cdoffs) into + * software-supervised idle mode, i.e., controlled manually by the + * Linux OMAP clockdomain code. No return value. + */ +static void omap4_cminst_clkdm_disable_hwsup(u8 part, u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_DISABLE_AUTO, part, inst, cdoffs); +} + +/** + * omap4_cminst_clkdm_force_sleep - try to take a clockdomain out of idle + * @part: PRCM partition ID that the clockdomain registers exist in + * @inst: CM instance register offset (*_INST macro) + * @cdoffs: Clockdomain register offset (*_CDOFFS macro) + * + * Take a clockdomain referred to by (@part, @inst, @cdoffs) out of idle, + * waking it up. No return value. + */ +static void omap4_cminst_clkdm_force_wakeup(u8 part, u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_WAKEUP, part, inst, cdoffs); +} + +/* + * + */ + +static void omap4_cminst_clkdm_force_sleep(u8 part, u16 inst, u16 cdoffs) +{ + _clktrctrl_write(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, part, inst, cdoffs); +} + +/** + * omap4_cminst_wait_module_ready - wait for a module to be in 'func' state + * @part: PRCM partition ID that the CM_CLKCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * @bit_shift: bit shift for the register, ignored for OMAP4+ + * + * Wait for the module IDLEST to be functional. If the idle state is in any + * the non functional state (trans, idle or disabled), module and thus the + * sysconfig cannot be accessed and will probably lead to an "imprecise + * external abort" + */ +static int omap4_cminst_wait_module_ready(u8 part, s16 inst, u16 clkctrl_offs, + u8 bit_shift) +{ + int i = 0; + + omap_test_timeout(_is_module_ready(part, inst, clkctrl_offs), + MAX_MODULE_READY_TIME, i); + + return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY; +} + +/** + * omap4_cminst_wait_module_idle - wait for a module to be in 'disabled' + * state + * @part: PRCM partition ID that the CM_CLKCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * @bit_shift: Bit shift for the register, ignored for OMAP4+ + * + * Wait for the module IDLEST to be disabled. Some PRCM transition, + * like reset assertion or parent clock de-activation must wait the + * module to be fully disabled. + */ +static int omap4_cminst_wait_module_idle(u8 part, s16 inst, u16 clkctrl_offs, + u8 bit_shift) +{ + int i = 0; + + omap_test_timeout((_clkctrl_idlest(part, inst, clkctrl_offs) == + CLKCTRL_IDLEST_DISABLED), + MAX_MODULE_DISABLE_TIME, i); + + return (i < MAX_MODULE_DISABLE_TIME) ? 0 : -EBUSY; +} + +/** + * omap4_cminst_module_enable - Enable the modulemode inside CLKCTRL + * @mode: Module mode (SW or HW) + * @part: PRCM partition ID that the CM_CLKCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * No return value. + */ +static void omap4_cminst_module_enable(u8 mode, u8 part, u16 inst, + u16 clkctrl_offs) +{ + u32 v; + + v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs); + v &= ~OMAP4430_MODULEMODE_MASK; + v |= mode << OMAP4430_MODULEMODE_SHIFT; + omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs); +} + +/** + * omap4_cminst_module_disable - Disable the module inside CLKCTRL + * @part: PRCM partition ID that the CM_CLKCTRL register exists in + * @inst: CM instance register offset (*_INST macro) + * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro) + * + * No return value. + */ +static void omap4_cminst_module_disable(u8 part, u16 inst, u16 clkctrl_offs) +{ + u32 v; + + v = omap4_cminst_read_inst_reg(part, inst, clkctrl_offs); + v &= ~OMAP4430_MODULEMODE_MASK; + omap4_cminst_write_inst_reg(v, part, inst, clkctrl_offs); +} + +/* + * Clockdomain low-level functions + */ + +static int omap4_clkdm_add_wkup_sleep_dep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + omap4_cminst_set_inst_reg_bits((1 << clkdm2->dep_bit), + clkdm1->prcm_partition, + clkdm1->cm_inst, clkdm1->clkdm_offs + + OMAP4_CM_STATICDEP); + return 0; +} + +static int omap4_clkdm_del_wkup_sleep_dep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + omap4_cminst_clear_inst_reg_bits((1 << clkdm2->dep_bit), + clkdm1->prcm_partition, + clkdm1->cm_inst, clkdm1->clkdm_offs + + OMAP4_CM_STATICDEP); + return 0; +} + +static int omap4_clkdm_read_wkup_sleep_dep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + return omap4_cminst_read_inst_reg_bits(clkdm1->prcm_partition, + clkdm1->cm_inst, + clkdm1->clkdm_offs + + OMAP4_CM_STATICDEP, + (1 << clkdm2->dep_bit)); +} + +static int omap4_clkdm_clear_all_wkup_sleep_deps(struct clockdomain *clkdm) +{ + struct clkdm_dep *cd; + u32 mask = 0; + + if (!clkdm->prcm_partition) + return 0; + + for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) { + if (!cd->clkdm) + continue; /* only happens if data is erroneous */ + + mask |= 1 << cd->clkdm->dep_bit; + cd->wkdep_usecount = 0; + } + + omap4_cminst_clear_inst_reg_bits(mask, clkdm->prcm_partition, + clkdm->cm_inst, clkdm->clkdm_offs + + OMAP4_CM_STATICDEP); + return 0; +} + +static int omap4_clkdm_sleep(struct clockdomain *clkdm) +{ + if (clkdm->flags & CLKDM_CAN_HWSUP) + omap4_cminst_clkdm_enable_hwsup(clkdm->prcm_partition, + clkdm->cm_inst, + clkdm->clkdm_offs); + else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP) + omap4_cminst_clkdm_force_sleep(clkdm->prcm_partition, + clkdm->cm_inst, + clkdm->clkdm_offs); + else + return -EINVAL; + + return 0; +} + +static int omap4_clkdm_wakeup(struct clockdomain *clkdm) +{ + omap4_cminst_clkdm_force_wakeup(clkdm->prcm_partition, + clkdm->cm_inst, clkdm->clkdm_offs); + return 0; +} + +static void omap4_clkdm_allow_idle(struct clockdomain *clkdm) +{ + omap4_cminst_clkdm_enable_hwsup(clkdm->prcm_partition, + clkdm->cm_inst, clkdm->clkdm_offs); +} + +static void omap4_clkdm_deny_idle(struct clockdomain *clkdm) +{ + if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + omap4_clkdm_wakeup(clkdm); + else + omap4_cminst_clkdm_disable_hwsup(clkdm->prcm_partition, + clkdm->cm_inst, + clkdm->clkdm_offs); +} + +static int omap4_clkdm_clk_enable(struct clockdomain *clkdm) +{ + if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) + return omap4_clkdm_wakeup(clkdm); + + return 0; +} + +static int omap4_clkdm_clk_disable(struct clockdomain *clkdm) +{ + bool hwsup = false; + + if (!clkdm->prcm_partition) + return 0; + + /* + * The CLKDM_MISSING_IDLE_REPORTING flag documentation has + * more details on the unpleasant problem this is working + * around + */ + if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING && + !(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) { + omap4_clkdm_allow_idle(clkdm); + return 0; + } + + hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition, + clkdm->cm_inst, clkdm->clkdm_offs); + + if (!hwsup && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) + omap4_clkdm_sleep(clkdm); + + return 0; +} + +static u32 omap4_cminst_xlate_clkctrl(u8 part, u16 inst, u16 offset) +{ + return _cm_bases[part].pa + inst + offset; +} + +/** + * omap4_clkdm_save_context - Save the clockdomain modulemode context + * @clkdm: The clockdomain pointer whose context needs to be saved + * + * Save the clockdomain modulemode context. + */ +static int omap4_clkdm_save_context(struct clockdomain *clkdm) +{ + clkdm->context = omap4_cminst_read_inst_reg(clkdm->prcm_partition, + clkdm->cm_inst, + clkdm->clkdm_offs + + OMAP4_CM_CLKSTCTRL); + clkdm->context &= OMAP4430_MODULEMODE_MASK; + return 0; +} + +/** + * omap4_clkdm_restore_context - Restore the clockdomain modulemode context + * @clkdm: The clockdomain pointer whose context needs to be restored + * + * Restore the clockdomain modulemode context. + */ +static int omap4_clkdm_restore_context(struct clockdomain *clkdm) +{ + switch (clkdm->context) { + case OMAP34XX_CLKSTCTRL_DISABLE_AUTO: + omap4_clkdm_deny_idle(clkdm); + break; + case OMAP34XX_CLKSTCTRL_FORCE_SLEEP: + omap4_clkdm_sleep(clkdm); + break; + case OMAP34XX_CLKSTCTRL_FORCE_WAKEUP: + omap4_clkdm_wakeup(clkdm); + break; + case OMAP34XX_CLKSTCTRL_ENABLE_AUTO: + omap4_clkdm_allow_idle(clkdm); + break; + } + return 0; +} + +struct clkdm_ops omap4_clkdm_operations = { + .clkdm_add_wkdep = omap4_clkdm_add_wkup_sleep_dep, + .clkdm_del_wkdep = omap4_clkdm_del_wkup_sleep_dep, + .clkdm_read_wkdep = omap4_clkdm_read_wkup_sleep_dep, + .clkdm_clear_all_wkdeps = omap4_clkdm_clear_all_wkup_sleep_deps, + .clkdm_add_sleepdep = omap4_clkdm_add_wkup_sleep_dep, + .clkdm_del_sleepdep = omap4_clkdm_del_wkup_sleep_dep, + .clkdm_read_sleepdep = omap4_clkdm_read_wkup_sleep_dep, + .clkdm_clear_all_sleepdeps = omap4_clkdm_clear_all_wkup_sleep_deps, + .clkdm_sleep = omap4_clkdm_sleep, + .clkdm_wakeup = omap4_clkdm_wakeup, + .clkdm_allow_idle = omap4_clkdm_allow_idle, + .clkdm_deny_idle = omap4_clkdm_deny_idle, + .clkdm_clk_enable = omap4_clkdm_clk_enable, + .clkdm_clk_disable = omap4_clkdm_clk_disable, + .clkdm_save_context = omap4_clkdm_save_context, + .clkdm_restore_context = omap4_clkdm_restore_context, +}; + +struct clkdm_ops am43xx_clkdm_operations = { + .clkdm_sleep = omap4_clkdm_sleep, + .clkdm_wakeup = omap4_clkdm_wakeup, + .clkdm_allow_idle = omap4_clkdm_allow_idle, + .clkdm_deny_idle = omap4_clkdm_deny_idle, + .clkdm_clk_enable = omap4_clkdm_clk_enable, + .clkdm_clk_disable = omap4_clkdm_clk_disable, +}; + +static const struct cm_ll_data omap4xxx_cm_ll_data = { + .wait_module_ready = &omap4_cminst_wait_module_ready, + .wait_module_idle = &omap4_cminst_wait_module_idle, + .module_enable = &omap4_cminst_module_enable, + .module_disable = &omap4_cminst_module_disable, + .xlate_clkctrl = &omap4_cminst_xlate_clkctrl, +}; + +int __init omap4_cm_init(const struct omap_prcm_init_data *data) +{ + omap_cm_base_init(); + + return cm_register(&omap4xxx_cm_ll_data); +} + +static void __exit omap4_cm_exit(void) +{ + cm_unregister(&omap4xxx_cm_ll_data); +} +__exitcall(omap4_cm_exit); diff --git a/arch/arm/mach-omap2/common-board-devices.h b/arch/arm/mach-omap2/common-board-devices.h new file mode 100644 index 000000000..b23962c38 --- /dev/null +++ b/arch/arm/mach-omap2/common-board-devices.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __OMAP_COMMON_BOARD_DEVICES__ +#define __OMAP_COMMON_BOARD_DEVICES__ + +#include <sound/tlv320aic3x.h> +#include <linux/mfd/menelaus.h> + +void *n8x0_legacy_init(void); + +extern struct menelaus_platform_data n8x0_menelaus_platform_data; +extern struct aic3x_pdata n810_aic33_data; + +#endif /* __OMAP_COMMON_BOARD_DEVICES__ */ diff --git a/arch/arm/mach-omap2/common.c b/arch/arm/mach-omap2/common.c new file mode 100644 index 000000000..a4af202cb --- /dev/null +++ b/arch/arm/mach-omap2/common.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/common.c + * + * Code common to all OMAP2+ machines. + * + * Copyright (C) 2009 Texas Instruments + * Copyright (C) 2010 Nokia Corporation + * Tony Lindgren <tony@atomide.com> + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + */ +#include <linux/kernel.h> +#include <linux/init.h> + +#include "common.h" +#include "omap-secure.h" + +/* + * Stub function for OMAP2 so that common files + * continue to build when custom builds are used + */ +int __weak omap_secure_ram_reserve_memblock(void) +{ + return 0; +} + +void __init omap_reserve(void) +{ + omap_secure_ram_reserve_memblock(); + omap_barrier_reserve_memblock(); +} diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h new file mode 100644 index 000000000..bd5981945 --- /dev/null +++ b/arch/arm/mach-omap2/common.h @@ -0,0 +1,375 @@ +/* + * Header for code common to all OMAP2+ machines. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H +#define __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H +#ifndef __ASSEMBLER__ + +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/mfd/twl.h> +#include <linux/platform_data/i2c-omap.h> +#include <linux/reboot.h> +#include <linux/irqchip/irq-omap-intc.h> + +#include <asm/proc-fns.h> +#include <asm/hardware/cache-l2x0.h> + +#include "i2c.h" +#include "serial.h" + +#include "usb.h" + +#define OMAP_INTC_START NR_IRQS + +extern int (*omap_pm_soc_init)(void); +int omap_pm_nop_init(void); + +#if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP2) +int omap2_pm_init(void); +#else +static inline int omap2_pm_init(void) +{ + return 0; +} +#endif + +#if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP3) +int omap3_pm_init(void); +#else +static inline int omap3_pm_init(void) +{ + return 0; +} +#endif + +#if defined(CONFIG_PM) && (defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || defined(CONFIG_SOC_DRA7XX)) +int omap4_pm_init(void); +int omap4_pm_init_early(void); +#else +static inline int omap4_pm_init(void) +{ + return 0; +} + +static inline int omap4_pm_init_early(void) +{ + return 0; +} +#endif + +#if defined(CONFIG_PM) && (defined(CONFIG_SOC_AM33XX) || \ + defined(CONFIG_SOC_AM43XX)) +int amx3_common_pm_init(void); +#else +static inline int amx3_common_pm_init(void) +{ + return 0; +} +#endif + +extern void omap2_init_common_infrastructure(void); + +extern void omap_init_time(void); +extern void omap3_secure_sync32k_timer_init(void); +extern void omap3_gptimer_timer_init(void); +extern void omap4_local_timer_init(void); +#ifdef CONFIG_CACHE_L2X0 +int omap_l2_cache_init(void); +#define OMAP_L2C_AUX_CTRL (L2C_AUX_CTRL_SHARED_OVERRIDE | \ + L310_AUX_CTRL_DATA_PREFETCH | \ + L310_AUX_CTRL_INSTR_PREFETCH) +void omap4_l2c310_write_sec(unsigned long val, unsigned reg); +#else +static inline int omap_l2_cache_init(void) +{ + return 0; +} + +#define OMAP_L2C_AUX_CTRL 0 +#define omap4_l2c310_write_sec NULL +#endif + +#ifdef CONFIG_SOC_HAS_REALTIME_COUNTER +extern void omap5_realtime_timer_init(void); +#else +static inline void omap5_realtime_timer_init(void) +{ +} +#endif + +void omap2420_init_early(void); +void omap2430_init_early(void); +void omap3430_init_early(void); +void omap35xx_init_early(void); +void omap3630_init_early(void); +void omap3_init_early(void); /* Do not use this one */ +void am33xx_init_early(void); +void am35xx_init_early(void); +void ti814x_init_early(void); +void ti816x_init_early(void); +void am43xx_init_early(void); +void am43xx_init_late(void); +void omap4430_init_early(void); +void omap5_init_early(void); +void omap3_init_late(void); +void omap4430_init_late(void); +void omap2420_init_late(void); +void omap2430_init_late(void); +void ti81xx_init_late(void); +void am33xx_init_late(void); +void omap5_init_late(void); +int omap2_common_pm_late_init(void); +void dra7xx_init_early(void); +void dra7xx_init_late(void); + +#ifdef CONFIG_SOC_BUS +void omap_soc_device_init(void); +#else +static inline void omap_soc_device_init(void) +{ +} +#endif + +#if defined(CONFIG_SOC_OMAP2420) || defined(CONFIG_SOC_OMAP2430) +void omap2xxx_restart(enum reboot_mode mode, const char *cmd); +#else +static inline void omap2xxx_restart(enum reboot_mode mode, const char *cmd) +{ +} +#endif + +#ifdef CONFIG_SOC_AM33XX +void am33xx_restart(enum reboot_mode mode, const char *cmd); +#else +static inline void am33xx_restart(enum reboot_mode mode, const char *cmd) +{ +} +#endif + +#ifdef CONFIG_ARCH_OMAP3 +void omap3xxx_restart(enum reboot_mode mode, const char *cmd); +#else +static inline void omap3xxx_restart(enum reboot_mode mode, const char *cmd) +{ +} +#endif + +#ifdef CONFIG_SOC_TI81XX +void ti81xx_restart(enum reboot_mode mode, const char *cmd); +#else +static inline void ti81xx_restart(enum reboot_mode mode, const char *cmd) +{ +} +#endif + +#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) || \ + defined(CONFIG_SOC_DRA7XX) || defined(CONFIG_SOC_AM43XX) +void omap44xx_restart(enum reboot_mode mode, const char *cmd); +#else +static inline void omap44xx_restart(enum reboot_mode mode, const char *cmd) +{ +} +#endif + +#ifdef CONFIG_OMAP_INTERCONNECT_BARRIER +void omap_barrier_reserve_memblock(void); +void omap_barriers_init(void); +#else +static inline void omap_barrier_reserve_memblock(void) +{ +} +#endif + +/* This gets called from mach-omap2/io.c, do not call this */ +void __init omap2_set_globals_tap(u32 class, void __iomem *tap); + +void __init omap242x_map_io(void); +void __init omap243x_map_io(void); +void __init omap3_map_io(void); +void __init am33xx_map_io(void); +void __init omap4_map_io(void); +void __init omap5_map_io(void); +void __init dra7xx_map_io(void); +void __init ti81xx_map_io(void); + +/** + * omap_test_timeout - busy-loop, testing a condition + * @cond: condition to test until it evaluates to true + * @timeout: maximum number of microseconds in the timeout + * @index: loop index (integer) + * + * Loop waiting for @cond to become true or until at least @timeout + * microseconds have passed. To use, define some integer @index in the + * calling code. After running, if @index == @timeout, then the loop has + * timed out. + */ +#define omap_test_timeout(cond, timeout, index) \ +({ \ + for (index = 0; index < timeout; index++) { \ + if (cond) \ + break; \ + udelay(1); \ + } \ +}) + +extern struct device *omap2_get_mpuss_device(void); +extern struct device *omap2_get_iva_device(void); +extern struct device *omap2_get_l3_device(void); +extern struct device *omap4_get_dsp_device(void); + +void omap_gic_of_init(void); + +#ifdef CONFIG_CACHE_L2X0 +extern void __iomem *omap4_get_l2cache_base(void); +#endif + +struct device_node; + +#ifdef CONFIG_SMP +extern void __iomem *omap4_get_scu_base(void); +#else +static inline void __iomem *omap4_get_scu_base(void) +{ + return NULL; +} +#endif + +extern void gic_dist_disable(void); +extern void gic_dist_enable(void); +extern bool gic_dist_disabled(void); +extern void gic_timer_retrigger(void); +extern void _omap_smc1(u32 fn, u32 arg); +extern void omap4_sar_ram_init(void); +extern void __iomem *omap4_get_sar_ram_base(void); +extern void omap4_mpuss_early_init(void); +extern void omap_do_wfi(void); +extern void omap_interconnect_sync(void); + +#ifdef CONFIG_SMP +/* Needed for secondary core boot */ +extern u32 omap_modify_auxcoreboot0(u32 set_mask, u32 clear_mask); +extern void omap_auxcoreboot_addr(u32 cpu_addr); +extern u32 omap_read_auxcoreboot0(void); + +extern void omap4_cpu_die(unsigned int cpu); +extern int omap4_cpu_kill(unsigned int cpu); + +extern const struct smp_operations omap4_smp_ops; +#endif + +extern u32 omap4_get_cpu1_ns_pa_addr(void); + +#if defined(CONFIG_SMP) && defined(CONFIG_PM) +extern int omap4_mpuss_init(void); +extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state); +extern int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state); +#else +static inline int omap4_enter_lowpower(unsigned int cpu, + unsigned int power_state) +{ + cpu_do_idle(); + return 0; +} + +static inline int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state) +{ + cpu_do_idle(); + return 0; +} + +static inline int omap4_mpuss_init(void) +{ + return 0; +} + +#endif + +#ifdef CONFIG_ARCH_OMAP4 +void omap4_secondary_startup(void); +void omap4460_secondary_startup(void); +int omap4_finish_suspend(unsigned long cpu_state); +void omap4_cpu_resume(void); +#else +static inline void omap4_secondary_startup(void) +{ +} + +static inline void omap4460_secondary_startup(void) +{ +} +static inline int omap4_finish_suspend(unsigned long cpu_state) +{ + return 0; +} +static inline void omap4_cpu_resume(void) +{ +} +#endif + +#if defined(CONFIG_SOC_OMAP5) || defined(CONFIG_SOC_DRA7XX) +void omap5_secondary_startup(void); +void omap5_secondary_hyp_startup(void); +#else +static inline void omap5_secondary_startup(void) +{ +} + +static inline void omap5_secondary_hyp_startup(void) +{ +} +#endif + +struct omap_system_dma_plat_info; + +void pdata_quirks_init(const struct of_device_id *); +void omap_auxdata_legacy_init(struct device *dev); +void omap_pcs_legacy_init(int irq, void (*rearm)(void)); +extern struct omap_system_dma_plat_info dma_plat_info; + +struct omap_sdrc_params; +extern void omap_sdrc_init(struct omap_sdrc_params *sdrc_cs0, + struct omap_sdrc_params *sdrc_cs1); +extern void omap_reserve(void); + +struct omap_hwmod; +extern int omap_dss_reset(struct omap_hwmod *); + +/* SoC specific clock initializer */ +int omap_clk_init(void); + +#if IS_ENABLED(CONFIG_OMAP_IOMMU) +int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, + u8 *pwrst); +#else +static inline int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, + bool request, u8 *pwrst) +{ + return 0; +} +#endif + +#endif /* __ASSEMBLER__ */ +#endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */ diff --git a/arch/arm/mach-omap2/control.c b/arch/arm/mach-omap2/control.c new file mode 100644 index 000000000..c514a9602 --- /dev/null +++ b/arch/arm/mach-omap2/control.c @@ -0,0 +1,860 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2/3 System Control Module register access + * + * Copyright (C) 2007, 2012 Texas Instruments, Inc. + * Copyright (C) 2007 Nokia Corporation + * + * Written by Paul Walmsley + */ +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/cpu_pm.h> + +#include "soc.h" +#include "iomap.h" +#include "common.h" +#include "cm-regbits-34xx.h" +#include "prm-regbits-34xx.h" +#include "prm3xxx.h" +#include "cm3xxx.h" +#include "sdrc.h" +#include "pm.h" +#include "control.h" +#include "clock.h" + +/* Used by omap3_ctrl_save_padconf() */ +#define START_PADCONF_SAVE 0x2 +#define PADCONF_SAVE_DONE 0x1 + +static void __iomem *omap2_ctrl_base; +static s16 omap2_ctrl_offset; + +#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) +struct omap3_scratchpad { + u32 boot_config_ptr; + u32 public_restore_ptr; + u32 secure_ram_restore_ptr; + u32 sdrc_module_semaphore; + u32 prcm_block_offset; + u32 sdrc_block_offset; +}; + +struct omap3_scratchpad_prcm_block { + u32 prm_contents[2]; + u32 cm_contents[11]; + u32 prcm_block_size; +}; + +struct omap3_scratchpad_sdrc_block { + u16 sysconfig; + u16 cs_cfg; + u16 sharing; + u16 err_type; + u32 dll_a_ctrl; + u32 dll_b_ctrl; + u32 power; + u32 cs_0; + u32 mcfg_0; + u16 mr_0; + u16 emr_1_0; + u16 emr_2_0; + u16 emr_3_0; + u32 actim_ctrla_0; + u32 actim_ctrlb_0; + u32 rfr_ctrl_0; + u32 cs_1; + u32 mcfg_1; + u16 mr_1; + u16 emr_1_1; + u16 emr_2_1; + u16 emr_3_1; + u32 actim_ctrla_1; + u32 actim_ctrlb_1; + u32 rfr_ctrl_1; + u16 dcdl_1_ctrl; + u16 dcdl_2_ctrl; + u32 flags; + u32 block_size; +}; + +void *omap3_secure_ram_storage; + +/* + * This is used to store ARM registers in SDRAM before attempting + * an MPU OFF. The save and restore happens from the SRAM sleep code. + * The address is stored in scratchpad, so that it can be used + * during the restore path. + */ +u32 omap3_arm_context[128]; + +struct omap3_control_regs { + u32 sysconfig; + u32 devconf0; + u32 mem_dftrw0; + u32 mem_dftrw1; + u32 msuspendmux_0; + u32 msuspendmux_1; + u32 msuspendmux_2; + u32 msuspendmux_3; + u32 msuspendmux_4; + u32 msuspendmux_5; + u32 sec_ctrl; + u32 devconf1; + u32 csirxfe; + u32 iva2_bootaddr; + u32 iva2_bootmod; + u32 wkup_ctrl; + u32 debobs_0; + u32 debobs_1; + u32 debobs_2; + u32 debobs_3; + u32 debobs_4; + u32 debobs_5; + u32 debobs_6; + u32 debobs_7; + u32 debobs_8; + u32 prog_io0; + u32 prog_io1; + u32 dss_dpll_spreading; + u32 core_dpll_spreading; + u32 per_dpll_spreading; + u32 usbhost_dpll_spreading; + u32 pbias_lite; + u32 temp_sensor; + u32 sramldo4; + u32 sramldo5; + u32 csi; + u32 padconf_sys_nirq; +}; + +static struct omap3_control_regs control_context; +#endif /* CONFIG_ARCH_OMAP3 && CONFIG_PM */ + +u8 omap_ctrl_readb(u16 offset) +{ + u32 val; + u8 byte_offset = offset & 0x3; + + val = omap_ctrl_readl(offset); + + return (val >> (byte_offset * 8)) & 0xff; +} + +u16 omap_ctrl_readw(u16 offset) +{ + u32 val; + u16 byte_offset = offset & 0x2; + + val = omap_ctrl_readl(offset); + + return (val >> (byte_offset * 8)) & 0xffff; +} + +u32 omap_ctrl_readl(u16 offset) +{ + offset &= 0xfffc; + + return readl_relaxed(omap2_ctrl_base + offset); +} + +void omap_ctrl_writeb(u8 val, u16 offset) +{ + u32 tmp; + u8 byte_offset = offset & 0x3; + + tmp = omap_ctrl_readl(offset); + + tmp &= 0xffffffff ^ (0xff << (byte_offset * 8)); + tmp |= val << (byte_offset * 8); + + omap_ctrl_writel(tmp, offset); +} + +void omap_ctrl_writew(u16 val, u16 offset) +{ + u32 tmp; + u8 byte_offset = offset & 0x2; + + tmp = omap_ctrl_readl(offset); + + tmp &= 0xffffffff ^ (0xffff << (byte_offset * 8)); + tmp |= val << (byte_offset * 8); + + omap_ctrl_writel(tmp, offset); +} + +void omap_ctrl_writel(u32 val, u16 offset) +{ + offset &= 0xfffc; + writel_relaxed(val, omap2_ctrl_base + offset); +} + +#ifdef CONFIG_ARCH_OMAP3 + +/** + * omap3_ctrl_write_boot_mode - set scratchpad boot mode for the next boot + * @bootmode: 8-bit value to pass to some boot code + * + * Set the bootmode in the scratchpad RAM. This is used after the + * system restarts. Not sure what actually uses this - it may be the + * bootloader, rather than the boot ROM - contrary to the preserved + * comment below. No return value. + */ +void omap3_ctrl_write_boot_mode(u8 bootmode) +{ + u32 l; + + l = ('B' << 24) | ('M' << 16) | bootmode; + + /* + * Reserve the first word in scratchpad for communicating + * with the boot ROM. A pointer to a data structure + * describing the boot process can be stored there, + * cf. OMAP34xx TRM, Initialization / Software Booting + * Configuration. + * + * XXX This should use some omap_ctrl_writel()-type function + */ + writel_relaxed(l, OMAP2_L4_IO_ADDRESS(OMAP343X_SCRATCHPAD + 4)); +} + +#endif + +/** + * omap_ctrl_write_dsp_boot_addr - set boot address for a remote processor + * @bootaddr: physical address of the boot loader + * + * Set boot address for the boot loader of a supported processor + * when a power ON sequence occurs. + */ +void omap_ctrl_write_dsp_boot_addr(u32 bootaddr) +{ + u32 offset = cpu_is_omap243x() ? OMAP243X_CONTROL_IVA2_BOOTADDR : + cpu_is_omap34xx() ? OMAP343X_CONTROL_IVA2_BOOTADDR : + cpu_is_omap44xx() ? OMAP4_CTRL_MODULE_CORE_DSP_BOOTADDR : + soc_is_omap54xx() ? OMAP4_CTRL_MODULE_CORE_DSP_BOOTADDR : + 0; + + if (!offset) { + pr_err("%s: unsupported omap type\n", __func__); + return; + } + + omap_ctrl_writel(bootaddr, offset); +} + +/** + * omap_ctrl_write_dsp_boot_mode - set boot mode for a remote processor + * @bootmode: 8-bit value to pass to some boot code + * + * Sets boot mode for the boot loader of a supported processor + * when a power ON sequence occurs. + */ +void omap_ctrl_write_dsp_boot_mode(u8 bootmode) +{ + u32 offset = cpu_is_omap243x() ? OMAP243X_CONTROL_IVA2_BOOTMOD : + cpu_is_omap34xx() ? OMAP343X_CONTROL_IVA2_BOOTMOD : + 0; + + if (!offset) { + pr_err("%s: unsupported omap type\n", __func__); + return; + } + + omap_ctrl_writel(bootmode, offset); +} + +#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) +/* + * Clears the scratchpad contents in case of cold boot- + * called during bootup + */ +void omap3_clear_scratchpad_contents(void) +{ + u32 max_offset = OMAP343X_SCRATCHPAD_ROM_OFFSET; + void __iomem *v_addr; + u32 offset = 0; + + v_addr = OMAP2_L4_IO_ADDRESS(OMAP343X_SCRATCHPAD_ROM); + if (omap3xxx_prm_clear_global_cold_reset()) { + for ( ; offset <= max_offset; offset += 0x4) + writel_relaxed(0x0, (v_addr + offset)); + } +} + +/* Populate the scratchpad structure with restore structure */ +void omap3_save_scratchpad_contents(void) +{ + void __iomem *scratchpad_address; + u32 arm_context_addr; + struct omap3_scratchpad scratchpad_contents; + struct omap3_scratchpad_prcm_block prcm_block_contents; + struct omap3_scratchpad_sdrc_block sdrc_block_contents; + + /* + * Populate the Scratchpad contents + * + * The "get_*restore_pointer" functions are used to provide a + * physical restore address where the ROM code jumps while waking + * up from MPU OFF/OSWR state. + * The restore pointer is stored into the scratchpad. + */ + scratchpad_contents.boot_config_ptr = 0x0; + if (cpu_is_omap3630()) + scratchpad_contents.public_restore_ptr = + __pa_symbol(omap3_restore_3630); + else if (omap_rev() != OMAP3430_REV_ES3_0 && + omap_rev() != OMAP3430_REV_ES3_1 && + omap_rev() != OMAP3430_REV_ES3_1_2) + scratchpad_contents.public_restore_ptr = + __pa_symbol(omap3_restore); + else + scratchpad_contents.public_restore_ptr = + __pa_symbol(omap3_restore_es3); + + if (omap_type() == OMAP2_DEVICE_TYPE_GP) + scratchpad_contents.secure_ram_restore_ptr = 0x0; + else + scratchpad_contents.secure_ram_restore_ptr = + (u32) __pa(omap3_secure_ram_storage); + scratchpad_contents.sdrc_module_semaphore = 0x0; + scratchpad_contents.prcm_block_offset = 0x2C; + scratchpad_contents.sdrc_block_offset = 0x64; + + /* Populate the PRCM block contents */ + omap3_prm_save_scratchpad_contents(prcm_block_contents.prm_contents); + omap3_cm_save_scratchpad_contents(prcm_block_contents.cm_contents); + + prcm_block_contents.prcm_block_size = 0x0; + + /* Populate the SDRC block contents */ + sdrc_block_contents.sysconfig = + (sdrc_read_reg(SDRC_SYSCONFIG) & 0xFFFF); + sdrc_block_contents.cs_cfg = + (sdrc_read_reg(SDRC_CS_CFG) & 0xFFFF); + sdrc_block_contents.sharing = + (sdrc_read_reg(SDRC_SHARING) & 0xFFFF); + sdrc_block_contents.err_type = + (sdrc_read_reg(SDRC_ERR_TYPE) & 0xFFFF); + sdrc_block_contents.dll_a_ctrl = sdrc_read_reg(SDRC_DLLA_CTRL); + sdrc_block_contents.dll_b_ctrl = 0x0; + /* + * Due to a OMAP3 errata (1.142), on EMU/HS devices SRDC should + * be programed to issue automatic self refresh on timeout + * of AUTO_CNT = 1 prior to any transition to OFF mode. + */ + if ((omap_type() != OMAP2_DEVICE_TYPE_GP) + && (omap_rev() >= OMAP3430_REV_ES3_0)) + sdrc_block_contents.power = (sdrc_read_reg(SDRC_POWER) & + ~(SDRC_POWER_AUTOCOUNT_MASK| + SDRC_POWER_CLKCTRL_MASK)) | + (1 << SDRC_POWER_AUTOCOUNT_SHIFT) | + SDRC_SELF_REFRESH_ON_AUTOCOUNT; + else + sdrc_block_contents.power = sdrc_read_reg(SDRC_POWER); + + sdrc_block_contents.cs_0 = 0x0; + sdrc_block_contents.mcfg_0 = sdrc_read_reg(SDRC_MCFG_0); + sdrc_block_contents.mr_0 = (sdrc_read_reg(SDRC_MR_0) & 0xFFFF); + sdrc_block_contents.emr_1_0 = 0x0; + sdrc_block_contents.emr_2_0 = 0x0; + sdrc_block_contents.emr_3_0 = 0x0; + sdrc_block_contents.actim_ctrla_0 = + sdrc_read_reg(SDRC_ACTIM_CTRL_A_0); + sdrc_block_contents.actim_ctrlb_0 = + sdrc_read_reg(SDRC_ACTIM_CTRL_B_0); + sdrc_block_contents.rfr_ctrl_0 = + sdrc_read_reg(SDRC_RFR_CTRL_0); + sdrc_block_contents.cs_1 = 0x0; + sdrc_block_contents.mcfg_1 = sdrc_read_reg(SDRC_MCFG_1); + sdrc_block_contents.mr_1 = sdrc_read_reg(SDRC_MR_1) & 0xFFFF; + sdrc_block_contents.emr_1_1 = 0x0; + sdrc_block_contents.emr_2_1 = 0x0; + sdrc_block_contents.emr_3_1 = 0x0; + sdrc_block_contents.actim_ctrla_1 = + sdrc_read_reg(SDRC_ACTIM_CTRL_A_1); + sdrc_block_contents.actim_ctrlb_1 = + sdrc_read_reg(SDRC_ACTIM_CTRL_B_1); + sdrc_block_contents.rfr_ctrl_1 = + sdrc_read_reg(SDRC_RFR_CTRL_1); + sdrc_block_contents.dcdl_1_ctrl = 0x0; + sdrc_block_contents.dcdl_2_ctrl = 0x0; + sdrc_block_contents.flags = 0x0; + sdrc_block_contents.block_size = 0x0; + + arm_context_addr = __pa_symbol(omap3_arm_context); + + /* Copy all the contents to the scratchpad location */ + scratchpad_address = OMAP2_L4_IO_ADDRESS(OMAP343X_SCRATCHPAD); + memcpy_toio(scratchpad_address, &scratchpad_contents, + sizeof(scratchpad_contents)); + /* Scratchpad contents being 32 bits, a divide by 4 done here */ + memcpy_toio(scratchpad_address + + scratchpad_contents.prcm_block_offset, + &prcm_block_contents, sizeof(prcm_block_contents)); + memcpy_toio(scratchpad_address + + scratchpad_contents.sdrc_block_offset, + &sdrc_block_contents, sizeof(sdrc_block_contents)); + /* + * Copies the address of the location in SDRAM where ARM + * registers get saved during a MPU OFF transition. + */ + memcpy_toio(scratchpad_address + + scratchpad_contents.sdrc_block_offset + + sizeof(sdrc_block_contents), &arm_context_addr, 4); +} + +void omap3_control_save_context(void) +{ + control_context.sysconfig = omap_ctrl_readl(OMAP2_CONTROL_SYSCONFIG); + control_context.devconf0 = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); + control_context.mem_dftrw0 = + omap_ctrl_readl(OMAP343X_CONTROL_MEM_DFTRW0); + control_context.mem_dftrw1 = + omap_ctrl_readl(OMAP343X_CONTROL_MEM_DFTRW1); + control_context.msuspendmux_0 = + omap_ctrl_readl(OMAP2_CONTROL_MSUSPENDMUX_0); + control_context.msuspendmux_1 = + omap_ctrl_readl(OMAP2_CONTROL_MSUSPENDMUX_1); + control_context.msuspendmux_2 = + omap_ctrl_readl(OMAP2_CONTROL_MSUSPENDMUX_2); + control_context.msuspendmux_3 = + omap_ctrl_readl(OMAP2_CONTROL_MSUSPENDMUX_3); + control_context.msuspendmux_4 = + omap_ctrl_readl(OMAP2_CONTROL_MSUSPENDMUX_4); + control_context.msuspendmux_5 = + omap_ctrl_readl(OMAP2_CONTROL_MSUSPENDMUX_5); + control_context.sec_ctrl = omap_ctrl_readl(OMAP2_CONTROL_SEC_CTRL); + control_context.devconf1 = omap_ctrl_readl(OMAP343X_CONTROL_DEVCONF1); + control_context.csirxfe = omap_ctrl_readl(OMAP343X_CONTROL_CSIRXFE); + control_context.iva2_bootaddr = + omap_ctrl_readl(OMAP343X_CONTROL_IVA2_BOOTADDR); + control_context.iva2_bootmod = + omap_ctrl_readl(OMAP343X_CONTROL_IVA2_BOOTMOD); + control_context.wkup_ctrl = omap_ctrl_readl(OMAP34XX_CONTROL_WKUP_CTRL); + control_context.debobs_0 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(0)); + control_context.debobs_1 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(1)); + control_context.debobs_2 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(2)); + control_context.debobs_3 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(3)); + control_context.debobs_4 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(4)); + control_context.debobs_5 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(5)); + control_context.debobs_6 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(6)); + control_context.debobs_7 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(7)); + control_context.debobs_8 = omap_ctrl_readl(OMAP343X_CONTROL_DEBOBS(8)); + control_context.prog_io0 = omap_ctrl_readl(OMAP343X_CONTROL_PROG_IO0); + control_context.prog_io1 = omap_ctrl_readl(OMAP343X_CONTROL_PROG_IO1); + control_context.dss_dpll_spreading = + omap_ctrl_readl(OMAP343X_CONTROL_DSS_DPLL_SPREADING); + control_context.core_dpll_spreading = + omap_ctrl_readl(OMAP343X_CONTROL_CORE_DPLL_SPREADING); + control_context.per_dpll_spreading = + omap_ctrl_readl(OMAP343X_CONTROL_PER_DPLL_SPREADING); + control_context.usbhost_dpll_spreading = + omap_ctrl_readl(OMAP343X_CONTROL_USBHOST_DPLL_SPREADING); + control_context.pbias_lite = + omap_ctrl_readl(OMAP343X_CONTROL_PBIAS_LITE); + control_context.temp_sensor = + omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR); + control_context.sramldo4 = omap_ctrl_readl(OMAP343X_CONTROL_SRAMLDO4); + control_context.sramldo5 = omap_ctrl_readl(OMAP343X_CONTROL_SRAMLDO5); + control_context.csi = omap_ctrl_readl(OMAP343X_CONTROL_CSI); + control_context.padconf_sys_nirq = + omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_SYSNIRQ); +} + +void omap3_control_restore_context(void) +{ + omap_ctrl_writel(control_context.sysconfig, OMAP2_CONTROL_SYSCONFIG); + omap_ctrl_writel(control_context.devconf0, OMAP2_CONTROL_DEVCONF0); + omap_ctrl_writel(control_context.mem_dftrw0, + OMAP343X_CONTROL_MEM_DFTRW0); + omap_ctrl_writel(control_context.mem_dftrw1, + OMAP343X_CONTROL_MEM_DFTRW1); + omap_ctrl_writel(control_context.msuspendmux_0, + OMAP2_CONTROL_MSUSPENDMUX_0); + omap_ctrl_writel(control_context.msuspendmux_1, + OMAP2_CONTROL_MSUSPENDMUX_1); + omap_ctrl_writel(control_context.msuspendmux_2, + OMAP2_CONTROL_MSUSPENDMUX_2); + omap_ctrl_writel(control_context.msuspendmux_3, + OMAP2_CONTROL_MSUSPENDMUX_3); + omap_ctrl_writel(control_context.msuspendmux_4, + OMAP2_CONTROL_MSUSPENDMUX_4); + omap_ctrl_writel(control_context.msuspendmux_5, + OMAP2_CONTROL_MSUSPENDMUX_5); + omap_ctrl_writel(control_context.sec_ctrl, OMAP2_CONTROL_SEC_CTRL); + omap_ctrl_writel(control_context.devconf1, OMAP343X_CONTROL_DEVCONF1); + omap_ctrl_writel(control_context.csirxfe, OMAP343X_CONTROL_CSIRXFE); + omap_ctrl_writel(control_context.iva2_bootaddr, + OMAP343X_CONTROL_IVA2_BOOTADDR); + omap_ctrl_writel(control_context.iva2_bootmod, + OMAP343X_CONTROL_IVA2_BOOTMOD); + omap_ctrl_writel(control_context.wkup_ctrl, OMAP34XX_CONTROL_WKUP_CTRL); + omap_ctrl_writel(control_context.debobs_0, OMAP343X_CONTROL_DEBOBS(0)); + omap_ctrl_writel(control_context.debobs_1, OMAP343X_CONTROL_DEBOBS(1)); + omap_ctrl_writel(control_context.debobs_2, OMAP343X_CONTROL_DEBOBS(2)); + omap_ctrl_writel(control_context.debobs_3, OMAP343X_CONTROL_DEBOBS(3)); + omap_ctrl_writel(control_context.debobs_4, OMAP343X_CONTROL_DEBOBS(4)); + omap_ctrl_writel(control_context.debobs_5, OMAP343X_CONTROL_DEBOBS(5)); + omap_ctrl_writel(control_context.debobs_6, OMAP343X_CONTROL_DEBOBS(6)); + omap_ctrl_writel(control_context.debobs_7, OMAP343X_CONTROL_DEBOBS(7)); + omap_ctrl_writel(control_context.debobs_8, OMAP343X_CONTROL_DEBOBS(8)); + omap_ctrl_writel(control_context.prog_io0, OMAP343X_CONTROL_PROG_IO0); + omap_ctrl_writel(control_context.prog_io1, OMAP343X_CONTROL_PROG_IO1); + omap_ctrl_writel(control_context.dss_dpll_spreading, + OMAP343X_CONTROL_DSS_DPLL_SPREADING); + omap_ctrl_writel(control_context.core_dpll_spreading, + OMAP343X_CONTROL_CORE_DPLL_SPREADING); + omap_ctrl_writel(control_context.per_dpll_spreading, + OMAP343X_CONTROL_PER_DPLL_SPREADING); + omap_ctrl_writel(control_context.usbhost_dpll_spreading, + OMAP343X_CONTROL_USBHOST_DPLL_SPREADING); + omap_ctrl_writel(control_context.pbias_lite, + OMAP343X_CONTROL_PBIAS_LITE); + omap_ctrl_writel(control_context.temp_sensor, + OMAP343X_CONTROL_TEMP_SENSOR); + omap_ctrl_writel(control_context.sramldo4, OMAP343X_CONTROL_SRAMLDO4); + omap_ctrl_writel(control_context.sramldo5, OMAP343X_CONTROL_SRAMLDO5); + omap_ctrl_writel(control_context.csi, OMAP343X_CONTROL_CSI); + omap_ctrl_writel(control_context.padconf_sys_nirq, + OMAP343X_CONTROL_PADCONF_SYSNIRQ); +} + +void omap3630_ctrl_disable_rta(void) +{ + if (!cpu_is_omap3630()) + return; + omap_ctrl_writel(OMAP36XX_RTA_DISABLE, OMAP36XX_CONTROL_MEM_RTA_CTRL); +} + +/** + * omap3_ctrl_save_padconf - save padconf registers to scratchpad RAM + * + * Tell the SCM to start saving the padconf registers, then wait for + * the process to complete. Returns 0 unconditionally, although it + * should also eventually be able to return -ETIMEDOUT, if the save + * does not complete. + * + * XXX This function is missing a timeout. What should it be? + */ +int omap3_ctrl_save_padconf(void) +{ + u32 cpo; + + /* Save the padconf registers */ + cpo = omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF); + cpo |= START_PADCONF_SAVE; + omap_ctrl_writel(cpo, OMAP343X_CONTROL_PADCONF_OFF); + + /* wait for the save to complete */ + while (!(omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS) + & PADCONF_SAVE_DONE)) + udelay(1); + + return 0; +} + +/** + * omap3_ctrl_set_iva_bootmode_idle - sets the IVA2 bootmode to idle + * + * Sets the bootmode for IVA2 to idle. This is needed by the PM code to + * force disable IVA2 so that it does not prevent any low-power states. + */ +static void __init omap3_ctrl_set_iva_bootmode_idle(void) +{ + omap_ctrl_writel(OMAP3_IVA2_BOOTMOD_IDLE, + OMAP343X_CONTROL_IVA2_BOOTMOD); +} + +/** + * omap3_ctrl_setup_d2d_padconf - setup stacked modem pads for idle + * + * Sets up the pads controlling the stacked modem in such way that the + * device can enter idle. + */ +static void __init omap3_ctrl_setup_d2d_padconf(void) +{ + u16 mask, padconf; + + /* + * In a stand alone OMAP3430 where there is not a stacked + * modem for the D2D Idle Ack and D2D MStandby must be pulled + * high. S CONTROL_PADCONF_SAD2D_IDLEACK and + * CONTROL_PADCONF_SAD2D_MSTDBY to have a pull up. + */ + mask = (1 << 4) | (1 << 3); /* pull-up, enabled */ + padconf = omap_ctrl_readw(OMAP3_PADCONF_SAD2D_MSTANDBY); + padconf |= mask; + omap_ctrl_writew(padconf, OMAP3_PADCONF_SAD2D_MSTANDBY); + + padconf = omap_ctrl_readw(OMAP3_PADCONF_SAD2D_IDLEACK); + padconf |= mask; + omap_ctrl_writew(padconf, OMAP3_PADCONF_SAD2D_IDLEACK); +} + +/** + * omap3_ctrl_init - does static initializations for control module + * + * Initializes system control module. This sets up the sysconfig autoidle, + * and sets up modem and iva2 so that they can be idled properly. + */ +void __init omap3_ctrl_init(void) +{ + omap_ctrl_writel(OMAP3430_AUTOIDLE_MASK, OMAP2_CONTROL_SYSCONFIG); + + omap3_ctrl_set_iva_bootmode_idle(); + + omap3_ctrl_setup_d2d_padconf(); +} +#endif /* CONFIG_ARCH_OMAP3 && CONFIG_PM */ + +static unsigned long am43xx_control_reg_offsets[] = { + AM33XX_CONTROL_SYSCONFIG_OFFSET, + AM33XX_CONTROL_STATUS_OFFSET, + AM43XX_CONTROL_MPU_L2_CTRL_OFFSET, + AM33XX_CONTROL_CORE_SLDO_CTRL_OFFSET, + AM33XX_CONTROL_MPU_SLDO_CTRL_OFFSET, + AM33XX_CONTROL_CLK32KDIVRATIO_CTRL_OFFSET, + AM33XX_CONTROL_BANDGAP_CTRL_OFFSET, + AM33XX_CONTROL_BANDGAP_TRIM_OFFSET, + AM33XX_CONTROL_PLL_CLKINPULOW_CTRL_OFFSET, + AM33XX_CONTROL_MOSC_CTRL_OFFSET, + AM33XX_CONTROL_DEEPSLEEP_CTRL_OFFSET, + AM43XX_CONTROL_DISPLAY_PLL_SEL_OFFSET, + AM33XX_CONTROL_INIT_PRIORITY_0_OFFSET, + AM33XX_CONTROL_INIT_PRIORITY_1_OFFSET, + AM33XX_CONTROL_TPTC_CFG_OFFSET, + AM33XX_CONTROL_USB_CTRL0_OFFSET, + AM33XX_CONTROL_USB_CTRL1_OFFSET, + AM43XX_CONTROL_USB_CTRL2_OFFSET, + AM43XX_CONTROL_GMII_SEL_OFFSET, + AM43XX_CONTROL_MPUSS_CTRL_OFFSET, + AM43XX_CONTROL_TIMER_CASCADE_CTRL_OFFSET, + AM43XX_CONTROL_PWMSS_CTRL_OFFSET, + AM33XX_CONTROL_MREQPRIO_0_OFFSET, + AM33XX_CONTROL_MREQPRIO_1_OFFSET, + AM33XX_CONTROL_HW_EVENT_SEL_GRP1_OFFSET, + AM33XX_CONTROL_HW_EVENT_SEL_GRP2_OFFSET, + AM33XX_CONTROL_HW_EVENT_SEL_GRP3_OFFSET, + AM33XX_CONTROL_HW_EVENT_SEL_GRP4_OFFSET, + AM33XX_CONTROL_SMRT_CTRL_OFFSET, + AM33XX_CONTROL_MPUSS_HW_DEBUG_SEL_OFFSET, + AM43XX_CONTROL_CQDETECT_STS_OFFSET, + AM43XX_CONTROL_CQDETECT_STS2_OFFSET, + AM43XX_CONTROL_VTP_CTRL_OFFSET, + AM33XX_CONTROL_VREF_CTRL_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_0_3_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_4_7_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_8_11_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_12_15_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_16_19_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_20_23_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_24_27_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_28_31_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_32_35_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_36_39_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_40_43_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_44_47_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_48_51_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_52_55_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_56_59_OFFSET, + AM33XX_CONTROL_TPCC_EVT_MUX_60_63_OFFSET, + AM33XX_CONTROL_TIMER_EVT_CAPT_OFFSET, + AM33XX_CONTROL_ECAP_EVT_CAPT_OFFSET, + AM33XX_CONTROL_ADC_EVT_CAPT_OFFSET, + AM43XX_CONTROL_ADC1_EVT_CAPT_OFFSET, + AM33XX_CONTROL_RESET_ISO_OFFSET, +}; + +static u32 am33xx_control_vals[ARRAY_SIZE(am43xx_control_reg_offsets)]; + +/** + * am43xx_control_save_context - Save the wakeup domain registers + * + * Save the wkup domain registers + */ +static void am43xx_control_save_context(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(am43xx_control_reg_offsets); i++) + am33xx_control_vals[i] = + omap_ctrl_readl(am43xx_control_reg_offsets[i]); +} + +/** + * am43xx_control_restore_context - Restore the wakeup domain registers + * + * Restore the wkup domain registers + */ +static void am43xx_control_restore_context(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(am43xx_control_reg_offsets); i++) + omap_ctrl_writel(am33xx_control_vals[i], + am43xx_control_reg_offsets[i]); +} + +static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + if (enable_off_mode) + am43xx_control_save_context(); + break; + case CPU_CLUSTER_PM_EXIT: + if (enable_off_mode) + am43xx_control_restore_context(); + break; + } + + return NOTIFY_OK; +} + +struct control_init_data { + int index; + void __iomem *mem; + s16 offset; +}; + +static struct control_init_data ctrl_data = { + .index = TI_CLKM_CTRL, +}; + +static const struct control_init_data omap2_ctrl_data = { + .index = TI_CLKM_CTRL, + .offset = -OMAP2_CONTROL_GENERAL, +}; + +static const struct control_init_data ctrl_aux_data = { + .index = TI_CLKM_CTRL_AUX, +}; + +static const struct of_device_id omap_scrm_dt_match_table[] = { + { .compatible = "ti,am3-scm", .data = &ctrl_data }, + { .compatible = "ti,am4-scm", .data = &ctrl_data }, + { .compatible = "ti,omap2-scm", .data = &omap2_ctrl_data }, + { .compatible = "ti,omap3-scm", .data = &omap2_ctrl_data }, + { .compatible = "ti,dm814-scm", .data = &ctrl_data }, + { .compatible = "ti,dm816-scrm", .data = &ctrl_data }, + { .compatible = "ti,omap4-scm-core", .data = &ctrl_data }, + { .compatible = "ti,omap5-scm-core", .data = &ctrl_data }, + { .compatible = "ti,omap5-scm-wkup-pad-conf", .data = &ctrl_aux_data }, + { .compatible = "ti,dra7-scm-core", .data = &ctrl_data }, + { } +}; + +/** + * omap2_control_base_init - initialize iomappings for the control driver + * + * Detects and initializes the iomappings for the control driver, based + * on the DT data. Returns 0 in success, negative error value + * otherwise. + */ +int __init omap2_control_base_init(void) +{ + struct device_node *np; + const struct of_device_id *match; + struct control_init_data *data; + void __iomem *mem; + + for_each_matching_node_and_match(np, omap_scrm_dt_match_table, &match) { + data = (struct control_init_data *)match->data; + + mem = of_iomap(np, 0); + if (!mem) { + of_node_put(np); + return -ENOMEM; + } + + if (data->index == TI_CLKM_CTRL) { + omap2_ctrl_base = mem; + omap2_ctrl_offset = data->offset; + } + + data->mem = mem; + } + + return 0; +} + +/** + * omap_control_init - low level init for the control driver + * + * Initializes the low level clock infrastructure for control driver. + * Returns 0 in success, negative error value in failure. + */ +int __init omap_control_init(void) +{ + struct device_node *np, *scm_conf; + const struct of_device_id *match; + const struct omap_prcm_init_data *data; + int ret; + struct regmap *syscon; + static struct notifier_block nb; + + for_each_matching_node_and_match(np, omap_scrm_dt_match_table, &match) { + data = match->data; + + /* + * Check if we have scm_conf node, if yes, use this to + * access clock registers. + */ + scm_conf = of_get_child_by_name(np, "scm_conf"); + + if (scm_conf) { + syscon = syscon_node_to_regmap(scm_conf); + + if (IS_ERR(syscon)) { + ret = PTR_ERR(syscon); + goto of_node_put; + } + + if (of_get_child_by_name(scm_conf, "clocks")) { + ret = omap2_clk_provider_init(scm_conf, + data->index, + syscon, NULL); + if (ret) + goto of_node_put; + } + } else { + /* No scm_conf found, direct access */ + ret = omap2_clk_provider_init(np, data->index, NULL, + data->mem); + if (ret) + goto of_node_put; + } + } + + /* Only AM43XX can lose ctrl registers context during rtc-ddr suspend */ + if (soc_is_am43xx()) { + nb.notifier_call = cpu_notifier; + cpu_pm_register_notifier(&nb); + } + + return 0; + +of_node_put: + of_node_put(np); + return ret; + +} + +/** + * omap3_control_legacy_iomap_init - legacy iomap init for clock providers + * + * Legacy iomap init for clock provider. Needed only by legacy boot mode, + * where the base addresses are not parsed from DT, but still required + * by the clock driver to be setup properly. + */ +void __init omap3_control_legacy_iomap_init(void) +{ + omap2_clk_legacy_provider_init(TI_CLKM_SCRM, omap2_ctrl_base); +} diff --git a/arch/arm/mach-omap2/control.h b/arch/arm/mach-omap2/control.h new file mode 100644 index 000000000..c4ca30ba1 --- /dev/null +++ b/arch/arm/mach-omap2/control.h @@ -0,0 +1,545 @@ +/* + * arch/arm/mach-omap2/control.h + * + * OMAP2/3/4 System Control Module definitions + * + * Copyright (C) 2007-2010 Texas Instruments, Inc. + * Copyright (C) 2007-2008, 2010 Nokia Corporation + * + * Written by Paul Walmsley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CONTROL_H +#define __ARCH_ARM_MACH_OMAP2_CONTROL_H + +#include "am33xx.h" + +#ifndef __ASSEMBLY__ +#define OMAP242X_CTRL_REGADDR(reg) \ + OMAP2_L4_IO_ADDRESS(OMAP242X_CTRL_BASE + (reg)) +#define OMAP243X_CTRL_REGADDR(reg) \ + OMAP2_L4_IO_ADDRESS(OMAP243X_CTRL_BASE + (reg)) +#define OMAP343X_CTRL_REGADDR(reg) \ + OMAP2_L4_IO_ADDRESS(OMAP343X_CTRL_BASE + (reg)) +#define AM33XX_CTRL_REGADDR(reg) \ + AM33XX_L4_WK_IO_ADDRESS(AM33XX_SCM_BASE + (reg)) +#else +#define OMAP242X_CTRL_REGADDR(reg) \ + OMAP2_L4_IO_ADDRESS(OMAP242X_CTRL_BASE + (reg)) +#define OMAP243X_CTRL_REGADDR(reg) \ + OMAP2_L4_IO_ADDRESS(OMAP243X_CTRL_BASE + (reg)) +#define OMAP343X_CTRL_REGADDR(reg) \ + OMAP2_L4_IO_ADDRESS(OMAP343X_CTRL_BASE + (reg)) +#define AM33XX_CTRL_REGADDR(reg) \ + AM33XX_L4_WK_IO_ADDRESS(AM33XX_SCM_BASE + (reg)) +#endif /* __ASSEMBLY__ */ + +/* + * As elsewhere, the "OMAP2_" prefix indicates that the macro is valid for + * OMAP24XX and OMAP34XX. + */ + +/* Control submodule offsets */ + +#define OMAP2_CONTROL_INTERFACE 0x000 +#define OMAP2_CONTROL_PADCONFS 0x030 +#define OMAP2_CONTROL_GENERAL 0x270 +#define OMAP343X_CONTROL_MEM_WKUP 0x600 +#define OMAP343X_CONTROL_PADCONFS_WKUP 0xa00 +#define OMAP343X_CONTROL_GENERAL_WKUP 0xa60 + +/* TI81XX spefic control submodules */ +#define TI81XX_CONTROL_DEVBOOT 0x040 +#define TI81XX_CONTROL_DEVCONF 0x600 + +/* Control register offsets - read/write with omap_ctrl_{read,write}{bwl}() */ + +#define OMAP2_CONTROL_SYSCONFIG (OMAP2_CONTROL_INTERFACE + 0x10) + +/* CONTROL_GENERAL register offsets common to OMAP2 & 3 */ +#define OMAP2_CONTROL_DEVCONF0 (OMAP2_CONTROL_GENERAL + 0x0004) +#define OMAP2_CONTROL_MSUSPENDMUX_0 (OMAP2_CONTROL_GENERAL + 0x0020) +#define OMAP2_CONTROL_MSUSPENDMUX_1 (OMAP2_CONTROL_GENERAL + 0x0024) +#define OMAP2_CONTROL_MSUSPENDMUX_2 (OMAP2_CONTROL_GENERAL + 0x0028) +#define OMAP2_CONTROL_MSUSPENDMUX_3 (OMAP2_CONTROL_GENERAL + 0x002c) +#define OMAP2_CONTROL_MSUSPENDMUX_4 (OMAP2_CONTROL_GENERAL + 0x0030) +#define OMAP2_CONTROL_MSUSPENDMUX_5 (OMAP2_CONTROL_GENERAL + 0x0034) +#define OMAP2_CONTROL_SEC_CTRL (OMAP2_CONTROL_GENERAL + 0x0040) +#define OMAP2_CONTROL_RPUB_KEY_H_0 (OMAP2_CONTROL_GENERAL + 0x0090) +#define OMAP2_CONTROL_RPUB_KEY_H_1 (OMAP2_CONTROL_GENERAL + 0x0094) +#define OMAP2_CONTROL_RPUB_KEY_H_2 (OMAP2_CONTROL_GENERAL + 0x0098) +#define OMAP2_CONTROL_RPUB_KEY_H_3 (OMAP2_CONTROL_GENERAL + 0x009c) + +/* 242x-only CONTROL_GENERAL register offsets */ +#define OMAP242X_CONTROL_DEVCONF OMAP2_CONTROL_DEVCONF0 /* match TRM */ +#define OMAP242X_CONTROL_OCM_RAM_PERM (OMAP2_CONTROL_GENERAL + 0x0068) + +/* 243x-only CONTROL_GENERAL register offsets */ +/* CONTROL_IVA2_BOOT{ADDR,MOD} are at the same place on 343x - noted below */ +#define OMAP243X_CONTROL_DEVCONF1 (OMAP2_CONTROL_GENERAL + 0x0078) +#define OMAP243X_CONTROL_CSIRXFE (OMAP2_CONTROL_GENERAL + 0x007c) +#define OMAP243X_CONTROL_IVA2_BOOTADDR (OMAP2_CONTROL_GENERAL + 0x0190) +#define OMAP243X_CONTROL_IVA2_BOOTMOD (OMAP2_CONTROL_GENERAL + 0x0194) +#define OMAP243X_CONTROL_IVA2_GEMCFG (OMAP2_CONTROL_GENERAL + 0x0198) +#define OMAP243X_CONTROL_PBIAS_LITE (OMAP2_CONTROL_GENERAL + 0x0230) + +/* 24xx-only CONTROL_GENERAL register offsets */ +#define OMAP24XX_CONTROL_DEBOBS (OMAP2_CONTROL_GENERAL + 0x0000) +#define OMAP24XX_CONTROL_EMU_SUPPORT (OMAP2_CONTROL_GENERAL + 0x0008) +#define OMAP24XX_CONTROL_SEC_TEST (OMAP2_CONTROL_GENERAL + 0x0044) +#define OMAP24XX_CONTROL_PSA_CTRL (OMAP2_CONTROL_GENERAL + 0x0048) +#define OMAP24XX_CONTROL_PSA_CMD (OMAP2_CONTROL_GENERAL + 0x004c) +#define OMAP24XX_CONTROL_PSA_VALUE (OMAP2_CONTROL_GENERAL + 0x0050) +#define OMAP24XX_CONTROL_SEC_EMU (OMAP2_CONTROL_GENERAL + 0x0060) +#define OMAP24XX_CONTROL_SEC_TAP (OMAP2_CONTROL_GENERAL + 0x0064) +#define OMAP24XX_CONTROL_OCM_PUB_RAM_ADD (OMAP2_CONTROL_GENERAL + 0x006c) +#define OMAP24XX_CONTROL_EXT_SEC_RAM_START_ADD (OMAP2_CONTROL_GENERAL + 0x0070) +#define OMAP24XX_CONTROL_EXT_SEC_RAM_STOP_ADD (OMAP2_CONTROL_GENERAL + 0x0074) +#define OMAP24XX_CONTROL_SEC_STATUS (OMAP2_CONTROL_GENERAL + 0x0080) +#define OMAP24XX_CONTROL_SEC_ERR_STATUS (OMAP2_CONTROL_GENERAL + 0x0084) +#define OMAP24XX_CONTROL_STATUS (OMAP2_CONTROL_GENERAL + 0x0088) +#define OMAP24XX_CONTROL_GENERAL_PURPOSE_STATUS (OMAP2_CONTROL_GENERAL + 0x008c) +#define OMAP24XX_CONTROL_RAND_KEY_0 (OMAP2_CONTROL_GENERAL + 0x00a0) +#define OMAP24XX_CONTROL_RAND_KEY_1 (OMAP2_CONTROL_GENERAL + 0x00a4) +#define OMAP24XX_CONTROL_RAND_KEY_2 (OMAP2_CONTROL_GENERAL + 0x00a8) +#define OMAP24XX_CONTROL_RAND_KEY_3 (OMAP2_CONTROL_GENERAL + 0x00ac) +#define OMAP24XX_CONTROL_CUST_KEY_0 (OMAP2_CONTROL_GENERAL + 0x00b0) +#define OMAP24XX_CONTROL_CUST_KEY_1 (OMAP2_CONTROL_GENERAL + 0x00b4) +#define OMAP24XX_CONTROL_TEST_KEY_0 (OMAP2_CONTROL_GENERAL + 0x00c0) +#define OMAP24XX_CONTROL_TEST_KEY_1 (OMAP2_CONTROL_GENERAL + 0x00c4) +#define OMAP24XX_CONTROL_TEST_KEY_2 (OMAP2_CONTROL_GENERAL + 0x00c8) +#define OMAP24XX_CONTROL_TEST_KEY_3 (OMAP2_CONTROL_GENERAL + 0x00cc) +#define OMAP24XX_CONTROL_TEST_KEY_4 (OMAP2_CONTROL_GENERAL + 0x00d0) +#define OMAP24XX_CONTROL_TEST_KEY_5 (OMAP2_CONTROL_GENERAL + 0x00d4) +#define OMAP24XX_CONTROL_TEST_KEY_6 (OMAP2_CONTROL_GENERAL + 0x00d8) +#define OMAP24XX_CONTROL_TEST_KEY_7 (OMAP2_CONTROL_GENERAL + 0x00dc) +#define OMAP24XX_CONTROL_TEST_KEY_8 (OMAP2_CONTROL_GENERAL + 0x00e0) +#define OMAP24XX_CONTROL_TEST_KEY_9 (OMAP2_CONTROL_GENERAL + 0x00e4) + +#define OMAP343X_CONTROL_PADCONF_SYSNIRQ (OMAP2_CONTROL_INTERFACE + 0x01b0) + +/* 34xx-only CONTROL_GENERAL register offsets */ +#define OMAP343X_CONTROL_PADCONF_OFF (OMAP2_CONTROL_GENERAL + 0x0000) +#define OMAP343X_CONTROL_MEM_DFTRW0 (OMAP2_CONTROL_GENERAL + 0x0008) +#define OMAP343X_CONTROL_MEM_DFTRW1 (OMAP2_CONTROL_GENERAL + 0x000c) +#define OMAP343X_CONTROL_DEVCONF1 (OMAP2_CONTROL_GENERAL + 0x0068) +#define OMAP343X_CONTROL_CSIRXFE (OMAP2_CONTROL_GENERAL + 0x006c) +#define OMAP343X_CONTROL_SEC_STATUS (OMAP2_CONTROL_GENERAL + 0x0070) +#define OMAP343X_CONTROL_SEC_ERR_STATUS (OMAP2_CONTROL_GENERAL + 0x0074) +#define OMAP343X_CONTROL_SEC_ERR_STATUS_DEBUG (OMAP2_CONTROL_GENERAL + 0x0078) +#define OMAP343X_CONTROL_STATUS (OMAP2_CONTROL_GENERAL + 0x0080) +#define OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS (OMAP2_CONTROL_GENERAL + 0x0084) +#define OMAP343X_CONTROL_RPUB_KEY_H_4 (OMAP2_CONTROL_GENERAL + 0x00a0) +#define OMAP343X_CONTROL_RAND_KEY_0 (OMAP2_CONTROL_GENERAL + 0x00a8) +#define OMAP343X_CONTROL_RAND_KEY_1 (OMAP2_CONTROL_GENERAL + 0x00ac) +#define OMAP343X_CONTROL_RAND_KEY_2 (OMAP2_CONTROL_GENERAL + 0x00b0) +#define OMAP343X_CONTROL_RAND_KEY_3 (OMAP2_CONTROL_GENERAL + 0x00b4) +#define OMAP343X_CONTROL_TEST_KEY_0 (OMAP2_CONTROL_GENERAL + 0x00c8) +#define OMAP343X_CONTROL_TEST_KEY_1 (OMAP2_CONTROL_GENERAL + 0x00cc) +#define OMAP343X_CONTROL_TEST_KEY_2 (OMAP2_CONTROL_GENERAL + 0x00d0) +#define OMAP343X_CONTROL_TEST_KEY_3 (OMAP2_CONTROL_GENERAL + 0x00d4) +#define OMAP343X_CONTROL_TEST_KEY_4 (OMAP2_CONTROL_GENERAL + 0x00d8) +#define OMAP343X_CONTROL_TEST_KEY_5 (OMAP2_CONTROL_GENERAL + 0x00dc) +#define OMAP343X_CONTROL_TEST_KEY_6 (OMAP2_CONTROL_GENERAL + 0x00e0) +#define OMAP343X_CONTROL_TEST_KEY_7 (OMAP2_CONTROL_GENERAL + 0x00e4) +#define OMAP343X_CONTROL_TEST_KEY_8 (OMAP2_CONTROL_GENERAL + 0x00e8) +#define OMAP343X_CONTROL_TEST_KEY_9 (OMAP2_CONTROL_GENERAL + 0x00ec) +#define OMAP343X_CONTROL_TEST_KEY_10 (OMAP2_CONTROL_GENERAL + 0x00f0) +#define OMAP343X_CONTROL_TEST_KEY_11 (OMAP2_CONTROL_GENERAL + 0x00f4) +#define OMAP343X_CONTROL_TEST_KEY_12 (OMAP2_CONTROL_GENERAL + 0x00f8) +#define OMAP343X_CONTROL_TEST_KEY_13 (OMAP2_CONTROL_GENERAL + 0x00fc) +#define OMAP343X_CONTROL_FUSE_OPP1_VDD1 (OMAP2_CONTROL_GENERAL + 0x0110) +#define OMAP343X_CONTROL_FUSE_OPP2_VDD1 (OMAP2_CONTROL_GENERAL + 0x0114) +#define OMAP343X_CONTROL_FUSE_OPP3_VDD1 (OMAP2_CONTROL_GENERAL + 0x0118) +#define OMAP343X_CONTROL_FUSE_OPP4_VDD1 (OMAP2_CONTROL_GENERAL + 0x011c) +#define OMAP343X_CONTROL_FUSE_OPP5_VDD1 (OMAP2_CONTROL_GENERAL + 0x0120) +#define OMAP343X_CONTROL_FUSE_OPP1_VDD2 (OMAP2_CONTROL_GENERAL + 0x0124) +#define OMAP343X_CONTROL_FUSE_OPP2_VDD2 (OMAP2_CONTROL_GENERAL + 0x0128) +#define OMAP343X_CONTROL_FUSE_OPP3_VDD2 (OMAP2_CONTROL_GENERAL + 0x012c) +#define OMAP343X_CONTROL_FUSE_SR (OMAP2_CONTROL_GENERAL + 0x0130) +#define OMAP343X_CONTROL_IVA2_BOOTADDR (OMAP2_CONTROL_GENERAL + 0x0190) +#define OMAP343X_CONTROL_IVA2_BOOTMOD (OMAP2_CONTROL_GENERAL + 0x0194) +#define OMAP343X_CONTROL_DEBOBS(i) (OMAP2_CONTROL_GENERAL + 0x01B0 \ + + ((i) >> 1) * 4 + (!((i) & 1)) * 2) +#define OMAP343X_CONTROL_PROG_IO0 (OMAP2_CONTROL_GENERAL + 0x01D4) +#define OMAP343X_CONTROL_PROG_IO1 (OMAP2_CONTROL_GENERAL + 0x01D8) +#define OMAP343X_CONTROL_DSS_DPLL_SPREADING (OMAP2_CONTROL_GENERAL + 0x01E0) +#define OMAP343X_CONTROL_CORE_DPLL_SPREADING (OMAP2_CONTROL_GENERAL + 0x01E4) +#define OMAP343X_CONTROL_PER_DPLL_SPREADING (OMAP2_CONTROL_GENERAL + 0x01E8) +#define OMAP343X_CONTROL_USBHOST_DPLL_SPREADING (OMAP2_CONTROL_GENERAL + 0x01EC) +#define OMAP343X_CONTROL_PBIAS_LITE (OMAP2_CONTROL_GENERAL + 0x02B0) +#define OMAP343X_CONTROL_TEMP_SENSOR (OMAP2_CONTROL_GENERAL + 0x02B4) +#define OMAP343X_CONTROL_SRAMLDO4 (OMAP2_CONTROL_GENERAL + 0x02B8) +#define OMAP343X_CONTROL_SRAMLDO5 (OMAP2_CONTROL_GENERAL + 0x02C0) +#define OMAP343X_CONTROL_CSI (OMAP2_CONTROL_GENERAL + 0x02C4) + +/* OMAP3630 only CONTROL_GENERAL register offsets */ +#define OMAP3630_CONTROL_FUSE_OPP1G_VDD1 (OMAP2_CONTROL_GENERAL + 0x0110) +#define OMAP3630_CONTROL_FUSE_OPP50_VDD1 (OMAP2_CONTROL_GENERAL + 0x0114) +#define OMAP3630_CONTROL_FUSE_OPP100_VDD1 (OMAP2_CONTROL_GENERAL + 0x0118) +#define OMAP3630_CONTROL_FUSE_OPP120_VDD1 (OMAP2_CONTROL_GENERAL + 0x0120) +#define OMAP3630_CONTROL_FUSE_OPP50_VDD2 (OMAP2_CONTROL_GENERAL + 0x0128) +#define OMAP3630_CONTROL_FUSE_OPP100_VDD2 (OMAP2_CONTROL_GENERAL + 0x012C) +#define OMAP3630_CONTROL_CAMERA_PHY_CTRL (OMAP2_CONTROL_GENERAL + 0x02f0) + +/* OMAP44xx control efuse offsets */ +#define OMAP44XX_CONTROL_FUSE_IVA_OPP50 0x22C +#define OMAP44XX_CONTROL_FUSE_IVA_OPP100 0x22F +#define OMAP44XX_CONTROL_FUSE_IVA_OPPTURBO 0x232 +#define OMAP44XX_CONTROL_FUSE_IVA_OPPNITRO 0x235 +#define OMAP44XX_CONTROL_FUSE_MPU_OPP50 0x240 +#define OMAP44XX_CONTROL_FUSE_MPU_OPP100 0x243 +#define OMAP44XX_CONTROL_FUSE_MPU_OPPTURBO 0x246 +#define OMAP44XX_CONTROL_FUSE_MPU_OPPNITRO 0x249 +#define OMAP44XX_CONTROL_FUSE_MPU_OPPNITROSB 0x24C +#define OMAP44XX_CONTROL_FUSE_CORE_OPP50 0x254 +#define OMAP44XX_CONTROL_FUSE_CORE_OPP100 0x257 +#define OMAP44XX_CONTROL_FUSE_CORE_OPP100OV 0x25A + +/* AM35XX only CONTROL_GENERAL register offsets */ +#define AM35XX_CONTROL_MSUSPENDMUX_6 (OMAP2_CONTROL_GENERAL + 0x0038) +#define AM35XX_CONTROL_DEVCONF2 (OMAP2_CONTROL_GENERAL + 0x0310) +#define AM35XX_CONTROL_DEVCONF3 (OMAP2_CONTROL_GENERAL + 0x0314) +#define AM35XX_CONTROL_CBA_PRIORITY (OMAP2_CONTROL_GENERAL + 0x0320) +#define AM35XX_CONTROL_LVL_INTR_CLEAR (OMAP2_CONTROL_GENERAL + 0x0324) +#define AM35XX_CONTROL_IP_SW_RESET (OMAP2_CONTROL_GENERAL + 0x0328) +#define AM35XX_CONTROL_IPSS_CLK_CTRL (OMAP2_CONTROL_GENERAL + 0x032C) + +/* 34xx PADCONF register offsets */ +#define OMAP343X_PADCONF_ETK(i) (OMAP2_CONTROL_PADCONFS + 0x5a8 + \ + (i)*2) +#define OMAP343X_PADCONF_ETK_CLK OMAP343X_PADCONF_ETK(0) +#define OMAP343X_PADCONF_ETK_CTL OMAP343X_PADCONF_ETK(1) +#define OMAP343X_PADCONF_ETK_D0 OMAP343X_PADCONF_ETK(2) +#define OMAP343X_PADCONF_ETK_D1 OMAP343X_PADCONF_ETK(3) +#define OMAP343X_PADCONF_ETK_D2 OMAP343X_PADCONF_ETK(4) +#define OMAP343X_PADCONF_ETK_D3 OMAP343X_PADCONF_ETK(5) +#define OMAP343X_PADCONF_ETK_D4 OMAP343X_PADCONF_ETK(6) +#define OMAP343X_PADCONF_ETK_D5 OMAP343X_PADCONF_ETK(7) +#define OMAP343X_PADCONF_ETK_D6 OMAP343X_PADCONF_ETK(8) +#define OMAP343X_PADCONF_ETK_D7 OMAP343X_PADCONF_ETK(9) +#define OMAP343X_PADCONF_ETK_D8 OMAP343X_PADCONF_ETK(10) +#define OMAP343X_PADCONF_ETK_D9 OMAP343X_PADCONF_ETK(11) +#define OMAP343X_PADCONF_ETK_D10 OMAP343X_PADCONF_ETK(12) +#define OMAP343X_PADCONF_ETK_D11 OMAP343X_PADCONF_ETK(13) +#define OMAP343X_PADCONF_ETK_D12 OMAP343X_PADCONF_ETK(14) +#define OMAP343X_PADCONF_ETK_D13 OMAP343X_PADCONF_ETK(15) +#define OMAP343X_PADCONF_ETK_D14 OMAP343X_PADCONF_ETK(16) +#define OMAP343X_PADCONF_ETK_D15 OMAP343X_PADCONF_ETK(17) + +/* 34xx GENERAL_WKUP register offsets */ +#define OMAP34XX_CONTROL_WKUP_CTRL (OMAP343X_CONTROL_GENERAL_WKUP - 0x4) +#define OMAP36XX_GPIO_IO_PWRDNZ BIT(6) + +#define OMAP343X_CONTROL_WKUP_DEBOBSMUX(i) (OMAP343X_CONTROL_GENERAL_WKUP + \ + 0x008 + (i)) +#define OMAP343X_CONTROL_WKUP_DEBOBS0 (OMAP343X_CONTROL_GENERAL_WKUP + 0x008) +#define OMAP343X_CONTROL_WKUP_DEBOBS1 (OMAP343X_CONTROL_GENERAL_WKUP + 0x00C) +#define OMAP343X_CONTROL_WKUP_DEBOBS2 (OMAP343X_CONTROL_GENERAL_WKUP + 0x010) +#define OMAP343X_CONTROL_WKUP_DEBOBS3 (OMAP343X_CONTROL_GENERAL_WKUP + 0x014) +#define OMAP343X_CONTROL_WKUP_DEBOBS4 (OMAP343X_CONTROL_GENERAL_WKUP + 0x018) + +/* 36xx-only RTA - Retention till Access control registers and bits */ +#define OMAP36XX_CONTROL_MEM_RTA_CTRL 0x40C +#define OMAP36XX_RTA_DISABLE 0x0 + +/* 34xx D2D idle-related pins, handled by PM core */ +#define OMAP3_PADCONF_SAD2D_MSTANDBY 0x250 +#define OMAP3_PADCONF_SAD2D_IDLEACK 0x254 + +/* TI81XX CONTROL_DEVBOOT register offsets */ +#define TI81XX_CONTROL_STATUS (TI81XX_CONTROL_DEVBOOT + 0x000) + +/* TI81XX CONTROL_DEVCONF register offsets */ +#define TI81XX_CONTROL_DEVICE_ID (TI81XX_CONTROL_DEVCONF + 0x000) + +/* OMAP4 CONTROL MODULE */ +#define OMAP4_CTRL_MODULE_PAD_WKUP 0x4a31e000 +#define OMAP4_CTRL_MODULE_PAD_WKUP_CONTROL_I2C_2 0x0604 +#define OMAP4_CTRL_MODULE_CORE_STATUS 0x02c4 +#define OMAP4_CTRL_MODULE_CORE_STD_FUSE_PROD_ID_1 0x0218 +#define OMAP4_CTRL_MODULE_CORE_DSP_BOOTADDR 0x0304 +#define OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY 0x0618 +#define OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_CAMERA_RX 0x0608 + +/* OMAP4 CONTROL_DSIPHY */ +#define OMAP4_DSI2_LANEENABLE_SHIFT 29 +#define OMAP4_DSI2_LANEENABLE_MASK (0x7 << 29) +#define OMAP4_DSI1_LANEENABLE_SHIFT 24 +#define OMAP4_DSI1_LANEENABLE_MASK (0x1f << 24) +#define OMAP4_DSI1_PIPD_SHIFT 19 +#define OMAP4_DSI1_PIPD_MASK (0x1f << 19) +#define OMAP4_DSI2_PIPD_SHIFT 14 +#define OMAP4_DSI2_PIPD_MASK (0x1f << 14) + +/* OMAP4 CONTROL_CAMERA_RX */ +#define OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT 24 +#define OMAP4_CAMERARX_CSI21_LANEENABLE_MASK (0x1f << 24) +#define OMAP4_CAMERARX_CSI22_LANEENABLE_SHIFT 29 +#define OMAP4_CAMERARX_CSI22_LANEENABLE_MASK (0x3 << 29) +#define OMAP4_CAMERARX_CSI22_CTRLCLKEN_SHIFT 21 +#define OMAP4_CAMERARX_CSI22_CTRLCLKEN_MASK (1 << 21) +#define OMAP4_CAMERARX_CSI22_CAMMODE_SHIFT 19 +#define OMAP4_CAMERARX_CSI22_CAMMODE_MASK (0x3 << 19) +#define OMAP4_CAMERARX_CSI21_CTRLCLKEN_SHIFT 18 +#define OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK (1 << 18) +#define OMAP4_CAMERARX_CSI21_CAMMODE_SHIFT 16 +#define OMAP4_CAMERARX_CSI21_CAMMODE_MASK (0x3 << 16) + +/* OMAP54XX CONTROL STATUS register */ +#define OMAP5XXX_CONTROL_STATUS 0x134 +#define OMAP5_DEVICETYPE_MASK (0x7 << 6) + +/* DRA7XX CONTROL CORE BOOTSTRAP */ +#define DRA7_CTRL_CORE_BOOTSTRAP 0x6c4 +#define DRA7_SPEEDSELECT_MASK (0x3 << 8) + +/* + * REVISIT: This list of registers is not comprehensive - there are more + * that should be added. + */ + +/* + * Control module register bit defines - these should eventually go into + * their own regbits file. Some of these will be complicated, depending + * on the device type (general-purpose, emulator, test, secure, bad, other) + * and the security mode (secure, non-secure, don't care) + */ +/* CONTROL_DEVCONF0 bits */ +#define OMAP2_MMCSDIO1ADPCLKISEL (1 << 24) /* MMC1 loop back clock */ +#define OMAP24XX_USBSTANDBYCTRL (1 << 15) +#define OMAP2_MCBSP2_CLKS_MASK (1 << 6) +#define OMAP2_MCBSP1_FSR_MASK (1 << 4) +#define OMAP2_MCBSP1_CLKR_MASK (1 << 3) +#define OMAP2_MCBSP1_CLKS_MASK (1 << 2) + +/* CONTROL_DEVCONF1 bits */ +#define OMAP243X_MMC1_ACTIVE_OVERWRITE (1 << 31) +#define OMAP2_MMCSDIO2ADPCLKISEL (1 << 6) /* MMC2 loop back clock */ +#define OMAP2_MCBSP5_CLKS_MASK (1 << 4) /* > 242x */ +#define OMAP2_MCBSP4_CLKS_MASK (1 << 2) /* > 242x */ +#define OMAP2_MCBSP3_CLKS_MASK (1 << 0) /* > 242x */ + +/* CONTROL_STATUS bits */ +#define OMAP2_DEVICETYPE_MASK (0x7 << 8) +#define OMAP2_SYSBOOT_5_MASK (1 << 5) +#define OMAP2_SYSBOOT_4_MASK (1 << 4) +#define OMAP2_SYSBOOT_3_MASK (1 << 3) +#define OMAP2_SYSBOOT_2_MASK (1 << 2) +#define OMAP2_SYSBOOT_1_MASK (1 << 1) +#define OMAP2_SYSBOOT_0_MASK (1 << 0) + +/* CONTROL_PBIAS_LITE bits */ +#define OMAP343X_PBIASLITESUPPLY_HIGH1 (1 << 15) +#define OMAP343X_PBIASLITEVMODEERROR1 (1 << 11) +#define OMAP343X_PBIASSPEEDCTRL1 (1 << 10) +#define OMAP343X_PBIASLITEPWRDNZ1 (1 << 9) +#define OMAP343X_PBIASLITEVMODE1 (1 << 8) +#define OMAP343X_PBIASLITESUPPLY_HIGH0 (1 << 7) +#define OMAP343X_PBIASLITEVMODEERROR0 (1 << 3) +#define OMAP2_PBIASSPEEDCTRL0 (1 << 2) +#define OMAP2_PBIASLITEPWRDNZ0 (1 << 1) +#define OMAP2_PBIASLITEVMODE0 (1 << 0) + +/* CONTROL_PROG_IO1 bits */ +#define OMAP3630_PRG_SDMMC1_SPEEDCTRL (1 << 20) + +/* CONTROL_IVA2_BOOTMOD bits */ +#define OMAP3_IVA2_BOOTMOD_SHIFT 0 +#define OMAP3_IVA2_BOOTMOD_MASK (0xf << 0) +#define OMAP3_IVA2_BOOTMOD_IDLE (0x1 << 0) + +/* CONTROL_PADCONF_X bits */ +#define OMAP3_PADCONF_WAKEUPEVENT0 (1 << 15) +#define OMAP3_PADCONF_WAKEUPENABLE0 (1 << 14) + +#define OMAP343X_SCRATCHPAD_ROM (OMAP343X_CTRL_BASE + 0x860) +#define OMAP343X_SCRATCHPAD (OMAP343X_CTRL_BASE + 0x910) +#define OMAP343X_SCRATCHPAD_ROM_OFFSET 0x19C +#define OMAP343X_SCRATCHPAD_REGADDR(reg) OMAP2_L4_IO_ADDRESS(\ + OMAP343X_SCRATCHPAD + reg) + +/* AM35XX_CONTROL_IPSS_CLK_CTRL bits */ +#define AM35XX_USBOTG_VBUSP_CLK_SHIFT 0 +#define AM35XX_CPGMAC_VBUSP_CLK_SHIFT 1 +#define AM35XX_VPFE_VBUSP_CLK_SHIFT 2 +#define AM35XX_HECC_VBUSP_CLK_SHIFT 3 +#define AM35XX_USBOTG_FCLK_SHIFT 8 +#define AM35XX_CPGMAC_FCLK_SHIFT 9 +#define AM35XX_VPFE_FCLK_SHIFT 10 + +/* AM35XX CONTROL_LVL_INTR_CLEAR bits */ +#define AM35XX_CPGMAC_C0_MISC_PULSE_CLR BIT(0) +#define AM35XX_CPGMAC_C0_RX_PULSE_CLR BIT(1) +#define AM35XX_CPGMAC_C0_RX_THRESH_CLR BIT(2) +#define AM35XX_CPGMAC_C0_TX_PULSE_CLR BIT(3) +#define AM35XX_USBOTGSS_INT_CLR BIT(4) +#define AM35XX_VPFE_CCDC_VD0_INT_CLR BIT(5) +#define AM35XX_VPFE_CCDC_VD1_INT_CLR BIT(6) +#define AM35XX_VPFE_CCDC_VD2_INT_CLR BIT(7) + +/* AM35XX CONTROL_IP_SW_RESET bits */ +#define AM35XX_USBOTGSS_SW_RST BIT(0) +#define AM35XX_CPGMACSS_SW_RST BIT(1) +#define AM35XX_VPFE_VBUSP_SW_RST BIT(2) +#define AM35XX_HECC_SW_RST BIT(3) +#define AM35XX_VPFE_PCLK_SW_RST BIT(4) + +/* AM33XX CONTROL_STATUS register */ +#define AM33XX_CONTROL_STATUS 0x040 +#define AM33XX_CONTROL_SEC_CLK_CTRL 0x1bc + +/* AM33XX CONTROL_STATUS bitfields (partial) */ +#define AM33XX_CONTROL_STATUS_SYSBOOT1_SHIFT 22 +#define AM33XX_CONTROL_STATUS_SYSBOOT1_WIDTH 0x2 +#define AM33XX_CONTROL_STATUS_SYSBOOT1_MASK (0x3 << 22) + +/* AM33XX PWMSS Control register */ +#define AM33XX_PWMSS_TBCLK_CLKCTRL 0x664 + +/* AM33XX PWMSS Control bitfields */ +#define AM33XX_PWMSS0_TBCLKEN_SHIFT 0 +#define AM33XX_PWMSS1_TBCLKEN_SHIFT 1 +#define AM33XX_PWMSS2_TBCLKEN_SHIFT 2 + +/* DEV Feature register to identify AM33XX features */ +#define AM33XX_DEV_FEATURE 0x604 +#define AM33XX_SGX_MASK BIT(29) + +/* Additional AM33XX/AM43XX CONTROL registers */ +#define AM33XX_CONTROL_SYSCONFIG_OFFSET 0x0010 +#define AM33XX_CONTROL_STATUS_OFFSET 0x0040 +#define AM43XX_CONTROL_MPU_L2_CTRL_OFFSET 0x01e0 +#define AM33XX_CONTROL_CORTEX_VBBLDO_CTRL_OFFSET 0x041c +#define AM33XX_CONTROL_CORE_SLDO_CTRL_OFFSET 0x0428 +#define AM33XX_CONTROL_MPU_SLDO_CTRL_OFFSET 0x042c +#define AM33XX_CONTROL_CLK32KDIVRATIO_CTRL_OFFSET 0x0444 +#define AM33XX_CONTROL_BANDGAP_CTRL_OFFSET 0x0448 +#define AM33XX_CONTROL_BANDGAP_TRIM_OFFSET 0x044c +#define AM33XX_CONTROL_PLL_CLKINPULOW_CTRL_OFFSET 0x0458 +#define AM33XX_CONTROL_MOSC_CTRL_OFFSET 0x0468 +#define AM33XX_CONTROL_RCOSC_CTRL_OFFSET 0x046c +#define AM33XX_CONTROL_DEEPSLEEP_CTRL_OFFSET 0x0470 +#define AM43XX_CONTROL_DISPLAY_PLL_SEL_OFFSET 0x0534 +#define AM33XX_CONTROL_INIT_PRIORITY_0_OFFSET 0x0608 +#define AM33XX_CONTROL_INIT_PRIORITY_1_OFFSET 0x060c +#define AM33XX_CONTROL_MMU_CFG_OFFSET 0x0610 +#define AM33XX_CONTROL_TPTC_CFG_OFFSET 0x0614 +#define AM33XX_CONTROL_USB_CTRL0_OFFSET 0x0620 +#define AM33XX_CONTROL_USB_CTRL1_OFFSET 0x0628 +#define AM33XX_CONTROL_USB_WKUP_CTRL_OFFSET 0x0648 +#define AM43XX_CONTROL_USB_CTRL2_OFFSET 0x064c +#define AM43XX_CONTROL_GMII_SEL_OFFSET 0x0650 +#define AM43XX_CONTROL_MPUSS_CTRL_OFFSET 0x0654 +#define AM43XX_CONTROL_TIMER_CASCADE_CTRL_OFFSET 0x0658 +#define AM43XX_CONTROL_PWMSS_CTRL_OFFSET 0x0664 +#define AM33XX_CONTROL_MREQPRIO_0_OFFSET 0x0670 +#define AM33XX_CONTROL_MREQPRIO_1_OFFSET 0x0674 +#define AM33XX_CONTROL_HW_EVENT_SEL_GRP1_OFFSET 0x0690 +#define AM33XX_CONTROL_HW_EVENT_SEL_GRP2_OFFSET 0x0694 +#define AM33XX_CONTROL_HW_EVENT_SEL_GRP3_OFFSET 0x0698 +#define AM33XX_CONTROL_HW_EVENT_SEL_GRP4_OFFSET 0x069c +#define AM33XX_CONTROL_SMRT_CTRL_OFFSET 0x06a0 +#define AM33XX_CONTROL_MPUSS_HW_DEBUG_SEL_OFFSET 0x06a4 +#define AM43XX_CONTROL_CQDETECT_STS_OFFSET 0x0e00 +#define AM43XX_CONTROL_CQDETECT_STS2_OFFSET 0x0e08 +#define AM43XX_CONTROL_VTP_CTRL_OFFSET 0x0e0c +#define AM33XX_CONTROL_VREF_CTRL_OFFSET 0x0e14 +#define AM33XX_CONTROL_TPCC_EVT_MUX_0_3_OFFSET 0x0f90 +#define AM33XX_CONTROL_TPCC_EVT_MUX_4_7_OFFSET 0x0f94 +#define AM33XX_CONTROL_TPCC_EVT_MUX_8_11_OFFSET 0x0f98 +#define AM33XX_CONTROL_TPCC_EVT_MUX_12_15_OFFSET 0x0f9c +#define AM33XX_CONTROL_TPCC_EVT_MUX_16_19_OFFSET 0x0fa0 +#define AM33XX_CONTROL_TPCC_EVT_MUX_20_23_OFFSET 0x0fa4 +#define AM33XX_CONTROL_TPCC_EVT_MUX_24_27_OFFSET 0x0fa8 +#define AM33XX_CONTROL_TPCC_EVT_MUX_28_31_OFFSET 0x0fac +#define AM33XX_CONTROL_TPCC_EVT_MUX_32_35_OFFSET 0x0fb0 +#define AM33XX_CONTROL_TPCC_EVT_MUX_36_39_OFFSET 0x0fb4 +#define AM33XX_CONTROL_TPCC_EVT_MUX_40_43_OFFSET 0x0fb8 +#define AM33XX_CONTROL_TPCC_EVT_MUX_44_47_OFFSET 0x0fbc +#define AM33XX_CONTROL_TPCC_EVT_MUX_48_51_OFFSET 0x0fc0 +#define AM33XX_CONTROL_TPCC_EVT_MUX_52_55_OFFSET 0x0fc4 +#define AM33XX_CONTROL_TPCC_EVT_MUX_56_59_OFFSET 0x0fc8 +#define AM33XX_CONTROL_TPCC_EVT_MUX_60_63_OFFSET 0x0fcc +#define AM33XX_CONTROL_TIMER_EVT_CAPT_OFFSET 0x0fd0 +#define AM33XX_CONTROL_ECAP_EVT_CAPT_OFFSET 0x0fd4 +#define AM33XX_CONTROL_ADC_EVT_CAPT_OFFSET 0x0fd8 +#define AM43XX_CONTROL_ADC1_EVT_CAPT_OFFSET 0x0fdc +#define AM33XX_CONTROL_RESET_ISO_OFFSET 0x1000 + +/* CONTROL OMAP STATUS register to identify OMAP3 features */ +#define OMAP3_CONTROL_OMAP_STATUS 0x044c + +#define OMAP3_SGX_SHIFT 13 +#define OMAP3_SGX_MASK (3 << OMAP3_SGX_SHIFT) +#define FEAT_SGX_FULL 0 +#define FEAT_SGX_HALF 1 +#define FEAT_SGX_NONE 2 + +#define OMAP3_IVA_SHIFT 12 +#define OMAP3_IVA_MASK (1 << OMAP3_IVA_SHIFT) +#define FEAT_IVA 0 +#define FEAT_IVA_NONE 1 + +#define OMAP3_L2CACHE_SHIFT 10 +#define OMAP3_L2CACHE_MASK (3 << OMAP3_L2CACHE_SHIFT) +#define FEAT_L2CACHE_NONE 0 +#define FEAT_L2CACHE_64KB 1 +#define FEAT_L2CACHE_128KB 2 +#define FEAT_L2CACHE_256KB 3 + +#define OMAP3_ISP_SHIFT 5 +#define OMAP3_ISP_MASK (1 << OMAP3_ISP_SHIFT) +#define FEAT_ISP 0 +#define FEAT_ISP_NONE 1 + +#define OMAP3_NEON_SHIFT 4 +#define OMAP3_NEON_MASK (1 << OMAP3_NEON_SHIFT) +#define FEAT_NEON 0 +#define FEAT_NEON_NONE 1 + + +#ifndef __ASSEMBLY__ +#ifdef CONFIG_ARCH_OMAP2PLUS +extern u8 omap_ctrl_readb(u16 offset); +extern u16 omap_ctrl_readw(u16 offset); +extern u32 omap_ctrl_readl(u16 offset); +extern void omap_ctrl_writeb(u8 val, u16 offset); +extern void omap_ctrl_writew(u16 val, u16 offset); +extern void omap_ctrl_writel(u32 val, u16 offset); + +extern void omap3_save_scratchpad_contents(void); +extern void omap3_clear_scratchpad_contents(void); +extern void omap3_restore(void); +extern void omap3_restore_es3(void); +extern void omap3_restore_3630(void); +extern u32 omap3_arm_context[128]; +extern void omap3_control_save_context(void); +extern void omap3_control_restore_context(void); +extern void omap3_ctrl_write_boot_mode(u8 bootmode); +extern void omap_ctrl_write_dsp_boot_addr(u32 bootaddr); +extern void omap_ctrl_write_dsp_boot_mode(u8 bootmode); +extern void omap3630_ctrl_disable_rta(void); +extern int omap3_ctrl_save_padconf(void); +void omap3_ctrl_init(void); +int omap2_control_base_init(void); +int omap_control_init(void); +void __init omap3_control_legacy_iomap_init(void); +#else +#define omap_ctrl_readb(x) 0 +#define omap_ctrl_readw(x) 0 +#define omap_ctrl_readl(x) 0 +#define omap4_ctrl_pad_readl(x) 0 +#define omap_ctrl_writeb(x, y) WARN_ON(1) +#define omap_ctrl_writew(x, y) WARN_ON(1) +#define omap_ctrl_writel(x, y) WARN_ON(1) +#define omap4_ctrl_pad_writel(x, y) WARN_ON(1) +#endif +#endif /* __ASSEMBLY__ */ + +#endif /* __ARCH_ARM_MACH_OMAP2_CONTROL_H */ + diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c new file mode 100644 index 000000000..090a8aafb --- /dev/null +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/cpuidle34xx.c + * + * OMAP3 CPU IDLE Routines + * + * Copyright (C) 2008 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Karthik Dasu <karthik-dp@ti.com> + * + * Copyright (C) 2006 Nokia Corporation + * Tony Lindgren <tony@atomide.com> + * + * Copyright (C) 2005 Texas Instruments, Inc. + * Richard Woodruff <r-woodruff2@ti.com> + * + * Based on pm.c for omap2 + */ + +#include <linux/sched.h> +#include <linux/cpuidle.h> +#include <linux/export.h> +#include <linux/cpu_pm.h> +#include <asm/cpuidle.h> + +#include "powerdomain.h" +#include "clockdomain.h" + +#include "pm.h" +#include "control.h" +#include "common.h" +#include "soc.h" + +/* Mach specific information to be recorded in the C-state driver_data */ +struct omap3_idle_statedata { + u8 mpu_state; + u8 core_state; + u8 per_min_state; + u8 flags; +}; + +static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd; + +/* + * Possible flag bits for struct omap3_idle_statedata.flags: + * + * OMAP_CPUIDLE_CX_NO_CLKDM_IDLE: don't allow the MPU clockdomain to go + * inactive. This in turn prevents the MPU DPLL from entering autoidle + * mode, so wakeup latency is greatly reduced, at the cost of additional + * energy consumption. This also prevents the CORE clockdomain from + * entering idle. + */ +#define OMAP_CPUIDLE_CX_NO_CLKDM_IDLE BIT(0) + +/* + * Prevent PER OFF if CORE is not in RETention or OFF as this would + * disable PER wakeups completely. + */ +static struct omap3_idle_statedata omap3_idle_data[] = { + { + .mpu_state = PWRDM_POWER_ON, + .core_state = PWRDM_POWER_ON, + /* In C1 do not allow PER state lower than CORE state */ + .per_min_state = PWRDM_POWER_ON, + .flags = OMAP_CPUIDLE_CX_NO_CLKDM_IDLE, + }, + { + .mpu_state = PWRDM_POWER_ON, + .core_state = PWRDM_POWER_ON, + .per_min_state = PWRDM_POWER_RET, + }, + { + .mpu_state = PWRDM_POWER_RET, + .core_state = PWRDM_POWER_ON, + .per_min_state = PWRDM_POWER_RET, + }, + { + .mpu_state = PWRDM_POWER_OFF, + .core_state = PWRDM_POWER_ON, + .per_min_state = PWRDM_POWER_RET, + }, + { + .mpu_state = PWRDM_POWER_RET, + .core_state = PWRDM_POWER_RET, + .per_min_state = PWRDM_POWER_OFF, + }, + { + .mpu_state = PWRDM_POWER_OFF, + .core_state = PWRDM_POWER_RET, + .per_min_state = PWRDM_POWER_OFF, + }, + { + .mpu_state = PWRDM_POWER_OFF, + .core_state = PWRDM_POWER_OFF, + .per_min_state = PWRDM_POWER_OFF, + }, +}; + +/** + * omap3_enter_idle - Programs OMAP3 to enter the specified state + * @dev: cpuidle device + * @drv: cpuidle driver + * @index: the index of state to be entered + */ +static int omap3_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + struct omap3_idle_statedata *cx = &omap3_idle_data[index]; + int error; + + if (omap_irq_pending() || need_resched()) + goto return_sleep_time; + + /* Deny idle for C1 */ + if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) { + clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]); + } else { + pwrdm_set_next_pwrst(mpu_pd, cx->mpu_state); + pwrdm_set_next_pwrst(core_pd, cx->core_state); + } + + /* + * Call idle CPU PM enter notifier chain so that + * VFP context is saved. + */ + if (cx->mpu_state == PWRDM_POWER_OFF) { + error = cpu_pm_enter(); + if (error) + goto out_clkdm_set; + } + + /* Execute ARM wfi */ + omap_sram_idle(); + + /* + * Call idle CPU PM enter notifier chain to restore + * VFP context. + */ + if (cx->mpu_state == PWRDM_POWER_OFF && + pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF) + cpu_pm_exit(); + +out_clkdm_set: + /* Re-allow idle for C1 */ + if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) + clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]); + +return_sleep_time: + + return index; +} + +/** + * next_valid_state - Find next valid C-state + * @dev: cpuidle device + * @drv: cpuidle driver + * @index: Index of currently selected c-state + * + * If the state corresponding to index is valid, index is returned back + * to the caller. Else, this function searches for a lower c-state which is + * still valid (as defined in omap3_power_states[]) and returns its index. + * + * A state is valid if the 'valid' field is enabled and + * if it satisfies the enable_off_mode condition. + */ +static int next_valid_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct omap3_idle_statedata *cx = &omap3_idle_data[index]; + u32 mpu_deepest_state = PWRDM_POWER_RET; + u32 core_deepest_state = PWRDM_POWER_RET; + int idx; + int next_index = 0; /* C1 is the default value */ + + if (enable_off_mode) { + mpu_deepest_state = PWRDM_POWER_OFF; + /* + * Erratum i583: valable for ES rev < Es1.2 on 3630. + * CORE OFF mode is not supported in a stable form, restrict + * instead the CORE state to RET. + */ + if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583)) + core_deepest_state = PWRDM_POWER_OFF; + } + + /* Check if current state is valid */ + if ((cx->mpu_state >= mpu_deepest_state) && + (cx->core_state >= core_deepest_state)) + return index; + + /* + * Drop to next valid state. + * Start search from the next (lower) state. + */ + for (idx = index - 1; idx >= 0; idx--) { + cx = &omap3_idle_data[idx]; + if ((cx->mpu_state >= mpu_deepest_state) && + (cx->core_state >= core_deepest_state)) { + next_index = idx; + break; + } + } + + return next_index; +} + +/** + * omap3_enter_idle_bm - Checks for any bus activity + * @dev: cpuidle device + * @drv: cpuidle driver + * @index: array index of target state to be programmed + * + * This function checks for any pending activity and then programs + * the device to the specified or a safer state. + */ +static int omap3_enter_idle_bm(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + int new_state_idx, ret; + u8 per_next_state, per_saved_state; + struct omap3_idle_statedata *cx; + + /* + * Use only C1 if CAM is active. + * CAM does not have wakeup capability in OMAP3. + */ + if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON) + new_state_idx = drv->safe_state_index; + else + new_state_idx = next_valid_state(dev, drv, index); + + /* + * FIXME: we currently manage device-specific idle states + * for PER and CORE in combination with CPU-specific + * idle states. This is wrong, and device-specific + * idle management needs to be separated out into + * its own code. + */ + + /* Program PER state */ + cx = &omap3_idle_data[new_state_idx]; + + per_next_state = pwrdm_read_next_pwrst(per_pd); + per_saved_state = per_next_state; + if (per_next_state < cx->per_min_state) { + per_next_state = cx->per_min_state; + pwrdm_set_next_pwrst(per_pd, per_next_state); + } + + ret = omap3_enter_idle(dev, drv, new_state_idx); + + /* Restore original PER state if it was modified */ + if (per_next_state != per_saved_state) + pwrdm_set_next_pwrst(per_pd, per_saved_state); + + return ret; +} + +static struct cpuidle_driver omap3_idle_driver = { + .name = "omap3_idle", + .owner = THIS_MODULE, + .states = { + { + .enter = omap3_enter_idle_bm, + .exit_latency = 2 + 2, + .target_residency = 5, + .name = "C1", + .desc = "MPU ON + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 10 + 10, + .target_residency = 30, + .name = "C2", + .desc = "MPU ON + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 50 + 50, + .target_residency = 300, + .name = "C3", + .desc = "MPU RET + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 1500 + 1800, + .target_residency = 4000, + .name = "C4", + .desc = "MPU OFF + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 2500 + 7500, + .target_residency = 12000, + .name = "C5", + .desc = "MPU RET + CORE RET", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 3000 + 8500, + .target_residency = 15000, + .name = "C6", + .desc = "MPU OFF + CORE RET", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 10000 + 30000, + .target_residency = 30000, + .name = "C7", + .desc = "MPU OFF + CORE OFF", + }, + }, + .state_count = ARRAY_SIZE(omap3_idle_data), + .safe_state_index = 0, +}; + +/* + * Numbers based on measurements made in October 2009 for PM optimized kernel + * with CPU freq enabled on device Nokia N900. Assumes OPP2 (main idle OPP, + * and worst case latencies). + */ +static struct cpuidle_driver omap3430_idle_driver = { + .name = "omap3430_idle", + .owner = THIS_MODULE, + .states = { + { + .enter = omap3_enter_idle_bm, + .exit_latency = 110 + 162, + .target_residency = 5, + .name = "C1", + .desc = "MPU ON + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 106 + 180, + .target_residency = 309, + .name = "C2", + .desc = "MPU ON + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 107 + 410, + .target_residency = 46057, + .name = "C3", + .desc = "MPU RET + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 121 + 3374, + .target_residency = 46057, + .name = "C4", + .desc = "MPU OFF + CORE ON", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 855 + 1146, + .target_residency = 46057, + .name = "C5", + .desc = "MPU RET + CORE RET", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 7580 + 4134, + .target_residency = 484329, + .name = "C6", + .desc = "MPU OFF + CORE RET", + }, + { + .enter = omap3_enter_idle_bm, + .exit_latency = 7505 + 15274, + .target_residency = 484329, + .name = "C7", + .desc = "MPU OFF + CORE OFF", + }, + }, + .state_count = ARRAY_SIZE(omap3_idle_data), + .safe_state_index = 0, +}; + +/* Public functions */ + +/** + * omap3_idle_init - Init routine for OMAP3 idle + * + * Registers the OMAP3 specific cpuidle driver to the cpuidle + * framework with the valid set of states. + */ +int __init omap3_idle_init(void) +{ + mpu_pd = pwrdm_lookup("mpu_pwrdm"); + core_pd = pwrdm_lookup("core_pwrdm"); + per_pd = pwrdm_lookup("per_pwrdm"); + cam_pd = pwrdm_lookup("cam_pwrdm"); + + if (!mpu_pd || !core_pd || !per_pd || !cam_pd) + return -ENODEV; + + if (cpu_is_omap3430()) + return cpuidle_register(&omap3430_idle_driver, NULL); + else + return cpuidle_register(&omap3_idle_driver, NULL); +} diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c new file mode 100644 index 000000000..de37027ad --- /dev/null +++ b/arch/arm/mach-omap2/cpuidle44xx.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4+ CPU idle Routines + * + * Copyright (C) 2011-2013 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * Rajendra Nayak <rnayak@ti.com> + */ + +#include <linux/sched.h> +#include <linux/cpuidle.h> +#include <linux/cpu_pm.h> +#include <linux/export.h> +#include <linux/tick.h> + +#include <asm/cpuidle.h> + +#include "common.h" +#include "pm.h" +#include "prm.h" +#include "soc.h" +#include "clockdomain.h" + +#define MAX_CPUS 2 + +/* Machine specific information */ +struct idle_statedata { + u32 cpu_state; + u32 mpu_logic_state; + u32 mpu_state; + u32 mpu_state_vote; +}; + +static struct idle_statedata omap4_idle_data[] = { + { + .cpu_state = PWRDM_POWER_ON, + .mpu_state = PWRDM_POWER_ON, + .mpu_logic_state = PWRDM_POWER_RET, + }, + { + .cpu_state = PWRDM_POWER_OFF, + .mpu_state = PWRDM_POWER_RET, + .mpu_logic_state = PWRDM_POWER_RET, + }, + { + .cpu_state = PWRDM_POWER_OFF, + .mpu_state = PWRDM_POWER_RET, + .mpu_logic_state = PWRDM_POWER_OFF, + }, +}; + +static struct idle_statedata omap5_idle_data[] = { + { + .cpu_state = PWRDM_POWER_ON, + .mpu_state = PWRDM_POWER_ON, + .mpu_logic_state = PWRDM_POWER_ON, + }, + { + .cpu_state = PWRDM_POWER_RET, + .mpu_state = PWRDM_POWER_RET, + .mpu_logic_state = PWRDM_POWER_RET, + }, +}; + +static struct powerdomain *mpu_pd, *cpu_pd[MAX_CPUS]; +static struct clockdomain *cpu_clkdm[MAX_CPUS]; + +static atomic_t abort_barrier; +static bool cpu_done[MAX_CPUS]; +static struct idle_statedata *state_ptr = &omap4_idle_data[0]; +static DEFINE_RAW_SPINLOCK(mpu_lock); + +/* Private functions */ + +/** + * omap_enter_idle_[simple/coupled] - OMAP4PLUS cpuidle entry functions + * @dev: cpuidle device + * @drv: cpuidle driver + * @index: the index of state to be entered + * + * Called from the CPUidle framework to program the device to the + * specified low power state selected by the governor. + * Returns the amount of time spent in the low power state. + */ +static int omap_enter_idle_simple(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + omap_do_wfi(); + return index; +} + +static int omap_enter_idle_smp(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + struct idle_statedata *cx = state_ptr + index; + unsigned long flag; + + raw_spin_lock_irqsave(&mpu_lock, flag); + cx->mpu_state_vote++; + if (cx->mpu_state_vote == num_online_cpus()) { + pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state); + omap_set_pwrdm_state(mpu_pd, cx->mpu_state); + } + raw_spin_unlock_irqrestore(&mpu_lock, flag); + + omap4_enter_lowpower(dev->cpu, cx->cpu_state); + + raw_spin_lock_irqsave(&mpu_lock, flag); + if (cx->mpu_state_vote == num_online_cpus()) + omap_set_pwrdm_state(mpu_pd, PWRDM_POWER_ON); + cx->mpu_state_vote--; + raw_spin_unlock_irqrestore(&mpu_lock, flag); + + return index; +} + +static int omap_enter_idle_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + struct idle_statedata *cx = state_ptr + index; + u32 mpuss_can_lose_context = 0; + int error; + + /* + * CPU0 has to wait and stay ON until CPU1 is OFF state. + * This is necessary to honour hardware recommondation + * of triggeing all the possible low power modes once CPU1 is + * out of coherency and in OFF mode. + */ + if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) { + while (pwrdm_read_pwrst(cpu_pd[1]) != PWRDM_POWER_OFF) { + cpu_relax(); + + /* + * CPU1 could have already entered & exited idle + * without hitting off because of a wakeup + * or a failed attempt to hit off mode. Check for + * that here, otherwise we could spin forever + * waiting for CPU1 off. + */ + if (cpu_done[1]) + goto fail; + + } + } + + mpuss_can_lose_context = (cx->mpu_state == PWRDM_POWER_RET) && + (cx->mpu_logic_state == PWRDM_POWER_OFF); + + /* Enter broadcast mode for periodic timers */ + RCU_NONIDLE(tick_broadcast_enable()); + + /* Enter broadcast mode for one-shot timers */ + RCU_NONIDLE(tick_broadcast_enter()); + + /* + * Call idle CPU PM enter notifier chain so that + * VFP and per CPU interrupt context is saved. + */ + error = cpu_pm_enter(); + if (error) + goto cpu_pm_out; + + if (dev->cpu == 0) { + pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state); + RCU_NONIDLE(omap_set_pwrdm_state(mpu_pd, cx->mpu_state)); + + /* + * Call idle CPU cluster PM enter notifier chain + * to save GIC and wakeupgen context. + */ + if (mpuss_can_lose_context) { + error = cpu_cluster_pm_enter(); + if (error) { + index = 0; + cx = state_ptr + index; + pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state); + RCU_NONIDLE(omap_set_pwrdm_state(mpu_pd, cx->mpu_state)); + mpuss_can_lose_context = 0; + } + } + } + + omap4_enter_lowpower(dev->cpu, cx->cpu_state); + cpu_done[dev->cpu] = true; + + /* Wakeup CPU1 only if it is not offlined */ + if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) { + + if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD) && + mpuss_can_lose_context) + gic_dist_disable(); + + RCU_NONIDLE(clkdm_deny_idle(cpu_clkdm[1])); + RCU_NONIDLE(omap_set_pwrdm_state(cpu_pd[1], PWRDM_POWER_ON)); + RCU_NONIDLE(clkdm_allow_idle(cpu_clkdm[1])); + + if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD) && + mpuss_can_lose_context) { + while (gic_dist_disabled()) { + udelay(1); + cpu_relax(); + } + gic_timer_retrigger(); + } + } + + /* + * Call idle CPU cluster PM exit notifier chain + * to restore GIC and wakeupgen context. + */ + if (dev->cpu == 0 && mpuss_can_lose_context) + cpu_cluster_pm_exit(); + + /* + * Call idle CPU PM exit notifier chain to restore + * VFP and per CPU IRQ context. + */ + cpu_pm_exit(); + +cpu_pm_out: + RCU_NONIDLE(tick_broadcast_exit()); + +fail: + cpuidle_coupled_parallel_barrier(dev, &abort_barrier); + cpu_done[dev->cpu] = false; + + return index; +} + +static struct cpuidle_driver omap4_idle_driver = { + .name = "omap4_idle", + .owner = THIS_MODULE, + .states = { + { + /* C1 - CPU0 ON + CPU1 ON + MPU ON */ + .exit_latency = 2 + 2, + .target_residency = 5, + .enter = omap_enter_idle_simple, + .name = "C1", + .desc = "CPUx ON, MPUSS ON" + }, + { + /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */ + .exit_latency = 328 + 440, + .target_residency = 960, + .flags = CPUIDLE_FLAG_COUPLED, + .enter = omap_enter_idle_coupled, + .name = "C2", + .desc = "CPUx OFF, MPUSS CSWR", + }, + { + /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */ + .exit_latency = 460 + 518, + .target_residency = 1100, + .flags = CPUIDLE_FLAG_COUPLED, + .enter = omap_enter_idle_coupled, + .name = "C3", + .desc = "CPUx OFF, MPUSS OSWR", + }, + }, + .state_count = ARRAY_SIZE(omap4_idle_data), + .safe_state_index = 0, +}; + +static struct cpuidle_driver omap5_idle_driver = { + .name = "omap5_idle", + .owner = THIS_MODULE, + .states = { + { + /* C1 - CPU0 ON + CPU1 ON + MPU ON */ + .exit_latency = 2 + 2, + .target_residency = 5, + .enter = omap_enter_idle_simple, + .name = "C1", + .desc = "CPUx WFI, MPUSS ON" + }, + { + /* C2 - CPU0 RET + CPU1 RET + MPU CSWR */ + .exit_latency = 48 + 60, + .target_residency = 100, + .flags = CPUIDLE_FLAG_TIMER_STOP, + .enter = omap_enter_idle_smp, + .name = "C2", + .desc = "CPUx CSWR, MPUSS CSWR", + }, + }, + .state_count = ARRAY_SIZE(omap5_idle_data), + .safe_state_index = 0, +}; + +/* Public functions */ + +/** + * omap4_idle_init - Init routine for OMAP4+ idle + * + * Registers the OMAP4+ specific cpuidle driver to the cpuidle + * framework with the valid set of states. + */ +int __init omap4_idle_init(void) +{ + struct cpuidle_driver *idle_driver; + + if (soc_is_omap54xx()) { + state_ptr = &omap5_idle_data[0]; + idle_driver = &omap5_idle_driver; + } else { + state_ptr = &omap4_idle_data[0]; + idle_driver = &omap4_idle_driver; + } + + mpu_pd = pwrdm_lookup("mpu_pwrdm"); + cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm"); + cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm"); + if ((!mpu_pd) || (!cpu_pd[0]) || (!cpu_pd[1])) + return -ENODEV; + + cpu_clkdm[0] = clkdm_lookup("mpu0_clkdm"); + cpu_clkdm[1] = clkdm_lookup("mpu1_clkdm"); + if (!cpu_clkdm[0] || !cpu_clkdm[1]) + return -ENODEV; + + return cpuidle_register(idle_driver, cpu_online_mask); +} diff --git a/arch/arm/mach-omap2/ctrl_module_wkup_44xx.h b/arch/arm/mach-omap2/ctrl_module_wkup_44xx.h new file mode 100644 index 000000000..414f14d1e --- /dev/null +++ b/arch/arm/mach-omap2/ctrl_module_wkup_44xx.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx CTRL_MODULE_WKUP registers and bitfields + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * + * Benoit Cousson (b-cousson@ti.com) + * Santosh Shilimkar (santosh.shilimkar@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_CTRL_MODULE_WKUP_44XX_H +#define __ARCH_ARM_MACH_OMAP2_CTRL_MODULE_WKUP_44XX_H + + +/* Base address */ +#define OMAP4_CTRL_MODULE_WKUP 0x4a30c000 + +/* Registers offset */ +#define OMAP4_CTRL_MODULE_WKUP_IP_REVISION 0x0000 +#define OMAP4_CTRL_MODULE_WKUP_IP_HWINFO 0x0004 +#define OMAP4_CTRL_MODULE_WKUP_IP_SYSCONFIG 0x0010 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_0 0x0460 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_1 0x0464 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_2 0x0468 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_3 0x046c +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_4 0x0470 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_5 0x0474 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_6 0x0478 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_7 0x047c +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_8 0x0480 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_9 0x0484 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_10 0x0488 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_11 0x048c +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_12 0x0490 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_13 0x0494 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_14 0x0498 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_15 0x049c +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_16 0x04a0 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_17 0x04a4 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_18 0x04a8 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_19 0x04ac +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_20 0x04b0 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_21 0x04b4 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_22 0x04b8 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_23 0x04bc +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_24 0x04c0 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_25 0x04c4 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_26 0x04c8 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_27 0x04cc +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_28 0x04d0 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_29 0x04d4 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_30 0x04d8 +#define OMAP4_CTRL_MODULE_WKUP_CONF_DEBUG_SEL_TST_31 0x04dc + +/* Registers shifts and masks */ + +/* IP_REVISION */ +#define OMAP4_IP_REV_SCHEME_SHIFT 30 +#define OMAP4_IP_REV_SCHEME_MASK (0x3 << 30) +#define OMAP4_IP_REV_FUNC_SHIFT 16 +#define OMAP4_IP_REV_FUNC_MASK (0xfff << 16) +#define OMAP4_IP_REV_RTL_SHIFT 11 +#define OMAP4_IP_REV_RTL_MASK (0x1f << 11) +#define OMAP4_IP_REV_MAJOR_SHIFT 8 +#define OMAP4_IP_REV_MAJOR_MASK (0x7 << 8) +#define OMAP4_IP_REV_CUSTOM_SHIFT 6 +#define OMAP4_IP_REV_CUSTOM_MASK (0x3 << 6) +#define OMAP4_IP_REV_MINOR_SHIFT 0 +#define OMAP4_IP_REV_MINOR_MASK (0x3f << 0) + +/* IP_HWINFO */ +#define OMAP4_IP_HWINFO_SHIFT 0 +#define OMAP4_IP_HWINFO_MASK (0xffffffff << 0) + +/* IP_SYSCONFIG */ +#define OMAP4_IP_SYSCONFIG_IDLEMODE_SHIFT 2 +#define OMAP4_IP_SYSCONFIG_IDLEMODE_MASK (0x3 << 2) + +/* CONF_DEBUG_SEL_TST_0 */ +#define OMAP4_WKUP_MODE_SHIFT 0 +#define OMAP4_WKUP_MODE_MASK (1 << 0) + +#endif diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c new file mode 100644 index 000000000..5a2e198e7 --- /dev/null +++ b/arch/arm/mach-omap2/devices.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * linux/arch/arm/mach-omap2/devices.c + * + * OMAP2 platform device setup/initialization + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/pinctrl/machine.h> + +#include <asm/mach-types.h> +#include <asm/mach/map.h> + +#include <linux/omap-dma.h> + +#include "iomap.h" +#include "omap_hwmod.h" +#include "omap_device.h" + +#include "soc.h" +#include "common.h" +#include "control.h" +#include "display.h" + +#define L3_MODULES_MAX_LEN 12 +#define L3_MODULES 3 + +/*-------------------------------------------------------------------------*/ + +#if IS_ENABLED(CONFIG_VIDEO_OMAP2_VOUT) +#if IS_ENABLED(CONFIG_FB_OMAP2) +static struct resource omap_vout_resource[3 - CONFIG_FB_OMAP2_NUM_FBS] = { +}; +#else +static struct resource omap_vout_resource[2] = { +}; +#endif + +static u64 omap_vout_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device omap_vout_device = { + .name = "omap_vout", + .num_resources = ARRAY_SIZE(omap_vout_resource), + .resource = &omap_vout_resource[0], + .id = -1, + .dev = { + .dma_mask = &omap_vout_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +int __init omap_init_vout(void) +{ + return platform_device_register(&omap_vout_device); +} +#else +int __init omap_init_vout(void) { return 0; } +#endif diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c new file mode 100644 index 000000000..dbec3bb9f --- /dev/null +++ b/arch/arm/mach-omap2/display.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2plus display device setup / initialization. + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * Senthilvadivu Guruswamy + * Sumit Semwal + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/slab.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include <linux/platform_data/omapdss.h> +#include "omap_hwmod.h" +#include "omap_device.h" +#include "common.h" + +#include "soc.h" +#include "iomap.h" +#include "control.h" +#include "display.h" +#include "prm.h" + +#define DISPC_CONTROL 0x0040 +#define DISPC_CONTROL2 0x0238 +#define DISPC_CONTROL3 0x0848 +#define DISPC_IRQSTATUS 0x0018 + +#define DSS_CONTROL 0x40 +#define DSS_SDI_CONTROL 0x44 +#define DSS_PLL_CONTROL 0x48 + +#define LCD_EN_MASK (0x1 << 0) +#define DIGIT_EN_MASK (0x1 << 1) + +#define FRAMEDONE_IRQ_SHIFT 0 +#define EVSYNC_EVEN_IRQ_SHIFT 2 +#define EVSYNC_ODD_IRQ_SHIFT 3 +#define FRAMEDONE2_IRQ_SHIFT 22 +#define FRAMEDONE3_IRQ_SHIFT 30 +#define FRAMEDONETV_IRQ_SHIFT 24 + +/* + * FRAMEDONE_IRQ_TIMEOUT: how long (in milliseconds) to wait during DISPC + * reset before deciding that something has gone wrong + */ +#define FRAMEDONE_IRQ_TIMEOUT 100 + +#if defined(CONFIG_FB_OMAP2) +static struct platform_device omap_display_device = { + .name = "omapdss", + .id = -1, + .dev = { + .platform_data = NULL, + }, +}; + +#define OMAP4_DSIPHY_SYSCON_OFFSET 0x78 + +static struct regmap *omap4_dsi_mux_syscon; + +static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes) +{ + u32 enable_mask, enable_shift; + u32 pipd_mask, pipd_shift; + u32 reg; + int ret; + + if (dsi_id == 0) { + enable_mask = OMAP4_DSI1_LANEENABLE_MASK; + enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT; + pipd_mask = OMAP4_DSI1_PIPD_MASK; + pipd_shift = OMAP4_DSI1_PIPD_SHIFT; + } else if (dsi_id == 1) { + enable_mask = OMAP4_DSI2_LANEENABLE_MASK; + enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT; + pipd_mask = OMAP4_DSI2_PIPD_MASK; + pipd_shift = OMAP4_DSI2_PIPD_SHIFT; + } else { + return -ENODEV; + } + + ret = regmap_read(omap4_dsi_mux_syscon, + OMAP4_DSIPHY_SYSCON_OFFSET, + ®); + if (ret) + return ret; + + reg &= ~enable_mask; + reg &= ~pipd_mask; + + reg |= (lanes << enable_shift) & enable_mask; + reg |= (lanes << pipd_shift) & pipd_mask; + + regmap_write(omap4_dsi_mux_syscon, OMAP4_DSIPHY_SYSCON_OFFSET, reg); + + return 0; +} + +static int omap_dsi_enable_pads(int dsi_id, unsigned lane_mask) +{ + if (cpu_is_omap44xx()) + return omap4_dsi_mux_pads(dsi_id, lane_mask); + + return 0; +} + +static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask) +{ + if (cpu_is_omap44xx()) + omap4_dsi_mux_pads(dsi_id, 0); +} + +static enum omapdss_version __init omap_display_get_version(void) +{ + if (cpu_is_omap24xx()) + return OMAPDSS_VER_OMAP24xx; + else if (cpu_is_omap3630()) + return OMAPDSS_VER_OMAP3630; + else if (cpu_is_omap34xx()) { + if (soc_is_am35xx()) { + return OMAPDSS_VER_AM35xx; + } else { + if (omap_rev() < OMAP3430_REV_ES3_0) + return OMAPDSS_VER_OMAP34xx_ES1; + else + return OMAPDSS_VER_OMAP34xx_ES3; + } + } else if (omap_rev() == OMAP4430_REV_ES1_0) + return OMAPDSS_VER_OMAP4430_ES1; + else if (omap_rev() == OMAP4430_REV_ES2_0 || + omap_rev() == OMAP4430_REV_ES2_1 || + omap_rev() == OMAP4430_REV_ES2_2) + return OMAPDSS_VER_OMAP4430_ES2; + else if (cpu_is_omap44xx()) + return OMAPDSS_VER_OMAP4; + else if (soc_is_omap54xx()) + return OMAPDSS_VER_OMAP5; + else if (soc_is_am43xx()) + return OMAPDSS_VER_AM43xx; + else if (soc_is_dra7xx()) + return OMAPDSS_VER_DRA7xx; + else + return OMAPDSS_VER_UNKNOWN; +} + +static int __init omapdss_init_fbdev(void) +{ + static struct omap_dss_board_info board_data = { + .dsi_enable_pads = omap_dsi_enable_pads, + .dsi_disable_pads = omap_dsi_disable_pads, + }; + struct device_node *node; + int r; + + board_data.version = omap_display_get_version(); + if (board_data.version == OMAPDSS_VER_UNKNOWN) { + pr_err("DSS not supported on this SoC\n"); + return -ENODEV; + } + + omap_display_device.dev.platform_data = &board_data; + + r = platform_device_register(&omap_display_device); + if (r < 0) { + pr_err("Unable to register omapdss device\n"); + return r; + } + + /* create vrfb device */ + r = omap_init_vrfb(); + if (r < 0) { + pr_err("Unable to register omapvrfb device\n"); + return r; + } + + /* create FB device */ + r = omap_init_fb(); + if (r < 0) { + pr_err("Unable to register omapfb device\n"); + return r; + } + + /* create V4L2 display device */ + r = omap_init_vout(); + if (r < 0) { + pr_err("Unable to register omap_vout device\n"); + return r; + } + + /* add DSI info for omap4 */ + node = of_find_node_by_name(NULL, "omap4_padconf_global"); + if (node) + omap4_dsi_mux_syscon = syscon_node_to_regmap(node); + of_node_put(node); + + return 0; +} + +static const char * const omapdss_compat_names[] __initconst = { + "ti,omap2-dss", + "ti,omap3-dss", + "ti,omap4-dss", + "ti,omap5-dss", + "ti,dra7-dss", +}; + +static struct device_node * __init omapdss_find_dss_of_node(void) +{ + struct device_node *node; + int i; + + for (i = 0; i < ARRAY_SIZE(omapdss_compat_names); ++i) { + node = of_find_compatible_node(NULL, NULL, + omapdss_compat_names[i]); + if (node) + return node; + } + + return NULL; +} + +static int __init omapdss_init_of(void) +{ + int r; + struct device_node *node; + struct platform_device *pdev; + + /* only create dss helper devices if dss is enabled in the .dts */ + + node = omapdss_find_dss_of_node(); + if (!node) + return 0; + + if (!of_device_is_available(node)) { + of_node_put(node); + return 0; + } + + pdev = of_find_device_by_node(node); + + if (!pdev) { + pr_err("Unable to find DSS platform device\n"); + of_node_put(node); + return -ENODEV; + } + + r = of_platform_populate(node, NULL, NULL, &pdev->dev); + put_device(&pdev->dev); + of_node_put(node); + if (r) { + pr_err("Unable to populate DSS submodule devices\n"); + return r; + } + + return omapdss_init_fbdev(); +} +omap_device_initcall(omapdss_init_of); +#endif /* CONFIG_FB_OMAP2 */ + +static void dispc_disable_outputs(void) +{ + u32 v, irq_mask = 0; + bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false; + int i; + struct omap_dss_dispc_dev_attr *da; + struct omap_hwmod *oh; + + oh = omap_hwmod_lookup("dss_dispc"); + if (!oh) { + WARN(1, "display: could not disable outputs during reset - could not find dss_dispc hwmod\n"); + return; + } + + if (!oh->dev_attr) { + pr_err("display: could not disable outputs during reset due to missing dev_attr\n"); + return; + } + + da = (struct omap_dss_dispc_dev_attr *)oh->dev_attr; + + /* store value of LCDENABLE and DIGITENABLE bits */ + v = omap_hwmod_read(oh, DISPC_CONTROL); + lcd_en = v & LCD_EN_MASK; + digit_en = v & DIGIT_EN_MASK; + + /* store value of LCDENABLE for LCD2 */ + if (da->manager_count > 2) { + v = omap_hwmod_read(oh, DISPC_CONTROL2); + lcd2_en = v & LCD_EN_MASK; + } + + /* store value of LCDENABLE for LCD3 */ + if (da->manager_count > 3) { + v = omap_hwmod_read(oh, DISPC_CONTROL3); + lcd3_en = v & LCD_EN_MASK; + } + + if (!(lcd_en | digit_en | lcd2_en | lcd3_en)) + return; /* no managers currently enabled */ + + /* + * If any manager was enabled, we need to disable it before + * DSS clocks are disabled or DISPC module is reset + */ + if (lcd_en) + irq_mask |= 1 << FRAMEDONE_IRQ_SHIFT; + + if (digit_en) { + if (da->has_framedonetv_irq) { + irq_mask |= 1 << FRAMEDONETV_IRQ_SHIFT; + } else { + irq_mask |= 1 << EVSYNC_EVEN_IRQ_SHIFT | + 1 << EVSYNC_ODD_IRQ_SHIFT; + } + } + + if (lcd2_en) + irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT; + if (lcd3_en) + irq_mask |= 1 << FRAMEDONE3_IRQ_SHIFT; + + /* + * clear any previous FRAMEDONE, FRAMEDONETV, + * EVSYNC_EVEN/ODD, FRAMEDONE2 or FRAMEDONE3 interrupts + */ + omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS); + + /* disable LCD and TV managers */ + v = omap_hwmod_read(oh, DISPC_CONTROL); + v &= ~(LCD_EN_MASK | DIGIT_EN_MASK); + omap_hwmod_write(v, oh, DISPC_CONTROL); + + /* disable LCD2 manager */ + if (da->manager_count > 2) { + v = omap_hwmod_read(oh, DISPC_CONTROL2); + v &= ~LCD_EN_MASK; + omap_hwmod_write(v, oh, DISPC_CONTROL2); + } + + /* disable LCD3 manager */ + if (da->manager_count > 3) { + v = omap_hwmod_read(oh, DISPC_CONTROL3); + v &= ~LCD_EN_MASK; + omap_hwmod_write(v, oh, DISPC_CONTROL3); + } + + i = 0; + while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) != + irq_mask) { + i++; + if (i > FRAMEDONE_IRQ_TIMEOUT) { + pr_err("didn't get FRAMEDONE1/2/3 or TV interrupt\n"); + break; + } + mdelay(1); + } +} + +int omap_dss_reset(struct omap_hwmod *oh) +{ + struct omap_hwmod_opt_clk *oc; + int c = 0; + int i, r; + + if (!(oh->class->sysc->sysc_flags & SYSS_HAS_RESET_STATUS)) { + pr_err("dss_core: hwmod data doesn't contain reset data\n"); + return -EINVAL; + } + + for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) + clk_prepare_enable(oc->_clk); + + dispc_disable_outputs(); + + /* clear SDI registers */ + if (cpu_is_omap3430()) { + omap_hwmod_write(0x0, oh, DSS_SDI_CONTROL); + omap_hwmod_write(0x0, oh, DSS_PLL_CONTROL); + } + + /* + * clear DSS_CONTROL register to switch DSS clock sources to + * PRCM clock, if any + */ + omap_hwmod_write(0x0, oh, DSS_CONTROL); + + omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs) + & SYSS_RESETDONE_MASK), + MAX_MODULE_SOFTRESET_WAIT, c); + + if (c == MAX_MODULE_SOFTRESET_WAIT) + pr_warn("dss_core: waiting for reset to finish failed\n"); + else + pr_debug("dss_core: softreset done\n"); + + for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) + clk_disable_unprepare(oc->_clk); + + r = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0; + + return r; +} diff --git a/arch/arm/mach-omap2/display.h b/arch/arm/mach-omap2/display.h new file mode 100644 index 000000000..22ffca68f --- /dev/null +++ b/arch/arm/mach-omap2/display.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * display.h - OMAP2+ integration-specific DSS header + * + * Copyright (C) 2011 Texas Instruments, Inc. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_DISPLAY_H +#define __ARCH_ARM_MACH_OMAP2_DISPLAY_H + +#include <linux/kernel.h> + +struct omap_dss_dispc_dev_attr { + u8 manager_count; + bool has_framedonetv_irq; +}; + +int omap_init_vrfb(void); +int omap_init_fb(void); +int omap_init_vout(void); + +#endif diff --git a/arch/arm/mach-omap2/dma.c b/arch/arm/mach-omap2/dma.c new file mode 100644 index 000000000..830cd4e7e --- /dev/null +++ b/arch/arm/mach-omap2/dma.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2+ DMA driver + * + * Copyright (C) 2003 - 2008 Nokia Corporation + * Author: Juha Yrjölä <juha.yrjola@nokia.com> + * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> + * Graphics DMA and LCD DMA graphics tranformations + * by Imre Deak <imre.deak@nokia.com> + * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc. + * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. + * + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * Converted DMA library into platform driver + * - G, Manjunath Kondaiah <manjugk@ti.com> + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/of.h> +#include <linux/omap-dma.h> + +#include "soc.h" +#include "common.h" + +static const struct omap_dma_reg reg_map[] = { + [REVISION] = { 0x0000, 0x00, OMAP_DMA_REG_32BIT }, + [GCR] = { 0x0078, 0x00, OMAP_DMA_REG_32BIT }, + [IRQSTATUS_L0] = { 0x0008, 0x00, OMAP_DMA_REG_32BIT }, + [IRQSTATUS_L1] = { 0x000c, 0x00, OMAP_DMA_REG_32BIT }, + [IRQSTATUS_L2] = { 0x0010, 0x00, OMAP_DMA_REG_32BIT }, + [IRQSTATUS_L3] = { 0x0014, 0x00, OMAP_DMA_REG_32BIT }, + [IRQENABLE_L0] = { 0x0018, 0x00, OMAP_DMA_REG_32BIT }, + [IRQENABLE_L1] = { 0x001c, 0x00, OMAP_DMA_REG_32BIT }, + [IRQENABLE_L2] = { 0x0020, 0x00, OMAP_DMA_REG_32BIT }, + [IRQENABLE_L3] = { 0x0024, 0x00, OMAP_DMA_REG_32BIT }, + [SYSSTATUS] = { 0x0028, 0x00, OMAP_DMA_REG_32BIT }, + [OCP_SYSCONFIG] = { 0x002c, 0x00, OMAP_DMA_REG_32BIT }, + [CAPS_0] = { 0x0064, 0x00, OMAP_DMA_REG_32BIT }, + [CAPS_2] = { 0x006c, 0x00, OMAP_DMA_REG_32BIT }, + [CAPS_3] = { 0x0070, 0x00, OMAP_DMA_REG_32BIT }, + [CAPS_4] = { 0x0074, 0x00, OMAP_DMA_REG_32BIT }, + + /* Common register offsets */ + [CCR] = { 0x0080, 0x60, OMAP_DMA_REG_32BIT }, + [CLNK_CTRL] = { 0x0084, 0x60, OMAP_DMA_REG_32BIT }, + [CICR] = { 0x0088, 0x60, OMAP_DMA_REG_32BIT }, + [CSR] = { 0x008c, 0x60, OMAP_DMA_REG_32BIT }, + [CSDP] = { 0x0090, 0x60, OMAP_DMA_REG_32BIT }, + [CEN] = { 0x0094, 0x60, OMAP_DMA_REG_32BIT }, + [CFN] = { 0x0098, 0x60, OMAP_DMA_REG_32BIT }, + [CSEI] = { 0x00a4, 0x60, OMAP_DMA_REG_32BIT }, + [CSFI] = { 0x00a8, 0x60, OMAP_DMA_REG_32BIT }, + [CDEI] = { 0x00ac, 0x60, OMAP_DMA_REG_32BIT }, + [CDFI] = { 0x00b0, 0x60, OMAP_DMA_REG_32BIT }, + [CSAC] = { 0x00b4, 0x60, OMAP_DMA_REG_32BIT }, + [CDAC] = { 0x00b8, 0x60, OMAP_DMA_REG_32BIT }, + + /* Channel specific register offsets */ + [CSSA] = { 0x009c, 0x60, OMAP_DMA_REG_32BIT }, + [CDSA] = { 0x00a0, 0x60, OMAP_DMA_REG_32BIT }, + [CCEN] = { 0x00bc, 0x60, OMAP_DMA_REG_32BIT }, + [CCFN] = { 0x00c0, 0x60, OMAP_DMA_REG_32BIT }, + [COLOR] = { 0x00c4, 0x60, OMAP_DMA_REG_32BIT }, + + /* OMAP4 specific registers */ + [CDP] = { 0x00d0, 0x60, OMAP_DMA_REG_32BIT }, + [CNDP] = { 0x00d4, 0x60, OMAP_DMA_REG_32BIT }, + [CCDN] = { 0x00d8, 0x60, OMAP_DMA_REG_32BIT }, +}; + +static unsigned configure_dma_errata(void) +{ + unsigned errata = 0; + + /* + * Errata applicable for OMAP2430ES1.0 and all omap2420 + * + * I. + * Erratum ID: Not Available + * Inter Frame DMA buffering issue DMA will wrongly + * buffer elements if packing and bursting is enabled. This might + * result in data gets stalled in FIFO at the end of the block. + * Workaround: DMA channels must have BUFFERING_DISABLED bit set to + * guarantee no data will stay in the DMA FIFO in case inter frame + * buffering occurs + * + * II. + * Erratum ID: Not Available + * DMA may hang when several channels are used in parallel + * In the following configuration, DMA channel hanging can occur: + * a. Channel i, hardware synchronized, is enabled + * b. Another channel (Channel x), software synchronized, is enabled. + * c. Channel i is disabled before end of transfer + * d. Channel i is reenabled. + * e. Steps 1 to 4 are repeated a certain number of times. + * f. A third channel (Channel y), software synchronized, is enabled. + * Channel x and Channel y may hang immediately after step 'f'. + * Workaround: + * For any channel used - make sure NextLCH_ID is set to the value j. + */ + if (cpu_is_omap2420() || (cpu_is_omap2430() && + (omap_type() == OMAP2430_REV_ES1_0))) { + + SET_DMA_ERRATA(DMA_ERRATA_IFRAME_BUFFERING); + SET_DMA_ERRATA(DMA_ERRATA_PARALLEL_CHANNELS); + } + + /* + * Erratum ID: i378: OMAP2+: sDMA Channel is not disabled + * after a transaction error. + * Workaround: SW should explicitely disable the channel. + */ + if (cpu_class_is_omap2()) + SET_DMA_ERRATA(DMA_ERRATA_i378); + + /* + * Erratum ID: i541: sDMA FIFO draining does not finish + * If sDMA channel is disabled on the fly, sDMA enters standby even + * through FIFO Drain is still in progress + * Workaround: Put sDMA in NoStandby more before a logical channel is + * disabled, then put it back to SmartStandby right after the channel + * finishes FIFO draining. + */ + if (cpu_is_omap34xx()) + SET_DMA_ERRATA(DMA_ERRATA_i541); + + /* + * Erratum ID: i88 : Special programming model needed to disable DMA + * before end of block. + * Workaround: software must ensure that the DMA is configured in No + * Standby mode(DMAx_OCP_SYSCONFIG.MIDLEMODE = "01") + */ + if (omap_type() == OMAP3430_REV_ES1_0) + SET_DMA_ERRATA(DMA_ERRATA_i88); + + /* + * Erratum 3.2/3.3: sometimes 0 is returned if CSAC/CDAC is + * read before the DMA controller finished disabling the channel. + */ + SET_DMA_ERRATA(DMA_ERRATA_3_3); + + /* + * Erratum ID: Not Available + * A bug in ROM code leaves IRQ status for channels 0 and 1 uncleared + * after secure sram context save and restore. + * Work around: Hence we need to manually clear those IRQs to avoid + * spurious interrupts. This affects only secure devices. + */ + if (cpu_is_omap34xx() && (omap_type() != OMAP2_DEVICE_TYPE_GP)) + SET_DMA_ERRATA(DMA_ROMCODE_BUG); + + return errata; +} + +static const struct dma_slave_map omap24xx_sdma_dt_map[] = { + /* external DMA requests when tusb6010 is used */ + { "musb-hdrc.1.auto", "dmareq0", SDMA_FILTER_PARAM(2) }, + { "musb-hdrc.1.auto", "dmareq1", SDMA_FILTER_PARAM(3) }, + { "musb-hdrc.1.auto", "dmareq2", SDMA_FILTER_PARAM(14) }, /* OMAP2420 only */ + { "musb-hdrc.1.auto", "dmareq3", SDMA_FILTER_PARAM(15) }, /* OMAP2420 only */ + { "musb-hdrc.1.auto", "dmareq4", SDMA_FILTER_PARAM(16) }, /* OMAP2420 only */ + { "musb-hdrc.1.auto", "dmareq5", SDMA_FILTER_PARAM(64) }, /* OMAP2420 only */ +}; + +static struct omap_dma_dev_attr dma_attr = { + .dev_caps = RESERVE_CHANNEL | DMA_LINKED_LCH | GLOBAL_PRIORITY | + IS_CSSA_32 | IS_CDSA_32, + .lch_count = 32, +}; + +struct omap_system_dma_plat_info dma_plat_info = { + .reg_map = reg_map, + .channel_stride = 0x60, + .dma_attr = &dma_attr, +}; + +/* One time initializations */ +static int __init omap2_system_dma_init(void) +{ + dma_plat_info.errata = configure_dma_errata(); + + if (soc_is_omap24xx()) { + /* DMA slave map for drivers not yet converted to DT */ + dma_plat_info.slave_map = omap24xx_sdma_dt_map; + dma_plat_info.slavecnt = ARRAY_SIZE(omap24xx_sdma_dt_map); + } + + if (!soc_is_omap242x()) + dma_attr.dev_caps |= IS_RW_PRIORITY; + + if (soc_is_omap34xx() && (omap_type() != OMAP2_DEVICE_TYPE_GP)) + dma_attr.dev_caps |= HS_CHANNELS_RESERVED; + + return 0; +} +omap_arch_initcall(omap2_system_dma_init); diff --git a/arch/arm/mach-omap2/fb.c b/arch/arm/mach-omap2/fb.c new file mode 100644 index 000000000..0f8c8efe3 --- /dev/null +++ b/arch/arm/mach-omap2/fb.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Framebuffer device registration for TI OMAP platforms + * + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/memblock.h> +#include <linux/io.h> +#include <linux/omapfb.h> +#include <linux/dma-mapping.h> + +#include <asm/mach/map.h> + +#include "soc.h" +#include "display.h" + +#ifdef CONFIG_OMAP2_VRFB + +/* + * The first memory resource is the register region for VRFB, + * the rest are VRFB virtual memory areas for each VRFB context. + */ + +static const struct resource omap2_vrfb_resources[] = { + DEFINE_RES_MEM_NAMED(0x68008000u, 0x40, "vrfb-regs"), + DEFINE_RES_MEM_NAMED(0x70000000u, 0x4000000, "vrfb-area-0"), + DEFINE_RES_MEM_NAMED(0x74000000u, 0x4000000, "vrfb-area-1"), + DEFINE_RES_MEM_NAMED(0x78000000u, 0x4000000, "vrfb-area-2"), + DEFINE_RES_MEM_NAMED(0x7c000000u, 0x4000000, "vrfb-area-3"), +}; + +static const struct resource omap3_vrfb_resources[] = { + DEFINE_RES_MEM_NAMED(0x6C000180u, 0xc0, "vrfb-regs"), + DEFINE_RES_MEM_NAMED(0x70000000u, 0x4000000, "vrfb-area-0"), + DEFINE_RES_MEM_NAMED(0x74000000u, 0x4000000, "vrfb-area-1"), + DEFINE_RES_MEM_NAMED(0x78000000u, 0x4000000, "vrfb-area-2"), + DEFINE_RES_MEM_NAMED(0x7c000000u, 0x4000000, "vrfb-area-3"), + DEFINE_RES_MEM_NAMED(0xe0000000u, 0x4000000, "vrfb-area-4"), + DEFINE_RES_MEM_NAMED(0xe4000000u, 0x4000000, "vrfb-area-5"), + DEFINE_RES_MEM_NAMED(0xe8000000u, 0x4000000, "vrfb-area-6"), + DEFINE_RES_MEM_NAMED(0xec000000u, 0x4000000, "vrfb-area-7"), + DEFINE_RES_MEM_NAMED(0xf0000000u, 0x4000000, "vrfb-area-8"), + DEFINE_RES_MEM_NAMED(0xf4000000u, 0x4000000, "vrfb-area-9"), + DEFINE_RES_MEM_NAMED(0xf8000000u, 0x4000000, "vrfb-area-10"), + DEFINE_RES_MEM_NAMED(0xfc000000u, 0x4000000, "vrfb-area-11"), +}; + +int __init omap_init_vrfb(void) +{ + struct platform_device *pdev; + const struct resource *res; + unsigned int num_res; + + if (cpu_is_omap24xx()) { + res = omap2_vrfb_resources; + num_res = ARRAY_SIZE(omap2_vrfb_resources); + } else if (cpu_is_omap34xx()) { + res = omap3_vrfb_resources; + num_res = ARRAY_SIZE(omap3_vrfb_resources); + } else { + return 0; + } + + pdev = platform_device_register_resndata(NULL, "omapvrfb", -1, + res, num_res, NULL, 0); + + return PTR_ERR_OR_ZERO(pdev); +} +#else +int __init omap_init_vrfb(void) { return 0; } +#endif + +#if IS_ENABLED(CONFIG_FB_OMAP2) + +static u64 omap_fb_dma_mask = ~(u32)0; +static struct omapfb_platform_data omapfb_config; + +static struct platform_device omap_fb_device = { + .name = "omapfb", + .id = -1, + .dev = { + .dma_mask = &omap_fb_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &omapfb_config, + }, + .num_resources = 0, +}; + +int __init omap_init_fb(void) +{ + return platform_device_register(&omap_fb_device); +} +#else +int __init omap_init_fb(void) { return 0; } +#endif diff --git a/arch/arm/mach-omap2/gpmc.h b/arch/arm/mach-omap2/gpmc.h new file mode 100644 index 000000000..e060f1b5c --- /dev/null +++ b/arch/arm/mach-omap2/gpmc.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * General-Purpose Memory Controller for OMAP2 + * + * Copyright (C) 2005-2006 Nokia Corporation + * + * Do not include this file in any new code, this will get removed + * once omap3 boots in device tree only mode. + */ +#include <linux/omap-gpmc.h> diff --git a/arch/arm/mach-omap2/hdq1w.c b/arch/arm/mach-omap2/hdq1w.c new file mode 100644 index 000000000..c0127f2c5 --- /dev/null +++ b/arch/arm/mach-omap2/hdq1w.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IP block integration code for the HDQ1W/1-wire IP block + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * Based on the I2C reset code in arch/arm/mach-omap2/i2c.c by + * Avinash.H.M <avinashhm@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> + +#include "soc.h" +#include "omap_hwmod.h" +#include "omap_device.h" +#include "hdq1w.h" + +#include "prm.h" +#include "common.h" + +/** + * omap_hdq1w_reset - reset the OMAP HDQ1W module + * @oh: struct omap_hwmod * + * + * OCP soft reset the HDQ1W IP block. Section 20.6.1.4 "HDQ1W/1-Wire + * Software Reset" of the OMAP34xx Technical Reference Manual Revision + * ZR (SWPU223R) does not include the rather important fact that, for + * the reset to succeed, the HDQ1W module's internal clock gate must be + * programmed to allow the clock to propagate to the rest of the + * module. In this sense, it's rather similar to the I2C custom reset + * function. Returns 0. + */ +int omap_hdq1w_reset(struct omap_hwmod *oh) +{ + u32 v; + int c = 0; + + /* Write to the SOFTRESET bit */ + omap_hwmod_softreset(oh); + + /* Enable the module's internal clocks */ + v = omap_hwmod_read(oh, HDQ_CTRL_STATUS_OFFSET); + v |= 1 << HDQ_CTRL_STATUS_CLOCKENABLE_SHIFT; + omap_hwmod_write(v, oh, HDQ_CTRL_STATUS_OFFSET); + + /* Poll on RESETDONE bit */ + omap_test_timeout((omap_hwmod_read(oh, + oh->class->sysc->syss_offs) + & SYSS_RESETDONE_MASK), + MAX_MODULE_SOFTRESET_WAIT, c); + + if (c == MAX_MODULE_SOFTRESET_WAIT) + pr_warn("%s: %s: softreset failed (waited %d usec)\n", + __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT); + else + pr_debug("%s: %s: softreset in %d usec\n", __func__, + oh->name, c); + + return 0; +} diff --git a/arch/arm/mach-omap2/hdq1w.h b/arch/arm/mach-omap2/hdq1w.h new file mode 100644 index 000000000..2e2f1e88a --- /dev/null +++ b/arch/arm/mach-omap2/hdq1w.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Shared macros and function prototypes for the HDQ1W/1-wire IP block + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + */ +#ifndef ARCH_ARM_MACH_OMAP2_HDQ1W_H +#define ARCH_ARM_MACH_OMAP2_HDQ1W_H + +#include "omap_hwmod.h" + +/* + * XXX A future cleanup patch should modify + * drivers/w1/masters/omap_hdq.c to use these macros + */ +#define HDQ_CTRL_STATUS_OFFSET 0x0c +#define HDQ_CTRL_STATUS_CLOCKENABLE_SHIFT 5 + + +extern int omap_hdq1w_reset(struct omap_hwmod *oh); + +#endif diff --git a/arch/arm/mach-omap2/i2c.c b/arch/arm/mach-omap2/i2c.c new file mode 100644 index 000000000..3b4b9514f --- /dev/null +++ b/arch/arm/mach-omap2/i2c.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Helper module for board specific I2C bus registration + * + * Copyright (C) 2009 Nokia Corporation. + */ + +#include "soc.h" +#include "omap_hwmod.h" +#include "omap_device.h" + +#include "prm.h" +#include "common.h" +#include "i2c.h" + +/* In register I2C_CON, Bit 15 is the I2C enable bit */ +#define I2C_EN BIT(15) +#define OMAP2_I2C_CON_OFFSET 0x24 +#define OMAP4_I2C_CON_OFFSET 0xA4 + +#define MAX_OMAP_I2C_HWMOD_NAME_LEN 16 + +/** + * omap_i2c_reset - reset the omap i2c module. + * @oh: struct omap_hwmod * + * + * The i2c moudle in omap2, omap3 had a special sequence to reset. The + * sequence is: + * - Disable the I2C. + * - Write to SOFTRESET bit. + * - Enable the I2C. + * - Poll on the RESETDONE bit. + * The sequence is implemented in below function. This is called for 2420, + * 2430 and omap3. + */ +int omap_i2c_reset(struct omap_hwmod *oh) +{ + u32 v; + u16 i2c_con; + int c = 0; + + if (soc_is_omap24xx() || soc_is_omap34xx() || soc_is_am35xx()) + i2c_con = OMAP2_I2C_CON_OFFSET; + else + i2c_con = OMAP4_I2C_CON_OFFSET; + + /* Disable I2C */ + v = omap_hwmod_read(oh, i2c_con); + v &= ~I2C_EN; + omap_hwmod_write(v, oh, i2c_con); + + /* Write to the SOFTRESET bit */ + omap_hwmod_softreset(oh); + + /* Enable I2C */ + v = omap_hwmod_read(oh, i2c_con); + v |= I2C_EN; + omap_hwmod_write(v, oh, i2c_con); + + /* Poll on RESETDONE bit */ + omap_test_timeout((omap_hwmod_read(oh, + oh->class->sysc->syss_offs) + & SYSS_RESETDONE_MASK), + MAX_MODULE_SOFTRESET_WAIT, c); + + if (c == MAX_MODULE_SOFTRESET_WAIT) + pr_warn("%s: %s: softreset failed (waited %d usec)\n", + __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT); + else + pr_debug("%s: %s: softreset in %d usec\n", __func__, + oh->name, c); + + return 0; +} diff --git a/arch/arm/mach-omap2/i2c.h b/arch/arm/mach-omap2/i2c.h new file mode 100644 index 000000000..198516927 --- /dev/null +++ b/arch/arm/mach-omap2/i2c.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Helper module for board specific I2C bus registration + * + * Copyright (C) 2009 Nokia Corporation. + */ + +#ifndef __MACH_OMAP2_I2C_H +#define __MACH_OMAP2_I2C_H + +struct omap_hwmod; + +int omap_i2c_reset(struct omap_hwmod *oh); + +#endif /* __MACH_OMAP2_I2C_H */ diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c new file mode 100644 index 000000000..75091aa72 --- /dev/null +++ b/arch/arm/mach-omap2/id.c @@ -0,0 +1,810 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/id.c + * + * OMAP2 CPU identification code + * + * Copyright (C) 2005 Nokia Corporation + * Written by Tony Lindgren <tony@atomide.com> + * + * Copyright (C) 2009-11 Texas Instruments + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/random.h> +#include <linux/slab.h> + +#ifdef CONFIG_SOC_BUS +#include <linux/sys_soc.h> +#endif + +#include <asm/cputype.h> + +#include "common.h" + +#include "id.h" + +#include "soc.h" +#include "control.h" + +#define OMAP4_SILICON_TYPE_STANDARD 0x01 +#define OMAP4_SILICON_TYPE_PERFORMANCE 0x02 + +#define OMAP_SOC_MAX_NAME_LENGTH 16 + +static unsigned int omap_revision; +static char soc_name[OMAP_SOC_MAX_NAME_LENGTH]; +static char soc_rev[OMAP_SOC_MAX_NAME_LENGTH]; +u32 omap_features; + +unsigned int omap_rev(void) +{ + return omap_revision; +} +EXPORT_SYMBOL(omap_rev); + +int omap_type(void) +{ + static u32 val = OMAP2_DEVICETYPE_MASK; + + if (val < OMAP2_DEVICETYPE_MASK) + return val; + + if (soc_is_omap24xx()) { + val = omap_ctrl_readl(OMAP24XX_CONTROL_STATUS); + } else if (soc_is_ti81xx()) { + val = omap_ctrl_readl(TI81XX_CONTROL_STATUS); + } else if (soc_is_am33xx() || soc_is_am43xx()) { + val = omap_ctrl_readl(AM33XX_CONTROL_STATUS); + } else if (soc_is_omap34xx()) { + val = omap_ctrl_readl(OMAP343X_CONTROL_STATUS); + } else if (soc_is_omap44xx()) { + val = omap_ctrl_readl(OMAP4_CTRL_MODULE_CORE_STATUS); + } else if (soc_is_omap54xx() || soc_is_dra7xx()) { + val = omap_ctrl_readl(OMAP5XXX_CONTROL_STATUS); + val &= OMAP5_DEVICETYPE_MASK; + val >>= 6; + goto out; + } else { + pr_err("Cannot detect omap type!\n"); + goto out; + } + + val &= OMAP2_DEVICETYPE_MASK; + val >>= 8; + +out: + return val; +} +EXPORT_SYMBOL(omap_type); + + +/*----------------------------------------------------------------------------*/ + +#define OMAP_TAP_IDCODE 0x0204 +#define OMAP_TAP_DIE_ID_0 0x0218 +#define OMAP_TAP_DIE_ID_1 0x021C +#define OMAP_TAP_DIE_ID_2 0x0220 +#define OMAP_TAP_DIE_ID_3 0x0224 + +#define OMAP_TAP_DIE_ID_44XX_0 0x0200 +#define OMAP_TAP_DIE_ID_44XX_1 0x0208 +#define OMAP_TAP_DIE_ID_44XX_2 0x020c +#define OMAP_TAP_DIE_ID_44XX_3 0x0210 + +#define read_tap_reg(reg) readl_relaxed(tap_base + (reg)) + +struct omap_id { + u16 hawkeye; /* Silicon type (Hawkeye id) */ + u8 dev; /* Device type from production_id reg */ + u32 type; /* Combined type id copied to omap_revision */ +}; + +/* Register values to detect the OMAP version */ +static struct omap_id omap_ids[] __initdata = { + { .hawkeye = 0xb5d9, .dev = 0x0, .type = 0x24200024 }, + { .hawkeye = 0xb5d9, .dev = 0x1, .type = 0x24201024 }, + { .hawkeye = 0xb5d9, .dev = 0x2, .type = 0x24202024 }, + { .hawkeye = 0xb5d9, .dev = 0x4, .type = 0x24220024 }, + { .hawkeye = 0xb5d9, .dev = 0x8, .type = 0x24230024 }, + { .hawkeye = 0xb68a, .dev = 0x0, .type = 0x24300024 }, +}; + +static void __iomem *tap_base; +static u16 tap_prod_id; + +void omap_get_die_id(struct omap_die_id *odi) +{ + if (soc_is_omap44xx() || soc_is_omap54xx() || soc_is_dra7xx()) { + odi->id_0 = read_tap_reg(OMAP_TAP_DIE_ID_44XX_0); + odi->id_1 = read_tap_reg(OMAP_TAP_DIE_ID_44XX_1); + odi->id_2 = read_tap_reg(OMAP_TAP_DIE_ID_44XX_2); + odi->id_3 = read_tap_reg(OMAP_TAP_DIE_ID_44XX_3); + + return; + } + odi->id_0 = read_tap_reg(OMAP_TAP_DIE_ID_0); + odi->id_1 = read_tap_reg(OMAP_TAP_DIE_ID_1); + odi->id_2 = read_tap_reg(OMAP_TAP_DIE_ID_2); + odi->id_3 = read_tap_reg(OMAP_TAP_DIE_ID_3); +} + +static int __init omap_feed_randpool(void) +{ + struct omap_die_id odi; + + /* Throw the die ID into the entropy pool at boot */ + omap_get_die_id(&odi); + add_device_randomness(&odi, sizeof(odi)); + return 0; +} +omap_device_initcall(omap_feed_randpool); + +void __init omap2xxx_check_revision(void) +{ + int i, j; + u32 idcode, prod_id; + u16 hawkeye; + u8 dev_type, rev; + struct omap_die_id odi; + + idcode = read_tap_reg(OMAP_TAP_IDCODE); + prod_id = read_tap_reg(tap_prod_id); + hawkeye = (idcode >> 12) & 0xffff; + rev = (idcode >> 28) & 0x0f; + dev_type = (prod_id >> 16) & 0x0f; + omap_get_die_id(&odi); + + pr_debug("OMAP_TAP_IDCODE 0x%08x REV %i HAWKEYE 0x%04x MANF %03x\n", + idcode, rev, hawkeye, (idcode >> 1) & 0x7ff); + pr_debug("OMAP_TAP_DIE_ID_0: 0x%08x\n", odi.id_0); + pr_debug("OMAP_TAP_DIE_ID_1: 0x%08x DEV_REV: %i\n", + odi.id_1, (odi.id_1 >> 28) & 0xf); + pr_debug("OMAP_TAP_DIE_ID_2: 0x%08x\n", odi.id_2); + pr_debug("OMAP_TAP_DIE_ID_3: 0x%08x\n", odi.id_3); + pr_debug("OMAP_TAP_PROD_ID_0: 0x%08x DEV_TYPE: %i\n", + prod_id, dev_type); + + /* Check hawkeye ids */ + for (i = 0; i < ARRAY_SIZE(omap_ids); i++) { + if (hawkeye == omap_ids[i].hawkeye) + break; + } + + if (i == ARRAY_SIZE(omap_ids)) { + printk(KERN_ERR "Unknown OMAP CPU id\n"); + return; + } + + for (j = i; j < ARRAY_SIZE(omap_ids); j++) { + if (dev_type == omap_ids[j].dev) + break; + } + + if (j == ARRAY_SIZE(omap_ids)) { + pr_err("Unknown OMAP device type. Handling it as OMAP%04x\n", + omap_ids[i].type >> 16); + j = i; + } + + sprintf(soc_name, "OMAP%04x", omap_rev() >> 16); + sprintf(soc_rev, "ES%x", (omap_rev() >> 12) & 0xf); + + pr_info("%s", soc_name); + if ((omap_rev() >> 8) & 0x0f) + pr_cont("%s", soc_rev); + pr_cont("\n"); +} + +#define OMAP3_SHOW_FEATURE(feat) \ + if (omap3_has_ ##feat()) \ + n += scnprintf(buf + n, sizeof(buf) - n, #feat " "); + +static void __init omap3_cpuinfo(void) +{ + const char *cpu_name; + char buf[64]; + int n = 0; + + memset(buf, 0, sizeof(buf)); + + /* + * OMAP3430 and OMAP3530 are assumed to be same. + * + * OMAP3525, OMAP3515 and OMAP3503 can be detected only based + * on available features. Upon detection, update the CPU id + * and CPU class bits. + */ + if (soc_is_omap3630()) { + if (omap3_has_iva() && omap3_has_sgx()) { + cpu_name = (omap3_has_isp()) ? "OMAP3630/DM3730" : "OMAP3621"; + } else if (omap3_has_iva()) { + cpu_name = "DM3725"; + } else if (omap3_has_sgx()) { + cpu_name = "OMAP3615/AM3715"; + } else { + cpu_name = (omap3_has_isp()) ? "AM3703" : "OMAP3611"; + } + } else if (soc_is_am35xx()) { + cpu_name = (omap3_has_sgx()) ? "AM3517" : "AM3505"; + } else if (soc_is_ti816x()) { + cpu_name = "TI816X"; + } else if (soc_is_am335x()) { + cpu_name = "AM335X"; + } else if (soc_is_am437x()) { + cpu_name = "AM437x"; + } else if (soc_is_ti814x()) { + cpu_name = "TI814X"; + } else if (omap3_has_iva() && omap3_has_sgx()) { + /* OMAP3430, OMAP3525, OMAP3515, OMAP3503 devices */ + cpu_name = "OMAP3430/3530"; + } else if (omap3_has_iva()) { + cpu_name = "OMAP3525"; + } else if (omap3_has_sgx()) { + cpu_name = "OMAP3515"; + } else { + cpu_name = "OMAP3503"; + } + + scnprintf(soc_name, sizeof(soc_name), "%s", cpu_name); + + /* Print verbose information */ + n += scnprintf(buf, sizeof(buf) - n, "%s %s (", soc_name, soc_rev); + + OMAP3_SHOW_FEATURE(l2cache); + OMAP3_SHOW_FEATURE(iva); + OMAP3_SHOW_FEATURE(sgx); + OMAP3_SHOW_FEATURE(neon); + OMAP3_SHOW_FEATURE(isp); + OMAP3_SHOW_FEATURE(192mhz_clk); + if (*(buf + n - 1) == ' ') + n--; + n += scnprintf(buf + n, sizeof(buf) - n, ")\n"); + pr_info("%s", buf); +} + +#define OMAP3_CHECK_FEATURE(status,feat) \ + if (((status & OMAP3_ ##feat## _MASK) \ + >> OMAP3_ ##feat## _SHIFT) != FEAT_ ##feat## _NONE) { \ + omap_features |= OMAP3_HAS_ ##feat; \ + } + +void __init omap3xxx_check_features(void) +{ + u32 status; + + omap_features = 0; + + status = omap_ctrl_readl(OMAP3_CONTROL_OMAP_STATUS); + + OMAP3_CHECK_FEATURE(status, L2CACHE); + OMAP3_CHECK_FEATURE(status, IVA); + OMAP3_CHECK_FEATURE(status, SGX); + OMAP3_CHECK_FEATURE(status, NEON); + OMAP3_CHECK_FEATURE(status, ISP); + if (soc_is_omap3630()) + omap_features |= OMAP3_HAS_192MHZ_CLK; + if (soc_is_omap3430() || soc_is_omap3630()) + omap_features |= OMAP3_HAS_IO_WAKEUP; + if (soc_is_omap3630() || omap_rev() == OMAP3430_REV_ES3_1 || + omap_rev() == OMAP3430_REV_ES3_1_2) + omap_features |= OMAP3_HAS_IO_CHAIN_CTRL; + + omap_features |= OMAP3_HAS_SDRC; + + /* + * am35x fixups: + * - The am35x Chip ID register has bits 12, 7:5, and 3:2 marked as + * reserved and therefore return 0 when read. Unfortunately, + * OMAP3_CHECK_FEATURE() will interpret some of those zeroes to + * mean that a feature is present even though it isn't so clear + * the incorrectly set feature bits. + */ + if (soc_is_am35xx()) + omap_features &= ~(OMAP3_HAS_IVA | OMAP3_HAS_ISP); + + /* + * TODO: Get additional info (where applicable) + * e.g. Size of L2 cache. + */ + + omap3_cpuinfo(); +} + +void __init omap4xxx_check_features(void) +{ + u32 si_type; + + si_type = + (read_tap_reg(OMAP4_CTRL_MODULE_CORE_STD_FUSE_PROD_ID_1) >> 16) & 0x03; + + if (si_type == OMAP4_SILICON_TYPE_PERFORMANCE) + omap_features = OMAP4_HAS_PERF_SILICON; +} + +void __init ti81xx_check_features(void) +{ + omap_features = OMAP3_HAS_NEON; + omap3_cpuinfo(); +} + +void __init am33xx_check_features(void) +{ + u32 status; + + omap_features = OMAP3_HAS_NEON; + + status = omap_ctrl_readl(AM33XX_DEV_FEATURE); + if (status & AM33XX_SGX_MASK) + omap_features |= OMAP3_HAS_SGX; + + omap3_cpuinfo(); +} + +void __init omap3xxx_check_revision(void) +{ + const char *cpu_rev; + u32 cpuid, idcode; + u16 hawkeye; + u8 rev; + + /* + * We cannot access revision registers on ES1.0. + * If the processor type is Cortex-A8 and the revision is 0x0 + * it means its Cortex r0p0 which is 3430 ES1.0. + */ + cpuid = read_cpuid_id(); + if ((((cpuid >> 4) & 0xfff) == 0xc08) && ((cpuid & 0xf) == 0x0)) { + omap_revision = OMAP3430_REV_ES1_0; + cpu_rev = "1.0"; + return; + } + + /* + * Detection for 34xx ES2.0 and above can be done with just + * hawkeye and rev. See TRM 1.5.2 Device Identification. + * Note that rev does not map directly to our defined processor + * revision numbers as ES1.0 uses value 0. + */ + idcode = read_tap_reg(OMAP_TAP_IDCODE); + hawkeye = (idcode >> 12) & 0xffff; + rev = (idcode >> 28) & 0xff; + + switch (hawkeye) { + case 0xb7ae: + /* Handle 34xx/35xx devices */ + switch (rev) { + case 0: /* Take care of early samples */ + case 1: + omap_revision = OMAP3430_REV_ES2_0; + cpu_rev = "2.0"; + break; + case 2: + omap_revision = OMAP3430_REV_ES2_1; + cpu_rev = "2.1"; + break; + case 3: + omap_revision = OMAP3430_REV_ES3_0; + cpu_rev = "3.0"; + break; + case 4: + omap_revision = OMAP3430_REV_ES3_1; + cpu_rev = "3.1"; + break; + case 7: + default: + /* Use the latest known revision as default */ + omap_revision = OMAP3430_REV_ES3_1_2; + cpu_rev = "3.1.2"; + } + break; + case 0xb868: + /* + * Handle OMAP/AM 3505/3517 devices + * + * Set the device to be OMAP3517 here. Actual device + * is identified later based on the features. + */ + switch (rev) { + case 0: + omap_revision = AM35XX_REV_ES1_0; + cpu_rev = "1.0"; + break; + case 1: + default: + omap_revision = AM35XX_REV_ES1_1; + cpu_rev = "1.1"; + } + break; + case 0xb891: + /* Handle 36xx devices */ + + switch(rev) { + case 0: /* Take care of early samples */ + omap_revision = OMAP3630_REV_ES1_0; + cpu_rev = "1.0"; + break; + case 1: + omap_revision = OMAP3630_REV_ES1_1; + cpu_rev = "1.1"; + break; + case 2: + default: + omap_revision = OMAP3630_REV_ES1_2; + cpu_rev = "1.2"; + } + break; + case 0xb81e: + switch (rev) { + case 0: + omap_revision = TI8168_REV_ES1_0; + cpu_rev = "1.0"; + break; + case 1: + omap_revision = TI8168_REV_ES1_1; + cpu_rev = "1.1"; + break; + case 2: + omap_revision = TI8168_REV_ES2_0; + cpu_rev = "2.0"; + break; + case 3: + default: + omap_revision = TI8168_REV_ES2_1; + cpu_rev = "2.1"; + } + break; + case 0xb944: + switch (rev) { + case 0: + omap_revision = AM335X_REV_ES1_0; + cpu_rev = "1.0"; + break; + case 1: + omap_revision = AM335X_REV_ES2_0; + cpu_rev = "2.0"; + break; + case 2: + default: + omap_revision = AM335X_REV_ES2_1; + cpu_rev = "2.1"; + break; + } + break; + case 0xb98c: + switch (rev) { + case 0: + omap_revision = AM437X_REV_ES1_0; + cpu_rev = "1.0"; + break; + case 1: + omap_revision = AM437X_REV_ES1_1; + cpu_rev = "1.1"; + break; + case 2: + default: + omap_revision = AM437X_REV_ES1_2; + cpu_rev = "1.2"; + break; + } + break; + case 0xb8f2: + case 0xb968: + switch (rev) { + case 0: + case 1: + omap_revision = TI8148_REV_ES1_0; + cpu_rev = "1.0"; + break; + case 2: + omap_revision = TI8148_REV_ES2_0; + cpu_rev = "2.0"; + break; + case 3: + default: + omap_revision = TI8148_REV_ES2_1; + cpu_rev = "2.1"; + break; + } + break; + default: + /* Unknown default to latest silicon rev as default */ + omap_revision = OMAP3630_REV_ES1_2; + cpu_rev = "1.2"; + pr_warn("Warning: unknown chip type: hawkeye %04x, assuming OMAP3630ES1.2\n", + hawkeye); + } + sprintf(soc_rev, "ES%s", cpu_rev); +} + +void __init omap4xxx_check_revision(void) +{ + u32 idcode; + u16 hawkeye; + u8 rev; + + /* + * The IC rev detection is done with hawkeye and rev. + * Note that rev does not map directly to defined processor + * revision numbers as ES1.0 uses value 0. + */ + idcode = read_tap_reg(OMAP_TAP_IDCODE); + hawkeye = (idcode >> 12) & 0xffff; + rev = (idcode >> 28) & 0xf; + + /* + * Few initial 4430 ES2.0 samples IDCODE is same as ES1.0 + * Use ARM register to detect the correct ES version + */ + if (!rev && (hawkeye != 0xb94e) && (hawkeye != 0xb975)) { + idcode = read_cpuid_id(); + rev = (idcode & 0xf) - 1; + } + + switch (hawkeye) { + case 0xb852: + switch (rev) { + case 0: + omap_revision = OMAP4430_REV_ES1_0; + break; + case 1: + default: + omap_revision = OMAP4430_REV_ES2_0; + } + break; + case 0xb95c: + switch (rev) { + case 3: + omap_revision = OMAP4430_REV_ES2_1; + break; + case 4: + omap_revision = OMAP4430_REV_ES2_2; + break; + case 6: + default: + omap_revision = OMAP4430_REV_ES2_3; + } + break; + case 0xb94e: + switch (rev) { + case 0: + omap_revision = OMAP4460_REV_ES1_0; + break; + case 2: + default: + omap_revision = OMAP4460_REV_ES1_1; + break; + } + break; + case 0xb975: + switch (rev) { + case 0: + default: + omap_revision = OMAP4470_REV_ES1_0; + break; + } + break; + default: + /* Unknown default to latest silicon rev as default */ + omap_revision = OMAP4430_REV_ES2_3; + } + + sprintf(soc_name, "OMAP%04x", omap_rev() >> 16); + sprintf(soc_rev, "ES%d.%d", (omap_rev() >> 12) & 0xf, + (omap_rev() >> 8) & 0xf); + pr_info("%s %s\n", soc_name, soc_rev); +} + +void __init omap5xxx_check_revision(void) +{ + u32 idcode; + u16 hawkeye; + u8 rev; + + idcode = read_tap_reg(OMAP_TAP_IDCODE); + hawkeye = (idcode >> 12) & 0xffff; + rev = (idcode >> 28) & 0xff; + switch (hawkeye) { + case 0xb942: + switch (rev) { + case 0: + /* No support for ES1.0 Test chip */ + BUG(); + case 1: + default: + omap_revision = OMAP5430_REV_ES2_0; + } + break; + + case 0xb998: + switch (rev) { + case 0: + /* No support for ES1.0 Test chip */ + BUG(); + case 1: + default: + omap_revision = OMAP5432_REV_ES2_0; + } + break; + + default: + /* Unknown default to latest silicon rev as default*/ + omap_revision = OMAP5430_REV_ES2_0; + } + + sprintf(soc_name, "OMAP%04x", omap_rev() >> 16); + sprintf(soc_rev, "ES%d.0", (omap_rev() >> 12) & 0xf); + + pr_info("%s %s\n", soc_name, soc_rev); +} + +void __init dra7xxx_check_revision(void) +{ + u32 idcode; + u16 hawkeye; + u8 rev, package; + struct omap_die_id odi; + + omap_get_die_id(&odi); + package = (odi.id_2 >> 16) & 0x3; + idcode = read_tap_reg(OMAP_TAP_IDCODE); + hawkeye = (idcode >> 12) & 0xffff; + rev = (idcode >> 28) & 0xff; + switch (hawkeye) { + case 0xbb50: + switch (rev) { + case 0: + default: + switch (package) { + case 0x2: + omap_revision = DRA762_ABZ_REV_ES1_0; + break; + case 0x3: + omap_revision = DRA762_ACD_REV_ES1_0; + break; + default: + omap_revision = DRA762_REV_ES1_0; + break; + } + break; + } + break; + + case 0xb990: + switch (rev) { + case 0: + omap_revision = DRA752_REV_ES1_0; + break; + case 1: + omap_revision = DRA752_REV_ES1_1; + break; + case 2: + default: + omap_revision = DRA752_REV_ES2_0; + break; + } + break; + + case 0xb9bc: + switch (rev) { + case 0: + omap_revision = DRA722_REV_ES1_0; + break; + case 1: + omap_revision = DRA722_REV_ES2_0; + break; + case 2: + default: + omap_revision = DRA722_REV_ES2_1; + break; + } + break; + + default: + /* Unknown default to latest silicon rev as default*/ + pr_warn("%s: unknown idcode=0x%08x (hawkeye=0x%08x,rev=0x%x)\n", + __func__, idcode, hawkeye, rev); + omap_revision = DRA752_REV_ES2_0; + } + + sprintf(soc_name, "DRA%03x", omap_rev() >> 16); + sprintf(soc_rev, "ES%d.%d", (omap_rev() >> 12) & 0xf, + (omap_rev() >> 8) & 0xf); + + pr_info("%s %s\n", soc_name, soc_rev); +} + +/* + * Set up things for map_io and processor detection later on. Gets called + * pretty much first thing from board init. For multi-omap, this gets + * cpu_is_omapxxxx() working accurately enough for map_io. Then we'll try to + * detect the exact revision later on in omap2_detect_revision() once map_io + * is done. + */ +void __init omap2_set_globals_tap(u32 class, void __iomem *tap) +{ + omap_revision = class; + tap_base = tap; + + /* XXX What is this intended to do? */ + if (soc_is_omap34xx()) + tap_prod_id = 0x0210; + else + tap_prod_id = 0x0208; +} + +#ifdef CONFIG_SOC_BUS + +static const char * const omap_types[] = { + [OMAP2_DEVICE_TYPE_TEST] = "TST", + [OMAP2_DEVICE_TYPE_EMU] = "EMU", + [OMAP2_DEVICE_TYPE_SEC] = "HS", + [OMAP2_DEVICE_TYPE_GP] = "GP", + [OMAP2_DEVICE_TYPE_BAD] = "BAD", +}; + +static const char * __init omap_get_family(void) +{ + if (soc_is_omap24xx()) + return kasprintf(GFP_KERNEL, "OMAP2"); + else if (soc_is_omap34xx()) + return kasprintf(GFP_KERNEL, "OMAP3"); + else if (soc_is_omap44xx()) + return kasprintf(GFP_KERNEL, "OMAP4"); + else if (soc_is_omap54xx()) + return kasprintf(GFP_KERNEL, "OMAP5"); + else if (soc_is_am33xx() || soc_is_am335x()) + return kasprintf(GFP_KERNEL, "AM33xx"); + else if (soc_is_am43xx()) + return kasprintf(GFP_KERNEL, "AM43xx"); + else if (soc_is_dra7xx()) + return kasprintf(GFP_KERNEL, "DRA7"); + else + return kasprintf(GFP_KERNEL, "Unknown"); +} + +static ssize_t +type_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", omap_types[omap_type()]); +} + +static DEVICE_ATTR_RO(type); + +static struct attribute *omap_soc_attrs[] = { + &dev_attr_type.attr, + NULL +}; + +ATTRIBUTE_GROUPS(omap_soc); + +void __init omap_soc_device_init(void) +{ + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return; + + soc_dev_attr->machine = soc_name; + soc_dev_attr->family = omap_get_family(); + if (!soc_dev_attr->family) { + kfree(soc_dev_attr); + return; + } + soc_dev_attr->revision = soc_rev; + soc_dev_attr->custom_attr_group = omap_soc_groups[0]; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->family); + kfree(soc_dev_attr); + return; + } +} +#endif /* CONFIG_SOC_BUS */ diff --git a/arch/arm/mach-omap2/id.h b/arch/arm/mach-omap2/id.h new file mode 100644 index 000000000..d1735f449 --- /dev/null +++ b/arch/arm/mach-omap2/id.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2 CPU identification code + * + * Copyright (C) 2010 Kan-Ru Chen <kanru@0xlab.org> + */ +#ifndef OMAP2_ARCH_ID_H +#define OMAP2_ARCH_ID_H + +struct omap_die_id { + u32 id_0; + u32 id_1; + u32 id_2; + u32 id_3; +}; + +void omap_get_die_id(struct omap_die_id *odi); + +#endif diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c new file mode 100644 index 000000000..fba0c7aa3 --- /dev/null +++ b/arch/arm/mach-omap2/io.c @@ -0,0 +1,722 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/io.c + * + * OMAP2 I/O mapping code + * + * Copyright (C) 2005 Nokia Corporation + * Copyright (C) 2007-2009 Texas Instruments + * + * Author: + * Juha Yrjola <juha.yrjola@nokia.com> + * Syed Khasim <x0khasim@ti.com> + * + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <asm/tlb.h> +#include <asm/mach/map.h> + +#include <linux/omap-dma.h> + +#include "omap_hwmod.h" +#include "soc.h" +#include "iomap.h" +#include "voltage.h" +#include "powerdomain.h" +#include "clockdomain.h" +#include "common.h" +#include "clock.h" +#include "clock2xxx.h" +#include "clock3xxx.h" +#include "sdrc.h" +#include "control.h" +#include "serial.h" +#include "sram.h" +#include "cm2xxx.h" +#include "cm3xxx.h" +#include "cm33xx.h" +#include "cm44xx.h" +#include "prm.h" +#include "cm.h" +#include "prcm_mpu44xx.h" +#include "prminst44xx.h" +#include "prm2xxx.h" +#include "prm3xxx.h" +#include "prm33xx.h" +#include "prm44xx.h" +#include "opp2xxx.h" +#include "omap-secure.h" + +/* + * omap_clk_soc_init: points to a function that does the SoC-specific + * clock initializations + */ +static int (*omap_clk_soc_init)(void); + +/* + * The machine specific code may provide the extra mapping besides the + * default mapping provided here. + */ + +#if defined(CONFIG_SOC_OMAP2420) || defined(CONFIG_SOC_OMAP2430) +static struct map_desc omap24xx_io_desc[] __initdata = { + { + .virtual = L3_24XX_VIRT, + .pfn = __phys_to_pfn(L3_24XX_PHYS), + .length = L3_24XX_SIZE, + .type = MT_DEVICE + }, + { + .virtual = L4_24XX_VIRT, + .pfn = __phys_to_pfn(L4_24XX_PHYS), + .length = L4_24XX_SIZE, + .type = MT_DEVICE + }, +}; + +#ifdef CONFIG_SOC_OMAP2420 +static struct map_desc omap242x_io_desc[] __initdata = { + { + .virtual = DSP_MEM_2420_VIRT, + .pfn = __phys_to_pfn(DSP_MEM_2420_PHYS), + .length = DSP_MEM_2420_SIZE, + .type = MT_DEVICE + }, + { + .virtual = DSP_IPI_2420_VIRT, + .pfn = __phys_to_pfn(DSP_IPI_2420_PHYS), + .length = DSP_IPI_2420_SIZE, + .type = MT_DEVICE + }, + { + .virtual = DSP_MMU_2420_VIRT, + .pfn = __phys_to_pfn(DSP_MMU_2420_PHYS), + .length = DSP_MMU_2420_SIZE, + .type = MT_DEVICE + }, +}; + +#endif + +#ifdef CONFIG_SOC_OMAP2430 +static struct map_desc omap243x_io_desc[] __initdata = { + { + .virtual = L4_WK_243X_VIRT, + .pfn = __phys_to_pfn(L4_WK_243X_PHYS), + .length = L4_WK_243X_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP243X_GPMC_VIRT, + .pfn = __phys_to_pfn(OMAP243X_GPMC_PHYS), + .length = OMAP243X_GPMC_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP243X_SDRC_VIRT, + .pfn = __phys_to_pfn(OMAP243X_SDRC_PHYS), + .length = OMAP243X_SDRC_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP243X_SMS_VIRT, + .pfn = __phys_to_pfn(OMAP243X_SMS_PHYS), + .length = OMAP243X_SMS_SIZE, + .type = MT_DEVICE + }, +}; +#endif +#endif + +#ifdef CONFIG_ARCH_OMAP3 +static struct map_desc omap34xx_io_desc[] __initdata = { + { + .virtual = L3_34XX_VIRT, + .pfn = __phys_to_pfn(L3_34XX_PHYS), + .length = L3_34XX_SIZE, + .type = MT_DEVICE + }, + { + .virtual = L4_34XX_VIRT, + .pfn = __phys_to_pfn(L4_34XX_PHYS), + .length = L4_34XX_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP34XX_GPMC_VIRT, + .pfn = __phys_to_pfn(OMAP34XX_GPMC_PHYS), + .length = OMAP34XX_GPMC_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP343X_SMS_VIRT, + .pfn = __phys_to_pfn(OMAP343X_SMS_PHYS), + .length = OMAP343X_SMS_SIZE, + .type = MT_DEVICE + }, + { + .virtual = OMAP343X_SDRC_VIRT, + .pfn = __phys_to_pfn(OMAP343X_SDRC_PHYS), + .length = OMAP343X_SDRC_SIZE, + .type = MT_DEVICE + }, + { + .virtual = L4_PER_34XX_VIRT, + .pfn = __phys_to_pfn(L4_PER_34XX_PHYS), + .length = L4_PER_34XX_SIZE, + .type = MT_DEVICE + }, + { + .virtual = L4_EMU_34XX_VIRT, + .pfn = __phys_to_pfn(L4_EMU_34XX_PHYS), + .length = L4_EMU_34XX_SIZE, + .type = MT_DEVICE + }, +}; +#endif + +#ifdef CONFIG_SOC_TI81XX +static struct map_desc omapti81xx_io_desc[] __initdata = { + { + .virtual = L4_34XX_VIRT, + .pfn = __phys_to_pfn(L4_34XX_PHYS), + .length = L4_34XX_SIZE, + .type = MT_DEVICE + } +}; +#endif + +#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_AM43XX) +static struct map_desc omapam33xx_io_desc[] __initdata = { + { + .virtual = L4_34XX_VIRT, + .pfn = __phys_to_pfn(L4_34XX_PHYS), + .length = L4_34XX_SIZE, + .type = MT_DEVICE + }, + { + .virtual = L4_WK_AM33XX_VIRT, + .pfn = __phys_to_pfn(L4_WK_AM33XX_PHYS), + .length = L4_WK_AM33XX_SIZE, + .type = MT_DEVICE + } +}; +#endif + +#ifdef CONFIG_ARCH_OMAP4 +static struct map_desc omap44xx_io_desc[] __initdata = { + { + .virtual = L3_44XX_VIRT, + .pfn = __phys_to_pfn(L3_44XX_PHYS), + .length = L3_44XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_44XX_VIRT, + .pfn = __phys_to_pfn(L4_44XX_PHYS), + .length = L4_44XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_PER_44XX_VIRT, + .pfn = __phys_to_pfn(L4_PER_44XX_PHYS), + .length = L4_PER_44XX_SIZE, + .type = MT_DEVICE, + }, +}; +#endif + +#ifdef CONFIG_SOC_OMAP5 +static struct map_desc omap54xx_io_desc[] __initdata = { + { + .virtual = L3_54XX_VIRT, + .pfn = __phys_to_pfn(L3_54XX_PHYS), + .length = L3_54XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_54XX_VIRT, + .pfn = __phys_to_pfn(L4_54XX_PHYS), + .length = L4_54XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_WK_54XX_VIRT, + .pfn = __phys_to_pfn(L4_WK_54XX_PHYS), + .length = L4_WK_54XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_PER_54XX_VIRT, + .pfn = __phys_to_pfn(L4_PER_54XX_PHYS), + .length = L4_PER_54XX_SIZE, + .type = MT_DEVICE, + }, +}; +#endif + +#ifdef CONFIG_SOC_DRA7XX +static struct map_desc dra7xx_io_desc[] __initdata = { + { + .virtual = L4_CFG_MPU_DRA7XX_VIRT, + .pfn = __phys_to_pfn(L4_CFG_MPU_DRA7XX_PHYS), + .length = L4_CFG_MPU_DRA7XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L3_MAIN_SN_DRA7XX_VIRT, + .pfn = __phys_to_pfn(L3_MAIN_SN_DRA7XX_PHYS), + .length = L3_MAIN_SN_DRA7XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_PER1_DRA7XX_VIRT, + .pfn = __phys_to_pfn(L4_PER1_DRA7XX_PHYS), + .length = L4_PER1_DRA7XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_PER2_DRA7XX_VIRT, + .pfn = __phys_to_pfn(L4_PER2_DRA7XX_PHYS), + .length = L4_PER2_DRA7XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_PER3_DRA7XX_VIRT, + .pfn = __phys_to_pfn(L4_PER3_DRA7XX_PHYS), + .length = L4_PER3_DRA7XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_CFG_DRA7XX_VIRT, + .pfn = __phys_to_pfn(L4_CFG_DRA7XX_PHYS), + .length = L4_CFG_DRA7XX_SIZE, + .type = MT_DEVICE, + }, + { + .virtual = L4_WKUP_DRA7XX_VIRT, + .pfn = __phys_to_pfn(L4_WKUP_DRA7XX_PHYS), + .length = L4_WKUP_DRA7XX_SIZE, + .type = MT_DEVICE, + }, +}; +#endif + +#ifdef CONFIG_SOC_OMAP2420 +void __init omap242x_map_io(void) +{ + iotable_init(omap24xx_io_desc, ARRAY_SIZE(omap24xx_io_desc)); + iotable_init(omap242x_io_desc, ARRAY_SIZE(omap242x_io_desc)); +} +#endif + +#ifdef CONFIG_SOC_OMAP2430 +void __init omap243x_map_io(void) +{ + iotable_init(omap24xx_io_desc, ARRAY_SIZE(omap24xx_io_desc)); + iotable_init(omap243x_io_desc, ARRAY_SIZE(omap243x_io_desc)); +} +#endif + +#ifdef CONFIG_ARCH_OMAP3 +void __init omap3_map_io(void) +{ + iotable_init(omap34xx_io_desc, ARRAY_SIZE(omap34xx_io_desc)); +} +#endif + +#ifdef CONFIG_SOC_TI81XX +void __init ti81xx_map_io(void) +{ + iotable_init(omapti81xx_io_desc, ARRAY_SIZE(omapti81xx_io_desc)); +} +#endif + +#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_AM43XX) +void __init am33xx_map_io(void) +{ + iotable_init(omapam33xx_io_desc, ARRAY_SIZE(omapam33xx_io_desc)); +} +#endif + +#ifdef CONFIG_ARCH_OMAP4 +void __init omap4_map_io(void) +{ + iotable_init(omap44xx_io_desc, ARRAY_SIZE(omap44xx_io_desc)); + omap_barriers_init(); +} +#endif + +#ifdef CONFIG_SOC_OMAP5 +void __init omap5_map_io(void) +{ + iotable_init(omap54xx_io_desc, ARRAY_SIZE(omap54xx_io_desc)); + omap_barriers_init(); +} +#endif + +#ifdef CONFIG_SOC_DRA7XX +void __init dra7xx_map_io(void) +{ + iotable_init(dra7xx_io_desc, ARRAY_SIZE(dra7xx_io_desc)); + omap_barriers_init(); +} +#endif +/* + * omap2_init_reprogram_sdrc - reprogram SDRC timing parameters + * + * Sets the CORE DPLL3 M2 divider to the same value that it's at + * currently. This has the effect of setting the SDRC SDRAM AC timing + * registers to the values currently defined by the kernel. Currently + * only defined for OMAP3; will return 0 if called on OMAP2. Returns + * -EINVAL if the dpll3_m2_ck cannot be found, 0 if called on OMAP2, + * or passes along the return value of clk_set_rate(). + */ +static int __init _omap2_init_reprogram_sdrc(void) +{ + struct clk *dpll3_m2_ck; + int v = -EINVAL; + long rate; + + if (!cpu_is_omap34xx()) + return 0; + + dpll3_m2_ck = clk_get(NULL, "dpll3_m2_ck"); + if (IS_ERR(dpll3_m2_ck)) + return -EINVAL; + + rate = clk_get_rate(dpll3_m2_ck); + pr_info("Reprogramming SDRC clock to %ld Hz\n", rate); + v = clk_set_rate(dpll3_m2_ck, rate); + if (v) + pr_err("dpll3_m2_clk rate change failed: %d\n", v); + + clk_put(dpll3_m2_ck); + + return v; +} + +#ifdef CONFIG_OMAP_HWMOD +static int _set_hwmod_postsetup_state(struct omap_hwmod *oh, void *data) +{ + return omap_hwmod_set_postsetup_state(oh, *(u8 *)data); +} + +static void __init __maybe_unused omap_hwmod_init_postsetup(void) +{ + u8 postsetup_state = _HWMOD_STATE_DEFAULT; + + /* Set the default postsetup state for all hwmods */ + omap_hwmod_for_each(_set_hwmod_postsetup_state, &postsetup_state); +} +#else +static inline void omap_hwmod_init_postsetup(void) +{ +} +#endif + +#ifdef CONFIG_SOC_OMAP2420 +void __init omap2420_init_early(void) +{ + omap2_set_globals_tap(OMAP242X_CLASS, OMAP2_L4_IO_ADDRESS(0x48014000)); + omap2_set_globals_sdrc(OMAP2_L3_IO_ADDRESS(OMAP2420_SDRC_BASE), + OMAP2_L3_IO_ADDRESS(OMAP2420_SMS_BASE)); + omap2_control_base_init(); + omap2xxx_check_revision(); + omap2_prcm_base_init(); + omap2xxx_voltagedomains_init(); + omap242x_powerdomains_init(); + omap242x_clockdomains_init(); + omap2420_hwmod_init(); + omap_hwmod_init_postsetup(); + omap_clk_soc_init = omap2420_dt_clk_init; + rate_table = omap2420_rate_table; +} + +void __init omap2420_init_late(void) +{ + omap_pm_soc_init = omap2_pm_init; +} +#endif + +#ifdef CONFIG_SOC_OMAP2430 +void __init omap2430_init_early(void) +{ + omap2_set_globals_tap(OMAP243X_CLASS, OMAP2_L4_IO_ADDRESS(0x4900a000)); + omap2_set_globals_sdrc(OMAP2_L3_IO_ADDRESS(OMAP243X_SDRC_BASE), + OMAP2_L3_IO_ADDRESS(OMAP243X_SMS_BASE)); + omap2_control_base_init(); + omap2xxx_check_revision(); + omap2_prcm_base_init(); + omap2xxx_voltagedomains_init(); + omap243x_powerdomains_init(); + omap243x_clockdomains_init(); + omap2430_hwmod_init(); + omap_hwmod_init_postsetup(); + omap_clk_soc_init = omap2430_dt_clk_init; + rate_table = omap2430_rate_table; +} + +void __init omap2430_init_late(void) +{ + omap_pm_soc_init = omap2_pm_init; +} +#endif + +/* + * Currently only board-omap3beagle.c should call this because of the + * same machine_id for 34xx and 36xx beagle.. Will get fixed with DT. + */ +#ifdef CONFIG_ARCH_OMAP3 +void __init omap3_init_early(void) +{ + omap2_set_globals_tap(OMAP343X_CLASS, OMAP2_L4_IO_ADDRESS(0x4830A000)); + omap2_set_globals_sdrc(OMAP2_L3_IO_ADDRESS(OMAP343X_SDRC_BASE), + OMAP2_L3_IO_ADDRESS(OMAP343X_SMS_BASE)); + omap2_control_base_init(); + omap3xxx_check_revision(); + omap3xxx_check_features(); + omap2_prcm_base_init(); + omap3xxx_voltagedomains_init(); + omap3xxx_powerdomains_init(); + omap3xxx_clockdomains_init(); + omap3xxx_hwmod_init(); + omap_hwmod_init_postsetup(); + omap_secure_init(); +} + +void __init omap3430_init_early(void) +{ + omap3_init_early(); + omap_clk_soc_init = omap3430_dt_clk_init; +} + +void __init omap35xx_init_early(void) +{ + omap3_init_early(); + omap_clk_soc_init = omap3430_dt_clk_init; +} + +void __init omap3630_init_early(void) +{ + omap3_init_early(); + omap_clk_soc_init = omap3630_dt_clk_init; +} + +void __init am35xx_init_early(void) +{ + omap3_init_early(); + omap_clk_soc_init = am35xx_dt_clk_init; +} + +void __init omap3_init_late(void) +{ + omap_pm_soc_init = omap3_pm_init; +} + +void __init ti81xx_init_late(void) +{ + omap_pm_soc_init = omap_pm_nop_init; +} +#endif + +#ifdef CONFIG_SOC_TI81XX +void __init ti814x_init_early(void) +{ + omap2_set_globals_tap(TI814X_CLASS, + OMAP2_L4_IO_ADDRESS(TI81XX_TAP_BASE)); + omap2_control_base_init(); + omap3xxx_check_revision(); + ti81xx_check_features(); + omap2_prcm_base_init(); + omap3xxx_voltagedomains_init(); + omap3xxx_powerdomains_init(); + ti814x_clockdomains_init(); + dm814x_hwmod_init(); + omap_hwmod_init_postsetup(); + omap_clk_soc_init = dm814x_dt_clk_init; + omap_secure_init(); +} + +void __init ti816x_init_early(void) +{ + omap2_set_globals_tap(TI816X_CLASS, + OMAP2_L4_IO_ADDRESS(TI81XX_TAP_BASE)); + omap2_control_base_init(); + omap3xxx_check_revision(); + ti81xx_check_features(); + omap2_prcm_base_init(); + omap3xxx_voltagedomains_init(); + omap3xxx_powerdomains_init(); + ti816x_clockdomains_init(); + dm816x_hwmod_init(); + omap_hwmod_init_postsetup(); + omap_clk_soc_init = dm816x_dt_clk_init; + omap_secure_init(); +} +#endif + +#ifdef CONFIG_SOC_AM33XX +void __init am33xx_init_early(void) +{ + omap2_set_globals_tap(AM335X_CLASS, + AM33XX_L4_WK_IO_ADDRESS(AM33XX_TAP_BASE)); + omap2_control_base_init(); + omap3xxx_check_revision(); + am33xx_check_features(); + omap2_prcm_base_init(); + am33xx_powerdomains_init(); + am33xx_clockdomains_init(); + omap_clk_soc_init = am33xx_dt_clk_init; + omap_secure_init(); +} + +void __init am33xx_init_late(void) +{ + omap_pm_soc_init = amx3_common_pm_init; +} +#endif + +#ifdef CONFIG_SOC_AM43XX +void __init am43xx_init_early(void) +{ + omap2_set_globals_tap(AM335X_CLASS, + AM33XX_L4_WK_IO_ADDRESS(AM33XX_TAP_BASE)); + omap2_control_base_init(); + omap3xxx_check_revision(); + am33xx_check_features(); + omap2_prcm_base_init(); + am43xx_powerdomains_init(); + am43xx_clockdomains_init(); + omap_l2_cache_init(); + omap_clk_soc_init = am43xx_dt_clk_init; + omap_secure_init(); +} + +void __init am43xx_init_late(void) +{ + omap_pm_soc_init = amx3_common_pm_init; +} +#endif + +#ifdef CONFIG_ARCH_OMAP4 +void __init omap4430_init_early(void) +{ + omap2_set_globals_tap(OMAP443X_CLASS, + OMAP2_L4_IO_ADDRESS(OMAP443X_SCM_BASE)); + omap2_set_globals_prcm_mpu(OMAP2_L4_IO_ADDRESS(OMAP4430_PRCM_MPU_BASE)); + omap2_control_base_init(); + omap4xxx_check_revision(); + omap4xxx_check_features(); + omap2_prcm_base_init(); + omap4_sar_ram_init(); + omap4_mpuss_early_init(); + omap4_pm_init_early(); + omap44xx_voltagedomains_init(); + omap44xx_powerdomains_init(); + omap44xx_clockdomains_init(); + omap_l2_cache_init(); + omap_clk_soc_init = omap4xxx_dt_clk_init; + omap_secure_init(); +} + +void __init omap4430_init_late(void) +{ + omap_pm_soc_init = omap4_pm_init; +} +#endif + +#ifdef CONFIG_SOC_OMAP5 +void __init omap5_init_early(void) +{ + omap2_set_globals_tap(OMAP54XX_CLASS, + OMAP2_L4_IO_ADDRESS(OMAP54XX_SCM_BASE)); + omap2_set_globals_prcm_mpu(OMAP2_L4_IO_ADDRESS(OMAP54XX_PRCM_MPU_BASE)); + omap2_control_base_init(); + omap2_prcm_base_init(); + omap5xxx_check_revision(); + omap4_sar_ram_init(); + omap4_mpuss_early_init(); + omap4_pm_init_early(); + omap54xx_voltagedomains_init(); + omap54xx_powerdomains_init(); + omap54xx_clockdomains_init(); + omap_clk_soc_init = omap5xxx_dt_clk_init; + omap_secure_init(); +} + +void __init omap5_init_late(void) +{ + omap_pm_soc_init = omap4_pm_init; +} +#endif + +#ifdef CONFIG_SOC_DRA7XX +void __init dra7xx_init_early(void) +{ + omap2_set_globals_tap(DRA7XX_CLASS, + OMAP2_L4_IO_ADDRESS(DRA7XX_TAP_BASE)); + omap2_set_globals_prcm_mpu(OMAP2_L4_IO_ADDRESS(OMAP54XX_PRCM_MPU_BASE)); + omap2_control_base_init(); + omap4_pm_init_early(); + omap2_prcm_base_init(); + dra7xxx_check_revision(); + dra7xx_powerdomains_init(); + dra7xx_clockdomains_init(); + omap_clk_soc_init = dra7xx_dt_clk_init; + omap_secure_init(); +} + +void __init dra7xx_init_late(void) +{ + omap_pm_soc_init = omap4_pm_init; +} +#endif + + +void __init omap_sdrc_init(struct omap_sdrc_params *sdrc_cs0, + struct omap_sdrc_params *sdrc_cs1) +{ + omap_sram_init(); + + if (cpu_is_omap24xx() || omap3_has_sdrc()) { + omap2_sdrc_init(sdrc_cs0, sdrc_cs1); + _omap2_init_reprogram_sdrc(); + } +} + +int __init omap_clk_init(void) +{ + int ret = 0; + + if (!omap_clk_soc_init) + return 0; + + ti_clk_init_features(); + + omap2_clk_setup_ll_ops(); + + ret = omap_control_init(); + if (ret) + return ret; + + ret = omap_prcm_init(); + if (ret) + return ret; + + of_clk_init(NULL); + + ti_dt_clk_init_retry_clks(); + + ti_dt_clockdomains_setup(); + + ret = omap_clk_soc_init(); + + return ret; +} diff --git a/arch/arm/mach-omap2/iomap.h b/arch/arm/mach-omap2/iomap.h new file mode 100644 index 000000000..6191d2444 --- /dev/null +++ b/arch/arm/mach-omap2/iomap.h @@ -0,0 +1,259 @@ +/* + * IO mappings for OMAP2+ + * + * IO definitions for TI OMAP processors and boards + * + * Copied from arch/arm/mach-sa1100/include/mach/io.h + * Copyright (C) 1997-1999 Russell King + * + * Copyright (C) 2009-2012 Texas Instruments + * Added OMAP4/5 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define OMAP2_L3_IO_OFFSET 0x90000000 +#define OMAP2_L3_IO_ADDRESS(pa) IOMEM((pa) + OMAP2_L3_IO_OFFSET) /* L3 */ + +#define OMAP2_L4_IO_OFFSET 0xb2000000 +#define OMAP2_L4_IO_ADDRESS(pa) IOMEM((pa) + OMAP2_L4_IO_OFFSET) /* L4 */ + +#define OMAP4_L3_IO_OFFSET 0xb4000000 +#define OMAP4_L3_IO_ADDRESS(pa) IOMEM((pa) + OMAP4_L3_IO_OFFSET) /* L3 */ + +#define AM33XX_L4_WK_IO_OFFSET 0xb5000000 +#define AM33XX_L4_WK_IO_ADDRESS(pa) IOMEM((pa) + AM33XX_L4_WK_IO_OFFSET) + +#define OMAP4_L3_PER_IO_OFFSET 0xb1100000 +#define OMAP4_L3_PER_IO_ADDRESS(pa) IOMEM((pa) + OMAP4_L3_PER_IO_OFFSET) + +#define OMAP2_EMU_IO_OFFSET 0xaa800000 /* Emulation */ +#define OMAP2_EMU_IO_ADDRESS(pa) IOMEM((pa) + OMAP2_EMU_IO_OFFSET) + +/* + * ---------------------------------------------------------------------------- + * Omap2 specific IO mapping + * ---------------------------------------------------------------------------- + */ + +/* We map both L3 and L4 on OMAP2 */ +#define L3_24XX_PHYS L3_24XX_BASE /* 0x68000000 --> 0xf8000000*/ +#define L3_24XX_VIRT (L3_24XX_PHYS + OMAP2_L3_IO_OFFSET) +#define L3_24XX_SIZE SZ_1M /* 44kB of 128MB used, want 1MB sect */ +#define L4_24XX_PHYS L4_24XX_BASE /* 0x48000000 --> 0xfa000000 */ +#define L4_24XX_VIRT (L4_24XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_24XX_SIZE SZ_1M /* 1MB of 128MB used, want 1MB sect */ + +#define L4_WK_243X_PHYS L4_WK_243X_BASE /* 0x49000000 --> 0xfb000000 */ +#define L4_WK_243X_VIRT (L4_WK_243X_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_WK_243X_SIZE SZ_1M +#define OMAP243X_GPMC_PHYS OMAP243X_GPMC_BASE +#define OMAP243X_GPMC_VIRT (OMAP243X_GPMC_PHYS + OMAP2_L3_IO_OFFSET) + /* 0x6e000000 --> 0xfe000000 */ +#define OMAP243X_GPMC_SIZE SZ_1M +#define OMAP243X_SDRC_PHYS OMAP243X_SDRC_BASE + /* 0x6D000000 --> 0xfd000000 */ +#define OMAP243X_SDRC_VIRT (OMAP243X_SDRC_PHYS + OMAP2_L3_IO_OFFSET) +#define OMAP243X_SDRC_SIZE SZ_1M +#define OMAP243X_SMS_PHYS OMAP243X_SMS_BASE + /* 0x6c000000 --> 0xfc000000 */ +#define OMAP243X_SMS_VIRT (OMAP243X_SMS_PHYS + OMAP2_L3_IO_OFFSET) +#define OMAP243X_SMS_SIZE SZ_1M + +/* 2420 IVA */ +#define DSP_MEM_2420_PHYS OMAP2420_DSP_MEM_BASE + /* 0x58000000 --> 0xfc100000 */ +#define DSP_MEM_2420_VIRT 0xfc100000 +#define DSP_MEM_2420_SIZE 0x28000 +#define DSP_IPI_2420_PHYS OMAP2420_DSP_IPI_BASE + /* 0x59000000 --> 0xfc128000 */ +#define DSP_IPI_2420_VIRT 0xfc128000 +#define DSP_IPI_2420_SIZE SZ_4K +#define DSP_MMU_2420_PHYS OMAP2420_DSP_MMU_BASE + /* 0x5a000000 --> 0xfc129000 */ +#define DSP_MMU_2420_VIRT 0xfc129000 +#define DSP_MMU_2420_SIZE SZ_4K + +/* 2430 IVA2.1 - currently unmapped */ + +/* + * ---------------------------------------------------------------------------- + * Omap3 specific IO mapping + * ---------------------------------------------------------------------------- + */ + +/* We map both L3 and L4 on OMAP3 */ +#define L3_34XX_PHYS L3_34XX_BASE /* 0x68000000 --> 0xf8000000 */ +#define L3_34XX_VIRT (L3_34XX_PHYS + OMAP2_L3_IO_OFFSET) +#define L3_34XX_SIZE SZ_1M /* 44kB of 128MB used, want 1MB sect */ + +#define L4_34XX_PHYS L4_34XX_BASE /* 0x48000000 --> 0xfa000000 */ +#define L4_34XX_VIRT (L4_34XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_34XX_SIZE SZ_4M /* 1MB of 128MB used, want 1MB sect */ + +/* + * ---------------------------------------------------------------------------- + * AM33XX specific IO mapping + * ---------------------------------------------------------------------------- + */ +#define L4_WK_AM33XX_PHYS L4_WK_AM33XX_BASE +#define L4_WK_AM33XX_VIRT (L4_WK_AM33XX_PHYS + AM33XX_L4_WK_IO_OFFSET) +#define L4_WK_AM33XX_SIZE SZ_4M /* 1MB of 128MB used, want 1MB sect */ + +/* + * Need to look at the Size 4M for L4. + * VPOM3430 was not working for Int controller + */ + +#define L4_PER_34XX_PHYS L4_PER_34XX_BASE + /* 0x49000000 --> 0xfb000000 */ +#define L4_PER_34XX_VIRT (L4_PER_34XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_PER_34XX_SIZE SZ_1M + +#define L4_EMU_34XX_PHYS L4_EMU_34XX_BASE + /* 0x54000000 --> 0xfe800000 */ +#define L4_EMU_34XX_VIRT (L4_EMU_34XX_PHYS + OMAP2_EMU_IO_OFFSET) +#define L4_EMU_34XX_SIZE SZ_8M + +#define OMAP34XX_GPMC_PHYS OMAP34XX_GPMC_BASE + /* 0x6e000000 --> 0xfe000000 */ +#define OMAP34XX_GPMC_VIRT (OMAP34XX_GPMC_PHYS + OMAP2_L3_IO_OFFSET) +#define OMAP34XX_GPMC_SIZE SZ_1M + +#define OMAP343X_SMS_PHYS OMAP343X_SMS_BASE + /* 0x6c000000 --> 0xfc000000 */ +#define OMAP343X_SMS_VIRT (OMAP343X_SMS_PHYS + OMAP2_L3_IO_OFFSET) +#define OMAP343X_SMS_SIZE SZ_1M + +#define OMAP343X_SDRC_PHYS OMAP343X_SDRC_BASE + /* 0x6D000000 --> 0xfd000000 */ +#define OMAP343X_SDRC_VIRT (OMAP343X_SDRC_PHYS + OMAP2_L3_IO_OFFSET) +#define OMAP343X_SDRC_SIZE SZ_1M + +/* 3430 IVA - currently unmapped */ + +/* + * ---------------------------------------------------------------------------- + * Omap4 specific IO mapping + * ---------------------------------------------------------------------------- + */ + +/* We map both L3 and L4 on OMAP4 */ +#define L3_44XX_PHYS L3_44XX_BASE /* 0x44000000 --> 0xf8000000 */ +#define L3_44XX_VIRT (L3_44XX_PHYS + OMAP4_L3_IO_OFFSET) +#define L3_44XX_SIZE SZ_1M + +#define L4_44XX_PHYS L4_44XX_BASE /* 0x4a000000 --> 0xfc000000 */ +#define L4_44XX_VIRT (L4_44XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_44XX_SIZE SZ_4M + +#define L4_PER_44XX_PHYS L4_PER_44XX_BASE + /* 0x48000000 --> 0xfa000000 */ +#define L4_PER_44XX_VIRT (L4_PER_44XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_PER_44XX_SIZE SZ_4M + +#define L4_ABE_44XX_PHYS L4_ABE_44XX_BASE + /* 0x49000000 --> 0xfb000000 */ +#define L4_ABE_44XX_VIRT (L4_ABE_44XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_ABE_44XX_SIZE SZ_1M +/* + * ---------------------------------------------------------------------------- + * Omap5 specific IO mapping + * ---------------------------------------------------------------------------- + */ +#define L3_54XX_PHYS L3_54XX_BASE /* 0x44000000 --> 0xf8000000 */ +#define L3_54XX_VIRT (L3_54XX_PHYS + OMAP4_L3_IO_OFFSET) +#define L3_54XX_SIZE SZ_1M + +#define L4_54XX_PHYS L4_54XX_BASE /* 0x4a000000 --> 0xfc000000 */ +#define L4_54XX_VIRT (L4_54XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_54XX_SIZE SZ_4M + +#define L4_WK_54XX_PHYS L4_WK_54XX_BASE /* 0x4ae00000 --> 0xfce00000 */ +#define L4_WK_54XX_VIRT (L4_WK_54XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_WK_54XX_SIZE SZ_2M + +#define L4_PER_54XX_PHYS L4_PER_54XX_BASE /* 0x48000000 --> 0xfa000000 */ +#define L4_PER_54XX_VIRT (L4_PER_54XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_PER_54XX_SIZE SZ_4M + +/* + * ---------------------------------------------------------------------------- + * DRA7xx specific IO mapping + * ---------------------------------------------------------------------------- + */ +/* + * L3_MAIN_SN_DRA7XX_PHYS 0x44000000 --> 0xf8000000 + * The overall space is 24MiB (0x4400_0000<->0x457F_FFFF), but mapping + * everything is just inefficient, since, there are too many address holes. + */ +#define L3_MAIN_SN_DRA7XX_PHYS L3_MAIN_SN_DRA7XX_BASE +#define L3_MAIN_SN_DRA7XX_VIRT (L3_MAIN_SN_DRA7XX_PHYS + OMAP4_L3_IO_OFFSET) +#define L3_MAIN_SN_DRA7XX_SIZE SZ_1M + +/* + * L4_PER1_DRA7XX_PHYS (0x4800_000<>0x480D_2FFF) -> 0.82MiB (alloc 1MiB) + * (0x48000000<->0x48100000) <=> (0xFA000000<->0xFA100000) + */ +#define L4_PER1_DRA7XX_PHYS L4_PER1_DRA7XX_BASE +#define L4_PER1_DRA7XX_VIRT (L4_PER1_DRA7XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_PER1_DRA7XX_SIZE SZ_1M + +/* + * L4_CFG_MPU_DRA7XX_PHYS (0x48210000<>0x482A_F2FF) -> 0.62MiB (alloc 1MiB) + * (0x48210000<->0x48310000) <=> (0xFA210000<->0xFA310000) + * NOTE: This is a bit of an orphan memory map sitting isolated in TRM + */ +#define L4_CFG_MPU_DRA7XX_PHYS L4_CFG_MPU_DRA7XX_BASE +#define L4_CFG_MPU_DRA7XX_VIRT (L4_CFG_MPU_DRA7XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_CFG_MPU_DRA7XX_SIZE SZ_1M + +/* + * L4_PER2_DRA7XX_PHYS (0x4840_0000<>0x4848_8FFF) -> .53MiB (alloc 1MiB) + * (0x48400000<->0x48500000) <=> (0xFA400000<->0xFA500000) + */ +#define L4_PER2_DRA7XX_PHYS L4_PER2_DRA7XX_BASE +#define L4_PER2_DRA7XX_VIRT (L4_PER2_DRA7XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_PER2_DRA7XX_SIZE SZ_1M + +/* + * L4_PER3_DRA7XX_PHYS (0x4880_0000<>0x489E_0FFF) -> 1.87MiB (alloc 2MiB) + * (0x48800000<->0x48A00000) <=> (0xFA800000<->0xFAA00000) + */ +#define L4_PER3_DRA7XX_PHYS L4_PER3_DRA7XX_BASE +#define L4_PER3_DRA7XX_VIRT (L4_PER3_DRA7XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_PER3_DRA7XX_SIZE SZ_2M + +/* + * L4_CFG_DRA7XX_PHYS (0x4A00_0000<>0x4A22_BFFF) ->2.17MiB (alloc 3MiB)? + * (0x4A000000<->0x4A300000) <=> (0xFC000000<->0xFC300000) + */ +#define L4_CFG_DRA7XX_PHYS L4_CFG_DRA7XX_BASE +#define L4_CFG_DRA7XX_VIRT (L4_CFG_DRA7XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_CFG_DRA7XX_SIZE (SZ_1M + SZ_2M) + +/* + * L4_WKUP_DRA7XX_PHYS (0x4AE0_0000<>0x4AE3_EFFF) -> .24 mb (alloc 1MiB)? + * (0x4AE00000<->4AF00000) <=> (0xFCE00000<->0xFCF00000) + */ +#define L4_WKUP_DRA7XX_PHYS L4_WKUP_DRA7XX_BASE +#define L4_WKUP_DRA7XX_VIRT (L4_WKUP_DRA7XX_PHYS + OMAP2_L4_IO_OFFSET) +#define L4_WKUP_DRA7XX_SIZE SZ_1M diff --git a/arch/arm/mach-omap2/l3_2xxx.h b/arch/arm/mach-omap2/l3_2xxx.h new file mode 100644 index 000000000..6297c6242 --- /dev/null +++ b/arch/arm/mach-omap2/l3_2xxx.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * arch/arm/plat-omap/include/plat/l3_2xxx.h - L3 firewall definitions + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * Sumit Semwal + */ +#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_L3_2XXX_H +#define __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_L3_2XXX_H + +/* L3 CONNIDs */ +/* Display Sub system (DSS) */ +#define OMAP2_L3_CORE_FW_CONNID_DSS 8 + +#endif diff --git a/arch/arm/mach-omap2/l3_3xxx.h b/arch/arm/mach-omap2/l3_3xxx.h new file mode 100644 index 000000000..60ea7b201 --- /dev/null +++ b/arch/arm/mach-omap2/l3_3xxx.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * arch/arm/plat-omap/include/plat/l3_3xxx.h - L3 firewall definitions + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * Sumit Semwal + */ +#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_L3_3XXX_H +#define __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_L3_3XXX_H + +/* L3 Initiator IDs */ +/* Display Sub system (DSS) */ +#define OMAP3_L3_CORE_FW_INIT_ID_DSS 29 + +#endif diff --git a/arch/arm/mach-omap2/l4_2xxx.h b/arch/arm/mach-omap2/l4_2xxx.h new file mode 100644 index 000000000..418e1072d --- /dev/null +++ b/arch/arm/mach-omap2/l4_2xxx.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * arch/arm/plat-omap/include/plat/l4_2xxx.h - L4 firewall definitions + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ + * Sumit Semwal + */ +#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_L4_2XXX_H +#define __ARCH_ARM_PLAT_OMAP_INCLUDE_PLAT_L4_2XXX_H + +/* L4 CORE */ +/* Display Sub system (DSS) */ +#define OMAP2420_L4_CORE_FW_DSS_CORE_REGION 28 +#define OMAP2420_L4_CORE_FW_DSS_DISPC_REGION 29 +#define OMAP2420_L4_CORE_FW_DSS_RFBI_REGION 30 +#define OMAP2420_L4_CORE_FW_DSS_VENC_REGION 31 +#define OMAP2420_L4_CORE_FW_DSS_TA_REGION 32 + +#endif diff --git a/arch/arm/mach-omap2/l4_3xxx.h b/arch/arm/mach-omap2/l4_3xxx.h new file mode 100644 index 000000000..4d9daa2e6 --- /dev/null +++ b/arch/arm/mach-omap2/l4_3xxx.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * arch/arm/plat-omap/include/mach/l4_3xxx.h - L4 firewall definitions + * + * Copyright (C) 2009 Nokia Corporation + * Paul Walmsley + */ +#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_L4_3XXX_H +#define __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_L4_3XXX_H + +/* L4 CORE */ +#define OMAP3_L4_CORE_FW_I2C1_REGION 21 +#define OMAP3_L4_CORE_FW_I2C1_TA_REGION 22 +#define OMAP3_L4_CORE_FW_I2C2_REGION 23 +#define OMAP3_L4_CORE_FW_I2C2_TA_REGION 24 +#define OMAP3_L4_CORE_FW_I2C3_REGION 73 +#define OMAP3_L4_CORE_FW_I2C3_TA_REGION 74 + +/* Display Sub system (DSS) */ +#define OMAP3_L4_CORE_FW_DSS_PROT_GROUP 2 + +#define OMAP3_L4_CORE_FW_DSS_DSI_REGION 104 +#define OMAP3ES1_L4_CORE_FW_DSS_CORE_REGION 3 +#define OMAP3_L4_CORE_FW_DSS_CORE_REGION 4 +#define OMAP3_L4_CORE_FW_DSS_DISPC_REGION 4 +#define OMAP3_L4_CORE_FW_DSS_RFBI_REGION 5 +#define OMAP3_L4_CORE_FW_DSS_VENC_REGION 6 +#define OMAP3_L4_CORE_FW_DSS_TA_REGION 7 +#endif diff --git a/arch/arm/mach-omap2/mcbsp.c b/arch/arm/mach-omap2/mcbsp.c new file mode 100644 index 000000000..c3604b458 --- /dev/null +++ b/arch/arm/mach-omap2/mcbsp.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/mcbsp.c + * + * Copyright (C) 2008 Instituto Nokia de Tecnologia + * Contact: Eduardo Valentin <eduardo.valentin@indt.org.br> + * + * Multichannel mode not supported. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/platform_data/asoc-ti-mcbsp.h> +#include <linux/pm_runtime.h> + +#include <linux/omap-dma.h> + +#include "soc.h" +#include "omap_device.h" +#include "clock.h" + +/* + * FIXME: Find a mechanism to enable/disable runtime the McBSP ICLK autoidle. + * Sidetone needs non-gated ICLK and sidetone autoidle is broken. + */ +#include "cm3xxx.h" +#include "cm-regbits-34xx.h" + +static int omap3_mcbsp_force_ick_on(struct clk *clk, bool force_on) +{ + if (!clk) + return 0; + + if (force_on) + return omap2_clk_deny_idle(clk); + else + return omap2_clk_allow_idle(clk); +} + +void __init omap3_mcbsp_init_pdata_callback( + struct omap_mcbsp_platform_data *pdata) +{ + if (!pdata) + return; + + pdata->force_ick_on = omap3_mcbsp_force_ick_on; +} diff --git a/arch/arm/mach-omap2/mmc.h b/arch/arm/mach-omap2/mmc.h new file mode 100644 index 000000000..b5533e93c --- /dev/null +++ b/arch/arm/mach-omap2/mmc.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define OMAP24XX_NR_MMC 2 +#define OMAP2420_MMC_SIZE OMAP1_MMC_SIZE +#define OMAP2_MMC1_BASE 0x4809c000 + +#define OMAP4_MMC_REG_OFFSET 0x100 + +struct omap_hwmod; + +#ifdef CONFIG_SOC_OMAP2420 +int omap_msdi_reset(struct omap_hwmod *oh); +#else +static inline int omap_msdi_reset(struct omap_hwmod *oh) +{ + return 0; +} +#endif diff --git a/arch/arm/mach-omap2/msdi.c b/arch/arm/mach-omap2/msdi.c new file mode 100644 index 000000000..552b2fa4e --- /dev/null +++ b/arch/arm/mach-omap2/msdi.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MSDI IP block reset + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * XXX What about pad muxing? + */ + +#include <linux/kernel.h> +#include <linux/err.h> + +#include "prm.h" +#include "common.h" +#include "control.h" +#include "omap_hwmod.h" +#include "omap_device.h" +#include "mmc.h" + +/* + * MSDI_CON_OFFSET: offset in bytes of the MSDI IP block's CON register + * from the IP block's base address + */ +#define MSDI_CON_OFFSET 0x0c + +/* Register bitfields in the CON register */ +#define MSDI_CON_POW_MASK BIT(11) +#define MSDI_CON_CLKD_MASK (0x3f << 0) +#define MSDI_CON_CLKD_SHIFT 0 + +/* MSDI_TARGET_RESET_CLKD: clock divisor to use throughout the reset */ +#define MSDI_TARGET_RESET_CLKD 0x3ff + +/** + * omap_msdi_reset - reset the MSDI IP block + * @oh: struct omap_hwmod * + * + * The MSDI IP block on OMAP2420 has to have both the POW and CLKD + * fields set inside its CON register for a reset to complete + * successfully. This is not documented in the TRM. For CLKD, we use + * the value that results in the lowest possible clock rate, to attempt + * to avoid disturbing any cards. + */ +int omap_msdi_reset(struct omap_hwmod *oh) +{ + u16 v = 0; + int c = 0; + + /* Write to the SOFTRESET bit */ + omap_hwmod_softreset(oh); + + /* Enable the MSDI core and internal clock */ + v |= MSDI_CON_POW_MASK; + v |= MSDI_TARGET_RESET_CLKD << MSDI_CON_CLKD_SHIFT; + omap_hwmod_write(v, oh, MSDI_CON_OFFSET); + + /* Poll on RESETDONE bit */ + omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs) + & SYSS_RESETDONE_MASK), + MAX_MODULE_SOFTRESET_WAIT, c); + + if (c == MAX_MODULE_SOFTRESET_WAIT) + pr_warn("%s: %s: softreset failed (waited %d usec)\n", + __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT); + else + pr_debug("%s: %s: softreset in %d usec\n", __func__, + oh->name, c); + + /* Disable the MSDI internal clock */ + v &= ~MSDI_CON_CLKD_MASK; + omap_hwmod_write(v, oh, MSDI_CON_OFFSET); + + return 0; +} diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S new file mode 100644 index 000000000..1762f9199 --- /dev/null +++ b/arch/arm/mach-omap2/omap-headsmp.S @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Secondary CPU startup routine source file. + * + * Copyright (C) 2009-2014 Texas Instruments, Inc. + * + * Author: + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * Interface functions needed for the SMP. This file is based on arm + * realview smp platform. + * Copyright (c) 2003 ARM Limited. + */ + +#include <linux/linkage.h> +#include <linux/init.h> +#include <asm/assembler.h> + +#include "omap44xx.h" + +/* Physical address needed since MMU not enabled yet on secondary core */ +#define AUX_CORE_BOOT0_PA 0x48281800 +#define API_HYP_ENTRY 0x102 + +ENTRY(omap_secondary_startup) +#ifdef CONFIG_SMP + b secondary_startup +#else +/* Should never get here */ +again: wfi + b again +#endif +#ENDPROC(omap_secondary_startup) + +/* + * OMAP5 specific entry point for secondary CPU to jump from ROM + * code. This routine also provides a holding flag into which + * secondary core is held until we're ready for it to initialise. + * The primary core will update this flag using a hardware + * register AuxCoreBoot0. + */ +ENTRY(omap5_secondary_startup) +wait: ldr r2, =AUX_CORE_BOOT0_PA @ read from AuxCoreBoot0 + ldr r0, [r2] + mov r0, r0, lsr #5 + mrc p15, 0, r4, c0, c0, 5 + and r4, r4, #0x0f + cmp r0, r4 + bne wait + b omap_secondary_startup +ENDPROC(omap5_secondary_startup) +/* + * Same as omap5_secondary_startup except we call into the ROM to + * enable HYP mode first. This is called instead of + * omap5_secondary_startup if the primary CPU was put into HYP mode by + * the boot loader. + */ + .arch armv7-a + .arch_extension sec +ENTRY(omap5_secondary_hyp_startup) +wait_2: ldr r2, =AUX_CORE_BOOT0_PA @ read from AuxCoreBoot0 + ldr r0, [r2] + mov r0, r0, lsr #5 + mrc p15, 0, r4, c0, c0, 5 + and r4, r4, #0x0f + cmp r0, r4 + bne wait_2 + ldr r12, =API_HYP_ENTRY + badr r0, hyp_boot + smc #0 +hyp_boot: + b omap_secondary_startup +ENDPROC(omap5_secondary_hyp_startup) +/* + * OMAP4 specific entry point for secondary CPU to jump from ROM + * code. This routine also provides a holding flag into which + * secondary core is held until we're ready for it to initialise. + * The primary core will update this flag using a hardware + * register AuxCoreBoot0. + */ +ENTRY(omap4_secondary_startup) +hold: ldr r12,=0x103 + dsb + smc #0 @ read from AuxCoreBoot0 + mov r0, r0, lsr #9 + mrc p15, 0, r4, c0, c0, 5 + and r4, r4, #0x0f + cmp r0, r4 + bne hold + + /* + * we've been released from the wait loop,secondary_stack + * should now contain the SVC stack for this core + */ + b omap_secondary_startup +ENDPROC(omap4_secondary_startup) + +ENTRY(omap4460_secondary_startup) +hold_2: ldr r12,=0x103 + dsb + smc #0 @ read from AuxCoreBoot0 + mov r0, r0, lsr #9 + mrc p15, 0, r4, c0, c0, 5 + and r4, r4, #0x0f + cmp r0, r4 + bne hold_2 + + /* + * GIC distributor control register has changed between + * CortexA9 r1pX and r2pX. The Control Register secure + * banked version is now composed of 2 bits: + * bit 0 == Secure Enable + * bit 1 == Non-Secure Enable + * The Non-Secure banked register has not changed + * Because the ROM Code is based on the r1pX GIC, the CPU1 + * GIC restoration will cause a problem to CPU0 Non-Secure SW. + * The workaround must be: + * 1) Before doing the CPU1 wakeup, CPU0 must disable + * the GIC distributor + * 2) CPU1 must re-enable the GIC distributor on + * it's wakeup path. + */ + ldr r1, =OMAP44XX_GIC_DIST_BASE + ldr r0, [r1] + orr r0, #1 + str r0, [r1] + + /* + * we've been released from the wait loop,secondary_stack + * should now contain the SVC stack for this core + */ + b omap_secondary_startup +ENDPROC(omap4460_secondary_startup) diff --git a/arch/arm/mach-omap2/omap-hotplug.c b/arch/arm/mach-omap2/omap-hotplug.c new file mode 100644 index 000000000..8343fcf4f --- /dev/null +++ b/arch/arm/mach-omap2/omap-hotplug.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 SMP cpu-hotplug support + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Author: + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * Platform file needed for the OMAP4 SMP. This file is based on arm + * realview smp platform. + * Copyright (c) 2002 ARM Limited. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/smp.h> +#include <linux/io.h> + +#include "omap-wakeupgen.h" +#include "common.h" +#include "powerdomain.h" + +/* + * platform-specific code to shutdown a CPU + * Called with IRQs disabled + */ +void omap4_cpu_die(unsigned int cpu) +{ + unsigned int boot_cpu = 0; + void __iomem *base = omap_get_wakeupgen_base(); + + /* + * we're ready for shutdown now, so do it + */ + if (omap_secure_apis_support()) { + if (omap_modify_auxcoreboot0(0x0, 0x200) != 0x0) + pr_err("Secure clear status failed\n"); + } else { + writel_relaxed(0, base + OMAP_AUX_CORE_BOOT_0); + } + + + for (;;) { + /* + * Enter into low power state + */ + omap4_hotplug_cpu(cpu, PWRDM_POWER_OFF); + + if (omap_secure_apis_support()) + boot_cpu = omap_read_auxcoreboot0() >> 9; + else + boot_cpu = + readl_relaxed(base + OMAP_AUX_CORE_BOOT_0) >> 5; + + if (boot_cpu == smp_processor_id()) { + /* + * OK, proper wakeup, we're done + */ + break; + } + pr_debug("CPU%u: spurious wakeup call\n", cpu); + } +} + +/* Needed by kexec and platform_can_cpu_hotplug() */ +int omap4_cpu_kill(unsigned int cpu) +{ + return 1; +} diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c new file mode 100644 index 000000000..9c8a85198 --- /dev/null +++ b/arch/arm/mach-omap2/omap-iommu.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP IOMMU quirks for various TI SoCs + * + * Copyright (C) 2015-2019 Texas Instruments Incorporated - https://www.ti.com/ + * Suman Anna <s-anna@ti.com> + */ + +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/list.h> + +#include "clockdomain.h" +#include "powerdomain.h" +#include "common.h" + +struct pwrdm_link { + struct device *dev; + struct powerdomain *pwrdm; + struct list_head node; +}; + +static DEFINE_SPINLOCK(iommu_lock); +static struct clockdomain *emu_clkdm; +static atomic_t emu_count; + +static void omap_iommu_dra7_emu_swsup_config(struct platform_device *pdev, + bool enable) +{ + struct device_node *np = pdev->dev.of_node; + unsigned long flags; + + if (!of_device_is_compatible(np, "ti,dra7-dsp-iommu")) + return; + + if (!emu_clkdm) { + emu_clkdm = clkdm_lookup("emu_clkdm"); + if (WARN_ON_ONCE(!emu_clkdm)) + return; + } + + spin_lock_irqsave(&iommu_lock, flags); + + if (enable && (atomic_inc_return(&emu_count) == 1)) + clkdm_deny_idle(emu_clkdm); + else if (!enable && (atomic_dec_return(&emu_count) == 0)) + clkdm_allow_idle(emu_clkdm); + + spin_unlock_irqrestore(&iommu_lock, flags); +} + +static struct powerdomain *_get_pwrdm(struct device *dev) +{ + struct clk *clk; + struct clk_hw_omap *hwclk; + struct clockdomain *clkdm; + struct powerdomain *pwrdm = NULL; + struct pwrdm_link *entry; + unsigned long flags; + static LIST_HEAD(cache); + + spin_lock_irqsave(&iommu_lock, flags); + + list_for_each_entry(entry, &cache, node) { + if (entry->dev == dev) { + pwrdm = entry->pwrdm; + break; + } + } + + spin_unlock_irqrestore(&iommu_lock, flags); + + if (pwrdm) + return pwrdm; + + clk = of_clk_get(dev->of_node->parent, 0); + if (IS_ERR(clk)) { + dev_err(dev, "no fck found\n"); + return NULL; + } + + hwclk = to_clk_hw_omap(__clk_get_hw(clk)); + clk_put(clk); + if (!hwclk || !hwclk->clkdm_name) { + dev_err(dev, "no hwclk data\n"); + return NULL; + } + + clkdm = clkdm_lookup(hwclk->clkdm_name); + if (!clkdm) { + dev_err(dev, "clkdm not found: %s\n", hwclk->clkdm_name); + return NULL; + } + + pwrdm = clkdm_get_pwrdm(clkdm); + if (!pwrdm) { + dev_err(dev, "pwrdm not found: %s\n", clkdm->name); + return NULL; + } + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (entry) { + entry->dev = dev; + entry->pwrdm = pwrdm; + spin_lock_irqsave(&iommu_lock, flags); + list_add(&entry->node, &cache); + spin_unlock_irqrestore(&iommu_lock, flags); + } + + return pwrdm; +} + +int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, + u8 *pwrst) +{ + struct powerdomain *pwrdm; + u8 next_pwrst; + int ret = 0; + + pwrdm = _get_pwrdm(&pdev->dev); + if (!pwrdm) + return -ENODEV; + + if (request) { + *pwrst = pwrdm_read_next_pwrst(pwrdm); + omap_iommu_dra7_emu_swsup_config(pdev, true); + } + + if (*pwrst > PWRDM_POWER_RET) + goto out; + + next_pwrst = request ? PWRDM_POWER_ON : *pwrst; + + ret = pwrdm_set_next_pwrst(pwrdm, next_pwrst); + +out: + if (!request) + omap_iommu_dra7_emu_swsup_config(pdev, false); + + return ret; +} diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c new file mode 100644 index 000000000..9fba98c23 --- /dev/null +++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP MPUSS low power code + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * OMAP4430 MPUSS mainly consists of dual Cortex-A9 with per-CPU + * Local timer and Watchdog, GIC, SCU, PL310 L2 cache controller, + * CPU0 and CPU1 LPRM modules. + * CPU0, CPU1 and MPUSS each have there own power domain and + * hence multiple low power combinations of MPUSS are possible. + * + * The CPU0 and CPU1 can't support Closed switch Retention (CSWR) + * because the mode is not supported by hw constraints of dormant + * mode. While waking up from the dormant mode, a reset signal + * to the Cortex-A9 processor must be asserted by the external + * power controller. + * + * With architectural inputs and hardware recommendations, only + * below modes are supported from power gain vs latency point of view. + * + * CPU0 CPU1 MPUSS + * ---------------------------------------------- + * ON ON ON + * ON(Inactive) OFF ON(Inactive) + * OFF OFF CSWR + * OFF OFF OSWR + * OFF OFF OFF(Device OFF *TBD) + * ---------------------------------------------- + * + * Note: CPU0 is the master core and it is the last CPU to go down + * and first to wake-up when MPUSS low power states are excercised + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <linux/smp.h> + +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> +#include <asm/smp_scu.h> +#include <asm/suspend.h> +#include <asm/virt.h> +#include <asm/hardware/cache-l2x0.h> + +#include "soc.h" +#include "common.h" +#include "omap44xx.h" +#include "omap4-sar-layout.h" +#include "pm.h" +#include "prcm_mpu44xx.h" +#include "prcm_mpu54xx.h" +#include "prminst44xx.h" +#include "prcm44xx.h" +#include "prm44xx.h" +#include "prm-regbits-44xx.h" + +static void __iomem *sar_base; +static u32 old_cpu1_ns_pa_addr; + +#if defined(CONFIG_PM) && defined(CONFIG_SMP) + +struct omap4_cpu_pm_info { + struct powerdomain *pwrdm; + void __iomem *scu_sar_addr; + void __iomem *wkup_sar_addr; + void __iomem *l2x0_sar_addr; +}; + +/** + * struct cpu_pm_ops - CPU pm operations + * @finish_suspend: CPU suspend finisher function pointer + * @resume: CPU resume function pointer + * @scu_prepare: CPU Snoop Control program function pointer + * @hotplug_restart: CPU restart function pointer + * + * Structure holds functions pointer for CPU low power operations like + * suspend, resume and scu programming. + */ +struct cpu_pm_ops { + int (*finish_suspend)(unsigned long cpu_state); + void (*resume)(void); + void (*scu_prepare)(unsigned int cpu_id, unsigned int cpu_state); + void (*hotplug_restart)(void); +}; + +static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info); +static struct powerdomain *mpuss_pd; +static u32 cpu_context_offset; + +static int default_finish_suspend(unsigned long cpu_state) +{ + omap_do_wfi(); + return 0; +} + +static void dummy_cpu_resume(void) +{} + +static void dummy_scu_prepare(unsigned int cpu_id, unsigned int cpu_state) +{} + +static struct cpu_pm_ops omap_pm_ops = { + .finish_suspend = default_finish_suspend, + .resume = dummy_cpu_resume, + .scu_prepare = dummy_scu_prepare, + .hotplug_restart = dummy_cpu_resume, +}; + +/* + * Program the wakeup routine address for the CPU0 and CPU1 + * used for OFF or DORMANT wakeup. + */ +static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr) +{ + struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id); + + if (pm_info->wkup_sar_addr) + writel_relaxed(addr, pm_info->wkup_sar_addr); +} + +/* + * Store the SCU power status value to scratchpad memory + */ +static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state) +{ + struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id); + u32 scu_pwr_st; + + switch (cpu_state) { + case PWRDM_POWER_RET: + scu_pwr_st = SCU_PM_DORMANT; + break; + case PWRDM_POWER_OFF: + scu_pwr_st = SCU_PM_POWEROFF; + break; + case PWRDM_POWER_ON: + case PWRDM_POWER_INACTIVE: + default: + scu_pwr_st = SCU_PM_NORMAL; + break; + } + + if (pm_info->scu_sar_addr) + writel_relaxed(scu_pwr_st, pm_info->scu_sar_addr); +} + +/* Helper functions for MPUSS OSWR */ +static inline void mpuss_clear_prev_logic_pwrst(void) +{ + u32 reg; + + reg = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, + OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET); + omap4_prminst_write_inst_reg(reg, OMAP4430_PRM_PARTITION, + OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET); +} + +static inline void cpu_clear_prev_logic_pwrst(unsigned int cpu_id) +{ + u32 reg; + + if (cpu_id) { + reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU1_INST, + cpu_context_offset); + omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU1_INST, + cpu_context_offset); + } else { + reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU0_INST, + cpu_context_offset); + omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU0_INST, + cpu_context_offset); + } +} + +/* + * Store the CPU cluster state for L2X0 low power operations. + */ +static void l2x0_pwrst_prepare(unsigned int cpu_id, unsigned int save_state) +{ + struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id); + + if (pm_info->l2x0_sar_addr) + writel_relaxed(save_state, pm_info->l2x0_sar_addr); +} + +/* + * Save the L2X0 AUXCTRL and POR value to SAR memory. Its used to + * in every restore MPUSS OFF path. + */ +#ifdef CONFIG_CACHE_L2X0 +static void __init save_l2x0_context(void) +{ + void __iomem *l2x0_base = omap4_get_l2cache_base(); + + if (l2x0_base && sar_base) { + writel_relaxed(l2x0_saved_regs.aux_ctrl, + sar_base + L2X0_AUXCTRL_OFFSET); + writel_relaxed(l2x0_saved_regs.prefetch_ctrl, + sar_base + L2X0_PREFETCH_CTRL_OFFSET); + } +} +#else +static void __init save_l2x0_context(void) +{} +#endif + +/** + * omap4_enter_lowpower: OMAP4 MPUSS Low Power Entry Function + * The purpose of this function is to manage low power programming + * of OMAP4 MPUSS subsystem + * @cpu : CPU ID + * @power_state: Low power state. + * + * MPUSS states for the context save: + * save_state = + * 0 - Nothing lost and no need to save: MPUSS INACTIVE + * 1 - CPUx L1 and logic lost: MPUSS CSWR + * 2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR + * 3 - CPUx L1 and logic lost + GIC + L2 lost: DEVICE OFF + */ +int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) +{ + struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu); + unsigned int save_state = 0, cpu_logic_state = PWRDM_POWER_RET; + + if (omap_rev() == OMAP4430_REV_ES1_0) + return -ENXIO; + + switch (power_state) { + case PWRDM_POWER_ON: + case PWRDM_POWER_INACTIVE: + save_state = 0; + break; + case PWRDM_POWER_OFF: + cpu_logic_state = PWRDM_POWER_OFF; + save_state = 1; + break; + case PWRDM_POWER_RET: + if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) + save_state = 0; + break; + default: + /* + * CPUx CSWR is invalid hardware state. Also CPUx OSWR + * doesn't make much scense, since logic is lost and $L1 + * needs to be cleaned because of coherency. This makes + * CPUx OSWR equivalent to CPUX OFF and hence not supported + */ + WARN_ON(1); + return -ENXIO; + } + + pwrdm_pre_transition(NULL); + + /* + * Check MPUSS next state and save interrupt controller if needed. + * In MPUSS OSWR or device OFF, interrupt controller contest is lost. + */ + mpuss_clear_prev_logic_pwrst(); + if ((pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_RET) && + (pwrdm_read_logic_retst(mpuss_pd) == PWRDM_POWER_OFF)) + save_state = 2; + + cpu_clear_prev_logic_pwrst(cpu); + pwrdm_set_next_pwrst(pm_info->pwrdm, power_state); + pwrdm_set_logic_retst(pm_info->pwrdm, cpu_logic_state); + set_cpu_wakeup_addr(cpu, __pa_symbol(omap_pm_ops.resume)); + omap_pm_ops.scu_prepare(cpu, power_state); + l2x0_pwrst_prepare(cpu, save_state); + + /* + * Call low level function with targeted low power state. + */ + if (save_state) + cpu_suspend(save_state, omap_pm_ops.finish_suspend); + else + omap_pm_ops.finish_suspend(save_state); + + if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD) && cpu) + gic_dist_enable(); + + /* + * Restore the CPUx power state to ON otherwise CPUx + * power domain can transitions to programmed low power + * state while doing WFI outside the low powe code. On + * secure devices, CPUx does WFI which can result in + * domain transition + */ + pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON); + + pwrdm_post_transition(NULL); + + return 0; +} + +/** + * omap4_hotplug_cpu: OMAP4 CPU hotplug entry + * @cpu : CPU ID + * @power_state: CPU low power state. + */ +int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state) +{ + struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu); + unsigned int cpu_state = 0; + + if (omap_rev() == OMAP4430_REV_ES1_0) + return -ENXIO; + + /* Use the achievable power state for the domain */ + power_state = pwrdm_get_valid_lp_state(pm_info->pwrdm, + false, power_state); + + if (power_state == PWRDM_POWER_OFF) + cpu_state = 1; + + pwrdm_clear_all_prev_pwrst(pm_info->pwrdm); + pwrdm_set_next_pwrst(pm_info->pwrdm, power_state); + set_cpu_wakeup_addr(cpu, __pa_symbol(omap_pm_ops.hotplug_restart)); + omap_pm_ops.scu_prepare(cpu, power_state); + + /* + * CPU never retuns back if targeted power state is OFF mode. + * CPU ONLINE follows normal CPU ONLINE ptah via + * omap4_secondary_startup(). + */ + omap_pm_ops.finish_suspend(cpu_state); + + pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON); + return 0; +} + + +/* + * Enable Mercury Fast HG retention mode by default. + */ +static void enable_mercury_retention_mode(void) +{ + u32 reg; + + reg = omap4_prcm_mpu_read_inst_reg(OMAP54XX_PRCM_MPU_DEVICE_INST, + OMAP54XX_PRCM_MPU_PRM_PSCON_COUNT_OFFSET); + /* Enable HG_EN, HG_RAMPUP = fast mode */ + reg |= BIT(24) | BIT(25); + omap4_prcm_mpu_write_inst_reg(reg, OMAP54XX_PRCM_MPU_DEVICE_INST, + OMAP54XX_PRCM_MPU_PRM_PSCON_COUNT_OFFSET); +} + +/* + * Initialise OMAP4 MPUSS + */ +int __init omap4_mpuss_init(void) +{ + struct omap4_cpu_pm_info *pm_info; + + if (omap_rev() == OMAP4430_REV_ES1_0) { + WARN(1, "Power Management not supported on OMAP4430 ES1.0\n"); + return -ENODEV; + } + + /* Initilaise per CPU PM information */ + pm_info = &per_cpu(omap4_pm_info, 0x0); + if (sar_base) { + pm_info->scu_sar_addr = sar_base + SCU_OFFSET0; + if (cpu_is_omap44xx()) + pm_info->wkup_sar_addr = sar_base + + CPU0_WAKEUP_NS_PA_ADDR_OFFSET; + else + pm_info->wkup_sar_addr = sar_base + + OMAP5_CPU0_WAKEUP_NS_PA_ADDR_OFFSET; + pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET0; + } + pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm"); + if (!pm_info->pwrdm) { + pr_err("Lookup failed for CPU0 pwrdm\n"); + return -ENODEV; + } + + /* Clear CPU previous power domain state */ + pwrdm_clear_all_prev_pwrst(pm_info->pwrdm); + cpu_clear_prev_logic_pwrst(0); + + /* Initialise CPU0 power domain state to ON */ + pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON); + + pm_info = &per_cpu(omap4_pm_info, 0x1); + if (sar_base) { + pm_info->scu_sar_addr = sar_base + SCU_OFFSET1; + if (cpu_is_omap44xx()) + pm_info->wkup_sar_addr = sar_base + + CPU1_WAKEUP_NS_PA_ADDR_OFFSET; + else + pm_info->wkup_sar_addr = sar_base + + OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET; + pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET1; + } + + pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm"); + if (!pm_info->pwrdm) { + pr_err("Lookup failed for CPU1 pwrdm\n"); + return -ENODEV; + } + + /* Clear CPU previous power domain state */ + pwrdm_clear_all_prev_pwrst(pm_info->pwrdm); + cpu_clear_prev_logic_pwrst(1); + + /* Initialise CPU1 power domain state to ON */ + pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON); + + mpuss_pd = pwrdm_lookup("mpu_pwrdm"); + if (!mpuss_pd) { + pr_err("Failed to lookup MPUSS power domain\n"); + return -ENODEV; + } + pwrdm_clear_all_prev_pwrst(mpuss_pd); + mpuss_clear_prev_logic_pwrst(); + + if (sar_base) { + /* Save device type on scratchpad for low level code to use */ + writel_relaxed((omap_type() != OMAP2_DEVICE_TYPE_GP) ? 1 : 0, + sar_base + OMAP_TYPE_OFFSET); + save_l2x0_context(); + } + + if (cpu_is_omap44xx()) { + omap_pm_ops.finish_suspend = omap4_finish_suspend; + omap_pm_ops.resume = omap4_cpu_resume; + omap_pm_ops.scu_prepare = scu_pwrst_prepare; + omap_pm_ops.hotplug_restart = omap4_secondary_startup; + cpu_context_offset = OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET; + } else if (soc_is_omap54xx() || soc_is_dra7xx()) { + cpu_context_offset = OMAP54XX_RM_CPU0_CPU0_CONTEXT_OFFSET; + enable_mercury_retention_mode(); + } + + if (cpu_is_omap446x()) + omap_pm_ops.hotplug_restart = omap4460_secondary_startup; + + return 0; +} + +#endif + +u32 omap4_get_cpu1_ns_pa_addr(void) +{ + return old_cpu1_ns_pa_addr; +} + +/* + * For kexec, we must set CPU1_WAKEUP_NS_PA_ADDR to point to + * current kernel's secondary_startup() early before + * clockdomains_init(). Otherwise clockdomain_init() can + * wake CPU1 and cause a hang. + */ +void __init omap4_mpuss_early_init(void) +{ + unsigned long startup_pa; + void __iomem *ns_pa_addr; + + if (!(soc_is_omap44xx() || soc_is_omap54xx())) + return; + + sar_base = omap4_get_sar_ram_base(); + + /* Save old NS_PA_ADDR for validity checks later on */ + if (soc_is_omap44xx()) + ns_pa_addr = sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET; + else + ns_pa_addr = sar_base + OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET; + old_cpu1_ns_pa_addr = readl_relaxed(ns_pa_addr); + + if (soc_is_omap443x()) + startup_pa = __pa_symbol(omap4_secondary_startup); + else if (soc_is_omap446x()) + startup_pa = __pa_symbol(omap4460_secondary_startup); + else if ((__boot_cpu_mode & MODE_MASK) == HYP_MODE) + startup_pa = __pa_symbol(omap5_secondary_hyp_startup); + else + startup_pa = __pa_symbol(omap5_secondary_startup); + + if (soc_is_omap44xx()) + writel_relaxed(startup_pa, sar_base + + CPU1_WAKEUP_NS_PA_ADDR_OFFSET); + else + writel_relaxed(startup_pa, sar_base + + OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET); +} diff --git a/arch/arm/mach-omap2/omap-secure.c b/arch/arm/mach-omap2/omap-secure.c new file mode 100644 index 000000000..fb9c114b9 --- /dev/null +++ b/arch/arm/mach-omap2/omap-secure.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP Secure API infrastructure. + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * Copyright (C) 2012 Ivaylo Dimitrov <freemangordon@abv.bg> + * Copyright (C) 2013 Pali Rohár <pali@kernel.org> + */ + +#include <linux/arm-smccc.h> +#include <linux/cpu_pm.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/memblock.h> +#include <linux/of.h> + +#include <asm/cacheflush.h> +#include <asm/memblock.h> + +#include "common.h" +#include "omap-secure.h" +#include "soc.h" + +static phys_addr_t omap_secure_memblock_base; + +bool optee_available; + +#define OMAP_SIP_SMC_STD_CALL_VAL(func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_SIP, (func_num)) + +static void __init omap_optee_init_check(void) +{ + struct device_node *np; + + /* + * We only check that the OP-TEE node is present and available. The + * OP-TEE kernel driver is not needed for the type of interaction made + * with OP-TEE here so the driver's status is not checked. + */ + np = of_find_node_by_path("/firmware/optee"); + if (np && of_device_is_available(np)) + optee_available = true; + of_node_put(np); +} + +/** + * omap_sec_dispatcher: Routine to dispatch low power secure + * service routines + * @idx: The HAL API index + * @flag: The flag indicating criticality of operation + * @nargs: Number of valid arguments out of four. + * @arg1, arg2, arg3 args4: Parameters passed to secure API + * + * Return the non-zero error value on failure. + */ +u32 omap_secure_dispatcher(u32 idx, u32 flag, u32 nargs, u32 arg1, u32 arg2, + u32 arg3, u32 arg4) +{ + static u32 buf[NR_CPUS][5]; + u32 *param; + int cpu; + u32 ret; + + cpu = get_cpu(); + param = buf[cpu]; + + param[0] = nargs; + param[1] = arg1; + param[2] = arg2; + param[3] = arg3; + param[4] = arg4; + + /* + * Secure API needs physical address + * pointer for the parameters + */ + flush_cache_all(); + outer_clean_range(__pa(param), __pa(param + 5)); + ret = omap_smc2(idx, flag, __pa(param)); + + put_cpu(); + + return ret; +} + +void omap_smccc_smc(u32 fn, u32 arg) +{ + struct arm_smccc_res res; + + arm_smccc_smc(OMAP_SIP_SMC_STD_CALL_VAL(fn), arg, + 0, 0, 0, 0, 0, 0, &res); + WARN(res.a0, "Secure function call 0x%08x failed\n", fn); +} + +void omap_smc1(u32 fn, u32 arg) +{ + /* + * If this platform has OP-TEE installed we use ARM SMC calls + * otherwise fall back to the OMAP ROM style calls. + */ + if (optee_available) + omap_smccc_smc(fn, arg); + else + _omap_smc1(fn, arg); +} + +/* Allocate the memory to save secure ram */ +int __init omap_secure_ram_reserve_memblock(void) +{ + u32 size = OMAP_SECURE_RAM_STORAGE; + + size = ALIGN(size, SECTION_SIZE); + omap_secure_memblock_base = arm_memblock_steal(size, SECTION_SIZE); + + return 0; +} + +phys_addr_t omap_secure_ram_mempool_base(void) +{ + return omap_secure_memblock_base; +} + +#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM) +u32 omap3_save_secure_ram(void *addr, int size) +{ + static u32 param[5]; + u32 ret; + + if (size != OMAP3_SAVE_SECURE_RAM_SZ) + return OMAP3_SAVE_SECURE_RAM_SZ; + + param[0] = 4; /* Number of arguments */ + param[1] = __pa(addr); /* Physical address for saving */ + param[2] = 0; + param[3] = 1; + param[4] = 1; + + ret = save_secure_ram_context(__pa(param)); + + return ret; +} +#endif + +/** + * rx51_secure_dispatcher: Routine to dispatch secure PPA API calls + * @idx: The PPA API index + * @process: Process ID + * @flag: The flag indicating criticality of operation + * @nargs: Number of valid arguments out of four. + * @arg1, arg2, arg3 args4: Parameters passed to secure API + * + * Return the non-zero error value on failure. + * + * NOTE: rx51_secure_dispatcher differs from omap_secure_dispatcher because + * it calling omap_smc3() instead omap_smc2() and param[0] is nargs+1 + */ +u32 rx51_secure_dispatcher(u32 idx, u32 process, u32 flag, u32 nargs, + u32 arg1, u32 arg2, u32 arg3, u32 arg4) +{ + static u32 param[5]; + u32 ret; + + param[0] = nargs+1; /* RX-51 needs number of arguments + 1 */ + param[1] = arg1; + param[2] = arg2; + param[3] = arg3; + param[4] = arg4; + + /* + * Secure API needs physical address + * pointer for the parameters + */ + local_irq_disable(); + local_fiq_disable(); + flush_cache_all(); + outer_clean_range(__pa(param), __pa(param + 5)); + ret = omap_smc3(idx, process, flag, __pa(param)); + flush_cache_all(); + local_fiq_enable(); + local_irq_enable(); + + return ret; +} + +/** + * rx51_secure_update_aux_cr: Routine to modify the contents of Auxiliary Control Register + * @set_bits: bits to set in ACR + * @clr_bits: bits to clear in ACR + * + * Return the non-zero error value on failure. +*/ +u32 rx51_secure_update_aux_cr(u32 set_bits, u32 clear_bits) +{ + u32 acr; + + /* Read ACR */ + asm volatile ("mrc p15, 0, %0, c1, c0, 1" : "=r" (acr)); + acr &= ~clear_bits; + acr |= set_bits; + + return rx51_secure_dispatcher(RX51_PPA_WRITE_ACR, + 0, + FLAG_START_CRITICAL, + 1, acr, 0, 0, 0); +} + +/** + * rx51_secure_rng_call: Routine for HW random generator + */ +u32 rx51_secure_rng_call(u32 ptr, u32 count, u32 flag) +{ + return rx51_secure_dispatcher(RX51_PPA_HWRNG, + 0, + NO_FLAG, + 3, ptr, count, flag, 0); +} + +void __init omap_secure_init(void) +{ + omap_optee_init_check(); +} + +/* + * Dummy dispatcher call after core OSWR and MPU off. Updates the ROM return + * address after MMU has been re-enabled after CPU1 has been woken up again. + * Otherwise the ROM code will attempt to use the earlier physical return + * address that got set with MMU off when waking up CPU1. Only used on secure + * devices. + */ +static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_EXIT: + omap_secure_dispatcher(OMAP4_PPA_SERVICE_0, + FLAG_START_CRITICAL, + 0, 0, 0, 0, 0); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block secure_notifier_block = { + .notifier_call = cpu_notifier, +}; + +static int __init secure_pm_init(void) +{ + if (omap_type() == OMAP2_DEVICE_TYPE_GP || !soc_is_omap44xx()) + return 0; + + cpu_pm_register_notifier(&secure_notifier_block); + + return 0; +} +omap_arch_initcall(secure_pm_init); diff --git a/arch/arm/mach-omap2/omap-secure.h b/arch/arm/mach-omap2/omap-secure.h new file mode 100644 index 000000000..9e67d4efd --- /dev/null +++ b/arch/arm/mach-omap2/omap-secure.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap-secure.h: OMAP Secure infrastructure header. + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * Copyright (C) 2012 Ivaylo Dimitrov <freemangordon@abv.bg> + * Copyright (C) 2013 Pali Rohár <pali@kernel.org> + */ +#ifndef OMAP_ARCH_OMAP_SECURE_H +#define OMAP_ARCH_OMAP_SECURE_H + +#include <linux/types.h> + +/* Monitor error code */ +#define API_HAL_RET_VALUE_NS2S_CONVERSION_ERROR 0xFFFFFFFE +#define API_HAL_RET_VALUE_SERVICE_UNKNWON 0xFFFFFFFF + +/* HAL API error codes */ +#define API_HAL_RET_VALUE_OK 0x00 +#define API_HAL_RET_VALUE_FAIL 0x01 + +/* Secure HAL API flags */ +#define FLAG_START_CRITICAL 0x4 +#define FLAG_IRQFIQ_MASK 0x3 +#define FLAG_IRQ_ENABLE 0x2 +#define FLAG_FIQ_ENABLE 0x1 +#define NO_FLAG 0x0 + +/* Maximum Secure memory storage size */ +#define OMAP_SECURE_RAM_STORAGE (88 * SZ_1K) + +#define OMAP3_SAVE_SECURE_RAM_SZ 0x803F + +/* Secure low power HAL API index */ +#define OMAP4_HAL_SAVESECURERAM_INDEX 0x1a +#define OMAP4_HAL_SAVEHW_INDEX 0x1b +#define OMAP4_HAL_SAVEALL_INDEX 0x1c +#define OMAP4_HAL_SAVEGIC_INDEX 0x1d + +/* Secure Monitor mode APIs */ +#define OMAP4_MON_SCU_PWR_INDEX 0x108 +#define OMAP4_MON_L2X0_DBG_CTRL_INDEX 0x100 +#define OMAP4_MON_L2X0_CTRL_INDEX 0x102 +#define OMAP4_MON_L2X0_AUXCTRL_INDEX 0x109 +#define OMAP4_MON_L2X0_PREFETCH_INDEX 0x113 + +#define OMAP5_DRA7_MON_SET_CNTFRQ_INDEX 0x109 +#define OMAP5_MON_AMBA_IF_INDEX 0x108 +#define OMAP5_DRA7_MON_SET_ACR_INDEX 0x107 + +/* Secure PPA(Primary Protected Application) APIs */ +#define OMAP4_PPA_SERVICE_0 0x21 +#define OMAP4_PPA_L2_POR_INDEX 0x23 +#define OMAP4_PPA_CPU_ACTRL_SMP_INDEX 0x25 + +#define AM43xx_PPA_SVC_PM_SUSPEND 0x71 +#define AM43xx_PPA_SVC_PM_RESUME 0x72 + +/* Secure RX-51 PPA (Primary Protected Application) APIs */ +#define RX51_PPA_HWRNG 29 +#define RX51_PPA_L2_INVAL 40 +#define RX51_PPA_WRITE_ACR 42 + +#ifndef __ASSEMBLER__ + +extern u32 omap_secure_dispatcher(u32 idx, u32 flag, u32 nargs, + u32 arg1, u32 arg2, u32 arg3, u32 arg4); +extern void omap_smccc_smc(u32 fn, u32 arg); +extern void omap_smc1(u32 fn, u32 arg); +extern u32 omap_smc2(u32 id, u32 falg, u32 pargs); +extern u32 omap_smc3(u32 id, u32 process, u32 flag, u32 pargs); +extern phys_addr_t omap_secure_ram_mempool_base(void); +extern int omap_secure_ram_reserve_memblock(void); +extern u32 save_secure_ram_context(u32 args_pa); +extern u32 omap3_save_secure_ram(void *save_regs, int size); + +extern u32 rx51_secure_dispatcher(u32 idx, u32 process, u32 flag, u32 nargs, + u32 arg1, u32 arg2, u32 arg3, u32 arg4); +extern u32 rx51_secure_update_aux_cr(u32 set_bits, u32 clear_bits); +extern u32 rx51_secure_rng_call(u32 ptr, u32 count, u32 flag); + +extern bool optee_available; +void omap_secure_init(void); + +#ifdef CONFIG_SOC_HAS_REALTIME_COUNTER +void set_cntfreq(void); +#else +static inline void set_cntfreq(void) +{ +} +#endif + +#endif /* __ASSEMBLER__ */ +#endif /* OMAP_ARCH_OMAP_SECURE_H */ diff --git a/arch/arm/mach-omap2/omap-smc.S b/arch/arm/mach-omap2/omap-smc.S new file mode 100644 index 000000000..7376f5280 --- /dev/null +++ b/arch/arm/mach-omap2/omap-smc.S @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP34xx and OMAP44xx secure APIs file. + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Written by Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * Copyright (C) 2012 Ivaylo Dimitrov <freemangordon@abv.bg> + * Copyright (C) 2013 Pali Rohár <pali@kernel.org> + */ + +#include <linux/linkage.h> + +/* + * This is common routine to manage secure monitor API + * used to modify the PL310 secure registers. + * 'r0' contains the value to be modified and 'r12' contains + * the monitor API number. It uses few CPU registers + * internally and hence they need be backed up including + * link register "lr". + * Function signature : void _omap_smc1(u32 fn, u32 arg) + */ + .arch armv7-a + .arch_extension sec +ENTRY(_omap_smc1) + stmfd sp!, {r2-r12, lr} + mov r12, r0 + mov r0, r1 + dsb + smc #0 + ldmfd sp!, {r2-r12, pc} +ENDPROC(_omap_smc1) + +/** + * u32 omap_smc2(u32 id, u32 falg, u32 pargs) + * Low level common routine for secure HAL and PPA APIs. + * @id: Application ID of HAL APIs + * @flag: Flag to indicate the criticality of operation + * @pargs: Physical address of parameter list starting + * with number of parametrs + */ +ENTRY(omap_smc2) + stmfd sp!, {r4-r12, lr} + mov r3, r2 + mov r2, r1 + mov r1, #0x0 @ Process ID + mov r6, #0xff + mov r12, #0x00 @ Secure Service ID + mov r7, #0 + mcr p15, 0, r7, c7, c5, 6 + dsb + dmb + smc #0 + ldmfd sp!, {r4-r12, pc} +ENDPROC(omap_smc2) + +/** + * u32 omap_smc3(u32 service_id, u32 process_id, u32 flag, u32 pargs) + * Low level common routine for secure HAL and PPA APIs via smc #1 + * r0 - @service_id: Secure Service ID + * r1 - @process_id: Process ID + * r2 - @flag: Flag to indicate the criticality of operation + * r3 - @pargs: Physical address of parameter list + */ +ENTRY(omap_smc3) + stmfd sp!, {r4-r11, lr} + mov r12, r0 @ Copy the secure service ID + mov r6, #0xff @ Indicate new Task call + dsb @ Memory Barrier (not sure if needed, copied from omap_smc2) + smc #1 @ Call PPA service + ldmfd sp!, {r4-r11, pc} +ENDPROC(omap_smc3) + +ENTRY(omap_modify_auxcoreboot0) + stmfd sp!, {r1-r12, lr} + ldr r12, =0x104 + dsb + smc #0 + ldmfd sp!, {r1-r12, pc} +ENDPROC(omap_modify_auxcoreboot0) + +ENTRY(omap_auxcoreboot_addr) + stmfd sp!, {r2-r12, lr} + ldr r12, =0x105 + dsb + smc #0 + ldmfd sp!, {r2-r12, pc} +ENDPROC(omap_auxcoreboot_addr) + +ENTRY(omap_read_auxcoreboot0) + stmfd sp!, {r2-r12, lr} + ldr r12, =0x103 + dsb + smc #0 + ldmfd sp!, {r2-r12, pc} +ENDPROC(omap_read_auxcoreboot0) diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c new file mode 100644 index 000000000..570a987e6 --- /dev/null +++ b/arch/arm/mach-omap2/omap-smp.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 SMP source file. It contains platform specific functions + * needed for the linux smp kernel. + * + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Author: + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * Platform file needed for the OMAP4 SMP. This file is based on arm + * realview smp platform. + * * Copyright (c) 2002 ARM Limited. + */ +#include <linux/init.h> +#include <linux/device.h> +#include <linux/smp.h> +#include <linux/io.h> +#include <linux/irqchip/arm-gic.h> + +#include <asm/sections.h> +#include <asm/smp_scu.h> +#include <asm/virt.h> + +#include "omap-secure.h" +#include "omap-wakeupgen.h" +#include <asm/cputype.h> + +#include "soc.h" +#include "iomap.h" +#include "common.h" +#include "clockdomain.h" +#include "pm.h" + +#define CPU_MASK 0xff0ffff0 +#define CPU_CORTEX_A9 0x410FC090 +#define CPU_CORTEX_A15 0x410FC0F0 + +#define OMAP5_CORE_COUNT 0x2 + +#define AUX_CORE_BOOT0_GP_RELEASE 0x020 +#define AUX_CORE_BOOT0_HS_RELEASE 0x200 + +struct omap_smp_config { + unsigned long cpu1_rstctrl_pa; + void __iomem *cpu1_rstctrl_va; + void __iomem *scu_base; + void __iomem *wakeupgen_base; + void *startup_addr; +}; + +static struct omap_smp_config cfg; + +static const struct omap_smp_config omap443x_cfg __initconst = { + .cpu1_rstctrl_pa = 0x4824380c, + .startup_addr = omap4_secondary_startup, +}; + +static const struct omap_smp_config omap446x_cfg __initconst = { + .cpu1_rstctrl_pa = 0x4824380c, + .startup_addr = omap4460_secondary_startup, +}; + +static const struct omap_smp_config omap5_cfg __initconst = { + .cpu1_rstctrl_pa = 0x48243810, + .startup_addr = omap5_secondary_startup, +}; + +void __iomem *omap4_get_scu_base(void) +{ + return cfg.scu_base; +} + +#ifdef CONFIG_OMAP5_ERRATA_801819 +static void omap5_erratum_workaround_801819(void) +{ + u32 acr, revidr; + u32 acr_mask; + + /* REVIDR[3] indicates erratum fix available on silicon */ + asm volatile ("mrc p15, 0, %0, c0, c0, 6" : "=r" (revidr)); + if (revidr & (0x1 << 3)) + return; + + asm volatile ("mrc p15, 0, %0, c1, c0, 1" : "=r" (acr)); + /* + * BIT(27) - Disables streaming. All write-allocate lines allocate in + * the L1 or L2 cache. + * BIT(25) - Disables streaming. All write-allocate lines allocate in + * the L1 cache. + */ + acr_mask = (0x3 << 25) | (0x3 << 27); + /* do we already have it done.. if yes, skip expensive smc */ + if ((acr & acr_mask) == acr_mask) + return; + + acr |= acr_mask; + omap_smc1(OMAP5_DRA7_MON_SET_ACR_INDEX, acr); + + pr_debug("%s: ARM erratum workaround 801819 applied on CPU%d\n", + __func__, smp_processor_id()); +} +#else +static inline void omap5_erratum_workaround_801819(void) { } +#endif + +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +/* + * Configure ACR and enable ACTLR[0] (Enable invalidates of BTB with + * ICIALLU) to activate the workaround for secondary Core. + * NOTE: it is assumed that the primary core's configuration is done + * by the boot loader (kernel will detect a misconfiguration and complain + * if this is not done). + * + * In General Purpose(GP) devices, ACR bit settings can only be done + * by ROM code in "secure world" using the smc call and there is no + * option to update the "firmware" on such devices. This also works for + * High security(HS) devices, as a backup option in case the + * "update" is not done in the "security firmware". + */ +static void omap5_secondary_harden_predictor(void) +{ + u32 acr, acr_mask; + + asm volatile ("mrc p15, 0, %0, c1, c0, 1" : "=r" (acr)); + + /* + * ACTLR[0] (Enable invalidates of BTB with ICIALLU) + */ + acr_mask = BIT(0); + + /* Do we already have it done.. if yes, skip expensive smc */ + if ((acr & acr_mask) == acr_mask) + return; + + acr |= acr_mask; + omap_smc1(OMAP5_DRA7_MON_SET_ACR_INDEX, acr); + + pr_debug("%s: ARM ACR setup for CVE_2017_5715 applied on CPU%d\n", + __func__, smp_processor_id()); +} +#else +static inline void omap5_secondary_harden_predictor(void) { } +#endif + +static void omap4_secondary_init(unsigned int cpu) +{ + /* + * Configure ACTRL and enable NS SMP bit access on CPU1 on HS device. + * OMAP44XX EMU/HS devices - CPU0 SMP bit access is enabled in PPA + * init and for CPU1, a secure PPA API provided. CPU0 must be ON + * while executing NS_SMP API on CPU1 and PPA version must be 1.4.0+. + * OMAP443X GP devices- SMP bit isn't accessible. + * OMAP446X GP devices - SMP bit access is enabled on both CPUs. + */ + if (soc_is_omap443x() && (omap_type() != OMAP2_DEVICE_TYPE_GP)) + omap_secure_dispatcher(OMAP4_PPA_CPU_ACTRL_SMP_INDEX, + 4, 0, 0, 0, 0, 0); + + if (soc_is_omap54xx() || soc_is_dra7xx()) { + /* + * Configure the CNTFRQ register for the secondary cpu's which + * indicates the frequency of the cpu local timers. + */ + set_cntfreq(); + /* Configure ACR to disable streaming WA for 801819 */ + omap5_erratum_workaround_801819(); + /* Enable ACR to allow for ICUALLU workaround */ + omap5_secondary_harden_predictor(); + } +} + +static int omap4_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + static struct clockdomain *cpu1_clkdm; + static bool booted; + static struct powerdomain *cpu1_pwrdm; + + /* + * Update the AuxCoreBoot0 with boot state for secondary core. + * omap4_secondary_startup() routine will hold the secondary core till + * the AuxCoreBoot1 register is updated with cpu state + * A barrier is added to ensure that write buffer is drained + */ + if (omap_secure_apis_support()) + omap_modify_auxcoreboot0(AUX_CORE_BOOT0_HS_RELEASE, + 0xfffffdff); + else + writel_relaxed(AUX_CORE_BOOT0_GP_RELEASE, + cfg.wakeupgen_base + OMAP_AUX_CORE_BOOT_0); + + if (!cpu1_clkdm && !cpu1_pwrdm) { + cpu1_clkdm = clkdm_lookup("mpu1_clkdm"); + cpu1_pwrdm = pwrdm_lookup("cpu1_pwrdm"); + } + + /* + * The SGI(Software Generated Interrupts) are not wakeup capable + * from low power states. This is known limitation on OMAP4 and + * needs to be worked around by using software forced clockdomain + * wake-up. To wakeup CPU1, CPU0 forces the CPU1 clockdomain to + * software force wakeup. The clockdomain is then put back to + * hardware supervised mode. + * More details can be found in OMAP4430 TRM - Version J + * Section : + * 4.3.4.2 Power States of CPU0 and CPU1 + */ + if (booted && cpu1_pwrdm && cpu1_clkdm) { + /* + * GIC distributor control register has changed between + * CortexA9 r1pX and r2pX. The Control Register secure + * banked version is now composed of 2 bits: + * bit 0 == Secure Enable + * bit 1 == Non-Secure Enable + * The Non-Secure banked register has not changed + * Because the ROM Code is based on the r1pX GIC, the CPU1 + * GIC restoration will cause a problem to CPU0 Non-Secure SW. + * The workaround must be: + * 1) Before doing the CPU1 wakeup, CPU0 must disable + * the GIC distributor + * 2) CPU1 must re-enable the GIC distributor on + * it's wakeup path. + */ + if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD)) { + local_irq_disable(); + gic_dist_disable(); + } + + /* + * Ensure that CPU power state is set to ON to avoid CPU + * powerdomain transition on wfi + */ + clkdm_deny_idle_nolock(cpu1_clkdm); + pwrdm_set_next_pwrst(cpu1_pwrdm, PWRDM_POWER_ON); + clkdm_allow_idle_nolock(cpu1_clkdm); + + if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD)) { + while (gic_dist_disabled()) { + udelay(1); + cpu_relax(); + } + gic_timer_retrigger(); + local_irq_enable(); + } + } else { + dsb_sev(); + booted = true; + } + + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + return 0; +} + +/* + * Initialise the CPU possible map early - this describes the CPUs + * which may be present or become present in the system. + */ +static void __init omap4_smp_init_cpus(void) +{ + unsigned int i = 0, ncores = 1, cpu_id; + + /* Use ARM cpuid check here, as SoC detection will not work so early */ + cpu_id = read_cpuid_id() & CPU_MASK; + if (cpu_id == CPU_CORTEX_A9) { + /* + * Currently we can't call ioremap here because + * SoC detection won't work until after init_early. + */ + cfg.scu_base = OMAP2_L4_IO_ADDRESS(scu_a9_get_base()); + BUG_ON(!cfg.scu_base); + ncores = scu_get_core_count(cfg.scu_base); + } else if (cpu_id == CPU_CORTEX_A15) { + ncores = OMAP5_CORE_COUNT; + } + + /* sanity check */ + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; + } + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); +} + +/* + * For now, just make sure the start-up address is not within the booting + * kernel space as that means we just overwrote whatever secondary_startup() + * code there was. + */ +static bool __init omap4_smp_cpu1_startup_valid(unsigned long addr) +{ + if ((addr >= __pa(PAGE_OFFSET)) && (addr <= __pa(__bss_start))) + return false; + + return true; +} + +/* + * We may need to reset CPU1 before configuring, otherwise kexec boot can end + * up trying to use old kernel startup address or suspend-resume will + * occasionally fail to bring up CPU1 on 4430 if CPU1 fails to enter deeper + * idle states. + */ +static void __init omap4_smp_maybe_reset_cpu1(struct omap_smp_config *c) +{ + unsigned long cpu1_startup_pa, cpu1_ns_pa_addr; + bool needs_reset = false; + u32 released; + + if (omap_secure_apis_support()) + released = omap_read_auxcoreboot0() & AUX_CORE_BOOT0_HS_RELEASE; + else + released = readl_relaxed(cfg.wakeupgen_base + + OMAP_AUX_CORE_BOOT_0) & + AUX_CORE_BOOT0_GP_RELEASE; + if (released) { + pr_warn("smp: CPU1 not parked?\n"); + + return; + } + + cpu1_startup_pa = readl_relaxed(cfg.wakeupgen_base + + OMAP_AUX_CORE_BOOT_1); + + /* Did the configured secondary_startup() get overwritten? */ + if (!omap4_smp_cpu1_startup_valid(cpu1_startup_pa)) + needs_reset = true; + + /* + * If omap4 or 5 has NS_PA_ADDR configured, CPU1 may be in a + * deeper idle state in WFI and will wake to an invalid address. + */ + if ((soc_is_omap44xx() || soc_is_omap54xx())) { + cpu1_ns_pa_addr = omap4_get_cpu1_ns_pa_addr(); + if (!omap4_smp_cpu1_startup_valid(cpu1_ns_pa_addr)) + needs_reset = true; + } else { + cpu1_ns_pa_addr = 0; + } + + if (!needs_reset || !c->cpu1_rstctrl_va) + return; + + pr_info("smp: CPU1 parked within kernel, needs reset (0x%lx 0x%lx)\n", + cpu1_startup_pa, cpu1_ns_pa_addr); + + writel_relaxed(1, c->cpu1_rstctrl_va); + readl_relaxed(c->cpu1_rstctrl_va); + writel_relaxed(0, c->cpu1_rstctrl_va); +} + +static void __init omap4_smp_prepare_cpus(unsigned int max_cpus) +{ + const struct omap_smp_config *c = NULL; + + if (soc_is_omap443x()) + c = &omap443x_cfg; + else if (soc_is_omap446x()) + c = &omap446x_cfg; + else if (soc_is_dra74x() || soc_is_omap54xx() || soc_is_dra76x()) + c = &omap5_cfg; + + if (!c) { + pr_err("%s Unknown SMP SoC?\n", __func__); + return; + } + + /* Must preserve cfg.scu_base set earlier */ + cfg.cpu1_rstctrl_pa = c->cpu1_rstctrl_pa; + cfg.startup_addr = c->startup_addr; + cfg.wakeupgen_base = omap_get_wakeupgen_base(); + + if (soc_is_dra74x() || soc_is_omap54xx() || soc_is_dra76x()) { + if ((__boot_cpu_mode & MODE_MASK) == HYP_MODE) + cfg.startup_addr = omap5_secondary_hyp_startup; + omap5_erratum_workaround_801819(); + } + + cfg.cpu1_rstctrl_va = ioremap(cfg.cpu1_rstctrl_pa, 4); + if (!cfg.cpu1_rstctrl_va) + return; + + /* + * Initialise the SCU and wake up the secondary core using + * wakeup_secondary(). + */ + if (cfg.scu_base) + scu_enable(cfg.scu_base); + + omap4_smp_maybe_reset_cpu1(&cfg); + + /* + * Write the address of secondary startup routine into the + * AuxCoreBoot1 where ROM code will jump and start executing + * on secondary core once out of WFE + * A barrier is added to ensure that write buffer is drained + */ + if (omap_secure_apis_support()) + omap_auxcoreboot_addr(__pa_symbol(cfg.startup_addr)); + else + writel_relaxed(__pa_symbol(cfg.startup_addr), + cfg.wakeupgen_base + OMAP_AUX_CORE_BOOT_1); +} + +const struct smp_operations omap4_smp_ops __initconst = { + .smp_init_cpus = omap4_smp_init_cpus, + .smp_prepare_cpus = omap4_smp_prepare_cpus, + .smp_secondary_init = omap4_secondary_init, + .smp_boot_secondary = omap4_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = omap4_cpu_die, + .cpu_kill = omap4_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c new file mode 100644 index 000000000..8d21e3a3c --- /dev/null +++ b/arch/arm/mach-omap2/omap-wakeupgen.c @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP WakeupGen Source file + * + * OMAP WakeupGen is the interrupt controller extension used along + * with ARM GIC to wake the CPU out from low power states on + * external interrupts. It is responsible for generating wakeup + * event from the incoming interrupts and enable bits. It is + * implemented in MPU always ON power domain. During normal operation, + * WakeupGen delivers external interrupts directly to the GIC. + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqdomain.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/cpu.h> +#include <linux/notifier.h> +#include <linux/cpu_pm.h> + +#include "omap-wakeupgen.h" +#include "omap-secure.h" + +#include "soc.h" +#include "omap4-sar-layout.h" +#include "common.h" +#include "pm.h" + +#define AM43XX_NR_REG_BANKS 7 +#define AM43XX_IRQS 224 +#define MAX_NR_REG_BANKS AM43XX_NR_REG_BANKS +#define MAX_IRQS AM43XX_IRQS +#define DEFAULT_NR_REG_BANKS 5 +#define DEFAULT_IRQS 160 +#define WKG_MASK_ALL 0x00000000 +#define WKG_UNMASK_ALL 0xffffffff +#define CPU_ENA_OFFSET 0x400 +#define CPU0_ID 0x0 +#define CPU1_ID 0x1 +#define OMAP4_NR_BANKS 4 +#define OMAP4_NR_IRQS 128 + +#define SYS_NIRQ1_EXT_SYS_IRQ_1 7 +#define SYS_NIRQ2_EXT_SYS_IRQ_2 119 + +static void __iomem *wakeupgen_base; +static void __iomem *sar_base; +static DEFINE_RAW_SPINLOCK(wakeupgen_lock); +static unsigned int irq_target_cpu[MAX_IRQS]; +static unsigned int irq_banks = DEFAULT_NR_REG_BANKS; +static unsigned int max_irqs = DEFAULT_IRQS; +static unsigned int omap_secure_apis; + +#ifdef CONFIG_CPU_PM +static unsigned int wakeupgen_context[MAX_NR_REG_BANKS]; +#endif + +struct omap_wakeupgen_ops { + void (*save_context)(void); + void (*restore_context)(void); +}; + +static struct omap_wakeupgen_ops *wakeupgen_ops; + +/* + * Static helper functions. + */ +static inline u32 wakeupgen_readl(u8 idx, u32 cpu) +{ + return readl_relaxed(wakeupgen_base + OMAP_WKG_ENB_A_0 + + (cpu * CPU_ENA_OFFSET) + (idx * 4)); +} + +static inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu) +{ + writel_relaxed(val, wakeupgen_base + OMAP_WKG_ENB_A_0 + + (cpu * CPU_ENA_OFFSET) + (idx * 4)); +} + +static inline void sar_writel(u32 val, u32 offset, u8 idx) +{ + writel_relaxed(val, sar_base + offset + (idx * 4)); +} + +static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index) +{ + /* + * Each WakeupGen register controls 32 interrupt. + * i.e. 1 bit per SPI IRQ + */ + *reg_index = irq >> 5; + *bit_posn = irq %= 32; + + return 0; +} + +static void _wakeupgen_clear(unsigned int irq, unsigned int cpu) +{ + u32 val, bit_number; + u8 i; + + if (_wakeupgen_get_irq_info(irq, &bit_number, &i)) + return; + + val = wakeupgen_readl(i, cpu); + val &= ~BIT(bit_number); + wakeupgen_writel(val, i, cpu); +} + +static void _wakeupgen_set(unsigned int irq, unsigned int cpu) +{ + u32 val, bit_number; + u8 i; + + if (_wakeupgen_get_irq_info(irq, &bit_number, &i)) + return; + + val = wakeupgen_readl(i, cpu); + val |= BIT(bit_number); + wakeupgen_writel(val, i, cpu); +} + +/* + * Architecture specific Mask extension + */ +static void wakeupgen_mask(struct irq_data *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&wakeupgen_lock, flags); + _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]); + raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); + irq_chip_mask_parent(d); +} + +/* + * Architecture specific Unmask extension + */ +static void wakeupgen_unmask(struct irq_data *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&wakeupgen_lock, flags); + _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]); + raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); + irq_chip_unmask_parent(d); +} + +/* + * The sys_nirq pins bypass peripheral modules and are wired directly + * to MPUSS wakeupgen. They get automatically inverted for GIC. + */ +static int wakeupgen_irq_set_type(struct irq_data *d, unsigned int type) +{ + bool inverted = false; + + switch (type) { + case IRQ_TYPE_LEVEL_LOW: + type &= ~IRQ_TYPE_LEVEL_MASK; + type |= IRQ_TYPE_LEVEL_HIGH; + inverted = true; + break; + case IRQ_TYPE_EDGE_FALLING: + type &= ~IRQ_TYPE_EDGE_BOTH; + type |= IRQ_TYPE_EDGE_RISING; + inverted = true; + break; + default: + break; + } + + if (inverted && d->hwirq != SYS_NIRQ1_EXT_SYS_IRQ_1 && + d->hwirq != SYS_NIRQ2_EXT_SYS_IRQ_2) + pr_warn("wakeupgen: irq%li polarity inverted in dts\n", + d->hwirq); + + return irq_chip_set_type_parent(d, type); +} + +#ifdef CONFIG_HOTPLUG_CPU +static DEFINE_PER_CPU(u32 [MAX_NR_REG_BANKS], irqmasks); + +static void _wakeupgen_save_masks(unsigned int cpu) +{ + u8 i; + + for (i = 0; i < irq_banks; i++) + per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu); +} + +static void _wakeupgen_restore_masks(unsigned int cpu) +{ + u8 i; + + for (i = 0; i < irq_banks; i++) + wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu); +} + +static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg) +{ + u8 i; + + for (i = 0; i < irq_banks; i++) + wakeupgen_writel(reg, i, cpu); +} + +/* + * Mask or unmask all interrupts on given CPU. + * 0 = Mask all interrupts on the 'cpu' + * 1 = Unmask all interrupts on the 'cpu' + * Ensure that the initial mask is maintained. This is faster than + * iterating through GIC registers to arrive at the correct masks. + */ +static void wakeupgen_irqmask_all(unsigned int cpu, unsigned int set) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&wakeupgen_lock, flags); + if (set) { + _wakeupgen_save_masks(cpu); + _wakeupgen_set_all(cpu, WKG_MASK_ALL); + } else { + _wakeupgen_set_all(cpu, WKG_UNMASK_ALL); + _wakeupgen_restore_masks(cpu); + } + raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); +} +#endif + +#ifdef CONFIG_CPU_PM +static inline void omap4_irq_save_context(void) +{ + u32 i, val; + + if (omap_rev() == OMAP4430_REV_ES1_0) + return; + + for (i = 0; i < irq_banks; i++) { + /* Save the CPUx interrupt mask for IRQ 0 to 127 */ + val = wakeupgen_readl(i, 0); + sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i); + val = wakeupgen_readl(i, 1); + sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i); + + /* + * Disable the secure interrupts for CPUx. The restore + * code blindly restores secure and non-secure interrupt + * masks from SAR RAM. Secure interrupts are not suppose + * to be enabled from HLOS. So overwrite the SAR location + * so that the secure interrupt remains disabled. + */ + sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i); + sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i); + } + + /* Save AuxBoot* registers */ + val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); + writel_relaxed(val, sar_base + AUXCOREBOOT0_OFFSET); + val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_1); + writel_relaxed(val, sar_base + AUXCOREBOOT1_OFFSET); + + /* Save SyncReq generation logic */ + val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_MASK); + writel_relaxed(val, sar_base + PTMSYNCREQ_MASK_OFFSET); + val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_EN); + writel_relaxed(val, sar_base + PTMSYNCREQ_EN_OFFSET); + + /* Set the Backup Bit Mask status */ + val = readl_relaxed(sar_base + SAR_BACKUP_STATUS_OFFSET); + val |= SAR_BACKUP_STATUS_WAKEUPGEN; + writel_relaxed(val, sar_base + SAR_BACKUP_STATUS_OFFSET); + +} + +static inline void omap5_irq_save_context(void) +{ + u32 i, val; + + for (i = 0; i < irq_banks; i++) { + /* Save the CPUx interrupt mask for IRQ 0 to 159 */ + val = wakeupgen_readl(i, 0); + sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU0, i); + val = wakeupgen_readl(i, 1); + sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU1, i); + sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0, i); + sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1, i); + } + + /* Save AuxBoot* registers */ + val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); + writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT0_OFFSET); + val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); + writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT1_OFFSET); + + /* Set the Backup Bit Mask status */ + val = readl_relaxed(sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); + val |= SAR_BACKUP_STATUS_WAKEUPGEN; + writel_relaxed(val, sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); + +} + +static inline void am43xx_irq_save_context(void) +{ + u32 i; + + for (i = 0; i < irq_banks; i++) { + wakeupgen_context[i] = wakeupgen_readl(i, 0); + wakeupgen_writel(0, i, CPU0_ID); + } +} + +/* + * Save WakeupGen interrupt context in SAR BANK3. Restore is done by + * ROM code. WakeupGen IP is integrated along with GIC to manage the + * interrupt wakeups from CPU low power states. It manages + * masking/unmasking of Shared peripheral interrupts(SPI). So the + * interrupt enable/disable control should be in sync and consistent + * at WakeupGen and GIC so that interrupts are not lost. + */ +static void irq_save_context(void) +{ + /* DRA7 has no SAR to save */ + if (soc_is_dra7xx()) + return; + + if (wakeupgen_ops && wakeupgen_ops->save_context) + wakeupgen_ops->save_context(); +} + +/* + * Clear WakeupGen SAR backup status. + */ +static void irq_sar_clear(void) +{ + u32 val; + u32 offset = SAR_BACKUP_STATUS_OFFSET; + /* DRA7 has no SAR to save */ + if (soc_is_dra7xx()) + return; + + if (soc_is_omap54xx()) + offset = OMAP5_SAR_BACKUP_STATUS_OFFSET; + + val = readl_relaxed(sar_base + offset); + val &= ~SAR_BACKUP_STATUS_WAKEUPGEN; + writel_relaxed(val, sar_base + offset); +} + +static void am43xx_irq_restore_context(void) +{ + u32 i; + + for (i = 0; i < irq_banks; i++) + wakeupgen_writel(wakeupgen_context[i], i, CPU0_ID); +} + +static void irq_restore_context(void) +{ + if (wakeupgen_ops && wakeupgen_ops->restore_context) + wakeupgen_ops->restore_context(); +} + +/* + * Save GIC and Wakeupgen interrupt context using secure API + * for HS/EMU devices. + */ +static void irq_save_secure_context(void) +{ + u32 ret; + ret = omap_secure_dispatcher(OMAP4_HAL_SAVEGIC_INDEX, + FLAG_START_CRITICAL, + 0, 0, 0, 0, 0); + if (ret != API_HAL_RET_VALUE_OK) + pr_err("GIC and Wakeupgen context save failed\n"); +} + +/* Define ops for context save and restore for each SoC */ +static struct omap_wakeupgen_ops omap4_wakeupgen_ops = { + .save_context = omap4_irq_save_context, + .restore_context = irq_sar_clear, +}; + +static struct omap_wakeupgen_ops omap5_wakeupgen_ops = { + .save_context = omap5_irq_save_context, + .restore_context = irq_sar_clear, +}; + +static struct omap_wakeupgen_ops am43xx_wakeupgen_ops = { + .save_context = am43xx_irq_save_context, + .restore_context = am43xx_irq_restore_context, +}; +#else +static struct omap_wakeupgen_ops omap4_wakeupgen_ops = {}; +static struct omap_wakeupgen_ops omap5_wakeupgen_ops = {}; +static struct omap_wakeupgen_ops am43xx_wakeupgen_ops = {}; +#endif + +#ifdef CONFIG_HOTPLUG_CPU +static int omap_wakeupgen_cpu_online(unsigned int cpu) +{ + wakeupgen_irqmask_all(cpu, 0); + return 0; +} + +static int omap_wakeupgen_cpu_dead(unsigned int cpu) +{ + wakeupgen_irqmask_all(cpu, 1); + return 0; +} + +static void __init irq_hotplug_init(void) +{ + cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/omap-wake:online", + omap_wakeupgen_cpu_online, NULL); + cpuhp_setup_state_nocalls(CPUHP_ARM_OMAP_WAKE_DEAD, + "arm/omap-wake:dead", NULL, + omap_wakeupgen_cpu_dead); +} +#else +static void __init irq_hotplug_init(void) +{} +#endif + +#ifdef CONFIG_CPU_PM +static int irq_notifier(struct notifier_block *self, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + if (omap_type() == OMAP2_DEVICE_TYPE_GP || soc_is_am43xx()) + irq_save_context(); + else + irq_save_secure_context(); + break; + case CPU_CLUSTER_PM_EXIT: + if (omap_type() == OMAP2_DEVICE_TYPE_GP || soc_is_am43xx()) + irq_restore_context(); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block irq_notifier_block = { + .notifier_call = irq_notifier, +}; + +static void __init irq_pm_init(void) +{ + /* FIXME: Remove this when MPU OSWR support is added */ + if (!IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) + cpu_pm_register_notifier(&irq_notifier_block); +} +#else +static void __init irq_pm_init(void) +{} +#endif + +void __iomem *omap_get_wakeupgen_base(void) +{ + return wakeupgen_base; +} + +int omap_secure_apis_support(void) +{ + return omap_secure_apis; +} + +static struct irq_chip wakeupgen_chip = { + .name = "WUGEN", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = wakeupgen_mask, + .irq_unmask = wakeupgen_unmask, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = wakeupgen_irq_set_type, + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif +}; + +static int wakeupgen_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (is_of_node(fwspec->fwnode)) { + if (fwspec->param_count != 3) + return -EINVAL; + + /* No PPI should point to this domain */ + if (fwspec->param[0] != 0) + return -EINVAL; + + *hwirq = fwspec->param[1]; + *type = fwspec->param[2]; + return 0; + } + + return -EINVAL; +} + +static int wakeupgen_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + int i; + + if (fwspec->param_count != 3) + return -EINVAL; /* Not GIC compliant */ + if (fwspec->param[0] != 0) + return -EINVAL; /* No PPI should point to this domain */ + + hwirq = fwspec->param[1]; + if (hwirq >= MAX_IRQS) + return -EINVAL; /* Can't deal with this */ + + for (i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &wakeupgen_chip, NULL); + + parent_fwspec = *fwspec; + parent_fwspec.fwnode = domain->parent->fwnode; + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); +} + +static const struct irq_domain_ops wakeupgen_domain_ops = { + .translate = wakeupgen_domain_translate, + .alloc = wakeupgen_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +/* + * Initialise the wakeupgen module. + */ +static int __init wakeupgen_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *parent_domain, *domain; + int i; + unsigned int boot_cpu = smp_processor_id(); + u32 val; + + if (!parent) { + pr_err("%pOF: no parent, giving up\n", node); + return -ENODEV; + } + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("%pOF: unable to obtain parent domain\n", node); + return -ENXIO; + } + /* Not supported on OMAP4 ES1.0 silicon */ + if (omap_rev() == OMAP4430_REV_ES1_0) { + WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n"); + return -EPERM; + } + + /* Static mapping, never released */ + wakeupgen_base = of_iomap(node, 0); + if (WARN_ON(!wakeupgen_base)) + return -ENOMEM; + + if (cpu_is_omap44xx()) { + irq_banks = OMAP4_NR_BANKS; + max_irqs = OMAP4_NR_IRQS; + omap_secure_apis = 1; + wakeupgen_ops = &omap4_wakeupgen_ops; + } else if (soc_is_omap54xx()) { + wakeupgen_ops = &omap5_wakeupgen_ops; + } else if (soc_is_am43xx()) { + irq_banks = AM43XX_NR_REG_BANKS; + max_irqs = AM43XX_IRQS; + wakeupgen_ops = &am43xx_wakeupgen_ops; + } + + domain = irq_domain_add_hierarchy(parent_domain, 0, max_irqs, + node, &wakeupgen_domain_ops, + NULL); + if (!domain) { + iounmap(wakeupgen_base); + return -ENOMEM; + } + + /* Clear all IRQ bitmasks at wakeupGen level */ + for (i = 0; i < irq_banks; i++) { + wakeupgen_writel(0, i, CPU0_ID); + if (!soc_is_am43xx()) + wakeupgen_writel(0, i, CPU1_ID); + } + + /* + * FIXME: Add support to set_smp_affinity() once the core + * GIC code has necessary hooks in place. + */ + + /* Associate all the IRQs to boot CPU like GIC init does. */ + for (i = 0; i < max_irqs; i++) + irq_target_cpu[i] = boot_cpu; + + /* + * Enables OMAP5 ES2 PM Mode using ES2_PM_MODE in AMBA_IF_MODE + * 0x0: ES1 behavior, CPU cores would enter and exit OFF mode together. + * 0x1: ES2 behavior, CPU cores are allowed to enter/exit OFF mode + * independently. + * This needs to be set one time thanks to always ON domain. + * + * We do not support ES1 behavior anymore. OMAP5 is assumed to be + * ES2.0, and the same is applicable for DRA7. + */ + if (soc_is_omap54xx() || soc_is_dra7xx()) { + val = __raw_readl(wakeupgen_base + OMAP_AMBA_IF_MODE); + val |= BIT(5); + omap_smc1(OMAP5_MON_AMBA_IF_INDEX, val); + } + + irq_hotplug_init(); + irq_pm_init(); + + sar_base = omap4_get_sar_ram_base(); + + return 0; +} +IRQCHIP_DECLARE(ti_wakeupgen, "ti,omap4-wugen-mpu", wakeupgen_init); diff --git a/arch/arm/mach-omap2/omap-wakeupgen.h b/arch/arm/mach-omap2/omap-wakeupgen.h new file mode 100644 index 000000000..bcc37b2c8 --- /dev/null +++ b/arch/arm/mach-omap2/omap-wakeupgen.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP WakeupGen header file + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + */ +#ifndef OMAP_ARCH_WAKEUPGEN_H +#define OMAP_ARCH_WAKEUPGEN_H + +/* OMAP4 and OMAP5 has same base address */ +#define OMAP_WKUPGEN_BASE 0x48281000 + +#define OMAP_WKG_CONTROL_0 0x00 +#define OMAP_WKG_ENB_A_0 0x10 +#define OMAP_WKG_ENB_B_0 0x14 +#define OMAP_WKG_ENB_C_0 0x18 +#define OMAP_WKG_ENB_D_0 0x1c +#define OMAP_WKG_ENB_E_0 0x20 +#define OMAP_WKG_ENB_A_1 0x410 +#define OMAP_WKG_ENB_B_1 0x414 +#define OMAP_WKG_ENB_C_1 0x418 +#define OMAP_WKG_ENB_D_1 0x41c +#define OMAP_WKG_ENB_E_1 0x420 +#define OMAP_AUX_CORE_BOOT_0 0x800 +#define OMAP_AUX_CORE_BOOT_1 0x804 +#define OMAP_AMBA_IF_MODE 0x80c +#define OMAP_PTMSYNCREQ_MASK 0xc00 +#define OMAP_PTMSYNCREQ_EN 0xc04 +#define OMAP_TIMESTAMPCYCLELO 0xc08 +#define OMAP_TIMESTAMPCYCLEHI 0xc0c + +extern void __iomem *omap_get_wakeupgen_base(void); +extern int omap_secure_apis_support(void); +#endif diff --git a/arch/arm/mach-omap2/omap2-restart.c b/arch/arm/mach-omap2/omap2-restart.c new file mode 100644 index 000000000..fdcc75c97 --- /dev/null +++ b/arch/arm/mach-omap2/omap2-restart.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap2-restart.c - code common to all OMAP2xxx machines. + * + * Copyright (C) 2012 Texas Instruments + * Paul Walmsley + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include "soc.h" +#include "common.h" +#include "prm.h" + +/* + * reset_virt_prcm_set_ck, reset_sys_ck: pointers to the virt_prcm_set + * clock and the sys_ck. Used during the reset process + */ +static struct clk *reset_virt_prcm_set_ck, *reset_sys_ck; + +/* Reboot handling */ + +/** + * omap2xxx_restart - Set DPLL to bypass mode for reboot to work + * + * Set the DPLL to bypass so that reboot completes successfully. No + * return value. + */ +void omap2xxx_restart(enum reboot_mode mode, const char *cmd) +{ + u32 rate; + + rate = clk_get_rate(reset_sys_ck); + clk_set_rate(reset_virt_prcm_set_ck, rate); + + /* XXX Should save the cmd argument for use after the reboot */ + + omap_prm_reset_system(); +} + +/** + * omap2xxx_common_look_up_clks_for_reset - look up clocks needed for restart + * + * Some clocks need to be looked up in advance for the SoC restart + * operation to work - see omap2xxx_restart(). Returns -EINVAL upon + * error or 0 upon success. + */ +static int __init omap2xxx_common_look_up_clks_for_reset(void) +{ + reset_virt_prcm_set_ck = clk_get(NULL, "virt_prcm_set"); + if (IS_ERR(reset_virt_prcm_set_ck)) + return -EINVAL; + + reset_sys_ck = clk_get(NULL, "sys_ck"); + if (IS_ERR(reset_sys_ck)) + return -EINVAL; + + return 0; +} +omap_postcore_initcall(omap2xxx_common_look_up_clks_for_reset); diff --git a/arch/arm/mach-omap2/omap24xx.h b/arch/arm/mach-omap2/omap24xx.h new file mode 100644 index 000000000..d9ecc0578 --- /dev/null +++ b/arch/arm/mach-omap2/omap24xx.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file contains the processor specific definitions + * of the TI OMAP24XX. + * + * Copyright (C) 2007 Texas Instruments. + * Copyright (C) 2007 Nokia Corporation. + */ + +#ifndef __ASM_ARCH_OMAP2_H +#define __ASM_ARCH_OMAP2_H + +/* + * Please place only base defines here and put the rest in device + * specific headers. Note also that some of these defines are needed + * for omap1 to compile without adding ifdefs. + */ + +#define L4_24XX_BASE 0x48000000 +#define L4_WK_243X_BASE 0x49000000 +#define L3_24XX_BASE 0x68000000 + +/* interrupt controller */ +#define OMAP24XX_IC_BASE (L4_24XX_BASE + 0xfe000) +#define OMAP24XX_IVA_INTC_BASE 0x40000000 + +#define OMAP242X_CTRL_BASE L4_24XX_BASE +#define OMAP2420_32KSYNCT_BASE (L4_24XX_BASE + 0x4000) +#define OMAP2420_PRCM_BASE (L4_24XX_BASE + 0x8000) +#define OMAP2420_CM_BASE (L4_24XX_BASE + 0x8000) +#define OMAP2420_PRM_BASE OMAP2420_CM_BASE +#define OMAP2420_SDRC_BASE (L3_24XX_BASE + 0x9000) +#define OMAP2420_SMS_BASE 0x68008000 +#define OMAP2420_GPMC_BASE 0x6800a000 + +#define OMAP2430_32KSYNCT_BASE (L4_WK_243X_BASE + 0x20000) +#define OMAP2430_PRCM_BASE (L4_WK_243X_BASE + 0x6000) +#define OMAP2430_CM_BASE (L4_WK_243X_BASE + 0x6000) +#define OMAP2430_PRM_BASE OMAP2430_CM_BASE + +#define OMAP243X_SMS_BASE 0x6C000000 +#define OMAP243X_SDRC_BASE 0x6D000000 +#define OMAP243X_GPMC_BASE 0x6E000000 +#define OMAP243X_SCM_BASE (L4_WK_243X_BASE + 0x2000) +#define OMAP243X_CTRL_BASE OMAP243X_SCM_BASE +#define OMAP243X_HS_BASE (L4_24XX_BASE + 0x000ac000) + +/* DSP SS */ +#define OMAP2420_DSP_BASE 0x58000000 +#define OMAP2420_DSP_MEM_BASE (OMAP2420_DSP_BASE + 0x0) +#define OMAP2420_DSP_IPI_BASE (OMAP2420_DSP_BASE + 0x1000000) +#define OMAP2420_DSP_MMU_BASE (OMAP2420_DSP_BASE + 0x2000000) + +#define OMAP243X_DSP_BASE 0x5C000000 +#define OMAP243X_DSP_MEM_BASE (OMAP243X_DSP_BASE + 0x0) +#define OMAP243X_DSP_MMU_BASE (OMAP243X_DSP_BASE + 0x1000000) + +/* Mailbox */ +#define OMAP24XX_MAILBOX_BASE (L4_24XX_BASE + 0x94000) + +/* Camera */ +#define OMAP24XX_CAMERA_BASE (L4_24XX_BASE + 0x52000) + +/* Security */ +#define OMAP24XX_SEC_BASE (L4_24XX_BASE + 0xA0000) +#define OMAP24XX_SEC_RNG_BASE (OMAP24XX_SEC_BASE + 0x0000) +#define OMAP24XX_SEC_DES_BASE (OMAP24XX_SEC_BASE + 0x2000) +#define OMAP24XX_SEC_SHA1MD5_BASE (OMAP24XX_SEC_BASE + 0x4000) +#define OMAP24XX_SEC_AES_BASE (OMAP24XX_SEC_BASE + 0x6000) +#define OMAP24XX_SEC_PKA_BASE (OMAP24XX_SEC_BASE + 0x8000) + +#endif /* __ASM_ARCH_OMAP2_H */ + diff --git a/arch/arm/mach-omap2/omap3-restart.c b/arch/arm/mach-omap2/omap3-restart.c new file mode 100644 index 000000000..bcabe6fa1 --- /dev/null +++ b/arch/arm/mach-omap2/omap3-restart.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap3-restart.c - Code common to all OMAP3xxx machines. + * + * Copyright (C) 2009, 2012 Texas Instruments + * Copyright (C) 2010 Nokia Corporation + * Tony Lindgren <tony@atomide.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/reboot.h> + +#include "common.h" +#include "control.h" +#include "prm.h" + +/* Global address base setup code */ + +/** + * omap3xxx_restart - trigger a software restart of the SoC + * @mode: the "reboot mode", see arch/arm/kernel/{setup,process}.c + * @cmd: passed from the userspace program rebooting the system (if provided) + * + * Resets the SoC. For @cmd, see the 'reboot' syscall in + * kernel/sys.c. No return value. + */ +void omap3xxx_restart(enum reboot_mode mode, const char *cmd) +{ + omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0)); + omap_prm_reset_system(); +} diff --git a/arch/arm/mach-omap2/omap34xx.h b/arch/arm/mach-omap2/omap34xx.h new file mode 100644 index 000000000..ce8cd3317 --- /dev/null +++ b/arch/arm/mach-omap2/omap34xx.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file contains the processor specific definitions of the TI OMAP34XX. + * + * Copyright (C) 2007 Texas Instruments. + * Copyright (C) 2007 Nokia Corporation. + */ + +#ifndef __ASM_ARCH_OMAP3_H +#define __ASM_ARCH_OMAP3_H + +/* + * Please place only base defines here and put the rest in device + * specific headers. + */ + +#define L4_34XX_BASE 0x48000000 +#define L4_WK_34XX_BASE 0x48300000 +#define L4_PER_34XX_BASE 0x49000000 +#define L4_EMU_34XX_BASE 0x54000000 +#define L3_34XX_BASE 0x68000000 + +#define L4_WK_AM33XX_BASE 0x44C00000 + +#define OMAP3430_32KSYNCT_BASE 0x48320000 +#define OMAP3430_CM_BASE 0x48004800 +#define OMAP3430_PRM_BASE 0x48306800 +#define OMAP343X_SMS_BASE 0x6C000000 +#define OMAP343X_SDRC_BASE 0x6D000000 +#define OMAP34XX_GPMC_BASE 0x6E000000 +#define OMAP343X_SCM_BASE 0x48002000 +#define OMAP343X_CTRL_BASE OMAP343X_SCM_BASE + +#define OMAP34XX_IC_BASE 0x48200000 + +#define OMAP3430_ISP_BASE (L4_34XX_BASE + 0xBC000) +#define OMAP3430_ISP_MMU_BASE (OMAP3430_ISP_BASE + 0x1400) +#define OMAP3430_ISP_BASE2 (OMAP3430_ISP_BASE + 0x1800) + +#define OMAP34XX_HSUSB_OTG_BASE (L4_34XX_BASE + 0xAB000) +#define OMAP34XX_USBTLL_BASE (L4_34XX_BASE + 0x62000) +#define OMAP34XX_UHH_CONFIG_BASE (L4_34XX_BASE + 0x64000) +#define OMAP34XX_OHCI_BASE (L4_34XX_BASE + 0x64400) +#define OMAP34XX_EHCI_BASE (L4_34XX_BASE + 0x64800) +#define OMAP34XX_SR1_BASE 0x480C9000 +#define OMAP34XX_SR2_BASE 0x480CB000 + +#define OMAP34XX_MAILBOX_BASE (L4_34XX_BASE + 0x94000) + +/* Security */ +#define OMAP34XX_SEC_BASE (L4_34XX_BASE + 0xA0000) +#define OMAP34XX_SEC_SHA1MD5_BASE (OMAP34XX_SEC_BASE + 0x23000) +#define OMAP34XX_SEC_AES_BASE (OMAP34XX_SEC_BASE + 0x25000) + +#endif /* __ASM_ARCH_OMAP3_H */ + diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c new file mode 100644 index 000000000..d9ed2a5dc --- /dev/null +++ b/arch/arm/mach-omap2/omap4-common.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 specific common source file. + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Author: + * Santosh Shilimkar <santosh.shilimkar@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/platform_device.h> +#include <linux/memblock.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/export.h> +#include <linux/irqchip/arm-gic.h> +#include <linux/of_address.h> +#include <linux/reboot.h> +#include <linux/genalloc.h> + +#include <asm/hardware/cache-l2x0.h> +#include <asm/mach/map.h> +#include <asm/memblock.h> +#include <asm/smp_twd.h> + +#include "omap-wakeupgen.h" +#include "soc.h" +#include "iomap.h" +#include "common.h" +#include "prminst44xx.h" +#include "prcm_mpu44xx.h" +#include "omap4-sar-layout.h" +#include "omap-secure.h" +#include "sram.h" + +#ifdef CONFIG_CACHE_L2X0 +static void __iomem *l2cache_base; +#endif + +static void __iomem *sar_ram_base; +static void __iomem *gic_dist_base_addr; +static void __iomem *twd_base; + +#define IRQ_LOCALTIMER 29 + +#ifdef CONFIG_OMAP_INTERCONNECT_BARRIER + +/* Used to implement memory barrier on DRAM path */ +#define OMAP4_DRAM_BARRIER_VA 0xfe600000 + +static void __iomem *dram_sync, *sram_sync; +static phys_addr_t dram_sync_paddr; +static u32 dram_sync_size; + +/* + * The OMAP4 bus structure contains asynchronous bridges which can buffer + * data writes from the MPU. These asynchronous bridges can be found on + * paths between the MPU to EMIF, and the MPU to L3 interconnects. + * + * We need to be careful about re-ordering which can happen as a result + * of different accesses being performed via different paths, and + * therefore different asynchronous bridges. + */ + +/* + * OMAP4 interconnect barrier which is called for each mb() and wmb(). + * This is to ensure that normal paths to DRAM (normal memory, cacheable + * accesses) are properly synchronised with writes to DMA coherent memory + * (normal memory, uncacheable) and device writes. + * + * The mb() and wmb() barriers only operate only on the MPU->MA->EMIF + * path, as we need to ensure that data is visible to other system + * masters prior to writes to those system masters being seen. + * + * Note: the SRAM path is not synchronised via mb() and wmb(). + */ +static void omap4_mb(void) +{ + if (dram_sync) + writel_relaxed(0, dram_sync); +} + +/* + * OMAP4 Errata i688 - asynchronous bridge corruption when entering WFI. + * + * If a data is stalled inside asynchronous bridge because of back + * pressure, it may be accepted multiple times, creating pointer + * misalignment that will corrupt next transfers on that data path until + * next reset of the system. No recovery procedure once the issue is hit, + * the path remains consistently broken. + * + * Async bridges can be found on paths between MPU to EMIF and MPU to L3 + * interconnects. + * + * This situation can happen only when the idle is initiated by a Master + * Request Disconnection (which is trigged by software when executing WFI + * on the CPU). + * + * The work-around for this errata needs all the initiators connected + * through an async bridge to ensure that data path is properly drained + * before issuing WFI. This condition will be met if one Strongly ordered + * access is performed to the target right before executing the WFI. + * + * In MPU case, L3 T2ASYNC FIFO and DDR T2ASYNC FIFO needs to be drained. + * IO barrier ensure that there is no synchronisation loss on initiators + * operating on both interconnect port simultaneously. + * + * This is a stronger version of the OMAP4 memory barrier below, and + * operates on both the MPU->MA->EMIF path but also the MPU->OCP path + * as well, and is necessary prior to executing a WFI. + */ +void omap_interconnect_sync(void) +{ + if (dram_sync && sram_sync) { + writel_relaxed(readl_relaxed(dram_sync), dram_sync); + writel_relaxed(readl_relaxed(sram_sync), sram_sync); + isb(); + } +} + +static int __init omap4_sram_init(void) +{ + struct device_node *np; + struct gen_pool *sram_pool; + + if (!soc_is_omap44xx() && !soc_is_omap54xx()) + return 0; + + np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu"); + if (!np) + pr_warn("%s:Unable to allocate sram needed to handle errata I688\n", + __func__); + sram_pool = of_gen_pool_get(np, "sram", 0); + if (!sram_pool) + pr_warn("%s:Unable to get sram pool needed to handle errata I688\n", + __func__); + else + sram_sync = (void __iomem *)gen_pool_alloc(sram_pool, PAGE_SIZE); + of_node_put(np); + + return 0; +} +omap_arch_initcall(omap4_sram_init); + +/* Steal one page physical memory for barrier implementation */ +void __init omap_barrier_reserve_memblock(void) +{ + dram_sync_size = ALIGN(PAGE_SIZE, SZ_1M); + dram_sync_paddr = arm_memblock_steal(dram_sync_size, SZ_1M); +} + +void __init omap_barriers_init(void) +{ + struct map_desc dram_io_desc[1]; + + dram_io_desc[0].virtual = OMAP4_DRAM_BARRIER_VA; + dram_io_desc[0].pfn = __phys_to_pfn(dram_sync_paddr); + dram_io_desc[0].length = dram_sync_size; + dram_io_desc[0].type = MT_MEMORY_RW_SO; + iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc)); + dram_sync = (void __iomem *) dram_io_desc[0].virtual; + + pr_info("OMAP4: Map %pa to %p for dram barrier\n", + &dram_sync_paddr, dram_sync); + + soc_mb = omap4_mb; +} + +#endif + +void gic_dist_disable(void) +{ + if (gic_dist_base_addr) + writel_relaxed(0x0, gic_dist_base_addr + GIC_DIST_CTRL); +} + +void gic_dist_enable(void) +{ + if (gic_dist_base_addr) + writel_relaxed(0x1, gic_dist_base_addr + GIC_DIST_CTRL); +} + +bool gic_dist_disabled(void) +{ + return !(readl_relaxed(gic_dist_base_addr + GIC_DIST_CTRL) & 0x1); +} + +void gic_timer_retrigger(void) +{ + u32 twd_int = readl_relaxed(twd_base + TWD_TIMER_INTSTAT); + u32 gic_int = readl_relaxed(gic_dist_base_addr + GIC_DIST_PENDING_SET); + u32 twd_ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL); + + if (twd_int && !(gic_int & BIT(IRQ_LOCALTIMER))) { + /* + * The local timer interrupt got lost while the distributor was + * disabled. Ack the pending interrupt, and retrigger it. + */ + pr_warn("%s: lost localtimer interrupt\n", __func__); + writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT); + if (!(twd_ctrl & TWD_TIMER_CONTROL_PERIODIC)) { + writel_relaxed(1, twd_base + TWD_TIMER_COUNTER); + twd_ctrl |= TWD_TIMER_CONTROL_ENABLE; + writel_relaxed(twd_ctrl, twd_base + TWD_TIMER_CONTROL); + } + } +} + +#ifdef CONFIG_CACHE_L2X0 + +void __iomem *omap4_get_l2cache_base(void) +{ + return l2cache_base; +} + +void omap4_l2c310_write_sec(unsigned long val, unsigned reg) +{ + unsigned smc_op; + + switch (reg) { + case L2X0_CTRL: + smc_op = OMAP4_MON_L2X0_CTRL_INDEX; + break; + + case L2X0_AUX_CTRL: + smc_op = OMAP4_MON_L2X0_AUXCTRL_INDEX; + break; + + case L2X0_DEBUG_CTRL: + smc_op = OMAP4_MON_L2X0_DBG_CTRL_INDEX; + break; + + case L310_PREFETCH_CTRL: + smc_op = OMAP4_MON_L2X0_PREFETCH_INDEX; + break; + + case L310_POWER_CTRL: + pr_info_once("OMAP L2C310: ROM does not support power control setting\n"); + return; + + default: + WARN_ONCE(1, "OMAP L2C310: ignoring write to reg 0x%x\n", reg); + return; + } + + omap_smc1(smc_op, val); +} + +int __init omap_l2_cache_init(void) +{ + /* Static mapping, never released */ + l2cache_base = ioremap(OMAP44XX_L2CACHE_BASE, SZ_4K); + if (WARN_ON(!l2cache_base)) + return -ENOMEM; + return 0; +} +#endif + +void __iomem *omap4_get_sar_ram_base(void) +{ + return sar_ram_base; +} + +/* + * SAR RAM used to save and restore the HW context in low power modes. + * Note that we need to initialize this very early for kexec. See + * omap4_mpuss_early_init(). + */ +void __init omap4_sar_ram_init(void) +{ + unsigned long sar_base; + + /* + * To avoid code running on other OMAPs in + * multi-omap builds + */ + if (cpu_is_omap44xx()) + sar_base = OMAP44XX_SAR_RAM_BASE; + else if (soc_is_omap54xx()) + sar_base = OMAP54XX_SAR_RAM_BASE; + else + return; + + /* Static mapping, never released */ + sar_ram_base = ioremap(sar_base, SZ_16K); + if (WARN_ON(!sar_ram_base)) + return; +} + +static const struct of_device_id intc_match[] = { + { .compatible = "ti,omap4-wugen-mpu", }, + { .compatible = "ti,omap5-wugen-mpu", }, + { }, +}; + +static struct device_node *intc_node; + +void __init omap_gic_of_init(void) +{ + struct device_node *np; + + intc_node = of_find_matching_node(NULL, intc_match); + if (WARN_ON(!intc_node)) { + pr_err("No WUGEN found in DT, system will misbehave.\n"); + pr_err("UPDATE YOUR DEVICE TREE!\n"); + } + + /* Extract GIC distributor and TWD bases for OMAP4460 ROM Errata WA */ + if (!cpu_is_omap446x()) + goto skip_errata_init; + + np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic"); + gic_dist_base_addr = of_iomap(np, 0); + of_node_put(np); + WARN_ON(!gic_dist_base_addr); + + np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-twd-timer"); + twd_base = of_iomap(np, 0); + of_node_put(np); + WARN_ON(!twd_base); + +skip_errata_init: + irqchip_init(); +} diff --git a/arch/arm/mach-omap2/omap4-restart.c b/arch/arm/mach-omap2/omap4-restart.c new file mode 100644 index 000000000..56f621314 --- /dev/null +++ b/arch/arm/mach-omap2/omap4-restart.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap4-restart.c - Common to OMAP4 and OMAP5 + */ + +#include <linux/types.h> +#include <linux/reboot.h> +#include "common.h" +#include "prm.h" + +/** + * omap44xx_restart - trigger a software restart of the SoC + * @mode: the "reboot mode", see arch/arm/kernel/{setup,process}.c + * @cmd: passed from the userspace program rebooting the system (if provided) + * + * Resets the SoC. For @cmd, see the 'reboot' syscall in + * kernel/sys.c. No return value. + */ +void omap44xx_restart(enum reboot_mode mode, const char *cmd) +{ + /* XXX Should save 'cmd' into scratchpad for use after reboot */ + omap_prm_reset_system(); +} diff --git a/arch/arm/mach-omap2/omap4-sar-layout.h b/arch/arm/mach-omap2/omap4-sar-layout.h new file mode 100644 index 000000000..aabf06401 --- /dev/null +++ b/arch/arm/mach-omap2/omap4-sar-layout.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap4-sar-layout.h: OMAP4 SAR RAM layout header file + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + */ +#ifndef OMAP_ARCH_OMAP4_SAR_LAYOUT_H +#define OMAP_ARCH_OMAP4_SAR_LAYOUT_H + +/* + * SAR BANK offsets from base address OMAP44XX/54XX_SAR_RAM_BASE + */ +#define SAR_BANK1_OFFSET 0x0000 +#define SAR_BANK2_OFFSET 0x1000 +#define SAR_BANK3_OFFSET 0x2000 +#define SAR_BANK4_OFFSET 0x3000 + +/* Scratch pad memory offsets from SAR_BANK1 */ +#define SCU_OFFSET0 0xfe4 +#define SCU_OFFSET1 0xfe8 +#define OMAP_TYPE_OFFSET 0xfec +#define L2X0_SAVE_OFFSET0 0xff0 +#define L2X0_SAVE_OFFSET1 0xff4 +#define L2X0_AUXCTRL_OFFSET 0xff8 +#define L2X0_PREFETCH_CTRL_OFFSET 0xffc + +/* CPUx Wakeup Non-Secure Physical Address offsets in SAR_BANK1 */ +#define CPU0_WAKEUP_NS_PA_ADDR_OFFSET 0xa04 +#define CPU1_WAKEUP_NS_PA_ADDR_OFFSET 0xa08 +#define OMAP5_CPU0_WAKEUP_NS_PA_ADDR_OFFSET 0xe00 +#define OMAP5_CPU1_WAKEUP_NS_PA_ADDR_OFFSET 0xe04 + +#define SAR_BACKUP_STATUS_OFFSET (SAR_BANK3_OFFSET + 0x500) +#define SAR_SECURE_RAM_SIZE_OFFSET (SAR_BANK3_OFFSET + 0x504) +#define SAR_SECRAM_SAVED_AT_OFFSET (SAR_BANK3_OFFSET + 0x508) + +/* WakeUpGen save restore offset from OMAP44XX_SAR_RAM_BASE */ +#define WAKEUPGENENB_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x684) +#define WAKEUPGENENB_SECURE_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x694) +#define WAKEUPGENENB_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0x6a4) +#define WAKEUPGENENB_SECURE_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0x6b4) +#define AUXCOREBOOT0_OFFSET (SAR_BANK3_OFFSET + 0x6c4) +#define AUXCOREBOOT1_OFFSET (SAR_BANK3_OFFSET + 0x6c8) +#define PTMSYNCREQ_MASK_OFFSET (SAR_BANK3_OFFSET + 0x6cc) +#define PTMSYNCREQ_EN_OFFSET (SAR_BANK3_OFFSET + 0x6d0) +#define SAR_BACKUP_STATUS_WAKEUPGEN 0x10 + +/* WakeUpGen save restore offset from OMAP54XX_SAR_RAM_BASE */ +#define OMAP5_WAKEUPGENENB_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x9dc) +#define OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0 (SAR_BANK3_OFFSET + 0x9f0) +#define OMAP5_WAKEUPGENENB_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0xa04) +#define OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1 (SAR_BANK3_OFFSET + 0xa18) +#define OMAP5_AUXCOREBOOT0_OFFSET (SAR_BANK3_OFFSET + 0xa2c) +#define OMAP5_AUXCOREBOOT1_OFFSET (SAR_BANK3_OFFSET + 0x930) +#define OMAP5_AMBA_IF_MODE_OFFSET (SAR_BANK3_OFFSET + 0xa34) +#define OMAP5_SAR_BACKUP_STATUS_OFFSET (SAR_BANK3_OFFSET + 0x800) + +#endif diff --git a/arch/arm/mach-omap2/omap44xx.h b/arch/arm/mach-omap2/omap44xx.h new file mode 100644 index 000000000..0595175a5 --- /dev/null +++ b/arch/arm/mach-omap2/omap44xx.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/*: + * Address mappings and base address for OMAP4 interconnects + * and peripherals. + * + * Copyright (C) 2009 Texas Instruments + * + * Author: Santosh Shilimkar <santosh.shilimkar@ti.com> + */ +#ifndef __ASM_ARCH_OMAP44XX_H +#define __ASM_ARCH_OMAP44XX_H + +/* + * Please place only base defines here and put the rest in device + * specific headers. + */ +#define L4_44XX_BASE 0x4a000000 +#define L4_WK_44XX_BASE 0x4a300000 +#define L4_PER_44XX_BASE 0x48000000 +#define L4_EMU_44XX_BASE 0x54000000 +#define L3_44XX_BASE 0x44000000 +#define OMAP44XX_EMIF1_BASE 0x4c000000 +#define OMAP44XX_EMIF2_BASE 0x4d000000 +#define OMAP44XX_DMM_BASE 0x4e000000 +#define OMAP4430_32KSYNCT_BASE 0x4a304000 +#define OMAP4430_CM1_BASE 0x4a004000 +#define OMAP4430_CM_BASE OMAP4430_CM1_BASE +#define OMAP4430_CM2_BASE 0x4a008000 +#define OMAP4430_PRM_BASE 0x4a306000 +#define OMAP4430_PRCM_MPU_BASE 0x48243000 +#define OMAP44XX_GPMC_BASE 0x50000000 +#define OMAP443X_SCM_BASE 0x4a002000 +#define OMAP443X_CTRL_BASE 0x4a100000 +#define OMAP44XX_IC_BASE 0x48200000 +#define OMAP44XX_IVA_INTC_BASE 0x40000000 +#define IRQ_SIR_IRQ 0x0040 +#define OMAP44XX_GIC_DIST_BASE 0x48241000 +#define OMAP44XX_GIC_CPU_BASE 0x48240100 +#define OMAP44XX_IRQ_GIC_START 32 +#define OMAP44XX_LOCAL_TWD_BASE 0x48240600 +#define OMAP44XX_L2CACHE_BASE 0x48242000 +#define OMAP44XX_WKUPGEN_BASE 0x48281000 +#define OMAP44XX_MCPDM_BASE 0x40132000 +#define OMAP44XX_SAR_RAM_BASE 0x4a326000 + +#define OMAP44XX_MAILBOX_BASE (L4_44XX_BASE + 0xF4000) +#define OMAP44XX_HSUSB_OTG_BASE (L4_44XX_BASE + 0xAB000) + +#define OMAP4_MMU1_BASE 0x55082000 +#define OMAP4_MMU2_BASE 0x4A066000 + +#define OMAP44XX_USBTLL_BASE (L4_44XX_BASE + 0x62000) +#define OMAP44XX_UHH_CONFIG_BASE (L4_44XX_BASE + 0x64000) +#define OMAP44XX_HSUSB_OHCI_BASE (L4_44XX_BASE + 0x64800) +#define OMAP44XX_HSUSB_EHCI_BASE (L4_44XX_BASE + 0x64C00) + +#endif /* __ASM_ARCH_OMAP44XX_H */ + diff --git a/arch/arm/mach-omap2/omap54xx.h b/arch/arm/mach-omap2/omap54xx.h new file mode 100644 index 000000000..b49e633c3 --- /dev/null +++ b/arch/arm/mach-omap2/omap54xx.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/*: + * Address mappings and base address for OMAP5 interconnects + * and peripherals. + * + * Copyright (C) 2012 Texas Instruments + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * Sricharan <r.sricharan@ti.com> + */ +#ifndef __ASM_SOC_OMAP54XX_H +#define __ASM_SOC_OMAP54XX_H + +/* + * Please place only base defines here and put the rest in device + * specific headers. + */ +#define L4_54XX_BASE 0x4a000000 +#define L4_WK_54XX_BASE 0x4ae00000 +#define L4_PER_54XX_BASE 0x48000000 +#define L3_54XX_BASE 0x44000000 +#define OMAP54XX_32KSYNCT_BASE 0x4ae04000 +#define OMAP54XX_CM_CORE_AON_BASE 0x4a004000 +#define OMAP54XX_CM_CORE_BASE 0x4a008000 +#define OMAP54XX_PRM_BASE 0x4ae06000 +#define OMAP54XX_PRCM_MPU_BASE 0x48243000 +#define OMAP54XX_SCM_BASE 0x4a002000 +#define OMAP54XX_CTRL_BASE 0x4a002800 +#define OMAP54XX_SAR_RAM_BASE 0x4ae26000 + +/* DRA7 specific base addresses */ +#define L3_MAIN_SN_DRA7XX_BASE 0x44000000 +#define L4_PER1_DRA7XX_BASE 0x48000000 +#define L4_CFG_MPU_DRA7XX_BASE 0x48210000 +#define L4_PER2_DRA7XX_BASE 0x48400000 +#define L4_PER3_DRA7XX_BASE 0x48800000 +#define L4_CFG_DRA7XX_BASE 0x4A000000 +#define L4_WKUP_DRA7XX_BASE 0x4ae00000 +#define DRA7XX_CM_CORE_AON_BASE 0x4a005000 +#define DRA7XX_CTRL_BASE 0x4a003400 +#define DRA7XX_TAP_BASE 0x4ae0c000 + +#endif /* __ASM_SOC_OMAP555554XX_H */ diff --git a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c new file mode 100644 index 000000000..8b3701901 --- /dev/null +++ b/arch/arm/mach-omap2/omap_device.c @@ -0,0 +1,686 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_device implementation + * + * Copyright (C) 2009-2010 Nokia Corporation + * Paul Walmsley, Kevin Hilman + * + * Developed in collaboration with (alphabetical order): Benoit + * Cousson, Thara Gopinath, Tony Lindgren, Rajendra Nayak, Vikram + * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard + * Woodruff + * + * This code provides a consistent interface for OMAP device drivers + * to control power management and interconnect properties of their + * devices. + * + * In the medium- to long-term, this code should be implemented as a + * proper omap_bus/omap_device in Linux, no more platform_data func + * pointers + */ +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/notifier.h> + +#include "common.h" +#include "soc.h" +#include "omap_device.h" +#include "omap_hwmod.h" + +/* Private functions */ + +static void _add_clkdev(struct omap_device *od, const char *clk_alias, + const char *clk_name) +{ + struct clk *r; + int rc; + + if (!clk_alias || !clk_name) + return; + + dev_dbg(&od->pdev->dev, "Creating %s -> %s\n", clk_alias, clk_name); + + r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias); + if (!IS_ERR(r)) { + dev_dbg(&od->pdev->dev, + "alias %s already exists\n", clk_alias); + clk_put(r); + return; + } + + r = clk_get_sys(NULL, clk_name); + + if (IS_ERR(r)) { + struct of_phandle_args clkspec; + + clkspec.np = of_find_node_by_name(NULL, clk_name); + + r = of_clk_get_from_provider(&clkspec); + + rc = clk_register_clkdev(r, clk_alias, + dev_name(&od->pdev->dev)); + } else { + rc = clk_add_alias(clk_alias, dev_name(&od->pdev->dev), + clk_name, NULL); + } + + if (rc) { + if (rc == -ENODEV || rc == -ENOMEM) + dev_err(&od->pdev->dev, + "clkdev_alloc for %s failed\n", clk_alias); + else + dev_err(&od->pdev->dev, + "clk_get for %s failed\n", clk_name); + } +} + +/** + * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks + * and main clock + * @od: struct omap_device *od + * @oh: struct omap_hwmod *oh + * + * For the main clock and every optional clock present per hwmod per + * omap_device, this function adds an entry in the clkdev table of the + * form <dev-id=dev_name, con-id=role> if it does not exist already. + * + * This allows drivers to get a pointer to its optional clocks based on its role + * by calling clk_get(<dev*>, <role>). + * In the case of the main clock, a "fck" alias is used. + * + * No return value. + */ +static void _add_hwmod_clocks_clkdev(struct omap_device *od, + struct omap_hwmod *oh) +{ + int i; + + _add_clkdev(od, "fck", oh->main_clk); + + for (i = 0; i < oh->opt_clks_cnt; i++) + _add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk); +} + + +/** + * omap_device_build_from_dt - build an omap_device with multiple hwmods + * @pdev: The platform device to update. + * + * Function for building an omap_device already registered from device-tree + * + * Returns 0 or PTR_ERR() on error. + */ +static int omap_device_build_from_dt(struct platform_device *pdev) +{ + struct omap_hwmod **hwmods; + struct omap_device *od; + struct omap_hwmod *oh; + struct device_node *node = pdev->dev.of_node; + struct resource res; + const char *oh_name; + int oh_cnt, i, ret = 0; + bool device_active = false, skip_pm_domain = false; + + oh_cnt = of_property_count_strings(node, "ti,hwmods"); + if (oh_cnt <= 0) { + dev_dbg(&pdev->dev, "No 'hwmods' to build omap_device\n"); + return -ENODEV; + } + + /* SDMA still needs special handling for omap_device_build() */ + ret = of_property_read_string_index(node, "ti,hwmods", 0, &oh_name); + if (!ret && (!strncmp("dma_system", oh_name, 10) || + !strncmp("dma", oh_name, 3))) + skip_pm_domain = true; + + /* Use ti-sysc driver instead of omap_device? */ + if (!skip_pm_domain && + !omap_hwmod_parse_module_range(NULL, node, &res)) + return -ENODEV; + + hwmods = kcalloc(oh_cnt, sizeof(struct omap_hwmod *), GFP_KERNEL); + if (!hwmods) { + ret = -ENOMEM; + goto odbfd_exit; + } + + for (i = 0; i < oh_cnt; i++) { + of_property_read_string_index(node, "ti,hwmods", i, &oh_name); + oh = omap_hwmod_lookup(oh_name); + if (!oh) { + dev_err(&pdev->dev, "Cannot lookup hwmod '%s'\n", + oh_name); + ret = -EINVAL; + goto odbfd_exit1; + } + hwmods[i] = oh; + if (oh->flags & HWMOD_INIT_NO_IDLE) + device_active = true; + } + + od = omap_device_alloc(pdev, hwmods, oh_cnt); + if (IS_ERR(od)) { + dev_err(&pdev->dev, "Cannot allocate omap_device for :%s\n", + oh_name); + ret = PTR_ERR(od); + goto odbfd_exit1; + } + + /* Fix up missing resource names */ + for (i = 0; i < pdev->num_resources; i++) { + struct resource *r = &pdev->resource[i]; + + if (r->name == NULL) + r->name = dev_name(&pdev->dev); + } + + if (!skip_pm_domain) { + dev_pm_domain_set(&pdev->dev, &omap_device_pm_domain); + if (device_active) { + omap_device_enable(pdev); + pm_runtime_set_active(&pdev->dev); + } + } + +odbfd_exit1: + kfree(hwmods); +odbfd_exit: + /* if data/we are at fault.. load up a fail handler */ + if (ret) + dev_pm_domain_set(&pdev->dev, &omap_device_fail_pm_domain); + + return ret; +} + +static int _omap_device_notifier_call(struct notifier_block *nb, + unsigned long event, void *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od; + int err; + + switch (event) { + case BUS_NOTIFY_REMOVED_DEVICE: + if (pdev->archdata.od) + omap_device_delete(pdev->archdata.od); + break; + case BUS_NOTIFY_UNBOUND_DRIVER: + od = to_omap_device(pdev); + if (od && (od->_state == OMAP_DEVICE_STATE_ENABLED)) { + dev_info(dev, "enabled after unload, idling\n"); + err = omap_device_idle(pdev); + if (err) + dev_err(dev, "failed to idle\n"); + } + break; + case BUS_NOTIFY_BIND_DRIVER: + od = to_omap_device(pdev); + if (od) { + od->_driver_status = BUS_NOTIFY_BIND_DRIVER; + if (od->_state == OMAP_DEVICE_STATE_ENABLED && + pm_runtime_status_suspended(dev)) { + pm_runtime_set_active(dev); + } + } + break; + case BUS_NOTIFY_ADD_DEVICE: + if (pdev->dev.of_node) + omap_device_build_from_dt(pdev); + omap_auxdata_legacy_init(dev); + fallthrough; + default: + od = to_omap_device(pdev); + if (od) + od->_driver_status = event; + } + + return NOTIFY_DONE; +} + +/** + * _omap_device_enable_hwmods - call omap_hwmod_enable() on all hwmods + * @od: struct omap_device *od + * + * Enable all underlying hwmods. Returns 0. + */ +static int _omap_device_enable_hwmods(struct omap_device *od) +{ + int ret = 0; + int i; + + for (i = 0; i < od->hwmods_cnt; i++) + ret |= omap_hwmod_enable(od->hwmods[i]); + + return ret; +} + +/** + * _omap_device_idle_hwmods - call omap_hwmod_idle() on all hwmods + * @od: struct omap_device *od + * + * Idle all underlying hwmods. Returns 0. + */ +static int _omap_device_idle_hwmods(struct omap_device *od) +{ + int ret = 0; + int i; + + for (i = 0; i < od->hwmods_cnt; i++) + ret |= omap_hwmod_idle(od->hwmods[i]); + + return ret; +} + +/* Public functions for use by core code */ + +/** + * omap_device_get_context_loss_count - get lost context count + * @pdev: The platform device to update. + * + * Using the primary hwmod, query the context loss count for this + * device. + * + * Callers should consider context for this device lost any time this + * function returns a value different than the value the caller got + * the last time it called this function. + * + * If any hwmods exist for the omap_device associated with @pdev, + * return the context loss counter for that hwmod, otherwise return + * zero. + */ +int omap_device_get_context_loss_count(struct platform_device *pdev) +{ + struct omap_device *od; + u32 ret = 0; + + od = to_omap_device(pdev); + + if (od->hwmods_cnt) + ret = omap_hwmod_get_context_loss_count(od->hwmods[0]); + + return ret; +} + +/** + * omap_device_alloc - allocate an omap_device + * @pdev: platform_device that will be included in this omap_device + * @ohs: ptr to the omap_hwmod for this omap_device + * @oh_cnt: the size of the ohs list + * + * Convenience function for allocating an omap_device structure and filling + * hwmods, and resources. + * + * Returns an struct omap_device pointer or ERR_PTR() on error; + */ +struct omap_device *omap_device_alloc(struct platform_device *pdev, + struct omap_hwmod **ohs, int oh_cnt) +{ + int ret = -ENOMEM; + struct omap_device *od; + int i; + struct omap_hwmod **hwmods; + + od = kzalloc(sizeof(struct omap_device), GFP_KERNEL); + if (!od) + goto oda_exit1; + + od->hwmods_cnt = oh_cnt; + + hwmods = kmemdup(ohs, sizeof(struct omap_hwmod *) * oh_cnt, GFP_KERNEL); + if (!hwmods) + goto oda_exit2; + + od->hwmods = hwmods; + od->pdev = pdev; + pdev->archdata.od = od; + + for (i = 0; i < oh_cnt; i++) { + hwmods[i]->od = od; + _add_hwmod_clocks_clkdev(od, hwmods[i]); + } + + return od; + +oda_exit2: + kfree(od); +oda_exit1: + dev_err(&pdev->dev, "omap_device: build failed (%d)\n", ret); + + return ERR_PTR(ret); +} + +void omap_device_delete(struct omap_device *od) +{ + if (!od) + return; + + od->pdev->archdata.od = NULL; + kfree(od->hwmods); + kfree(od); +} + +#ifdef CONFIG_PM +static int _od_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int ret; + + ret = pm_generic_runtime_suspend(dev); + if (ret) + return ret; + + return omap_device_idle(pdev); +} + +static int _od_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int ret; + + ret = omap_device_enable(pdev); + if (ret) { + dev_err(dev, "use pm_runtime_put_sync_suspend() in driver?\n"); + return ret; + } + + return pm_generic_runtime_resume(dev); +} + +static int _od_fail_runtime_suspend(struct device *dev) +{ + dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__); + return -ENODEV; +} + +static int _od_fail_runtime_resume(struct device *dev) +{ + dev_warn(dev, "%s: FIXME: missing hwmod/omap_dev info\n", __func__); + return -ENODEV; +} + +#endif + +#ifdef CONFIG_SUSPEND +static int _od_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + int ret; + + /* Don't attempt late suspend on a driver that is not bound */ + if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER) + return 0; + + ret = pm_generic_suspend_noirq(dev); + + if (!ret && !pm_runtime_status_suspended(dev)) { + if (pm_generic_runtime_suspend(dev) == 0) { + omap_device_idle(pdev); + od->flags |= OMAP_DEVICE_SUSPENDED; + } + } + + return ret; +} + +static int _od_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + + if (od->flags & OMAP_DEVICE_SUSPENDED) { + od->flags &= ~OMAP_DEVICE_SUSPENDED; + omap_device_enable(pdev); + pm_generic_runtime_resume(dev); + } + + return pm_generic_resume_noirq(dev); +} +#else +#define _od_suspend_noirq NULL +#define _od_resume_noirq NULL +#endif + +struct dev_pm_domain omap_device_fail_pm_domain = { + .ops = { + SET_RUNTIME_PM_OPS(_od_fail_runtime_suspend, + _od_fail_runtime_resume, NULL) + } +}; + +struct dev_pm_domain omap_device_pm_domain = { + .ops = { + SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume, + NULL) + USE_PLATFORM_PM_SLEEP_OPS + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq, + _od_resume_noirq) + } +}; + +/* Public functions for use by device drivers through struct platform_data */ + +/** + * omap_device_enable - fully activate an omap_device + * @pdev: the platform device to activate + * + * Do whatever is necessary for the hwmods underlying omap_device @od + * to be accessible and ready to operate. This generally involves + * enabling clocks, setting SYSCONFIG registers; and in the future may + * involve remuxing pins. Device drivers should call this function + * indirectly via pm_runtime_get*(). Returns -EINVAL if called when + * the omap_device is already enabled, or passes along the return + * value of _omap_device_enable_hwmods(). + */ +int omap_device_enable(struct platform_device *pdev) +{ + int ret; + struct omap_device *od; + + od = to_omap_device(pdev); + + if (od->_state == OMAP_DEVICE_STATE_ENABLED) { + dev_warn(&pdev->dev, + "omap_device: %s() called from invalid state %d\n", + __func__, od->_state); + return -EINVAL; + } + + ret = _omap_device_enable_hwmods(od); + + if (ret == 0) + od->_state = OMAP_DEVICE_STATE_ENABLED; + + return ret; +} + +/** + * omap_device_idle - idle an omap_device + * @pdev: The platform_device (omap_device) to idle + * + * Idle omap_device @od. Device drivers call this function indirectly + * via pm_runtime_put*(). Returns -EINVAL if the omap_device is not + * currently enabled, or passes along the return value of + * _omap_device_idle_hwmods(). + */ +int omap_device_idle(struct platform_device *pdev) +{ + int ret; + struct omap_device *od; + + od = to_omap_device(pdev); + + if (od->_state != OMAP_DEVICE_STATE_ENABLED) { + dev_warn(&pdev->dev, + "omap_device: %s() called from invalid state %d\n", + __func__, od->_state); + return -EINVAL; + } + + ret = _omap_device_idle_hwmods(od); + + if (ret == 0) + od->_state = OMAP_DEVICE_STATE_IDLE; + + return ret; +} + +/** + * omap_device_assert_hardreset - set a device's hardreset line + * @pdev: struct platform_device * to reset + * @name: const char * name of the reset line + * + * Set the hardreset line identified by @name on the IP blocks + * associated with the hwmods backing the platform_device @pdev. All + * of the hwmods associated with @pdev must have the same hardreset + * line linked to them for this to work. Passes along the return value + * of omap_hwmod_assert_hardreset() in the event of any failure, or + * returns 0 upon success. + */ +int omap_device_assert_hardreset(struct platform_device *pdev, const char *name) +{ + struct omap_device *od = to_omap_device(pdev); + int ret = 0; + int i; + + for (i = 0; i < od->hwmods_cnt; i++) { + ret = omap_hwmod_assert_hardreset(od->hwmods[i], name); + if (ret) + break; + } + + return ret; +} + +/** + * omap_device_deassert_hardreset - release a device's hardreset line + * @pdev: struct platform_device * to reset + * @name: const char * name of the reset line + * + * Release the hardreset line identified by @name on the IP blocks + * associated with the hwmods backing the platform_device @pdev. All + * of the hwmods associated with @pdev must have the same hardreset + * line linked to them for this to work. Passes along the return + * value of omap_hwmod_deassert_hardreset() in the event of any + * failure, or returns 0 upon success. + */ +int omap_device_deassert_hardreset(struct platform_device *pdev, + const char *name) +{ + struct omap_device *od = to_omap_device(pdev); + int ret = 0; + int i; + + for (i = 0; i < od->hwmods_cnt; i++) { + ret = omap_hwmod_deassert_hardreset(od->hwmods[i], name); + if (ret) + break; + } + + return ret; +} + +/** + * omap_device_get_by_hwmod_name() - convert a hwmod name to + * device pointer. + * @oh_name: name of the hwmod device + * + * Returns back a struct device * pointer associated with a hwmod + * device represented by a hwmod_name + */ +struct device *omap_device_get_by_hwmod_name(const char *oh_name) +{ + struct omap_hwmod *oh; + + if (!oh_name) { + WARN(1, "%s: no hwmod name!\n", __func__); + return ERR_PTR(-EINVAL); + } + + oh = omap_hwmod_lookup(oh_name); + if (!oh) { + WARN(1, "%s: no hwmod for %s\n", __func__, + oh_name); + return ERR_PTR(-ENODEV); + } + if (!oh->od) { + WARN(1, "%s: no omap_device for %s\n", __func__, + oh_name); + return ERR_PTR(-ENODEV); + } + + return &oh->od->pdev->dev; +} + +static struct notifier_block platform_nb = { + .notifier_call = _omap_device_notifier_call, +}; + +static int __init omap_device_init(void) +{ + bus_register_notifier(&platform_bus_type, &platform_nb); + return 0; +} +omap_postcore_initcall(omap_device_init); + +/** + * omap_device_late_idle - idle devices without drivers + * @dev: struct device * associated with omap_device + * @data: unused + * + * Check the driver bound status of this device, and idle it + * if there is no driver attached. + */ +static int __init omap_device_late_idle(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_device *od = to_omap_device(pdev); + int i; + + if (!od) + return 0; + + /* + * If omap_device state is enabled, but has no driver bound, + * idle it. + */ + + /* + * Some devices (like memory controllers) are always kept + * enabled, and should not be idled even with no drivers. + */ + for (i = 0; i < od->hwmods_cnt; i++) + if (od->hwmods[i]->flags & HWMOD_INIT_NO_IDLE) + return 0; + + if (od->_driver_status != BUS_NOTIFY_BOUND_DRIVER && + od->_driver_status != BUS_NOTIFY_BIND_DRIVER) { + if (od->_state == OMAP_DEVICE_STATE_ENABLED) { + dev_warn(dev, "%s: enabled but no driver. Idling\n", + __func__); + omap_device_idle(pdev); + } + } + + return 0; +} + +static int __init omap_device_late_init(void) +{ + bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle); + + return 0; +} +omap_late_initcall_sync(omap_device_late_init); diff --git a/arch/arm/mach-omap2/omap_device.h b/arch/arm/mach-omap2/omap_device.h new file mode 100644 index 000000000..d607532cf --- /dev/null +++ b/arch/arm/mach-omap2/omap_device.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_device headers + * + * Copyright (C) 2009 Nokia Corporation + * Paul Walmsley + * + * Developed in collaboration with (alphabetical order): Benoit + * Cousson, Kevin Hilman, Tony Lindgren, Rajendra Nayak, Vikram + * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard + * Woodruff + * + * This type of functionality should be implemented as a proper + * omap_bus/omap_device in Linux. + * + * omap_device differs from omap_hwmod in that it includes external + * (e.g., board- and system-level) integration details. omap_hwmod + * stores hardware data that is invariant for a given OMAP chip. + */ +#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_DEVICE_H +#define __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_DEVICE_H + +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#include "omap_hwmod.h" + +extern struct dev_pm_domain omap_device_pm_domain; +extern struct dev_pm_domain omap_device_fail_pm_domain; + +/* omap_device._state values */ +#define OMAP_DEVICE_STATE_UNKNOWN 0 +#define OMAP_DEVICE_STATE_ENABLED 1 +#define OMAP_DEVICE_STATE_IDLE 2 +#define OMAP_DEVICE_STATE_SHUTDOWN 3 + +/* omap_device.flags values */ +#define OMAP_DEVICE_SUSPENDED BIT(0) + +/** + * struct omap_device - omap_device wrapper for platform_devices + * @pdev: platform_device + * @hwmods: (one .. many per omap_device) + * @hwmods_cnt: ARRAY_SIZE() of @hwmods + * @_state: one of OMAP_DEVICE_STATE_* (see above) + * @flags: device flags + * @_driver_status: one of BUS_NOTIFY_*_DRIVER from <linux/device.h> + * + * Integrates omap_hwmod data into Linux platform_device. + * + * Field names beginning with underscores are for the internal use of + * the omap_device code. + * + */ +struct omap_device { + struct platform_device *pdev; + struct omap_hwmod **hwmods; + unsigned long _driver_status; + u8 hwmods_cnt; + u8 _state; + u8 flags; +}; + +/* Device driver interface (call via platform_data fn ptrs) */ + +int omap_device_enable(struct platform_device *pdev); +int omap_device_idle(struct platform_device *pdev); + +/* Core code interface */ + +struct omap_device *omap_device_alloc(struct platform_device *pdev, + struct omap_hwmod **ohs, int oh_cnt); +void omap_device_delete(struct omap_device *od); + +struct device *omap_device_get_by_hwmod_name(const char *oh_name); + +/* OMAP PM interface */ +int omap_device_get_context_loss_count(struct platform_device *pdev); + +/* Other */ + +int omap_device_assert_hardreset(struct platform_device *pdev, + const char *name); +int omap_device_deassert_hardreset(struct platform_device *pdev, + const char *name); + +/* Get omap_device pointer from platform_device pointer */ +static inline struct omap_device *to_omap_device(struct platform_device *pdev) +{ + return pdev ? pdev->archdata.od : NULL; +} +#endif diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c new file mode 100644 index 000000000..31d1a21f6 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -0,0 +1,4071 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod implementation for OMAP2/3/4 + * + * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2011-2012 Texas Instruments, Inc. + * + * Paul Walmsley, Benoît Cousson, Kevin Hilman + * + * Created in collaboration with (alphabetical order): Thara Gopinath, + * Tony Lindgren, Rajendra Nayak, Vikram Pandita, Sakari Poussa, Anand + * Sawant, Santosh Shilimkar, Richard Woodruff + * + * Introduction + * ------------ + * One way to view an OMAP SoC is as a collection of largely unrelated + * IP blocks connected by interconnects. The IP blocks include + * devices such as ARM processors, audio serial interfaces, UARTs, + * etc. Some of these devices, like the DSP, are created by TI; + * others, like the SGX, largely originate from external vendors. In + * TI's documentation, on-chip devices are referred to as "OMAP + * modules." Some of these IP blocks are identical across several + * OMAP versions. Others are revised frequently. + * + * These OMAP modules are tied together by various interconnects. + * Most of the address and data flow between modules is via OCP-based + * interconnects such as the L3 and L4 buses; but there are other + * interconnects that distribute the hardware clock tree, handle idle + * and reset signaling, supply power, and connect the modules to + * various pads or balls on the OMAP package. + * + * OMAP hwmod provides a consistent way to describe the on-chip + * hardware blocks and their integration into the rest of the chip. + * This description can be automatically generated from the TI + * hardware database. OMAP hwmod provides a standard, consistent API + * to reset, enable, idle, and disable these hardware blocks. And + * hwmod provides a way for other core code, such as the Linux device + * code or the OMAP power management and address space mapping code, + * to query the hardware database. + * + * Using hwmod + * ----------- + * Drivers won't call hwmod functions directly. That is done by the + * omap_device code, and in rare occasions, by custom integration code + * in arch/arm/ *omap*. The omap_device code includes functions to + * build a struct platform_device using omap_hwmod data, and that is + * currently how hwmod data is communicated to drivers and to the + * Linux driver model. Most drivers will call omap_hwmod functions only + * indirectly, via pm_runtime*() functions. + * + * From a layering perspective, here is where the OMAP hwmod code + * fits into the kernel software stack: + * + * +-------------------------------+ + * | Device driver code | + * | (e.g., drivers/) | + * +-------------------------------+ + * | Linux driver model | + * | (platform_device / | + * | platform_driver data/code) | + * +-------------------------------+ + * | OMAP core-driver integration | + * |(arch/arm/mach-omap2/devices.c)| + * +-------------------------------+ + * | omap_device code | + * | (../plat-omap/omap_device.c) | + * +-------------------------------+ + * ----> | omap_hwmod code/data | <----- + * | (../mach-omap2/omap_hwmod*) | + * +-------------------------------+ + * | OMAP clock/PRCM/register fns | + * | ({read,write}l_relaxed, clk*) | + * +-------------------------------+ + * + * Device drivers should not contain any OMAP-specific code or data in + * them. They should only contain code to operate the IP block that + * the driver is responsible for. This is because these IP blocks can + * also appear in other SoCs, either from TI (such as DaVinci) or from + * other manufacturers; and drivers should be reusable across other + * platforms. + * + * The OMAP hwmod code also will attempt to reset and idle all on-chip + * devices upon boot. The goal here is for the kernel to be + * completely self-reliant and independent from bootloaders. This is + * to ensure a repeatable configuration, both to ensure consistent + * runtime behavior, and to make it easier for others to reproduce + * bugs. + * + * OMAP module activity states + * --------------------------- + * The hwmod code considers modules to be in one of several activity + * states. IP blocks start out in an UNKNOWN state, then once they + * are registered via the hwmod code, proceed to the REGISTERED state. + * Once their clock names are resolved to clock pointers, the module + * enters the CLKS_INITED state; and finally, once the module has been + * reset and the integration registers programmed, the INITIALIZED state + * is entered. The hwmod code will then place the module into either + * the IDLE state to save power, or in the case of a critical system + * module, the ENABLED state. + * + * OMAP core integration code can then call omap_hwmod*() functions + * directly to move the module between the IDLE, ENABLED, and DISABLED + * states, as needed. This is done during both the PM idle loop, and + * in the OMAP core integration code's implementation of the PM runtime + * functions. + * + * References + * ---------- + * This is a partial list. + * - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064) + * - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090) + * - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108) + * - OMAP4430 Multimedia Device Silicon Revision 1.0 (SWPU140) + * - Open Core Protocol Specification 2.2 + * + * To do: + * - handle IO mapping + * - bus throughput & module latency measurement code + * + * XXX add tests at the beginning of each function to ensure the hwmod is + * in the appropriate state + * XXX error return values should be checked to ensure that they are + * appropriate + */ +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/cpu.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/memblock.h> + +#include <linux/platform_data/ti-sysc.h> + +#include <dt-bindings/bus/ti-sysc.h> + +#include <asm/system_misc.h> + +#include "clock.h" +#include "omap_hwmod.h" + +#include "soc.h" +#include "common.h" +#include "clockdomain.h" +#include "hdq1w.h" +#include "mmc.h" +#include "powerdomain.h" +#include "cm2xxx.h" +#include "cm3xxx.h" +#include "cm33xx.h" +#include "prm.h" +#include "prm3xxx.h" +#include "prm44xx.h" +#include "prm33xx.h" +#include "prminst44xx.h" +#include "pm.h" +#include "wd_timer.h" + +/* Name of the OMAP hwmod for the MPU */ +#define MPU_INITIATOR_NAME "mpu" + +/* + * Number of struct omap_hwmod_link records per struct + * omap_hwmod_ocp_if record (master->slave and slave->master) + */ +#define LINKS_PER_OCP_IF 2 + +/* + * Address offset (in bytes) between the reset control and the reset + * status registers: 4 bytes on OMAP4 + */ +#define OMAP4_RST_CTRL_ST_OFFSET 4 + +/* + * Maximum length for module clock handle names + */ +#define MOD_CLK_MAX_NAME_LEN 32 + +/** + * struct clkctrl_provider - clkctrl provider mapping data + * @num_addrs: number of base address ranges for the provider + * @addr: base address(es) for the provider + * @size: size(s) of the provider address space(s) + * @node: device node associated with the provider + * @link: list link + */ +struct clkctrl_provider { + int num_addrs; + u32 *addr; + u32 *size; + struct device_node *node; + struct list_head link; +}; + +static LIST_HEAD(clkctrl_providers); + +/** + * struct omap_hwmod_reset - IP specific reset functions + * @match: string to match against the module name + * @len: number of characters to match + * @reset: IP specific reset function + * + * Used only in cases where struct omap_hwmod is dynamically allocated. + */ +struct omap_hwmod_reset { + const char *match; + int len; + int (*reset)(struct omap_hwmod *oh); +}; + +/** + * struct omap_hwmod_soc_ops - fn ptrs for some SoC-specific operations + * @enable_module: function to enable a module (via MODULEMODE) + * @disable_module: function to disable a module (via MODULEMODE) + * + * XXX Eventually this functionality will be hidden inside the PRM/CM + * device drivers. Until then, this should avoid huge blocks of cpu_is_*() + * conditionals in this code. + */ +struct omap_hwmod_soc_ops { + void (*enable_module)(struct omap_hwmod *oh); + int (*disable_module)(struct omap_hwmod *oh); + int (*wait_target_ready)(struct omap_hwmod *oh); + int (*assert_hardreset)(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri); + int (*deassert_hardreset)(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri); + int (*is_hardreset_asserted)(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri); + int (*init_clkdm)(struct omap_hwmod *oh); + void (*update_context_lost)(struct omap_hwmod *oh); + int (*get_context_lost)(struct omap_hwmod *oh); + int (*disable_direct_prcm)(struct omap_hwmod *oh); + u32 (*xlate_clkctrl)(struct omap_hwmod *oh); +}; + +/* soc_ops: adapts the omap_hwmod code to the currently-booted SoC */ +static struct omap_hwmod_soc_ops soc_ops; + +/* omap_hwmod_list contains all registered struct omap_hwmods */ +static LIST_HEAD(omap_hwmod_list); +static DEFINE_MUTEX(list_lock); + +/* mpu_oh: used to add/remove MPU initiator from sleepdep list */ +static struct omap_hwmod *mpu_oh; + +/* inited: set to true once the hwmod code is initialized */ +static bool inited; + +/* Private functions */ + +/** + * _update_sysc_cache - return the module OCP_SYSCONFIG register, keep copy + * @oh: struct omap_hwmod * + * + * Load the current value of the hwmod OCP_SYSCONFIG register into the + * struct omap_hwmod for later use. Returns -EINVAL if the hwmod has no + * OCP_SYSCONFIG register or 0 upon success. + */ +static int _update_sysc_cache(struct omap_hwmod *oh) +{ + if (!oh->class->sysc) { + WARN(1, "omap_hwmod: %s: cannot read OCP_SYSCONFIG: not defined on hwmod's class\n", oh->name); + return -EINVAL; + } + + /* XXX ensure module interface clock is up */ + + oh->_sysc_cache = omap_hwmod_read(oh, oh->class->sysc->sysc_offs); + + if (!(oh->class->sysc->sysc_flags & SYSC_NO_CACHE)) + oh->_int_flags |= _HWMOD_SYSCONFIG_LOADED; + + return 0; +} + +/** + * _write_sysconfig - write a value to the module's OCP_SYSCONFIG register + * @v: OCP_SYSCONFIG value to write + * @oh: struct omap_hwmod * + * + * Write @v into the module class' OCP_SYSCONFIG register, if it has + * one. No return value. + */ +static void _write_sysconfig(u32 v, struct omap_hwmod *oh) +{ + if (!oh->class->sysc) { + WARN(1, "omap_hwmod: %s: cannot write OCP_SYSCONFIG: not defined on hwmod's class\n", oh->name); + return; + } + + /* XXX ensure module interface clock is up */ + + /* Module might have lost context, always update cache and register */ + oh->_sysc_cache = v; + + /* + * Some IP blocks (such as RTC) require unlocking of IP before + * accessing its registers. If a function pointer is present + * to unlock, then call it before accessing sysconfig and + * call lock after writing sysconfig. + */ + if (oh->class->unlock) + oh->class->unlock(oh); + + omap_hwmod_write(v, oh, oh->class->sysc->sysc_offs); + + if (oh->class->lock) + oh->class->lock(oh); +} + +/** + * _set_master_standbymode: set the OCP_SYSCONFIG MIDLEMODE field in @v + * @oh: struct omap_hwmod * + * @standbymode: MIDLEMODE field bits + * @v: pointer to register contents to modify + * + * Update the master standby mode bits in @v to be @standbymode for + * the @oh hwmod. Does not write to the hardware. Returns -EINVAL + * upon error or 0 upon success. + */ +static int _set_master_standbymode(struct omap_hwmod *oh, u8 standbymode, + u32 *v) +{ + u32 mstandby_mask; + u8 mstandby_shift; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_MIDLEMODE)) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); + return -EINVAL; + } + + mstandby_shift = oh->class->sysc->sysc_fields->midle_shift; + mstandby_mask = (0x3 << mstandby_shift); + + *v &= ~mstandby_mask; + *v |= __ffs(standbymode) << mstandby_shift; + + return 0; +} + +/** + * _set_slave_idlemode: set the OCP_SYSCONFIG SIDLEMODE field in @v + * @oh: struct omap_hwmod * + * @idlemode: SIDLEMODE field bits + * @v: pointer to register contents to modify + * + * Update the slave idle mode bits in @v to be @idlemode for the @oh + * hwmod. Does not write to the hardware. Returns -EINVAL upon error + * or 0 upon success. + */ +static int _set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode, u32 *v) +{ + u32 sidle_mask; + u8 sidle_shift; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_SIDLEMODE)) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); + return -EINVAL; + } + + sidle_shift = oh->class->sysc->sysc_fields->sidle_shift; + sidle_mask = (0x3 << sidle_shift); + + *v &= ~sidle_mask; + *v |= __ffs(idlemode) << sidle_shift; + + return 0; +} + +/** + * _set_clockactivity: set OCP_SYSCONFIG.CLOCKACTIVITY bits in @v + * @oh: struct omap_hwmod * + * @clockact: CLOCKACTIVITY field bits + * @v: pointer to register contents to modify + * + * Update the clockactivity mode bits in @v to be @clockact for the + * @oh hwmod. Used for additional powersaving on some modules. Does + * not write to the hardware. Returns -EINVAL upon error or 0 upon + * success. + */ +static int _set_clockactivity(struct omap_hwmod *oh, u8 clockact, u32 *v) +{ + u32 clkact_mask; + u8 clkact_shift; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_CLOCKACTIVITY)) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); + return -EINVAL; + } + + clkact_shift = oh->class->sysc->sysc_fields->clkact_shift; + clkact_mask = (0x3 << clkact_shift); + + *v &= ~clkact_mask; + *v |= clockact << clkact_shift; + + return 0; +} + +/** + * _set_softreset: set OCP_SYSCONFIG.SOFTRESET bit in @v + * @oh: struct omap_hwmod * + * @v: pointer to register contents to modify + * + * Set the SOFTRESET bit in @v for hwmod @oh. Returns -EINVAL upon + * error or 0 upon success. + */ +static int _set_softreset(struct omap_hwmod *oh, u32 *v) +{ + u32 softrst_mask; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); + return -EINVAL; + } + + softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift); + + *v |= softrst_mask; + + return 0; +} + +/** + * _clear_softreset: clear OCP_SYSCONFIG.SOFTRESET bit in @v + * @oh: struct omap_hwmod * + * @v: pointer to register contents to modify + * + * Clear the SOFTRESET bit in @v for hwmod @oh. Returns -EINVAL upon + * error or 0 upon success. + */ +static int _clear_softreset(struct omap_hwmod *oh, u32 *v) +{ + u32 softrst_mask; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, + "omap_hwmod: %s: sysc_fields absent for sysconfig class\n", + oh->name); + return -EINVAL; + } + + softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift); + + *v &= ~softrst_mask; + + return 0; +} + +/** + * _wait_softreset_complete - wait for an OCP softreset to complete + * @oh: struct omap_hwmod * to wait on + * + * Wait until the IP block represented by @oh reports that its OCP + * softreset is complete. This can be triggered by software (see + * _ocp_softreset()) or by hardware upon returning from off-mode (one + * example is HSMMC). Waits for up to MAX_MODULE_SOFTRESET_WAIT + * microseconds. Returns the number of microseconds waited. + */ +static int _wait_softreset_complete(struct omap_hwmod *oh) +{ + struct omap_hwmod_class_sysconfig *sysc; + u32 softrst_mask; + int c = 0; + + sysc = oh->class->sysc; + + if (sysc->sysc_flags & SYSS_HAS_RESET_STATUS && sysc->syss_offs > 0) + omap_test_timeout((omap_hwmod_read(oh, sysc->syss_offs) + & SYSS_RESETDONE_MASK), + MAX_MODULE_SOFTRESET_WAIT, c); + else if (sysc->sysc_flags & SYSC_HAS_RESET_STATUS) { + softrst_mask = (0x1 << sysc->sysc_fields->srst_shift); + omap_test_timeout(!(omap_hwmod_read(oh, sysc->sysc_offs) + & softrst_mask), + MAX_MODULE_SOFTRESET_WAIT, c); + } + + return c; +} + +/** + * _set_dmadisable: set OCP_SYSCONFIG.DMADISABLE bit in @v + * @oh: struct omap_hwmod * + * + * The DMADISABLE bit is a semi-automatic bit present in sysconfig register + * of some modules. When the DMA must perform read/write accesses, the + * DMADISABLE bit is cleared by the hardware. But when the DMA must stop + * for power management, software must set the DMADISABLE bit back to 1. + * + * Set the DMADISABLE bit in @v for hwmod @oh. Returns -EINVAL upon + * error or 0 upon success. + */ +static int _set_dmadisable(struct omap_hwmod *oh) +{ + u32 v; + u32 dmadisable_mask; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_DMADISABLE)) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); + return -EINVAL; + } + + /* clocks must be on for this operation */ + if (oh->_state != _HWMOD_STATE_ENABLED) { + pr_warn("omap_hwmod: %s: dma can be disabled only from enabled state\n", oh->name); + return -EINVAL; + } + + pr_debug("omap_hwmod: %s: setting DMADISABLE\n", oh->name); + + v = oh->_sysc_cache; + dmadisable_mask = + (0x1 << oh->class->sysc->sysc_fields->dmadisable_shift); + v |= dmadisable_mask; + _write_sysconfig(v, oh); + + return 0; +} + +/** + * _set_module_autoidle: set the OCP_SYSCONFIG AUTOIDLE field in @v + * @oh: struct omap_hwmod * + * @autoidle: desired AUTOIDLE bitfield value (0 or 1) + * @v: pointer to register contents to modify + * + * Update the module autoidle bit in @v to be @autoidle for the @oh + * hwmod. The autoidle bit controls whether the module can gate + * internal clocks automatically when it isn't doing anything; the + * exact function of this bit varies on a per-module basis. This + * function does not write to the hardware. Returns -EINVAL upon + * error or 0 upon success. + */ +static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle, + u32 *v) +{ + u32 autoidle_mask; + u8 autoidle_shift; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_AUTOIDLE)) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); + return -EINVAL; + } + + autoidle_shift = oh->class->sysc->sysc_fields->autoidle_shift; + autoidle_mask = (0x1 << autoidle_shift); + + *v &= ~autoidle_mask; + *v |= autoidle << autoidle_shift; + + return 0; +} + +/** + * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware + * @oh: struct omap_hwmod * + * + * Allow the hardware module @oh to send wakeups. Returns -EINVAL + * upon error or 0 upon success. + */ +static int _enable_wakeup(struct omap_hwmod *oh, u32 *v) +{ + if (!oh->class->sysc || + !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) || + (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) || + (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP))) + return -EINVAL; + + if (!oh->class->sysc->sysc_fields) { + WARN(1, "omap_hwmod: %s: offset struct for sysconfig not provided in class\n", oh->name); + return -EINVAL; + } + + if (oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) + *v |= 0x1 << oh->class->sysc->sysc_fields->enwkup_shift; + + if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) + _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); + if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) + _set_master_standbymode(oh, HWMOD_IDLEMODE_SMART_WKUP, v); + + /* XXX test pwrdm_get_wken for this hwmod's subsystem */ + + return 0; +} + +static struct clockdomain *_get_clkdm(struct omap_hwmod *oh) +{ + struct clk_hw_omap *clk; + + if (!oh) + return NULL; + + if (oh->clkdm) { + return oh->clkdm; + } else if (oh->_clk) { + if (!omap2_clk_is_hw_omap(__clk_get_hw(oh->_clk))) + return NULL; + clk = to_clk_hw_omap(__clk_get_hw(oh->_clk)); + return clk->clkdm; + } + return NULL; +} + +/** + * _add_initiator_dep: prevent @oh from smart-idling while @init_oh is active + * @oh: struct omap_hwmod * + * + * Prevent the hardware module @oh from entering idle while the + * hardare module initiator @init_oh is active. Useful when a module + * will be accessed by a particular initiator (e.g., if a module will + * be accessed by the IVA, there should be a sleepdep between the IVA + * initiator and the module). Only applies to modules in smart-idle + * mode. If the clockdomain is marked as not needing autodeps, return + * 0 without doing anything. Otherwise, returns -EINVAL upon error or + * passes along clkdm_add_sleepdep() value upon success. + */ +static int _add_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh) +{ + struct clockdomain *clkdm, *init_clkdm; + + clkdm = _get_clkdm(oh); + init_clkdm = _get_clkdm(init_oh); + + if (!clkdm || !init_clkdm) + return -EINVAL; + + if (clkdm && clkdm->flags & CLKDM_NO_AUTODEPS) + return 0; + + return clkdm_add_sleepdep(clkdm, init_clkdm); +} + +/** + * _del_initiator_dep: allow @oh to smart-idle even if @init_oh is active + * @oh: struct omap_hwmod * + * + * Allow the hardware module @oh to enter idle while the hardare + * module initiator @init_oh is active. Useful when a module will not + * be accessed by a particular initiator (e.g., if a module will not + * be accessed by the IVA, there should be no sleepdep between the IVA + * initiator and the module). Only applies to modules in smart-idle + * mode. If the clockdomain is marked as not needing autodeps, return + * 0 without doing anything. Returns -EINVAL upon error or passes + * along clkdm_del_sleepdep() value upon success. + */ +static int _del_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh) +{ + struct clockdomain *clkdm, *init_clkdm; + + clkdm = _get_clkdm(oh); + init_clkdm = _get_clkdm(init_oh); + + if (!clkdm || !init_clkdm) + return -EINVAL; + + if (clkdm && clkdm->flags & CLKDM_NO_AUTODEPS) + return 0; + + return clkdm_del_sleepdep(clkdm, init_clkdm); +} + +static const struct of_device_id ti_clkctrl_match_table[] __initconst = { + { .compatible = "ti,clkctrl" }, + { } +}; + +static int __init _setup_clkctrl_provider(struct device_node *np) +{ + const __be32 *addrp; + struct clkctrl_provider *provider; + u64 size; + int i; + + provider = memblock_alloc(sizeof(*provider), SMP_CACHE_BYTES); + if (!provider) + return -ENOMEM; + + provider->node = np; + + provider->num_addrs = + of_property_count_elems_of_size(np, "reg", sizeof(u32)) / 2; + + provider->addr = + memblock_alloc(sizeof(void *) * provider->num_addrs, + SMP_CACHE_BYTES); + if (!provider->addr) + return -ENOMEM; + + provider->size = + memblock_alloc(sizeof(u32) * provider->num_addrs, + SMP_CACHE_BYTES); + if (!provider->size) + return -ENOMEM; + + for (i = 0; i < provider->num_addrs; i++) { + addrp = of_get_address(np, i, &size, NULL); + provider->addr[i] = (u32)of_translate_address(np, addrp); + provider->size[i] = size; + pr_debug("%s: %pOF: %x...%x\n", __func__, np, provider->addr[i], + provider->addr[i] + provider->size[i]); + } + + list_add(&provider->link, &clkctrl_providers); + + return 0; +} + +static int __init _init_clkctrl_providers(void) +{ + struct device_node *np; + int ret = 0; + + for_each_matching_node(np, ti_clkctrl_match_table) { + ret = _setup_clkctrl_provider(np); + if (ret) { + of_node_put(np); + break; + } + } + + return ret; +} + +static u32 _omap4_xlate_clkctrl(struct omap_hwmod *oh) +{ + if (!oh->prcm.omap4.modulemode) + return 0; + + return omap_cm_xlate_clkctrl(oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, + oh->prcm.omap4.clkctrl_offs); +} + +static struct clk *_lookup_clkctrl_clk(struct omap_hwmod *oh) +{ + struct clkctrl_provider *provider; + struct clk *clk; + u32 addr; + + if (!soc_ops.xlate_clkctrl) + return NULL; + + addr = soc_ops.xlate_clkctrl(oh); + if (!addr) + return NULL; + + pr_debug("%s: %s: addr=%x\n", __func__, oh->name, addr); + + list_for_each_entry(provider, &clkctrl_providers, link) { + int i; + + for (i = 0; i < provider->num_addrs; i++) { + if (provider->addr[i] <= addr && + provider->addr[i] + provider->size[i] > addr) { + struct of_phandle_args clkspec; + + clkspec.np = provider->node; + clkspec.args_count = 2; + clkspec.args[0] = addr - provider->addr[0]; + clkspec.args[1] = 0; + + clk = of_clk_get_from_provider(&clkspec); + + pr_debug("%s: %s got %p (offset=%x, provider=%pOF)\n", + __func__, oh->name, clk, + clkspec.args[0], provider->node); + + return clk; + } + } + } + + return NULL; +} + +/** + * _init_main_clk - get a struct clk * for the hwmod's main functional clk + * @oh: struct omap_hwmod * + * + * Called from _init_clocks(). Populates the @oh _clk (main + * functional clock pointer) if a clock matching the hwmod name is found, + * or a main_clk is present. Returns 0 on success or -EINVAL on error. + */ +static int _init_main_clk(struct omap_hwmod *oh) +{ + int ret = 0; + struct clk *clk = NULL; + + clk = _lookup_clkctrl_clk(oh); + + if (!IS_ERR_OR_NULL(clk)) { + pr_debug("%s: mapped main_clk %s for %s\n", __func__, + __clk_get_name(clk), oh->name); + oh->main_clk = __clk_get_name(clk); + oh->_clk = clk; + soc_ops.disable_direct_prcm(oh); + } else { + if (!oh->main_clk) + return 0; + + oh->_clk = clk_get(NULL, oh->main_clk); + } + + if (IS_ERR(oh->_clk)) { + pr_warn("omap_hwmod: %s: cannot clk_get main_clk %s\n", + oh->name, oh->main_clk); + return -EINVAL; + } + /* + * HACK: This needs a re-visit once clk_prepare() is implemented + * to do something meaningful. Today its just a no-op. + * If clk_prepare() is used at some point to do things like + * voltage scaling etc, then this would have to be moved to + * some point where subsystems like i2c and pmic become + * available. + */ + clk_prepare(oh->_clk); + + if (!_get_clkdm(oh)) + pr_debug("omap_hwmod: %s: missing clockdomain for %s.\n", + oh->name, oh->main_clk); + + return ret; +} + +/** + * _init_interface_clks - get a struct clk * for the hwmod's interface clks + * @oh: struct omap_hwmod * + * + * Called from _init_clocks(). Populates the @oh OCP slave interface + * clock pointers. Returns 0 on success or -EINVAL on error. + */ +static int _init_interface_clks(struct omap_hwmod *oh) +{ + struct omap_hwmod_ocp_if *os; + struct clk *c; + int ret = 0; + + list_for_each_entry(os, &oh->slave_ports, node) { + if (!os->clk) + continue; + + c = clk_get(NULL, os->clk); + if (IS_ERR(c)) { + pr_warn("omap_hwmod: %s: cannot clk_get interface_clk %s\n", + oh->name, os->clk); + ret = -EINVAL; + continue; + } + os->_clk = c; + /* + * HACK: This needs a re-visit once clk_prepare() is implemented + * to do something meaningful. Today its just a no-op. + * If clk_prepare() is used at some point to do things like + * voltage scaling etc, then this would have to be moved to + * some point where subsystems like i2c and pmic become + * available. + */ + clk_prepare(os->_clk); + } + + return ret; +} + +/** + * _init_opt_clk - get a struct clk * for the hwmod's optional clocks + * @oh: struct omap_hwmod * + * + * Called from _init_clocks(). Populates the @oh omap_hwmod_opt_clk + * clock pointers. Returns 0 on success or -EINVAL on error. + */ +static int _init_opt_clks(struct omap_hwmod *oh) +{ + struct omap_hwmod_opt_clk *oc; + struct clk *c; + int i; + int ret = 0; + + for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) { + c = clk_get(NULL, oc->clk); + if (IS_ERR(c)) { + pr_warn("omap_hwmod: %s: cannot clk_get opt_clk %s\n", + oh->name, oc->clk); + ret = -EINVAL; + continue; + } + oc->_clk = c; + /* + * HACK: This needs a re-visit once clk_prepare() is implemented + * to do something meaningful. Today its just a no-op. + * If clk_prepare() is used at some point to do things like + * voltage scaling etc, then this would have to be moved to + * some point where subsystems like i2c and pmic become + * available. + */ + clk_prepare(oc->_clk); + } + + return ret; +} + +static void _enable_optional_clocks(struct omap_hwmod *oh) +{ + struct omap_hwmod_opt_clk *oc; + int i; + + pr_debug("omap_hwmod: %s: enabling optional clocks\n", oh->name); + + for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) + if (oc->_clk) { + pr_debug("omap_hwmod: enable %s:%s\n", oc->role, + __clk_get_name(oc->_clk)); + clk_enable(oc->_clk); + } +} + +static void _disable_optional_clocks(struct omap_hwmod *oh) +{ + struct omap_hwmod_opt_clk *oc; + int i; + + pr_debug("omap_hwmod: %s: disabling optional clocks\n", oh->name); + + for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) + if (oc->_clk) { + pr_debug("omap_hwmod: disable %s:%s\n", oc->role, + __clk_get_name(oc->_clk)); + clk_disable(oc->_clk); + } +} + +/** + * _enable_clocks - enable hwmod main clock and interface clocks + * @oh: struct omap_hwmod * + * + * Enables all clocks necessary for register reads and writes to succeed + * on the hwmod @oh. Returns 0. + */ +static int _enable_clocks(struct omap_hwmod *oh) +{ + struct omap_hwmod_ocp_if *os; + + pr_debug("omap_hwmod: %s: enabling clocks\n", oh->name); + + if (oh->flags & HWMOD_OPT_CLKS_NEEDED) + _enable_optional_clocks(oh); + + if (oh->_clk) + clk_enable(oh->_clk); + + list_for_each_entry(os, &oh->slave_ports, node) { + if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { + omap2_clk_deny_idle(os->_clk); + clk_enable(os->_clk); + } + } + + /* The opt clocks are controlled by the device driver. */ + + return 0; +} + +/** + * _omap4_clkctrl_managed_by_clkfwk - true if clkctrl managed by clock framework + * @oh: struct omap_hwmod * + */ +static bool _omap4_clkctrl_managed_by_clkfwk(struct omap_hwmod *oh) +{ + if (oh->prcm.omap4.flags & HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK) + return true; + + return false; +} + +/** + * _omap4_has_clkctrl_clock - returns true if a module has clkctrl clock + * @oh: struct omap_hwmod * + */ +static bool _omap4_has_clkctrl_clock(struct omap_hwmod *oh) +{ + if (oh->prcm.omap4.clkctrl_offs) + return true; + + if (!oh->prcm.omap4.clkctrl_offs && + oh->prcm.omap4.flags & HWMOD_OMAP4_ZERO_CLKCTRL_OFFSET) + return true; + + return false; +} + +/** + * _disable_clocks - disable hwmod main clock and interface clocks + * @oh: struct omap_hwmod * + * + * Disables the hwmod @oh main functional and interface clocks. Returns 0. + */ +static int _disable_clocks(struct omap_hwmod *oh) +{ + struct omap_hwmod_ocp_if *os; + + pr_debug("omap_hwmod: %s: disabling clocks\n", oh->name); + + if (oh->_clk) + clk_disable(oh->_clk); + + list_for_each_entry(os, &oh->slave_ports, node) { + if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE)) { + clk_disable(os->_clk); + omap2_clk_allow_idle(os->_clk); + } + } + + if (oh->flags & HWMOD_OPT_CLKS_NEEDED) + _disable_optional_clocks(oh); + + /* The opt clocks are controlled by the device driver. */ + + return 0; +} + +/** + * _omap4_enable_module - enable CLKCTRL modulemode on OMAP4 + * @oh: struct omap_hwmod * + * + * Enables the PRCM module mode related to the hwmod @oh. + * No return value. + */ +static void _omap4_enable_module(struct omap_hwmod *oh) +{ + if (!oh->clkdm || !oh->prcm.omap4.modulemode || + _omap4_clkctrl_managed_by_clkfwk(oh)) + return; + + pr_debug("omap_hwmod: %s: %s: %d\n", + oh->name, __func__, oh->prcm.omap4.modulemode); + + omap_cm_module_enable(oh->prcm.omap4.modulemode, + oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, oh->prcm.omap4.clkctrl_offs); +} + +/** + * _omap4_wait_target_disable - wait for a module to be disabled on OMAP4 + * @oh: struct omap_hwmod * + * + * Wait for a module @oh to enter slave idle. Returns 0 if the module + * does not have an IDLEST bit or if the module successfully enters + * slave idle; otherwise, pass along the return value of the + * appropriate *_cm*_wait_module_idle() function. + */ +static int _omap4_wait_target_disable(struct omap_hwmod *oh) +{ + if (!oh) + return -EINVAL; + + if (oh->_int_flags & _HWMOD_NO_MPU_PORT || !oh->clkdm) + return 0; + + if (oh->flags & HWMOD_NO_IDLEST) + return 0; + + if (_omap4_clkctrl_managed_by_clkfwk(oh)) + return 0; + + if (!_omap4_has_clkctrl_clock(oh)) + return 0; + + return omap_cm_wait_module_idle(oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, + oh->prcm.omap4.clkctrl_offs, 0); +} + +/** + * _save_mpu_port_index - find and save the index to @oh's MPU port + * @oh: struct omap_hwmod * + * + * Determines the array index of the OCP slave port that the MPU uses + * to address the device, and saves it into the struct omap_hwmod. + * Intended to be called during hwmod registration only. No return + * value. + */ +static void __init _save_mpu_port_index(struct omap_hwmod *oh) +{ + struct omap_hwmod_ocp_if *os = NULL; + + if (!oh) + return; + + oh->_int_flags |= _HWMOD_NO_MPU_PORT; + + list_for_each_entry(os, &oh->slave_ports, node) { + if (os->user & OCP_USER_MPU) { + oh->_mpu_port = os; + oh->_int_flags &= ~_HWMOD_NO_MPU_PORT; + break; + } + } + + return; +} + +/** + * _find_mpu_rt_port - return omap_hwmod_ocp_if accessible by the MPU + * @oh: struct omap_hwmod * + * + * Given a pointer to a struct omap_hwmod record @oh, return a pointer + * to the struct omap_hwmod_ocp_if record that is used by the MPU to + * communicate with the IP block. This interface need not be directly + * connected to the MPU (and almost certainly is not), but is directly + * connected to the IP block represented by @oh. Returns a pointer + * to the struct omap_hwmod_ocp_if * upon success, or returns NULL upon + * error or if there does not appear to be a path from the MPU to this + * IP block. + */ +static struct omap_hwmod_ocp_if *_find_mpu_rt_port(struct omap_hwmod *oh) +{ + if (!oh || oh->_int_flags & _HWMOD_NO_MPU_PORT || oh->slaves_cnt == 0) + return NULL; + + return oh->_mpu_port; +}; + +/** + * _enable_sysc - try to bring a module out of idle via OCP_SYSCONFIG + * @oh: struct omap_hwmod * + * + * Ensure that the OCP_SYSCONFIG register for the IP block represented + * by @oh is set to indicate to the PRCM that the IP block is active. + * Usually this means placing the module into smart-idle mode and + * smart-standby, but if there is a bug in the automatic idle handling + * for the IP block, it may need to be placed into the force-idle or + * no-idle variants of these modes. No return value. + */ +static void _enable_sysc(struct omap_hwmod *oh) +{ + u8 idlemode, sf; + u32 v; + bool clkdm_act; + struct clockdomain *clkdm; + + if (!oh->class->sysc) + return; + + /* + * Wait until reset has completed, this is needed as the IP + * block is reset automatically by hardware in some cases + * (off-mode for example), and the drivers require the + * IP to be ready when they access it + */ + if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) + _enable_optional_clocks(oh); + _wait_softreset_complete(oh); + if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) + _disable_optional_clocks(oh); + + v = oh->_sysc_cache; + sf = oh->class->sysc->sysc_flags; + + clkdm = _get_clkdm(oh); + if (sf & SYSC_HAS_SIDLEMODE) { + if (oh->flags & HWMOD_SWSUP_SIDLE || + oh->flags & HWMOD_SWSUP_SIDLE_ACT) { + idlemode = HWMOD_IDLEMODE_NO; + } else { + if (sf & SYSC_HAS_ENAWAKEUP) + _enable_wakeup(oh, &v); + if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) + idlemode = HWMOD_IDLEMODE_SMART_WKUP; + else + idlemode = HWMOD_IDLEMODE_SMART; + } + + /* + * This is special handling for some IPs like + * 32k sync timer. Force them to idle! + */ + clkdm_act = (clkdm && clkdm->flags & CLKDM_ACTIVE_WITH_MPU); + if (clkdm_act && !(oh->class->sysc->idlemodes & + (SIDLE_SMART | SIDLE_SMART_WKUP))) + idlemode = HWMOD_IDLEMODE_FORCE; + + _set_slave_idlemode(oh, idlemode, &v); + } + + if (sf & SYSC_HAS_MIDLEMODE) { + if (oh->flags & HWMOD_FORCE_MSTANDBY) { + idlemode = HWMOD_IDLEMODE_FORCE; + } else if (oh->flags & HWMOD_SWSUP_MSTANDBY) { + idlemode = HWMOD_IDLEMODE_NO; + } else { + if (sf & SYSC_HAS_ENAWAKEUP) + _enable_wakeup(oh, &v); + if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) + idlemode = HWMOD_IDLEMODE_SMART_WKUP; + else + idlemode = HWMOD_IDLEMODE_SMART; + } + _set_master_standbymode(oh, idlemode, &v); + } + + /* + * XXX The clock framework should handle this, by + * calling into this code. But this must wait until the + * clock structures are tagged with omap_hwmod entries + */ + if ((oh->flags & HWMOD_SET_DEFAULT_CLOCKACT) && + (sf & SYSC_HAS_CLOCKACTIVITY)) + _set_clockactivity(oh, CLOCKACT_TEST_ICLK, &v); + + _write_sysconfig(v, oh); + + /* + * Set the autoidle bit only after setting the smartidle bit + * Setting this will not have any impact on the other modules. + */ + if (sf & SYSC_HAS_AUTOIDLE) { + idlemode = (oh->flags & HWMOD_NO_OCP_AUTOIDLE) ? + 0 : 1; + _set_module_autoidle(oh, idlemode, &v); + _write_sysconfig(v, oh); + } +} + +/** + * _idle_sysc - try to put a module into idle via OCP_SYSCONFIG + * @oh: struct omap_hwmod * + * + * If module is marked as SWSUP_SIDLE, force the module into slave + * idle; otherwise, configure it for smart-idle. If module is marked + * as SWSUP_MSUSPEND, force the module into master standby; otherwise, + * configure it for smart-standby. No return value. + */ +static void _idle_sysc(struct omap_hwmod *oh) +{ + u8 idlemode, sf; + u32 v; + + if (!oh->class->sysc) + return; + + v = oh->_sysc_cache; + sf = oh->class->sysc->sysc_flags; + + if (sf & SYSC_HAS_SIDLEMODE) { + if (oh->flags & HWMOD_SWSUP_SIDLE) { + idlemode = HWMOD_IDLEMODE_FORCE; + } else { + if (sf & SYSC_HAS_ENAWAKEUP) + _enable_wakeup(oh, &v); + if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP) + idlemode = HWMOD_IDLEMODE_SMART_WKUP; + else + idlemode = HWMOD_IDLEMODE_SMART; + } + _set_slave_idlemode(oh, idlemode, &v); + } + + if (sf & SYSC_HAS_MIDLEMODE) { + if ((oh->flags & HWMOD_SWSUP_MSTANDBY) || + (oh->flags & HWMOD_FORCE_MSTANDBY)) { + idlemode = HWMOD_IDLEMODE_FORCE; + } else { + if (sf & SYSC_HAS_ENAWAKEUP) + _enable_wakeup(oh, &v); + if (oh->class->sysc->idlemodes & MSTANDBY_SMART_WKUP) + idlemode = HWMOD_IDLEMODE_SMART_WKUP; + else + idlemode = HWMOD_IDLEMODE_SMART; + } + _set_master_standbymode(oh, idlemode, &v); + } + + /* If the cached value is the same as the new value, skip the write */ + if (oh->_sysc_cache != v) + _write_sysconfig(v, oh); +} + +/** + * _shutdown_sysc - force a module into idle via OCP_SYSCONFIG + * @oh: struct omap_hwmod * + * + * Force the module into slave idle and master suspend. No return + * value. + */ +static void _shutdown_sysc(struct omap_hwmod *oh) +{ + u32 v; + u8 sf; + + if (!oh->class->sysc) + return; + + v = oh->_sysc_cache; + sf = oh->class->sysc->sysc_flags; + + if (sf & SYSC_HAS_SIDLEMODE) + _set_slave_idlemode(oh, HWMOD_IDLEMODE_FORCE, &v); + + if (sf & SYSC_HAS_MIDLEMODE) + _set_master_standbymode(oh, HWMOD_IDLEMODE_FORCE, &v); + + if (sf & SYSC_HAS_AUTOIDLE) + _set_module_autoidle(oh, 1, &v); + + _write_sysconfig(v, oh); +} + +/** + * _lookup - find an omap_hwmod by name + * @name: find an omap_hwmod by name + * + * Return a pointer to an omap_hwmod by name, or NULL if not found. + */ +static struct omap_hwmod *_lookup(const char *name) +{ + struct omap_hwmod *oh, *temp_oh; + + oh = NULL; + + list_for_each_entry(temp_oh, &omap_hwmod_list, node) { + if (!strcmp(name, temp_oh->name)) { + oh = temp_oh; + break; + } + } + + return oh; +} + +/** + * _init_clkdm - look up a clockdomain name, store pointer in omap_hwmod + * @oh: struct omap_hwmod * + * + * Convert a clockdomain name stored in a struct omap_hwmod into a + * clockdomain pointer, and save it into the struct omap_hwmod. + * Return -EINVAL if the clkdm_name lookup failed. + */ +static int _init_clkdm(struct omap_hwmod *oh) +{ + if (!oh->clkdm_name) { + pr_debug("omap_hwmod: %s: missing clockdomain\n", oh->name); + return 0; + } + + oh->clkdm = clkdm_lookup(oh->clkdm_name); + if (!oh->clkdm) { + pr_warn("omap_hwmod: %s: could not associate to clkdm %s\n", + oh->name, oh->clkdm_name); + return 0; + } + + pr_debug("omap_hwmod: %s: associated to clkdm %s\n", + oh->name, oh->clkdm_name); + + return 0; +} + +/** + * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as + * well the clockdomain. + * @oh: struct omap_hwmod * + * @np: device_node mapped to this hwmod + * + * Called by omap_hwmod_setup_*() (after omap2_clk_init()). + * Resolves all clock names embedded in the hwmod. Returns 0 on + * success, or a negative error code on failure. + */ +static int _init_clocks(struct omap_hwmod *oh, struct device_node *np) +{ + int ret = 0; + + if (oh->_state != _HWMOD_STATE_REGISTERED) + return 0; + + pr_debug("omap_hwmod: %s: looking up clocks\n", oh->name); + + if (soc_ops.init_clkdm) + ret |= soc_ops.init_clkdm(oh); + + ret |= _init_main_clk(oh); + ret |= _init_interface_clks(oh); + ret |= _init_opt_clks(oh); + + if (!ret) + oh->_state = _HWMOD_STATE_CLKS_INITED; + else + pr_warn("omap_hwmod: %s: cannot _init_clocks\n", oh->name); + + return ret; +} + +/** + * _lookup_hardreset - fill register bit info for this hwmod/reset line + * @oh: struct omap_hwmod * + * @name: name of the reset line in the context of this hwmod + * @ohri: struct omap_hwmod_rst_info * that this function will fill in + * + * Return the bit position of the reset line that match the + * input name. Return -ENOENT if not found. + */ +static int _lookup_hardreset(struct omap_hwmod *oh, const char *name, + struct omap_hwmod_rst_info *ohri) +{ + int i; + + for (i = 0; i < oh->rst_lines_cnt; i++) { + const char *rst_line = oh->rst_lines[i].name; + if (!strcmp(rst_line, name)) { + ohri->rst_shift = oh->rst_lines[i].rst_shift; + ohri->st_shift = oh->rst_lines[i].st_shift; + pr_debug("omap_hwmod: %s: %s: %s: rst %d st %d\n", + oh->name, __func__, rst_line, ohri->rst_shift, + ohri->st_shift); + + return 0; + } + } + + return -ENOENT; +} + +/** + * _assert_hardreset - assert the HW reset line of submodules + * contained in the hwmod module. + * @oh: struct omap_hwmod * + * @name: name of the reset line to lookup and assert + * + * Some IP like dsp, ipu or iva contain processor that require an HW + * reset line to be assert / deassert in order to enable fully the IP. + * Returns -EINVAL if @oh is null, -ENOSYS if we have no way of + * asserting the hardreset line on the currently-booted SoC, or passes + * along the return value from _lookup_hardreset() or the SoC's + * assert_hardreset code. + */ +static int _assert_hardreset(struct omap_hwmod *oh, const char *name) +{ + struct omap_hwmod_rst_info ohri; + int ret = -EINVAL; + + if (!oh) + return -EINVAL; + + if (!soc_ops.assert_hardreset) + return -ENOSYS; + + ret = _lookup_hardreset(oh, name, &ohri); + if (ret < 0) + return ret; + + ret = soc_ops.assert_hardreset(oh, &ohri); + + return ret; +} + +/** + * _deassert_hardreset - deassert the HW reset line of submodules contained + * in the hwmod module. + * @oh: struct omap_hwmod * + * @name: name of the reset line to look up and deassert + * + * Some IP like dsp, ipu or iva contain processor that require an HW + * reset line to be assert / deassert in order to enable fully the IP. + * Returns -EINVAL if @oh is null, -ENOSYS if we have no way of + * deasserting the hardreset line on the currently-booted SoC, or passes + * along the return value from _lookup_hardreset() or the SoC's + * deassert_hardreset code. + */ +static int _deassert_hardreset(struct omap_hwmod *oh, const char *name) +{ + struct omap_hwmod_rst_info ohri; + int ret = -EINVAL; + + if (!oh) + return -EINVAL; + + if (!soc_ops.deassert_hardreset) + return -ENOSYS; + + ret = _lookup_hardreset(oh, name, &ohri); + if (ret < 0) + return ret; + + if (oh->clkdm) { + /* + * A clockdomain must be in SW_SUP otherwise reset + * might not be completed. The clockdomain can be set + * in HW_AUTO only when the module become ready. + */ + clkdm_deny_idle(oh->clkdm); + ret = clkdm_hwmod_enable(oh->clkdm, oh); + if (ret) { + WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", + oh->name, oh->clkdm->name, ret); + return ret; + } + } + + _enable_clocks(oh); + if (soc_ops.enable_module) + soc_ops.enable_module(oh); + + ret = soc_ops.deassert_hardreset(oh, &ohri); + + if (soc_ops.disable_module) + soc_ops.disable_module(oh); + _disable_clocks(oh); + + if (ret == -EBUSY) + pr_warn("omap_hwmod: %s: failed to hardreset\n", oh->name); + + if (oh->clkdm) { + /* + * Set the clockdomain to HW_AUTO, assuming that the + * previous state was HW_AUTO. + */ + clkdm_allow_idle(oh->clkdm); + + clkdm_hwmod_disable(oh->clkdm, oh); + } + + return ret; +} + +/** + * _read_hardreset - read the HW reset line state of submodules + * contained in the hwmod module + * @oh: struct omap_hwmod * + * @name: name of the reset line to look up and read + * + * Return the state of the reset line. Returns -EINVAL if @oh is + * null, -ENOSYS if we have no way of reading the hardreset line + * status on the currently-booted SoC, or passes along the return + * value from _lookup_hardreset() or the SoC's is_hardreset_asserted + * code. + */ +static int _read_hardreset(struct omap_hwmod *oh, const char *name) +{ + struct omap_hwmod_rst_info ohri; + int ret = -EINVAL; + + if (!oh) + return -EINVAL; + + if (!soc_ops.is_hardreset_asserted) + return -ENOSYS; + + ret = _lookup_hardreset(oh, name, &ohri); + if (ret < 0) + return ret; + + return soc_ops.is_hardreset_asserted(oh, &ohri); +} + +/** + * _are_all_hardreset_lines_asserted - return true if the @oh is hard-reset + * @oh: struct omap_hwmod * + * + * If all hardreset lines associated with @oh are asserted, then return true. + * Otherwise, if part of @oh is out hardreset or if no hardreset lines + * associated with @oh are asserted, then return false. + * This function is used to avoid executing some parts of the IP block + * enable/disable sequence if its hardreset line is set. + */ +static bool _are_all_hardreset_lines_asserted(struct omap_hwmod *oh) +{ + int i, rst_cnt = 0; + + if (oh->rst_lines_cnt == 0) + return false; + + for (i = 0; i < oh->rst_lines_cnt; i++) + if (_read_hardreset(oh, oh->rst_lines[i].name) > 0) + rst_cnt++; + + if (oh->rst_lines_cnt == rst_cnt) + return true; + + return false; +} + +/** + * _are_any_hardreset_lines_asserted - return true if any part of @oh is + * hard-reset + * @oh: struct omap_hwmod * + * + * If any hardreset lines associated with @oh are asserted, then + * return true. Otherwise, if no hardreset lines associated with @oh + * are asserted, or if @oh has no hardreset lines, then return false. + * This function is used to avoid executing some parts of the IP block + * enable/disable sequence if any hardreset line is set. + */ +static bool _are_any_hardreset_lines_asserted(struct omap_hwmod *oh) +{ + int rst_cnt = 0; + int i; + + for (i = 0; i < oh->rst_lines_cnt && rst_cnt == 0; i++) + if (_read_hardreset(oh, oh->rst_lines[i].name) > 0) + rst_cnt++; + + return (rst_cnt) ? true : false; +} + +/** + * _omap4_disable_module - enable CLKCTRL modulemode on OMAP4 + * @oh: struct omap_hwmod * + * + * Disable the PRCM module mode related to the hwmod @oh. + * Return EINVAL if the modulemode is not supported and 0 in case of success. + */ +static int _omap4_disable_module(struct omap_hwmod *oh) +{ + int v; + + if (!oh->clkdm || !oh->prcm.omap4.modulemode || + _omap4_clkctrl_managed_by_clkfwk(oh)) + return -EINVAL; + + /* + * Since integration code might still be doing something, only + * disable if all lines are under hardreset. + */ + if (_are_any_hardreset_lines_asserted(oh)) + return 0; + + pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__); + + omap_cm_module_disable(oh->clkdm->prcm_partition, oh->clkdm->cm_inst, + oh->prcm.omap4.clkctrl_offs); + + v = _omap4_wait_target_disable(oh); + if (v) + pr_warn("omap_hwmod: %s: _wait_target_disable failed\n", + oh->name); + + return 0; +} + +/** + * _ocp_softreset - reset an omap_hwmod via the OCP_SYSCONFIG bit + * @oh: struct omap_hwmod * + * + * Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit. hwmod must be + * enabled for this to work. Returns -ENOENT if the hwmod cannot be + * reset this way, -EINVAL if the hwmod is in the wrong state, + * -ETIMEDOUT if the module did not reset in time, or 0 upon success. + * + * In OMAP3 a specific SYSSTATUS register is used to get the reset status. + * Starting in OMAP4, some IPs do not have SYSSTATUS registers and instead + * use the SYSCONFIG softreset bit to provide the status. + * + * Note that some IP like McBSP do have reset control but don't have + * reset status. + */ +static int _ocp_softreset(struct omap_hwmod *oh) +{ + u32 v; + int c = 0; + int ret = 0; + + if (!oh->class->sysc || + !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) + return -ENOENT; + + /* clocks must be on for this operation */ + if (oh->_state != _HWMOD_STATE_ENABLED) { + pr_warn("omap_hwmod: %s: reset can only be entered from enabled state\n", + oh->name); + return -EINVAL; + } + + /* For some modules, all optionnal clocks need to be enabled as well */ + if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) + _enable_optional_clocks(oh); + + pr_debug("omap_hwmod: %s: resetting via OCP SOFTRESET\n", oh->name); + + v = oh->_sysc_cache; + ret = _set_softreset(oh, &v); + if (ret) + goto dis_opt_clks; + + _write_sysconfig(v, oh); + + if (oh->class->sysc->srst_udelay) + udelay(oh->class->sysc->srst_udelay); + + c = _wait_softreset_complete(oh); + if (c == MAX_MODULE_SOFTRESET_WAIT) { + pr_warn("omap_hwmod: %s: softreset failed (waited %d usec)\n", + oh->name, MAX_MODULE_SOFTRESET_WAIT); + ret = -ETIMEDOUT; + goto dis_opt_clks; + } else { + pr_debug("omap_hwmod: %s: softreset in %d usec\n", oh->name, c); + } + + ret = _clear_softreset(oh, &v); + if (ret) + goto dis_opt_clks; + + _write_sysconfig(v, oh); + + /* + * XXX add _HWMOD_STATE_WEDGED for modules that don't come back from + * _wait_target_ready() or _reset() + */ + +dis_opt_clks: + if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET) + _disable_optional_clocks(oh); + + return ret; +} + +/** + * _reset - reset an omap_hwmod + * @oh: struct omap_hwmod * + * + * Resets an omap_hwmod @oh. If the module has a custom reset + * function pointer defined, then call it to reset the IP block, and + * pass along its return value to the caller. Otherwise, if the IP + * block has an OCP_SYSCONFIG register with a SOFTRESET bitfield + * associated with it, call a function to reset the IP block via that + * method, and pass along the return value to the caller. Finally, if + * the IP block has some hardreset lines associated with it, assert + * all of those, but do _not_ deassert them. (This is because driver + * authors have expressed an apparent requirement to control the + * deassertion of the hardreset lines themselves.) + * + * The default software reset mechanism for most OMAP IP blocks is + * triggered via the OCP_SYSCONFIG.SOFTRESET bit. However, some + * hwmods cannot be reset via this method. Some are not targets and + * therefore have no OCP header registers to access. Others (like the + * IVA) have idiosyncratic reset sequences. So for these relatively + * rare cases, custom reset code can be supplied in the struct + * omap_hwmod_class .reset function pointer. + * + * _set_dmadisable() is called to set the DMADISABLE bit so that it + * does not prevent idling of the system. This is necessary for cases + * where ROMCODE/BOOTLOADER uses dma and transfers control to the + * kernel without disabling dma. + * + * Passes along the return value from either _ocp_softreset() or the + * custom reset function - these must return -EINVAL if the hwmod + * cannot be reset this way or if the hwmod is in the wrong state, + * -ETIMEDOUT if the module did not reset in time, or 0 upon success. + */ +static int _reset(struct omap_hwmod *oh) +{ + int i, r; + + pr_debug("omap_hwmod: %s: resetting\n", oh->name); + + if (oh->class->reset) { + r = oh->class->reset(oh); + } else { + if (oh->rst_lines_cnt > 0) { + for (i = 0; i < oh->rst_lines_cnt; i++) + _assert_hardreset(oh, oh->rst_lines[i].name); + return 0; + } else { + r = _ocp_softreset(oh); + if (r == -ENOENT) + r = 0; + } + } + + _set_dmadisable(oh); + + /* + * OCP_SYSCONFIG bits need to be reprogrammed after a + * softreset. The _enable() function should be split to avoid + * the rewrite of the OCP_SYSCONFIG register. + */ + if (oh->class->sysc) { + _update_sysc_cache(oh); + _enable_sysc(oh); + } + + return r; +} + +/** + * _omap4_update_context_lost - increment hwmod context loss counter if + * hwmod context was lost, and clear hardware context loss reg + * @oh: hwmod to check for context loss + * + * If the PRCM indicates that the hwmod @oh lost context, increment + * our in-memory context loss counter, and clear the RM_*_CONTEXT + * bits. No return value. + */ +static void _omap4_update_context_lost(struct omap_hwmod *oh) +{ + if (oh->prcm.omap4.flags & HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT) + return; + + if (!prm_was_any_context_lost_old(oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.context_offs)) + return; + + oh->prcm.omap4.context_lost_counter++; + prm_clear_context_loss_flags_old(oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.context_offs); +} + +/** + * _omap4_get_context_lost - get context loss counter for a hwmod + * @oh: hwmod to get context loss counter for + * + * Returns the in-memory context loss counter for a hwmod. + */ +static int _omap4_get_context_lost(struct omap_hwmod *oh) +{ + return oh->prcm.omap4.context_lost_counter; +} + +/** + * _enable - enable an omap_hwmod + * @oh: struct omap_hwmod * + * + * Enables an omap_hwmod @oh such that the MPU can access the hwmod's + * register target. Returns -EINVAL if the hwmod is in the wrong + * state or passes along the return value of _wait_target_ready(). + */ +static int _enable(struct omap_hwmod *oh) +{ + int r; + + pr_debug("omap_hwmod: %s: enabling\n", oh->name); + + /* + * hwmods with HWMOD_INIT_NO_IDLE flag set are left in enabled + * state at init. + */ + if (oh->_int_flags & _HWMOD_SKIP_ENABLE) { + oh->_int_flags &= ~_HWMOD_SKIP_ENABLE; + return 0; + } + + if (oh->_state != _HWMOD_STATE_INITIALIZED && + oh->_state != _HWMOD_STATE_IDLE && + oh->_state != _HWMOD_STATE_DISABLED) { + WARN(1, "omap_hwmod: %s: enabled state can only be entered from initialized, idle, or disabled state\n", + oh->name); + return -EINVAL; + } + + /* + * If an IP block contains HW reset lines and all of them are + * asserted, we let integration code associated with that + * block handle the enable. We've received very little + * information on what those driver authors need, and until + * detailed information is provided and the driver code is + * posted to the public lists, this is probably the best we + * can do. + */ + if (_are_all_hardreset_lines_asserted(oh)) + return 0; + + _add_initiator_dep(oh, mpu_oh); + + if (oh->clkdm) { + /* + * A clockdomain must be in SW_SUP before enabling + * completely the module. The clockdomain can be set + * in HW_AUTO only when the module become ready. + */ + clkdm_deny_idle(oh->clkdm); + r = clkdm_hwmod_enable(oh->clkdm, oh); + if (r) { + WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n", + oh->name, oh->clkdm->name, r); + return r; + } + } + + _enable_clocks(oh); + if (soc_ops.enable_module) + soc_ops.enable_module(oh); + if (oh->flags & HWMOD_BLOCK_WFI) + cpu_idle_poll_ctrl(true); + + if (soc_ops.update_context_lost) + soc_ops.update_context_lost(oh); + + r = (soc_ops.wait_target_ready) ? soc_ops.wait_target_ready(oh) : + -EINVAL; + if (oh->clkdm && !(oh->flags & HWMOD_CLKDM_NOAUTO)) + clkdm_allow_idle(oh->clkdm); + + if (!r) { + oh->_state = _HWMOD_STATE_ENABLED; + + /* Access the sysconfig only if the target is ready */ + if (oh->class->sysc) { + if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) + _update_sysc_cache(oh); + _enable_sysc(oh); + } + } else { + if (soc_ops.disable_module) + soc_ops.disable_module(oh); + _disable_clocks(oh); + pr_err("omap_hwmod: %s: _wait_target_ready failed: %d\n", + oh->name, r); + + if (oh->clkdm) + clkdm_hwmod_disable(oh->clkdm, oh); + } + + return r; +} + +/** + * _idle - idle an omap_hwmod + * @oh: struct omap_hwmod * + * + * Idles an omap_hwmod @oh. This should be called once the hwmod has + * no further work. Returns -EINVAL if the hwmod is in the wrong + * state or returns 0. + */ +static int _idle(struct omap_hwmod *oh) +{ + if (oh->flags & HWMOD_NO_IDLE) { + oh->_int_flags |= _HWMOD_SKIP_ENABLE; + return 0; + } + + pr_debug("omap_hwmod: %s: idling\n", oh->name); + + if (_are_all_hardreset_lines_asserted(oh)) + return 0; + + if (oh->_state != _HWMOD_STATE_ENABLED) { + WARN(1, "omap_hwmod: %s: idle state can only be entered from enabled state\n", + oh->name); + return -EINVAL; + } + + if (oh->class->sysc) + _idle_sysc(oh); + _del_initiator_dep(oh, mpu_oh); + + /* + * If HWMOD_CLKDM_NOAUTO is set then we don't + * deny idle the clkdm again since idle was already denied + * in _enable() + */ + if (oh->clkdm && !(oh->flags & HWMOD_CLKDM_NOAUTO)) + clkdm_deny_idle(oh->clkdm); + + if (oh->flags & HWMOD_BLOCK_WFI) + cpu_idle_poll_ctrl(false); + if (soc_ops.disable_module) + soc_ops.disable_module(oh); + + /* + * The module must be in idle mode before disabling any parents + * clocks. Otherwise, the parent clock might be disabled before + * the module transition is done, and thus will prevent the + * transition to complete properly. + */ + _disable_clocks(oh); + if (oh->clkdm) { + clkdm_allow_idle(oh->clkdm); + clkdm_hwmod_disable(oh->clkdm, oh); + } + + oh->_state = _HWMOD_STATE_IDLE; + + return 0; +} + +/** + * _shutdown - shutdown an omap_hwmod + * @oh: struct omap_hwmod * + * + * Shut down an omap_hwmod @oh. This should be called when the driver + * used for the hwmod is removed or unloaded or if the driver is not + * used by the system. Returns -EINVAL if the hwmod is in the wrong + * state or returns 0. + */ +static int _shutdown(struct omap_hwmod *oh) +{ + int ret, i; + u8 prev_state; + + if (_are_all_hardreset_lines_asserted(oh)) + return 0; + + if (oh->_state != _HWMOD_STATE_IDLE && + oh->_state != _HWMOD_STATE_ENABLED) { + WARN(1, "omap_hwmod: %s: disabled state can only be entered from idle, or enabled state\n", + oh->name); + return -EINVAL; + } + + pr_debug("omap_hwmod: %s: disabling\n", oh->name); + + if (oh->class->pre_shutdown) { + prev_state = oh->_state; + if (oh->_state == _HWMOD_STATE_IDLE) + _enable(oh); + ret = oh->class->pre_shutdown(oh); + if (ret) { + if (prev_state == _HWMOD_STATE_IDLE) + _idle(oh); + return ret; + } + } + + if (oh->class->sysc) { + if (oh->_state == _HWMOD_STATE_IDLE) + _enable(oh); + _shutdown_sysc(oh); + } + + /* clocks and deps are already disabled in idle */ + if (oh->_state == _HWMOD_STATE_ENABLED) { + _del_initiator_dep(oh, mpu_oh); + /* XXX what about the other system initiators here? dma, dsp */ + if (oh->flags & HWMOD_BLOCK_WFI) + cpu_idle_poll_ctrl(false); + if (soc_ops.disable_module) + soc_ops.disable_module(oh); + _disable_clocks(oh); + if (oh->clkdm) + clkdm_hwmod_disable(oh->clkdm, oh); + } + /* XXX Should this code also force-disable the optional clocks? */ + + for (i = 0; i < oh->rst_lines_cnt; i++) + _assert_hardreset(oh, oh->rst_lines[i].name); + + oh->_state = _HWMOD_STATE_DISABLED; + + return 0; +} + +static int of_dev_find_hwmod(struct device_node *np, + struct omap_hwmod *oh) +{ + int count, i, res; + const char *p; + + count = of_property_count_strings(np, "ti,hwmods"); + if (count < 1) + return -ENODEV; + + for (i = 0; i < count; i++) { + res = of_property_read_string_index(np, "ti,hwmods", + i, &p); + if (res) + continue; + if (!strcmp(p, oh->name)) { + pr_debug("omap_hwmod: dt %pOFn[%i] uses hwmod %s\n", + np, i, oh->name); + return i; + } + } + + return -ENODEV; +} + +/** + * of_dev_hwmod_lookup - look up needed hwmod from dt blob + * @np: struct device_node * + * @oh: struct omap_hwmod * + * @index: index of the entry found + * @found: struct device_node * found or NULL + * + * Parse the dt blob and find out needed hwmod. Recursive function is + * implemented to take care hierarchical dt blob parsing. + * Return: Returns 0 on success, -ENODEV when not found. + */ +static int of_dev_hwmod_lookup(struct device_node *np, + struct omap_hwmod *oh, + int *index, + struct device_node **found) +{ + struct device_node *np0 = NULL; + int res; + + res = of_dev_find_hwmod(np, oh); + if (res >= 0) { + *found = np; + *index = res; + return 0; + } + + for_each_child_of_node(np, np0) { + struct device_node *fc; + int i; + + res = of_dev_hwmod_lookup(np0, oh, &i, &fc); + if (res == 0) { + *found = fc; + *index = i; + of_node_put(np0); + return 0; + } + } + + *found = NULL; + *index = 0; + + return -ENODEV; +} + +/** + * omap_hwmod_fix_mpu_rt_idx - fix up mpu_rt_idx register offsets + * + * @oh: struct omap_hwmod * + * @np: struct device_node * + * + * Fix up module register offsets for modules with mpu_rt_idx. + * Only needed for cpsw with interconnect target module defined + * in device tree while still using legacy hwmod platform data + * for rev, sysc and syss registers. + * + * Can be removed when all cpsw hwmod platform data has been + * dropped. + */ +static void omap_hwmod_fix_mpu_rt_idx(struct omap_hwmod *oh, + struct device_node *np, + struct resource *res) +{ + struct device_node *child = NULL; + int error; + + child = of_get_next_child(np, child); + if (!child) + return; + + error = of_address_to_resource(child, oh->mpu_rt_idx, res); + if (error) + pr_err("%s: error mapping mpu_rt_idx: %i\n", + __func__, error); +} + +/** + * omap_hwmod_parse_module_range - map module IO range from device tree + * @oh: struct omap_hwmod * + * @np: struct device_node * + * + * Parse the device tree range an interconnect target module provides + * for it's child device IP blocks. This way we can support the old + * "ti,hwmods" property with just dts data without a need for platform + * data for IO resources. And we don't need all the child IP device + * nodes available in the dts. + */ +int omap_hwmod_parse_module_range(struct omap_hwmod *oh, + struct device_node *np, + struct resource *res) +{ + struct property *prop; + const __be32 *ranges; + const char *name; + u32 nr_addr, nr_size; + u64 base, size; + int len, error; + + if (!res) + return -EINVAL; + + ranges = of_get_property(np, "ranges", &len); + if (!ranges) + return -ENOENT; + + len /= sizeof(*ranges); + + if (len < 3) + return -EINVAL; + + of_property_for_each_string(np, "compatible", prop, name) + if (!strncmp("ti,sysc-", name, 8)) + break; + + if (!name) + return -ENOENT; + + error = of_property_read_u32(np, "#address-cells", &nr_addr); + if (error) + return -ENOENT; + + error = of_property_read_u32(np, "#size-cells", &nr_size); + if (error) + return -ENOENT; + + if (nr_addr != 1 || nr_size != 1) { + pr_err("%s: invalid range for %s->%pOFn\n", __func__, + oh->name, np); + return -EINVAL; + } + + ranges++; + base = of_translate_address(np, ranges++); + size = be32_to_cpup(ranges); + + pr_debug("omap_hwmod: %s %pOFn at 0x%llx size 0x%llx\n", + oh->name, np, base, size); + + if (oh && oh->mpu_rt_idx) { + omap_hwmod_fix_mpu_rt_idx(oh, np, res); + + return 0; + } + + res->start = base; + res->end = base + size - 1; + res->flags = IORESOURCE_MEM; + + return 0; +} + +/** + * _init_mpu_rt_base - populate the virtual address for a hwmod + * @oh: struct omap_hwmod * to locate the virtual address + * @data: (unused, caller should pass NULL) + * @index: index of the reg entry iospace in device tree + * @np: struct device_node * of the IP block's device node in the DT data + * + * Cache the virtual address used by the MPU to access this IP block's + * registers. This address is needed early so the OCP registers that + * are part of the device's address space can be ioremapped properly. + * + * If SYSC access is not needed, the registers will not be remapped + * and non-availability of MPU access is not treated as an error. + * + * Returns 0 on success, -EINVAL if an invalid hwmod is passed, and + * -ENXIO on absent or invalid register target address space. + */ +static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, + int index, struct device_node *np) +{ + void __iomem *va_start = NULL; + struct resource res; + int error; + + if (!oh) + return -EINVAL; + + _save_mpu_port_index(oh); + + /* if we don't need sysc access we don't need to ioremap */ + if (!oh->class->sysc) + return 0; + + /* we can't continue without MPU PORT if we need sysc access */ + if (oh->_int_flags & _HWMOD_NO_MPU_PORT) + return -ENXIO; + + if (!np) { + pr_err("omap_hwmod: %s: no dt node\n", oh->name); + return -ENXIO; + } + + /* Do we have a dts range for the interconnect target module? */ + error = omap_hwmod_parse_module_range(oh, np, &res); + if (!error) + va_start = ioremap(res.start, resource_size(&res)); + + /* No ranges, rely on device reg entry */ + if (!va_start) + va_start = of_iomap(np, index + oh->mpu_rt_idx); + if (!va_start) { + pr_err("omap_hwmod: %s: Missing dt reg%i for %pOF\n", + oh->name, index, np); + return -ENXIO; + } + + pr_debug("omap_hwmod: %s: MPU register target at va %p\n", + oh->name, va_start); + + oh->_mpu_rt_va = va_start; + return 0; +} + +static void __init parse_module_flags(struct omap_hwmod *oh, + struct device_node *np) +{ + if (of_find_property(np, "ti,no-reset-on-init", NULL)) + oh->flags |= HWMOD_INIT_NO_RESET; + if (of_find_property(np, "ti,no-idle-on-init", NULL)) + oh->flags |= HWMOD_INIT_NO_IDLE; + if (of_find_property(np, "ti,no-idle", NULL)) + oh->flags |= HWMOD_NO_IDLE; +} + +/** + * _init - initialize internal data for the hwmod @oh + * @oh: struct omap_hwmod * + * @n: (unused) + * + * Look up the clocks and the address space used by the MPU to access + * registers belonging to the hwmod @oh. @oh must already be + * registered at this point. This is the first of two phases for + * hwmod initialization. Code called here does not touch any hardware + * registers, it simply prepares internal data structures. Returns 0 + * upon success or if the hwmod isn't registered or if the hwmod's + * address space is not defined, or -EINVAL upon failure. + */ +static int __init _init(struct omap_hwmod *oh, void *data) +{ + int r, index; + struct device_node *np = NULL; + struct device_node *bus; + + if (oh->_state != _HWMOD_STATE_REGISTERED) + return 0; + + bus = of_find_node_by_name(NULL, "ocp"); + if (!bus) + return -ENODEV; + + r = of_dev_hwmod_lookup(bus, oh, &index, &np); + if (r) + pr_debug("omap_hwmod: %s missing dt data\n", oh->name); + else if (np && index) + pr_warn("omap_hwmod: %s using broken dt data from %pOFn\n", + oh->name, np); + + r = _init_mpu_rt_base(oh, NULL, index, np); + if (r < 0) { + WARN(1, "omap_hwmod: %s: doesn't have mpu register target base\n", + oh->name); + return 0; + } + + r = _init_clocks(oh, np); + if (r < 0) { + WARN(1, "omap_hwmod: %s: couldn't init clocks\n", oh->name); + return -EINVAL; + } + + if (np) { + struct device_node *child; + + parse_module_flags(oh, np); + child = of_get_next_child(np, NULL); + if (child) + parse_module_flags(oh, child); + } + + oh->_state = _HWMOD_STATE_INITIALIZED; + + return 0; +} + +/** + * _setup_iclk_autoidle - configure an IP block's interface clocks + * @oh: struct omap_hwmod * + * + * Set up the module's interface clocks. XXX This function is still mostly + * a stub; implementing this properly requires iclk autoidle usecounting in + * the clock code. No return value. + */ +static void _setup_iclk_autoidle(struct omap_hwmod *oh) +{ + struct omap_hwmod_ocp_if *os; + + if (oh->_state != _HWMOD_STATE_INITIALIZED) + return; + + list_for_each_entry(os, &oh->slave_ports, node) { + if (!os->_clk) + continue; + + if (os->flags & OCPIF_SWSUP_IDLE) { + /* + * we might have multiple users of one iclk with + * different requirements, disable autoidle when + * the module is enabled, e.g. dss iclk + */ + } else { + /* we are enabling autoidle afterwards anyways */ + clk_enable(os->_clk); + } + } + + return; +} + +/** + * _setup_reset - reset an IP block during the setup process + * @oh: struct omap_hwmod * + * + * Reset the IP block corresponding to the hwmod @oh during the setup + * process. The IP block is first enabled so it can be successfully + * reset. Returns 0 upon success or a negative error code upon + * failure. + */ +static int _setup_reset(struct omap_hwmod *oh) +{ + int r = 0; + + if (oh->_state != _HWMOD_STATE_INITIALIZED) + return -EINVAL; + + if (oh->flags & HWMOD_EXT_OPT_MAIN_CLK) + return -EPERM; + + if (oh->rst_lines_cnt == 0) { + r = _enable(oh); + if (r) { + pr_warn("omap_hwmod: %s: cannot be enabled for reset (%d)\n", + oh->name, oh->_state); + return -EINVAL; + } + } + + if (!(oh->flags & HWMOD_INIT_NO_RESET)) + r = _reset(oh); + + return r; +} + +/** + * _setup_postsetup - transition to the appropriate state after _setup + * @oh: struct omap_hwmod * + * + * Place an IP block represented by @oh into a "post-setup" state -- + * either IDLE, ENABLED, or DISABLED. ("post-setup" simply means that + * this function is called at the end of _setup().) The postsetup + * state for an IP block can be changed by calling + * omap_hwmod_enter_postsetup_state() early in the boot process, + * before one of the omap_hwmod_setup*() functions are called for the + * IP block. + * + * The IP block stays in this state until a PM runtime-based driver is + * loaded for that IP block. A post-setup state of IDLE is + * appropriate for almost all IP blocks with runtime PM-enabled + * drivers, since those drivers are able to enable the IP block. A + * post-setup state of ENABLED is appropriate for kernels with PM + * runtime disabled. The DISABLED state is appropriate for unusual IP + * blocks such as the MPU WDTIMER on kernels without WDTIMER drivers + * included, since the WDTIMER starts running on reset and will reset + * the MPU if left active. + * + * This post-setup mechanism is deprecated. Once all of the OMAP + * drivers have been converted to use PM runtime, and all of the IP + * block data and interconnect data is available to the hwmod code, it + * should be possible to replace this mechanism with a "lazy reset" + * arrangement. In a "lazy reset" setup, each IP block is enabled + * when the driver first probes, then all remaining IP blocks without + * drivers are either shut down or enabled after the drivers have + * loaded. However, this cannot take place until the above + * preconditions have been met, since otherwise the late reset code + * has no way of knowing which IP blocks are in use by drivers, and + * which ones are unused. + * + * No return value. + */ +static void _setup_postsetup(struct omap_hwmod *oh) +{ + u8 postsetup_state; + + if (oh->rst_lines_cnt > 0) + return; + + postsetup_state = oh->_postsetup_state; + if (postsetup_state == _HWMOD_STATE_UNKNOWN) + postsetup_state = _HWMOD_STATE_ENABLED; + + /* + * XXX HWMOD_INIT_NO_IDLE does not belong in hwmod data - + * it should be set by the core code as a runtime flag during startup + */ + if ((oh->flags & (HWMOD_INIT_NO_IDLE | HWMOD_NO_IDLE)) && + (postsetup_state == _HWMOD_STATE_IDLE)) { + oh->_int_flags |= _HWMOD_SKIP_ENABLE; + postsetup_state = _HWMOD_STATE_ENABLED; + } + + if (postsetup_state == _HWMOD_STATE_IDLE) + _idle(oh); + else if (postsetup_state == _HWMOD_STATE_DISABLED) + _shutdown(oh); + else if (postsetup_state != _HWMOD_STATE_ENABLED) + WARN(1, "hwmod: %s: unknown postsetup state %d! defaulting to enabled\n", + oh->name, postsetup_state); + + return; +} + +/** + * _setup - prepare IP block hardware for use + * @oh: struct omap_hwmod * + * @n: (unused, pass NULL) + * + * Configure the IP block represented by @oh. This may include + * enabling the IP block, resetting it, and placing it into a + * post-setup state, depending on the type of IP block and applicable + * flags. IP blocks are reset to prevent any previous configuration + * by the bootloader or previous operating system from interfering + * with power management or other parts of the system. The reset can + * be avoided; see omap_hwmod_no_setup_reset(). This is the second of + * two phases for hwmod initialization. Code called here generally + * affects the IP block hardware, or system integration hardware + * associated with the IP block. Returns 0. + */ +static int _setup(struct omap_hwmod *oh, void *data) +{ + if (oh->_state != _HWMOD_STATE_INITIALIZED) + return 0; + + if (oh->parent_hwmod) { + int r; + + r = _enable(oh->parent_hwmod); + WARN(r, "hwmod: %s: setup: failed to enable parent hwmod %s\n", + oh->name, oh->parent_hwmod->name); + } + + _setup_iclk_autoidle(oh); + + if (!_setup_reset(oh)) + _setup_postsetup(oh); + + if (oh->parent_hwmod) { + u8 postsetup_state; + + postsetup_state = oh->parent_hwmod->_postsetup_state; + + if (postsetup_state == _HWMOD_STATE_IDLE) + _idle(oh->parent_hwmod); + else if (postsetup_state == _HWMOD_STATE_DISABLED) + _shutdown(oh->parent_hwmod); + else if (postsetup_state != _HWMOD_STATE_ENABLED) + WARN(1, "hwmod: %s: unknown postsetup state %d! defaulting to enabled\n", + oh->parent_hwmod->name, postsetup_state); + } + + return 0; +} + +/** + * _register - register a struct omap_hwmod + * @oh: struct omap_hwmod * + * + * Registers the omap_hwmod @oh. Returns -EEXIST if an omap_hwmod + * already has been registered by the same name; -EINVAL if the + * omap_hwmod is in the wrong state, if @oh is NULL, if the + * omap_hwmod's class field is NULL; if the omap_hwmod is missing a + * name, or if the omap_hwmod's class is missing a name; or 0 upon + * success. + * + * XXX The data should be copied into bootmem, so the original data + * should be marked __initdata and freed after init. This would allow + * unneeded omap_hwmods to be freed on multi-OMAP configurations. Note + * that the copy process would be relatively complex due to the large number + * of substructures. + */ +static int _register(struct omap_hwmod *oh) +{ + if (!oh || !oh->name || !oh->class || !oh->class->name || + (oh->_state != _HWMOD_STATE_UNKNOWN)) + return -EINVAL; + + pr_debug("omap_hwmod: %s: registering\n", oh->name); + + if (_lookup(oh->name)) + return -EEXIST; + + list_add_tail(&oh->node, &omap_hwmod_list); + + INIT_LIST_HEAD(&oh->slave_ports); + spin_lock_init(&oh->_lock); + lockdep_set_class(&oh->_lock, &oh->hwmod_key); + + oh->_state = _HWMOD_STATE_REGISTERED; + + /* + * XXX Rather than doing a strcmp(), this should test a flag + * set in the hwmod data, inserted by the autogenerator code. + */ + if (!strcmp(oh->name, MPU_INITIATOR_NAME)) + mpu_oh = oh; + + return 0; +} + +/** + * _add_link - add an interconnect between two IP blocks + * @oi: pointer to a struct omap_hwmod_ocp_if record + * + * Add struct omap_hwmod_link records connecting the slave IP block + * specified in @oi->slave to @oi. This code is assumed to run before + * preemption or SMP has been enabled, thus avoiding the need for + * locking in this code. Changes to this assumption will require + * additional locking. Returns 0. + */ +static int _add_link(struct omap_hwmod_ocp_if *oi) +{ + pr_debug("omap_hwmod: %s -> %s: adding link\n", oi->master->name, + oi->slave->name); + + list_add(&oi->node, &oi->slave->slave_ports); + oi->slave->slaves_cnt++; + + return 0; +} + +/** + * _register_link - register a struct omap_hwmod_ocp_if + * @oi: struct omap_hwmod_ocp_if * + * + * Registers the omap_hwmod_ocp_if record @oi. Returns -EEXIST if it + * has already been registered; -EINVAL if @oi is NULL or if the + * record pointed to by @oi is missing required fields; or 0 upon + * success. + * + * XXX The data should be copied into bootmem, so the original data + * should be marked __initdata and freed after init. This would allow + * unneeded omap_hwmods to be freed on multi-OMAP configurations. + */ +static int __init _register_link(struct omap_hwmod_ocp_if *oi) +{ + if (!oi || !oi->master || !oi->slave || !oi->user) + return -EINVAL; + + if (oi->_int_flags & _OCPIF_INT_FLAGS_REGISTERED) + return -EEXIST; + + pr_debug("omap_hwmod: registering link from %s to %s\n", + oi->master->name, oi->slave->name); + + /* + * Register the connected hwmods, if they haven't been + * registered already + */ + if (oi->master->_state != _HWMOD_STATE_REGISTERED) + _register(oi->master); + + if (oi->slave->_state != _HWMOD_STATE_REGISTERED) + _register(oi->slave); + + _add_link(oi); + + oi->_int_flags |= _OCPIF_INT_FLAGS_REGISTERED; + + return 0; +} + +/* Static functions intended only for use in soc_ops field function pointers */ + +/** + * _omap2xxx_3xxx_wait_target_ready - wait for a module to leave slave idle + * @oh: struct omap_hwmod * + * + * Wait for a module @oh to leave slave idle. Returns 0 if the module + * does not have an IDLEST bit or if the module successfully leaves + * slave idle; otherwise, pass along the return value of the + * appropriate *_cm*_wait_module_ready() function. + */ +static int _omap2xxx_3xxx_wait_target_ready(struct omap_hwmod *oh) +{ + if (!oh) + return -EINVAL; + + if (oh->flags & HWMOD_NO_IDLEST) + return 0; + + if (!_find_mpu_rt_port(oh)) + return 0; + + /* XXX check module SIDLEMODE, hardreset status, enabled clocks */ + + return omap_cm_wait_module_ready(0, oh->prcm.omap2.module_offs, + oh->prcm.omap2.idlest_reg_id, + oh->prcm.omap2.idlest_idle_bit); +} + +/** + * _omap4_wait_target_ready - wait for a module to leave slave idle + * @oh: struct omap_hwmod * + * + * Wait for a module @oh to leave slave idle. Returns 0 if the module + * does not have an IDLEST bit or if the module successfully leaves + * slave idle; otherwise, pass along the return value of the + * appropriate *_cm*_wait_module_ready() function. + */ +static int _omap4_wait_target_ready(struct omap_hwmod *oh) +{ + if (!oh) + return -EINVAL; + + if (oh->flags & HWMOD_NO_IDLEST || !oh->clkdm) + return 0; + + if (!_find_mpu_rt_port(oh)) + return 0; + + if (_omap4_clkctrl_managed_by_clkfwk(oh)) + return 0; + + if (!_omap4_has_clkctrl_clock(oh)) + return 0; + + /* XXX check module SIDLEMODE, hardreset status */ + + return omap_cm_wait_module_ready(oh->clkdm->prcm_partition, + oh->clkdm->cm_inst, + oh->prcm.omap4.clkctrl_offs, 0); +} + +/** + * _omap2_assert_hardreset - call OMAP2 PRM hardreset fn with hwmod args + * @oh: struct omap_hwmod * to assert hardreset + * @ohri: hardreset line data + * + * Call omap2_prm_assert_hardreset() with parameters extracted from + * the hwmod @oh and the hardreset line data @ohri. Only intended for + * use as an soc_ops function pointer. Passes along the return value + * from omap2_prm_assert_hardreset(). XXX This function is scheduled + * for removal when the PRM code is moved into drivers/. + */ +static int _omap2_assert_hardreset(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri) +{ + return omap_prm_assert_hardreset(ohri->rst_shift, 0, + oh->prcm.omap2.module_offs, 0); +} + +/** + * _omap2_deassert_hardreset - call OMAP2 PRM hardreset fn with hwmod args + * @oh: struct omap_hwmod * to deassert hardreset + * @ohri: hardreset line data + * + * Call omap2_prm_deassert_hardreset() with parameters extracted from + * the hwmod @oh and the hardreset line data @ohri. Only intended for + * use as an soc_ops function pointer. Passes along the return value + * from omap2_prm_deassert_hardreset(). XXX This function is + * scheduled for removal when the PRM code is moved into drivers/. + */ +static int _omap2_deassert_hardreset(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri) +{ + return omap_prm_deassert_hardreset(ohri->rst_shift, ohri->st_shift, 0, + oh->prcm.omap2.module_offs, 0, 0); +} + +/** + * _omap2_is_hardreset_asserted - call OMAP2 PRM hardreset fn with hwmod args + * @oh: struct omap_hwmod * to test hardreset + * @ohri: hardreset line data + * + * Call omap2_prm_is_hardreset_asserted() with parameters extracted + * from the hwmod @oh and the hardreset line data @ohri. Only + * intended for use as an soc_ops function pointer. Passes along the + * return value from omap2_prm_is_hardreset_asserted(). XXX This + * function is scheduled for removal when the PRM code is moved into + * drivers/. + */ +static int _omap2_is_hardreset_asserted(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri) +{ + return omap_prm_is_hardreset_asserted(ohri->st_shift, 0, + oh->prcm.omap2.module_offs, 0); +} + +/** + * _omap4_assert_hardreset - call OMAP4 PRM hardreset fn with hwmod args + * @oh: struct omap_hwmod * to assert hardreset + * @ohri: hardreset line data + * + * Call omap4_prminst_assert_hardreset() with parameters extracted + * from the hwmod @oh and the hardreset line data @ohri. Only + * intended for use as an soc_ops function pointer. Passes along the + * return value from omap4_prminst_assert_hardreset(). XXX This + * function is scheduled for removal when the PRM code is moved into + * drivers/. + */ +static int _omap4_assert_hardreset(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri) +{ + if (!oh->clkdm) + return -EINVAL; + + return omap_prm_assert_hardreset(ohri->rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); +} + +/** + * _omap4_deassert_hardreset - call OMAP4 PRM hardreset fn with hwmod args + * @oh: struct omap_hwmod * to deassert hardreset + * @ohri: hardreset line data + * + * Call omap4_prminst_deassert_hardreset() with parameters extracted + * from the hwmod @oh and the hardreset line data @ohri. Only + * intended for use as an soc_ops function pointer. Passes along the + * return value from omap4_prminst_deassert_hardreset(). XXX This + * function is scheduled for removal when the PRM code is moved into + * drivers/. + */ +static int _omap4_deassert_hardreset(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri) +{ + if (!oh->clkdm) + return -EINVAL; + + if (ohri->st_shift) + pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", + oh->name, ohri->name); + return omap_prm_deassert_hardreset(ohri->rst_shift, ohri->rst_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs, + oh->prcm.omap4.rstctrl_offs + + OMAP4_RST_CTRL_ST_OFFSET); +} + +/** + * _omap4_is_hardreset_asserted - call OMAP4 PRM hardreset fn with hwmod args + * @oh: struct omap_hwmod * to test hardreset + * @ohri: hardreset line data + * + * Call omap4_prminst_is_hardreset_asserted() with parameters + * extracted from the hwmod @oh and the hardreset line data @ohri. + * Only intended for use as an soc_ops function pointer. Passes along + * the return value from omap4_prminst_is_hardreset_asserted(). XXX + * This function is scheduled for removal when the PRM code is moved + * into drivers/. + */ +static int _omap4_is_hardreset_asserted(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri) +{ + if (!oh->clkdm) + return -EINVAL; + + return omap_prm_is_hardreset_asserted(ohri->rst_shift, + oh->clkdm->pwrdm.ptr-> + prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs); +} + +/** + * _omap4_disable_direct_prcm - disable direct PRCM control for hwmod + * @oh: struct omap_hwmod * to disable control for + * + * Disables direct PRCM clkctrl done by hwmod core. Instead, the hwmod + * will be using its main_clk to enable/disable the module. Returns + * 0 if successful. + */ +static int _omap4_disable_direct_prcm(struct omap_hwmod *oh) +{ + if (!oh) + return -EINVAL; + + oh->prcm.omap4.flags |= HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK; + + return 0; +} + +/** + * _am33xx_deassert_hardreset - call AM33XX PRM hardreset fn with hwmod args + * @oh: struct omap_hwmod * to deassert hardreset + * @ohri: hardreset line data + * + * Call am33xx_prminst_deassert_hardreset() with parameters extracted + * from the hwmod @oh and the hardreset line data @ohri. Only + * intended for use as an soc_ops function pointer. Passes along the + * return value from am33xx_prminst_deassert_hardreset(). XXX This + * function is scheduled for removal when the PRM code is moved into + * drivers/. + */ +static int _am33xx_deassert_hardreset(struct omap_hwmod *oh, + struct omap_hwmod_rst_info *ohri) +{ + return omap_prm_deassert_hardreset(ohri->rst_shift, ohri->st_shift, + oh->clkdm->pwrdm.ptr->prcm_partition, + oh->clkdm->pwrdm.ptr->prcm_offs, + oh->prcm.omap4.rstctrl_offs, + oh->prcm.omap4.rstst_offs); +} + +/* Public functions */ + +u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs) +{ + if (oh->flags & HWMOD_16BIT_REG) + return readw_relaxed(oh->_mpu_rt_va + reg_offs); + else + return readl_relaxed(oh->_mpu_rt_va + reg_offs); +} + +void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs) +{ + if (oh->flags & HWMOD_16BIT_REG) + writew_relaxed(v, oh->_mpu_rt_va + reg_offs); + else + writel_relaxed(v, oh->_mpu_rt_va + reg_offs); +} + +/** + * omap_hwmod_softreset - reset a module via SYSCONFIG.SOFTRESET bit + * @oh: struct omap_hwmod * + * + * This is a public function exposed to drivers. Some drivers may need to do + * some settings before and after resetting the device. Those drivers after + * doing the necessary settings could use this function to start a reset by + * setting the SYSCONFIG.SOFTRESET bit. + */ +int omap_hwmod_softreset(struct omap_hwmod *oh) +{ + u32 v; + int ret; + + if (!oh || !(oh->_sysc_cache)) + return -EINVAL; + + v = oh->_sysc_cache; + ret = _set_softreset(oh, &v); + if (ret) + goto error; + _write_sysconfig(v, oh); + + ret = _clear_softreset(oh, &v); + if (ret) + goto error; + _write_sysconfig(v, oh); + +error: + return ret; +} + +/** + * omap_hwmod_lookup - look up a registered omap_hwmod by name + * @name: name of the omap_hwmod to look up + * + * Given a @name of an omap_hwmod, return a pointer to the registered + * struct omap_hwmod *, or NULL upon error. + */ +struct omap_hwmod *omap_hwmod_lookup(const char *name) +{ + struct omap_hwmod *oh; + + if (!name) + return NULL; + + oh = _lookup(name); + + return oh; +} + +/** + * omap_hwmod_for_each - call function for each registered omap_hwmod + * @fn: pointer to a callback function + * @data: void * data to pass to callback function + * + * Call @fn for each registered omap_hwmod, passing @data to each + * function. @fn must return 0 for success or any other value for + * failure. If @fn returns non-zero, the iteration across omap_hwmods + * will stop and the non-zero return value will be passed to the + * caller of omap_hwmod_for_each(). @fn is called with + * omap_hwmod_for_each() held. + */ +int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data), + void *data) +{ + struct omap_hwmod *temp_oh; + int ret = 0; + + if (!fn) + return -EINVAL; + + list_for_each_entry(temp_oh, &omap_hwmod_list, node) { + ret = (*fn)(temp_oh, data); + if (ret) + break; + } + + return ret; +} + +/** + * omap_hwmod_register_links - register an array of hwmod links + * @ois: pointer to an array of omap_hwmod_ocp_if to register + * + * Intended to be called early in boot before the clock framework is + * initialized. If @ois is not null, will register all omap_hwmods + * listed in @ois that are valid for this chip. Returns -EINVAL if + * omap_hwmod_init() hasn't been called before calling this function, + * -ENOMEM if the link memory area can't be allocated, or 0 upon + * success. + */ +int __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois) +{ + int r, i; + + if (!inited) + return -EINVAL; + + if (!ois) + return 0; + + if (ois[0] == NULL) /* Empty list */ + return 0; + + i = 0; + do { + r = _register_link(ois[i]); + WARN(r && r != -EEXIST, + "omap_hwmod: _register_link(%s -> %s) returned %d\n", + ois[i]->master->name, ois[i]->slave->name, r); + } while (ois[++i]); + + return 0; +} + +/** + * _ensure_mpu_hwmod_is_setup - ensure the MPU SS hwmod is init'ed and set up + * @oh: pointer to the hwmod currently being set up (usually not the MPU) + * + * If the hwmod data corresponding to the MPU subsystem IP block + * hasn't been initialized and set up yet, do so now. This must be + * done first since sleep dependencies may be added from other hwmods + * to the MPU. Intended to be called only by omap_hwmod_setup*(). No + * return value. + */ +static void __init _ensure_mpu_hwmod_is_setup(struct omap_hwmod *oh) +{ + if (!mpu_oh || mpu_oh->_state == _HWMOD_STATE_UNKNOWN) + pr_err("omap_hwmod: %s: MPU initiator hwmod %s not yet registered\n", + __func__, MPU_INITIATOR_NAME); + else if (mpu_oh->_state == _HWMOD_STATE_REGISTERED && oh != mpu_oh) + omap_hwmod_setup_one(MPU_INITIATOR_NAME); +} + +/** + * omap_hwmod_setup_one - set up a single hwmod + * @oh_name: const char * name of the already-registered hwmod to set up + * + * Initialize and set up a single hwmod. Intended to be used for a + * small number of early devices, such as the timer IP blocks used for + * the scheduler clock. Must be called after omap2_clk_init(). + * Resolves the struct clk names to struct clk pointers for each + * registered omap_hwmod. Also calls _setup() on each hwmod. Returns + * -EINVAL upon error or 0 upon success. + */ +int __init omap_hwmod_setup_one(const char *oh_name) +{ + struct omap_hwmod *oh; + + pr_debug("omap_hwmod: %s: %s\n", oh_name, __func__); + + oh = _lookup(oh_name); + if (!oh) { + WARN(1, "omap_hwmod: %s: hwmod not yet registered\n", oh_name); + return -EINVAL; + } + + _ensure_mpu_hwmod_is_setup(oh); + + _init(oh, NULL); + _setup(oh, NULL); + + return 0; +} + +static void omap_hwmod_check_one(struct device *dev, + const char *name, s8 v1, u8 v2) +{ + if (v1 < 0) + return; + + if (v1 != v2) + dev_warn(dev, "%s %d != %d\n", name, v1, v2); +} + +/** + * omap_hwmod_check_sysc - check sysc against platform sysc + * @dev: struct device + * @data: module data + * @sysc_fields: new sysc configuration + */ +static int omap_hwmod_check_sysc(struct device *dev, + const struct ti_sysc_module_data *data, + struct sysc_regbits *sysc_fields) +{ + const struct sysc_regbits *regbits = data->cap->regbits; + + omap_hwmod_check_one(dev, "dmadisable_shift", + regbits->dmadisable_shift, + sysc_fields->dmadisable_shift); + omap_hwmod_check_one(dev, "midle_shift", + regbits->midle_shift, + sysc_fields->midle_shift); + omap_hwmod_check_one(dev, "sidle_shift", + regbits->sidle_shift, + sysc_fields->sidle_shift); + omap_hwmod_check_one(dev, "clkact_shift", + regbits->clkact_shift, + sysc_fields->clkact_shift); + omap_hwmod_check_one(dev, "enwkup_shift", + regbits->enwkup_shift, + sysc_fields->enwkup_shift); + omap_hwmod_check_one(dev, "srst_shift", + regbits->srst_shift, + sysc_fields->srst_shift); + omap_hwmod_check_one(dev, "autoidle_shift", + regbits->autoidle_shift, + sysc_fields->autoidle_shift); + + return 0; +} + +/** + * omap_hwmod_init_regbits - init sysconfig specific register bits + * @dev: struct device + * @oh: module + * @data: module data + * @sysc_fields: new sysc configuration + */ +static int omap_hwmod_init_regbits(struct device *dev, struct omap_hwmod *oh, + const struct ti_sysc_module_data *data, + struct sysc_regbits **sysc_fields) +{ + switch (data->cap->type) { + case TI_SYSC_OMAP2: + case TI_SYSC_OMAP2_TIMER: + *sysc_fields = &omap_hwmod_sysc_type1; + break; + case TI_SYSC_OMAP3_SHAM: + *sysc_fields = &omap3_sham_sysc_fields; + break; + case TI_SYSC_OMAP3_AES: + *sysc_fields = &omap3xxx_aes_sysc_fields; + break; + case TI_SYSC_OMAP4: + case TI_SYSC_OMAP4_TIMER: + *sysc_fields = &omap_hwmod_sysc_type2; + break; + case TI_SYSC_OMAP4_SIMPLE: + *sysc_fields = &omap_hwmod_sysc_type3; + break; + case TI_SYSC_OMAP34XX_SR: + *sysc_fields = &omap34xx_sr_sysc_fields; + break; + case TI_SYSC_OMAP36XX_SR: + *sysc_fields = &omap36xx_sr_sysc_fields; + break; + case TI_SYSC_OMAP4_SR: + *sysc_fields = &omap36xx_sr_sysc_fields; + break; + case TI_SYSC_OMAP4_MCASP: + *sysc_fields = &omap_hwmod_sysc_type_mcasp; + break; + case TI_SYSC_OMAP4_USB_HOST_FS: + *sysc_fields = &omap_hwmod_sysc_type_usb_host_fs; + break; + default: + *sysc_fields = NULL; + if (!oh->class->sysc->sysc_fields) + return 0; + + dev_err(dev, "sysc_fields not found\n"); + + return -EINVAL; + } + + return omap_hwmod_check_sysc(dev, data, *sysc_fields); +} + +/** + * omap_hwmod_init_reg_offs - initialize sysconfig register offsets + * @dev: struct device + * @data: module data + * @rev_offs: revision register offset + * @sysc_offs: sysc register offset + * @syss_offs: syss register offset + */ +static int omap_hwmod_init_reg_offs(struct device *dev, + const struct ti_sysc_module_data *data, + s32 *rev_offs, s32 *sysc_offs, + s32 *syss_offs) +{ + *rev_offs = -ENODEV; + *sysc_offs = 0; + *syss_offs = 0; + + if (data->offsets[SYSC_REVISION] >= 0) + *rev_offs = data->offsets[SYSC_REVISION]; + + if (data->offsets[SYSC_SYSCONFIG] >= 0) + *sysc_offs = data->offsets[SYSC_SYSCONFIG]; + + if (data->offsets[SYSC_SYSSTATUS] >= 0) + *syss_offs = data->offsets[SYSC_SYSSTATUS]; + + return 0; +} + +/** + * omap_hwmod_init_sysc_flags - initialize sysconfig features + * @dev: struct device + * @data: module data + * @sysc_flags: module configuration + */ +static int omap_hwmod_init_sysc_flags(struct device *dev, + const struct ti_sysc_module_data *data, + u32 *sysc_flags) +{ + *sysc_flags = 0; + + switch (data->cap->type) { + case TI_SYSC_OMAP2: + case TI_SYSC_OMAP2_TIMER: + /* See SYSC_OMAP2_* in include/dt-bindings/bus/ti-sysc.h */ + if (data->cfg->sysc_val & SYSC_OMAP2_CLOCKACTIVITY) + *sysc_flags |= SYSC_HAS_CLOCKACTIVITY; + if (data->cfg->sysc_val & SYSC_OMAP2_EMUFREE) + *sysc_flags |= SYSC_HAS_EMUFREE; + if (data->cfg->sysc_val & SYSC_OMAP2_ENAWAKEUP) + *sysc_flags |= SYSC_HAS_ENAWAKEUP; + if (data->cfg->sysc_val & SYSC_OMAP2_SOFTRESET) + *sysc_flags |= SYSC_HAS_SOFTRESET; + if (data->cfg->sysc_val & SYSC_OMAP2_AUTOIDLE) + *sysc_flags |= SYSC_HAS_AUTOIDLE; + break; + case TI_SYSC_OMAP4: + case TI_SYSC_OMAP4_TIMER: + /* See SYSC_OMAP4_* in include/dt-bindings/bus/ti-sysc.h */ + if (data->cfg->sysc_val & SYSC_OMAP4_DMADISABLE) + *sysc_flags |= SYSC_HAS_DMADISABLE; + if (data->cfg->sysc_val & SYSC_OMAP4_FREEEMU) + *sysc_flags |= SYSC_HAS_EMUFREE; + if (data->cfg->sysc_val & SYSC_OMAP4_SOFTRESET) + *sysc_flags |= SYSC_HAS_SOFTRESET; + break; + case TI_SYSC_OMAP34XX_SR: + case TI_SYSC_OMAP36XX_SR: + /* See SYSC_OMAP3_SR_* in include/dt-bindings/bus/ti-sysc.h */ + if (data->cfg->sysc_val & SYSC_OMAP3_SR_ENAWAKEUP) + *sysc_flags |= SYSC_HAS_ENAWAKEUP; + break; + default: + if (data->cap->regbits->emufree_shift >= 0) + *sysc_flags |= SYSC_HAS_EMUFREE; + if (data->cap->regbits->enwkup_shift >= 0) + *sysc_flags |= SYSC_HAS_ENAWAKEUP; + if (data->cap->regbits->srst_shift >= 0) + *sysc_flags |= SYSC_HAS_SOFTRESET; + if (data->cap->regbits->autoidle_shift >= 0) + *sysc_flags |= SYSC_HAS_AUTOIDLE; + break; + } + + if (data->cap->regbits->midle_shift >= 0 && + data->cfg->midlemodes) + *sysc_flags |= SYSC_HAS_MIDLEMODE; + + if (data->cap->regbits->sidle_shift >= 0 && + data->cfg->sidlemodes) + *sysc_flags |= SYSC_HAS_SIDLEMODE; + + if (data->cfg->quirks & SYSC_QUIRK_UNCACHED) + *sysc_flags |= SYSC_NO_CACHE; + if (data->cfg->quirks & SYSC_QUIRK_RESET_STATUS) + *sysc_flags |= SYSC_HAS_RESET_STATUS; + + if (data->cfg->syss_mask & 1) + *sysc_flags |= SYSS_HAS_RESET_STATUS; + + return 0; +} + +/** + * omap_hwmod_init_idlemodes - initialize module idle modes + * @dev: struct device + * @data: module data + * @idlemodes: module supported idle modes + */ +static int omap_hwmod_init_idlemodes(struct device *dev, + const struct ti_sysc_module_data *data, + u32 *idlemodes) +{ + *idlemodes = 0; + + if (data->cfg->midlemodes & BIT(SYSC_IDLE_FORCE)) + *idlemodes |= MSTANDBY_FORCE; + if (data->cfg->midlemodes & BIT(SYSC_IDLE_NO)) + *idlemodes |= MSTANDBY_NO; + if (data->cfg->midlemodes & BIT(SYSC_IDLE_SMART)) + *idlemodes |= MSTANDBY_SMART; + if (data->cfg->midlemodes & BIT(SYSC_IDLE_SMART_WKUP)) + *idlemodes |= MSTANDBY_SMART_WKUP; + + if (data->cfg->sidlemodes & BIT(SYSC_IDLE_FORCE)) + *idlemodes |= SIDLE_FORCE; + if (data->cfg->sidlemodes & BIT(SYSC_IDLE_NO)) + *idlemodes |= SIDLE_NO; + if (data->cfg->sidlemodes & BIT(SYSC_IDLE_SMART)) + *idlemodes |= SIDLE_SMART; + if (data->cfg->sidlemodes & BIT(SYSC_IDLE_SMART_WKUP)) + *idlemodes |= SIDLE_SMART_WKUP; + + return 0; +} + +/** + * omap_hwmod_check_module - check new module against platform data + * @dev: struct device + * @oh: module + * @data: new module data + * @sysc_fields: sysc register bits + * @rev_offs: revision register offset + * @sysc_offs: sysconfig register offset + * @syss_offs: sysstatus register offset + * @sysc_flags: sysc specific flags + * @idlemodes: sysc supported idlemodes + */ +static int omap_hwmod_check_module(struct device *dev, + struct omap_hwmod *oh, + const struct ti_sysc_module_data *data, + struct sysc_regbits *sysc_fields, + s32 rev_offs, s32 sysc_offs, + s32 syss_offs, u32 sysc_flags, + u32 idlemodes) +{ + if (!oh->class->sysc) + return -ENODEV; + + if (oh->class->sysc->sysc_fields && + sysc_fields != oh->class->sysc->sysc_fields) + dev_warn(dev, "sysc_fields mismatch\n"); + + if (rev_offs != oh->class->sysc->rev_offs) + dev_warn(dev, "rev_offs %08x != %08x\n", rev_offs, + oh->class->sysc->rev_offs); + if (sysc_offs != oh->class->sysc->sysc_offs) + dev_warn(dev, "sysc_offs %08x != %08x\n", sysc_offs, + oh->class->sysc->sysc_offs); + if (syss_offs != oh->class->sysc->syss_offs) + dev_warn(dev, "syss_offs %08x != %08x\n", syss_offs, + oh->class->sysc->syss_offs); + + if (sysc_flags != oh->class->sysc->sysc_flags) + dev_warn(dev, "sysc_flags %08x != %08x\n", sysc_flags, + oh->class->sysc->sysc_flags); + + if (idlemodes != oh->class->sysc->idlemodes) + dev_warn(dev, "idlemodes %08x != %08x\n", idlemodes, + oh->class->sysc->idlemodes); + + if (data->cfg->srst_udelay != oh->class->sysc->srst_udelay) + dev_warn(dev, "srst_udelay %i != %i\n", + data->cfg->srst_udelay, + oh->class->sysc->srst_udelay); + + return 0; +} + +/** + * omap_hwmod_allocate_module - allocate new module + * @dev: struct device + * @oh: module + * @sysc_fields: sysc register bits + * @clockdomain: clockdomain + * @rev_offs: revision register offset + * @sysc_offs: sysconfig register offset + * @syss_offs: sysstatus register offset + * @sysc_flags: sysc specific flags + * @idlemodes: sysc supported idlemodes + * + * Note that the allocations here cannot use devm as ti-sysc can rebind. + */ +static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, + const struct ti_sysc_module_data *data, + struct sysc_regbits *sysc_fields, + struct clockdomain *clkdm, + s32 rev_offs, s32 sysc_offs, + s32 syss_offs, u32 sysc_flags, + u32 idlemodes) +{ + struct omap_hwmod_class_sysconfig *sysc; + struct omap_hwmod_class *class = NULL; + struct omap_hwmod_ocp_if *oi = NULL; + void __iomem *regs = NULL; + unsigned long flags; + + sysc = kzalloc(sizeof(*sysc), GFP_KERNEL); + if (!sysc) + return -ENOMEM; + + sysc->sysc_fields = sysc_fields; + sysc->rev_offs = rev_offs; + sysc->sysc_offs = sysc_offs; + sysc->syss_offs = syss_offs; + sysc->sysc_flags = sysc_flags; + sysc->idlemodes = idlemodes; + sysc->srst_udelay = data->cfg->srst_udelay; + + if (!oh->_mpu_rt_va) { + regs = ioremap(data->module_pa, + data->module_size); + if (!regs) + goto out_free_sysc; + } + + /* + * We may need a new oh->class as the other devices in the same class + * may not yet have ioremapped their registers. + */ + if (oh->class->name && strcmp(oh->class->name, data->name)) { + class = kmemdup(oh->class, sizeof(*oh->class), GFP_KERNEL); + if (!class) + goto out_unmap; + } + + if (list_empty(&oh->slave_ports)) { + oi = kcalloc(1, sizeof(*oi), GFP_KERNEL); + if (!oi) + goto out_free_class; + + /* + * Note that we assume interconnect interface clocks will be + * managed by the interconnect driver for OCPIF_SWSUP_IDLE case + * on omap24xx and omap3. + */ + oi->slave = oh; + oi->user = OCP_USER_MPU | OCP_USER_SDMA; + } + + spin_lock_irqsave(&oh->_lock, flags); + if (regs) + oh->_mpu_rt_va = regs; + if (class) + oh->class = class; + oh->class->sysc = sysc; + if (oi) + _add_link(oi); + if (clkdm) + oh->clkdm = clkdm; + oh->_state = _HWMOD_STATE_INITIALIZED; + oh->_postsetup_state = _HWMOD_STATE_DEFAULT; + _setup(oh, NULL); + spin_unlock_irqrestore(&oh->_lock, flags); + + return 0; + +out_free_class: + kfree(class); +out_unmap: + iounmap(regs); +out_free_sysc: + kfree(sysc); + return -ENOMEM; +} + +static const struct omap_hwmod_reset omap24xx_reset_quirks[] = { + { .match = "msdi", .len = 4, .reset = omap_msdi_reset, }, +}; + +static const struct omap_hwmod_reset omap_reset_quirks[] = { + { .match = "dss_core", .len = 8, .reset = omap_dss_reset, }, + { .match = "hdq1w", .len = 5, .reset = omap_hdq1w_reset, }, + { .match = "i2c", .len = 3, .reset = omap_i2c_reset, }, + { .match = "wd_timer", .len = 8, .reset = omap2_wd_timer_reset, }, +}; + +static void +omap_hwmod_init_reset_quirk(struct device *dev, struct omap_hwmod *oh, + const struct ti_sysc_module_data *data, + const struct omap_hwmod_reset *quirks, + int quirks_sz) +{ + const struct omap_hwmod_reset *quirk; + int i; + + for (i = 0; i < quirks_sz; i++) { + quirk = &quirks[i]; + if (!strncmp(data->name, quirk->match, quirk->len)) { + oh->class->reset = quirk->reset; + + return; + } + } +} + +static void +omap_hwmod_init_reset_quirks(struct device *dev, struct omap_hwmod *oh, + const struct ti_sysc_module_data *data) +{ + if (soc_is_omap24xx()) + omap_hwmod_init_reset_quirk(dev, oh, data, + omap24xx_reset_quirks, + ARRAY_SIZE(omap24xx_reset_quirks)); + + omap_hwmod_init_reset_quirk(dev, oh, data, omap_reset_quirks, + ARRAY_SIZE(omap_reset_quirks)); +} + +/** + * omap_hwmod_init_module - initialize new module + * @dev: struct device + * @data: module data + * @cookie: cookie for the caller to use for later calls + */ +int omap_hwmod_init_module(struct device *dev, + const struct ti_sysc_module_data *data, + struct ti_sysc_cookie *cookie) +{ + struct omap_hwmod *oh; + struct sysc_regbits *sysc_fields; + s32 rev_offs, sysc_offs, syss_offs; + u32 sysc_flags, idlemodes; + int error; + + if (!dev || !data || !data->name || !cookie) + return -EINVAL; + + oh = _lookup(data->name); + if (!oh) { + oh = kzalloc(sizeof(*oh), GFP_KERNEL); + if (!oh) + return -ENOMEM; + + oh->name = data->name; + oh->_state = _HWMOD_STATE_UNKNOWN; + lockdep_register_key(&oh->hwmod_key); + + /* Unused, can be handled by PRM driver handling resets */ + oh->prcm.omap4.flags = HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT; + + oh->class = kzalloc(sizeof(*oh->class), GFP_KERNEL); + if (!oh->class) { + kfree(oh); + return -ENOMEM; + } + + omap_hwmod_init_reset_quirks(dev, oh, data); + + oh->class->name = data->name; + mutex_lock(&list_lock); + error = _register(oh); + mutex_unlock(&list_lock); + } + + cookie->data = oh; + + error = omap_hwmod_init_regbits(dev, oh, data, &sysc_fields); + if (error) + return error; + + error = omap_hwmod_init_reg_offs(dev, data, &rev_offs, + &sysc_offs, &syss_offs); + if (error) + return error; + + error = omap_hwmod_init_sysc_flags(dev, data, &sysc_flags); + if (error) + return error; + + error = omap_hwmod_init_idlemodes(dev, data, &idlemodes); + if (error) + return error; + + if (data->cfg->quirks & SYSC_QUIRK_NO_IDLE) + oh->flags |= HWMOD_NO_IDLE; + if (data->cfg->quirks & SYSC_QUIRK_NO_IDLE_ON_INIT) + oh->flags |= HWMOD_INIT_NO_IDLE; + if (data->cfg->quirks & SYSC_QUIRK_NO_RESET_ON_INIT) + oh->flags |= HWMOD_INIT_NO_RESET; + if (data->cfg->quirks & SYSC_QUIRK_USE_CLOCKACT) + oh->flags |= HWMOD_SET_DEFAULT_CLOCKACT; + if (data->cfg->quirks & SYSC_QUIRK_SWSUP_SIDLE) + oh->flags |= HWMOD_SWSUP_SIDLE; + if (data->cfg->quirks & SYSC_QUIRK_SWSUP_SIDLE_ACT) + oh->flags |= HWMOD_SWSUP_SIDLE_ACT; + if (data->cfg->quirks & SYSC_QUIRK_SWSUP_MSTANDBY) + oh->flags |= HWMOD_SWSUP_MSTANDBY; + if (data->cfg->quirks & SYSC_QUIRK_CLKDM_NOAUTO) + oh->flags |= HWMOD_CLKDM_NOAUTO; + + error = omap_hwmod_check_module(dev, oh, data, sysc_fields, + rev_offs, sysc_offs, syss_offs, + sysc_flags, idlemodes); + if (!error) + return error; + + return omap_hwmod_allocate_module(dev, oh, data, sysc_fields, + cookie->clkdm, rev_offs, + sysc_offs, syss_offs, + sysc_flags, idlemodes); +} + +/** + * omap_hwmod_setup_earlycon_flags - set up flags for early console + * + * Enable DEBUG_OMAPUART_FLAGS for uart hwmod that is being used as + * early concole so that hwmod core doesn't reset and keep it in idle + * that specific uart. + */ +#ifdef CONFIG_SERIAL_EARLYCON +static void __init omap_hwmod_setup_earlycon_flags(void) +{ + struct device_node *np; + struct omap_hwmod *oh; + const char *uart; + + np = of_find_node_by_path("/chosen"); + if (np) { + uart = of_get_property(np, "stdout-path", NULL); + if (uart) { + np = of_find_node_by_path(uart); + if (np) { + uart = of_get_property(np, "ti,hwmods", NULL); + oh = omap_hwmod_lookup(uart); + if (!oh) { + uart = of_get_property(np->parent, + "ti,hwmods", + NULL); + oh = omap_hwmod_lookup(uart); + } + if (oh) + oh->flags |= DEBUG_OMAPUART_FLAGS; + } + } + } +} +#endif + +/** + * omap_hwmod_setup_all - set up all registered IP blocks + * + * Initialize and set up all IP blocks registered with the hwmod code. + * Must be called after omap2_clk_init(). Resolves the struct clk + * names to struct clk pointers for each registered omap_hwmod. Also + * calls _setup() on each hwmod. Returns 0 upon success. + */ +static int __init omap_hwmod_setup_all(void) +{ + if (!inited) + return 0; + + _ensure_mpu_hwmod_is_setup(NULL); + + omap_hwmod_for_each(_init, NULL); +#ifdef CONFIG_SERIAL_EARLYCON + omap_hwmod_setup_earlycon_flags(); +#endif + omap_hwmod_for_each(_setup, NULL); + + return 0; +} +omap_postcore_initcall(omap_hwmod_setup_all); + +/** + * omap_hwmod_enable - enable an omap_hwmod + * @oh: struct omap_hwmod * + * + * Enable an omap_hwmod @oh. Intended to be called by omap_device_enable(). + * Returns -EINVAL on error or passes along the return value from _enable(). + */ +int omap_hwmod_enable(struct omap_hwmod *oh) +{ + int r; + unsigned long flags; + + if (!oh) + return -EINVAL; + + spin_lock_irqsave(&oh->_lock, flags); + r = _enable(oh); + spin_unlock_irqrestore(&oh->_lock, flags); + + return r; +} + +/** + * omap_hwmod_idle - idle an omap_hwmod + * @oh: struct omap_hwmod * + * + * Idle an omap_hwmod @oh. Intended to be called by omap_device_idle(). + * Returns -EINVAL on error or passes along the return value from _idle(). + */ +int omap_hwmod_idle(struct omap_hwmod *oh) +{ + int r; + unsigned long flags; + + if (!oh) + return -EINVAL; + + spin_lock_irqsave(&oh->_lock, flags); + r = _idle(oh); + spin_unlock_irqrestore(&oh->_lock, flags); + + return r; +} + +/** + * omap_hwmod_shutdown - shutdown an omap_hwmod + * @oh: struct omap_hwmod * + * + * Shutdown an omap_hwmod @oh. Intended to be called by + * omap_device_shutdown(). Returns -EINVAL on error or passes along + * the return value from _shutdown(). + */ +int omap_hwmod_shutdown(struct omap_hwmod *oh) +{ + int r; + unsigned long flags; + + if (!oh) + return -EINVAL; + + spin_lock_irqsave(&oh->_lock, flags); + r = _shutdown(oh); + spin_unlock_irqrestore(&oh->_lock, flags); + + return r; +} + +/* + * IP block data retrieval functions + */ + +/** + * omap_hwmod_get_pwrdm - return pointer to this module's main powerdomain + * @oh: struct omap_hwmod * + * + * Return the powerdomain pointer associated with the OMAP module + * @oh's main clock. If @oh does not have a main clk, return the + * powerdomain associated with the interface clock associated with the + * module's MPU port. (XXX Perhaps this should use the SDMA port + * instead?) Returns NULL on error, or a struct powerdomain * on + * success. + */ +struct powerdomain *omap_hwmod_get_pwrdm(struct omap_hwmod *oh) +{ + struct clk *c; + struct omap_hwmod_ocp_if *oi; + struct clockdomain *clkdm; + struct clk_hw_omap *clk; + struct clk_hw *hw; + + if (!oh) + return NULL; + + if (oh->clkdm) + return oh->clkdm->pwrdm.ptr; + + if (oh->_clk) { + c = oh->_clk; + } else { + oi = _find_mpu_rt_port(oh); + if (!oi) + return NULL; + c = oi->_clk; + } + + hw = __clk_get_hw(c); + if (!hw) + return NULL; + + clk = to_clk_hw_omap(hw); + if (!clk) + return NULL; + + clkdm = clk->clkdm; + if (!clkdm) + return NULL; + + return clkdm->pwrdm.ptr; +} + +/** + * omap_hwmod_get_mpu_rt_va - return the module's base address (for the MPU) + * @oh: struct omap_hwmod * + * + * Returns the virtual address corresponding to the beginning of the + * module's register target, in the address range that is intended to + * be used by the MPU. Returns the virtual address upon success or NULL + * upon error. + */ +void __iomem *omap_hwmod_get_mpu_rt_va(struct omap_hwmod *oh) +{ + if (!oh) + return NULL; + + if (oh->_int_flags & _HWMOD_NO_MPU_PORT) + return NULL; + + if (oh->_state == _HWMOD_STATE_UNKNOWN) + return NULL; + + return oh->_mpu_rt_va; +} + +/* + * XXX what about functions for drivers to save/restore ocp_sysconfig + * for context save/restore operations? + */ + +/** + * omap_hwmod_assert_hardreset - assert the HW reset line of submodules + * contained in the hwmod module. + * @oh: struct omap_hwmod * + * @name: name of the reset line to lookup and assert + * + * Some IP like dsp, ipu or iva contain processor that require + * an HW reset line to be assert / deassert in order to enable fully + * the IP. Returns -EINVAL if @oh is null or if the operation is not + * yet supported on this OMAP; otherwise, passes along the return value + * from _assert_hardreset(). + */ +int omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name) +{ + int ret; + unsigned long flags; + + if (!oh) + return -EINVAL; + + spin_lock_irqsave(&oh->_lock, flags); + ret = _assert_hardreset(oh, name); + spin_unlock_irqrestore(&oh->_lock, flags); + + return ret; +} + +/** + * omap_hwmod_deassert_hardreset - deassert the HW reset line of submodules + * contained in the hwmod module. + * @oh: struct omap_hwmod * + * @name: name of the reset line to look up and deassert + * + * Some IP like dsp, ipu or iva contain processor that require + * an HW reset line to be assert / deassert in order to enable fully + * the IP. Returns -EINVAL if @oh is null or if the operation is not + * yet supported on this OMAP; otherwise, passes along the return value + * from _deassert_hardreset(). + */ +int omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name) +{ + int ret; + unsigned long flags; + + if (!oh) + return -EINVAL; + + spin_lock_irqsave(&oh->_lock, flags); + ret = _deassert_hardreset(oh, name); + spin_unlock_irqrestore(&oh->_lock, flags); + + return ret; +} + +/** + * omap_hwmod_for_each_by_class - call @fn for each hwmod of class @classname + * @classname: struct omap_hwmod_class name to search for + * @fn: callback function pointer to call for each hwmod in class @classname + * @user: arbitrary context data to pass to the callback function + * + * For each omap_hwmod of class @classname, call @fn. + * If the callback function returns something other than + * zero, the iterator is terminated, and the callback function's return + * value is passed back to the caller. Returns 0 upon success, -EINVAL + * if @classname or @fn are NULL, or passes back the error code from @fn. + */ +int omap_hwmod_for_each_by_class(const char *classname, + int (*fn)(struct omap_hwmod *oh, + void *user), + void *user) +{ + struct omap_hwmod *temp_oh; + int ret = 0; + + if (!classname || !fn) + return -EINVAL; + + pr_debug("omap_hwmod: %s: looking for modules of class %s\n", + __func__, classname); + + list_for_each_entry(temp_oh, &omap_hwmod_list, node) { + if (!strcmp(temp_oh->class->name, classname)) { + pr_debug("omap_hwmod: %s: %s: calling callback fn\n", + __func__, temp_oh->name); + ret = (*fn)(temp_oh, user); + if (ret) + break; + } + } + + if (ret) + pr_debug("omap_hwmod: %s: iterator terminated early: %d\n", + __func__, ret); + + return ret; +} + +/** + * omap_hwmod_set_postsetup_state - set the post-_setup() state for this hwmod + * @oh: struct omap_hwmod * + * @state: state that _setup() should leave the hwmod in + * + * Sets the hwmod state that @oh will enter at the end of _setup() + * (called by omap_hwmod_setup_*()). See also the documentation + * for _setup_postsetup(), above. Returns 0 upon success or + * -EINVAL if there is a problem with the arguments or if the hwmod is + * in the wrong state. + */ +int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state) +{ + int ret; + unsigned long flags; + + if (!oh) + return -EINVAL; + + if (state != _HWMOD_STATE_DISABLED && + state != _HWMOD_STATE_ENABLED && + state != _HWMOD_STATE_IDLE) + return -EINVAL; + + spin_lock_irqsave(&oh->_lock, flags); + + if (oh->_state != _HWMOD_STATE_REGISTERED) { + ret = -EINVAL; + goto ohsps_unlock; + } + + oh->_postsetup_state = state; + ret = 0; + +ohsps_unlock: + spin_unlock_irqrestore(&oh->_lock, flags); + + return ret; +} + +/** + * omap_hwmod_get_context_loss_count - get lost context count + * @oh: struct omap_hwmod * + * + * Returns the context loss count of associated @oh + * upon success, or zero if no context loss data is available. + * + * On OMAP4, this queries the per-hwmod context loss register, + * assuming one exists. If not, or on OMAP2/3, this queries the + * enclosing powerdomain context loss count. + */ +int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh) +{ + struct powerdomain *pwrdm; + int ret = 0; + + if (soc_ops.get_context_lost) + return soc_ops.get_context_lost(oh); + + pwrdm = omap_hwmod_get_pwrdm(oh); + if (pwrdm) + ret = pwrdm_get_context_loss_count(pwrdm); + + return ret; +} + +/** + * omap_hwmod_init - initialize the hwmod code + * + * Sets up some function pointers needed by the hwmod code to operate on the + * currently-booted SoC. Intended to be called once during kernel init + * before any hwmods are registered. No return value. + */ +void __init omap_hwmod_init(void) +{ + if (cpu_is_omap24xx()) { + soc_ops.wait_target_ready = _omap2xxx_3xxx_wait_target_ready; + soc_ops.assert_hardreset = _omap2_assert_hardreset; + soc_ops.deassert_hardreset = _omap2_deassert_hardreset; + soc_ops.is_hardreset_asserted = _omap2_is_hardreset_asserted; + } else if (cpu_is_omap34xx()) { + soc_ops.wait_target_ready = _omap2xxx_3xxx_wait_target_ready; + soc_ops.assert_hardreset = _omap2_assert_hardreset; + soc_ops.deassert_hardreset = _omap2_deassert_hardreset; + soc_ops.is_hardreset_asserted = _omap2_is_hardreset_asserted; + soc_ops.init_clkdm = _init_clkdm; + } else if (cpu_is_omap44xx() || soc_is_omap54xx() || soc_is_dra7xx()) { + soc_ops.enable_module = _omap4_enable_module; + soc_ops.disable_module = _omap4_disable_module; + soc_ops.wait_target_ready = _omap4_wait_target_ready; + soc_ops.assert_hardreset = _omap4_assert_hardreset; + soc_ops.deassert_hardreset = _omap4_deassert_hardreset; + soc_ops.is_hardreset_asserted = _omap4_is_hardreset_asserted; + soc_ops.init_clkdm = _init_clkdm; + soc_ops.update_context_lost = _omap4_update_context_lost; + soc_ops.get_context_lost = _omap4_get_context_lost; + soc_ops.disable_direct_prcm = _omap4_disable_direct_prcm; + soc_ops.xlate_clkctrl = _omap4_xlate_clkctrl; + } else if (cpu_is_ti814x() || cpu_is_ti816x() || soc_is_am33xx() || + soc_is_am43xx()) { + soc_ops.enable_module = _omap4_enable_module; + soc_ops.disable_module = _omap4_disable_module; + soc_ops.wait_target_ready = _omap4_wait_target_ready; + soc_ops.assert_hardreset = _omap4_assert_hardreset; + soc_ops.deassert_hardreset = _am33xx_deassert_hardreset; + soc_ops.is_hardreset_asserted = _omap4_is_hardreset_asserted; + soc_ops.init_clkdm = _init_clkdm; + soc_ops.disable_direct_prcm = _omap4_disable_direct_prcm; + soc_ops.xlate_clkctrl = _omap4_xlate_clkctrl; + } else { + WARN(1, "omap_hwmod: unknown SoC type\n"); + } + + _init_clkctrl_providers(); + + inited = true; +} + +/** + * omap_hwmod_get_main_clk - get pointer to main clock name + * @oh: struct omap_hwmod * + * + * Returns the main clock name assocated with @oh upon success, + * or NULL if @oh is NULL. + */ +const char *omap_hwmod_get_main_clk(struct omap_hwmod *oh) +{ + if (!oh) + return NULL; + + return oh->main_clk; +} diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h new file mode 100644 index 000000000..6962a8d26 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod.h @@ -0,0 +1,695 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_hwmod macros, structures + * + * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2011-2012 Texas Instruments, Inc. + * Paul Walmsley + * + * Created in collaboration with (alphabetical order): Benoît Cousson, + * Kevin Hilman, Tony Lindgren, Rajendra Nayak, Vikram Pandita, Sakari + * Poussa, Anand Sawant, Santosh Shilimkar, Richard Woodruff + * + * These headers and macros are used to define OMAP on-chip module + * data and their integration with other OMAP modules and Linux. + * Copious documentation and references can also be found in the + * omap_hwmod code, in arch/arm/mach-omap2/omap_hwmod.c (as of this + * writing). + * + * To do: + * - add interconnect error log structures + * - init_conn_id_bit (CONNID_BIT_VECTOR) + * - implement default hwmod SMS/SDRC flags? + * - move Linux-specific data ("non-ROM data") out + */ +#ifndef __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_HWMOD_H +#define __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_HWMOD_H + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/ioport.h> +#include <linux/spinlock.h> + +struct omap_device; + +extern struct sysc_regbits omap_hwmod_sysc_type1; +extern struct sysc_regbits omap_hwmod_sysc_type2; +extern struct sysc_regbits omap_hwmod_sysc_type3; +extern struct sysc_regbits omap34xx_sr_sysc_fields; +extern struct sysc_regbits omap36xx_sr_sysc_fields; +extern struct sysc_regbits omap3_sham_sysc_fields; +extern struct sysc_regbits omap3xxx_aes_sysc_fields; +extern struct sysc_regbits omap_hwmod_sysc_type_mcasp; +extern struct sysc_regbits omap_hwmod_sysc_type_usb_host_fs; + +/* + * OCP SYSCONFIG bit shifts/masks TYPE1. These are for IPs compliant + * with the original PRCM protocol defined for OMAP2420 + */ +#define SYSC_TYPE1_MIDLEMODE_SHIFT 12 +#define SYSC_TYPE1_MIDLEMODE_MASK (0x3 << SYSC_TYPE1_MIDLEMODE_SHIFT) +#define SYSC_TYPE1_CLOCKACTIVITY_SHIFT 8 +#define SYSC_TYPE1_CLOCKACTIVITY_MASK (0x3 << SYSC_TYPE1_CLOCKACTIVITY_SHIFT) +#define SYSC_TYPE1_SIDLEMODE_SHIFT 3 +#define SYSC_TYPE1_SIDLEMODE_MASK (0x3 << SYSC_TYPE1_SIDLEMODE_SHIFT) +#define SYSC_TYPE1_ENAWAKEUP_SHIFT 2 +#define SYSC_TYPE1_ENAWAKEUP_MASK (1 << SYSC_TYPE1_ENAWAKEUP_SHIFT) +#define SYSC_TYPE1_SOFTRESET_SHIFT 1 +#define SYSC_TYPE1_SOFTRESET_MASK (1 << SYSC_TYPE1_SOFTRESET_SHIFT) +#define SYSC_TYPE1_AUTOIDLE_SHIFT 0 +#define SYSC_TYPE1_AUTOIDLE_MASK (1 << SYSC_TYPE1_AUTOIDLE_SHIFT) + +/* + * OCP SYSCONFIG bit shifts/masks TYPE2. These are for IPs compliant + * with the new PRCM protocol defined for new OMAP4 IPs. + */ +#define SYSC_TYPE2_SOFTRESET_SHIFT 0 +#define SYSC_TYPE2_SOFTRESET_MASK (1 << SYSC_TYPE2_SOFTRESET_SHIFT) +#define SYSC_TYPE2_SIDLEMODE_SHIFT 2 +#define SYSC_TYPE2_SIDLEMODE_MASK (0x3 << SYSC_TYPE2_SIDLEMODE_SHIFT) +#define SYSC_TYPE2_MIDLEMODE_SHIFT 4 +#define SYSC_TYPE2_MIDLEMODE_MASK (0x3 << SYSC_TYPE2_MIDLEMODE_SHIFT) +#define SYSC_TYPE2_DMADISABLE_SHIFT 16 +#define SYSC_TYPE2_DMADISABLE_MASK (0x1 << SYSC_TYPE2_DMADISABLE_SHIFT) + +/* + * OCP SYSCONFIG bit shifts/masks TYPE3. + * This is applicable for some IPs present in AM33XX + */ +#define SYSC_TYPE3_SIDLEMODE_SHIFT 0 +#define SYSC_TYPE3_SIDLEMODE_MASK (0x3 << SYSC_TYPE3_SIDLEMODE_SHIFT) +#define SYSC_TYPE3_MIDLEMODE_SHIFT 2 +#define SYSC_TYPE3_MIDLEMODE_MASK (0x3 << SYSC_TYPE3_MIDLEMODE_SHIFT) + +/* OCP SYSSTATUS bit shifts/masks */ +#define SYSS_RESETDONE_SHIFT 0 +#define SYSS_RESETDONE_MASK (1 << SYSS_RESETDONE_SHIFT) + +/* Master standby/slave idle mode flags */ +#define HWMOD_IDLEMODE_FORCE (1 << 0) +#define HWMOD_IDLEMODE_NO (1 << 1) +#define HWMOD_IDLEMODE_SMART (1 << 2) +#define HWMOD_IDLEMODE_SMART_WKUP (1 << 3) + +/* modulemode control type (SW or HW) */ +#define MODULEMODE_HWCTRL 1 +#define MODULEMODE_SWCTRL 2 + +#define DEBUG_OMAP2UART1_FLAGS 0 +#define DEBUG_OMAP2UART2_FLAGS 0 +#define DEBUG_OMAP2UART3_FLAGS 0 +#define DEBUG_OMAP3UART3_FLAGS 0 +#define DEBUG_OMAP3UART4_FLAGS 0 +#define DEBUG_OMAP4UART3_FLAGS 0 +#define DEBUG_OMAP4UART4_FLAGS 0 +#define DEBUG_TI81XXUART1_FLAGS 0 +#define DEBUG_TI81XXUART2_FLAGS 0 +#define DEBUG_TI81XXUART3_FLAGS 0 +#define DEBUG_AM33XXUART1_FLAGS 0 + +#define DEBUG_OMAPUART_FLAGS (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET) + +#ifdef CONFIG_OMAP_GPMC_DEBUG +#define DEBUG_OMAP_GPMC_HWMOD_FLAGS HWMOD_INIT_NO_RESET +#else +#define DEBUG_OMAP_GPMC_HWMOD_FLAGS 0 +#endif + +#if defined(CONFIG_DEBUG_OMAP2UART1) +#undef DEBUG_OMAP2UART1_FLAGS +#define DEBUG_OMAP2UART1_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_OMAP2UART2) +#undef DEBUG_OMAP2UART2_FLAGS +#define DEBUG_OMAP2UART2_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_OMAP2UART3) +#undef DEBUG_OMAP2UART3_FLAGS +#define DEBUG_OMAP2UART3_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_OMAP3UART3) +#undef DEBUG_OMAP3UART3_FLAGS +#define DEBUG_OMAP3UART3_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_OMAP3UART4) +#undef DEBUG_OMAP3UART4_FLAGS +#define DEBUG_OMAP3UART4_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_OMAP4UART3) +#undef DEBUG_OMAP4UART3_FLAGS +#define DEBUG_OMAP4UART3_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_OMAP4UART4) +#undef DEBUG_OMAP4UART4_FLAGS +#define DEBUG_OMAP4UART4_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_TI81XXUART1) +#undef DEBUG_TI81XXUART1_FLAGS +#define DEBUG_TI81XXUART1_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_TI81XXUART2) +#undef DEBUG_TI81XXUART2_FLAGS +#define DEBUG_TI81XXUART2_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_TI81XXUART3) +#undef DEBUG_TI81XXUART3_FLAGS +#define DEBUG_TI81XXUART3_FLAGS DEBUG_OMAPUART_FLAGS +#elif defined(CONFIG_DEBUG_AM33XXUART1) +#undef DEBUG_AM33XXUART1_FLAGS +#define DEBUG_AM33XXUART1_FLAGS DEBUG_OMAPUART_FLAGS +#endif + +/** + * struct omap_hwmod_rst_info - IPs reset lines use by hwmod + * @name: name of the reset line (module local name) + * @rst_shift: Offset of the reset bit + * @st_shift: Offset of the reset status bit (OMAP2/3 only) + * + * @name should be something short, e.g., "cpu0" or "rst". It is defined + * locally to the hwmod. + */ +struct omap_hwmod_rst_info { + const char *name; + u8 rst_shift; + u8 st_shift; +}; + +/** + * struct omap_hwmod_opt_clk - optional clocks used by this hwmod + * @role: "sys", "32k", "tv", etc -- for use in clk_get() + * @clk: opt clock: OMAP clock name + * @_clk: pointer to the struct clk (filled in at runtime) + * + * The module's interface clock and main functional clock should not + * be added as optional clocks. + */ +struct omap_hwmod_opt_clk { + const char *role; + const char *clk; + struct clk *_clk; +}; + + +/* omap_hwmod_omap2_firewall.flags bits */ +#define OMAP_FIREWALL_L3 (1 << 0) +#define OMAP_FIREWALL_L4 (1 << 1) + +/** + * struct omap_hwmod_omap2_firewall - OMAP2/3 device firewall data + * @l3_perm_bit: bit shift for L3_PM_*_PERMISSION_* + * @l4_fw_region: L4 firewall region ID + * @l4_prot_group: L4 protection group ID + * @flags: (see omap_hwmod_omap2_firewall.flags macros above) + */ +struct omap_hwmod_omap2_firewall { + u8 l3_perm_bit; + u8 l4_fw_region; + u8 l4_prot_group; + u8 flags; +}; + +/* + * omap_hwmod_ocp_if.user bits: these indicate the initiators that use this + * interface to interact with the hwmod. Used to add sleep dependencies + * when the module is enabled or disabled. + */ +#define OCP_USER_MPU (1 << 0) +#define OCP_USER_SDMA (1 << 1) +#define OCP_USER_DSP (1 << 2) +#define OCP_USER_IVA (1 << 3) + +/* omap_hwmod_ocp_if.flags bits */ +#define OCPIF_SWSUP_IDLE (1 << 0) +#define OCPIF_CAN_BURST (1 << 1) + +/* omap_hwmod_ocp_if._int_flags possibilities */ +#define _OCPIF_INT_FLAGS_REGISTERED (1 << 0) + + +/** + * struct omap_hwmod_ocp_if - OCP interface data + * @master: struct omap_hwmod that initiates OCP transactions on this link + * @slave: struct omap_hwmod that responds to OCP transactions on this link + * @addr: address space associated with this link + * @clk: interface clock: OMAP clock name + * @_clk: pointer to the interface struct clk (filled in at runtime) + * @fw: interface firewall data + * @width: OCP data width + * @user: initiators using this interface (see OCP_USER_* macros above) + * @flags: OCP interface flags (see OCPIF_* macros above) + * @_int_flags: internal flags (see _OCPIF_INT_FLAGS* macros above) + * + * It may also be useful to add a tag_cnt field for OCP2.x devices. + * + * Parameter names beginning with an underscore are managed internally by + * the omap_hwmod code and should not be set during initialization. + */ +struct omap_hwmod_ocp_if { + struct omap_hwmod *master; + struct omap_hwmod *slave; + struct omap_hwmod_addr_space *addr; + const char *clk; + struct clk *_clk; + struct list_head node; + union { + struct omap_hwmod_omap2_firewall omap2; + } fw; + u8 width; + u8 user; + u8 flags; + u8 _int_flags; +}; + + +/* Macros for use in struct omap_hwmod_sysconfig */ + +/* Flags for use in omap_hwmod_sysconfig.idlemodes */ +#define MASTER_STANDBY_SHIFT 4 +#define SLAVE_IDLE_SHIFT 0 +#define SIDLE_FORCE (HWMOD_IDLEMODE_FORCE << SLAVE_IDLE_SHIFT) +#define SIDLE_NO (HWMOD_IDLEMODE_NO << SLAVE_IDLE_SHIFT) +#define SIDLE_SMART (HWMOD_IDLEMODE_SMART << SLAVE_IDLE_SHIFT) +#define SIDLE_SMART_WKUP (HWMOD_IDLEMODE_SMART_WKUP << SLAVE_IDLE_SHIFT) +#define MSTANDBY_FORCE (HWMOD_IDLEMODE_FORCE << MASTER_STANDBY_SHIFT) +#define MSTANDBY_NO (HWMOD_IDLEMODE_NO << MASTER_STANDBY_SHIFT) +#define MSTANDBY_SMART (HWMOD_IDLEMODE_SMART << MASTER_STANDBY_SHIFT) +#define MSTANDBY_SMART_WKUP (HWMOD_IDLEMODE_SMART_WKUP << MASTER_STANDBY_SHIFT) + +/* omap_hwmod_sysconfig.sysc_flags capability flags */ +#define SYSC_HAS_AUTOIDLE (1 << 0) +#define SYSC_HAS_SOFTRESET (1 << 1) +#define SYSC_HAS_ENAWAKEUP (1 << 2) +#define SYSC_HAS_EMUFREE (1 << 3) +#define SYSC_HAS_CLOCKACTIVITY (1 << 4) +#define SYSC_HAS_SIDLEMODE (1 << 5) +#define SYSC_HAS_MIDLEMODE (1 << 6) +#define SYSS_HAS_RESET_STATUS (1 << 7) +#define SYSC_NO_CACHE (1 << 8) /* XXX SW flag, belongs elsewhere */ +#define SYSC_HAS_RESET_STATUS (1 << 9) +#define SYSC_HAS_DMADISABLE (1 << 10) + +/* omap_hwmod_sysconfig.clockact flags */ +#define CLOCKACT_TEST_BOTH 0x0 +#define CLOCKACT_TEST_MAIN 0x1 +#define CLOCKACT_TEST_ICLK 0x2 +#define CLOCKACT_TEST_NONE 0x3 + +/** + * struct omap_hwmod_class_sysconfig - hwmod class OCP_SYS* data + * @rev_offs: IP block revision register offset (from module base addr) + * @sysc_offs: OCP_SYSCONFIG register offset (from module base addr) + * @syss_offs: OCP_SYSSTATUS register offset (from module base addr) + * @srst_udelay: Delay needed after doing a softreset in usecs + * @idlemodes: One or more of {SIDLE,MSTANDBY}_{OFF,FORCE,SMART} + * @sysc_flags: SYS{C,S}_HAS* flags indicating SYSCONFIG bits supported + * @clockact: the default value of the module CLOCKACTIVITY bits + * + * @clockact describes to the module which clocks are likely to be + * disabled when the PRCM issues its idle request to the module. Some + * modules have separate clockdomains for the interface clock and main + * functional clock, and can check whether they should acknowledge the + * idle request based on the internal module functionality that has + * been associated with the clocks marked in @clockact. This field is + * only used if HWMOD_SET_DEFAULT_CLOCKACT is set (see below) + * + * @sysc_fields: structure containing the offset positions of various bits in + * SYSCONFIG register. This can be populated using omap_hwmod_sysc_type1 or + * omap_hwmod_sysc_type2 defined in omap_hwmod_common_data.c depending on + * whether the device ip is compliant with the original PRCM protocol + * defined for OMAP2420 or the new PRCM protocol for new OMAP4 IPs. + * If the device follows a different scheme for the sysconfig register , + * then this field has to be populated with the correct offset structure. + */ +struct omap_hwmod_class_sysconfig { + s32 rev_offs; + s32 sysc_offs; + s32 syss_offs; + u16 sysc_flags; + struct sysc_regbits *sysc_fields; + u8 srst_udelay; + u8 idlemodes; +}; + +/** + * struct omap_hwmod_omap2_prcm - OMAP2/3-specific PRCM data + * @module_offs: PRCM submodule offset from the start of the PRM/CM + * @idlest_reg_id: IDLEST register ID (e.g., 3 for CM_IDLEST3) + * @idlest_idle_bit: register bit shift for CM_IDLEST slave idle bit + * + * @prcm_reg_id and @module_bit are specific to the AUTOIDLE, WKST, + * WKEN, GRPSEL registers. In an ideal world, no extra information + * would be needed for IDLEST information, but alas, there are some + * exceptions, so @idlest_reg_id, @idlest_idle_bit, @idlest_stdby_bit + * are needed for the IDLEST registers (c.f. 2430 I2CHS, 3430 USBHOST) + */ +struct omap_hwmod_omap2_prcm { + s16 module_offs; + u8 idlest_reg_id; + u8 idlest_idle_bit; +}; + +/* + * Possible values for struct omap_hwmod_omap4_prcm.flags + * + * HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT: Some IP blocks don't have a PRCM + * module-level context loss register associated with them; this + * flag bit should be set in those cases + * HWMOD_OMAP4_ZERO_CLKCTRL_OFFSET: Some IP blocks have a valid CLKCTRL + * offset of zero; this flag bit should be set in those cases to + * distinguish from hwmods that have no clkctrl offset. + * HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK: Module clockctrl clock is managed + * by the common clock framework and not hwmod. + */ +#define HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT (1 << 0) +#define HWMOD_OMAP4_ZERO_CLKCTRL_OFFSET (1 << 1) +#define HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK (1 << 2) + +/** + * struct omap_hwmod_omap4_prcm - OMAP4-specific PRCM data + * @clkctrl_offs: offset of the PRCM clock control register + * @rstctrl_offs: offset of the XXX_RSTCTRL register located in the PRM + * @context_offs: offset of the RM_*_CONTEXT register + * @lostcontext_mask: bitmask for selecting bits from RM_*_CONTEXT register + * @rstst_reg: (AM33XX only) address of the XXX_RSTST register in the PRM + * @submodule_wkdep_bit: bit shift of the WKDEP range + * @flags: PRCM register capabilities for this IP block + * @modulemode: allowable modulemodes + * @context_lost_counter: Count of module level context lost + * + * If @lostcontext_mask is not defined, context loss check code uses + * whole register without masking. @lostcontext_mask should only be + * defined in cases where @context_offs register is shared by two or + * more hwmods. + */ +struct omap_hwmod_omap4_prcm { + u16 clkctrl_offs; + u16 rstctrl_offs; + u16 rstst_offs; + u16 context_offs; + u32 lostcontext_mask; + u8 submodule_wkdep_bit; + u8 modulemode; + u8 flags; + int context_lost_counter; +}; + + +/* + * omap_hwmod.flags definitions + * + * HWMOD_SWSUP_SIDLE: omap_hwmod code should manually bring module in and out + * of idle, rather than relying on module smart-idle + * HWMOD_SWSUP_MSTANDBY: omap_hwmod code should manually bring module in and + * out of standby, rather than relying on module smart-standby + * HWMOD_INIT_NO_RESET: don't reset this module at boot - important for + * SDRAM controller, etc. XXX probably belongs outside the main hwmod file + * XXX Should be HWMOD_SETUP_NO_RESET + * HWMOD_INIT_NO_IDLE: don't idle this module at boot - important for SDRAM + * controller, etc. XXX probably belongs outside the main hwmod file + * XXX Should be HWMOD_SETUP_NO_IDLE + * HWMOD_NO_OCP_AUTOIDLE: disable module autoidle (OCP_SYSCONFIG.AUTOIDLE) + * when module is enabled, rather than the default, which is to + * enable autoidle + * HWMOD_SET_DEFAULT_CLOCKACT: program CLOCKACTIVITY bits at startup + * HWMOD_NO_IDLEST: this module does not have idle status - this is the case + * only for few initiator modules on OMAP2 & 3. + * HWMOD_CONTROL_OPT_CLKS_IN_RESET: Enable all optional clocks during reset. + * This is needed for devices like DSS that require optional clocks enabled + * in order to complete the reset. Optional clocks will be disabled + * again after the reset. + * HWMOD_16BIT_REG: Module has 16bit registers + * HWMOD_EXT_OPT_MAIN_CLK: The only main functional clock source for + * this IP block comes from an off-chip source and is not always + * enabled. This prevents the hwmod code from being able to + * enable and reset the IP block early. XXX Eventually it should + * be possible to query the clock framework for this information. + * HWMOD_BLOCK_WFI: Some OMAP peripherals apparently don't work + * correctly if the MPU is allowed to go idle while the + * peripherals are active. This is apparently true for the I2C on + * OMAP2420, and also the EMAC on AM3517/3505. It's unlikely that + * this is really true -- we're probably not configuring something + * correctly, or this is being abused to deal with some PM latency + * issues -- but we're currently suffering from a shortage of + * folks who are able to track these issues down properly. + * HWMOD_FORCE_MSTANDBY: Always keep MIDLEMODE bits cleared so that device + * is kept in force-standby mode. Failing to do so causes PM problems + * with musb on OMAP3630 at least. Note that musb has a dedicated register + * to control MSTANDBY signal when MIDLEMODE is set to force-standby. + * HWMOD_SWSUP_SIDLE_ACT: omap_hwmod code should manually bring the module + * out of idle, but rely on smart-idle to the put it back in idle, + * so the wakeups are still functional (Only known case for now is UART) + * HWMOD_RECONFIG_IO_CHAIN: omap_hwmod code needs to reconfigure wake-up + * events by calling _reconfigure_io_chain() when a device is enabled + * or idled. + * HWMOD_OPT_CLKS_NEEDED: The optional clocks are needed for the module to + * operate and they need to be handled at the same time as the main_clk. + * HWMOD_NO_IDLE: Do not idle the hwmod at all. Useful to handle certain + * IPs like CPSW on DRA7, where clocks to this module cannot be disabled. + * HWMOD_CLKDM_NOAUTO: Allows the hwmod's clockdomain to be prevented from + * entering HW_AUTO while hwmod is active. This is needed to workaround + * some modules which don't function correctly with HW_AUTO. For example, + * DCAN on DRA7x SoC needs this to workaround errata i893. + */ +#define HWMOD_SWSUP_SIDLE (1 << 0) +#define HWMOD_SWSUP_MSTANDBY (1 << 1) +#define HWMOD_INIT_NO_RESET (1 << 2) +#define HWMOD_INIT_NO_IDLE (1 << 3) +#define HWMOD_NO_OCP_AUTOIDLE (1 << 4) +#define HWMOD_SET_DEFAULT_CLOCKACT (1 << 5) +#define HWMOD_NO_IDLEST (1 << 6) +#define HWMOD_CONTROL_OPT_CLKS_IN_RESET (1 << 7) +#define HWMOD_16BIT_REG (1 << 8) +#define HWMOD_EXT_OPT_MAIN_CLK (1 << 9) +#define HWMOD_BLOCK_WFI (1 << 10) +#define HWMOD_FORCE_MSTANDBY (1 << 11) +#define HWMOD_SWSUP_SIDLE_ACT (1 << 12) +#define HWMOD_RECONFIG_IO_CHAIN (1 << 13) +#define HWMOD_OPT_CLKS_NEEDED (1 << 14) +#define HWMOD_NO_IDLE (1 << 15) +#define HWMOD_CLKDM_NOAUTO (1 << 16) + +/* + * omap_hwmod._int_flags definitions + * These are for internal use only and are managed by the omap_hwmod code. + * + * _HWMOD_NO_MPU_PORT: no path exists for the MPU to write to this module + * _HWMOD_SYSCONFIG_LOADED: set when the OCP_SYSCONFIG value has been cached + * _HWMOD_SKIP_ENABLE: set if hwmod enabled during init (HWMOD_INIT_NO_IDLE) - + * causes the first call to _enable() to only update the pinmux + */ +#define _HWMOD_NO_MPU_PORT (1 << 0) +#define _HWMOD_SYSCONFIG_LOADED (1 << 1) +#define _HWMOD_SKIP_ENABLE (1 << 2) + +/* + * omap_hwmod._state definitions + * + * INITIALIZED: reset (optionally), initialized, enabled, disabled + * (optionally) + * + * + */ +#define _HWMOD_STATE_UNKNOWN 0 +#define _HWMOD_STATE_REGISTERED 1 +#define _HWMOD_STATE_CLKS_INITED 2 +#define _HWMOD_STATE_INITIALIZED 3 +#define _HWMOD_STATE_ENABLED 4 +#define _HWMOD_STATE_IDLE 5 +#define _HWMOD_STATE_DISABLED 6 + +#ifdef CONFIG_PM +#define _HWMOD_STATE_DEFAULT _HWMOD_STATE_IDLE +#else +#define _HWMOD_STATE_DEFAULT _HWMOD_STATE_ENABLED +#endif + +/** + * struct omap_hwmod_class - the type of an IP block + * @name: name of the hwmod_class + * @sysc: device SYSCONFIG/SYSSTATUS register data + * @pre_shutdown: ptr to fn to be executed immediately prior to device shutdown + * @reset: ptr to fn to be executed in place of the standard hwmod reset fn + * @lock: ptr to fn to be executed to lock IP registers + * @unlock: ptr to fn to be executed to unlock IP registers + * + * Represent the class of a OMAP hardware "modules" (e.g. timer, + * smartreflex, gpio, uart...) + * + * @pre_shutdown is a function that will be run immediately before + * hwmod clocks are disabled, etc. It is intended for use for hwmods + * like the MPU watchdog, which cannot be disabled with the standard + * omap_hwmod_shutdown(). The function should return 0 upon success, + * or some negative error upon failure. Returning an error will cause + * omap_hwmod_shutdown() to abort the device shutdown and return an + * error. + * + * If @reset is defined, then the function it points to will be + * executed in place of the standard hwmod _reset() code in + * mach-omap2/omap_hwmod.c. This is needed for IP blocks which have + * unusual reset sequences - usually processor IP blocks like the IVA. + */ +struct omap_hwmod_class { + const char *name; + struct omap_hwmod_class_sysconfig *sysc; + int (*pre_shutdown)(struct omap_hwmod *oh); + int (*reset)(struct omap_hwmod *oh); + void (*lock)(struct omap_hwmod *oh); + void (*unlock)(struct omap_hwmod *oh); +}; + +/** + * struct omap_hwmod - integration data for OMAP hardware "modules" (IP blocks) + * @name: name of the hwmod + * @class: struct omap_hwmod_class * to the class of this hwmod + * @od: struct omap_device currently associated with this hwmod (internal use) + * @prcm: PRCM data pertaining to this hwmod + * @main_clk: main clock: OMAP clock name + * @_clk: pointer to the main struct clk (filled in at runtime) + * @opt_clks: other device clocks that drivers can request (0..*) + * @voltdm: pointer to voltage domain (filled in at runtime) + * @dev_attr: arbitrary device attributes that can be passed to the driver + * @_sysc_cache: internal-use hwmod flags + * @mpu_rt_idx: index of device address space for register target (for DT boot) + * @_mpu_rt_va: cached register target start address (internal use) + * @_mpu_port: cached MPU register target slave (internal use) + * @opt_clks_cnt: number of @opt_clks + * @master_cnt: number of @master entries + * @slaves_cnt: number of @slave entries + * @response_lat: device OCP response latency (in interface clock cycles) + * @_int_flags: internal-use hwmod flags + * @_state: internal-use hwmod state + * @_postsetup_state: internal-use state to leave the hwmod in after _setup() + * @flags: hwmod flags (documented below) + * @_lock: spinlock serializing operations on this hwmod + * @node: list node for hwmod list (internal use) + * @parent_hwmod: (temporary) a pointer to the hierarchical parent of this hwmod + * + * @main_clk refers to this module's "main clock," which for our + * purposes is defined as "the functional clock needed for register + * accesses to complete." Modules may not have a main clock if the + * interface clock also serves as a main clock. + * + * Parameter names beginning with an underscore are managed internally by + * the omap_hwmod code and should not be set during initialization. + * + * @masters and @slaves are now deprecated. + * + * @parent_hwmod is temporary; there should be no need for it, as this + * information should already be expressed in the OCP interface + * structures. @parent_hwmod is present as a workaround until we improve + * handling for hwmods with multiple parents (e.g., OMAP4+ DSS with + * multiple register targets across different interconnects). + */ +struct omap_hwmod { + const char *name; + struct omap_hwmod_class *class; + struct omap_device *od; + struct omap_hwmod_rst_info *rst_lines; + union { + struct omap_hwmod_omap2_prcm omap2; + struct omap_hwmod_omap4_prcm omap4; + } prcm; + const char *main_clk; + struct clk *_clk; + struct omap_hwmod_opt_clk *opt_clks; + const char *clkdm_name; + struct clockdomain *clkdm; + struct list_head slave_ports; /* connect to *_TA */ + void *dev_attr; + u32 _sysc_cache; + void __iomem *_mpu_rt_va; + spinlock_t _lock; + struct lock_class_key hwmod_key; /* unique lock class */ + struct list_head node; + struct omap_hwmod_ocp_if *_mpu_port; + u32 flags; + u8 mpu_rt_idx; + u8 response_lat; + u8 rst_lines_cnt; + u8 opt_clks_cnt; + u8 slaves_cnt; + u8 hwmods_cnt; + u8 _int_flags; + u8 _state; + u8 _postsetup_state; + struct omap_hwmod *parent_hwmod; +}; + +#ifdef CONFIG_OMAP_HWMOD + +struct device_node; + +struct omap_hwmod *omap_hwmod_lookup(const char *name); +int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data), + void *data); + +int __init omap_hwmod_setup_one(const char *name); +int omap_hwmod_parse_module_range(struct omap_hwmod *oh, + struct device_node *np, + struct resource *res); + +struct ti_sysc_module_data; +struct ti_sysc_cookie; + +int omap_hwmod_init_module(struct device *dev, + const struct ti_sysc_module_data *data, + struct ti_sysc_cookie *cookie); + +int omap_hwmod_enable(struct omap_hwmod *oh); +int omap_hwmod_idle(struct omap_hwmod *oh); +int omap_hwmod_shutdown(struct omap_hwmod *oh); + +int omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name); +int omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name); + +void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs); +u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs); +int omap_hwmod_softreset(struct omap_hwmod *oh); + +int omap_hwmod_count_resources(struct omap_hwmod *oh, unsigned long flags); +int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res); +int omap_hwmod_get_resource_byname(struct omap_hwmod *oh, unsigned int type, + const char *name, struct resource *res); + +struct powerdomain *omap_hwmod_get_pwrdm(struct omap_hwmod *oh); +void __iomem *omap_hwmod_get_mpu_rt_va(struct omap_hwmod *oh); + +int omap_hwmod_for_each_by_class(const char *classname, + int (*fn)(struct omap_hwmod *oh, + void *user), + void *user); + +int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state); +int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh); + +extern void __init omap_hwmod_init(void); + +const char *omap_hwmod_get_main_clk(struct omap_hwmod *oh); + +#else /* CONFIG_OMAP_HWMOD */ + +static inline int +omap_hwmod_for_each_by_class(const char *classname, + int (*fn)(struct omap_hwmod *oh, void *user), + void *user) +{ + return 0; +} +#endif /* CONFIG_OMAP_HWMOD */ + +/* + * + */ + +void omap_hwmod_rtc_unlock(struct omap_hwmod *oh); +void omap_hwmod_rtc_lock(struct omap_hwmod *oh); + +/* + * Chip variant-specific hwmod init routines - XXX should be converted + * to use initcalls once the initial boot ordering is straightened out + */ +extern int omap2420_hwmod_init(void); +extern int omap2430_hwmod_init(void); +extern int omap3xxx_hwmod_init(void); +extern int omap44xx_hwmod_init(void); +extern int am33xx_hwmod_init(void); +extern int dm814x_hwmod_init(void); +extern int dm816x_hwmod_init(void); +extern int dra7xx_hwmod_init(void); +int am43xx_hwmod_init(void); + +extern int __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois); + +#endif diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c new file mode 100644 index 000000000..558fae437 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod_2420_data.c - hardware modules present on the OMAP2420 chips + * + * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * XXX handle crossbar/shared link difference for L3? + * XXX these should be marked initdata for multi-OMAP kernels + */ + +#include <linux/platform_data/i2c-omap.h> + +#include "omap_hwmod.h" +#include "l3_2xxx.h" +#include "l4_2xxx.h" + +#include "omap_hwmod_common_data.h" + +#include "cm-regbits-24xx.h" +#include "prm-regbits-24xx.h" +#include "i2c.h" +#include "mmc.h" +#include "serial.h" +#include "wd_timer.h" + +/* + * OMAP2420 hardware module integration data + * + * All of the data in this section should be autogeneratable from the + * TI hardware database or other technical documentation. Data that + * is driver-specific or driver-kernel integration-specific belongs + * elsewhere. + */ + +/* + * IP blocks + */ + +/* IVA1 (IVA1) */ +static struct omap_hwmod_class iva1_hwmod_class = { + .name = "iva1", +}; + +static struct omap_hwmod_rst_info omap2420_iva_resets[] = { + { .name = "iva", .rst_shift = 8 }, +}; + +static struct omap_hwmod omap2420_iva_hwmod = { + .name = "iva", + .class = &iva1_hwmod_class, + .clkdm_name = "iva1_clkdm", + .rst_lines = omap2420_iva_resets, + .rst_lines_cnt = ARRAY_SIZE(omap2420_iva_resets), + .main_clk = "iva1_ifck", +}; + +/* DSP */ +static struct omap_hwmod_class dsp_hwmod_class = { + .name = "dsp", +}; + +static struct omap_hwmod_rst_info omap2420_dsp_resets[] = { + { .name = "logic", .rst_shift = 0 }, + { .name = "mmu", .rst_shift = 1 }, +}; + +static struct omap_hwmod omap2420_dsp_hwmod = { + .name = "dsp", + .class = &dsp_hwmod_class, + .clkdm_name = "dsp_clkdm", + .rst_lines = omap2420_dsp_resets, + .rst_lines_cnt = ARRAY_SIZE(omap2420_dsp_resets), + .main_clk = "dsp_fck", +}; + +/* I2C common */ +static struct omap_hwmod_class_sysconfig i2c_sysc = { + .rev_offs = 0x00, + .sysc_offs = 0x20, + .syss_offs = 0x10, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class i2c_class = { + .name = "i2c", + .sysc = &i2c_sysc, + .reset = &omap_i2c_reset, +}; + +/* I2C1 */ +static struct omap_hwmod omap2420_i2c1_hwmod = { + .name = "i2c1", + .main_clk = "i2c1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP2420_ST_I2C1_SHIFT, + }, + }, + .class = &i2c_class, + /* + * From mach-omap2/pm24xx.c: "Putting MPU into the WFI state + * while a transfer is active seems to cause the I2C block to + * timeout. Why? Good question." + */ + .flags = (HWMOD_16BIT_REG | HWMOD_BLOCK_WFI), +}; + +/* I2C2 */ +static struct omap_hwmod omap2420_i2c2_hwmod = { + .name = "i2c2", + .main_clk = "i2c2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP2420_ST_I2C2_SHIFT, + }, + }, + .class = &i2c_class, + .flags = HWMOD_16BIT_REG, +}; + +/* mailbox */ +static struct omap_hwmod omap2420_mailbox_hwmod = { + .name = "mailbox", + .class = &omap2xxx_mailbox_hwmod_class, + .main_clk = "mailboxes_ick", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT, + }, + }, +}; + +/* + * 'mcbsp' class + * multi channel buffered serial port controller + */ + +static struct omap_hwmod_class omap2420_mcbsp_hwmod_class = { + .name = "mcbsp", +}; + +static struct omap_hwmod_opt_clk mcbsp_opt_clks[] = { + { .role = "pad_fck", .clk = "mcbsp_clks" }, + { .role = "prcm_fck", .clk = "func_96m_ck" }, +}; + +/* mcbsp1 */ +static struct omap_hwmod omap2420_mcbsp1_hwmod = { + .name = "mcbsp1", + .class = &omap2420_mcbsp_hwmod_class, + .main_clk = "mcbsp1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MCBSP1_SHIFT, + }, + }, + .opt_clks = mcbsp_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp_opt_clks), +}; + +/* mcbsp2 */ +static struct omap_hwmod omap2420_mcbsp2_hwmod = { + .name = "mcbsp2", + .class = &omap2420_mcbsp_hwmod_class, + .main_clk = "mcbsp2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MCBSP2_SHIFT, + }, + }, + .opt_clks = mcbsp_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp_opt_clks), +}; + +static struct omap_hwmod_class_sysconfig omap2420_msdi_sysc = { + .rev_offs = 0x3c, + .sysc_offs = 0x64, + .syss_offs = 0x68, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap2420_msdi_hwmod_class = { + .name = "msdi", + .sysc = &omap2420_msdi_sysc, + .reset = &omap_msdi_reset, +}; + +/* msdi1 */ +static struct omap_hwmod omap2420_msdi1_hwmod = { + .name = "msdi1", + .class = &omap2420_msdi_hwmod_class, + .main_clk = "mmc_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP2420_ST_MMC_SHIFT, + }, + }, + .flags = HWMOD_16BIT_REG, +}; + +/* HDQ1W/1-wire */ +static struct omap_hwmod omap2420_hdq1w_hwmod = { + .name = "hdq1w", + .main_clk = "hdq_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_HDQ_SHIFT, + }, + }, + .class = &omap2_hdq1w_class, +}; + +/* + * interfaces + */ + +/* L4 CORE -> I2C1 interface */ +static struct omap_hwmod_ocp_if omap2420_l4_core__i2c1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2420_i2c1_hwmod, + .clk = "i2c1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> I2C2 interface */ +static struct omap_hwmod_ocp_if omap2420_l4_core__i2c2 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2420_i2c2_hwmod, + .clk = "i2c2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* IVA <- L3 interface */ +static struct omap_hwmod_ocp_if omap2420_l3__iva = { + .master = &omap2xxx_l3_main_hwmod, + .slave = &omap2420_iva_hwmod, + .clk = "core_l3_ck", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* DSP <- L3 interface */ +static struct omap_hwmod_ocp_if omap2420_l3__dsp = { + .master = &omap2xxx_l3_main_hwmod, + .slave = &omap2420_dsp_hwmod, + .clk = "dsp_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> wd_timer2 */ +static struct omap_hwmod_ocp_if omap2420_l4_wkup__wd_timer2 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_wd_timer2_hwmod, + .clk = "mpu_wdt_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio1 */ +static struct omap_hwmod_ocp_if omap2420_l4_wkup__gpio1 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio1_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio2 */ +static struct omap_hwmod_ocp_if omap2420_l4_wkup__gpio2 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio2_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio3 */ +static struct omap_hwmod_ocp_if omap2420_l4_wkup__gpio3 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio3_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio4 */ +static struct omap_hwmod_ocp_if omap2420_l4_wkup__gpio4 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio4_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mailbox */ +static struct omap_hwmod_ocp_if omap2420_l4_core__mailbox = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2420_mailbox_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp1 */ +static struct omap_hwmod_ocp_if omap2420_l4_core__mcbsp1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2420_mcbsp1_hwmod, + .clk = "mcbsp1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp2 */ +static struct omap_hwmod_ocp_if omap2420_l4_core__mcbsp2 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2420_mcbsp2_hwmod, + .clk = "mcbsp2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> msdi1 */ +static struct omap_hwmod_ocp_if omap2420_l4_core__msdi1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2420_msdi1_hwmod, + .clk = "mmc_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> hdq1w interface */ +static struct omap_hwmod_ocp_if omap2420_l4_core__hdq1w = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2420_hdq1w_hwmod, + .clk = "hdq_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4 | OCPIF_SWSUP_IDLE, +}; + +static struct omap_hwmod_ocp_if omap2420_l3__gpmc = { + .master = &omap2xxx_l3_main_hwmod, + .slave = &omap2xxx_gpmc_hwmod, + .clk = "core_l3_ck", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod_ocp_if *omap2420_hwmod_ocp_ifs[] __initdata = { + &omap2xxx_l3_main__l4_core, + &omap2xxx_mpu__l3_main, + &omap2xxx_dss__l3, + &omap2xxx_l4_core__mcspi1, + &omap2xxx_l4_core__mcspi2, + &omap2xxx_l4_core__l4_wkup, + &omap2_l4_core__uart1, + &omap2_l4_core__uart2, + &omap2_l4_core__uart3, + &omap2420_l4_core__i2c1, + &omap2420_l4_core__i2c2, + &omap2420_l3__iva, + &omap2420_l3__dsp, + &omap2xxx_l4_core__timer3, + &omap2xxx_l4_core__timer4, + &omap2xxx_l4_core__timer5, + &omap2xxx_l4_core__timer6, + &omap2xxx_l4_core__timer7, + &omap2xxx_l4_core__timer8, + &omap2xxx_l4_core__timer9, + &omap2xxx_l4_core__timer10, + &omap2xxx_l4_core__timer11, + &omap2xxx_l4_core__timer12, + &omap2420_l4_wkup__wd_timer2, + &omap2xxx_l4_core__dss, + &omap2xxx_l4_core__dss_dispc, + &omap2xxx_l4_core__dss_rfbi, + &omap2xxx_l4_core__dss_venc, + &omap2420_l4_wkup__gpio1, + &omap2420_l4_wkup__gpio2, + &omap2420_l4_wkup__gpio3, + &omap2420_l4_wkup__gpio4, + &omap2420_l4_core__mailbox, + &omap2420_l4_core__mcbsp1, + &omap2420_l4_core__mcbsp2, + &omap2420_l4_core__msdi1, + &omap2xxx_l4_core__rng, + &omap2xxx_l4_core__sham, + &omap2xxx_l4_core__aes, + &omap2420_l4_core__hdq1w, + &omap2420_l3__gpmc, + NULL, +}; + +int __init omap2420_hwmod_init(void) +{ + omap_hwmod_init(); + return omap_hwmod_register_links(omap2420_hwmod_ocp_ifs); +} diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c new file mode 100644 index 000000000..c93200801 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod_2430_data.c - hardware modules present on the OMAP2430 chips + * + * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * XXX handle crossbar/shared link difference for L3? + * XXX these should be marked initdata for multi-OMAP kernels + */ + +#include <linux/platform_data/i2c-omap.h> +#include <linux/platform_data/hsmmc-omap.h> + +#include "omap_hwmod.h" +#include "l3_2xxx.h" + +#include "soc.h" +#include "omap_hwmod_common_data.h" +#include "prm-regbits-24xx.h" +#include "cm-regbits-24xx.h" +#include "i2c.h" +#include "wd_timer.h" + +/* + * OMAP2430 hardware module integration data + * + * All of the data in this section should be autogeneratable from the + * TI hardware database or other technical documentation. Data that + * is driver-specific or driver-kernel integration-specific belongs + * elsewhere. + */ + +/* + * IP blocks + */ + +/* IVA2 (IVA2) */ +static struct omap_hwmod_rst_info omap2430_iva_resets[] = { + { .name = "logic", .rst_shift = 0 }, + { .name = "mmu", .rst_shift = 1 }, +}; + +static struct omap_hwmod omap2430_iva_hwmod = { + .name = "iva", + .class = &iva_hwmod_class, + .clkdm_name = "dsp_clkdm", + .rst_lines = omap2430_iva_resets, + .rst_lines_cnt = ARRAY_SIZE(omap2430_iva_resets), + .main_clk = "dsp_fck", +}; + +/* I2C common */ +static struct omap_hwmod_class_sysconfig i2c_sysc = { + .rev_offs = 0x00, + .sysc_offs = 0x20, + .syss_offs = 0x10, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class i2c_class = { + .name = "i2c", + .sysc = &i2c_sysc, + .reset = &omap_i2c_reset, +}; + +/* I2C1 */ +static struct omap_hwmod omap2430_i2c1_hwmod = { + .name = "i2c1", + .flags = HWMOD_16BIT_REG, + .main_clk = "i2chs1_fck", + .prcm = { + .omap2 = { + /* + * NOTE: The CM_FCLKEN* and CM_ICLKEN* for + * I2CHS IP's do not follow the usual pattern. + * prcm_reg_id alone cannot be used to program + * the iclk and fclk. Needs to be handled using + * additional flags when clk handling is moved + * to hwmod framework. + */ + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP2430_ST_I2CHS1_SHIFT, + }, + }, + .class = &i2c_class, +}; + +/* I2C2 */ +static struct omap_hwmod omap2430_i2c2_hwmod = { + .name = "i2c2", + .flags = HWMOD_16BIT_REG, + .main_clk = "i2chs2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP2430_ST_I2CHS2_SHIFT, + }, + }, + .class = &i2c_class, +}; + +/* gpio5 */ +static struct omap_hwmod omap2430_gpio5_hwmod = { + .name = "gpio5", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpio5_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP2430_ST_GPIO5_SHIFT, + }, + }, + .class = &omap2xxx_gpio_hwmod_class, +}; + +/* mailbox */ +static struct omap_hwmod omap2430_mailbox_hwmod = { + .name = "mailbox", + .class = &omap2xxx_mailbox_hwmod_class, + .main_clk = "mailboxes_ick", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MAILBOXES_SHIFT, + }, + }, +}; + +/* mcspi3 */ +static struct omap_hwmod omap2430_mcspi3_hwmod = { + .name = "mcspi3", + .main_clk = "mcspi3_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP2430_ST_MCSPI3_SHIFT, + }, + }, + .class = &omap2xxx_mcspi_class, +}; + +/* usbhsotg */ +static struct omap_hwmod_class_sysconfig omap2430_usbhsotg_sysc = { + .rev_offs = 0x0400, + .sysc_offs = 0x0404, + .syss_offs = 0x0408, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE| + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class usbotg_class = { + .name = "usbotg", + .sysc = &omap2430_usbhsotg_sysc, +}; + +/* usb_otg_hs */ +static struct omap_hwmod omap2430_usbhsotg_hwmod = { + .name = "usb_otg_hs", + .main_clk = "usbhs_ick", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP2430_ST_USBHS_SHIFT, + }, + }, + .class = &usbotg_class, + /* + * Erratum ID: i479 idle_req / idle_ack mechanism potentially + * broken when autoidle is enabled + * workaround is to disable the autoidle bit at module level. + */ + .flags = HWMOD_NO_OCP_AUTOIDLE | HWMOD_SWSUP_SIDLE + | HWMOD_SWSUP_MSTANDBY, +}; + +/* + * 'mcbsp' class + * multi channel buffered serial port controller + */ + +static struct omap_hwmod_class_sysconfig omap2430_mcbsp_sysc = { + .rev_offs = 0x007C, + .sysc_offs = 0x008C, + .sysc_flags = (SYSC_HAS_SOFTRESET), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap2430_mcbsp_hwmod_class = { + .name = "mcbsp", + .sysc = &omap2430_mcbsp_sysc, +}; + +static struct omap_hwmod_opt_clk mcbsp_opt_clks[] = { + { .role = "pad_fck", .clk = "mcbsp_clks" }, + { .role = "prcm_fck", .clk = "func_96m_ck" }, +}; + +/* mcbsp1 */ +static struct omap_hwmod omap2430_mcbsp1_hwmod = { + .name = "mcbsp1", + .class = &omap2430_mcbsp_hwmod_class, + .main_clk = "mcbsp1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MCBSP1_SHIFT, + }, + }, + .opt_clks = mcbsp_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp_opt_clks), +}; + +/* mcbsp2 */ +static struct omap_hwmod omap2430_mcbsp2_hwmod = { + .name = "mcbsp2", + .class = &omap2430_mcbsp_hwmod_class, + .main_clk = "mcbsp2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MCBSP2_SHIFT, + }, + }, + .opt_clks = mcbsp_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp_opt_clks), +}; + +/* mcbsp3 */ +static struct omap_hwmod omap2430_mcbsp3_hwmod = { + .name = "mcbsp3", + .class = &omap2430_mcbsp_hwmod_class, + .main_clk = "mcbsp3_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP2430_ST_MCBSP3_SHIFT, + }, + }, + .opt_clks = mcbsp_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp_opt_clks), +}; + +/* mcbsp4 */ +static struct omap_hwmod omap2430_mcbsp4_hwmod = { + .name = "mcbsp4", + .class = &omap2430_mcbsp_hwmod_class, + .main_clk = "mcbsp4_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP2430_ST_MCBSP4_SHIFT, + }, + }, + .opt_clks = mcbsp_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp_opt_clks), +}; + +/* mcbsp5 */ +static struct omap_hwmod omap2430_mcbsp5_hwmod = { + .name = "mcbsp5", + .class = &omap2430_mcbsp_hwmod_class, + .main_clk = "mcbsp5_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP2430_ST_MCBSP5_SHIFT, + }, + }, + .opt_clks = mcbsp_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp_opt_clks), +}; + +/* MMC/SD/SDIO common */ +static struct omap_hwmod_class_sysconfig omap2430_mmc_sysc = { + .rev_offs = 0x1fc, + .sysc_offs = 0x10, + .syss_offs = 0x14, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap2430_mmc_class = { + .name = "mmc", + .sysc = &omap2430_mmc_sysc, +}; + +/* MMC/SD/SDIO1 */ +static struct omap_hwmod_opt_clk omap2430_mmc1_opt_clks[] = { + { .role = "dbck", .clk = "mmchsdb1_fck" }, +}; + +static struct omap_hsmmc_dev_attr mmc1_dev_attr = { + .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, +}; + +static struct omap_hwmod omap2430_mmc1_hwmod = { + .name = "mmc1", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .opt_clks = omap2430_mmc1_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(omap2430_mmc1_opt_clks), + .main_clk = "mmchs1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP2430_ST_MMCHS1_SHIFT, + }, + }, + .dev_attr = &mmc1_dev_attr, + .class = &omap2430_mmc_class, +}; + +/* MMC/SD/SDIO2 */ +static struct omap_hwmod_opt_clk omap2430_mmc2_opt_clks[] = { + { .role = "dbck", .clk = "mmchsdb2_fck" }, +}; + +static struct omap_hwmod omap2430_mmc2_hwmod = { + .name = "mmc2", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .opt_clks = omap2430_mmc2_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(omap2430_mmc2_opt_clks), + .main_clk = "mmchs2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP2430_ST_MMCHS2_SHIFT, + }, + }, + .class = &omap2430_mmc_class, +}; + +/* HDQ1W/1-wire */ +static struct omap_hwmod omap2430_hdq1w_hwmod = { + .name = "hdq1w", + .main_clk = "hdq_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_HDQ_SHIFT, + }, + }, + .class = &omap2_hdq1w_class, +}; + +/* + * interfaces + */ + +/* L3 -> L4_CORE interface */ +/* l3_core -> usbhsotg interface */ +static struct omap_hwmod_ocp_if omap2430_usbhsotg__l3 = { + .master = &omap2430_usbhsotg_hwmod, + .slave = &omap2xxx_l3_main_hwmod, + .clk = "core_l3_ck", + .user = OCP_USER_MPU, +}; + +/* L4 CORE -> I2C1 interface */ +static struct omap_hwmod_ocp_if omap2430_l4_core__i2c1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_i2c1_hwmod, + .clk = "i2c1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> I2C2 interface */ +static struct omap_hwmod_ocp_if omap2430_l4_core__i2c2 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_i2c2_hwmod, + .clk = "i2c2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core ->usbhsotg interface */ +static struct omap_hwmod_ocp_if omap2430_l4_core__usbhsotg = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_usbhsotg_hwmod, + .clk = "usb_l4_ick", + .user = OCP_USER_MPU, +}; + +/* L4 CORE -> MMC1 interface */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mmc1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mmc1_hwmod, + .clk = "mmchs1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> MMC2 interface */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mmc2 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mmc2_hwmod, + .clk = "mmchs2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> mcspi3 interface */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mcspi3 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mcspi3_hwmod, + .clk = "mcspi3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* IVA2 <- L3 interface */ +static struct omap_hwmod_ocp_if omap2430_l3__iva = { + .master = &omap2xxx_l3_main_hwmod, + .slave = &omap2430_iva_hwmod, + .clk = "core_l3_ck", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> wd_timer2 */ +static struct omap_hwmod_ocp_if omap2430_l4_wkup__wd_timer2 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_wd_timer2_hwmod, + .clk = "mpu_wdt_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio1 */ +static struct omap_hwmod_ocp_if omap2430_l4_wkup__gpio1 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio1_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio2 */ +static struct omap_hwmod_ocp_if omap2430_l4_wkup__gpio2 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio2_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio3 */ +static struct omap_hwmod_ocp_if omap2430_l4_wkup__gpio3 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio3_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio4 */ +static struct omap_hwmod_ocp_if omap2430_l4_wkup__gpio4 = { + .master = &omap2xxx_l4_wkup_hwmod, + .slave = &omap2xxx_gpio4_hwmod, + .clk = "gpios_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> gpio5 */ +static struct omap_hwmod_ocp_if omap2430_l4_core__gpio5 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_gpio5_hwmod, + .clk = "gpio5_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mailbox */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mailbox = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mailbox_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp1 */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mcbsp1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mcbsp1_hwmod, + .clk = "mcbsp1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp2 */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mcbsp2 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mcbsp2_hwmod, + .clk = "mcbsp2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp3 */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mcbsp3 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mcbsp3_hwmod, + .clk = "mcbsp3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp4 */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mcbsp4 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mcbsp4_hwmod, + .clk = "mcbsp4_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp5 */ +static struct omap_hwmod_ocp_if omap2430_l4_core__mcbsp5 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_mcbsp5_hwmod, + .clk = "mcbsp5_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> hdq1w */ +static struct omap_hwmod_ocp_if omap2430_l4_core__hdq1w = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2430_hdq1w_hwmod, + .clk = "hdq_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4 | OCPIF_SWSUP_IDLE, +}; + +static struct omap_hwmod_ocp_if omap2430_l3__gpmc = { + .master = &omap2xxx_l3_main_hwmod, + .slave = &omap2xxx_gpmc_hwmod, + .clk = "core_l3_ck", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod_ocp_if *omap2430_hwmod_ocp_ifs[] __initdata = { + &omap2xxx_l3_main__l4_core, + &omap2xxx_mpu__l3_main, + &omap2xxx_dss__l3, + &omap2430_usbhsotg__l3, + &omap2430_l4_core__i2c1, + &omap2430_l4_core__i2c2, + &omap2xxx_l4_core__l4_wkup, + &omap2_l4_core__uart1, + &omap2_l4_core__uart2, + &omap2_l4_core__uart3, + &omap2430_l4_core__usbhsotg, + &omap2430_l4_core__mmc1, + &omap2430_l4_core__mmc2, + &omap2xxx_l4_core__mcspi1, + &omap2xxx_l4_core__mcspi2, + &omap2430_l4_core__mcspi3, + &omap2430_l3__iva, + &omap2xxx_l4_core__timer3, + &omap2xxx_l4_core__timer4, + &omap2xxx_l4_core__timer5, + &omap2xxx_l4_core__timer6, + &omap2xxx_l4_core__timer7, + &omap2xxx_l4_core__timer8, + &omap2xxx_l4_core__timer9, + &omap2xxx_l4_core__timer10, + &omap2xxx_l4_core__timer11, + &omap2xxx_l4_core__timer12, + &omap2430_l4_wkup__wd_timer2, + &omap2xxx_l4_core__dss, + &omap2xxx_l4_core__dss_dispc, + &omap2xxx_l4_core__dss_rfbi, + &omap2xxx_l4_core__dss_venc, + &omap2430_l4_wkup__gpio1, + &omap2430_l4_wkup__gpio2, + &omap2430_l4_wkup__gpio3, + &omap2430_l4_wkup__gpio4, + &omap2430_l4_core__gpio5, + &omap2430_l4_core__mailbox, + &omap2430_l4_core__mcbsp1, + &omap2430_l4_core__mcbsp2, + &omap2430_l4_core__mcbsp3, + &omap2430_l4_core__mcbsp4, + &omap2430_l4_core__mcbsp5, + &omap2430_l4_core__hdq1w, + &omap2xxx_l4_core__rng, + &omap2xxx_l4_core__sham, + &omap2xxx_l4_core__aes, + &omap2430_l3__gpmc, + NULL, +}; + +int __init omap2430_hwmod_init(void) +{ + omap_hwmod_init(); + return omap_hwmod_register_links(omap2430_hwmod_ocp_ifs); +} diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c new file mode 100644 index 000000000..2581b8a5f --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_3xxx_ipblock_data.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod_2xxx_3xxx_ipblock_data.c - common IP block data for OMAP2/3 + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + */ + +#include <linux/dmaengine.h> +#include <linux/omap-dma.h> + +#include "omap_hwmod.h" +#include "hdq1w.h" + +#include "omap_hwmod_common_data.h" + +/* UART */ + +static struct omap_hwmod_class_sysconfig omap2_uart_sysc = { + .rev_offs = 0x50, + .sysc_offs = 0x54, + .syss_offs = 0x58, + .sysc_flags = (SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2_uart_class = { + .name = "uart", + .sysc = &omap2_uart_sysc, +}; + +/* + * 'venc' class + * video encoder + */ + +struct omap_hwmod_class omap2_venc_hwmod_class = { + .name = "venc", +}; + +/* + * omap_hwmod class data + */ + +struct omap_hwmod_class l3_hwmod_class = { + .name = "l3", +}; + +struct omap_hwmod_class l4_hwmod_class = { + .name = "l4", +}; + +struct omap_hwmod_class mpu_hwmod_class = { + .name = "mpu", +}; + +struct omap_hwmod_class iva_hwmod_class = { + .name = "iva", +}; + +struct omap_hwmod_class_sysconfig omap2_hdq1w_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x14, + .syss_offs = 0x18, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2_hdq1w_class = { + .name = "hdq1w", + .sysc = &omap2_hdq1w_sysc, + .reset = &omap_hdq1w_reset, +}; diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_interconnect_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_interconnect_data.c new file mode 100644 index 000000000..518e877bb --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_interconnect_data.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod_2xxx_interconnect_data.c - common interconnect data for OMAP2xxx + * + * Copyright (C) 2009-2011 Nokia Corporation + * Paul Walmsley + * + * XXX handle crossbar/shared link difference for L3? + * XXX these should be marked initdata for multi-OMAP kernels + */ +#include <linux/sizes.h> + +#include "omap_hwmod.h" +#include "l3_2xxx.h" +#include "l4_2xxx.h" +#include "serial.h" + +#include "omap_hwmod_common_data.h" + +/* + * Common interconnect data + */ + +/* L3 -> L4_CORE interface */ +struct omap_hwmod_ocp_if omap2xxx_l3_main__l4_core = { + .master = &omap2xxx_l3_main_hwmod, + .slave = &omap2xxx_l4_core_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* MPU -> L3 interface */ +struct omap_hwmod_ocp_if omap2xxx_mpu__l3_main = { + .master = &omap2xxx_mpu_hwmod, + .slave = &omap2xxx_l3_main_hwmod, + .user = OCP_USER_MPU, +}; + +/* DSS -> l3 */ +struct omap_hwmod_ocp_if omap2xxx_dss__l3 = { + .master = &omap2xxx_dss_core_hwmod, + .slave = &omap2xxx_l3_main_hwmod, + .fw = { + .omap2 = { + .l3_perm_bit = OMAP2_L3_CORE_FW_CONNID_DSS, + .flags = OMAP_FIREWALL_L3, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4_CORE -> L4_WKUP interface */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__l4_wkup = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_l4_wkup_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> UART1 interface */ +struct omap_hwmod_ocp_if omap2_l4_core__uart1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_uart1_hwmod, + .clk = "uart1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> UART2 interface */ +struct omap_hwmod_ocp_if omap2_l4_core__uart2 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_uart2_hwmod, + .clk = "uart2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 PER -> UART3 interface */ +struct omap_hwmod_ocp_if omap2_l4_core__uart3 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_uart3_hwmod, + .clk = "uart3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> mcspi1 interface */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__mcspi1 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_mcspi1_hwmod, + .clk = "mcspi1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> mcspi2 interface */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__mcspi2 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_mcspi2_hwmod, + .clk = "mcspi2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer3 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer3 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer3_hwmod, + .clk = "gpt3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer4 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer4 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer4_hwmod, + .clk = "gpt4_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer5 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer5 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer5_hwmod, + .clk = "gpt5_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer6 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer6 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer6_hwmod, + .clk = "gpt6_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer7 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer7 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer7_hwmod, + .clk = "gpt7_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer8 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer8 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer8_hwmod, + .clk = "gpt8_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer9 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer9 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer9_hwmod, + .clk = "gpt9_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer10 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer10 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer10_hwmod, + .clk = "gpt10_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer11 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer11 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer11_hwmod, + .clk = "gpt11_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer12 */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__timer12 = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_timer12_hwmod, + .clk = "gpt12_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__dss = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_dss_core_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP2420_L4_CORE_FW_DSS_CORE_REGION, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss_dispc */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_dispc = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_dss_dispc_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP2420_L4_CORE_FW_DSS_DISPC_REGION, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss_rfbi */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_rfbi = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_dss_rfbi_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP2420_L4_CORE_FW_DSS_CORE_REGION, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss_venc */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_venc = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_dss_venc_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP2420_L4_CORE_FW_DSS_VENC_REGION, + .flags = OMAP_FIREWALL_L4, + }, + }, + .flags = OCPIF_SWSUP_IDLE, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> rng */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__rng = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_rng_hwmod, + .clk = "rng_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> sham interface */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__sham = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_sham_hwmod, + .clk = "sha_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> aes interface */ +struct omap_hwmod_ocp_if omap2xxx_l4_core__aes = { + .master = &omap2xxx_l4_core_hwmod, + .slave = &omap2xxx_aes_hwmod, + .clk = "aes_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c new file mode 100644 index 000000000..9156f2bfb --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod_2xxx_ipblock_data.c - common IP block data for OMAP2xxx + * + * Copyright (C) 2011 Nokia Corporation + * Paul Walmsley + */ + +#include <linux/types.h> + +#include "omap_hwmod.h" +#include "omap_hwmod_common_data.h" +#include "cm-regbits-24xx.h" +#include "prm-regbits-24xx.h" +#include "wd_timer.h" + +/* + * 'dispc' class + * display controller + */ + +static struct omap_hwmod_class_sysconfig omap2_dispc_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2_dispc_hwmod_class = { + .name = "dispc", + .sysc = &omap2_dispc_sysc, +}; + +/* OMAP2xxx Timer Common */ +static struct omap_hwmod_class_sysconfig omap2xxx_timer_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_CLOCKACTIVITY | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2xxx_timer_hwmod_class = { + .name = "timer", + .sysc = &omap2xxx_timer_sysc, +}; + +/* + * 'wd_timer' class + * 32-bit watchdog upward counter that generates a pulse on the reset pin on + * overflow condition + */ + +static struct omap_hwmod_class_sysconfig omap2xxx_wd_timer_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_EMUFREE | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2xxx_wd_timer_hwmod_class = { + .name = "wd_timer", + .sysc = &omap2xxx_wd_timer_sysc, + .pre_shutdown = &omap2_wd_timer_disable, + .reset = &omap2_wd_timer_reset, +}; + +/* + * 'gpio' class + * general purpose io module + */ +static struct omap_hwmod_class_sysconfig omap2xxx_gpio_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2xxx_gpio_hwmod_class = { + .name = "gpio", + .sysc = &omap2xxx_gpio_sysc, +}; + +/* + * 'mailbox' class + * mailbox module allowing communication between the on-chip processors + * using a queued mailbox-interrupt mechanism. + */ + +static struct omap_hwmod_class_sysconfig omap2xxx_mailbox_sysc = { + .rev_offs = 0x000, + .sysc_offs = 0x010, + .syss_offs = 0x014, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2xxx_mailbox_hwmod_class = { + .name = "mailbox", + .sysc = &omap2xxx_mailbox_sysc, +}; + +/* + * 'mcspi' class + * multichannel serial port interface (mcspi) / master/slave synchronous serial + * bus + */ + +static struct omap_hwmod_class_sysconfig omap2xxx_mcspi_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2xxx_mcspi_class = { + .name = "mcspi", + .sysc = &omap2xxx_mcspi_sysc, +}; + +/* + * 'gpmc' class + * general purpose memory controller + */ + +static struct omap_hwmod_class_sysconfig omap2xxx_gpmc_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap2xxx_gpmc_hwmod_class = { + .name = "gpmc", + .sysc = &omap2xxx_gpmc_sysc, +}; + +/* + * IP blocks + */ + +/* L3 */ +struct omap_hwmod omap2xxx_l3_main_hwmod = { + .name = "l3_main", + .class = &l3_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* L4 CORE */ +struct omap_hwmod omap2xxx_l4_core_hwmod = { + .name = "l4_core", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* L4 WKUP */ +struct omap_hwmod omap2xxx_l4_wkup_hwmod = { + .name = "l4_wkup", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* MPU */ +struct omap_hwmod omap2xxx_mpu_hwmod = { + .name = "mpu", + .class = &mpu_hwmod_class, + .main_clk = "mpu_ck", +}; + +/* IVA2 */ +struct omap_hwmod omap2xxx_iva_hwmod = { + .name = "iva", + .class = &iva_hwmod_class, +}; + +/* timer3 */ +struct omap_hwmod omap2xxx_timer3_hwmod = { + .name = "timer3", + .main_clk = "gpt3_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT3_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer4 */ +struct omap_hwmod omap2xxx_timer4_hwmod = { + .name = "timer4", + .main_clk = "gpt4_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT4_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer5 */ +struct omap_hwmod omap2xxx_timer5_hwmod = { + .name = "timer5", + .main_clk = "gpt5_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT5_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer6 */ +struct omap_hwmod omap2xxx_timer6_hwmod = { + .name = "timer6", + .main_clk = "gpt6_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT6_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer7 */ +struct omap_hwmod omap2xxx_timer7_hwmod = { + .name = "timer7", + .main_clk = "gpt7_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT7_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer8 */ +struct omap_hwmod omap2xxx_timer8_hwmod = { + .name = "timer8", + .main_clk = "gpt8_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT8_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer9 */ +struct omap_hwmod omap2xxx_timer9_hwmod = { + .name = "timer9", + .main_clk = "gpt9_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT9_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer10 */ +struct omap_hwmod omap2xxx_timer10_hwmod = { + .name = "timer10", + .main_clk = "gpt10_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT10_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer11 */ +struct omap_hwmod omap2xxx_timer11_hwmod = { + .name = "timer11", + .main_clk = "gpt11_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT11_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer12 */ +struct omap_hwmod omap2xxx_timer12_hwmod = { + .name = "timer12", + .main_clk = "gpt12_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPT12_SHIFT, + }, + }, + .class = &omap2xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* wd_timer2 */ +struct omap_hwmod omap2xxx_wd_timer2_hwmod = { + .name = "wd_timer2", + .class = &omap2xxx_wd_timer_hwmod_class, + .main_clk = "mpu_wdt_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MPU_WDT_SHIFT, + }, + }, +}; + +/* UART1 */ + +struct omap_hwmod omap2xxx_uart1_hwmod = { + .name = "uart1", + .main_clk = "uart1_fck", + .flags = DEBUG_OMAP2UART1_FLAGS | HWMOD_SWSUP_SIDLE_ACT, + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_EN_UART1_SHIFT, + }, + }, + .class = &omap2_uart_class, +}; + +/* UART2 */ + +struct omap_hwmod omap2xxx_uart2_hwmod = { + .name = "uart2", + .main_clk = "uart2_fck", + .flags = DEBUG_OMAP2UART2_FLAGS | HWMOD_SWSUP_SIDLE_ACT, + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_EN_UART2_SHIFT, + }, + }, + .class = &omap2_uart_class, +}; + +/* UART3 */ + +struct omap_hwmod omap2xxx_uart3_hwmod = { + .name = "uart3", + .main_clk = "uart3_fck", + .flags = DEBUG_OMAP2UART3_FLAGS | HWMOD_SWSUP_SIDLE_ACT, + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 2, + .idlest_idle_bit = OMAP24XX_EN_UART3_SHIFT, + }, + }, + .class = &omap2_uart_class, +}; + +/* dss */ + +static struct omap_hwmod_opt_clk dss_opt_clks[] = { + /* + * The DSS HW needs all DSS clocks enabled during reset. The dss_core + * driver does not use these clocks. + */ + { .role = "tv_clk", .clk = "dss_54m_fck" }, + { .role = "sys_clk", .clk = "dss2_fck" }, +}; + +struct omap_hwmod omap2xxx_dss_core_hwmod = { + .name = "dss_core", + .class = &omap2_dss_hwmod_class, + .main_clk = "dss1_fck", /* instead of dss_fck */ + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + }, + }, + .opt_clks = dss_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dss_opt_clks), + .flags = HWMOD_NO_IDLEST | HWMOD_CONTROL_OPT_CLKS_IN_RESET, +}; + +struct omap_hwmod omap2xxx_dss_dispc_hwmod = { + .name = "dss_dispc", + .class = &omap2_dispc_hwmod_class, + .main_clk = "dss1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + }, + }, + .flags = HWMOD_NO_IDLEST, + .dev_attr = &omap2_3_dss_dispc_dev_attr, +}; + +static struct omap_hwmod_opt_clk dss_rfbi_opt_clks[] = { + { .role = "ick", .clk = "dss_ick" }, +}; + +struct omap_hwmod omap2xxx_dss_rfbi_hwmod = { + .name = "dss_rfbi", + .class = &omap2_rfbi_hwmod_class, + .main_clk = "dss1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + }, + }, + .opt_clks = dss_rfbi_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dss_rfbi_opt_clks), + .flags = HWMOD_NO_IDLEST, +}; + +struct omap_hwmod omap2xxx_dss_venc_hwmod = { + .name = "dss_venc", + .class = &omap2_venc_hwmod_class, + .main_clk = "dss_54m_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + }, + }, + .flags = HWMOD_NO_IDLEST, +}; + +/* gpio1 */ +struct omap_hwmod omap2xxx_gpio1_hwmod = { + .name = "gpio1", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpios_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPIOS_SHIFT, + }, + }, + .class = &omap2xxx_gpio_hwmod_class, +}; + +/* gpio2 */ +struct omap_hwmod omap2xxx_gpio2_hwmod = { + .name = "gpio2", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpios_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPIOS_SHIFT, + }, + }, + .class = &omap2xxx_gpio_hwmod_class, +}; + +/* gpio3 */ +struct omap_hwmod omap2xxx_gpio3_hwmod = { + .name = "gpio3", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpios_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPIOS_SHIFT, + }, + }, + .class = &omap2xxx_gpio_hwmod_class, +}; + +/* gpio4 */ +struct omap_hwmod omap2xxx_gpio4_hwmod = { + .name = "gpio4", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpios_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_GPIOS_SHIFT, + }, + }, + .class = &omap2xxx_gpio_hwmod_class, +}; + +/* mcspi1 */ +struct omap_hwmod omap2xxx_mcspi1_hwmod = { + .name = "mcspi1", + .main_clk = "mcspi1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MCSPI1_SHIFT, + }, + }, + .class = &omap2xxx_mcspi_class, +}; + +/* mcspi2 */ +struct omap_hwmod omap2xxx_mcspi2_hwmod = { + .name = "mcspi2", + .main_clk = "mcspi2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP24XX_ST_MCSPI2_SHIFT, + }, + }, + .class = &omap2xxx_mcspi_class, +}; + +/* gpmc */ +struct omap_hwmod omap2xxx_gpmc_hwmod = { + .name = "gpmc", + .class = &omap2xxx_gpmc_hwmod_class, + .main_clk = "gpmc_fck", + /* Skip reset for CONFIG_OMAP_GPMC_DEBUG for bootloader timings */ + .flags = HWMOD_NO_IDLEST | DEBUG_OMAP_GPMC_HWMOD_FLAGS, + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + }, + }, +}; + +/* RNG */ + +static struct omap_hwmod_class_sysconfig omap2_rng_sysc = { + .rev_offs = 0x3c, + .sysc_offs = 0x40, + .syss_offs = 0x44, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap2_rng_hwmod_class = { + .name = "rng", + .sysc = &omap2_rng_sysc, +}; + +struct omap_hwmod omap2xxx_rng_hwmod = { + .name = "rng", + .main_clk = "l4_ck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 4, + .idlest_idle_bit = OMAP24XX_ST_RNG_SHIFT, + }, + }, + /* + * XXX The first read from the SYSSTATUS register of the RNG + * after the SYSCONFIG SOFTRESET bit is set triggers an + * imprecise external abort. It's unclear why this happens. + * Until this is analyzed, skip the IP block reset. + */ + .flags = HWMOD_INIT_NO_RESET, + .class = &omap2_rng_hwmod_class, +}; + +/* SHAM */ + +static struct omap_hwmod_class_sysconfig omap2_sham_sysc = { + .rev_offs = 0x5c, + .sysc_offs = 0x60, + .syss_offs = 0x64, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap2xxx_sham_class = { + .name = "sham", + .sysc = &omap2_sham_sysc, +}; + +struct omap_hwmod omap2xxx_sham_hwmod = { + .name = "sham", + .main_clk = "l4_ck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 4, + .idlest_idle_bit = OMAP24XX_ST_SHA_SHIFT, + }, + }, + .class = &omap2xxx_sham_class, +}; + +/* AES */ + +static struct omap_hwmod_class_sysconfig omap2_aes_sysc = { + .rev_offs = 0x44, + .sysc_offs = 0x48, + .syss_offs = 0x4c, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap2xxx_aes_class = { + .name = "aes", + .sysc = &omap2_aes_sysc, +}; + +struct omap_hwmod omap2xxx_aes_hwmod = { + .name = "aes", + .main_clk = "l4_ck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 4, + .idlest_idle_bit = OMAP24XX_ST_AES_SHIFT, + }, + }, + .class = &omap2xxx_aes_class, +}; diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c new file mode 100644 index 000000000..b6c7d98a9 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -0,0 +1,2696 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod_3xxx_data.c - hardware modules present on the OMAP3xxx chips + * + * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * The data in this file should be completely autogeneratable from + * the TI hardware database or other technical documentation. + * + * XXX these should be marked initdata for multi-OMAP kernels + */ + +#include <linux/platform_data/i2c-omap.h> +#include <linux/power/smartreflex.h> +#include <linux/platform_data/hsmmc-omap.h> + +#include "l3_3xxx.h" +#include "l4_3xxx.h" + +#include "soc.h" +#include "omap_hwmod.h" +#include "omap_hwmod_common_data.h" +#include "prm-regbits-34xx.h" +#include "cm-regbits-34xx.h" + +#include "i2c.h" +#include "wd_timer.h" +#include "serial.h" + +/* + * OMAP3xxx hardware module integration data + * + * All of the data in this section should be autogeneratable from the + * TI hardware database or other technical documentation. Data that + * is driver-specific or driver-kernel integration-specific belongs + * elsewhere. + */ + +#define AM35XX_IPSS_USBOTGSS_BASE 0x5C040000 + +/* + * IP blocks + */ + +/* L3 */ + +static struct omap_hwmod omap3xxx_l3_main_hwmod = { + .name = "l3_main", + .class = &l3_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* L4 CORE */ +static struct omap_hwmod omap3xxx_l4_core_hwmod = { + .name = "l4_core", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* L4 PER */ +static struct omap_hwmod omap3xxx_l4_per_hwmod = { + .name = "l4_per", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* L4 WKUP */ +static struct omap_hwmod omap3xxx_l4_wkup_hwmod = { + .name = "l4_wkup", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* L4 SEC */ +static struct omap_hwmod omap3xxx_l4_sec_hwmod = { + .name = "l4_sec", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* MPU */ + +static struct omap_hwmod omap3xxx_mpu_hwmod = { + .name = "mpu", + .class = &mpu_hwmod_class, + .main_clk = "arm_fck", +}; + +/* IVA2 (IVA2) */ +static struct omap_hwmod_rst_info omap3xxx_iva_resets[] = { + { .name = "logic", .rst_shift = 0, .st_shift = 8 }, + { .name = "seq0", .rst_shift = 1, .st_shift = 9 }, + { .name = "seq1", .rst_shift = 2, .st_shift = 10 }, +}; + +static struct omap_hwmod omap3xxx_iva_hwmod = { + .name = "iva", + .class = &iva_hwmod_class, + .clkdm_name = "iva2_clkdm", + .rst_lines = omap3xxx_iva_resets, + .rst_lines_cnt = ARRAY_SIZE(omap3xxx_iva_resets), + .main_clk = "iva2_ck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_IVA2_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_IVA2_SHIFT, + }, + }, +}; + +/* + * 'debugss' class + * debug and emulation sub system + */ + +static struct omap_hwmod_class omap3xxx_debugss_hwmod_class = { + .name = "debugss", +}; + +/* debugss */ +static struct omap_hwmod omap3xxx_debugss_hwmod = { + .name = "debugss", + .class = &omap3xxx_debugss_hwmod_class, + .clkdm_name = "emu_clkdm", + .main_clk = "emu_src_ck", + .flags = HWMOD_NO_IDLEST, +}; + +/* timer class */ +static struct omap_hwmod_class_sysconfig omap3xxx_timer_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_CLOCKACTIVITY | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_EMUFREE | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_timer_hwmod_class = { + .name = "timer", + .sysc = &omap3xxx_timer_sysc, +}; + +/* timer3 */ +static struct omap_hwmod omap3xxx_timer3_hwmod = { + .name = "timer3", + .main_clk = "gpt3_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT3_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer4 */ +static struct omap_hwmod omap3xxx_timer4_hwmod = { + .name = "timer4", + .main_clk = "gpt4_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT4_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer5 */ +static struct omap_hwmod omap3xxx_timer5_hwmod = { + .name = "timer5", + .main_clk = "gpt5_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT5_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer6 */ +static struct omap_hwmod omap3xxx_timer6_hwmod = { + .name = "timer6", + .main_clk = "gpt6_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT6_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer7 */ +static struct omap_hwmod omap3xxx_timer7_hwmod = { + .name = "timer7", + .main_clk = "gpt7_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT7_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer8 */ +static struct omap_hwmod omap3xxx_timer8_hwmod = { + .name = "timer8", + .main_clk = "gpt8_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT8_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer9 */ +static struct omap_hwmod omap3xxx_timer9_hwmod = { + .name = "timer9", + .main_clk = "gpt9_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT9_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer10 */ +static struct omap_hwmod omap3xxx_timer10_hwmod = { + .name = "timer10", + .main_clk = "gpt10_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT10_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* timer11 */ +static struct omap_hwmod omap3xxx_timer11_hwmod = { + .name = "timer11", + .main_clk = "gpt11_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPT11_SHIFT, + }, + }, + .class = &omap3xxx_timer_hwmod_class, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +/* + * 'wd_timer' class + * 32-bit watchdog upward counter that generates a pulse on the reset pin on + * overflow condition + */ + +static struct omap_hwmod_class_sysconfig omap3xxx_wd_timer_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_EMUFREE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY | + SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +/* I2C common */ +static struct omap_hwmod_class_sysconfig i2c_sysc = { + .rev_offs = 0x00, + .sysc_offs = 0x20, + .syss_offs = 0x10, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_wd_timer_hwmod_class = { + .name = "wd_timer", + .sysc = &omap3xxx_wd_timer_sysc, + .pre_shutdown = &omap2_wd_timer_disable, + .reset = &omap2_wd_timer_reset, +}; + +static struct omap_hwmod omap3xxx_wd_timer2_hwmod = { + .name = "wd_timer2", + .class = &omap3xxx_wd_timer_hwmod_class, + .main_clk = "wdt2_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_WDT2_SHIFT, + }, + }, + /* + * XXX: Use software supervised mode, HW supervised smartidle seems to + * block CORE power domain idle transitions. Maybe a HW bug in wdt2? + */ + .flags = HWMOD_SWSUP_SIDLE, +}; + +/* UART1 */ +static struct omap_hwmod omap3xxx_uart1_hwmod = { + .name = "uart1", + .main_clk = "uart1_fck", + .flags = DEBUG_TI81XXUART1_FLAGS | HWMOD_SWSUP_SIDLE, + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_EN_UART1_SHIFT, + }, + }, + .class = &omap2_uart_class, +}; + +/* UART2 */ +static struct omap_hwmod omap3xxx_uart2_hwmod = { + .name = "uart2", + .main_clk = "uart2_fck", + .flags = DEBUG_TI81XXUART2_FLAGS | HWMOD_SWSUP_SIDLE, + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_EN_UART2_SHIFT, + }, + }, + .class = &omap2_uart_class, +}; + +/* UART3 */ +static struct omap_hwmod omap3xxx_uart3_hwmod = { + .name = "uart3", + .main_clk = "uart3_fck", + .flags = DEBUG_OMAP3UART3_FLAGS | DEBUG_TI81XXUART3_FLAGS | + HWMOD_SWSUP_SIDLE, + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_EN_UART3_SHIFT, + }, + }, + .class = &omap2_uart_class, +}; + +/* UART4 */ + + +static struct omap_hwmod omap36xx_uart4_hwmod = { + .name = "uart4", + .main_clk = "uart4_fck", + .flags = DEBUG_OMAP3UART4_FLAGS | HWMOD_SWSUP_SIDLE, + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3630_EN_UART4_SHIFT, + }, + }, + .class = &omap2_uart_class, +}; + + + +/* + * XXX AM35xx UART4 cannot complete its softreset without uart1_fck or + * uart2_fck being enabled. So we add uart1_fck as an optional clock, + * below, and set the HWMOD_CONTROL_OPT_CLKS_IN_RESET. This really + * should not be needed. The functional clock structure of the AM35xx + * UART4 is extremely unclear and opaque; it is unclear what the role + * of uart1/2_fck is for the UART4. Any clarification from either + * empirical testing or the AM3505/3517 hardware designers would be + * most welcome. + */ +static struct omap_hwmod_opt_clk am35xx_uart4_opt_clks[] = { + { .role = "softreset_uart1_fck", .clk = "uart1_fck" }, +}; + +static struct omap_hwmod am35xx_uart4_hwmod = { + .name = "uart4", + .main_clk = "uart4_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = AM35XX_ST_UART4_SHIFT, + }, + }, + .opt_clks = am35xx_uart4_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(am35xx_uart4_opt_clks), + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .class = &omap2_uart_class, +}; + +static struct omap_hwmod_class i2c_class = { + .name = "i2c", + .sysc = &i2c_sysc, + .reset = &omap_i2c_reset, +}; + +/* dss */ +static struct omap_hwmod_opt_clk dss_opt_clks[] = { + /* + * The DSS HW needs all DSS clocks enabled during reset. The dss_core + * driver does not use these clocks. + */ + { .role = "sys_clk", .clk = "dss2_alwon_fck" }, + { .role = "tv_clk", .clk = "dss_tv_fck" }, + /* required only on OMAP3430 */ + { .role = "tv_dac_clk", .clk = "dss_96m_fck" }, +}; + +static struct omap_hwmod omap3430es1_dss_core_hwmod = { + .name = "dss_core", + .class = &omap2_dss_hwmod_class, + .main_clk = "dss1_alwon_fck", /* instead of dss_fck */ + .prcm = { + .omap2 = { + .module_offs = OMAP3430_DSS_MOD, + .idlest_reg_id = 1, + }, + }, + .opt_clks = dss_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dss_opt_clks), + .flags = HWMOD_NO_IDLEST | HWMOD_CONTROL_OPT_CLKS_IN_RESET, +}; + +static struct omap_hwmod omap3xxx_dss_core_hwmod = { + .name = "dss_core", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .class = &omap2_dss_hwmod_class, + .main_clk = "dss1_alwon_fck", /* instead of dss_fck */ + .prcm = { + .omap2 = { + .module_offs = OMAP3430_DSS_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430ES2_ST_DSS_IDLE_SHIFT, + }, + }, + .opt_clks = dss_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dss_opt_clks), +}; + +/* + * 'dispc' class + * display controller + */ + +static struct omap_hwmod_class_sysconfig omap3_dispc_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSC_HAS_ENAWAKEUP), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3_dispc_hwmod_class = { + .name = "dispc", + .sysc = &omap3_dispc_sysc, +}; + +static struct omap_hwmod omap3xxx_dss_dispc_hwmod = { + .name = "dss_dispc", + .class = &omap3_dispc_hwmod_class, + .main_clk = "dss1_alwon_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_DSS_MOD, + }, + }, + .flags = HWMOD_NO_IDLEST, + .dev_attr = &omap2_3_dss_dispc_dev_attr, +}; + +/* + * 'dsi' class + * display serial interface controller + */ + +static struct omap_hwmod_class_sysconfig omap3xxx_dsi_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_dsi_hwmod_class = { + .name = "dsi", + .sysc = &omap3xxx_dsi_sysc, +}; + +/* dss_dsi1 */ +static struct omap_hwmod_opt_clk dss_dsi1_opt_clks[] = { + { .role = "sys_clk", .clk = "dss2_alwon_fck" }, +}; + +static struct omap_hwmod omap3xxx_dss_dsi1_hwmod = { + .name = "dss_dsi1", + .class = &omap3xxx_dsi_hwmod_class, + .main_clk = "dss1_alwon_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_DSS_MOD, + }, + }, + .opt_clks = dss_dsi1_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dss_dsi1_opt_clks), + .flags = HWMOD_NO_IDLEST, +}; + +static struct omap_hwmod_opt_clk dss_rfbi_opt_clks[] = { + { .role = "ick", .clk = "dss_ick" }, +}; + +static struct omap_hwmod omap3xxx_dss_rfbi_hwmod = { + .name = "dss_rfbi", + .class = &omap2_rfbi_hwmod_class, + .main_clk = "dss1_alwon_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_DSS_MOD, + }, + }, + .opt_clks = dss_rfbi_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dss_rfbi_opt_clks), + .flags = HWMOD_NO_IDLEST, +}; + +static struct omap_hwmod_opt_clk dss_venc_opt_clks[] = { + /* required only on OMAP3430 */ + { .role = "tv_dac_clk", .clk = "dss_96m_fck" }, +}; + +static struct omap_hwmod omap3xxx_dss_venc_hwmod = { + .name = "dss_venc", + .class = &omap2_venc_hwmod_class, + .main_clk = "dss_tv_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_DSS_MOD, + }, + }, + .opt_clks = dss_venc_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dss_venc_opt_clks), + .flags = HWMOD_NO_IDLEST, +}; + +/* I2C1 */ +static struct omap_hwmod omap3xxx_i2c1_hwmod = { + .name = "i2c1", + .flags = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT, + .main_clk = "i2c1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_I2C1_SHIFT, + }, + }, + .class = &i2c_class, +}; + +/* I2C2 */ +static struct omap_hwmod omap3xxx_i2c2_hwmod = { + .name = "i2c2", + .flags = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT, + .main_clk = "i2c2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_I2C2_SHIFT, + }, + }, + .class = &i2c_class, +}; + +/* I2C3 */ +static struct omap_hwmod omap3xxx_i2c3_hwmod = { + .name = "i2c3", + .flags = HWMOD_16BIT_REG | HWMOD_SET_DEFAULT_CLOCKACT, + .main_clk = "i2c3_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_I2C3_SHIFT, + }, + }, + .class = &i2c_class, +}; + +/* + * 'gpio' class + * general purpose io module + */ + +static struct omap_hwmod_class_sysconfig omap3xxx_gpio_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_gpio_hwmod_class = { + .name = "gpio", + .sysc = &omap3xxx_gpio_sysc, +}; + +/* gpio1 */ +static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { + { .role = "dbclk", .clk = "gpio1_dbck", }, +}; + +static struct omap_hwmod omap3xxx_gpio1_hwmod = { + .name = "gpio1", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpio1_ick", + .opt_clks = gpio1_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPIO1_SHIFT, + }, + }, + .class = &omap3xxx_gpio_hwmod_class, +}; + +/* gpio2 */ +static struct omap_hwmod_opt_clk gpio2_opt_clks[] = { + { .role = "dbclk", .clk = "gpio2_dbck", }, +}; + +static struct omap_hwmod omap3xxx_gpio2_hwmod = { + .name = "gpio2", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpio2_ick", + .opt_clks = gpio2_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio2_opt_clks), + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPIO2_SHIFT, + }, + }, + .class = &omap3xxx_gpio_hwmod_class, +}; + +/* gpio3 */ +static struct omap_hwmod_opt_clk gpio3_opt_clks[] = { + { .role = "dbclk", .clk = "gpio3_dbck", }, +}; + +static struct omap_hwmod omap3xxx_gpio3_hwmod = { + .name = "gpio3", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpio3_ick", + .opt_clks = gpio3_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio3_opt_clks), + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPIO3_SHIFT, + }, + }, + .class = &omap3xxx_gpio_hwmod_class, +}; + +/* gpio4 */ +static struct omap_hwmod_opt_clk gpio4_opt_clks[] = { + { .role = "dbclk", .clk = "gpio4_dbck", }, +}; + +static struct omap_hwmod omap3xxx_gpio4_hwmod = { + .name = "gpio4", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpio4_ick", + .opt_clks = gpio4_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio4_opt_clks), + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPIO4_SHIFT, + }, + }, + .class = &omap3xxx_gpio_hwmod_class, +}; + +/* gpio5 */ + +static struct omap_hwmod_opt_clk gpio5_opt_clks[] = { + { .role = "dbclk", .clk = "gpio5_dbck", }, +}; + +static struct omap_hwmod omap3xxx_gpio5_hwmod = { + .name = "gpio5", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpio5_ick", + .opt_clks = gpio5_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio5_opt_clks), + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPIO5_SHIFT, + }, + }, + .class = &omap3xxx_gpio_hwmod_class, +}; + +/* gpio6 */ + +static struct omap_hwmod_opt_clk gpio6_opt_clks[] = { + { .role = "dbclk", .clk = "gpio6_dbck", }, +}; + +static struct omap_hwmod omap3xxx_gpio6_hwmod = { + .name = "gpio6", + .flags = HWMOD_CONTROL_OPT_CLKS_IN_RESET, + .main_clk = "gpio6_ick", + .opt_clks = gpio6_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio6_opt_clks), + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_GPIO6_SHIFT, + }, + }, + .class = &omap3xxx_gpio_hwmod_class, +}; + +/* + * 'mcbsp' class + * multi channel buffered serial port controller + */ + +static struct omap_hwmod_class_sysconfig omap3xxx_mcbsp_sysc = { + .rev_offs = -ENODEV, + .sysc_offs = 0x008c, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_ENAWAKEUP | + SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_mcbsp_hwmod_class = { + .name = "mcbsp", + .sysc = &omap3xxx_mcbsp_sysc, +}; + +/* McBSP functional clock mapping */ +static struct omap_hwmod_opt_clk mcbsp15_opt_clks[] = { + { .role = "pad_fck", .clk = "mcbsp_clks" }, + { .role = "prcm_fck", .clk = "core_96m_fck" }, +}; + +static struct omap_hwmod_opt_clk mcbsp234_opt_clks[] = { + { .role = "pad_fck", .clk = "mcbsp_clks" }, + { .role = "prcm_fck", .clk = "per_96m_fck" }, +}; + +/* mcbsp1 */ +static struct omap_hwmod omap3xxx_mcbsp1_hwmod = { + .name = "mcbsp1", + .class = &omap3xxx_mcbsp_hwmod_class, + .main_clk = "mcbsp1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCBSP1_SHIFT, + }, + }, + .opt_clks = mcbsp15_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp15_opt_clks), +}; + +/* mcbsp2 */ +static struct omap_hwmod omap3xxx_mcbsp2_hwmod = { + .name = "mcbsp2", + .class = &omap3xxx_mcbsp_hwmod_class, + .main_clk = "mcbsp2_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCBSP2_SHIFT, + }, + }, + .opt_clks = mcbsp234_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp234_opt_clks), +}; + +/* mcbsp3 */ +static struct omap_hwmod omap3xxx_mcbsp3_hwmod = { + .name = "mcbsp3", + .class = &omap3xxx_mcbsp_hwmod_class, + .main_clk = "mcbsp3_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCBSP3_SHIFT, + }, + }, + .opt_clks = mcbsp234_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp234_opt_clks), +}; + +/* mcbsp4 */ +static struct omap_hwmod omap3xxx_mcbsp4_hwmod = { + .name = "mcbsp4", + .class = &omap3xxx_mcbsp_hwmod_class, + .main_clk = "mcbsp4_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_PER_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCBSP4_SHIFT, + }, + }, + .opt_clks = mcbsp234_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp234_opt_clks), +}; + +/* mcbsp5 */ +static struct omap_hwmod omap3xxx_mcbsp5_hwmod = { + .name = "mcbsp5", + .class = &omap3xxx_mcbsp_hwmod_class, + .main_clk = "mcbsp5_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCBSP5_SHIFT, + }, + }, + .opt_clks = mcbsp15_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(mcbsp15_opt_clks), +}; + +/* 'mcbsp sidetone' class */ +static struct omap_hwmod_class_sysconfig omap3xxx_mcbsp_sidetone_sysc = { + .rev_offs = -ENODEV, + .sysc_offs = 0x0010, + .sysc_flags = SYSC_HAS_AUTOIDLE, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_mcbsp_sidetone_hwmod_class = { + .name = "mcbsp_sidetone", + .sysc = &omap3xxx_mcbsp_sidetone_sysc, +}; + +/* mcbsp2_sidetone */ +static struct omap_hwmod omap3xxx_mcbsp2_sidetone_hwmod = { + .name = "mcbsp2_sidetone", + .class = &omap3xxx_mcbsp_sidetone_hwmod_class, + .main_clk = "mcbsp2_ick", + .flags = HWMOD_NO_IDLEST, +}; + +/* mcbsp3_sidetone */ +static struct omap_hwmod omap3xxx_mcbsp3_sidetone_hwmod = { + .name = "mcbsp3_sidetone", + .class = &omap3xxx_mcbsp_sidetone_hwmod_class, + .main_clk = "mcbsp3_ick", + .flags = HWMOD_NO_IDLEST, +}; + +/* SR common */ +static struct omap_hwmod_class_sysconfig omap34xx_sr_sysc = { + .rev_offs = -ENODEV, + .sysc_offs = 0x24, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_NO_CACHE), + .sysc_fields = &omap34xx_sr_sysc_fields, +}; + +static struct omap_hwmod_class omap34xx_smartreflex_hwmod_class = { + .name = "smartreflex", + .sysc = &omap34xx_sr_sysc, +}; + +static struct omap_hwmod_class_sysconfig omap36xx_sr_sysc = { + .rev_offs = -ENODEV, + .sysc_offs = 0x38, + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_ENAWAKEUP | + SYSC_NO_CACHE), + .sysc_fields = &omap36xx_sr_sysc_fields, +}; + +static struct omap_hwmod_class omap36xx_smartreflex_hwmod_class = { + .name = "smartreflex", + .sysc = &omap36xx_sr_sysc, +}; + +/* SR1 */ +static struct omap_smartreflex_dev_attr sr1_dev_attr = { + .sensor_voltdm_name = "mpu_iva", +}; + + +static struct omap_hwmod omap34xx_sr1_hwmod = { + .name = "smartreflex_mpu_iva", + .class = &omap34xx_smartreflex_hwmod_class, + .main_clk = "sr1_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_EN_SR1_SHIFT, + }, + }, + .dev_attr = &sr1_dev_attr, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +static struct omap_hwmod omap36xx_sr1_hwmod = { + .name = "smartreflex_mpu_iva", + .class = &omap36xx_smartreflex_hwmod_class, + .main_clk = "sr1_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_EN_SR1_SHIFT, + }, + }, + .dev_attr = &sr1_dev_attr, +}; + +/* SR2 */ +static struct omap_smartreflex_dev_attr sr2_dev_attr = { + .sensor_voltdm_name = "core", +}; + + +static struct omap_hwmod omap34xx_sr2_hwmod = { + .name = "smartreflex_core", + .class = &omap34xx_smartreflex_hwmod_class, + .main_clk = "sr2_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_EN_SR2_SHIFT, + }, + }, + .dev_attr = &sr2_dev_attr, + .flags = HWMOD_SET_DEFAULT_CLOCKACT, +}; + +static struct omap_hwmod omap36xx_sr2_hwmod = { + .name = "smartreflex_core", + .class = &omap36xx_smartreflex_hwmod_class, + .main_clk = "sr2_fck", + .prcm = { + .omap2 = { + .module_offs = WKUP_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_EN_SR2_SHIFT, + }, + }, + .dev_attr = &sr2_dev_attr, +}; + +/* + * 'mailbox' class + * mailbox module allowing communication between the on-chip processors + * using a queued mailbox-interrupt mechanism. + */ + +static struct omap_hwmod_class_sysconfig omap3xxx_mailbox_sysc = { + .rev_offs = 0x000, + .sysc_offs = 0x010, + .syss_offs = 0x014, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_mailbox_hwmod_class = { + .name = "mailbox", + .sysc = &omap3xxx_mailbox_sysc, +}; + +static struct omap_hwmod omap3xxx_mailbox_hwmod = { + .name = "mailbox", + .class = &omap3xxx_mailbox_hwmod_class, + .main_clk = "mailboxes_ick", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MAILBOXES_SHIFT, + }, + }, +}; + +/* + * 'mcspi' class + * multichannel serial port interface (mcspi) / master/slave synchronous serial + * bus + */ + +static struct omap_hwmod_class_sysconfig omap34xx_mcspi_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap34xx_mcspi_class = { + .name = "mcspi", + .sysc = &omap34xx_mcspi_sysc, +}; + +/* mcspi1 */ +static struct omap_hwmod omap34xx_mcspi1 = { + .name = "mcspi1", + .main_clk = "mcspi1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCSPI1_SHIFT, + }, + }, + .class = &omap34xx_mcspi_class, +}; + +/* mcspi2 */ +static struct omap_hwmod omap34xx_mcspi2 = { + .name = "mcspi2", + .main_clk = "mcspi2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCSPI2_SHIFT, + }, + }, + .class = &omap34xx_mcspi_class, +}; + +/* mcspi3 */ +static struct omap_hwmod omap34xx_mcspi3 = { + .name = "mcspi3", + .main_clk = "mcspi3_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCSPI3_SHIFT, + }, + }, + .class = &omap34xx_mcspi_class, +}; + +/* mcspi4 */ +static struct omap_hwmod omap34xx_mcspi4 = { + .name = "mcspi4", + .main_clk = "mcspi4_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MCSPI4_SHIFT, + }, + }, + .class = &omap34xx_mcspi_class, +}; + +/* usbhsotg */ +static struct omap_hwmod_class_sysconfig omap3xxx_usbhsotg_sysc = { + .rev_offs = 0x0400, + .sysc_offs = 0x0404, + .syss_offs = 0x0408, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE| + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class usbotg_class = { + .name = "usbotg", + .sysc = &omap3xxx_usbhsotg_sysc, +}; + +/* usb_otg_hs */ + +static struct omap_hwmod omap3xxx_usbhsotg_hwmod = { + .name = "usb_otg_hs", + .main_clk = "hsotgusb_ick", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT, + }, + }, + .class = &usbotg_class, + + /* + * Erratum ID: i479 idle_req / idle_ack mechanism potentially + * broken when autoidle is enabled + * workaround is to disable the autoidle bit at module level. + * + * Enabling the device in any other MIDLEMODE setting but force-idle + * causes core_pwrdm not enter idle states at least on OMAP3630. + * Note that musb has OTG_FORCESTDBY register that controls MSTANDBY + * signal when MIDLEMODE is set to force-idle. + */ + .flags = HWMOD_NO_OCP_AUTOIDLE | HWMOD_SWSUP_SIDLE | + HWMOD_FORCE_MSTANDBY | HWMOD_RECONFIG_IO_CHAIN, +}; + +/* usb_otg_hs */ + +static struct omap_hwmod_class am35xx_usbotg_class = { + .name = "am35xx_usbotg", +}; + +static struct omap_hwmod am35xx_usbhsotg_hwmod = { + .name = "am35x_otg_hs", + .main_clk = "hsotgusb_fck", + .class = &am35xx_usbotg_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* MMC/SD/SDIO common */ +static struct omap_hwmod_class_sysconfig omap34xx_mmc_sysc = { + .rev_offs = 0x1fc, + .sysc_offs = 0x10, + .syss_offs = 0x14, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap34xx_mmc_class = { + .name = "mmc", + .sysc = &omap34xx_mmc_sysc, +}; + +/* MMC/SD/SDIO1 */ + + + +static struct omap_hwmod_opt_clk omap34xx_mmc1_opt_clks[] = { + { .role = "dbck", .clk = "omap_32k_fck", }, +}; + +static struct omap_hsmmc_dev_attr mmc1_dev_attr = { + .flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT, +}; + +/* See 35xx errata 2.1.1.128 in SPRZ278F */ +static struct omap_hsmmc_dev_attr mmc1_pre_es3_dev_attr = { + .flags = (OMAP_HSMMC_SUPPORTS_DUAL_VOLT | + OMAP_HSMMC_BROKEN_MULTIBLOCK_READ), +}; + +static struct omap_hwmod omap3xxx_pre_es3_mmc1_hwmod = { + .name = "mmc1", + .opt_clks = omap34xx_mmc1_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(omap34xx_mmc1_opt_clks), + .main_clk = "mmchs1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MMC1_SHIFT, + }, + }, + .dev_attr = &mmc1_pre_es3_dev_attr, + .class = &omap34xx_mmc_class, +}; + +static struct omap_hwmod omap3xxx_es3plus_mmc1_hwmod = { + .name = "mmc1", + .opt_clks = omap34xx_mmc1_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(omap34xx_mmc1_opt_clks), + .main_clk = "mmchs1_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MMC1_SHIFT, + }, + }, + .dev_attr = &mmc1_dev_attr, + .class = &omap34xx_mmc_class, +}; + +/* MMC/SD/SDIO2 */ + + + +static struct omap_hwmod_opt_clk omap34xx_mmc2_opt_clks[] = { + { .role = "dbck", .clk = "omap_32k_fck", }, +}; + +/* See 35xx errata 2.1.1.128 in SPRZ278F */ +static struct omap_hsmmc_dev_attr mmc2_pre_es3_dev_attr = { + .flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ, +}; + +static struct omap_hwmod omap3xxx_pre_es3_mmc2_hwmod = { + .name = "mmc2", + .opt_clks = omap34xx_mmc2_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(omap34xx_mmc2_opt_clks), + .main_clk = "mmchs2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MMC2_SHIFT, + }, + }, + .dev_attr = &mmc2_pre_es3_dev_attr, + .class = &omap34xx_mmc_class, +}; + +static struct omap_hwmod omap3xxx_es3plus_mmc2_hwmod = { + .name = "mmc2", + .opt_clks = omap34xx_mmc2_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(omap34xx_mmc2_opt_clks), + .main_clk = "mmchs2_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MMC2_SHIFT, + }, + }, + .class = &omap34xx_mmc_class, +}; + +/* MMC/SD/SDIO3 */ + + + +static struct omap_hwmod_opt_clk omap34xx_mmc3_opt_clks[] = { + { .role = "dbck", .clk = "omap_32k_fck", }, +}; + +static struct omap_hwmod omap3xxx_mmc3_hwmod = { + .name = "mmc3", + .opt_clks = omap34xx_mmc3_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(omap34xx_mmc3_opt_clks), + .main_clk = "mmchs3_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_MMC3_SHIFT, + }, + }, + .class = &omap34xx_mmc_class, +}; + +/* + * 'usb_host_hs' class + * high-speed multi-port usb host controller + */ + +static struct omap_hwmod_class_sysconfig omap3xxx_usb_host_hs_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_MIDLEMODE | SYSC_HAS_CLOCKACTIVITY | + SYSC_HAS_SIDLEMODE | SYSC_HAS_ENAWAKEUP | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_FORCE | MSTANDBY_NO | MSTANDBY_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_usb_host_hs_hwmod_class = { + .name = "usb_host_hs", + .sysc = &omap3xxx_usb_host_hs_sysc, +}; + + +static struct omap_hwmod omap3xxx_usb_host_hs_hwmod = { + .name = "usb_host_hs", + .class = &omap3xxx_usb_host_hs_hwmod_class, + .clkdm_name = "usbhost_clkdm", + .main_clk = "usbhost_48m_fck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430ES2_USBHOST_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430ES2_ST_USBHOST_IDLE_SHIFT, + }, + }, + + /* + * Errata: USBHOST Configured In Smart-Idle Can Lead To a Deadlock + * id: i660 + * + * Description: + * In the following configuration : + * - USBHOST module is set to smart-idle mode + * - PRCM asserts idle_req to the USBHOST module ( This typically + * happens when the system is going to a low power mode : all ports + * have been suspended, the master part of the USBHOST module has + * entered the standby state, and SW has cut the functional clocks) + * - an USBHOST interrupt occurs before the module is able to answer + * idle_ack, typically a remote wakeup IRQ. + * Then the USB HOST module will enter a deadlock situation where it + * is no more accessible nor functional. + * + * Workaround: + * Don't use smart idle; use only force idle, hence HWMOD_SWSUP_SIDLE + */ + + /* + * Errata: USB host EHCI may stall when entering smart-standby mode + * Id: i571 + * + * Description: + * When the USBHOST module is set to smart-standby mode, and when it is + * ready to enter the standby state (i.e. all ports are suspended and + * all attached devices are in suspend mode), then it can wrongly assert + * the Mstandby signal too early while there are still some residual OCP + * transactions ongoing. If this condition occurs, the internal state + * machine may go to an undefined state and the USB link may be stuck + * upon the next resume. + * + * Workaround: + * Don't use smart standby; use only force standby, + * hence HWMOD_SWSUP_MSTANDBY + */ + + .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY, +}; + +/* + * 'usb_tll_hs' class + * usb_tll_hs module is the adapter on the usb_host_hs ports + */ +static struct omap_hwmod_class_sysconfig omap3xxx_usb_tll_hs_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_usb_tll_hs_hwmod_class = { + .name = "usb_tll_hs", + .sysc = &omap3xxx_usb_tll_hs_sysc, +}; + + +static struct omap_hwmod omap3xxx_usb_tll_hs_hwmod = { + .name = "usb_tll_hs", + .class = &omap3xxx_usb_tll_hs_hwmod_class, + .clkdm_name = "core_l4_clkdm", + .main_clk = "usbtll_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 3, + .idlest_idle_bit = OMAP3430ES2_ST_USBTLL_SHIFT, + }, + }, +}; + +static struct omap_hwmod omap3xxx_hdq1w_hwmod = { + .name = "hdq1w", + .main_clk = "hdq_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_HDQ_SHIFT, + }, + }, + .class = &omap2_hdq1w_class, +}; + +/* SAD2D */ +static struct omap_hwmod_rst_info omap3xxx_sad2d_resets[] = { + { .name = "rst_modem_pwron_sw", .rst_shift = 0 }, + { .name = "rst_modem_sw", .rst_shift = 1 }, +}; + +static struct omap_hwmod_class omap3xxx_sad2d_class = { + .name = "sad2d", +}; + +static struct omap_hwmod omap3xxx_sad2d_hwmod = { + .name = "sad2d", + .rst_lines = omap3xxx_sad2d_resets, + .rst_lines_cnt = ARRAY_SIZE(omap3xxx_sad2d_resets), + .main_clk = "sad2d_ick", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_SAD2D_SHIFT, + }, + }, + .class = &omap3xxx_sad2d_class, +}; + +/* + * 'gpmc' class + * general purpose memory controller + */ + +static struct omap_hwmod_class_sysconfig omap3xxx_gpmc_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_gpmc_hwmod_class = { + .name = "gpmc", + .sysc = &omap3xxx_gpmc_sysc, +}; + +static struct omap_hwmod omap3xxx_gpmc_hwmod = { + .name = "gpmc", + .class = &omap3xxx_gpmc_hwmod_class, + .clkdm_name = "core_l3_clkdm", + .main_clk = "gpmc_fck", + /* Skip reset for CONFIG_OMAP_GPMC_DEBUG for bootloader timings */ + .flags = HWMOD_NO_IDLEST | DEBUG_OMAP_GPMC_HWMOD_FLAGS, +}; + +/* + * interfaces + */ + +/* L3 -> L4_CORE interface */ +static struct omap_hwmod_ocp_if omap3xxx_l3_main__l4_core = { + .master = &omap3xxx_l3_main_hwmod, + .slave = &omap3xxx_l4_core_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L3 -> L4_PER interface */ +static struct omap_hwmod_ocp_if omap3xxx_l3_main__l4_per = { + .master = &omap3xxx_l3_main_hwmod, + .slave = &omap3xxx_l4_per_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* MPU -> L3 interface */ +static struct omap_hwmod_ocp_if omap3xxx_mpu__l3_main = { + .master = &omap3xxx_mpu_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .user = OCP_USER_MPU, +}; + + +/* l3 -> debugss */ +static struct omap_hwmod_ocp_if omap3xxx_l3_main__l4_debugss = { + .master = &omap3xxx_l3_main_hwmod, + .slave = &omap3xxx_debugss_hwmod, + .user = OCP_USER_MPU, +}; + +/* DSS -> l3 */ +static struct omap_hwmod_ocp_if omap3430es1_dss__l3 = { + .master = &omap3430es1_dss_core_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod_ocp_if omap3xxx_dss__l3 = { + .master = &omap3xxx_dss_core_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .fw = { + .omap2 = { + .l3_perm_bit = OMAP3_L3_CORE_FW_INIT_ID_DSS, + .flags = OMAP_FIREWALL_L3, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l3_core -> usbhsotg interface */ +static struct omap_hwmod_ocp_if omap3xxx_usbhsotg__l3 = { + .master = &omap3xxx_usbhsotg_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .clk = "core_l3_ick", + .user = OCP_USER_MPU, +}; + +/* l3_core -> am35xx_usbhsotg interface */ +static struct omap_hwmod_ocp_if am35xx_usbhsotg__l3 = { + .master = &am35xx_usbhsotg_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .clk = "hsotgusb_ick", + .user = OCP_USER_MPU, +}; + +/* l3_core -> sad2d interface */ +static struct omap_hwmod_ocp_if omap3xxx_sad2d__l3 = { + .master = &omap3xxx_sad2d_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .clk = "core_l3_ick", + .user = OCP_USER_MPU, +}; + +/* L4_CORE -> L4_WKUP interface */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__l4_wkup = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_l4_wkup_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> MMC1 interface */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__pre_es3_mmc1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_pre_es3_mmc1_hwmod, + .clk = "mmchs1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4, +}; + +static struct omap_hwmod_ocp_if omap3xxx_l4_core__es3plus_mmc1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_es3plus_mmc1_hwmod, + .clk = "mmchs1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4, +}; + +/* L4 CORE -> MMC2 interface */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__pre_es3_mmc2 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_pre_es3_mmc2_hwmod, + .clk = "mmchs2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4, +}; + +static struct omap_hwmod_ocp_if omap3xxx_l4_core__es3plus_mmc2 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_es3plus_mmc2_hwmod, + .clk = "mmchs2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4, +}; + +/* L4 CORE -> MMC3 interface */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_core__mmc3 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_mmc3_hwmod, + .clk = "mmchs3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4, +}; + +/* L4 CORE -> UART1 interface */ + +static struct omap_hwmod_ocp_if omap3_l4_core__uart1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_uart1_hwmod, + .clk = "uart1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> UART2 interface */ + +static struct omap_hwmod_ocp_if omap3_l4_core__uart2 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_uart2_hwmod, + .clk = "uart2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 PER -> UART3 interface */ + +static struct omap_hwmod_ocp_if omap3_l4_per__uart3 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_uart3_hwmod, + .clk = "uart3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 PER -> UART4 interface */ + +static struct omap_hwmod_ocp_if omap36xx_l4_per__uart4 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap36xx_uart4_hwmod, + .clk = "uart4_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* AM35xx: L4 CORE -> UART4 interface */ + +static struct omap_hwmod_ocp_if am35xx_l4_core__uart4 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &am35xx_uart4_hwmod, + .clk = "uart4_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> I2C1 interface */ +static struct omap_hwmod_ocp_if omap3_l4_core__i2c1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_i2c1_hwmod, + .clk = "i2c1_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_I2C1_REGION, + .l4_prot_group = 7, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> I2C2 interface */ +static struct omap_hwmod_ocp_if omap3_l4_core__i2c2 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_i2c2_hwmod, + .clk = "i2c2_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_I2C2_REGION, + .l4_prot_group = 7, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> I2C3 interface */ + +static struct omap_hwmod_ocp_if omap3_l4_core__i2c3 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_i2c3_hwmod, + .clk = "i2c3_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_I2C3_REGION, + .l4_prot_group = 7, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* L4 CORE -> SR1 interface */ +static struct omap_hwmod_ocp_if omap34xx_l4_core__sr1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap34xx_sr1_hwmod, + .clk = "sr_l4_ick", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_ocp_if omap36xx_l4_core__sr1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap36xx_sr1_hwmod, + .clk = "sr_l4_ick", + .user = OCP_USER_MPU, +}; + +/* L4 CORE -> SR2 interface */ + +static struct omap_hwmod_ocp_if omap34xx_l4_core__sr2 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap34xx_sr2_hwmod, + .clk = "sr_l4_ick", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_ocp_if omap36xx_l4_core__sr2 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap36xx_sr2_hwmod, + .clk = "sr_l4_ick", + .user = OCP_USER_MPU, +}; + + +/* l4_core -> usbhsotg */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__usbhsotg = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_usbhsotg_hwmod, + .clk = "l4_ick", + .user = OCP_USER_MPU, +}; + + +/* l4_core -> usbhsotg */ +static struct omap_hwmod_ocp_if am35xx_l4_core__usbhsotg = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &am35xx_usbhsotg_hwmod, + .clk = "hsotgusb_ick", + .user = OCP_USER_MPU, +}; + +/* L4_WKUP -> L4_SEC interface */ +static struct omap_hwmod_ocp_if omap3xxx_l4_wkup__l4_sec = { + .master = &omap3xxx_l4_wkup_hwmod, + .slave = &omap3xxx_l4_sec_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* IVA2 <- L3 interface */ +static struct omap_hwmod_ocp_if omap3xxx_l3__iva = { + .master = &omap3xxx_l3_main_hwmod, + .slave = &omap3xxx_iva_hwmod, + .clk = "core_l3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_per -> timer3 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer3 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_timer3_hwmod, + .clk = "gpt3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> timer4 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer4 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_timer4_hwmod, + .clk = "gpt4_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> timer5 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer5 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_timer5_hwmod, + .clk = "gpt5_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> timer6 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer6 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_timer6_hwmod, + .clk = "gpt6_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> timer7 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer7 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_timer7_hwmod, + .clk = "gpt7_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> timer8 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer8 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_timer8_hwmod, + .clk = "gpt8_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> timer9 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__timer9 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_timer9_hwmod, + .clk = "gpt9_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer10 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__timer10 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_timer10_hwmod, + .clk = "gpt10_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> timer11 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__timer11 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_timer11_hwmod, + .clk = "gpt11_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> wd_timer2 */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_wkup__wd_timer2 = { + .master = &omap3xxx_l4_wkup_hwmod, + .slave = &omap3xxx_wd_timer2_hwmod, + .clk = "wdt2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss */ +static struct omap_hwmod_ocp_if omap3430es1_l4_core__dss = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3430es1_dss_core_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3ES1_L4_CORE_FW_DSS_CORE_REGION, + .l4_prot_group = OMAP3_L4_CORE_FW_DSS_PROT_GROUP, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_dss_core_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_DSS_CORE_REGION, + .l4_prot_group = OMAP3_L4_CORE_FW_DSS_PROT_GROUP, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss_dispc */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss_dispc = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_dss_dispc_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_DSS_DISPC_REGION, + .l4_prot_group = OMAP3_L4_CORE_FW_DSS_PROT_GROUP, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss_dsi1 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss_dsi1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_dss_dsi1_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_DSS_DSI_REGION, + .l4_prot_group = OMAP3_L4_CORE_FW_DSS_PROT_GROUP, + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss_rfbi */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss_rfbi = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_dss_rfbi_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_DSS_RFBI_REGION, + .l4_prot_group = OMAP3_L4_CORE_FW_DSS_PROT_GROUP , + .flags = OMAP_FIREWALL_L4, + }, + }, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> dss_venc */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__dss_venc = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_dss_venc_hwmod, + .clk = "dss_ick", + .fw = { + .omap2 = { + .l4_fw_region = OMAP3_L4_CORE_FW_DSS_VENC_REGION, + .l4_prot_group = OMAP3_L4_CORE_FW_DSS_PROT_GROUP, + .flags = OMAP_FIREWALL_L4, + }, + }, + .flags = OCPIF_SWSUP_IDLE, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_wkup -> gpio1 */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_wkup__gpio1 = { + .master = &omap3xxx_l4_wkup_hwmod, + .slave = &omap3xxx_gpio1_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_per -> gpio2 */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_per__gpio2 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_gpio2_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_per -> gpio3 */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_per__gpio3 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_gpio3_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* + * 'mmu' class + * The memory management unit performs virtual to physical address translation + * for its requestors. + */ + +static struct omap_hwmod_class_sysconfig mmu_sysc = { + .rev_offs = 0x000, + .sysc_offs = 0x010, + .syss_offs = 0x014, + .sysc_flags = (SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_mmu_hwmod_class = { + .name = "mmu", + .sysc = &mmu_sysc, +}; + +/* mmu isp */ +static struct omap_hwmod omap3xxx_mmu_isp_hwmod; + +/* l4_core -> mmu isp */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__mmu_isp = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_mmu_isp_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod omap3xxx_mmu_isp_hwmod = { + .name = "mmu_isp", + .class = &omap3xxx_mmu_hwmod_class, + .main_clk = "cam_ick", + .flags = HWMOD_NO_IDLEST, +}; + +/* mmu iva */ + +static struct omap_hwmod omap3xxx_mmu_iva_hwmod; + +static struct omap_hwmod_rst_info omap3xxx_mmu_iva_resets[] = { + { .name = "mmu", .rst_shift = 1, .st_shift = 9 }, +}; + +/* l3_main -> iva mmu */ +static struct omap_hwmod_ocp_if omap3xxx_l3_main__mmu_iva = { + .master = &omap3xxx_l3_main_hwmod, + .slave = &omap3xxx_mmu_iva_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod omap3xxx_mmu_iva_hwmod = { + .name = "mmu_iva", + .class = &omap3xxx_mmu_hwmod_class, + .clkdm_name = "iva2_clkdm", + .rst_lines = omap3xxx_mmu_iva_resets, + .rst_lines_cnt = ARRAY_SIZE(omap3xxx_mmu_iva_resets), + .main_clk = "iva2_ck", + .prcm = { + .omap2 = { + .module_offs = OMAP3430_IVA2_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_IVA2_SHIFT, + }, + }, + .flags = HWMOD_NO_IDLEST, +}; + +/* l4_per -> gpio4 */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_per__gpio4 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_gpio4_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_per -> gpio5 */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_per__gpio5 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_gpio5_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_per -> gpio6 */ + +static struct omap_hwmod_ocp_if omap3xxx_l4_per__gpio6 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_gpio6_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> mcbsp1 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__mcbsp1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_mcbsp1_hwmod, + .clk = "mcbsp1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> mcbsp2 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__mcbsp2 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_mcbsp2_hwmod, + .clk = "mcbsp2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> mcbsp3 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__mcbsp3 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_mcbsp3_hwmod, + .clk = "mcbsp3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> mcbsp4 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__mcbsp4 = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_mcbsp4_hwmod, + .clk = "mcbsp4_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_core -> mcbsp5 */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__mcbsp5 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_mcbsp5_hwmod, + .clk = "mcbsp5_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +/* l4_per -> mcbsp2_sidetone */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__mcbsp2_sidetone = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_mcbsp2_sidetone_hwmod, + .clk = "mcbsp2_ick", + .user = OCP_USER_MPU, +}; + + +/* l4_per -> mcbsp3_sidetone */ +static struct omap_hwmod_ocp_if omap3xxx_l4_per__mcbsp3_sidetone = { + .master = &omap3xxx_l4_per_hwmod, + .slave = &omap3xxx_mcbsp3_sidetone_hwmod, + .clk = "mcbsp3_ick", + .user = OCP_USER_MPU, +}; + +/* l4_core -> mailbox */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__mailbox = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_mailbox_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> mcspi1 interface */ +static struct omap_hwmod_ocp_if omap34xx_l4_core__mcspi1 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap34xx_mcspi1, + .clk = "mcspi1_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> mcspi2 interface */ +static struct omap_hwmod_ocp_if omap34xx_l4_core__mcspi2 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap34xx_mcspi2, + .clk = "mcspi2_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> mcspi3 interface */ +static struct omap_hwmod_ocp_if omap34xx_l4_core__mcspi3 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap34xx_mcspi3, + .clk = "mcspi3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4 core -> mcspi4 interface */ + +static struct omap_hwmod_ocp_if omap34xx_l4_core__mcspi4 = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap34xx_mcspi4, + .clk = "mcspi4_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod_ocp_if omap3xxx_usb_host_hs__l3_main_2 = { + .master = &omap3xxx_usb_host_hs_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .clk = "core_l3_ick", + .user = OCP_USER_MPU, +}; + + +static struct omap_hwmod_ocp_if omap3xxx_l4_core__usb_host_hs = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_usb_host_hs_hwmod, + .clk = "usbhost_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + + +static struct omap_hwmod_ocp_if omap3xxx_l4_core__usb_tll_hs = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_usb_tll_hs_hwmod, + .clk = "usbtll_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> hdq1w interface */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__hdq1w = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_hdq1w_hwmod, + .clk = "hdq_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, + .flags = OMAP_FIREWALL_L4 | OCPIF_SWSUP_IDLE, +}; + +/* am35xx has Davinci MDIO & EMAC */ +static struct omap_hwmod_class am35xx_mdio_class = { + .name = "davinci_mdio", +}; + +static struct omap_hwmod am35xx_mdio_hwmod = { + .name = "davinci_mdio", + .class = &am35xx_mdio_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* + * XXX Should be connected to an IPSS hwmod, not the L3 directly; + * but this will probably require some additional hwmod core support, + * so is left as a future to-do item. + */ +static struct omap_hwmod_ocp_if am35xx_mdio__l3 = { + .master = &am35xx_mdio_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .clk = "emac_fck", + .user = OCP_USER_MPU, +}; + +/* l4_core -> davinci mdio */ +/* + * XXX Should be connected to an IPSS hwmod, not the L4_CORE directly; + * but this will probably require some additional hwmod core support, + * so is left as a future to-do item. + */ +static struct omap_hwmod_ocp_if am35xx_l4_core__mdio = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &am35xx_mdio_hwmod, + .clk = "emac_fck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class am35xx_emac_class = { + .name = "davinci_emac", +}; + +static struct omap_hwmod am35xx_emac_hwmod = { + .name = "davinci_emac", + .class = &am35xx_emac_class, + /* + * According to Mark Greer, the MPU will not return from WFI + * when the EMAC signals an interrupt. + * http://www.spinics.net/lists/arm-kernel/msg174734.html + */ + .flags = (HWMOD_NO_IDLEST | HWMOD_BLOCK_WFI), +}; + +/* l3_core -> davinci emac interface */ +/* + * XXX Should be connected to an IPSS hwmod, not the L3 directly; + * but this will probably require some additional hwmod core support, + * so is left as a future to-do item. + */ +static struct omap_hwmod_ocp_if am35xx_emac__l3 = { + .master = &am35xx_emac_hwmod, + .slave = &omap3xxx_l3_main_hwmod, + .clk = "emac_ick", + .user = OCP_USER_MPU, +}; + +/* l4_core -> davinci emac */ +/* + * XXX Should be connected to an IPSS hwmod, not the L4_CORE directly; + * but this will probably require some additional hwmod core support, + * so is left as a future to-do item. + */ +static struct omap_hwmod_ocp_if am35xx_l4_core__emac = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &am35xx_emac_hwmod, + .clk = "emac_ick", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_ocp_if omap3xxx_l3_main__gpmc = { + .master = &omap3xxx_l3_main_hwmod, + .slave = &omap3xxx_gpmc_hwmod, + .clk = "core_l3_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* l4_core -> SHAM2 (SHA1/MD5) (similar to omap24xx) */ +static struct omap_hwmod_class_sysconfig omap3_sham_sysc = { + .rev_offs = 0x5c, + .sysc_offs = 0x60, + .syss_offs = 0x64, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap3_sham_sysc_fields, +}; + +static struct omap_hwmod_class omap3xxx_sham_class = { + .name = "sham", + .sysc = &omap3_sham_sysc, +}; + + + +static struct omap_hwmod omap3xxx_sham_hwmod = { + .name = "sham", + .main_clk = "sha12_ick", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430_ST_SHA12_SHIFT, + }, + }, + .class = &omap3xxx_sham_class, +}; + + +static struct omap_hwmod_ocp_if omap3xxx_l4_core__sham = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_sham_hwmod, + .clk = "sha12_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +/* + * 'ssi' class + * synchronous serial interface (multichannel and full-duplex serial if) + */ + +static struct omap_hwmod_class_sysconfig omap34xx_ssi_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_MIDLEMODE | + SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class omap3xxx_ssi_hwmod_class = { + .name = "ssi", + .sysc = &omap34xx_ssi_sysc, +}; + +static struct omap_hwmod omap3xxx_ssi_hwmod = { + .name = "ssi", + .class = &omap3xxx_ssi_hwmod_class, + .clkdm_name = "core_l4_clkdm", + .main_clk = "ssi_ssr_fck", + .prcm = { + .omap2 = { + .module_offs = CORE_MOD, + .idlest_reg_id = 1, + .idlest_idle_bit = OMAP3430ES2_ST_SSI_IDLE_SHIFT, + }, + }, +}; + +/* L4 CORE -> SSI */ +static struct omap_hwmod_ocp_if omap3xxx_l4_core__ssi = { + .master = &omap3xxx_l4_core_hwmod, + .slave = &omap3xxx_ssi_hwmod, + .clk = "ssi_ick", + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + +static struct omap_hwmod_ocp_if *omap3xxx_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l3_main__l4_core, + &omap3xxx_l3_main__l4_per, + &omap3xxx_mpu__l3_main, + &omap3xxx_l3_main__l4_debugss, + &omap3xxx_l4_core__l4_wkup, + &omap3xxx_l4_core__mmc3, + &omap3_l4_core__uart1, + &omap3_l4_core__uart2, + &omap3_l4_per__uart3, + &omap3_l4_core__i2c1, + &omap3_l4_core__i2c2, + &omap3_l4_core__i2c3, + &omap3xxx_l4_wkup__l4_sec, + &omap3xxx_l4_per__timer3, + &omap3xxx_l4_per__timer4, + &omap3xxx_l4_per__timer5, + &omap3xxx_l4_per__timer6, + &omap3xxx_l4_per__timer7, + &omap3xxx_l4_per__timer8, + &omap3xxx_l4_per__timer9, + &omap3xxx_l4_core__timer10, + &omap3xxx_l4_core__timer11, + &omap3xxx_l4_wkup__wd_timer2, + &omap3xxx_l4_wkup__gpio1, + &omap3xxx_l4_per__gpio2, + &omap3xxx_l4_per__gpio3, + &omap3xxx_l4_per__gpio4, + &omap3xxx_l4_per__gpio5, + &omap3xxx_l4_per__gpio6, + &omap3xxx_l4_core__mcbsp1, + &omap3xxx_l4_per__mcbsp2, + &omap3xxx_l4_per__mcbsp3, + &omap3xxx_l4_per__mcbsp4, + &omap3xxx_l4_core__mcbsp5, + &omap3xxx_l4_per__mcbsp2_sidetone, + &omap3xxx_l4_per__mcbsp3_sidetone, + &omap34xx_l4_core__mcspi1, + &omap34xx_l4_core__mcspi2, + &omap34xx_l4_core__mcspi3, + &omap34xx_l4_core__mcspi4, + &omap3xxx_l3_main__gpmc, + NULL, +}; + +/* crypto hwmod links */ +static struct omap_hwmod_ocp_if *omap34xx_sham_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l4_core__sham, + NULL, +}; + +static struct omap_hwmod_ocp_if *omap36xx_sham_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l4_core__sham, + NULL +}; + + +/* + * Apparently the SHA/MD5 and AES accelerator IP blocks are + * only present on some AM35xx chips, and no one knows which + * ones. See + * http://www.spinics.net/lists/arm-kernel/msg215466.html So + * if you need these IP blocks on an AM35xx, try uncommenting + * the following lines. + */ +static struct omap_hwmod_ocp_if *am35xx_sham_hwmod_ocp_ifs[] __initdata = { + /* &omap3xxx_l4_core__sham, */ + NULL +}; + +/* 3430ES1-only hwmod links */ +static struct omap_hwmod_ocp_if *omap3430es1_hwmod_ocp_ifs[] __initdata = { + &omap3430es1_dss__l3, + &omap3430es1_l4_core__dss, + NULL, +}; + +/* 3430ES2+-only hwmod links */ +static struct omap_hwmod_ocp_if *omap3430es2plus_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_dss__l3, + &omap3xxx_l4_core__dss, + &omap3xxx_usbhsotg__l3, + &omap3xxx_l4_core__usbhsotg, + &omap3xxx_usb_host_hs__l3_main_2, + &omap3xxx_l4_core__usb_host_hs, + &omap3xxx_l4_core__usb_tll_hs, + NULL, +}; + +/* <= 3430ES3-only hwmod links */ +static struct omap_hwmod_ocp_if *omap3430_pre_es3_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l4_core__pre_es3_mmc1, + &omap3xxx_l4_core__pre_es3_mmc2, + NULL, +}; + +/* 3430ES3+-only hwmod links */ +static struct omap_hwmod_ocp_if *omap3430_es3plus_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l4_core__es3plus_mmc1, + &omap3xxx_l4_core__es3plus_mmc2, + NULL, +}; + +/* 34xx-only hwmod links (all ES revisions) */ +static struct omap_hwmod_ocp_if *omap34xx_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l3__iva, + &omap34xx_l4_core__sr1, + &omap34xx_l4_core__sr2, + &omap3xxx_l4_core__mailbox, + &omap3xxx_l4_core__hdq1w, + &omap3xxx_sad2d__l3, + &omap3xxx_l4_core__mmu_isp, + &omap3xxx_l3_main__mmu_iva, + &omap3xxx_l4_core__ssi, + NULL, +}; + +/* 36xx-only hwmod links (all ES revisions) */ +static struct omap_hwmod_ocp_if *omap36xx_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l3__iva, + &omap36xx_l4_per__uart4, + &omap3xxx_dss__l3, + &omap3xxx_l4_core__dss, + &omap36xx_l4_core__sr1, + &omap36xx_l4_core__sr2, + &omap3xxx_usbhsotg__l3, + &omap3xxx_l4_core__usbhsotg, + &omap3xxx_l4_core__mailbox, + &omap3xxx_usb_host_hs__l3_main_2, + &omap3xxx_l4_core__usb_host_hs, + &omap3xxx_l4_core__usb_tll_hs, + &omap3xxx_l4_core__es3plus_mmc1, + &omap3xxx_l4_core__es3plus_mmc2, + &omap3xxx_l4_core__hdq1w, + &omap3xxx_sad2d__l3, + &omap3xxx_l4_core__mmu_isp, + &omap3xxx_l3_main__mmu_iva, + &omap3xxx_l4_core__ssi, + NULL, +}; + +static struct omap_hwmod_ocp_if *am35xx_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_dss__l3, + &omap3xxx_l4_core__dss, + &am35xx_usbhsotg__l3, + &am35xx_l4_core__usbhsotg, + &am35xx_l4_core__uart4, + &omap3xxx_usb_host_hs__l3_main_2, + &omap3xxx_l4_core__usb_host_hs, + &omap3xxx_l4_core__usb_tll_hs, + &omap3xxx_l4_core__es3plus_mmc1, + &omap3xxx_l4_core__es3plus_mmc2, + &omap3xxx_l4_core__hdq1w, + &am35xx_mdio__l3, + &am35xx_l4_core__mdio, + &am35xx_emac__l3, + &am35xx_l4_core__emac, + NULL, +}; + +static struct omap_hwmod_ocp_if *omap3xxx_dss_hwmod_ocp_ifs[] __initdata = { + &omap3xxx_l4_core__dss_dispc, + &omap3xxx_l4_core__dss_dsi1, + &omap3xxx_l4_core__dss_rfbi, + &omap3xxx_l4_core__dss_venc, + NULL, +}; + +/** + * omap3xxx_hwmod_is_hs_ip_block_usable - is a security IP block accessible? + * @bus: struct device_node * for the top-level OMAP DT data + * @dev_name: device name used in the DT file + * + * Determine whether a "secure" IP block @dev_name is usable by Linux. + * There doesn't appear to be a 100% reliable way to determine this, + * so we rely on heuristics. If @bus is null, meaning there's no DT + * data, then we only assume the IP block is accessible if the OMAP is + * fused as a 'general-purpose' SoC. If however DT data is present, + * test to see if the IP block is described in the DT data and set to + * 'status = "okay"'. If so then we assume the ODM has configured the + * OMAP firewalls to allow access to the IP block. + * + * Return: 0 if device named @dev_name is not likely to be accessible, + * or 1 if it is likely to be accessible. + */ +static bool __init omap3xxx_hwmod_is_hs_ip_block_usable(struct device_node *bus, + const char *dev_name) +{ + struct device_node *node; + bool available; + + if (!bus) + return omap_type() == OMAP2_DEVICE_TYPE_GP; + + node = of_get_child_by_name(bus, dev_name); + available = of_device_is_available(node); + of_node_put(node); + + return available; +} + +int __init omap3xxx_hwmod_init(void) +{ + int r; + struct omap_hwmod_ocp_if **h = NULL, **h_sham = NULL; + struct device_node *bus; + unsigned int rev; + + omap_hwmod_init(); + + /* Register hwmod links common to all OMAP3 */ + r = omap_hwmod_register_links(omap3xxx_hwmod_ocp_ifs); + if (r < 0) + return r; + + rev = omap_rev(); + + /* + * Register hwmod links common to individual OMAP3 families, all + * silicon revisions (e.g., 34xx, or AM3505/3517, or 36xx) + * All possible revisions should be included in this conditional. + */ + if (rev == OMAP3430_REV_ES1_0 || rev == OMAP3430_REV_ES2_0 || + rev == OMAP3430_REV_ES2_1 || rev == OMAP3430_REV_ES3_0 || + rev == OMAP3430_REV_ES3_1 || rev == OMAP3430_REV_ES3_1_2) { + h = omap34xx_hwmod_ocp_ifs; + h_sham = omap34xx_sham_hwmod_ocp_ifs; + } else if (rev == AM35XX_REV_ES1_0 || rev == AM35XX_REV_ES1_1) { + h = am35xx_hwmod_ocp_ifs; + h_sham = am35xx_sham_hwmod_ocp_ifs; + } else if (rev == OMAP3630_REV_ES1_0 || rev == OMAP3630_REV_ES1_1 || + rev == OMAP3630_REV_ES1_2) { + h = omap36xx_hwmod_ocp_ifs; + h_sham = omap36xx_sham_hwmod_ocp_ifs; + } else { + WARN(1, "OMAP3 hwmod family init: unknown chip type\n"); + return -EINVAL; + } + + r = omap_hwmod_register_links(h); + if (r < 0) + return r; + + /* + * Register crypto hwmod links only if they are not disabled in DT. + * If DT information is missing, enable them only for GP devices. + */ + + bus = of_find_node_by_name(NULL, "ocp"); + + if (h_sham && omap3xxx_hwmod_is_hs_ip_block_usable(bus, "sham")) { + r = omap_hwmod_register_links(h_sham); + if (r < 0) + goto put_node; + } + + of_node_put(bus); + + /* + * Register hwmod links specific to certain ES levels of a + * particular family of silicon (e.g., 34xx ES1.0) + */ + h = NULL; + if (rev == OMAP3430_REV_ES1_0) { + h = omap3430es1_hwmod_ocp_ifs; + } else if (rev == OMAP3430_REV_ES2_0 || rev == OMAP3430_REV_ES2_1 || + rev == OMAP3430_REV_ES3_0 || rev == OMAP3430_REV_ES3_1 || + rev == OMAP3430_REV_ES3_1_2) { + h = omap3430es2plus_hwmod_ocp_ifs; + } + + if (h) { + r = omap_hwmod_register_links(h); + if (r < 0) + return r; + } + + h = NULL; + if (rev == OMAP3430_REV_ES1_0 || rev == OMAP3430_REV_ES2_0 || + rev == OMAP3430_REV_ES2_1) { + h = omap3430_pre_es3_hwmod_ocp_ifs; + } else if (rev == OMAP3430_REV_ES3_0 || rev == OMAP3430_REV_ES3_1 || + rev == OMAP3430_REV_ES3_1_2) { + h = omap3430_es3plus_hwmod_ocp_ifs; + } + + if (h) + r = omap_hwmod_register_links(h); + if (r < 0) + return r; + + /* + * DSS code presumes that dss_core hwmod is handled first, + * _before_ any other DSS related hwmods so register common + * DSS hwmod links last to ensure that dss_core is already + * registered. Otherwise some change things may happen, for + * ex. if dispc is handled before dss_core and DSS is enabled + * in bootloader DISPC will be reset with outputs enabled + * which sometimes leads to unrecoverable L3 error. XXX The + * long-term fix to this is to ensure hwmods are set up in + * dependency order in the hwmod core code. + */ + r = omap_hwmod_register_links(omap3xxx_dss_hwmod_ocp_ifs); + + return r; + +put_node: + of_node_put(bus); + return r; +} diff --git a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c new file mode 100644 index 000000000..9b5c728fb --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c @@ -0,0 +1,1261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DM81xx hwmod data. + * + * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ + * Copyright (C) 2013 SKTB SKiT, http://www.skitlab.ru/ + */ + +#include <linux/types.h> + +#include <linux/platform_data/hsmmc-omap.h> + +#include "omap_hwmod_common_data.h" +#include "cm81xx.h" +#include "ti81xx.h" +#include "wd_timer.h" + +/* + * DM816X hardware modules integration data + * + * Note: This is incomplete and at present, not generated from h/w database. + */ + +/* + * Common alwon .clkctrl_offs from dm814x TRM "Table 2-278. CM_ALWON REGISTERS" + * also dm816x TRM 18.7.17 CM_ALWON device register values minus 0x1400. + */ +#define DM81XX_CM_ALWON_MCASP0_CLKCTRL 0x140 +#define DM81XX_CM_ALWON_MCASP1_CLKCTRL 0x144 +#define DM81XX_CM_ALWON_MCASP2_CLKCTRL 0x148 +#define DM81XX_CM_ALWON_MCBSP_CLKCTRL 0x14c +#define DM81XX_CM_ALWON_UART_0_CLKCTRL 0x150 +#define DM81XX_CM_ALWON_UART_1_CLKCTRL 0x154 +#define DM81XX_CM_ALWON_UART_2_CLKCTRL 0x158 +#define DM81XX_CM_ALWON_GPIO_0_CLKCTRL 0x15c +#define DM81XX_CM_ALWON_GPIO_1_CLKCTRL 0x160 +#define DM81XX_CM_ALWON_I2C_0_CLKCTRL 0x164 +#define DM81XX_CM_ALWON_I2C_1_CLKCTRL 0x168 +#define DM81XX_CM_ALWON_WDTIMER_CLKCTRL 0x18c +#define DM81XX_CM_ALWON_SPI_CLKCTRL 0x190 +#define DM81XX_CM_ALWON_MAILBOX_CLKCTRL 0x194 +#define DM81XX_CM_ALWON_SPINBOX_CLKCTRL 0x198 +#define DM81XX_CM_ALWON_MMUDATA_CLKCTRL 0x19c +#define DM81XX_CM_ALWON_MMUCFG_CLKCTRL 0x1a8 +#define DM81XX_CM_ALWON_CONTROL_CLKCTRL 0x1c4 +#define DM81XX_CM_ALWON_GPMC_CLKCTRL 0x1d0 +#define DM81XX_CM_ALWON_ETHERNET_0_CLKCTRL 0x1d4 +#define DM81XX_CM_ALWON_L3_CLKCTRL 0x1e4 +#define DM81XX_CM_ALWON_L4HS_CLKCTRL 0x1e8 +#define DM81XX_CM_ALWON_L4LS_CLKCTRL 0x1ec +#define DM81XX_CM_ALWON_RTC_CLKCTRL 0x1f0 +#define DM81XX_CM_ALWON_TPCC_CLKCTRL 0x1f4 +#define DM81XX_CM_ALWON_TPTC0_CLKCTRL 0x1f8 +#define DM81XX_CM_ALWON_TPTC1_CLKCTRL 0x1fc +#define DM81XX_CM_ALWON_TPTC2_CLKCTRL 0x200 +#define DM81XX_CM_ALWON_TPTC3_CLKCTRL 0x204 + +/* Registers specific to dm814x */ +#define DM814X_CM_ALWON_MCASP_3_4_5_CLKCTRL 0x16c +#define DM814X_CM_ALWON_ATL_CLKCTRL 0x170 +#define DM814X_CM_ALWON_MLB_CLKCTRL 0x174 +#define DM814X_CM_ALWON_PATA_CLKCTRL 0x178 +#define DM814X_CM_ALWON_UART_3_CLKCTRL 0x180 +#define DM814X_CM_ALWON_UART_4_CLKCTRL 0x184 +#define DM814X_CM_ALWON_UART_5_CLKCTRL 0x188 +#define DM814X_CM_ALWON_OCM_0_CLKCTRL 0x1b4 +#define DM814X_CM_ALWON_VCP_CLKCTRL 0x1b8 +#define DM814X_CM_ALWON_MPU_CLKCTRL 0x1dc +#define DM814X_CM_ALWON_DEBUGSS_CLKCTRL 0x1e0 +#define DM814X_CM_ALWON_DCAN_0_1_CLKCTRL 0x218 +#define DM814X_CM_ALWON_MMCHS_0_CLKCTRL 0x21c +#define DM814X_CM_ALWON_MMCHS_1_CLKCTRL 0x220 +#define DM814X_CM_ALWON_MMCHS_2_CLKCTRL 0x224 +#define DM814X_CM_ALWON_CUST_EFUSE_CLKCTRL 0x228 + +/* Registers specific to dm816x */ +#define DM816X_DM_ALWON_BASE 0x1400 +#define DM816X_CM_ALWON_TIMER_1_CLKCTRL (0x1570 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_TIMER_2_CLKCTRL (0x1574 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_TIMER_3_CLKCTRL (0x1578 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_TIMER_4_CLKCTRL (0x157c - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_TIMER_5_CLKCTRL (0x1580 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_TIMER_6_CLKCTRL (0x1584 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_TIMER_7_CLKCTRL (0x1588 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_SDIO_CLKCTRL (0x15b0 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_OCMC_0_CLKCTRL (0x15b4 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_OCMC_1_CLKCTRL (0x15b8 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_ETHERNET_1_CLKCTRL (0x15d8 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_MPU_CLKCTRL (0x15dc - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_SR_0_CLKCTRL (0x1608 - DM816X_DM_ALWON_BASE) +#define DM816X_CM_ALWON_SR_1_CLKCTRL (0x160c - DM816X_DM_ALWON_BASE) + +/* + * The default .clkctrl_offs field is offset from CM_DEFAULT, that's + * TRM 18.7.6 CM_DEFAULT device register values minus 0x500 + */ +#define DM81XX_CM_DEFAULT_OFFSET 0x500 +#define DM81XX_CM_DEFAULT_USB_CLKCTRL (0x558 - DM81XX_CM_DEFAULT_OFFSET) +#define DM81XX_CM_DEFAULT_SATA_CLKCTRL (0x560 - DM81XX_CM_DEFAULT_OFFSET) + +/* L3 Interconnect entries clocked at 125, 250 and 500MHz */ +static struct omap_hwmod dm81xx_alwon_l3_slow_hwmod = { + .name = "alwon_l3_slow", + .clkdm_name = "alwon_l3s_clkdm", + .class = &l3_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +static struct omap_hwmod dm81xx_default_l3_slow_hwmod = { + .name = "default_l3_slow", + .clkdm_name = "default_l3_slow_clkdm", + .class = &l3_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +static struct omap_hwmod dm81xx_alwon_l3_med_hwmod = { + .name = "l3_med", + .clkdm_name = "alwon_l3_med_clkdm", + .class = &l3_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* + * L4 standard peripherals, see TRM table 1-12 for devices using this. + * See TRM table 1-73 for devices using the 125MHz SYSCLK6 clock. + */ +static struct omap_hwmod dm81xx_l4_ls_hwmod = { + .name = "l4_ls", + .clkdm_name = "alwon_l3s_clkdm", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* + * L4 high-speed peripherals. For devices using this, please see the TRM + * table 1-13. On dm816x, only EMAC, MDIO and SATA use this. See also TRM + * table 1-73 for devices using 250MHz SYSCLK5 clock. + */ +static struct omap_hwmod dm81xx_l4_hs_hwmod = { + .name = "l4_hs", + .clkdm_name = "alwon_l3_med_clkdm", + .class = &l4_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +/* L3 slow -> L4 ls peripheral interface running at 125MHz */ +static struct omap_hwmod_ocp_if dm81xx_alwon_l3_slow__l4_ls = { + .master = &dm81xx_alwon_l3_slow_hwmod, + .slave = &dm81xx_l4_ls_hwmod, + .user = OCP_USER_MPU, +}; + +/* L3 med -> L4 fast peripheral interface running at 250MHz */ +static struct omap_hwmod_ocp_if dm81xx_alwon_l3_slow__l4_hs = { + .master = &dm81xx_alwon_l3_med_hwmod, + .slave = &dm81xx_l4_hs_hwmod, + .user = OCP_USER_MPU, +}; + +/* MPU */ +static struct omap_hwmod dm814x_mpu_hwmod = { + .name = "mpu", + .clkdm_name = "alwon_l3s_clkdm", + .class = &mpu_hwmod_class, + .flags = HWMOD_INIT_NO_IDLE, + .main_clk = "mpu_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM814X_CM_ALWON_MPU_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +static struct omap_hwmod_ocp_if dm814x_mpu__alwon_l3_slow = { + .master = &dm814x_mpu_hwmod, + .slave = &dm81xx_alwon_l3_slow_hwmod, + .user = OCP_USER_MPU, +}; + +/* L3 med peripheral interface running at 200MHz */ +static struct omap_hwmod_ocp_if dm814x_mpu__alwon_l3_med = { + .master = &dm814x_mpu_hwmod, + .slave = &dm81xx_alwon_l3_med_hwmod, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_mpu_hwmod = { + .name = "mpu", + .clkdm_name = "alwon_mpu_clkdm", + .class = &mpu_hwmod_class, + .flags = HWMOD_INIT_NO_IDLE, + .main_clk = "mpu_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_MPU_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +static struct omap_hwmod_ocp_if dm816x_mpu__alwon_l3_slow = { + .master = &dm816x_mpu_hwmod, + .slave = &dm81xx_alwon_l3_slow_hwmod, + .user = OCP_USER_MPU, +}; + +/* L3 med peripheral interface running at 250MHz */ +static struct omap_hwmod_ocp_if dm816x_mpu__alwon_l3_med = { + .master = &dm816x_mpu_hwmod, + .slave = &dm81xx_alwon_l3_med_hwmod, + .user = OCP_USER_MPU, +}; + +/* RTC */ +static struct omap_hwmod_class_sysconfig ti81xx_rtc_sysc = { + .rev_offs = 0x74, + .sysc_offs = 0x78, + .sysc_flags = SYSC_HAS_SIDLEMODE, + .idlemodes = SIDLE_FORCE | SIDLE_NO | + SIDLE_SMART | SIDLE_SMART_WKUP, + .sysc_fields = &omap_hwmod_sysc_type3, +}; + +static struct omap_hwmod_class ti81xx_rtc_hwmod_class = { + .name = "rtc", + .sysc = &ti81xx_rtc_sysc, +}; + +static struct omap_hwmod ti81xx_rtc_hwmod = { + .name = "rtc", + .class = &ti81xx_rtc_hwmod_class, + .clkdm_name = "alwon_l3s_clkdm", + .flags = HWMOD_NO_IDLEST, + .main_clk = "sysclk18_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_RTC_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +static struct omap_hwmod_ocp_if ti81xx_l4_ls__rtc = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &ti81xx_rtc_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +/* UART common */ +static struct omap_hwmod_class_sysconfig uart_sysc = { + .rev_offs = 0x50, + .sysc_offs = 0x54, + .syss_offs = 0x58, + .sysc_flags = SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + MSTANDBY_SMART_WKUP, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class uart_class = { + .name = "uart", + .sysc = &uart_sysc, +}; + +static struct omap_hwmod dm81xx_uart1_hwmod = { + .name = "uart1", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_UART_0_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &uart_class, + .flags = DEBUG_TI81XXUART1_FLAGS, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__uart1 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_uart1_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm81xx_uart2_hwmod = { + .name = "uart2", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_UART_1_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &uart_class, + .flags = DEBUG_TI81XXUART2_FLAGS, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__uart2 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_uart2_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm81xx_uart3_hwmod = { + .name = "uart3", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_UART_2_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &uart_class, + .flags = DEBUG_TI81XXUART3_FLAGS, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__uart3 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_uart3_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig wd_timer_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x10, + .syss_offs = 0x14, + .sysc_flags = SYSC_HAS_EMUFREE | SYSC_HAS_SOFTRESET | + SYSS_HAS_RESET_STATUS, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class wd_timer_class = { + .name = "wd_timer", + .sysc = &wd_timer_sysc, + .pre_shutdown = &omap2_wd_timer_disable, + .reset = &omap2_wd_timer_reset, +}; + +static struct omap_hwmod dm81xx_wd_timer_hwmod = { + .name = "wd_timer", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk18_ck", + .flags = HWMOD_NO_IDLEST, + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_WDTIMER_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &wd_timer_class, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__wd_timer1 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_wd_timer_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +/* I2C common */ +static struct omap_hwmod_class_sysconfig i2c_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x10, + .syss_offs = 0x90, + .sysc_flags = SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class i2c_class = { + .name = "i2c", + .sysc = &i2c_sysc, +}; + +static struct omap_hwmod dm81xx_i2c1_hwmod = { + .name = "i2c1", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_I2C_0_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &i2c_class, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__i2c1 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_i2c1_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm81xx_i2c2_hwmod = { + .name = "i2c2", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_I2C_1_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &i2c_class, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__i2c2 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_i2c2_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm81xx_elm_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | + SYSS_HAS_RESET_STATUS, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class dm81xx_elm_hwmod_class = { + .name = "elm", + .sysc = &dm81xx_elm_sysc, +}; + +static struct omap_hwmod dm81xx_elm_hwmod = { + .name = "elm", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_elm_hwmod_class, + .main_clk = "sysclk6_ck", +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__elm = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_elm_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm81xx_gpio_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0114, + .sysc_flags = SYSC_HAS_AUTOIDLE | SYSC_HAS_ENAWAKEUP | + SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET | + SYSS_HAS_RESET_STATUS, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + SIDLE_SMART_WKUP, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class dm81xx_gpio_hwmod_class = { + .name = "gpio", + .sysc = &dm81xx_gpio_sysc, +}; + +static struct omap_hwmod_opt_clk gpio1_opt_clks[] = { + { .role = "dbclk", .clk = "sysclk18_ck" }, +}; + +static struct omap_hwmod dm81xx_gpio1_hwmod = { + .name = "gpio1", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_gpio_hwmod_class, + .main_clk = "sysclk6_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_GPIO_0_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .opt_clks = gpio1_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio1_opt_clks), +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__gpio1 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_gpio1_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_opt_clk gpio2_opt_clks[] = { + { .role = "dbclk", .clk = "sysclk18_ck" }, +}; + +static struct omap_hwmod dm81xx_gpio2_hwmod = { + .name = "gpio2", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_gpio_hwmod_class, + .main_clk = "sysclk6_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_GPIO_1_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .opt_clks = gpio2_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio2_opt_clks), +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__gpio2 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_gpio2_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_opt_clk gpio3_opt_clks[] = { + { .role = "dbclk", .clk = "sysclk18_ck" }, +}; + +static struct omap_hwmod dm81xx_gpio3_hwmod = { + .name = "gpio3", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_gpio_hwmod_class, + .main_clk = "sysclk6_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_GPIO_1_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .opt_clks = gpio3_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio3_opt_clks), +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__gpio3 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_gpio3_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_opt_clk gpio4_opt_clks[] = { + { .role = "dbclk", .clk = "sysclk18_ck" }, +}; + +static struct omap_hwmod dm81xx_gpio4_hwmod = { + .name = "gpio4", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_gpio_hwmod_class, + .main_clk = "sysclk6_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_GPIO_1_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .opt_clks = gpio4_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(gpio4_opt_clks), +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__gpio4 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_gpio4_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm81xx_gpmc_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x10, + .syss_offs = 0x14, + .sysc_flags = SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class dm81xx_gpmc_hwmod_class = { + .name = "gpmc", + .sysc = &dm81xx_gpmc_sysc, +}; + +static struct omap_hwmod dm81xx_gpmc_hwmod = { + .name = "gpmc", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_gpmc_hwmod_class, + .main_clk = "sysclk6_ck", + /* Skip reset for CONFIG_OMAP_GPMC_DEBUG for bootloader timings */ + .flags = DEBUG_OMAP_GPMC_HWMOD_FLAGS, + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_GPMC_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +static struct omap_hwmod_ocp_if dm81xx_alwon_l3_slow__gpmc = { + .master = &dm81xx_alwon_l3_slow_hwmod, + .slave = &dm81xx_gpmc_hwmod, + .user = OCP_USER_MPU, +}; + +/* USB needs udelay 1 after reset at least on hp t410, use 2 for margin */ +static struct omap_hwmod_class_sysconfig dm81xx_usbhsotg_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x10, + .srst_udelay = 2, + .sysc_flags = SYSC_HAS_SIDLEMODE | SYSC_HAS_MIDLEMODE | + SYSC_HAS_SOFTRESET, + .idlemodes = SIDLE_SMART | MSTANDBY_FORCE | MSTANDBY_SMART, + .sysc_fields = &omap_hwmod_sysc_type2, +}; + +static struct omap_hwmod_class dm81xx_usbotg_class = { + .name = "usbotg", + .sysc = &dm81xx_usbhsotg_sysc, +}; + +static struct omap_hwmod dm814x_usbss_hwmod = { + .name = "usb_otg_hs", + .clkdm_name = "default_l3_slow_clkdm", + .main_clk = "pll260dcoclkldo", /* 481c5260.adpll.dcoclkldo */ + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_DEFAULT_USB_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm81xx_usbotg_class, +}; + +static struct omap_hwmod_ocp_if dm814x_default_l3_slow__usbss = { + .master = &dm81xx_default_l3_slow_hwmod, + .slave = &dm814x_usbss_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_usbss_hwmod = { + .name = "usb_otg_hs", + .clkdm_name = "default_l3_slow_clkdm", + .main_clk = "sysclk6_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_DEFAULT_USB_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm81xx_usbotg_class, +}; + +static struct omap_hwmod_ocp_if dm816x_default_l3_slow__usbss = { + .master = &dm81xx_default_l3_slow_hwmod, + .slave = &dm816x_usbss_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm816x_timer_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART | + SIDLE_SMART_WKUP, + .sysc_fields = &omap_hwmod_sysc_type2, +}; + +static struct omap_hwmod_class dm816x_timer_hwmod_class = { + .name = "timer", + .sysc = &dm816x_timer_sysc, +}; + +static struct omap_hwmod dm816x_timer3_hwmod = { + .name = "timer3", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "timer3_fck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_TIMER_3_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_timer_hwmod_class, +}; + +static struct omap_hwmod_ocp_if dm816x_l4_ls__timer3 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm816x_timer3_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_timer4_hwmod = { + .name = "timer4", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "timer4_fck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_TIMER_4_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_timer_hwmod_class, +}; + +static struct omap_hwmod_ocp_if dm816x_l4_ls__timer4 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm816x_timer4_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_timer5_hwmod = { + .name = "timer5", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "timer5_fck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_TIMER_5_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_timer_hwmod_class, +}; + +static struct omap_hwmod_ocp_if dm816x_l4_ls__timer5 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm816x_timer5_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_timer6_hwmod = { + .name = "timer6", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "timer6_fck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_TIMER_6_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_timer_hwmod_class, +}; + +static struct omap_hwmod_ocp_if dm816x_l4_ls__timer6 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm816x_timer6_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_timer7_hwmod = { + .name = "timer7", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "timer7_fck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_TIMER_7_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_timer_hwmod_class, +}; + +static struct omap_hwmod_ocp_if dm816x_l4_ls__timer7 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm816x_timer7_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +/* EMAC Ethernet */ +static struct omap_hwmod_class_sysconfig dm816x_emac_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x4, + .sysc_flags = SYSC_HAS_SOFTRESET, + .sysc_fields = &omap_hwmod_sysc_type2, +}; + +static struct omap_hwmod_class dm816x_emac_hwmod_class = { + .name = "emac", + .sysc = &dm816x_emac_sysc, +}; + +/* + * On dm816x the MDIO is within EMAC0. As the MDIO driver is a separate + * driver probed before EMAC0, we let MDIO do the clock idling. + */ +static struct omap_hwmod dm816x_emac0_hwmod = { + .name = "emac0", + .clkdm_name = "alwon_ethernet_clkdm", + .class = &dm816x_emac_hwmod_class, + .flags = HWMOD_NO_IDLEST, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_hs__emac0 = { + .master = &dm81xx_l4_hs_hwmod, + .slave = &dm816x_emac0_hwmod, + .clk = "sysclk5_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class dm81xx_mdio_hwmod_class = { + .name = "davinci_mdio", + .sysc = &dm816x_emac_sysc, +}; + +static struct omap_hwmod dm81xx_emac0_mdio_hwmod = { + .name = "davinci_mdio", + .class = &dm81xx_mdio_hwmod_class, + .clkdm_name = "alwon_ethernet_clkdm", + .main_clk = "sysclk24_ck", + .flags = HWMOD_NO_IDLEST, + /* + * REVISIT: This should be moved to the emac0_hwmod + * once we have a better way to handle device slaves. + */ + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_ETHERNET_0_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +static struct omap_hwmod_ocp_if dm81xx_emac0__mdio = { + .master = &dm81xx_l4_hs_hwmod, + .slave = &dm81xx_emac0_mdio_hwmod, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_emac1_hwmod = { + .name = "emac1", + .clkdm_name = "alwon_ethernet_clkdm", + .main_clk = "sysclk24_ck", + .flags = HWMOD_NO_IDLEST, + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_ETHERNET_1_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_emac_hwmod_class, +}; + +static struct omap_hwmod_ocp_if dm816x_l4_hs__emac1 = { + .master = &dm81xx_l4_hs_hwmod, + .slave = &dm816x_emac1_hwmod, + .clk = "sysclk5_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm81xx_sata_sysc = { + .rev_offs = 0x00fc, + .sysc_offs = 0x1100, + .sysc_flags = SYSC_HAS_SIDLEMODE, + .idlemodes = SIDLE_FORCE, + .sysc_fields = &omap_hwmod_sysc_type3, +}; + +static struct omap_hwmod_class dm81xx_sata_hwmod_class = { + .name = "sata", + .sysc = &dm81xx_sata_sysc, +}; + +static struct omap_hwmod dm81xx_sata_hwmod = { + .name = "sata", + .clkdm_name = "default_clkdm", + .flags = HWMOD_NO_IDLEST, + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_DEFAULT_SATA_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm81xx_sata_hwmod_class, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_hs__sata = { + .master = &dm81xx_l4_hs_hwmod, + .slave = &dm81xx_sata_hwmod, + .clk = "sysclk5_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm81xx_mmc_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x110, + .syss_offs = 0x114, + .sysc_flags = SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class dm81xx_mmc_class = { + .name = "mmc", + .sysc = &dm81xx_mmc_sysc, +}; + +static struct omap_hwmod_opt_clk dm81xx_mmc_opt_clks[] = { + { .role = "dbck", .clk = "sysclk18_ck", }, +}; + +static struct omap_hsmmc_dev_attr mmc_dev_attr = { +}; + +static struct omap_hwmod dm814x_mmc1_hwmod = { + .name = "mmc1", + .clkdm_name = "alwon_l3s_clkdm", + .opt_clks = dm81xx_mmc_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dm81xx_mmc_opt_clks), + .main_clk = "sysclk8_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM814X_CM_ALWON_MMCHS_0_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .dev_attr = &mmc_dev_attr, + .class = &dm81xx_mmc_class, +}; + +static struct omap_hwmod_ocp_if dm814x_l4_ls__mmc1 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm814x_mmc1_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, + .flags = OMAP_FIREWALL_L4 +}; + +static struct omap_hwmod dm814x_mmc2_hwmod = { + .name = "mmc2", + .clkdm_name = "alwon_l3s_clkdm", + .opt_clks = dm81xx_mmc_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dm81xx_mmc_opt_clks), + .main_clk = "sysclk8_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM814X_CM_ALWON_MMCHS_1_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .dev_attr = &mmc_dev_attr, + .class = &dm81xx_mmc_class, +}; + +static struct omap_hwmod_ocp_if dm814x_l4_ls__mmc2 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm814x_mmc2_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, + .flags = OMAP_FIREWALL_L4 +}; + +static struct omap_hwmod dm814x_mmc3_hwmod = { + .name = "mmc3", + .clkdm_name = "alwon_l3_med_clkdm", + .opt_clks = dm81xx_mmc_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dm81xx_mmc_opt_clks), + .main_clk = "sysclk8_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM814X_CM_ALWON_MMCHS_2_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .dev_attr = &mmc_dev_attr, + .class = &dm81xx_mmc_class, +}; + +static struct omap_hwmod_ocp_if dm814x_alwon_l3_med__mmc3 = { + .master = &dm81xx_alwon_l3_med_hwmod, + .slave = &dm814x_mmc3_hwmod, + .clk = "sysclk4_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod dm816x_mmc1_hwmod = { + .name = "mmc1", + .clkdm_name = "alwon_l3s_clkdm", + .opt_clks = dm81xx_mmc_opt_clks, + .opt_clks_cnt = ARRAY_SIZE(dm81xx_mmc_opt_clks), + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM816X_CM_ALWON_SDIO_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .dev_attr = &mmc_dev_attr, + .class = &dm81xx_mmc_class, +}; + +static struct omap_hwmod_ocp_if dm816x_l4_ls__mmc1 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm816x_mmc1_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, + .flags = OMAP_FIREWALL_L4 +}; + +static struct omap_hwmod_class_sysconfig dm816x_mcspi_sysc = { + .rev_offs = 0x0, + .sysc_offs = 0x110, + .syss_offs = 0x114, + .sysc_flags = SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_ENAWAKEUP | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE | SYSS_HAS_RESET_STATUS, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class dm816x_mcspi_class = { + .name = "mcspi", + .sysc = &dm816x_mcspi_sysc, +}; + +static struct omap_hwmod dm81xx_mcspi1_hwmod = { + .name = "mcspi1", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_SPI_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_mcspi_class, +}; + +static struct omap_hwmod dm81xx_mcspi2_hwmod = { + .name = "mcspi2", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_SPI_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_mcspi_class, +}; + +static struct omap_hwmod dm81xx_mcspi3_hwmod = { + .name = "mcspi3", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_SPI_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_mcspi_class, +}; + +static struct omap_hwmod dm81xx_mcspi4_hwmod = { + .name = "mcspi4", + .clkdm_name = "alwon_l3s_clkdm", + .main_clk = "sysclk10_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_SPI_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, + .class = &dm816x_mcspi_class, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__mcspi1 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_mcspi1_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__mcspi2 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_mcspi2_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__mcspi3 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_mcspi3_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__mcspi4 = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_mcspi4_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm81xx_mailbox_sysc = { + .rev_offs = 0x000, + .sysc_offs = 0x010, + .syss_offs = 0x014, + .sysc_flags = SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class dm81xx_mailbox_hwmod_class = { + .name = "mailbox", + .sysc = &dm81xx_mailbox_sysc, +}; + +static struct omap_hwmod dm81xx_mailbox_hwmod = { + .name = "mailbox", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_mailbox_hwmod_class, + .main_clk = "sysclk6_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_MAILBOX_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__mailbox = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_mailbox_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_class_sysconfig dm81xx_spinbox_sysc = { + .rev_offs = 0x000, + .sysc_offs = 0x010, + .syss_offs = 0x014, + .sysc_flags = SYSC_HAS_CLOCKACTIVITY | SYSC_HAS_SIDLEMODE | + SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE, + .idlemodes = SIDLE_FORCE | SIDLE_NO | SIDLE_SMART, + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +static struct omap_hwmod_class dm81xx_spinbox_hwmod_class = { + .name = "spinbox", + .sysc = &dm81xx_spinbox_sysc, +}; + +static struct omap_hwmod dm81xx_spinbox_hwmod = { + .name = "spinbox", + .clkdm_name = "alwon_l3s_clkdm", + .class = &dm81xx_spinbox_hwmod_class, + .main_clk = "sysclk6_ck", + .prcm = { + .omap4 = { + .clkctrl_offs = DM81XX_CM_ALWON_SPINBOX_CLKCTRL, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +static struct omap_hwmod_ocp_if dm81xx_l4_ls__spinbox = { + .master = &dm81xx_l4_ls_hwmod, + .slave = &dm81xx_spinbox_hwmod, + .clk = "sysclk6_ck", + .user = OCP_USER_MPU, +}; + +/* + * REVISIT: Test and enable the following once clocks work: + * dm81xx_l4_ls__mailbox + * + * Also note that some devices share a single clkctrl_offs.. + * For example, i2c1 and 3 share one, and i2c2 and 4 share one. + */ +static struct omap_hwmod_ocp_if *dm814x_hwmod_ocp_ifs[] __initdata = { + &dm814x_mpu__alwon_l3_slow, + &dm814x_mpu__alwon_l3_med, + &dm81xx_alwon_l3_slow__l4_ls, + &dm81xx_alwon_l3_slow__l4_hs, + &dm81xx_l4_ls__uart1, + &dm81xx_l4_ls__uart2, + &dm81xx_l4_ls__uart3, + &dm81xx_l4_ls__wd_timer1, + &dm81xx_l4_ls__i2c1, + &dm81xx_l4_ls__i2c2, + &dm81xx_l4_ls__gpio1, + &dm81xx_l4_ls__gpio2, + &dm81xx_l4_ls__gpio3, + &dm81xx_l4_ls__gpio4, + &dm81xx_l4_ls__elm, + &dm81xx_l4_ls__mcspi1, + &dm81xx_l4_ls__mcspi2, + &dm81xx_l4_ls__mcspi3, + &dm81xx_l4_ls__mcspi4, + &dm814x_l4_ls__mmc1, + &dm814x_l4_ls__mmc2, + &ti81xx_l4_ls__rtc, + &dm81xx_alwon_l3_slow__gpmc, + &dm814x_default_l3_slow__usbss, + &dm814x_alwon_l3_med__mmc3, + NULL, +}; + +int __init dm814x_hwmod_init(void) +{ + omap_hwmod_init(); + return omap_hwmod_register_links(dm814x_hwmod_ocp_ifs); +} + +static struct omap_hwmod_ocp_if *dm816x_hwmod_ocp_ifs[] __initdata = { + &dm816x_mpu__alwon_l3_slow, + &dm816x_mpu__alwon_l3_med, + &dm81xx_alwon_l3_slow__l4_ls, + &dm81xx_alwon_l3_slow__l4_hs, + &dm81xx_l4_ls__uart1, + &dm81xx_l4_ls__uart2, + &dm81xx_l4_ls__uart3, + &dm81xx_l4_ls__wd_timer1, + &dm81xx_l4_ls__i2c1, + &dm81xx_l4_ls__i2c2, + &dm81xx_l4_ls__gpio1, + &dm81xx_l4_ls__gpio2, + &dm81xx_l4_ls__elm, + &ti81xx_l4_ls__rtc, + &dm816x_l4_ls__mmc1, + &dm816x_l4_ls__timer3, + &dm816x_l4_ls__timer4, + &dm816x_l4_ls__timer5, + &dm816x_l4_ls__timer6, + &dm816x_l4_ls__timer7, + &dm81xx_l4_ls__mcspi1, + &dm81xx_l4_ls__mailbox, + &dm81xx_l4_ls__spinbox, + &dm81xx_l4_hs__emac0, + &dm81xx_emac0__mdio, + &dm816x_l4_hs__emac1, + &dm81xx_l4_hs__sata, + &dm81xx_alwon_l3_slow__gpmc, + &dm816x_default_l3_slow__usbss, + NULL, +}; + +int __init dm816x_hwmod_init(void) +{ + omap_hwmod_init(); + return omap_hwmod_register_links(dm816x_hwmod_ocp_ifs); +} diff --git a/arch/arm/mach-omap2/omap_hwmod_common_data.c b/arch/arm/mach-omap2/omap_hwmod_common_data.c new file mode 100644 index 000000000..246f1e5da --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_common_data.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod common data structures + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Thara Gopinath <thara@ti.com> + * Benoît Cousson + * + * Copyright (C) 2010 Nokia Corporation + * Paul Walmsley + * + * This data/structures are to be used while defining OMAP on-chip module + * data and their integration with other OMAP modules and Linux. + */ + +#include <linux/types.h> +#include <linux/platform_data/ti-sysc.h> + +#include "omap_hwmod.h" + +#include "omap_hwmod_common_data.h" + +/** + * struct omap_hwmod_sysc_type1 - TYPE1 sysconfig scheme. + * + * To be used by hwmod structure to specify the sysconfig offsets + * if the device ip is compliant with the original PRCM protocol + * defined for OMAP2420. + */ +struct sysc_regbits omap_hwmod_sysc_type1 = { + .midle_shift = SYSC_TYPE1_MIDLEMODE_SHIFT, + .clkact_shift = SYSC_TYPE1_CLOCKACTIVITY_SHIFT, + .sidle_shift = SYSC_TYPE1_SIDLEMODE_SHIFT, + .enwkup_shift = SYSC_TYPE1_ENAWAKEUP_SHIFT, + .srst_shift = SYSC_TYPE1_SOFTRESET_SHIFT, + .autoidle_shift = SYSC_TYPE1_AUTOIDLE_SHIFT, +}; + +/** + * struct omap_hwmod_sysc_type2 - TYPE2 sysconfig scheme. + * + * To be used by hwmod structure to specify the sysconfig offsets if the + * device ip is compliant with the new PRCM protocol defined for new + * OMAP4 IPs. + */ +struct sysc_regbits omap_hwmod_sysc_type2 = { + .midle_shift = SYSC_TYPE2_MIDLEMODE_SHIFT, + .sidle_shift = SYSC_TYPE2_SIDLEMODE_SHIFT, + .srst_shift = SYSC_TYPE2_SOFTRESET_SHIFT, + .dmadisable_shift = SYSC_TYPE2_DMADISABLE_SHIFT, +}; + +/** + * struct omap_hwmod_sysc_type3 - TYPE3 sysconfig scheme. + * Used by some IPs on AM33xx + */ +struct sysc_regbits omap_hwmod_sysc_type3 = { + .midle_shift = SYSC_TYPE3_MIDLEMODE_SHIFT, + .sidle_shift = SYSC_TYPE3_SIDLEMODE_SHIFT, +}; + +struct omap_dss_dispc_dev_attr omap2_3_dss_dispc_dev_attr = { + .manager_count = 2, + .has_framedonetv_irq = 0 +}; + +struct sysc_regbits omap34xx_sr_sysc_fields = { + .clkact_shift = 20, +}; + +struct sysc_regbits omap36xx_sr_sysc_fields = { + .sidle_shift = 24, + .enwkup_shift = 26, +}; + +struct sysc_regbits omap3_sham_sysc_fields = { + .sidle_shift = 4, + .srst_shift = 1, + .autoidle_shift = 0, +}; + +struct sysc_regbits omap3xxx_aes_sysc_fields = { + .sidle_shift = 6, + .srst_shift = 1, + .autoidle_shift = 0, +}; + +struct sysc_regbits omap_hwmod_sysc_type_mcasp = { + .sidle_shift = 0, +}; + +struct sysc_regbits omap_hwmod_sysc_type_usb_host_fs = { + .midle_shift = 4, + .sidle_shift = 2, + .srst_shift = 1, +}; diff --git a/arch/arm/mach-omap2/omap_hwmod_common_data.h b/arch/arm/mach-omap2/omap_hwmod_common_data.h new file mode 100644 index 000000000..0045e6680 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_common_data.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * omap_hwmod_common_data.h - OMAP hwmod common macros and declarations + * + * Copyright (C) 2010-2011 Nokia Corporation + * Copyright (C) 2010-2012 Texas Instruments, Inc. + * Paul Walmsley + * Benoît Cousson + */ +#ifndef __ARCH_ARM_MACH_OMAP2_OMAP_HWMOD_COMMON_DATA_H +#define __ARCH_ARM_MACH_OMAP2_OMAP_HWMOD_COMMON_DATA_H + +#include "omap_hwmod.h" + +#include "common.h" +#include "display.h" + +/* Common IP block data across OMAP2xxx */ +extern struct omap_hwmod omap2xxx_l3_main_hwmod; +extern struct omap_hwmod omap2xxx_l4_core_hwmod; +extern struct omap_hwmod omap2xxx_l4_wkup_hwmod; +extern struct omap_hwmod omap2xxx_mpu_hwmod; +extern struct omap_hwmod omap2xxx_iva_hwmod; +extern struct omap_hwmod omap2xxx_timer3_hwmod; +extern struct omap_hwmod omap2xxx_timer4_hwmod; +extern struct omap_hwmod omap2xxx_timer5_hwmod; +extern struct omap_hwmod omap2xxx_timer6_hwmod; +extern struct omap_hwmod omap2xxx_timer7_hwmod; +extern struct omap_hwmod omap2xxx_timer8_hwmod; +extern struct omap_hwmod omap2xxx_timer9_hwmod; +extern struct omap_hwmod omap2xxx_timer10_hwmod; +extern struct omap_hwmod omap2xxx_timer11_hwmod; +extern struct omap_hwmod omap2xxx_timer12_hwmod; +extern struct omap_hwmod omap2xxx_wd_timer2_hwmod; +extern struct omap_hwmod omap2xxx_uart1_hwmod; +extern struct omap_hwmod omap2xxx_uart2_hwmod; +extern struct omap_hwmod omap2xxx_uart3_hwmod; +extern struct omap_hwmod omap2xxx_dss_core_hwmod; +extern struct omap_hwmod omap2xxx_dss_dispc_hwmod; +extern struct omap_hwmod omap2xxx_dss_rfbi_hwmod; +extern struct omap_hwmod omap2xxx_dss_venc_hwmod; +extern struct omap_hwmod omap2xxx_gpio1_hwmod; +extern struct omap_hwmod omap2xxx_gpio2_hwmod; +extern struct omap_hwmod omap2xxx_gpio3_hwmod; +extern struct omap_hwmod omap2xxx_gpio4_hwmod; +extern struct omap_hwmod omap2xxx_mcspi1_hwmod; +extern struct omap_hwmod omap2xxx_mcspi2_hwmod; +extern struct omap_hwmod omap2xxx_gpmc_hwmod; +extern struct omap_hwmod omap2xxx_rng_hwmod; +extern struct omap_hwmod omap2xxx_sham_hwmod; +extern struct omap_hwmod omap2xxx_aes_hwmod; + +/* Common interface data across OMAP2xxx */ +extern struct omap_hwmod_ocp_if omap2xxx_l3_main__l4_core; +extern struct omap_hwmod_ocp_if omap2xxx_mpu__l3_main; +extern struct omap_hwmod_ocp_if omap2xxx_dss__l3; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__l4_wkup; +extern struct omap_hwmod_ocp_if omap2_l4_core__uart1; +extern struct omap_hwmod_ocp_if omap2_l4_core__uart2; +extern struct omap_hwmod_ocp_if omap2_l4_core__uart3; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__mcspi1; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__mcspi2; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer2; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer3; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer4; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer5; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer6; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer7; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer8; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer9; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer10; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer11; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__timer12; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__dss; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_dispc; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_rfbi; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__dss_venc; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__rng; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__sham; +extern struct omap_hwmod_ocp_if omap2xxx_l4_core__aes; + +/* OMAP hwmod classes - forward declarations */ +extern struct omap_hwmod_class l3_hwmod_class; +extern struct omap_hwmod_class l4_hwmod_class; +extern struct omap_hwmod_class mpu_hwmod_class; +extern struct omap_hwmod_class iva_hwmod_class; +extern struct omap_hwmod_class omap2_uart_class; +extern struct omap_hwmod_class omap2_dss_hwmod_class; +extern struct omap_hwmod_class omap2_dispc_hwmod_class; +extern struct omap_hwmod_class omap2_rfbi_hwmod_class; +extern struct omap_hwmod_class omap2_venc_hwmod_class; +extern struct omap_hwmod_class_sysconfig omap2_hdq1w_sysc; +extern struct omap_hwmod_class omap2_hdq1w_class; + +extern struct omap_hwmod_class omap2xxx_timer_hwmod_class; +extern struct omap_hwmod_class omap2xxx_wd_timer_hwmod_class; +extern struct omap_hwmod_class omap2xxx_gpio_hwmod_class; +extern struct omap_hwmod_class omap2xxx_mailbox_hwmod_class; +extern struct omap_hwmod_class omap2xxx_mcspi_class; + +extern struct omap_dss_dispc_dev_attr omap2_3_dss_dispc_dev_attr; + +#endif diff --git a/arch/arm/mach-omap2/omap_hwmod_common_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_common_ipblock_data.c new file mode 100644 index 000000000..39ca491cb --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_common_ipblock_data.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * omap_hwmod_common_ipblock_data.c - common IP block data for OMAP2+ + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + */ + +#include "omap_hwmod.h" +#include "omap_hwmod_common_data.h" + +/* + * 'dss' class + * display sub-system + */ + +static struct omap_hwmod_class_sysconfig omap2_dss_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SOFTRESET | SYSC_HAS_AUTOIDLE | + SYSS_HAS_RESET_STATUS), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2_dss_hwmod_class = { + .name = "dss", + .sysc = &omap2_dss_sysc, + .reset = omap_dss_reset, +}; + +/* + * 'rfbi' class + * remote frame buffer interface + */ + +static struct omap_hwmod_class_sysconfig omap2_rfbi_sysc = { + .rev_offs = 0x0000, + .sysc_offs = 0x0010, + .syss_offs = 0x0014, + .sysc_flags = (SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET | + SYSC_HAS_AUTOIDLE), + .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART), + .sysc_fields = &omap_hwmod_sysc_type1, +}; + +struct omap_hwmod_class omap2_rfbi_hwmod_class = { + .name = "rfbi", + .sysc = &omap2_rfbi_sysc, +}; + diff --git a/arch/arm/mach-omap2/omap_hwmod_reset.c b/arch/arm/mach-omap2/omap_hwmod_reset.c new file mode 100644 index 000000000..143623bb0 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_reset.c @@ -0,0 +1,98 @@ +/* + * OMAP IP block custom reset and preprogramming stubs + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * A small number of IP blocks need custom reset and preprogramming + * functions. The stubs in this file provide a standard way for the + * hwmod code to call these functions, which are to be located under + * drivers/. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include <linux/kernel.h> +#include <linux/errno.h> + +#include "omap_hwmod.h" +#include "common.h" + +#define OMAP_RTC_STATUS_REG 0x44 +#define OMAP_RTC_KICK0_REG 0x6c +#define OMAP_RTC_KICK1_REG 0x70 + +#define OMAP_RTC_KICK0_VALUE 0x83E70B13 +#define OMAP_RTC_KICK1_VALUE 0x95A4F1E0 +#define OMAP_RTC_STATUS_BUSY BIT(0) +#define OMAP_RTC_MAX_READY_TIME 50 + +/** + * omap_rtc_wait_not_busy - Wait for the RTC BUSY flag + * @oh: struct omap_hwmod * + * + * For updating certain RTC registers, the MPU must wait + * for the BUSY status in OMAP_RTC_STATUS_REG to become zero. + * Once the BUSY status is zero, there is a 15 microseconds access + * period in which the MPU can program. + */ +static void omap_rtc_wait_not_busy(struct omap_hwmod *oh) +{ + int i; + + /* BUSY may stay active for 1/32768 second (~30 usec) */ + omap_test_timeout(omap_hwmod_read(oh, OMAP_RTC_STATUS_REG) + & OMAP_RTC_STATUS_BUSY, OMAP_RTC_MAX_READY_TIME, i); + /* now we have ~15 microseconds to read/write various registers */ +} + +/** + * omap_hwmod_rtc_unlock - Unlock the Kicker mechanism. + * @oh: struct omap_hwmod * + * + * RTC IP have kicker feature. This prevents spurious writes to its registers. + * In order to write into any of the RTC registers, KICK values has te be + * written in respective KICK registers. This is needed for hwmod to write into + * sysconfig register. + */ +void omap_hwmod_rtc_unlock(struct omap_hwmod *oh) +{ + unsigned long flags; + + local_irq_save(flags); + omap_rtc_wait_not_busy(oh); + omap_hwmod_write(OMAP_RTC_KICK0_VALUE, oh, OMAP_RTC_KICK0_REG); + omap_hwmod_write(OMAP_RTC_KICK1_VALUE, oh, OMAP_RTC_KICK1_REG); + local_irq_restore(flags); +} + +/** + * omap_hwmod_rtc_lock - Lock the Kicker mechanism. + * @oh: struct omap_hwmod * + * + * RTC IP have kicker feature. This prevents spurious writes to its registers. + * Once the RTC registers are written, KICK mechanism needs to be locked, + * in order to prevent any spurious writes. This function locks back the RTC + * registers once hwmod completes its write into sysconfig register. + */ +void omap_hwmod_rtc_lock(struct omap_hwmod *oh) +{ + unsigned long flags; + + local_irq_save(flags); + omap_rtc_wait_not_busy(oh); + omap_hwmod_write(0x0, oh, OMAP_RTC_KICK0_REG); + omap_hwmod_write(0x0, oh, OMAP_RTC_KICK1_REG); + local_irq_restore(flags); +} diff --git a/arch/arm/mach-omap2/omap_opp_data.h b/arch/arm/mach-omap2/omap_opp_data.h new file mode 100644 index 000000000..88375ab38 --- /dev/null +++ b/arch/arm/mach-omap2/omap_opp_data.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP SoC specific OPP Data helpers + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated - https://www.ti.com/ + * Nishanth Menon + * Kevin Hilman + * Copyright (C) 2010 Nokia Corporation. + * Eduardo Valentin + */ +#ifndef __ARCH_ARM_MACH_OMAP2_OMAP_OPP_DATA_H +#define __ARCH_ARM_MACH_OMAP2_OMAP_OPP_DATA_H + +#include "omap_hwmod.h" + +#include "voltage.h" + +/* + * *BIG FAT WARNING*: + * USE the following ONLY in opp data initialization common to an SoC. + * DO NOT USE these in board files/pm core etc. + */ + +/** + * struct omap_opp_def - OMAP OPP Definition + * @hwmod_name: Name of the hwmod for this domain + * @freq: Frequency in hertz corresponding to this OPP + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @default_available: True/false - is this OPP available by default + * + * OMAP SOCs have a standard set of tuples consisting of frequency and voltage + * pairs that the device will support per voltage domain. This is called + * Operating Points or OPP. The actual definitions of OMAP Operating Points + * varies over silicon within the same family of devices. For a specific + * domain, you can have a set of {frequency, voltage} pairs and this is denoted + * by an array of omap_opp_def. As the kernel boots and more information is + * available, a set of these are activated based on the precise nature of + * device the kernel boots up on. It is interesting to remember that each IP + * which belongs to a voltage domain may define their own set of OPPs on top + * of this - but this is handled by the appropriate driver. + */ +struct omap_opp_def { + char *hwmod_name; + + unsigned long freq; + unsigned long u_volt; + + bool default_available; +}; + +/* + * Initialization wrapper used to define an OPP for OMAP variants. + */ +#define OPP_INITIALIZER(_hwmod_name, _enabled, _freq, _uv) \ +{ \ + .hwmod_name = _hwmod_name, \ + .default_available = _enabled, \ + .freq = _freq, \ + .u_volt = _uv, \ +} + +/* + * Initialization wrapper used to define SmartReflex process data + * XXX Is this needed? Just use C99 initializers in data files? + */ +#define VOLT_DATA_DEFINE(_v_nom, _efuse_offs, _errminlimit, _errgain) \ +{ \ + .volt_nominal = _v_nom, \ + .sr_efuse_offs = _efuse_offs, \ + .sr_errminlimit = _errminlimit, \ + .vp_errgain = _errgain \ +} + +/* Use this to initialize the default table */ +extern int __init omap_init_opp_table(struct omap_opp_def *opp_def, + u32 opp_def_size); + + +extern struct omap_volt_data omap34xx_vddmpu_volt_data[]; +extern struct omap_volt_data omap34xx_vddcore_volt_data[]; +extern struct omap_volt_data omap36xx_vddmpu_volt_data[]; +extern struct omap_volt_data omap36xx_vddcore_volt_data[]; + +extern struct omap_volt_data omap443x_vdd_mpu_volt_data[]; +extern struct omap_volt_data omap443x_vdd_iva_volt_data[]; +extern struct omap_volt_data omap443x_vdd_core_volt_data[]; +extern struct omap_volt_data omap446x_vdd_mpu_volt_data[]; +extern struct omap_volt_data omap446x_vdd_iva_volt_data[]; +extern struct omap_volt_data omap446x_vdd_core_volt_data[]; + +#endif /* __ARCH_ARM_MACH_OMAP2_OMAP_OPP_DATA_H */ diff --git a/arch/arm/mach-omap2/omap_phy_internal.c b/arch/arm/mach-omap2/omap_phy_internal.c new file mode 100644 index 000000000..6f6a6a66c --- /dev/null +++ b/arch/arm/mach-omap2/omap_phy_internal.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file configures the internal USB PHY in OMAP4430. Used + * with TWL6030 transceiver and MUSB on OMAP4430. + * + * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com + * Author: Hema HK <hemahk@ti.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/usb.h> +#include <linux/usb/musb.h> + +#include "soc.h" +#include "control.h" +#include "usb.h" + +#define CONTROL_DEV_CONF 0x300 +#define PHY_PD 0x1 + +/** + * omap4430_phy_power_down: disable MUSB PHY during early init + * + * OMAP4 MUSB PHY module is enabled by default on reset, but this will + * prevent core retention if not disabled by SW. USB driver will + * later on enable this, once and if the driver needs it. + */ +static int __init omap4430_phy_power_down(void) +{ + void __iomem *ctrl_base; + + if (!cpu_is_omap44xx()) + return 0; + + ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K); + if (!ctrl_base) { + pr_err("control module ioremap failed\n"); + return -ENOMEM; + } + + /* Power down the phy */ + writel_relaxed(PHY_PD, ctrl_base + CONTROL_DEV_CONF); + + iounmap(ctrl_base); + + return 0; +} +omap_early_initcall(omap4430_phy_power_down); + +void am35x_musb_reset(void) +{ + u32 regval; + + /* Reset the musb interface */ + regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); + + regval |= AM35XX_USBOTGSS_SW_RST; + omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET); + + regval &= ~AM35XX_USBOTGSS_SW_RST; + omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET); + + regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); +} + +void am35x_musb_phy_power(u8 on) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + u32 devconf2; + + if (on) { + /* + * Start the on-chip PHY and its PLL. + */ + devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); + + devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN); + devconf2 |= CONF2_PHY_PLLON; + + omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); + + pr_info("Waiting for PHY clock good...\n"); + while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2) + & CONF2_PHYCLKGD)) { + cpu_relax(); + + if (time_after(jiffies, timeout)) { + pr_err("musb PHY clock good timed out\n"); + break; + } + } + } else { + /* + * Power down the on-chip PHY. + */ + devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); + + devconf2 &= ~CONF2_PHY_PLLON; + devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN; + omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); + } +} + +void am35x_musb_clear_irq(void) +{ + u32 regval; + + regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); + regval |= AM35XX_USBOTGSS_INT_CLR; + omap_ctrl_writel(regval, AM35XX_CONTROL_LVL_INTR_CLEAR); + regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); +} + +void am35x_set_mode(u8 musb_mode) +{ + u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); + + devconf2 &= ~CONF2_OTGMODE; + switch (musb_mode) { + case MUSB_HOST: /* Force VBUS valid, ID = 0 */ + devconf2 |= CONF2_FORCE_HOST; + break; + case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */ + devconf2 |= CONF2_FORCE_DEVICE; + break; + case MUSB_OTG: /* Don't override the VBUS/ID comparators */ + devconf2 |= CONF2_NO_OVERRIDE; + break; + default: + pr_info("Unsupported mode %u\n", musb_mode); + } + + omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); +} diff --git a/arch/arm/mach-omap2/omap_twl.c b/arch/arm/mach-omap2/omap_twl.c new file mode 100644 index 000000000..d4dab0413 --- /dev/null +++ b/arch/arm/mach-omap2/omap_twl.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP and TWL PMIC specific initializations. + * + * Copyright (C) 2010 Texas Instruments Incorporated. + * Thara Gopinath + * Copyright (C) 2009 Texas Instruments Incorporated. + * Nishanth Menon + * Copyright (C) 2009 Nokia Corporation + * Paul Walmsley + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/twl.h> + +#include "soc.h" +#include "voltage.h" + +#include "pm.h" + +#define OMAP3_SRI2C_SLAVE_ADDR 0x12 +#define OMAP3_VDD_MPU_SR_CONTROL_REG 0x00 +#define OMAP3_VDD_CORE_SR_CONTROL_REG 0x01 +#define OMAP3_VP_CONFIG_ERROROFFSET 0x00 +#define OMAP3_VP_VSTEPMIN_VSTEPMIN 0x1 +#define OMAP3_VP_VSTEPMAX_VSTEPMAX 0x04 +#define OMAP3_VP_VLIMITTO_TIMEOUT_US 200 + +#define OMAP4_SRI2C_SLAVE_ADDR 0x12 +#define OMAP4_VDD_MPU_SR_VOLT_REG 0x55 +#define OMAP4_VDD_MPU_SR_CMD_REG 0x56 +#define OMAP4_VDD_IVA_SR_VOLT_REG 0x5B +#define OMAP4_VDD_IVA_SR_CMD_REG 0x5C +#define OMAP4_VDD_CORE_SR_VOLT_REG 0x61 +#define OMAP4_VDD_CORE_SR_CMD_REG 0x62 + +static bool is_offset_valid; +static u8 smps_offset; + +#define REG_SMPS_OFFSET 0xE0 + +static unsigned long twl4030_vsel_to_uv(const u8 vsel) +{ + return (((vsel * 125) + 6000)) * 100; +} + +static u8 twl4030_uv_to_vsel(unsigned long uv) +{ + return DIV_ROUND_UP(uv - 600000, 12500); +} + +static unsigned long twl6030_vsel_to_uv(const u8 vsel) +{ + /* + * In TWL6030 depending on the value of SMPS_OFFSET + * efuse register the voltage range supported in + * standard mode can be either between 0.6V - 1.3V or + * 0.7V - 1.4V. In TWL6030 ES1.0 SMPS_OFFSET efuse + * is programmed to all 0's where as starting from + * TWL6030 ES1.1 the efuse is programmed to 1 + */ + if (!is_offset_valid) { + twl_i2c_read_u8(TWL6030_MODULE_ID0, &smps_offset, + REG_SMPS_OFFSET); + is_offset_valid = true; + } + + if (!vsel) + return 0; + /* + * There is no specific formula for voltage to vsel + * conversion above 1.3V. There are special hardcoded + * values for voltages above 1.3V. Currently we are + * hardcoding only for 1.35 V which is used for 1GH OPP for + * OMAP4430. + */ + if (vsel == 0x3A) + return 1350000; + + if (smps_offset & 0x8) + return ((((vsel - 1) * 1266) + 70900)) * 10; + else + return ((((vsel - 1) * 1266) + 60770)) * 10; +} + +static u8 twl6030_uv_to_vsel(unsigned long uv) +{ + /* + * In TWL6030 depending on the value of SMPS_OFFSET + * efuse register the voltage range supported in + * standard mode can be either between 0.6V - 1.3V or + * 0.7V - 1.4V. In TWL6030 ES1.0 SMPS_OFFSET efuse + * is programmed to all 0's where as starting from + * TWL6030 ES1.1 the efuse is programmed to 1 + */ + if (!is_offset_valid) { + twl_i2c_read_u8(TWL6030_MODULE_ID0, &smps_offset, + REG_SMPS_OFFSET); + is_offset_valid = true; + } + + if (!uv) + return 0x00; + /* + * There is no specific formula for voltage to vsel + * conversion above 1.3V. There are special hardcoded + * values for voltages above 1.3V. Currently we are + * hardcoding only for 1.35 V which is used for 1GH OPP for + * OMAP4430. + */ + if (uv > twl6030_vsel_to_uv(0x39)) { + if (uv == 1350000) + return 0x3A; + pr_err("%s:OUT OF RANGE! non mapped vsel for %ld Vs max %ld\n", + __func__, uv, twl6030_vsel_to_uv(0x39)); + return 0x3A; + } + + if (smps_offset & 0x8) + return DIV_ROUND_UP(uv - 709000, 12660) + 1; + else + return DIV_ROUND_UP(uv - 607700, 12660) + 1; +} + +static struct omap_voltdm_pmic omap3_mpu_pmic = { + .slew_rate = 4000, + .step_size = 12500, + .vp_erroroffset = OMAP3_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP3_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP3_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 600000, + .vddmax = 1450000, + .vp_timeout_us = OMAP3_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR, + .volt_reg_addr = OMAP3_VDD_MPU_SR_CONTROL_REG, + .i2c_high_speed = true, + .vsel_to_uv = twl4030_vsel_to_uv, + .uv_to_vsel = twl4030_uv_to_vsel, +}; + +static struct omap_voltdm_pmic omap3_core_pmic = { + .slew_rate = 4000, + .step_size = 12500, + .vp_erroroffset = OMAP3_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP3_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP3_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 600000, + .vddmax = 1450000, + .vp_timeout_us = OMAP3_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = OMAP3_SRI2C_SLAVE_ADDR, + .volt_reg_addr = OMAP3_VDD_CORE_SR_CONTROL_REG, + .i2c_high_speed = true, + .vsel_to_uv = twl4030_vsel_to_uv, + .uv_to_vsel = twl4030_uv_to_vsel, +}; + +static struct omap_voltdm_pmic omap4_mpu_pmic = { + .slew_rate = 4000, + .step_size = 12660, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 0, + .vddmax = 2100000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = OMAP4_SRI2C_SLAVE_ADDR, + .volt_reg_addr = OMAP4_VDD_MPU_SR_VOLT_REG, + .cmd_reg_addr = OMAP4_VDD_MPU_SR_CMD_REG, + .i2c_high_speed = true, + .i2c_pad_load = 3, + .vsel_to_uv = twl6030_vsel_to_uv, + .uv_to_vsel = twl6030_uv_to_vsel, +}; + +static struct omap_voltdm_pmic omap4_iva_pmic = { + .slew_rate = 4000, + .step_size = 12660, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 0, + .vddmax = 2100000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = OMAP4_SRI2C_SLAVE_ADDR, + .volt_reg_addr = OMAP4_VDD_IVA_SR_VOLT_REG, + .cmd_reg_addr = OMAP4_VDD_IVA_SR_CMD_REG, + .i2c_high_speed = true, + .i2c_pad_load = 3, + .vsel_to_uv = twl6030_vsel_to_uv, + .uv_to_vsel = twl6030_uv_to_vsel, +}; + +static struct omap_voltdm_pmic omap4_core_pmic = { + .slew_rate = 4000, + .step_size = 12660, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 0, + .vddmax = 2100000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = OMAP4_SRI2C_SLAVE_ADDR, + .volt_reg_addr = OMAP4_VDD_CORE_SR_VOLT_REG, + .cmd_reg_addr = OMAP4_VDD_CORE_SR_CMD_REG, + .i2c_high_speed = true, + .i2c_pad_load = 3, + .vsel_to_uv = twl6030_vsel_to_uv, + .uv_to_vsel = twl6030_uv_to_vsel, +}; + +int __init omap4_twl_init(void) +{ + struct voltagedomain *voltdm; + + if (!cpu_is_omap44xx() || + of_find_compatible_node(NULL, NULL, "motorola,cpcap")) + return -ENODEV; + + voltdm = voltdm_lookup("mpu"); + omap_voltage_register_pmic(voltdm, &omap4_mpu_pmic); + + voltdm = voltdm_lookup("iva"); + omap_voltage_register_pmic(voltdm, &omap4_iva_pmic); + + voltdm = voltdm_lookup("core"); + omap_voltage_register_pmic(voltdm, &omap4_core_pmic); + + return 0; +} + +int __init omap3_twl_init(void) +{ + struct voltagedomain *voltdm; + + if (!cpu_is_omap34xx()) + return -ENODEV; + + voltdm = voltdm_lookup("mpu_iva"); + omap_voltage_register_pmic(voltdm, &omap3_mpu_pmic); + + voltdm = voltdm_lookup("core"); + omap_voltage_register_pmic(voltdm, &omap3_core_pmic); + + return 0; +} diff --git a/arch/arm/mach-omap2/opp2420_data.c b/arch/arm/mach-omap2/opp2420_data.c new file mode 100644 index 000000000..18a448f93 --- /dev/null +++ b/arch/arm/mach-omap2/opp2420_data.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * opp2420_data.c - old-style "OPP" table for OMAP2420 + * + * Copyright (C) 2005-2009 Texas Instruments, Inc. + * Copyright (C) 2004-2009 Nokia Corporation + * + * Richard Woodruff <r-woodruff2@ti.com> + * + * The OMAP2 processor can be run at several discrete 'PRCM configurations'. + * These configurations are characterized by voltage and speed for clocks. + * The device is only validated for certain combinations. One way to express + * these combinations is via the 'ratios' which the clocks operate with + * respect to each other. These ratio sets are for a given voltage/DPLL + * setting. All configurations can be described by a DPLL setting and a ratio. + * + * XXX Missing voltage data. + * XXX Missing 19.2MHz sys_clk rate sets (needed for N800/N810) + * + * THe format described in this file is deprecated. Once a reasonable + * OPP API exists, the data in this file should be converted to use it. + * + * This is technically part of the OMAP2xxx clock code. + * + * Considerable work is still needed to fully support dynamic frequency + * changes on OMAP2xxx-series chips. Readers interested in such a + * project are encouraged to review the Maemo Diablo RX-34 and RX-44 + * kernel source at: + * http://repository.maemo.org/pool/diablo/free/k/kernel-source-diablo/ + */ + +#include <linux/kernel.h> + +#include "opp2xxx.h" +#include "sdrc.h" +#include "clock.h" + +/* + * Key dividers which make up a PRCM set. Ratios for a PRCM are mandated. + * xtal_speed, dpll_speed, mpu_speed, CM_CLKSEL_MPU, + * CM_CLKSEL_DSP, CM_CLKSEL_GFX, CM_CLKSEL1_CORE, CM_CLKSEL1_PLL, + * CM_CLKSEL2_PLL, CM_CLKSEL_MDM + * + * Filling in table based on H4 boards available. There are quite a + * few more rate combinations which could be defined. + * + * When multiple values are defined the start up will try and choose + * the fastest one. If a 'fast' value is defined, then automatically, + * the /2 one should be included as it can be used. Generally having + * more than one fast set does not make sense, as static timings need + * to be changed to change the set. The exception is the bypass + * setting which is available for low power bypass. + * + * Note: This table needs to be sorted, fastest to slowest. + **/ +const struct prcm_config omap2420_rate_table[] = { + /* PRCM I - FAST */ + {S12M, S660M, S330M, RI_CM_CLKSEL_MPU_VAL, /* 330MHz ARM */ + RI_CM_CLKSEL_DSP_VAL, RI_CM_CLKSEL_GFX_VAL, + RI_CM_CLKSEL1_CORE_VAL, MI_CM_CLKSEL1_PLL_12_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_165MHz, + RATE_IN_242X}, + + /* PRCM II - FAST */ + {S12M, S600M, S300M, RII_CM_CLKSEL_MPU_VAL, /* 300MHz ARM */ + RII_CM_CLKSEL_DSP_VAL, RII_CM_CLKSEL_GFX_VAL, + RII_CM_CLKSEL1_CORE_VAL, MII_CM_CLKSEL1_PLL_12_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_100MHz, + RATE_IN_242X}, + + {S13M, S600M, S300M, RII_CM_CLKSEL_MPU_VAL, /* 300MHz ARM */ + RII_CM_CLKSEL_DSP_VAL, RII_CM_CLKSEL_GFX_VAL, + RII_CM_CLKSEL1_CORE_VAL, MII_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_100MHz, + RATE_IN_242X}, + + /* PRCM III - FAST */ + {S12M, S532M, S266M, RIII_CM_CLKSEL_MPU_VAL, /* 266MHz ARM */ + RIII_CM_CLKSEL_DSP_VAL, RIII_CM_CLKSEL_GFX_VAL, + RIII_CM_CLKSEL1_CORE_VAL, MIII_CM_CLKSEL1_PLL_12_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_133MHz, + RATE_IN_242X}, + + {S13M, S532M, S266M, RIII_CM_CLKSEL_MPU_VAL, /* 266MHz ARM */ + RIII_CM_CLKSEL_DSP_VAL, RIII_CM_CLKSEL_GFX_VAL, + RIII_CM_CLKSEL1_CORE_VAL, MIII_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_133MHz, + RATE_IN_242X}, + + /* PRCM II - SLOW */ + {S12M, S300M, S150M, RII_CM_CLKSEL_MPU_VAL, /* 150MHz ARM */ + RII_CM_CLKSEL_DSP_VAL, RII_CM_CLKSEL_GFX_VAL, + RII_CM_CLKSEL1_CORE_VAL, MII_CM_CLKSEL1_PLL_12_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_100MHz, + RATE_IN_242X}, + + {S13M, S300M, S150M, RII_CM_CLKSEL_MPU_VAL, /* 150MHz ARM */ + RII_CM_CLKSEL_DSP_VAL, RII_CM_CLKSEL_GFX_VAL, + RII_CM_CLKSEL1_CORE_VAL, MII_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_100MHz, + RATE_IN_242X}, + + /* PRCM III - SLOW */ + {S12M, S266M, S133M, RIII_CM_CLKSEL_MPU_VAL, /* 133MHz ARM */ + RIII_CM_CLKSEL_DSP_VAL, RIII_CM_CLKSEL_GFX_VAL, + RIII_CM_CLKSEL1_CORE_VAL, MIII_CM_CLKSEL1_PLL_12_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_133MHz, + RATE_IN_242X}, + + {S13M, S266M, S133M, RIII_CM_CLKSEL_MPU_VAL, /* 133MHz ARM */ + RIII_CM_CLKSEL_DSP_VAL, RIII_CM_CLKSEL_GFX_VAL, + RIII_CM_CLKSEL1_CORE_VAL, MIII_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_133MHz, + RATE_IN_242X}, + + /* PRCM-VII (boot-bypass) */ + {S12M, S12M, S12M, RVII_CM_CLKSEL_MPU_VAL, /* 12MHz ARM*/ + RVII_CM_CLKSEL_DSP_VAL, RVII_CM_CLKSEL_GFX_VAL, + RVII_CM_CLKSEL1_CORE_VAL, MVII_CM_CLKSEL1_PLL_12_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_BYPASS, + RATE_IN_242X}, + + /* PRCM-VII (boot-bypass) */ + {S13M, S13M, S13M, RVII_CM_CLKSEL_MPU_VAL, /* 13MHz ARM */ + RVII_CM_CLKSEL_DSP_VAL, RVII_CM_CLKSEL_GFX_VAL, + RVII_CM_CLKSEL1_CORE_VAL, MVII_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, 0, SDRC_RFR_CTRL_BYPASS, + RATE_IN_242X}, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; diff --git a/arch/arm/mach-omap2/opp2430_data.c b/arch/arm/mach-omap2/opp2430_data.c new file mode 100644 index 000000000..ded8b566a --- /dev/null +++ b/arch/arm/mach-omap2/opp2430_data.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * opp2430_data.c - old-style "OPP" table for OMAP2430 + * + * Copyright (C) 2005-2009 Texas Instruments, Inc. + * Copyright (C) 2004-2009 Nokia Corporation + * + * Richard Woodruff <r-woodruff2@ti.com> + * + * The OMAP2 processor can be run at several discrete 'PRCM configurations'. + * These configurations are characterized by voltage and speed for clocks. + * The device is only validated for certain combinations. One way to express + * these combinations is via the 'ratios' which the clocks operate with + * respect to each other. These ratio sets are for a given voltage/DPLL + * setting. All configurations can be described by a DPLL setting and a ratio. + * + * 2430 differs from 2420 in that there are no more phase synchronizers used. + * They both have a slightly different clock domain setup. 2420(iva1,dsp) vs + * 2430 (iva2.1, NOdsp, mdm) + * + * XXX Missing voltage data. + * XXX Missing 19.2MHz sys_clk rate sets. + * + * THe format described in this file is deprecated. Once a reasonable + * OPP API exists, the data in this file should be converted to use it. + * + * This is technically part of the OMAP2xxx clock code. + */ + +#include <linux/kernel.h> + +#include "opp2xxx.h" +#include "sdrc.h" +#include "clock.h" + +/* + * Key dividers which make up a PRCM set. Ratios for a PRCM are mandated. + * xtal_speed, dpll_speed, mpu_speed, CM_CLKSEL_MPU, + * CM_CLKSEL_DSP, CM_CLKSEL_GFX, CM_CLKSEL1_CORE, CM_CLKSEL1_PLL, + * CM_CLKSEL2_PLL, CM_CLKSEL_MDM + * + * Filling in table based on 2430-SDPs variants available. There are + * quite a few more rate combinations which could be defined. + * + * When multiple values are defined the start up will try and choose + * the fastest one. If a 'fast' value is defined, then automatically, + * the /2 one should be included as it can be used. Generally having + * more than one fast set does not make sense, as static timings need + * to be changed to change the set. The exception is the bypass + * setting which is available for low power bypass. + * + * Note: This table needs to be sorted, fastest to slowest. + */ +const struct prcm_config omap2430_rate_table[] = { + /* PRCM #4 - ratio2 (ES2.1) - FAST */ + {S13M, S798M, S399M, R2_CM_CLKSEL_MPU_VAL, /* 399MHz ARM */ + R2_CM_CLKSEL_DSP_VAL, R2_CM_CLKSEL_GFX_VAL, + R2_CM_CLKSEL1_CORE_VAL, M4_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, R2_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_133MHz, + RATE_IN_243X}, + + /* PRCM #2 - ratio1 (ES2) - FAST */ + {S13M, S658M, S329M, R1_CM_CLKSEL_MPU_VAL, /* 330MHz ARM */ + R1_CM_CLKSEL_DSP_VAL, R1_CM_CLKSEL_GFX_VAL, + R1_CM_CLKSEL1_CORE_VAL, M2_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, R1_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_165MHz, + RATE_IN_243X}, + + /* PRCM #5a - ratio1 - FAST */ + {S13M, S532M, S266M, R1_CM_CLKSEL_MPU_VAL, /* 266MHz ARM */ + R1_CM_CLKSEL_DSP_VAL, R1_CM_CLKSEL_GFX_VAL, + R1_CM_CLKSEL1_CORE_VAL, M5A_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, R1_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_133MHz, + RATE_IN_243X}, + + /* PRCM #5b - ratio1 - FAST */ + {S13M, S400M, S200M, R1_CM_CLKSEL_MPU_VAL, /* 200MHz ARM */ + R1_CM_CLKSEL_DSP_VAL, R1_CM_CLKSEL_GFX_VAL, + R1_CM_CLKSEL1_CORE_VAL, M5B_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, R1_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_100MHz, + RATE_IN_243X}, + + /* PRCM #4 - ratio1 (ES2.1) - SLOW */ + {S13M, S399M, S199M, R2_CM_CLKSEL_MPU_VAL, /* 200MHz ARM */ + R2_CM_CLKSEL_DSP_VAL, R2_CM_CLKSEL_GFX_VAL, + R2_CM_CLKSEL1_CORE_VAL, M4_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_1x_VAL, R2_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_133MHz, + RATE_IN_243X}, + + /* PRCM #2 - ratio1 (ES2) - SLOW */ + {S13M, S329M, S164M, R1_CM_CLKSEL_MPU_VAL, /* 165MHz ARM */ + R1_CM_CLKSEL_DSP_VAL, R1_CM_CLKSEL_GFX_VAL, + R1_CM_CLKSEL1_CORE_VAL, M2_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_1x_VAL, R1_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_165MHz, + RATE_IN_243X}, + + /* PRCM #5a - ratio1 - SLOW */ + {S13M, S266M, S133M, R1_CM_CLKSEL_MPU_VAL, /* 133MHz ARM */ + R1_CM_CLKSEL_DSP_VAL, R1_CM_CLKSEL_GFX_VAL, + R1_CM_CLKSEL1_CORE_VAL, M5A_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_1x_VAL, R1_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_133MHz, + RATE_IN_243X}, + + /* PRCM #5b - ratio1 - SLOW*/ + {S13M, S200M, S100M, R1_CM_CLKSEL_MPU_VAL, /* 100MHz ARM */ + R1_CM_CLKSEL_DSP_VAL, R1_CM_CLKSEL_GFX_VAL, + R1_CM_CLKSEL1_CORE_VAL, M5B_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_1x_VAL, R1_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_100MHz, + RATE_IN_243X}, + + /* PRCM-boot/bypass */ + {S13M, S13M, S13M, RB_CM_CLKSEL_MPU_VAL, /* 13MHz */ + RB_CM_CLKSEL_DSP_VAL, RB_CM_CLKSEL_GFX_VAL, + RB_CM_CLKSEL1_CORE_VAL, MB_CM_CLKSEL1_PLL_13_VAL, + MX_CLKSEL2_PLL_2x_VAL, RB_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_BYPASS, + RATE_IN_243X}, + + /* PRCM-boot/bypass */ + {S12M, S12M, S12M, RB_CM_CLKSEL_MPU_VAL, /* 12MHz */ + RB_CM_CLKSEL_DSP_VAL, RB_CM_CLKSEL_GFX_VAL, + RB_CM_CLKSEL1_CORE_VAL, MB_CM_CLKSEL1_PLL_12_VAL, + MX_CLKSEL2_PLL_2x_VAL, RB_CM_CLKSEL_MDM_VAL, + SDRC_RFR_CTRL_BYPASS, + RATE_IN_243X}, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; diff --git a/arch/arm/mach-omap2/opp2xxx.h b/arch/arm/mach-omap2/opp2xxx.h new file mode 100644 index 000000000..a451182cd --- /dev/null +++ b/arch/arm/mach-omap2/opp2xxx.h @@ -0,0 +1,430 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * opp2xxx.h - macros for old-style OMAP2xxx "OPP" definitions + * + * Copyright (C) 2005-2009 Texas Instruments, Inc. + * Copyright (C) 2004-2009 Nokia Corporation + * + * Richard Woodruff <r-woodruff2@ti.com> + * + * The OMAP2 processor can be run at several discrete 'PRCM configurations'. + * These configurations are characterized by voltage and speed for clocks. + * The device is only validated for certain combinations. One way to express + * these combinations is via the 'ratio's' which the clocks operate with + * respect to each other. These ratio sets are for a given voltage/DPLL + * setting. All configurations can be described by a DPLL setting and a ratio + * There are 3 ratio sets for the 2430 and X ratio sets for 2420. + * + * 2430 differs from 2420 in that there are no more phase synchronizers used. + * They both have a slightly different clock domain setup. 2420(iva1,dsp) vs + * 2430 (iva2.1, NOdsp, mdm) + * + * XXX Missing voltage data. + * + * THe format described in this file is deprecated. Once a reasonable + * OPP API exists, the data in this file should be converted to use it. + * + * This is technically part of the OMAP2xxx clock code. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_OPP2XXX_H +#define __ARCH_ARM_MACH_OMAP2_OPP2XXX_H + +/** + * struct prcm_config - define clock rates on a per-OPP basis (24xx) + * + * Key dividers which make up a PRCM set. Ratio's for a PRCM are mandated. + * xtal_speed, dpll_speed, mpu_speed, CM_CLKSEL_MPU,CM_CLKSEL_DSP + * CM_CLKSEL_GFX, CM_CLKSEL1_CORE, CM_CLKSEL1_PLL CM_CLKSEL2_PLL, CM_CLKSEL_MDM + * + * This is deprecated. As soon as we have a decent OPP API, we should + * move all this stuff to it. + */ +struct prcm_config { + unsigned long xtal_speed; /* crystal rate */ + unsigned long dpll_speed; /* dpll: out*xtal*M/(N-1)table_recalc */ + unsigned long mpu_speed; /* speed of MPU */ + unsigned long cm_clksel_mpu; /* mpu divider */ + unsigned long cm_clksel_dsp; /* dsp+iva1 div(2420), iva2.1(2430) */ + unsigned long cm_clksel_gfx; /* gfx dividers */ + unsigned long cm_clksel1_core; /* major subsystem dividers */ + unsigned long cm_clksel1_pll; /* m,n */ + unsigned long cm_clksel2_pll; /* dpllx1 or x2 out */ + unsigned long cm_clksel_mdm; /* modem dividers 2430 only */ + unsigned long base_sdrc_rfr; /* base refresh timing for a set */ + unsigned short flags; +}; + + +/* Core fields for cm_clksel, not ratio governed */ +#define RX_CLKSEL_DSS1 (0x10 << 8) +#define RX_CLKSEL_DSS2 (0x0 << 13) +#define RX_CLKSEL_SSI (0x5 << 20) + +/*------------------------------------------------------------------------- + * Voltage/DPLL ratios + *-------------------------------------------------------------------------*/ + +/* 2430 Ratio's, 2430-Ratio Config 1 */ +#define R1_CLKSEL_L3 (4 << 0) +#define R1_CLKSEL_L4 (2 << 5) +#define R1_CLKSEL_USB (4 << 25) +#define R1_CM_CLKSEL1_CORE_VAL (R1_CLKSEL_USB | RX_CLKSEL_SSI | \ + RX_CLKSEL_DSS2 | RX_CLKSEL_DSS1 | \ + R1_CLKSEL_L4 | R1_CLKSEL_L3) +#define R1_CLKSEL_MPU (2 << 0) +#define R1_CM_CLKSEL_MPU_VAL R1_CLKSEL_MPU +#define R1_CLKSEL_DSP (2 << 0) +#define R1_CLKSEL_DSP_IF (2 << 5) +#define R1_CM_CLKSEL_DSP_VAL (R1_CLKSEL_DSP | R1_CLKSEL_DSP_IF) +#define R1_CLKSEL_GFX (2 << 0) +#define R1_CM_CLKSEL_GFX_VAL R1_CLKSEL_GFX +#define R1_CLKSEL_MDM (4 << 0) +#define R1_CM_CLKSEL_MDM_VAL R1_CLKSEL_MDM + +/* 2430-Ratio Config 2 */ +#define R2_CLKSEL_L3 (6 << 0) +#define R2_CLKSEL_L4 (2 << 5) +#define R2_CLKSEL_USB (2 << 25) +#define R2_CM_CLKSEL1_CORE_VAL (R2_CLKSEL_USB | RX_CLKSEL_SSI | \ + RX_CLKSEL_DSS2 | RX_CLKSEL_DSS1 | \ + R2_CLKSEL_L4 | R2_CLKSEL_L3) +#define R2_CLKSEL_MPU (2 << 0) +#define R2_CM_CLKSEL_MPU_VAL R2_CLKSEL_MPU +#define R2_CLKSEL_DSP (2 << 0) +#define R2_CLKSEL_DSP_IF (3 << 5) +#define R2_CM_CLKSEL_DSP_VAL (R2_CLKSEL_DSP | R2_CLKSEL_DSP_IF) +#define R2_CLKSEL_GFX (2 << 0) +#define R2_CM_CLKSEL_GFX_VAL R2_CLKSEL_GFX +#define R2_CLKSEL_MDM (6 << 0) +#define R2_CM_CLKSEL_MDM_VAL R2_CLKSEL_MDM + +/* 2430-Ratio Bootm (BYPASS) */ +#define RB_CLKSEL_L3 (1 << 0) +#define RB_CLKSEL_L4 (1 << 5) +#define RB_CLKSEL_USB (1 << 25) +#define RB_CM_CLKSEL1_CORE_VAL (RB_CLKSEL_USB | RX_CLKSEL_SSI | \ + RX_CLKSEL_DSS2 | RX_CLKSEL_DSS1 | \ + RB_CLKSEL_L4 | RB_CLKSEL_L3) +#define RB_CLKSEL_MPU (1 << 0) +#define RB_CM_CLKSEL_MPU_VAL RB_CLKSEL_MPU +#define RB_CLKSEL_DSP (1 << 0) +#define RB_CLKSEL_DSP_IF (1 << 5) +#define RB_CM_CLKSEL_DSP_VAL (RB_CLKSEL_DSP | RB_CLKSEL_DSP_IF) +#define RB_CLKSEL_GFX (1 << 0) +#define RB_CM_CLKSEL_GFX_VAL RB_CLKSEL_GFX +#define RB_CLKSEL_MDM (1 << 0) +#define RB_CM_CLKSEL_MDM_VAL RB_CLKSEL_MDM + +/* 2420 Ratio Equivalents */ +#define RXX_CLKSEL_VLYNQ (0x12 << 15) +#define RXX_CLKSEL_SSI (0x8 << 20) + +/* 2420-PRCM III 532MHz core */ +#define RIII_CLKSEL_L3 (4 << 0) /* 133MHz */ +#define RIII_CLKSEL_L4 (2 << 5) /* 66.5MHz */ +#define RIII_CLKSEL_USB (4 << 25) /* 33.25MHz */ +#define RIII_CM_CLKSEL1_CORE_VAL (RIII_CLKSEL_USB | RXX_CLKSEL_SSI | \ + RXX_CLKSEL_VLYNQ | RX_CLKSEL_DSS2 | \ + RX_CLKSEL_DSS1 | RIII_CLKSEL_L4 | \ + RIII_CLKSEL_L3) +#define RIII_CLKSEL_MPU (2 << 0) /* 266MHz */ +#define RIII_CM_CLKSEL_MPU_VAL RIII_CLKSEL_MPU +#define RIII_CLKSEL_DSP (3 << 0) /* c5x - 177.3MHz */ +#define RIII_CLKSEL_DSP_IF (2 << 5) /* c5x - 88.67MHz */ +#define RIII_SYNC_DSP (1 << 7) /* Enable sync */ +#define RIII_CLKSEL_IVA (6 << 8) /* iva1 - 88.67MHz */ +#define RIII_SYNC_IVA (1 << 13) /* Enable sync */ +#define RIII_CM_CLKSEL_DSP_VAL (RIII_SYNC_IVA | RIII_CLKSEL_IVA | \ + RIII_SYNC_DSP | RIII_CLKSEL_DSP_IF | \ + RIII_CLKSEL_DSP) +#define RIII_CLKSEL_GFX (2 << 0) /* 66.5MHz */ +#define RIII_CM_CLKSEL_GFX_VAL RIII_CLKSEL_GFX + +/* 2420-PRCM II 600MHz core */ +#define RII_CLKSEL_L3 (6 << 0) /* 100MHz */ +#define RII_CLKSEL_L4 (2 << 5) /* 50MHz */ +#define RII_CLKSEL_USB (2 << 25) /* 50MHz */ +#define RII_CM_CLKSEL1_CORE_VAL (RII_CLKSEL_USB | RXX_CLKSEL_SSI | \ + RXX_CLKSEL_VLYNQ | RX_CLKSEL_DSS2 | \ + RX_CLKSEL_DSS1 | RII_CLKSEL_L4 | \ + RII_CLKSEL_L3) +#define RII_CLKSEL_MPU (2 << 0) /* 300MHz */ +#define RII_CM_CLKSEL_MPU_VAL RII_CLKSEL_MPU +#define RII_CLKSEL_DSP (3 << 0) /* c5x - 200MHz */ +#define RII_CLKSEL_DSP_IF (2 << 5) /* c5x - 100MHz */ +#define RII_SYNC_DSP (0 << 7) /* Bypass sync */ +#define RII_CLKSEL_IVA (3 << 8) /* iva1 - 200MHz */ +#define RII_SYNC_IVA (0 << 13) /* Bypass sync */ +#define RII_CM_CLKSEL_DSP_VAL (RII_SYNC_IVA | RII_CLKSEL_IVA | \ + RII_SYNC_DSP | RII_CLKSEL_DSP_IF | \ + RII_CLKSEL_DSP) +#define RII_CLKSEL_GFX (2 << 0) /* 50MHz */ +#define RII_CM_CLKSEL_GFX_VAL RII_CLKSEL_GFX + +/* 2420-PRCM I 660MHz core */ +#define RI_CLKSEL_L3 (4 << 0) /* 165MHz */ +#define RI_CLKSEL_L4 (2 << 5) /* 82.5MHz */ +#define RI_CLKSEL_USB (4 << 25) /* 41.25MHz */ +#define RI_CM_CLKSEL1_CORE_VAL (RI_CLKSEL_USB | \ + RXX_CLKSEL_SSI | RXX_CLKSEL_VLYNQ | \ + RX_CLKSEL_DSS2 | RX_CLKSEL_DSS1 | \ + RI_CLKSEL_L4 | RI_CLKSEL_L3) +#define RI_CLKSEL_MPU (2 << 0) /* 330MHz */ +#define RI_CM_CLKSEL_MPU_VAL RI_CLKSEL_MPU +#define RI_CLKSEL_DSP (3 << 0) /* c5x - 220MHz */ +#define RI_CLKSEL_DSP_IF (2 << 5) /* c5x - 110MHz */ +#define RI_SYNC_DSP (1 << 7) /* Activate sync */ +#define RI_CLKSEL_IVA (4 << 8) /* iva1 - 165MHz */ +#define RI_SYNC_IVA (0 << 13) /* Bypass sync */ +#define RI_CM_CLKSEL_DSP_VAL (RI_SYNC_IVA | RI_CLKSEL_IVA | \ + RI_SYNC_DSP | RI_CLKSEL_DSP_IF | \ + RI_CLKSEL_DSP) +#define RI_CLKSEL_GFX (1 << 0) /* 165MHz */ +#define RI_CM_CLKSEL_GFX_VAL RI_CLKSEL_GFX + +/* 2420-PRCM VII (boot) */ +#define RVII_CLKSEL_L3 (1 << 0) +#define RVII_CLKSEL_L4 (1 << 5) +#define RVII_CLKSEL_DSS1 (1 << 8) +#define RVII_CLKSEL_DSS2 (0 << 13) +#define RVII_CLKSEL_VLYNQ (1 << 15) +#define RVII_CLKSEL_SSI (1 << 20) +#define RVII_CLKSEL_USB (1 << 25) + +#define RVII_CM_CLKSEL1_CORE_VAL (RVII_CLKSEL_USB | RVII_CLKSEL_SSI | \ + RVII_CLKSEL_VLYNQ | \ + RVII_CLKSEL_DSS2 | RVII_CLKSEL_DSS1 | \ + RVII_CLKSEL_L4 | RVII_CLKSEL_L3) + +#define RVII_CLKSEL_MPU (1 << 0) /* all divide by 1 */ +#define RVII_CM_CLKSEL_MPU_VAL RVII_CLKSEL_MPU + +#define RVII_CLKSEL_DSP (1 << 0) +#define RVII_CLKSEL_DSP_IF (1 << 5) +#define RVII_SYNC_DSP (0 << 7) +#define RVII_CLKSEL_IVA (1 << 8) +#define RVII_SYNC_IVA (0 << 13) +#define RVII_CM_CLKSEL_DSP_VAL (RVII_SYNC_IVA | RVII_CLKSEL_IVA | \ + RVII_SYNC_DSP | RVII_CLKSEL_DSP_IF | \ + RVII_CLKSEL_DSP) + +#define RVII_CLKSEL_GFX (1 << 0) +#define RVII_CM_CLKSEL_GFX_VAL RVII_CLKSEL_GFX + +/*------------------------------------------------------------------------- + * 2430 Target modes: Along with each configuration the CPU has several + * modes which goes along with them. Modes mainly are the addition of + * describe DPLL combinations to go along with a ratio. + *-------------------------------------------------------------------------*/ + +/* Hardware governed */ +#define MX_48M_SRC (0 << 3) +#define MX_54M_SRC (0 << 5) +#define MX_APLLS_CLIKIN_12 (3 << 23) +#define MX_APLLS_CLIKIN_13 (2 << 23) +#define MX_APLLS_CLIKIN_19_2 (0 << 23) + +/* + * 2430 - standalone, 2*ref*M/(n+1), M/N is for exactness not relock speed + * #5a (ratio1) baseport-target, target DPLL = 266*2 = 532MHz + */ +#define M5A_DPLL_MULT_12 (133 << 12) +#define M5A_DPLL_DIV_12 (5 << 8) +#define M5A_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + M5A_DPLL_DIV_12 | M5A_DPLL_MULT_12 | \ + MX_APLLS_CLIKIN_12) +#define M5A_DPLL_MULT_13 (61 << 12) +#define M5A_DPLL_DIV_13 (2 << 8) +#define M5A_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + M5A_DPLL_DIV_13 | M5A_DPLL_MULT_13 | \ + MX_APLLS_CLIKIN_13) +#define M5A_DPLL_MULT_19 (55 << 12) +#define M5A_DPLL_DIV_19 (3 << 8) +#define M5A_CM_CLKSEL1_PLL_19_VAL (MX_48M_SRC | MX_54M_SRC | \ + M5A_DPLL_DIV_19 | M5A_DPLL_MULT_19 | \ + MX_APLLS_CLIKIN_19_2) +/* #5b (ratio1) target DPLL = 200*2 = 400MHz */ +#define M5B_DPLL_MULT_12 (50 << 12) +#define M5B_DPLL_DIV_12 (2 << 8) +#define M5B_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + M5B_DPLL_DIV_12 | M5B_DPLL_MULT_12 | \ + MX_APLLS_CLIKIN_12) +#define M5B_DPLL_MULT_13 (200 << 12) +#define M5B_DPLL_DIV_13 (12 << 8) + +#define M5B_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + M5B_DPLL_DIV_13 | M5B_DPLL_MULT_13 | \ + MX_APLLS_CLIKIN_13) +#define M5B_DPLL_MULT_19 (125 << 12) +#define M5B_DPLL_DIV_19 (31 << 8) +#define M5B_CM_CLKSEL1_PLL_19_VAL (MX_48M_SRC | MX_54M_SRC | \ + M5B_DPLL_DIV_19 | M5B_DPLL_MULT_19 | \ + MX_APLLS_CLIKIN_19_2) +/* + * #4 (ratio2), DPLL = 399*2 = 798MHz, L3=133MHz + */ +#define M4_DPLL_MULT_12 (133 << 12) +#define M4_DPLL_DIV_12 (3 << 8) +#define M4_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + M4_DPLL_DIV_12 | M4_DPLL_MULT_12 | \ + MX_APLLS_CLIKIN_12) + +#define M4_DPLL_MULT_13 (399 << 12) +#define M4_DPLL_DIV_13 (12 << 8) +#define M4_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + M4_DPLL_DIV_13 | M4_DPLL_MULT_13 | \ + MX_APLLS_CLIKIN_13) + +#define M4_DPLL_MULT_19 (145 << 12) +#define M4_DPLL_DIV_19 (6 << 8) +#define M4_CM_CLKSEL1_PLL_19_VAL (MX_48M_SRC | MX_54M_SRC | \ + M4_DPLL_DIV_19 | M4_DPLL_MULT_19 | \ + MX_APLLS_CLIKIN_19_2) + +/* + * #3 (ratio2) baseport-target, target DPLL = 330*2 = 660MHz + */ +#define M3_DPLL_MULT_12 (55 << 12) +#define M3_DPLL_DIV_12 (1 << 8) +#define M3_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + M3_DPLL_DIV_12 | M3_DPLL_MULT_12 | \ + MX_APLLS_CLIKIN_12) +#define M3_DPLL_MULT_13 (76 << 12) +#define M3_DPLL_DIV_13 (2 << 8) +#define M3_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + M3_DPLL_DIV_13 | M3_DPLL_MULT_13 | \ + MX_APLLS_CLIKIN_13) +#define M3_DPLL_MULT_19 (17 << 12) +#define M3_DPLL_DIV_19 (0 << 8) +#define M3_CM_CLKSEL1_PLL_19_VAL (MX_48M_SRC | MX_54M_SRC | \ + M3_DPLL_DIV_19 | M3_DPLL_MULT_19 | \ + MX_APLLS_CLIKIN_19_2) + +/* + * #2 (ratio1) DPLL = 330*2 = 660MHz, L3=165MHz + */ +#define M2_DPLL_MULT_12 (55 << 12) +#define M2_DPLL_DIV_12 (1 << 8) +#define M2_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + M2_DPLL_DIV_12 | M2_DPLL_MULT_12 | \ + MX_APLLS_CLIKIN_12) + +/* Speed changes - Used 658.7MHz instead of 660MHz for LP-Refresh M=76 N=2, + * relock time issue */ +/* Core frequency changed from 330/165 to 329/164 MHz*/ +#define M2_DPLL_MULT_13 (76 << 12) +#define M2_DPLL_DIV_13 (2 << 8) +#define M2_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + M2_DPLL_DIV_13 | M2_DPLL_MULT_13 | \ + MX_APLLS_CLIKIN_13) + +#define M2_DPLL_MULT_19 (17 << 12) +#define M2_DPLL_DIV_19 (0 << 8) +#define M2_CM_CLKSEL1_PLL_19_VAL (MX_48M_SRC | MX_54M_SRC | \ + M2_DPLL_DIV_19 | M2_DPLL_MULT_19 | \ + MX_APLLS_CLIKIN_19_2) + +/* boot (boot) */ +#define MB_DPLL_MULT (1 << 12) +#define MB_DPLL_DIV (0 << 8) +#define MB_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + MB_DPLL_DIV | MB_DPLL_MULT | \ + MX_APLLS_CLIKIN_12) + +#define MB_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + MB_DPLL_DIV | MB_DPLL_MULT | \ + MX_APLLS_CLIKIN_13) + +#define MB_CM_CLKSEL1_PLL_19_VAL (MX_48M_SRC | MX_54M_SRC | \ + MB_DPLL_DIV | MB_DPLL_MULT | \ + MX_APLLS_CLIKIN_19) + +/* + * 2430 - chassis (sedna) + * 165 (ratio1) same as above #2 + * 150 (ratio1) + * 133 (ratio2) same as above #4 + * 110 (ratio2) same as above #3 + * 104 (ratio2) + * boot (boot) + */ + +/* PRCM I target DPLL = 2*330MHz = 660MHz */ +#define MI_DPLL_MULT_12 (55 << 12) +#define MI_DPLL_DIV_12 (1 << 8) +#define MI_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + MI_DPLL_DIV_12 | MI_DPLL_MULT_12 | \ + MX_APLLS_CLIKIN_12) + +/* + * 2420 Equivalent - mode registers + * PRCM II , target DPLL = 2*300MHz = 600MHz + */ +#define MII_DPLL_MULT_12 (50 << 12) +#define MII_DPLL_DIV_12 (1 << 8) +#define MII_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + MII_DPLL_DIV_12 | MII_DPLL_MULT_12 | \ + MX_APLLS_CLIKIN_12) +#define MII_DPLL_MULT_13 (300 << 12) +#define MII_DPLL_DIV_13 (12 << 8) +#define MII_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + MII_DPLL_DIV_13 | MII_DPLL_MULT_13 | \ + MX_APLLS_CLIKIN_13) + +/* PRCM III target DPLL = 2*266 = 532MHz*/ +#define MIII_DPLL_MULT_12 (133 << 12) +#define MIII_DPLL_DIV_12 (5 << 8) +#define MIII_CM_CLKSEL1_PLL_12_VAL (MX_48M_SRC | MX_54M_SRC | \ + MIII_DPLL_DIV_12 | \ + MIII_DPLL_MULT_12 | MX_APLLS_CLIKIN_12) +#define MIII_DPLL_MULT_13 (266 << 12) +#define MIII_DPLL_DIV_13 (12 << 8) +#define MIII_CM_CLKSEL1_PLL_13_VAL (MX_48M_SRC | MX_54M_SRC | \ + MIII_DPLL_DIV_13 | \ + MIII_DPLL_MULT_13 | MX_APLLS_CLIKIN_13) + +/* PRCM VII (boot bypass) */ +#define MVII_CM_CLKSEL1_PLL_12_VAL MB_CM_CLKSEL1_PLL_12_VAL +#define MVII_CM_CLKSEL1_PLL_13_VAL MB_CM_CLKSEL1_PLL_13_VAL + +/* High and low operation value */ +#define MX_CLKSEL2_PLL_2x_VAL (2 << 0) +#define MX_CLKSEL2_PLL_1x_VAL (1 << 0) + +/* MPU speed defines */ +#define S12M 12000000 +#define S13M 13000000 +#define S19M 19200000 +#define S26M 26000000 +#define S100M 100000000 +#define S133M 133000000 +#define S150M 150000000 +#define S164M 164000000 +#define S165M 165000000 +#define S199M 199000000 +#define S200M 200000000 +#define S266M 266000000 +#define S300M 300000000 +#define S329M 329000000 +#define S330M 330000000 +#define S399M 399000000 +#define S400M 400000000 +#define S532M 532000000 +#define S600M 600000000 +#define S658M 658000000 +#define S660M 660000000 +#define S798M 798000000 + + +extern const struct prcm_config omap2420_rate_table[]; + +#ifdef CONFIG_SOC_OMAP2430 +extern const struct prcm_config omap2430_rate_table[]; +#else +#define omap2430_rate_table NULL +#endif +extern const struct prcm_config *rate_table; +extern const struct prcm_config *curr_prcm_set; + +#endif diff --git a/arch/arm/mach-omap2/opp3xxx_data.c b/arch/arm/mach-omap2/opp3xxx_data.c new file mode 100644 index 000000000..90257e2fb --- /dev/null +++ b/arch/arm/mach-omap2/opp3xxx_data.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 OPP table definitions. + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated - https://www.ti.com/ + * Nishanth Menon + * Kevin Hilman + * Copyright (C) 2010-2011 Nokia Corporation. + * Eduardo Valentin + * Paul Walmsley + */ +#include <linux/module.h> + +#include "soc.h" +#include "control.h" +#include "omap_opp_data.h" +#include "pm.h" + +/* 34xx */ + +/* VDD1 */ + +#define OMAP3430_VDD_MPU_OPP1_UV 975000 +#define OMAP3430_VDD_MPU_OPP2_UV 1075000 +#define OMAP3430_VDD_MPU_OPP3_UV 1200000 +#define OMAP3430_VDD_MPU_OPP4_UV 1270000 +#define OMAP3430_VDD_MPU_OPP5_UV 1350000 + +struct omap_volt_data omap34xx_vddmpu_volt_data[] = { + VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP1_UV, OMAP343X_CONTROL_FUSE_OPP1_VDD1, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP2_UV, OMAP343X_CONTROL_FUSE_OPP2_VDD1, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP3_UV, OMAP343X_CONTROL_FUSE_OPP3_VDD1, 0xf9, 0x18), + VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP4_UV, OMAP343X_CONTROL_FUSE_OPP4_VDD1, 0xf9, 0x18), + VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP5_UV, OMAP343X_CONTROL_FUSE_OPP5_VDD1, 0xf9, 0x18), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +/* VDD2 */ + +#define OMAP3430_VDD_CORE_OPP1_UV 975000 +#define OMAP3430_VDD_CORE_OPP2_UV 1050000 +#define OMAP3430_VDD_CORE_OPP3_UV 1150000 + +struct omap_volt_data omap34xx_vddcore_volt_data[] = { + VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP1_UV, OMAP343X_CONTROL_FUSE_OPP1_VDD2, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP2_UV, OMAP343X_CONTROL_FUSE_OPP2_VDD2, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP3430_VDD_CORE_OPP3_UV, OMAP343X_CONTROL_FUSE_OPP3_VDD2, 0xf9, 0x18), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +/* 36xx */ + +/* VDD1 */ + +#define OMAP3630_VDD_MPU_OPP50_UV 1012500 +#define OMAP3630_VDD_MPU_OPP100_UV 1200000 +#define OMAP3630_VDD_MPU_OPP120_UV 1325000 +#define OMAP3630_VDD_MPU_OPP1G_UV 1375000 + +struct omap_volt_data omap36xx_vddmpu_volt_data[] = { + VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP50_UV, OMAP3630_CONTROL_FUSE_OPP50_VDD1, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP100_UV, OMAP3630_CONTROL_FUSE_OPP100_VDD1, 0xf9, 0x16), + VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP120_UV, OMAP3630_CONTROL_FUSE_OPP120_VDD1, 0xfa, 0x23), + VOLT_DATA_DEFINE(OMAP3630_VDD_MPU_OPP1G_UV, OMAP3630_CONTROL_FUSE_OPP1G_VDD1, 0xfa, 0x27), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +/* VDD2 */ + +#define OMAP3630_VDD_CORE_OPP50_UV 1000000 +#define OMAP3630_VDD_CORE_OPP100_UV 1200000 + +struct omap_volt_data omap36xx_vddcore_volt_data[] = { + VOLT_DATA_DEFINE(OMAP3630_VDD_CORE_OPP50_UV, OMAP3630_CONTROL_FUSE_OPP50_VDD2, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP3630_VDD_CORE_OPP100_UV, OMAP3630_CONTROL_FUSE_OPP100_VDD2, 0xf9, 0x16), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; diff --git a/arch/arm/mach-omap2/opp4xxx_data.c b/arch/arm/mach-omap2/opp4xxx_data.c new file mode 100644 index 000000000..a98518860 --- /dev/null +++ b/arch/arm/mach-omap2/opp4xxx_data.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 OPP table definitions. + * + * Copyright (C) 2010-2012 Texas Instruments Incorporated - https://www.ti.com/ + * Nishanth Menon + * Kevin Hilman + * Thara Gopinath + * Copyright (C) 2010-2011 Nokia Corporation. + * Eduardo Valentin + * Paul Walmsley + */ +#include <linux/module.h> + +#include "soc.h" +#include "control.h" +#include "omap_opp_data.h" +#include "pm.h" + +/* + * Structures containing OMAP4430 voltage supported and various + * voltage dependent data for each VDD. + */ + +#define OMAP4430_VDD_MPU_OPP50_UV 1025000 +#define OMAP4430_VDD_MPU_OPP100_UV 1200000 +#define OMAP4430_VDD_MPU_OPPTURBO_UV 1325000 +#define OMAP4430_VDD_MPU_OPPNITRO_UV 1388000 +#define OMAP4430_VDD_MPU_OPPNITROSB_UV 1398000 + +struct omap_volt_data omap443x_vdd_mpu_volt_data[] = { + VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPP50_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP50, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPP100_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP100, 0xf9, 0x16), + VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPTURBO, 0xfa, 0x23), + VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPPNITRO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPNITRO, 0xfa, 0x27), + VOLT_DATA_DEFINE(OMAP4430_VDD_MPU_OPPNITROSB_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPNITROSB, 0xfa, 0x27), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +#define OMAP4430_VDD_IVA_OPP50_UV 950000 +#define OMAP4430_VDD_IVA_OPP100_UV 1114000 +#define OMAP4430_VDD_IVA_OPPTURBO_UV 1291000 + +struct omap_volt_data omap443x_vdd_iva_volt_data[] = { + VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPP50_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP50, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPP100_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP100, 0xf9, 0x16), + VOLT_DATA_DEFINE(OMAP4430_VDD_IVA_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_IVA_OPPTURBO, 0xfa, 0x23), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +#define OMAP4430_VDD_CORE_OPP50_UV 962000 +#define OMAP4430_VDD_CORE_OPP100_UV 1127000 + +struct omap_volt_data omap443x_vdd_core_volt_data[] = { + VOLT_DATA_DEFINE(OMAP4430_VDD_CORE_OPP50_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP50, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP4430_VDD_CORE_OPP100_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP100, 0xf9, 0x16), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +#define OMAP4460_VDD_MPU_OPP50_UV 1025000 +#define OMAP4460_VDD_MPU_OPP100_UV 1200000 +#define OMAP4460_VDD_MPU_OPPTURBO_UV 1313000 +#define OMAP4460_VDD_MPU_OPPNITRO_UV 1375000 + +struct omap_volt_data omap446x_vdd_mpu_volt_data[] = { + VOLT_DATA_DEFINE(OMAP4460_VDD_MPU_OPP50_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP50, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP4460_VDD_MPU_OPP100_UV, OMAP44XX_CONTROL_FUSE_MPU_OPP100, 0xf9, 0x16), + VOLT_DATA_DEFINE(OMAP4460_VDD_MPU_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPTURBO, 0xfa, 0x23), + VOLT_DATA_DEFINE(OMAP4460_VDD_MPU_OPPNITRO_UV, OMAP44XX_CONTROL_FUSE_MPU_OPPNITRO, 0xfa, 0x27), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +#define OMAP4460_VDD_IVA_OPP50_UV 1025000 +#define OMAP4460_VDD_IVA_OPP100_UV 1200000 +#define OMAP4460_VDD_IVA_OPPTURBO_UV 1313000 +#define OMAP4460_VDD_IVA_OPPNITRO_UV 1375000 + +struct omap_volt_data omap446x_vdd_iva_volt_data[] = { + VOLT_DATA_DEFINE(OMAP4460_VDD_IVA_OPP50_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP50, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP4460_VDD_IVA_OPP100_UV, OMAP44XX_CONTROL_FUSE_IVA_OPP100, 0xf9, 0x16), + VOLT_DATA_DEFINE(OMAP4460_VDD_IVA_OPPTURBO_UV, OMAP44XX_CONTROL_FUSE_IVA_OPPTURBO, 0xfa, 0x23), + VOLT_DATA_DEFINE(OMAP4460_VDD_IVA_OPPNITRO_UV, OMAP44XX_CONTROL_FUSE_IVA_OPPNITRO, 0xfa, 0x23), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; + +#define OMAP4460_VDD_CORE_OPP50_UV 1025000 +#define OMAP4460_VDD_CORE_OPP100_UV 1200000 +#define OMAP4460_VDD_CORE_OPP100_OV_UV 1250000 + +struct omap_volt_data omap446x_vdd_core_volt_data[] = { + VOLT_DATA_DEFINE(OMAP4460_VDD_CORE_OPP50_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP50, 0xf4, 0x0c), + VOLT_DATA_DEFINE(OMAP4460_VDD_CORE_OPP100_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP100, 0xf9, 0x16), + VOLT_DATA_DEFINE(OMAP4460_VDD_CORE_OPP100_OV_UV, OMAP44XX_CONTROL_FUSE_CORE_OPP100OV, 0xf9, 0x16), + VOLT_DATA_DEFINE(0, 0, 0, 0), +}; diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c new file mode 100644 index 000000000..5b99d602c --- /dev/null +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Legacy platform_data quirks + * + * Copyright (C) 2013 Texas Instruments + */ +#include <linux/clk.h> +#include <linux/davinci_emac.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of_platform.h> +#include <linux/wl12xx.h> +#include <linux/mmc/card.h> +#include <linux/mmc/host.h> +#include <linux/power/smartreflex.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> + +#include <linux/platform_data/pinctrl-single.h> +#include <linux/platform_data/hsmmc-omap.h> +#include <linux/platform_data/iommu-omap.h> +#include <linux/platform_data/ti-sysc.h> +#include <linux/platform_data/wkup_m3.h> +#include <linux/platform_data/asoc-ti-mcbsp.h> +#include <linux/platform_data/ti-prm.h> + +#include "clockdomain.h" +#include "common.h" +#include "common-board-devices.h" +#include "control.h" +#include "omap_device.h" +#include "omap-secure.h" +#include "soc.h" + +static struct omap_hsmmc_platform_data __maybe_unused mmc_pdata[2]; + +struct pdata_init { + const char *compatible; + void (*fn)(void); +}; + +static struct of_dev_auxdata omap_auxdata_lookup[]; +static struct twl4030_gpio_platform_data twl_gpio_auxdata; + +#ifdef CONFIG_MACH_NOKIA_N8X0 +static void __init omap2420_n8x0_legacy_init(void) +{ + omap_auxdata_lookup[0].platform_data = n8x0_legacy_init(); +} +#else +#define omap2420_n8x0_legacy_init NULL +#endif + +#ifdef CONFIG_ARCH_OMAP3 +/* + * Configures GPIOs 126, 127 and 129 to 1.8V mode instead of 3.0V + * mode for MMC1 in case bootloader did not configure things. + * Note that if the pins are used for MMC1, pbias-regulator + * manages the IO voltage. + */ +static void __init omap3_gpio126_127_129(void) +{ + u32 reg; + + reg = omap_ctrl_readl(OMAP343X_CONTROL_PBIAS_LITE); + reg &= ~OMAP343X_PBIASLITEVMODE1; + reg |= OMAP343X_PBIASLITEPWRDNZ1; + omap_ctrl_writel(reg, OMAP343X_CONTROL_PBIAS_LITE); + if (cpu_is_omap3630()) { + reg = omap_ctrl_readl(OMAP34XX_CONTROL_WKUP_CTRL); + reg |= OMAP36XX_GPIO_IO_PWRDNZ; + omap_ctrl_writel(reg, OMAP34XX_CONTROL_WKUP_CTRL); + } +} + +static void __init hsmmc2_internal_input_clk(void) +{ + u32 reg; + + reg = omap_ctrl_readl(OMAP343X_CONTROL_DEVCONF1); + reg |= OMAP2_MMCSDIO2ADPCLKISEL; + omap_ctrl_writel(reg, OMAP343X_CONTROL_DEVCONF1); +} + +#ifdef CONFIG_OMAP_HWMOD +static struct iommu_platform_data omap3_iommu_pdata = { + .reset_name = "mmu", + .assert_reset = omap_device_assert_hardreset, + .deassert_reset = omap_device_deassert_hardreset, + .device_enable = omap_device_enable, + .device_idle = omap_device_idle, +}; + +static struct iommu_platform_data omap3_iommu_isp_pdata = { + .device_enable = omap_device_enable, + .device_idle = omap_device_idle, +}; +#endif + +static int omap3_sbc_t3730_twl_callback(struct device *dev, + unsigned gpio, + unsigned ngpio) +{ + int res; + + res = gpio_request_one(gpio + 2, GPIOF_OUT_INIT_HIGH, + "wlan pwr"); + if (res) + return res; + + gpio_export(gpio, 0); + + return 0; +} + +static void __init omap3_sbc_t3x_usb_hub_init(int gpio, char *hub_name) +{ + int err = gpio_request_one(gpio, GPIOF_OUT_INIT_LOW, hub_name); + + if (err) { + pr_err("SBC-T3x: %s reset gpio request failed: %d\n", + hub_name, err); + return; + } + + gpio_export(gpio, 0); + + udelay(10); + gpio_set_value(gpio, 1); + msleep(1); +} + +static void __init omap3_sbc_t3730_twl_init(void) +{ + twl_gpio_auxdata.setup = omap3_sbc_t3730_twl_callback; +} + +static void __init omap3_sbc_t3730_legacy_init(void) +{ + omap3_sbc_t3x_usb_hub_init(167, "sb-t35 usb hub"); +} + +static void __init omap3_sbc_t3530_legacy_init(void) +{ + omap3_sbc_t3x_usb_hub_init(167, "sb-t35 usb hub"); +} + +static void __init omap3_evm_legacy_init(void) +{ + hsmmc2_internal_input_clk(); +} + +static void am35xx_enable_emac_int(void) +{ + u32 v; + + v = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); + v |= (AM35XX_CPGMAC_C0_RX_PULSE_CLR | AM35XX_CPGMAC_C0_TX_PULSE_CLR | + AM35XX_CPGMAC_C0_MISC_PULSE_CLR | AM35XX_CPGMAC_C0_RX_THRESH_CLR); + omap_ctrl_writel(v, AM35XX_CONTROL_LVL_INTR_CLEAR); + omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); /* OCP barrier */ +} + +static void am35xx_disable_emac_int(void) +{ + u32 v; + + v = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); + v |= (AM35XX_CPGMAC_C0_RX_PULSE_CLR | AM35XX_CPGMAC_C0_TX_PULSE_CLR); + omap_ctrl_writel(v, AM35XX_CONTROL_LVL_INTR_CLEAR); + omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); /* OCP barrier */ +} + +static struct emac_platform_data am35xx_emac_pdata = { + .interrupt_enable = am35xx_enable_emac_int, + .interrupt_disable = am35xx_disable_emac_int, +}; + +static void __init am35xx_emac_reset(void) +{ + u32 v; + + v = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); + v &= ~AM35XX_CPGMACSS_SW_RST; + omap_ctrl_writel(v, AM35XX_CONTROL_IP_SW_RESET); + omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); /* OCP barrier */ +} + +static struct gpio cm_t3517_wlan_gpios[] __initdata = { + { 56, GPIOF_OUT_INIT_HIGH, "wlan pwr" }, + { 4, GPIOF_OUT_INIT_HIGH, "xcvr noe" }, +}; + +static void __init omap3_sbc_t3517_wifi_init(void) +{ + int err = gpio_request_array(cm_t3517_wlan_gpios, + ARRAY_SIZE(cm_t3517_wlan_gpios)); + if (err) { + pr_err("SBC-T3517: wl12xx gpios request failed: %d\n", err); + return; + } + + gpio_export(cm_t3517_wlan_gpios[0].gpio, 0); + gpio_export(cm_t3517_wlan_gpios[1].gpio, 0); + + msleep(100); + gpio_set_value(cm_t3517_wlan_gpios[1].gpio, 0); +} + +static void __init omap3_sbc_t3517_legacy_init(void) +{ + omap3_sbc_t3x_usb_hub_init(152, "cm-t3517 usb hub"); + omap3_sbc_t3x_usb_hub_init(98, "sb-t35 usb hub"); + am35xx_emac_reset(); + hsmmc2_internal_input_clk(); + omap3_sbc_t3517_wifi_init(); +} + +static void __init am3517_evm_legacy_init(void) +{ + am35xx_emac_reset(); +} + +static void __init nokia_n900_legacy_init(void) +{ + hsmmc2_internal_input_clk(); + mmc_pdata[0].name = "external"; + mmc_pdata[1].name = "internal"; + + if (omap_type() != OMAP2_DEVICE_TYPE_GP) { + if (IS_ENABLED(CONFIG_ARM_ERRATA_430973)) { + pr_info("RX-51: Enabling ARM errata 430973 workaround\n"); + /* set IBE to 1 */ + rx51_secure_update_aux_cr(BIT(6), 0); + } else { + pr_warn("RX-51: Not enabling ARM errata 430973 workaround\n"); + pr_warn("Thumb binaries may crash randomly without this workaround\n"); + } + } +} + +static void __init omap3_tao3530_legacy_init(void) +{ + hsmmc2_internal_input_clk(); +} + +static void __init omap3_logicpd_torpedo_init(void) +{ + omap3_gpio126_127_129(); +} + +/* omap3pandora legacy devices */ + +static struct platform_device pandora_backlight = { + .name = "pandora-backlight", + .id = -1, +}; + +static void __init omap3_pandora_legacy_init(void) +{ + platform_device_register(&pandora_backlight); +} +#endif /* CONFIG_ARCH_OMAP3 */ + +#ifdef CONFIG_SOC_DRA7XX +static struct iommu_platform_data dra7_ipu1_dsp_iommu_pdata = { + .set_pwrdm_constraint = omap_iommu_set_pwrdm_constraint, +}; +#endif + +static struct clockdomain *ti_sysc_find_one_clockdomain(struct clk *clk) +{ + struct clk_hw *hw = __clk_get_hw(clk); + struct clockdomain *clkdm = NULL; + struct clk_hw_omap *hwclk; + + hwclk = to_clk_hw_omap(hw); + if (!omap2_clk_is_hw_omap(hw)) + return NULL; + + if (hwclk && hwclk->clkdm_name) + clkdm = clkdm_lookup(hwclk->clkdm_name); + + return clkdm; +} + +/** + * ti_sysc_clkdm_init - find clockdomain based on clock + * @fck: device functional clock + * @ick: device interface clock + * @dev: struct device + * + * Populate clockdomain based on clock. It is needed for + * clkdm_deny_idle() and clkdm_allow_idle() for blocking clockdomain + * clockdomain idle during reset, enable and idle. + * + * Note that we assume interconnect driver manages the clocks + * and do not need to populate oh->_clk for dynamically + * allocated modules. + */ +static int ti_sysc_clkdm_init(struct device *dev, + struct clk *fck, struct clk *ick, + struct ti_sysc_cookie *cookie) +{ + if (!IS_ERR(fck)) + cookie->clkdm = ti_sysc_find_one_clockdomain(fck); + if (cookie->clkdm) + return 0; + if (!IS_ERR(ick)) + cookie->clkdm = ti_sysc_find_one_clockdomain(ick); + if (cookie->clkdm) + return 0; + + return -ENODEV; +} + +static void ti_sysc_clkdm_deny_idle(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (cookie->clkdm) + clkdm_deny_idle(cookie->clkdm); +} + +static void ti_sysc_clkdm_allow_idle(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (cookie->clkdm) + clkdm_allow_idle(cookie->clkdm); +} + +#ifdef CONFIG_OMAP_HWMOD +static int ti_sysc_enable_module(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (!cookie->data) + return -EINVAL; + + return omap_hwmod_enable(cookie->data); +} + +static int ti_sysc_idle_module(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (!cookie->data) + return -EINVAL; + + return omap_hwmod_idle(cookie->data); +} + +static int ti_sysc_shutdown_module(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (!cookie->data) + return -EINVAL; + + return omap_hwmod_shutdown(cookie->data); +} +#endif /* CONFIG_OMAP_HWMOD */ + +static bool ti_sysc_soc_type_gp(void) +{ + return omap_type() == OMAP2_DEVICE_TYPE_GP; +} + +static struct of_dev_auxdata omap_auxdata_lookup[]; + +static struct ti_sysc_platform_data ti_sysc_pdata = { + .auxdata = omap_auxdata_lookup, + .soc_type_gp = ti_sysc_soc_type_gp, + .init_clockdomain = ti_sysc_clkdm_init, + .clkdm_deny_idle = ti_sysc_clkdm_deny_idle, + .clkdm_allow_idle = ti_sysc_clkdm_allow_idle, +#ifdef CONFIG_OMAP_HWMOD + .init_module = omap_hwmod_init_module, + .enable_module = ti_sysc_enable_module, + .idle_module = ti_sysc_idle_module, + .shutdown_module = ti_sysc_shutdown_module, +#endif +}; + +static struct pcs_pdata pcs_pdata; + +void omap_pcs_legacy_init(int irq, void (*rearm)(void)) +{ + pcs_pdata.irq = irq; + pcs_pdata.rearm = rearm; +} + +static struct ti_prm_platform_data ti_prm_pdata = { + .clkdm_deny_idle = clkdm_deny_idle, + .clkdm_allow_idle = clkdm_allow_idle, + .clkdm_lookup = clkdm_lookup, +}; + +/* + * GPIOs for TWL are initialized by the I2C bus and need custom + * handing until DSS has device tree bindings. + */ +void omap_auxdata_legacy_init(struct device *dev) +{ + if (dev->platform_data) + return; + + if (strcmp("twl4030-gpio", dev_name(dev))) + return; + + dev->platform_data = &twl_gpio_auxdata; +} + +#if defined(CONFIG_ARCH_OMAP3) && IS_ENABLED(CONFIG_SND_SOC_OMAP_MCBSP) +static struct omap_mcbsp_platform_data mcbsp_pdata; +static void __init omap3_mcbsp_init(void) +{ + omap3_mcbsp_init_pdata_callback(&mcbsp_pdata); +} +#else +static void __init omap3_mcbsp_init(void) {} +#endif + +/* + * Few boards still need auxdata populated before we populate + * the dev entries in of_platform_populate(). + */ +static struct pdata_init auxdata_quirks[] __initdata = { +#ifdef CONFIG_SOC_OMAP2420 + { "nokia,n800", omap2420_n8x0_legacy_init, }, + { "nokia,n810", omap2420_n8x0_legacy_init, }, + { "nokia,n810-wimax", omap2420_n8x0_legacy_init, }, +#endif +#ifdef CONFIG_ARCH_OMAP3 + { "compulab,omap3-sbc-t3730", omap3_sbc_t3730_twl_init, }, +#endif + { /* sentinel */ }, +}; + +struct omap_sr_data __maybe_unused omap_sr_pdata[OMAP_SR_NR]; + +static struct of_dev_auxdata omap_auxdata_lookup[] = { +#ifdef CONFIG_MACH_NOKIA_N8X0 + OF_DEV_AUXDATA("ti,omap2420-mmc", 0x4809c000, "mmci-omap.0", NULL), + OF_DEV_AUXDATA("menelaus", 0x72, "1-0072", &n8x0_menelaus_platform_data), + OF_DEV_AUXDATA("tlv320aic3x", 0x18, "2-0018", &n810_aic33_data), +#endif +#ifdef CONFIG_ARCH_OMAP3 + OF_DEV_AUXDATA("ti,omap2-iommu", 0x5d000000, "5d000000.mmu", + &omap3_iommu_pdata), + OF_DEV_AUXDATA("ti,omap2-iommu", 0x480bd400, "480bd400.mmu", + &omap3_iommu_isp_pdata), + OF_DEV_AUXDATA("ti,omap3-smartreflex-core", 0x480cb000, + "480cb000.smartreflex", &omap_sr_pdata[OMAP_SR_CORE]), + OF_DEV_AUXDATA("ti,omap3-smartreflex-mpu-iva", 0x480c9000, + "480c9000.smartreflex", &omap_sr_pdata[OMAP_SR_MPU]), + OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x4809c000, "4809c000.mmc", &mmc_pdata[0]), + OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x480b4000, "480b4000.mmc", &mmc_pdata[1]), + /* Only on am3517 */ + OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL), + OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0", + &am35xx_emac_pdata), + OF_DEV_AUXDATA("nokia,n900-rom-rng", 0, NULL, rx51_secure_rng_call), + /* McBSP modules with sidetone core */ +#if IS_ENABLED(CONFIG_SND_SOC_OMAP_MCBSP) + OF_DEV_AUXDATA("ti,omap3-mcbsp", 0x49022000, "49022000.mcbsp", &mcbsp_pdata), + OF_DEV_AUXDATA("ti,omap3-mcbsp", 0x49024000, "49024000.mcbsp", &mcbsp_pdata), +#endif +#endif +#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) + OF_DEV_AUXDATA("ti,omap4-smartreflex-iva", 0x4a0db000, + "4a0db000.smartreflex", &omap_sr_pdata[OMAP_SR_IVA]), + OF_DEV_AUXDATA("ti,omap4-smartreflex-core", 0x4a0dd000, + "4a0dd000.smartreflex", &omap_sr_pdata[OMAP_SR_CORE]), + OF_DEV_AUXDATA("ti,omap4-smartreflex-mpu", 0x4a0d9000, + "4a0d9000.smartreflex", &omap_sr_pdata[OMAP_SR_MPU]), +#endif +#ifdef CONFIG_SOC_DRA7XX + OF_DEV_AUXDATA("ti,dra7-dsp-iommu", 0x40d01000, "40d01000.mmu", + &dra7_ipu1_dsp_iommu_pdata), + OF_DEV_AUXDATA("ti,dra7-dsp-iommu", 0x41501000, "41501000.mmu", + &dra7_ipu1_dsp_iommu_pdata), + OF_DEV_AUXDATA("ti,dra7-iommu", 0x58882000, "58882000.mmu", + &dra7_ipu1_dsp_iommu_pdata), +#endif + /* Common auxdata */ + OF_DEV_AUXDATA("simple-pm-bus", 0, NULL, omap_auxdata_lookup), + OF_DEV_AUXDATA("ti,sysc", 0, NULL, &ti_sysc_pdata), + OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata), + OF_DEV_AUXDATA("ti,omap-prm-inst", 0, NULL, &ti_prm_pdata), + OF_DEV_AUXDATA("ti,omap-sdma", 0, NULL, &dma_plat_info), + { /* sentinel */ }, +}; + +/* + * Few boards still need to initialize some legacy devices with + * platform data until the drivers support device tree. + */ +static struct pdata_init pdata_quirks[] __initdata = { +#ifdef CONFIG_ARCH_OMAP3 + { "compulab,omap3-sbc-t3517", omap3_sbc_t3517_legacy_init, }, + { "compulab,omap3-sbc-t3530", omap3_sbc_t3530_legacy_init, }, + { "compulab,omap3-sbc-t3730", omap3_sbc_t3730_legacy_init, }, + { "nokia,omap3-n900", nokia_n900_legacy_init, }, + { "nokia,omap3-n9", hsmmc2_internal_input_clk, }, + { "nokia,omap3-n950", hsmmc2_internal_input_clk, }, + { "logicpd,dm3730-torpedo-devkit", omap3_logicpd_torpedo_init, }, + { "ti,omap3-evm-37xx", omap3_evm_legacy_init, }, + { "ti,am3517-evm", am3517_evm_legacy_init, }, + { "technexion,omap3-tao3530", omap3_tao3530_legacy_init, }, + { "openpandora,omap3-pandora-600mhz", omap3_pandora_legacy_init, }, + { "openpandora,omap3-pandora-1ghz", omap3_pandora_legacy_init, }, +#endif + { /* sentinel */ }, +}; + +static void pdata_quirks_check(struct pdata_init *quirks) +{ + while (quirks->compatible) { + if (of_machine_is_compatible(quirks->compatible)) { + if (quirks->fn) + quirks->fn(); + } + quirks++; + } +} + +static const char * const pdata_quirks_init_nodes[] = { + "prcm", + "prm", +}; + +static void __init +pdata_quirks_init_clocks(const struct of_device_id *omap_dt_match_table) +{ + struct device_node *np; + int i; + + for (i = 0; i < ARRAY_SIZE(pdata_quirks_init_nodes); i++) { + np = of_find_node_by_name(NULL, pdata_quirks_init_nodes[i]); + if (!np) + continue; + + of_platform_populate(np, omap_dt_match_table, + omap_auxdata_lookup, NULL); + + of_node_put(np); + } +} + +void __init pdata_quirks_init(const struct of_device_id *omap_dt_match_table) +{ + /* + * We still need this for omap2420 and omap3 PM to work, others are + * using drivers/misc/sram.c already. + */ + if (of_machine_is_compatible("ti,omap2420") || + of_machine_is_compatible("ti,omap3")) + omap_sdrc_init(NULL, NULL); + + if (of_machine_is_compatible("ti,omap3")) + omap3_mcbsp_init(); + pdata_quirks_check(auxdata_quirks); + + pdata_quirks_init_clocks(omap_dt_match_table); + + of_platform_populate(NULL, omap_dt_match_table, + omap_auxdata_lookup, NULL); + pdata_quirks_check(pdata_quirks); +} diff --git a/arch/arm/mach-omap2/pm-asm-offsets.c b/arch/arm/mach-omap2/pm-asm-offsets.c new file mode 100644 index 000000000..d8ae8a85b --- /dev/null +++ b/arch/arm/mach-omap2/pm-asm-offsets.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI AM33XX and AM43XX PM Assembly Offsets + * + * Copyright (C) 2017-2018 Texas Instruments Inc. + */ + +#include <linux/kbuild.h> +#include <linux/platform_data/pm33xx.h> +#include <linux/ti-emif-sram.h> + +int main(void) +{ + ti_emif_asm_offsets(); + + DEFINE(AMX3_PM_WFI_FLAGS_OFFSET, + offsetof(struct am33xx_pm_sram_data, wfi_flags)); + DEFINE(AMX3_PM_L2_AUX_CTRL_VAL_OFFSET, + offsetof(struct am33xx_pm_sram_data, l2_aux_ctrl_val)); + DEFINE(AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET, + offsetof(struct am33xx_pm_sram_data, l2_prefetch_ctrl_val)); + DEFINE(AMX3_PM_SRAM_DATA_SIZE, sizeof(struct am33xx_pm_sram_data)); + + BLANK(); + + DEFINE(AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET, + offsetof(struct am33xx_pm_ro_sram_data, amx3_pm_sram_data_virt)); + DEFINE(AMX3_PM_RO_SRAM_DATA_PHYS_OFFSET, + offsetof(struct am33xx_pm_ro_sram_data, amx3_pm_sram_data_phys)); + DEFINE(AMX3_PM_RTC_BASE_VIRT_OFFSET, + offsetof(struct am33xx_pm_ro_sram_data, rtc_base_virt)); + DEFINE(AMX3_PM_RO_SRAM_DATA_SIZE, + sizeof(struct am33xx_pm_ro_sram_data)); + + return 0; +} diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c new file mode 100644 index 000000000..b43eab987 --- /dev/null +++ b/arch/arm/mach-omap2/pm-debug.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP Power Management debug routines + * + * Copyright (C) 2005 Texas Instruments, Inc. + * Copyright (C) 2006-2008 Nokia Corporation + * + * Written by: + * Richard Woodruff <r-woodruff2@ti.com> + * Tony Lindgren + * Juha Yrjola + * Amit Kucheria <amit.kucheria@nokia.com> + * Igor Stoppa <igor.stoppa@nokia.com> + * Jouni Hogander + * + * Based on pm.c for omap2 + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/sched/clock.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "clock.h" +#include "powerdomain.h" +#include "clockdomain.h" + +#include "soc.h" +#include "cm2xxx_3xxx.h" +#include "prm2xxx_3xxx.h" +#include "pm.h" + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +static int pm_dbg_init_done; + +static int pm_dbg_init(void); + +static const char pwrdm_state_names[][PWRDM_MAX_PWRSTS] = { + "OFF", + "RET", + "INA", + "ON" +}; + +void pm_dbg_update_time(struct powerdomain *pwrdm, int prev) +{ + s64 t; + + if (!pm_dbg_init_done) + return ; + + /* Update timer for previous state */ + t = sched_clock(); + + pwrdm->state_timer[prev] += t - pwrdm->timer; + + pwrdm->timer = t; +} + +static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user) +{ + struct seq_file *s = (struct seq_file *)user; + + if (strcmp(clkdm->name, "emu_clkdm") == 0 || + strcmp(clkdm->name, "wkup_clkdm") == 0 || + strncmp(clkdm->name, "dpll", 4) == 0) + return 0; + + seq_printf(s, "%s->%s (%d)\n", clkdm->name, clkdm->pwrdm.ptr->name, + clkdm->usecount); + + return 0; +} + +static int pwrdm_dbg_show_counter(struct powerdomain *pwrdm, void *user) +{ + struct seq_file *s = (struct seq_file *)user; + int i; + + if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || + strcmp(pwrdm->name, "wkup_pwrdm") == 0 || + strncmp(pwrdm->name, "dpll", 4) == 0) + return 0; + + if (pwrdm->state != pwrdm_read_pwrst(pwrdm)) + printk(KERN_ERR "pwrdm state mismatch(%s) %d != %d\n", + pwrdm->name, pwrdm->state, pwrdm_read_pwrst(pwrdm)); + + seq_printf(s, "%s (%s)", pwrdm->name, + pwrdm_state_names[pwrdm->state]); + for (i = 0; i < PWRDM_MAX_PWRSTS; i++) + seq_printf(s, ",%s:%d", pwrdm_state_names[i], + pwrdm->state_counter[i]); + + seq_printf(s, ",RET-LOGIC-OFF:%d", pwrdm->ret_logic_off_counter); + for (i = 0; i < pwrdm->banks; i++) + seq_printf(s, ",RET-MEMBANK%d-OFF:%d", i + 1, + pwrdm->ret_mem_off_counter[i]); + + seq_putc(s, '\n'); + return 0; +} + +static int pwrdm_dbg_show_timer(struct powerdomain *pwrdm, void *user) +{ + struct seq_file *s = (struct seq_file *)user; + int i; + + if (strcmp(pwrdm->name, "emu_pwrdm") == 0 || + strcmp(pwrdm->name, "wkup_pwrdm") == 0 || + strncmp(pwrdm->name, "dpll", 4) == 0) + return 0; + + pwrdm_state_switch(pwrdm); + + seq_printf(s, "%s (%s)", pwrdm->name, + pwrdm_state_names[pwrdm->state]); + + for (i = 0; i < 4; i++) + seq_printf(s, ",%s:%lld", pwrdm_state_names[i], + pwrdm->state_timer[i]); + + seq_putc(s, '\n'); + return 0; +} + +static int pm_dbg_counters_show(struct seq_file *s, void *unused) +{ + pwrdm_for_each(pwrdm_dbg_show_counter, s); + clkdm_for_each(clkdm_dbg_show_counter, s); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(pm_dbg_counters); + +static int pm_dbg_timers_show(struct seq_file *s, void *unused) +{ + pwrdm_for_each(pwrdm_dbg_show_timer, s); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(pm_dbg_timers); + +static int pwrdm_suspend_get(void *data, u64 *val) +{ + int ret = -EINVAL; + + if (cpu_is_omap34xx()) + ret = omap3_pm_get_suspend_state((struct powerdomain *)data); + *val = ret; + + if (ret >= 0) + return 0; + return *val; +} + +static int pwrdm_suspend_set(void *data, u64 val) +{ + if (cpu_is_omap34xx()) + return omap3_pm_set_suspend_state( + (struct powerdomain *)data, (int)val); + return -EINVAL; +} + +DEFINE_DEBUGFS_ATTRIBUTE(pwrdm_suspend_fops, pwrdm_suspend_get, + pwrdm_suspend_set, "%llu\n"); + +static int __init pwrdms_setup(struct powerdomain *pwrdm, void *dir) +{ + int i; + s64 t; + struct dentry *d; + + t = sched_clock(); + + for (i = 0; i < 4; i++) + pwrdm->state_timer[i] = 0; + + pwrdm->timer = t; + + if (strncmp(pwrdm->name, "dpll", 4) == 0) + return 0; + + d = debugfs_create_dir(pwrdm->name, (struct dentry *)dir); + debugfs_create_file("suspend", S_IRUGO|S_IWUSR, d, pwrdm, + &pwrdm_suspend_fops); + + return 0; +} + +static int option_get(void *data, u64 *val) +{ + u32 *option = data; + + *val = *option; + + return 0; +} + +static int option_set(void *data, u64 val) +{ + u32 *option = data; + + *option = val; + + if (option == &enable_off_mode) { + if (cpu_is_omap34xx()) + omap3_pm_off_mode_enable(val); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(pm_dbg_option_fops, option_get, option_set, "%llu\n"); + +static int __init pm_dbg_init(void) +{ + struct dentry *d; + + if (pm_dbg_init_done) + return 0; + + d = debugfs_create_dir("pm_debug", NULL); + + debugfs_create_file("count", 0444, d, NULL, &pm_dbg_counters_fops); + debugfs_create_file("time", 0444, d, NULL, &pm_dbg_timers_fops); + + pwrdm_for_each(pwrdms_setup, (void *)d); + + debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d, + &enable_off_mode, &pm_dbg_option_fops); + pm_dbg_init_done = 1; + + return 0; +} +omap_arch_initcall(pm_dbg_init); + +#endif diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c new file mode 100644 index 000000000..da829a90f --- /dev/null +++ b/arch/arm/mach-omap2/pm.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * pm.c - Common OMAP2+ power management-related code + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/pm_opp.h> +#include <linux/export.h> +#include <linux/suspend.h> +#include <linux/clk.h> +#include <linux/cpu.h> + +#include <asm/system_misc.h> + +#include "omap_device.h" +#include "common.h" + +#include "soc.h" +#include "prcm-common.h" +#include "voltage.h" +#include "powerdomain.h" +#include "clockdomain.h" +#include "pm.h" + +u32 enable_off_mode; + +#ifdef CONFIG_SUSPEND +/* + * omap_pm_suspend: points to a function that does the SoC-specific + * suspend work + */ +static int (*omap_pm_suspend)(void); +#endif + +#ifdef CONFIG_PM +/** + * struct omap2_oscillator - Describe the board main oscillator latencies + * @startup_time: oscillator startup latency + * @shutdown_time: oscillator shutdown latency + */ +struct omap2_oscillator { + u32 startup_time; + u32 shutdown_time; +}; + +static struct omap2_oscillator oscillator = { + .startup_time = ULONG_MAX, + .shutdown_time = ULONG_MAX, +}; + +void omap_pm_setup_oscillator(u32 tstart, u32 tshut) +{ + oscillator.startup_time = tstart; + oscillator.shutdown_time = tshut; +} + +void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) +{ + if (!tstart || !tshut) + return; + + *tstart = oscillator.startup_time; + *tshut = oscillator.shutdown_time; +} +#endif + +int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) +{ + clkdm_allow_idle(clkdm); + return 0; +} + +#ifdef CONFIG_SUSPEND +static int omap_pm_enter(suspend_state_t suspend_state) +{ + int ret = 0; + + if (!omap_pm_suspend) + return -ENOENT; /* XXX doublecheck */ + + switch (suspend_state) { + case PM_SUSPEND_MEM: + ret = omap_pm_suspend(); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int omap_pm_begin(suspend_state_t state) +{ + cpu_idle_poll_ctrl(true); + if (soc_is_omap34xx()) + omap_prcm_irq_prepare(); + return 0; +} + +static void omap_pm_end(void) +{ + cpu_idle_poll_ctrl(false); +} + +static void omap_pm_wake(void) +{ + if (soc_is_omap34xx()) + omap_prcm_irq_complete(); +} + +static const struct platform_suspend_ops omap_pm_ops = { + .begin = omap_pm_begin, + .end = omap_pm_end, + .enter = omap_pm_enter, + .wake = omap_pm_wake, + .valid = suspend_valid_only_mem, +}; + +/** + * omap_common_suspend_init - Set common suspend routines for OMAP SoCs + * @pm_suspend: function pointer to SoC specific suspend function + */ +void omap_common_suspend_init(void *pm_suspend) +{ + omap_pm_suspend = pm_suspend; + suspend_set_ops(&omap_pm_ops); +} +#endif /* CONFIG_SUSPEND */ + +int __maybe_unused omap_pm_nop_init(void) +{ + return 0; +} + +int (*omap_pm_soc_init)(void); + +int __init omap2_common_pm_late_init(void) +{ + int error; + + if (!omap_pm_soc_init) + return 0; + + /* Init the voltage layer */ + omap3_twl_init(); + omap4_twl_init(); + omap4_cpcap_init(); + omap_voltage_late_init(); + + /* Smartreflex device init */ + omap_devinit_smartreflex(); + + error = omap_pm_soc_init(); + if (error) + pr_warn("%s: pm soc init failed: %i\n", __func__, error); + + omap2_clk_enable_autoidle_all(); + + return 0; +} +omap_late_initcall(omap2_common_pm_late_init); diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h new file mode 100644 index 000000000..80e84ae66 --- /dev/null +++ b/arch/arm/mach-omap2/pm.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2/3 Power Management Routines + * + * Copyright (C) 2008 Nokia Corporation + * Jouni Hogander + */ +#ifndef __ARCH_ARM_MACH_OMAP2_PM_H +#define __ARCH_ARM_MACH_OMAP2_PM_H + +#include <linux/err.h> + +#include "powerdomain.h" + +#ifdef CONFIG_CPU_IDLE +extern int __init omap3_idle_init(void); +extern int __init omap4_idle_init(void); +#else +static inline int omap3_idle_init(void) +{ + return 0; +} + +static inline int omap4_idle_init(void) +{ + return 0; +} +#endif + +extern void *omap3_secure_ram_storage; +extern void omap3_pm_off_mode_enable(int); +extern void omap_sram_idle(void); +extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused); + +#if defined(CONFIG_PM_OPP) +extern int omap3_opp_init(void); +extern int omap4_opp_init(void); +#else +static inline int omap3_opp_init(void) +{ + return -EINVAL; +} +static inline int omap4_opp_init(void) +{ + return -EINVAL; +} +#endif + +extern int omap3_pm_get_suspend_state(struct powerdomain *pwrdm); +extern int omap3_pm_set_suspend_state(struct powerdomain *pwrdm, int state); + +extern u32 enable_off_mode; + +#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) +extern void pm_dbg_update_time(struct powerdomain *pwrdm, int prev); +#else +#define pm_dbg_update_time(pwrdm, prev) do {} while (0); +#endif /* CONFIG_PM_DEBUG */ + +/* 24xx */ +extern void omap24xx_idle_loop_suspend(void); +extern unsigned int omap24xx_idle_loop_suspend_sz; + +extern void omap24xx_cpu_suspend(u32 dll_ctrl, void __iomem *sdrc_dlla_ctrl, + void __iomem *sdrc_power); +extern unsigned int omap24xx_cpu_suspend_sz; + +/* 3xxx */ +extern void omap34xx_cpu_suspend(int save_state); + +/* omap3_do_wfi function pointer and size, for copy to SRAM */ +extern void omap3_do_wfi(void); +extern unsigned int omap3_do_wfi_sz; +/* ... and its pointer from SRAM after copy */ +extern void (*omap3_do_wfi_sram)(void); + +extern struct am33xx_pm_sram_addr am33xx_pm_sram; +extern struct am33xx_pm_sram_addr am43xx_pm_sram; + +extern void omap3_save_scratchpad_contents(void); + +#define PM_RTA_ERRATUM_i608 (1 << 0) +#define PM_SDRC_WAKEUP_ERRATUM_i583 (1 << 1) +#define PM_PER_MEMORIES_ERRATUM_i582 (1 << 2) + +#if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP3) +extern u16 pm34xx_errata; +#define IS_PM34XX_ERRATUM(id) (pm34xx_errata & (id)) +extern void enable_omap3630_toggle_l2_on_restore(void); +#else +#define IS_PM34XX_ERRATUM(id) 0 +static inline void enable_omap3630_toggle_l2_on_restore(void) { } +#endif /* defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP3) */ + +#define PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD (1 << 0) +#define PM_OMAP4_CPU_OSWR_DISABLE (1 << 1) + +#if defined(CONFIG_PM) && (defined(CONFIG_ARCH_OMAP4) ||\ + defined(CONFIG_SOC_OMAP5) || defined(CONFIG_SOC_DRA7XX)) +extern u16 pm44xx_errata; +#define IS_PM44XX_ERRATUM(id) (pm44xx_errata & (id)) +#else +#define IS_PM44XX_ERRATUM(id) 0 +#endif + +#define OMAP4_VP_CONFIG_ERROROFFSET 0x00 +#define OMAP4_VP_VSTEPMIN_VSTEPMIN 0x01 +#define OMAP4_VP_VSTEPMAX_VSTEPMAX 0x04 +#define OMAP4_VP_VLIMITTO_TIMEOUT_US 200 + +#ifdef CONFIG_POWER_AVS_OMAP +extern int omap_devinit_smartreflex(void); +extern void omap_enable_smartreflex_on_init(void); +#else +static inline int omap_devinit_smartreflex(void) +{ + return -EINVAL; +} + +static inline void omap_enable_smartreflex_on_init(void) {} +#endif + +#ifdef CONFIG_TWL4030_CORE +extern int omap3_twl_init(void); +extern int omap4_twl_init(void); +extern int omap3_twl_set_sr_bit(bool enable); +#else +static inline int omap3_twl_init(void) +{ + return -EINVAL; +} +static inline int omap4_twl_init(void) +{ + return -EINVAL; +} +#endif + +#if IS_ENABLED(CONFIG_MFD_CPCAP) +extern int omap4_cpcap_init(void); +#else +static inline int omap4_cpcap_init(void) +{ + return -EINVAL; +} +#endif + +#ifdef CONFIG_PM +extern void omap_pm_setup_oscillator(u32 tstart, u32 tshut); +extern void omap_pm_get_oscillator(u32 *tstart, u32 *tshut); +extern void omap_pm_setup_sr_i2c_pcb_length(u32 mm); +#else +static inline void omap_pm_setup_oscillator(u32 tstart, u32 tshut) { } +static inline void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) { *tstart = *tshut = 0; } +static inline void omap_pm_setup_sr_i2c_pcb_length(u32 mm) { } +#endif + +#ifdef CONFIG_SUSPEND +void omap_common_suspend_init(void *pm_suspend); +#else +static inline void omap_common_suspend_init(void *pm_suspend) +{ +} +#endif /* CONFIG_SUSPEND */ +#endif diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c new file mode 100644 index 000000000..6953c47d8 --- /dev/null +++ b/arch/arm/mach-omap2/pm24xx.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2 Power Management Routines + * + * Copyright (C) 2005 Texas Instruments, Inc. + * Copyright (C) 2006-2008 Nokia Corporation + * + * Written by: + * Richard Woodruff <r-woodruff2@ti.com> + * Tony Lindgren + * Juha Yrjola + * Amit Kucheria <amit.kucheria@nokia.com> + * Igor Stoppa <igor.stoppa@nokia.com> + * + * Based on pm.c for omap1 + */ + +#include <linux/cpu_pm.h> +#include <linux/suspend.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/interrupt.h> +#include <linux/sysfs.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/irq.h> +#include <linux/time.h> + +#include <asm/fncpy.h> + +#include <asm/mach/time.h> +#include <asm/mach/irq.h> +#include <asm/mach-types.h> +#include <asm/system_misc.h> + +#include <linux/omap-dma.h> + +#include "soc.h" +#include "common.h" +#include "clock.h" +#include "prm2xxx.h" +#include "prm-regbits-24xx.h" +#include "cm2xxx.h" +#include "cm-regbits-24xx.h" +#include "sdrc.h" +#include "sram.h" +#include "pm.h" +#include "control.h" +#include "powerdomain.h" +#include "clockdomain.h" + +static void (*omap2_sram_suspend)(u32 dllctrl, void __iomem *sdrc_dlla_ctrl, + void __iomem *sdrc_power); + +static struct powerdomain *mpu_pwrdm, *core_pwrdm; +static struct clockdomain *dsp_clkdm, *mpu_clkdm, *wkup_clkdm, *gfx_clkdm; + +static struct clk *osc_ck, *emul_ck; + +static int omap2_enter_full_retention(void) +{ + u32 l; + + /* There is 1 reference hold for all children of the oscillator + * clock, the following will remove it. If no one else uses the + * oscillator itself it will be disabled if/when we enter retention + * mode. + */ + clk_disable(osc_ck); + + /* Clear old wake-up events */ + /* REVISIT: These write to reserved bits? */ + omap_prm_clear_mod_irqs(CORE_MOD, PM_WKST1, ~0); + omap_prm_clear_mod_irqs(CORE_MOD, OMAP24XX_PM_WKST2, ~0); + omap_prm_clear_mod_irqs(WKUP_MOD, PM_WKST, ~0); + + pwrdm_set_next_pwrst(core_pwrdm, PWRDM_POWER_RET); + pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET); + + /* Workaround to kill USB */ + l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL; + omap_ctrl_writel(l, OMAP2_CONTROL_DEVCONF0); + + /* One last check for pending IRQs to avoid extra latency due + * to sleeping unnecessarily. */ + if (omap_irq_pending()) + goto no_sleep; + + /* Jump to SRAM suspend code */ + omap2_sram_suspend(sdrc_read_reg(SDRC_DLLA_CTRL), + OMAP_SDRC_REGADDR(SDRC_DLLA_CTRL), + OMAP_SDRC_REGADDR(SDRC_POWER)); + +no_sleep: + clk_enable(osc_ck); + + /* clear CORE wake-up events */ + omap_prm_clear_mod_irqs(CORE_MOD, PM_WKST1, ~0); + omap_prm_clear_mod_irqs(CORE_MOD, OMAP24XX_PM_WKST2, ~0); + + /* wakeup domain events - bit 1: GPT1, bit5 GPIO */ + omap_prm_clear_mod_irqs(WKUP_MOD, PM_WKST, 0x4 | 0x1); + + /* MPU domain wake events */ + omap_prm_clear_mod_irqs(OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET, 0x1); + + omap_prm_clear_mod_irqs(OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET, 0x20); + + pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON); + pwrdm_set_next_pwrst(core_pwrdm, PWRDM_POWER_ON); + + return 0; +} + +static int sti_console_enabled; + +static int omap2_allow_mpu_retention(void) +{ + if (!omap2xxx_cm_mpu_retention_allowed()) + return 0; + if (sti_console_enabled) + return 0; + + return 1; +} + +static void omap2_enter_mpu_retention(void) +{ + const int zero = 0; + + /* The peripherals seem not to be able to wake up the MPU when + * it is in retention mode. */ + if (omap2_allow_mpu_retention()) { + /* REVISIT: These write to reserved bits? */ + omap_prm_clear_mod_irqs(CORE_MOD, PM_WKST1, ~0); + omap_prm_clear_mod_irqs(CORE_MOD, OMAP24XX_PM_WKST2, ~0); + omap_prm_clear_mod_irqs(WKUP_MOD, PM_WKST, ~0); + + /* Try to enter MPU retention */ + pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET); + + } else { + /* Block MPU retention */ + pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON); + } + + /* WFI */ + asm("mcr p15, 0, %0, c7, c0, 4" : : "r" (zero) : "memory", "cc"); + + pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON); +} + +static int omap2_can_sleep(void) +{ + if (omap2xxx_cm_fclks_active()) + return 0; + if (__clk_is_enabled(osc_ck)) + return 0; + + return 1; +} + +static void omap2_pm_idle(void) +{ + int error; + + if (omap_irq_pending()) + return; + + error = cpu_cluster_pm_enter(); + if (error || !omap2_can_sleep()) { + omap2_enter_mpu_retention(); + goto out_cpu_cluster_pm; + } + + omap2_enter_full_retention(); + +out_cpu_cluster_pm: + cpu_cluster_pm_exit(); +} + +static void __init prcm_setup_regs(void) +{ + int i, num_mem_banks; + struct powerdomain *pwrdm; + + /* + * Enable autoidle + * XXX This should be handled by hwmod code or PRCM init code + */ + omap2_prm_write_mod_reg(OMAP24XX_AUTOIDLE_MASK, OCP_MOD, + OMAP2_PRCM_SYSCONFIG_OFFSET); + + /* + * Set CORE powerdomain memory banks to retain their contents + * during RETENTION + */ + num_mem_banks = pwrdm_get_mem_bank_count(core_pwrdm); + for (i = 0; i < num_mem_banks; i++) + pwrdm_set_mem_retst(core_pwrdm, i, PWRDM_POWER_RET); + + pwrdm_set_logic_retst(core_pwrdm, PWRDM_POWER_RET); + + pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET); + + /* Force-power down DSP, GFX powerdomains */ + + pwrdm = clkdm_get_pwrdm(dsp_clkdm); + pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF); + + pwrdm = clkdm_get_pwrdm(gfx_clkdm); + pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF); + + /* Enable hardware-supervised idle for all clkdms */ + clkdm_for_each(omap_pm_clkdms_setup, NULL); + clkdm_add_wkdep(mpu_clkdm, wkup_clkdm); + + omap_common_suspend_init(omap2_enter_full_retention); + + /* REVISIT: Configure number of 32 kHz clock cycles for sys_clk + * stabilisation */ + omap2_prm_write_mod_reg(15 << OMAP_SETUP_TIME_SHIFT, OMAP24XX_GR_MOD, + OMAP2_PRCM_CLKSSETUP_OFFSET); + + /* Configure automatic voltage transition */ + omap2_prm_write_mod_reg(2 << OMAP_SETUP_TIME_SHIFT, OMAP24XX_GR_MOD, + OMAP2_PRCM_VOLTSETUP_OFFSET); + omap2_prm_write_mod_reg(OMAP24XX_AUTO_EXTVOLT_MASK | + (0x1 << OMAP24XX_SETOFF_LEVEL_SHIFT) | + OMAP24XX_MEMRETCTRL_MASK | + (0x1 << OMAP24XX_SETRET_LEVEL_SHIFT) | + (0x0 << OMAP24XX_VOLT_LEVEL_SHIFT), + OMAP24XX_GR_MOD, OMAP2_PRCM_VOLTCTRL_OFFSET); + + /* Enable wake-up events */ + omap2_prm_write_mod_reg(OMAP24XX_EN_GPIOS_MASK | OMAP24XX_EN_GPT1_MASK, + WKUP_MOD, PM_WKEN); + + /* Enable SYS_CLKEN control when all domains idle */ + omap2_prm_set_mod_reg_bits(OMAP_AUTOEXTCLKMODE_MASK, OMAP24XX_GR_MOD, + OMAP2_PRCM_CLKSRC_CTRL_OFFSET); +} + +int __init omap2_pm_init(void) +{ + u32 l; + + printk(KERN_INFO "Power Management for OMAP2 initializing\n"); + l = omap2_prm_read_mod_reg(OCP_MOD, OMAP2_PRCM_REVISION_OFFSET); + printk(KERN_INFO "PRCM revision %d.%d\n", (l >> 4) & 0x0f, l & 0x0f); + + /* Look up important powerdomains */ + + mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); + if (!mpu_pwrdm) + pr_err("PM: mpu_pwrdm not found\n"); + + core_pwrdm = pwrdm_lookup("core_pwrdm"); + if (!core_pwrdm) + pr_err("PM: core_pwrdm not found\n"); + + /* Look up important clockdomains */ + + mpu_clkdm = clkdm_lookup("mpu_clkdm"); + if (!mpu_clkdm) + pr_err("PM: mpu_clkdm not found\n"); + + wkup_clkdm = clkdm_lookup("wkup_clkdm"); + if (!wkup_clkdm) + pr_err("PM: wkup_clkdm not found\n"); + + dsp_clkdm = clkdm_lookup("dsp_clkdm"); + if (!dsp_clkdm) + pr_err("PM: dsp_clkdm not found\n"); + + gfx_clkdm = clkdm_lookup("gfx_clkdm"); + if (!gfx_clkdm) + pr_err("PM: gfx_clkdm not found\n"); + + + osc_ck = clk_get(NULL, "osc_ck"); + if (IS_ERR(osc_ck)) { + printk(KERN_ERR "could not get osc_ck\n"); + return -ENODEV; + } + + if (cpu_is_omap242x()) { + emul_ck = clk_get(NULL, "emul_ck"); + if (IS_ERR(emul_ck)) { + printk(KERN_ERR "could not get emul_ck\n"); + clk_put(osc_ck); + return -ENODEV; + } + } + + prcm_setup_regs(); + + /* + * We copy the assembler sleep/wakeup routines to SRAM. + * These routines need to be in SRAM as that's the only + * memory the MPU can see when it wakes up after the entire + * chip enters idle. + */ + omap2_sram_suspend = omap_sram_push(omap24xx_cpu_suspend, + omap24xx_cpu_suspend_sz); + + arm_pm_idle = omap2_pm_idle; + + return 0; +} diff --git a/arch/arm/mach-omap2/pm33xx-core.c b/arch/arm/mach-omap2/pm33xx-core.c new file mode 100644 index 000000000..bf0d25fd2 --- /dev/null +++ b/arch/arm/mach-omap2/pm33xx-core.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AM33XX Arch Power Management Routines + * + * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ + * Dave Gerlach + */ + +#include <linux/cpuidle.h> +#include <linux/platform_data/pm33xx.h> +#include <linux/suspend.h> +#include <asm/cpuidle.h> +#include <asm/smp_scu.h> +#include <asm/suspend.h> +#include <linux/errno.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/platform_data/gpio-omap.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/wkup_m3_ipc.h> +#include <linux/of.h> +#include <linux/rtc.h> + +#include "cm33xx.h" +#include "common.h" +#include "control.h" +#include "clockdomain.h" +#include "iomap.h" +#include "pm.h" +#include "powerdomain.h" +#include "prm33xx.h" +#include "soc.h" +#include "sram.h" +#include "omap-secure.h" + +static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm; +static struct clockdomain *gfx_l4ls_clkdm; +static void __iomem *scu_base; + +static int (*idle_fn)(u32 wfi_flags); + +struct amx3_idle_state { + int wfi_flags; +}; + +static struct amx3_idle_state *idle_states; + +static int am43xx_map_scu(void) +{ + scu_base = ioremap(scu_a9_get_base(), SZ_256); + + if (!scu_base) + return -ENOMEM; + + return 0; +} + +static int am33xx_check_off_mode_enable(void) +{ + if (enable_off_mode) + pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n"); + + /* off mode not supported on am335x so return 0 always */ + return 0; +} + +static int am43xx_check_off_mode_enable(void) +{ + /* + * Check for am437x-gp-evm which has the right Hardware design to + * support this mode reliably. + */ + if (of_machine_is_compatible("ti,am437x-gp-evm") && enable_off_mode) + return enable_off_mode; + else if (enable_off_mode) + pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n"); + + return 0; +} + +static int amx3_common_init(int (*idle)(u32 wfi_flags)) +{ + gfx_pwrdm = pwrdm_lookup("gfx_pwrdm"); + per_pwrdm = pwrdm_lookup("per_pwrdm"); + mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); + + if ((!gfx_pwrdm) || (!per_pwrdm) || (!mpu_pwrdm)) + return -ENODEV; + + (void)clkdm_for_each(omap_pm_clkdms_setup, NULL); + + /* CEFUSE domain can be turned off post bootup */ + cefuse_pwrdm = pwrdm_lookup("cefuse_pwrdm"); + if (!cefuse_pwrdm) + pr_err("PM: Failed to get cefuse_pwrdm\n"); + else if (omap_type() != OMAP2_DEVICE_TYPE_GP) + pr_info("PM: Leaving EFUSE power domain active\n"); + else + omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF); + + idle_fn = idle; + + return 0; +} + +static int am33xx_suspend_init(int (*idle)(u32 wfi_flags)) +{ + int ret; + + gfx_l4ls_clkdm = clkdm_lookup("gfx_l4ls_gfx_clkdm"); + + if (!gfx_l4ls_clkdm) { + pr_err("PM: Cannot lookup gfx_l4ls_clkdm clockdomains\n"); + return -ENODEV; + } + + ret = amx3_common_init(idle); + + return ret; +} + +static int am43xx_suspend_init(int (*idle)(u32 wfi_flags)) +{ + int ret = 0; + + ret = am43xx_map_scu(); + if (ret) { + pr_err("PM: Could not ioremap SCU\n"); + return ret; + } + + ret = amx3_common_init(idle); + + return ret; +} + +static int amx3_suspend_deinit(void) +{ + idle_fn = NULL; + return 0; +} + +static void amx3_pre_suspend_common(void) +{ + omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF); +} + +static void amx3_post_suspend_common(void) +{ + int status; + /* + * Because gfx_pwrdm is the only one under MPU control, + * comment on transition status + */ + status = pwrdm_read_pwrst(gfx_pwrdm); + if (status != PWRDM_POWER_OFF) + pr_err("PM: GFX domain did not transition: %x\n", status); +} + +static int am33xx_suspend(unsigned int state, int (*fn)(unsigned long), + unsigned long args) +{ + int ret = 0; + + amx3_pre_suspend_common(); + ret = cpu_suspend(args, fn); + amx3_post_suspend_common(); + + /* + * BUG: GFX_L4LS clock domain needs to be woken up to + * ensure thet L4LS clock domain does not get stuck in + * transition. If that happens L3 module does not get + * disabled, thereby leading to PER power domain + * transition failing + */ + + clkdm_wakeup(gfx_l4ls_clkdm); + clkdm_sleep(gfx_l4ls_clkdm); + + return ret; +} + +static int am43xx_suspend(unsigned int state, int (*fn)(unsigned long), + unsigned long args) +{ + int ret = 0; + + /* Suspend secure side on HS devices */ + if (omap_type() != OMAP2_DEVICE_TYPE_GP) { + if (optee_available) + omap_smccc_smc(AM43xx_PPA_SVC_PM_SUSPEND, 0); + else + omap_secure_dispatcher(AM43xx_PPA_SVC_PM_SUSPEND, + FLAG_START_CRITICAL, + 0, 0, 0, 0, 0); + } + + amx3_pre_suspend_common(); + scu_power_mode(scu_base, SCU_PM_POWEROFF); + ret = cpu_suspend(args, fn); + scu_power_mode(scu_base, SCU_PM_NORMAL); + + if (!am43xx_check_off_mode_enable()) + amx3_post_suspend_common(); + + /* + * Resume secure side on HS devices. + * + * Note that even on systems with OP-TEE available this resume call is + * issued to the ROM. This is because upon waking from suspend the ROM + * is restored as the secure monitor. On systems with OP-TEE ROM will + * restore OP-TEE during this call. + */ + if (omap_type() != OMAP2_DEVICE_TYPE_GP) + omap_secure_dispatcher(AM43xx_PPA_SVC_PM_RESUME, + FLAG_START_CRITICAL, + 0, 0, 0, 0, 0); + + return ret; +} + +static int am33xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args) +{ + int ret = 0; + + if (omap_irq_pending() || need_resched()) + return ret; + + ret = cpu_suspend(args, fn); + + return ret; +} + +static int am43xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args) +{ + int ret = 0; + + if (!scu_base) + return 0; + + scu_power_mode(scu_base, SCU_PM_DORMANT); + ret = cpu_suspend(args, fn); + scu_power_mode(scu_base, SCU_PM_NORMAL); + + return ret; +} + +static void amx3_begin_suspend(void) +{ + cpu_idle_poll_ctrl(true); +} + +static void amx3_finish_suspend(void) +{ + cpu_idle_poll_ctrl(false); +} + + +static struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void) +{ + if (soc_is_am33xx()) + return &am33xx_pm_sram; + else if (soc_is_am437x()) + return &am43xx_pm_sram; + else + return NULL; +} + +static void am43xx_save_context(void) +{ +} + +static void am33xx_save_context(void) +{ + omap_intc_save_context(); +} + +static void am33xx_restore_context(void) +{ + omap_intc_restore_context(); +} + +static void am43xx_restore_context(void) +{ + /* + * HACK: restore dpll_per_clkdcoldo register contents, to avoid + * breaking suspend-resume + */ + writel_relaxed(0x0, AM33XX_L4_WK_IO_ADDRESS(0x44df2e14)); +} + +static struct am33xx_pm_platform_data am33xx_ops = { + .init = am33xx_suspend_init, + .deinit = amx3_suspend_deinit, + .soc_suspend = am33xx_suspend, + .cpu_suspend = am33xx_cpu_suspend, + .begin_suspend = amx3_begin_suspend, + .finish_suspend = amx3_finish_suspend, + .get_sram_addrs = amx3_get_sram_addrs, + .save_context = am33xx_save_context, + .restore_context = am33xx_restore_context, + .check_off_mode_enable = am33xx_check_off_mode_enable, +}; + +static struct am33xx_pm_platform_data am43xx_ops = { + .init = am43xx_suspend_init, + .deinit = amx3_suspend_deinit, + .soc_suspend = am43xx_suspend, + .cpu_suspend = am43xx_cpu_suspend, + .begin_suspend = amx3_begin_suspend, + .finish_suspend = amx3_finish_suspend, + .get_sram_addrs = amx3_get_sram_addrs, + .save_context = am43xx_save_context, + .restore_context = am43xx_restore_context, + .check_off_mode_enable = am43xx_check_off_mode_enable, +}; + +static struct am33xx_pm_platform_data *am33xx_pm_get_pdata(void) +{ + if (soc_is_am33xx()) + return &am33xx_ops; + else if (soc_is_am437x()) + return &am43xx_ops; + else + return NULL; +} + +#ifdef CONFIG_SUSPEND +/* + * Block system suspend initially. Later on pm33xx sets up it's own + * platform_suspend_ops after probe. That depends also on loaded + * wkup_m3_ipc and booted am335x-pm-firmware.elf. + */ +static int amx3_suspend_block(suspend_state_t state) +{ + pr_warn("PM not initialized for pm33xx, wkup_m3_ipc, or am335x-pm-firmware.elf\n"); + + return -EINVAL; +} + +static int amx3_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + return 1; + default: + return 0; + } +} + +static const struct platform_suspend_ops amx3_blocked_pm_ops = { + .begin = amx3_suspend_block, + .valid = amx3_pm_valid, +}; + +static void __init amx3_block_suspend(void) +{ + suspend_set_ops(&amx3_blocked_pm_ops); +} +#else +static inline void amx3_block_suspend(void) +{ +} +#endif /* CONFIG_SUSPEND */ + +int __init amx3_common_pm_init(void) +{ + struct am33xx_pm_platform_data *pdata; + struct platform_device_info devinfo; + + pdata = am33xx_pm_get_pdata(); + + memset(&devinfo, 0, sizeof(devinfo)); + devinfo.name = "pm33xx"; + devinfo.data = pdata; + devinfo.size_data = sizeof(*pdata); + devinfo.id = -1; + platform_device_register_full(&devinfo); + amx3_block_suspend(); + + return 0; +} + +static int __init amx3_idle_init(struct device_node *cpu_node, int cpu) +{ + struct device_node *state_node; + struct amx3_idle_state states[CPUIDLE_STATE_MAX]; + int i; + int state_count = 1; + + for (i = 0; ; i++) { + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + if (!state_node) + break; + + if (!of_device_is_available(state_node)) + continue; + + if (i == CPUIDLE_STATE_MAX) { + pr_warn("%s: cpuidle states reached max possible\n", + __func__); + break; + } + + states[state_count].wfi_flags = 0; + + if (of_property_read_bool(state_node, "ti,idle-wkup-m3")) + states[state_count].wfi_flags |= WFI_FLAG_WAKE_M3 | + WFI_FLAG_FLUSH_CACHE; + + state_count++; + } + + idle_states = kcalloc(state_count, sizeof(*idle_states), GFP_KERNEL); + if (!idle_states) + return -ENOMEM; + + for (i = 1; i < state_count; i++) + idle_states[i].wfi_flags = states[i].wfi_flags; + + return 0; +} + +static int amx3_idle_enter(unsigned long index) +{ + struct amx3_idle_state *idle_state = &idle_states[index]; + + if (!idle_state) + return -EINVAL; + + if (idle_fn) + idle_fn(idle_state->wfi_flags); + + return 0; +} + +static struct cpuidle_ops amx3_cpuidle_ops __initdata = { + .init = amx3_idle_init, + .suspend = amx3_idle_enter, +}; + +CPUIDLE_METHOD_OF_DECLARE(pm33xx_idle, "ti,am3352", &amx3_cpuidle_ops); +CPUIDLE_METHOD_OF_DECLARE(pm43xx_idle, "ti,am4372", &amx3_cpuidle_ops); diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c new file mode 100644 index 000000000..d73c7b692 --- /dev/null +++ b/arch/arm/mach-omap2/pm34xx.c @@ -0,0 +1,584 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 Power Management Routines + * + * Copyright (C) 2006-2008 Nokia Corporation + * Tony Lindgren <tony@atomide.com> + * Jouni Hogander + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * + * Copyright (C) 2005 Texas Instruments, Inc. + * Richard Woodruff <r-woodruff2@ti.com> + * + * Based on pm.c for omap1 + */ + +#include <linux/cpu_pm.h> +#include <linux/pm.h> +#include <linux/suspend.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/of.h> + +#include <trace/events/power.h> + +#include <asm/fncpy.h> +#include <asm/suspend.h> +#include <asm/system_misc.h> + +#include "clockdomain.h" +#include "powerdomain.h" +#include "soc.h" +#include "common.h" +#include "cm3xxx.h" +#include "cm-regbits-34xx.h" +#include "prm-regbits-34xx.h" +#include "prm3xxx.h" +#include "pm.h" +#include "sdrc.h" +#include "omap-secure.h" +#include "sram.h" +#include "control.h" +#include "vc.h" + +/* pm34xx errata defined in pm.h */ +u16 pm34xx_errata; + +struct power_state { + struct powerdomain *pwrdm; + u32 next_state; +#ifdef CONFIG_SUSPEND + u32 saved_state; +#endif + struct list_head node; +}; + +static LIST_HEAD(pwrst_list); + +void (*omap3_do_wfi_sram)(void); + +static struct powerdomain *mpu_pwrdm, *neon_pwrdm; +static struct powerdomain *core_pwrdm, *per_pwrdm; + +static void omap3_core_save_context(void) +{ + omap3_ctrl_save_padconf(); + + /* + * Force write last pad into memory, as this can fail in some + * cases according to errata 1.157, 1.185 + */ + omap_ctrl_writel(omap_ctrl_readl(OMAP343X_PADCONF_ETK_D14), + OMAP343X_CONTROL_MEM_WKUP + 0x2a0); + + /* Save the Interrupt controller context */ + omap_intc_save_context(); + /* Save the system control module context, padconf already save above*/ + omap3_control_save_context(); +} + +static void omap3_core_restore_context(void) +{ + /* Restore the control module context, padconf restored by h/w */ + omap3_control_restore_context(); + /* Restore the interrupt controller context */ + omap_intc_restore_context(); +} + +/* + * FIXME: This function should be called before entering off-mode after + * OMAP3 secure services have been accessed. Currently it is only called + * once during boot sequence, but this works as we are not using secure + * services. + */ +static void omap3_save_secure_ram_context(void) +{ + u32 ret; + int mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); + + if (omap_type() != OMAP2_DEVICE_TYPE_GP) { + /* + * MPU next state must be set to POWER_ON temporarily, + * otherwise the WFI executed inside the ROM code + * will hang the system. + */ + pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON); + ret = omap3_save_secure_ram(omap3_secure_ram_storage, + OMAP3_SAVE_SECURE_RAM_SZ); + pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state); + /* Following is for error tracking, it should not happen */ + if (ret) { + pr_err("save_secure_sram() returns %08x\n", ret); + while (1) + ; + } + } +} + +static irqreturn_t _prcm_int_handle_io(int irq, void *unused) +{ + int c; + + c = omap_prm_clear_mod_irqs(WKUP_MOD, 1, OMAP3430_ST_IO_MASK | + OMAP3430_ST_IO_CHAIN_MASK); + + return c ? IRQ_HANDLED : IRQ_NONE; +} + +static irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused) +{ + int c; + + /* + * Clear all except ST_IO and ST_IO_CHAIN for wkup module, + * these are handled in a separate handler to avoid acking + * IO events before parsing in mux code + */ + c = omap_prm_clear_mod_irqs(WKUP_MOD, 1, ~(OMAP3430_ST_IO_MASK | + OMAP3430_ST_IO_CHAIN_MASK)); + c += omap_prm_clear_mod_irqs(CORE_MOD, 1, ~0); + c += omap_prm_clear_mod_irqs(OMAP3430_PER_MOD, 1, ~0); + if (omap_rev() > OMAP3430_REV_ES1_0) { + c += omap_prm_clear_mod_irqs(CORE_MOD, 3, ~0); + c += omap_prm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1, ~0); + } + + return c ? IRQ_HANDLED : IRQ_NONE; +} + +static void omap34xx_save_context(u32 *save) +{ + u32 val; + + /* Read Auxiliary Control Register */ + asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (val)); + *save++ = 1; + *save++ = val; + + /* Read L2 AUX ctrl register */ + asm("mrc p15, 1, %0, c9, c0, 2" : "=r" (val)); + *save++ = 1; + *save++ = val; +} + +static int omap34xx_do_sram_idle(unsigned long save_state) +{ + omap34xx_cpu_suspend(save_state); + return 0; +} + +void omap_sram_idle(void) +{ + /* Variable to tell what needs to be saved and restored + * in omap_sram_idle*/ + /* save_state = 0 => Nothing to save and restored */ + /* save_state = 1 => Only L1 and logic lost */ + /* save_state = 2 => Only L2 lost */ + /* save_state = 3 => L1, L2 and logic lost */ + int save_state = 0; + int mpu_next_state = PWRDM_POWER_ON; + int per_next_state = PWRDM_POWER_ON; + int core_next_state = PWRDM_POWER_ON; + u32 sdrc_pwr = 0; + int error; + + mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); + switch (mpu_next_state) { + case PWRDM_POWER_ON: + case PWRDM_POWER_RET: + /* No need to save context */ + save_state = 0; + break; + case PWRDM_POWER_OFF: + save_state = 3; + break; + default: + /* Invalid state */ + pr_err("Invalid mpu state in sram_idle\n"); + return; + } + + /* NEON control */ + if (pwrdm_read_pwrst(neon_pwrdm) == PWRDM_POWER_ON) + pwrdm_set_next_pwrst(neon_pwrdm, mpu_next_state); + + /* Enable IO-PAD and IO-CHAIN wakeups */ + per_next_state = pwrdm_read_next_pwrst(per_pwrdm); + core_next_state = pwrdm_read_next_pwrst(core_pwrdm); + + pwrdm_pre_transition(NULL); + + /* PER */ + if (per_next_state == PWRDM_POWER_OFF) { + error = cpu_cluster_pm_enter(); + if (error) + return; + } + + /* CORE */ + if (core_next_state < PWRDM_POWER_ON) { + if (core_next_state == PWRDM_POWER_OFF) { + omap3_core_save_context(); + omap3_cm_save_context(); + } + } + + /* Configure PMIC signaling for I2C4 or sys_off_mode */ + omap3_vc_set_pmic_signaling(core_next_state); + + omap3_intc_prepare_idle(); + + /* + * On EMU/HS devices ROM code restores a SRDC value + * from scratchpad which has automatic self refresh on timeout + * of AUTO_CNT = 1 enabled. This takes care of erratum ID i443. + * Hence store/restore the SDRC_POWER register here. + */ + if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 && + (omap_type() == OMAP2_DEVICE_TYPE_EMU || + omap_type() == OMAP2_DEVICE_TYPE_SEC) && + core_next_state == PWRDM_POWER_OFF) + sdrc_pwr = sdrc_read_reg(SDRC_POWER); + + /* + * omap3_arm_context is the location where some ARM context + * get saved. The rest is placed on the stack, and restored + * from there before resuming. + */ + if (save_state) + omap34xx_save_context(omap3_arm_context); + if (save_state == 1 || save_state == 3) + cpu_suspend(save_state, omap34xx_do_sram_idle); + else + omap34xx_do_sram_idle(save_state); + + /* Restore normal SDRC POWER settings */ + if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 && + (omap_type() == OMAP2_DEVICE_TYPE_EMU || + omap_type() == OMAP2_DEVICE_TYPE_SEC) && + core_next_state == PWRDM_POWER_OFF) + sdrc_write_reg(sdrc_pwr, SDRC_POWER); + + /* CORE */ + if (core_next_state < PWRDM_POWER_ON && + pwrdm_read_prev_pwrst(core_pwrdm) == PWRDM_POWER_OFF) { + omap3_core_restore_context(); + omap3_cm_restore_context(); + omap3_sram_restore_context(); + omap2_sms_restore_context(); + } else { + /* + * In off-mode resume path above, omap3_core_restore_context + * also handles the INTC autoidle restore done here so limit + * this to non-off mode resume paths so we don't do it twice. + */ + omap3_intc_resume_idle(); + } + + pwrdm_post_transition(NULL); + + /* PER */ + if (per_next_state == PWRDM_POWER_OFF) + cpu_cluster_pm_exit(); +} + +static void omap3_pm_idle(void) +{ + if (omap_irq_pending()) + return; + + omap_sram_idle(); +} + +#ifdef CONFIG_SUSPEND +static int omap3_pm_suspend(void) +{ + struct power_state *pwrst; + int state, ret = 0; + + /* Read current next_pwrsts */ + list_for_each_entry(pwrst, &pwrst_list, node) + pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm); + /* Set ones wanted by suspend */ + list_for_each_entry(pwrst, &pwrst_list, node) { + if (omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state)) + goto restore; + if (pwrdm_clear_all_prev_pwrst(pwrst->pwrdm)) + goto restore; + } + + omap3_intc_suspend(); + + omap_sram_idle(); + +restore: + /* Restore next_pwrsts */ + list_for_each_entry(pwrst, &pwrst_list, node) { + state = pwrdm_read_prev_pwrst(pwrst->pwrdm); + if (state > pwrst->next_state) { + pr_info("Powerdomain (%s) didn't enter target state %d\n", + pwrst->pwrdm->name, pwrst->next_state); + ret = -1; + } + omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); + } + if (ret) + pr_err("Could not enter target state in pm_suspend\n"); + else + pr_info("Successfully put all powerdomains to target state\n"); + + return ret; +} +#else +#define omap3_pm_suspend NULL +#endif /* CONFIG_SUSPEND */ + +static void __init prcm_setup_regs(void) +{ + omap3_ctrl_init(); + + omap3_prm_init_pm(cpu_is_omap3630(), omap3_has_iva()); +} + +void omap3_pm_off_mode_enable(int enable) +{ + struct power_state *pwrst; + u32 state; + + if (enable) + state = PWRDM_POWER_OFF; + else + state = PWRDM_POWER_RET; + + list_for_each_entry(pwrst, &pwrst_list, node) { + if (IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583) && + pwrst->pwrdm == core_pwrdm && + state == PWRDM_POWER_OFF) { + pwrst->next_state = PWRDM_POWER_RET; + pr_warn("%s: Core OFF disabled due to errata i583\n", + __func__); + } else { + pwrst->next_state = state; + } + omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); + } +} + +int omap3_pm_get_suspend_state(struct powerdomain *pwrdm) +{ + struct power_state *pwrst; + + list_for_each_entry(pwrst, &pwrst_list, node) { + if (pwrst->pwrdm == pwrdm) + return pwrst->next_state; + } + return -EINVAL; +} + +int omap3_pm_set_suspend_state(struct powerdomain *pwrdm, int state) +{ + struct power_state *pwrst; + + list_for_each_entry(pwrst, &pwrst_list, node) { + if (pwrst->pwrdm == pwrdm) { + pwrst->next_state = state; + return 0; + } + } + return -EINVAL; +} + +static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) +{ + struct power_state *pwrst; + + if (!pwrdm->pwrsts) + return 0; + + pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); + if (!pwrst) + return -ENOMEM; + pwrst->pwrdm = pwrdm; + + if (enable_off_mode) + pwrst->next_state = PWRDM_POWER_OFF; + else + pwrst->next_state = PWRDM_POWER_RET; + + list_add(&pwrst->node, &pwrst_list); + + if (pwrdm_has_hdwr_sar(pwrdm)) + pwrdm_enable_hdwr_sar(pwrdm); + + return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); +} + +/* + * Push functions to SRAM + * + * The minimum set of functions is pushed to SRAM for execution: + * - omap3_do_wfi for erratum i581 WA, + */ +void omap_push_sram_idle(void) +{ + omap3_do_wfi_sram = omap_sram_push(omap3_do_wfi, omap3_do_wfi_sz); +} + +static void __init pm_errata_configure(void) +{ + if (cpu_is_omap3630()) { + pm34xx_errata |= PM_RTA_ERRATUM_i608; + /* Enable the l2 cache toggling in sleep logic */ + enable_omap3630_toggle_l2_on_restore(); + if (omap_rev() < OMAP3630_REV_ES1_2) + pm34xx_errata |= (PM_SDRC_WAKEUP_ERRATUM_i583 | + PM_PER_MEMORIES_ERRATUM_i582); + } else if (cpu_is_omap34xx()) { + pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582; + } +} + +static void __init omap3_pm_check_pmic(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "ti,twl4030-power-idle"); + if (!np) + np = of_find_compatible_node(NULL, NULL, "ti,twl4030-power-idle-osc-off"); + + if (np) { + of_node_put(np); + enable_off_mode = 1; + } else { + enable_off_mode = 0; + } +} + +int __init omap3_pm_init(void) +{ + struct power_state *pwrst, *tmp; + struct clockdomain *neon_clkdm, *mpu_clkdm, *per_clkdm, *wkup_clkdm; + int ret; + + if (!omap3_has_io_chain_ctrl()) + pr_warn("PM: no software I/O chain control; some wakeups may be lost\n"); + + pm_errata_configure(); + + /* XXX prcm_setup_regs needs to be before enabling hw + * supervised mode for powerdomains */ + prcm_setup_regs(); + + ret = request_irq(omap_prcm_event_to_irq("wkup"), + _prcm_int_handle_wakeup, IRQF_NO_SUSPEND, "pm_wkup", NULL); + + if (ret) { + pr_err("pm: Failed to request pm_wkup irq\n"); + goto err1; + } + + /* IO interrupt is shared with mux code */ + ret = request_irq(omap_prcm_event_to_irq("io"), + _prcm_int_handle_io, IRQF_SHARED | IRQF_NO_SUSPEND, "pm_io", + omap3_pm_init); + + if (ret) { + pr_err("pm: Failed to request pm_io irq\n"); + goto err2; + } + + omap3_pm_check_pmic(); + + ret = pwrdm_for_each(pwrdms_setup, NULL); + if (ret) { + pr_err("Failed to setup powerdomains\n"); + goto err3; + } + + (void) clkdm_for_each(omap_pm_clkdms_setup, NULL); + + mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); + if (mpu_pwrdm == NULL) { + pr_err("Failed to get mpu_pwrdm\n"); + ret = -EINVAL; + goto err3; + } + + neon_pwrdm = pwrdm_lookup("neon_pwrdm"); + per_pwrdm = pwrdm_lookup("per_pwrdm"); + core_pwrdm = pwrdm_lookup("core_pwrdm"); + + neon_clkdm = clkdm_lookup("neon_clkdm"); + mpu_clkdm = clkdm_lookup("mpu_clkdm"); + per_clkdm = clkdm_lookup("per_clkdm"); + wkup_clkdm = clkdm_lookup("wkup_clkdm"); + + omap_common_suspend_init(omap3_pm_suspend); + + arm_pm_idle = omap3_pm_idle; + omap3_idle_init(); + + /* + * RTA is disabled during initialization as per erratum i608 + * it is safer to disable RTA by the bootloader, but we would like + * to be doubly sure here and prevent any mishaps. + */ + if (IS_PM34XX_ERRATUM(PM_RTA_ERRATUM_i608)) + omap3630_ctrl_disable_rta(); + + /* + * The UART3/4 FIFO and the sidetone memory in McBSP2/3 are + * not correctly reset when the PER powerdomain comes back + * from OFF or OSWR when the CORE powerdomain is kept active. + * See OMAP36xx Erratum i582 "PER Domain reset issue after + * Domain-OFF/OSWR Wakeup". This wakeup dependency is not a + * complete workaround. The kernel must also prevent the PER + * powerdomain from going to OSWR/OFF while the CORE + * powerdomain is not going to OSWR/OFF. And if PER last + * power state was off while CORE last power state was ON, the + * UART3/4 and McBSP2/3 SIDETONE devices need to run a + * self-test using their loopback tests; if that fails, those + * devices are unusable until the PER/CORE can complete a transition + * from ON to OSWR/OFF and then back to ON. + * + * XXX Technically this workaround is only needed if off-mode + * or OSWR is enabled. + */ + if (IS_PM34XX_ERRATUM(PM_PER_MEMORIES_ERRATUM_i582)) + clkdm_add_wkdep(per_clkdm, wkup_clkdm); + + clkdm_add_wkdep(neon_clkdm, mpu_clkdm); + if (omap_type() != OMAP2_DEVICE_TYPE_GP) { + omap3_secure_ram_storage = + kmalloc(OMAP3_SAVE_SECURE_RAM_SZ, GFP_KERNEL); + if (!omap3_secure_ram_storage) + pr_err("Memory allocation failed when allocating for secure sram context\n"); + + local_irq_disable(); + + omap3_save_secure_ram_context(); + + local_irq_enable(); + } + + omap3_save_scratchpad_contents(); + return ret; + +err3: + list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) { + list_del(&pwrst->node); + kfree(pwrst); + } + free_irq(omap_prcm_event_to_irq("io"), omap3_pm_init); +err2: + free_irq(omap_prcm_event_to_irq("wkup"), NULL); +err1: + return ret; +} diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c new file mode 100644 index 000000000..5a7a949ae --- /dev/null +++ b/arch/arm/mach-omap2/pm44xx.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4+ Power Management Routines + * + * Copyright (C) 2010-2013 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + */ + +#include <linux/pm.h> +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <asm/system_misc.h> + +#include "soc.h" +#include "common.h" +#include "clockdomain.h" +#include "powerdomain.h" +#include "pm.h" + +u16 pm44xx_errata; + +struct power_state { + struct powerdomain *pwrdm; + u32 next_state; + u32 next_logic_state; +#ifdef CONFIG_SUSPEND + u32 saved_state; + u32 saved_logic_state; +#endif + struct list_head node; +}; + +/** + * struct static_dep_map - Static dependency map + * @from: from clockdomain + * @to: to clockdomain + */ +struct static_dep_map { + const char *from; + const char *to; +}; + +static u32 cpu_suspend_state = PWRDM_POWER_OFF; + +static LIST_HEAD(pwrst_list); + +#ifdef CONFIG_SUSPEND +static int omap4_pm_suspend(void) +{ + struct power_state *pwrst; + int state, ret = 0; + u32 cpu_id = smp_processor_id(); + + /* Save current powerdomain state */ + list_for_each_entry(pwrst, &pwrst_list, node) { + pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm); + pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm); + } + + /* Set targeted power domain states by suspend */ + list_for_each_entry(pwrst, &pwrst_list, node) { + omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); + pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->next_logic_state); + } + + /* + * For MPUSS to hit power domain retention(CSWR or OSWR), + * CPU0 and CPU1 power domains need to be in OFF or DORMANT state, + * since CPU power domain CSWR is not supported by hardware + * Only master CPU follows suspend path. All other CPUs follow + * CPU hotplug path in system wide suspend. On OMAP4, CPU power + * domain CSWR is not supported by hardware. + * More details can be found in OMAP4430 TRM section 4.3.4.2. + */ + omap4_enter_lowpower(cpu_id, cpu_suspend_state); + + /* Restore next powerdomain state */ + list_for_each_entry(pwrst, &pwrst_list, node) { + state = pwrdm_read_prev_pwrst(pwrst->pwrdm); + if (state > pwrst->next_state) { + pr_info("Powerdomain (%s) didn't enter target state %d\n", + pwrst->pwrdm->name, pwrst->next_state); + ret = -1; + } + omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); + pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state); + } + if (ret) { + pr_crit("Could not enter target state in pm_suspend\n"); + /* + * OMAP4 chip PM currently works only with certain (newer) + * versions of bootloaders. This is due to missing code in the + * kernel to properly reset and initialize some devices. + * Warn the user about the bootloader version being one of the + * possible causes. + * http://www.spinics.net/lists/arm-kernel/msg218641.html + */ + pr_warn("A possible cause could be an old bootloader - try u-boot >= v2012.07\n"); + } else { + pr_info("Successfully put all powerdomains to target state\n"); + } + + return 0; +} +#else +#define omap4_pm_suspend NULL +#endif /* CONFIG_SUSPEND */ + +static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) +{ + struct power_state *pwrst; + + if (!pwrdm->pwrsts) + return 0; + + /* + * Skip CPU0 and CPU1 power domains. CPU1 is programmed + * through hotplug path and CPU0 explicitly programmed + * further down in the code path + */ + if (!strncmp(pwrdm->name, "cpu", 3)) { + if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) + cpu_suspend_state = PWRDM_POWER_RET; + return 0; + } + + if (!strncmp(pwrdm->name, "core", 4) || + !strncmp(pwrdm->name, "l4per", 5)) + pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF); + + pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); + if (!pwrst) + return -ENOMEM; + + pwrst->pwrdm = pwrdm; + pwrst->next_state = pwrdm_get_valid_lp_state(pwrdm, false, + PWRDM_POWER_RET); + pwrst->next_logic_state = pwrdm_get_valid_lp_state(pwrdm, true, + PWRDM_POWER_OFF); + + list_add(&pwrst->node, &pwrst_list); + + return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); +} + +/** + * omap_default_idle - OMAP4 default ilde routine.' + * + * Implements OMAP4 memory, IO ordering requirements which can't be addressed + * with default cpu_do_idle() hook. Used by all CPUs with !CONFIG_CPU_IDLE and + * by secondary CPU with CONFIG_CPU_IDLE. + */ +static void omap_default_idle(void) +{ + omap_do_wfi(); +} + +/* + * The dynamic dependency between MPUSS -> MEMIF and + * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as + * expected. The hardware recommendation is to enable static + * dependencies for these to avoid system lock ups or random crashes. + * The L4 wakeup depedency is added to workaround the OCP sync hardware + * BUG with 32K synctimer which lead to incorrect timer value read + * from the 32K counter. The BUG applies for GPTIMER1 and WDT2 which + * are part of L4 wakeup clockdomain. + */ +static const struct static_dep_map omap4_static_dep_map[] = { + {.from = "mpuss_clkdm", .to = "l3_emif_clkdm"}, + {.from = "mpuss_clkdm", .to = "l3_1_clkdm"}, + {.from = "mpuss_clkdm", .to = "l3_2_clkdm"}, + {.from = "ducati_clkdm", .to = "l3_1_clkdm"}, + {.from = "ducati_clkdm", .to = "l3_2_clkdm"}, + {.from = NULL} /* TERMINATION */ +}; + +static const struct static_dep_map omap5_dra7_static_dep_map[] = { + {.from = "mpu_clkdm", .to = "emif_clkdm"}, + {.from = NULL} /* TERMINATION */ +}; + +/** + * omap4plus_init_static_deps() - Initialize a static dependency map + * @map: Mapping of clock domains + */ +static inline int omap4plus_init_static_deps(const struct static_dep_map *map) +{ + int ret; + struct clockdomain *from, *to; + + if (!map) + return 0; + + while (map->from) { + from = clkdm_lookup(map->from); + to = clkdm_lookup(map->to); + if (!from || !to) { + pr_err("Failed lookup %s or %s for wakeup dependency\n", + map->from, map->to); + return -EINVAL; + } + ret = clkdm_add_wkdep(from, to); + if (ret) { + pr_err("Failed to add %s -> %s wakeup dependency(%d)\n", + map->from, map->to, ret); + return ret; + } + + map++; + } + + return 0; +} + +/** + * omap4_pm_init_early - Does early initialization necessary for OMAP4+ devices + * + * Initializes basic stuff for power management functionality. + */ +int __init omap4_pm_init_early(void) +{ + if (cpu_is_omap446x()) + pm44xx_errata |= PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD; + + if (soc_is_omap54xx() || soc_is_dra7xx()) + pm44xx_errata |= PM_OMAP4_CPU_OSWR_DISABLE; + + return 0; +} + +/** + * omap4_pm_init - Init routine for OMAP4+ devices + * + * Initializes all powerdomain and clockdomain target states + * and all PRCM settings. + * Return: Returns the error code returned by called functions. + */ +int __init omap4_pm_init(void) +{ + int ret = 0; + + if (omap_rev() == OMAP4430_REV_ES1_0) { + WARN(1, "Power Management not supported on OMAP4430 ES1.0\n"); + return -ENODEV; + } + + pr_info("Power Management for TI OMAP4+ devices.\n"); + + /* + * OMAP4 chip PM currently works only with certain (newer) + * versions of bootloaders. This is due to missing code in the + * kernel to properly reset and initialize some devices. + * http://www.spinics.net/lists/arm-kernel/msg218641.html + */ + if (cpu_is_omap44xx()) + pr_warn("OMAP4 PM: u-boot >= v2012.07 is required for full PM support\n"); + + ret = pwrdm_for_each(pwrdms_setup, NULL); + if (ret) { + pr_err("Failed to setup powerdomains.\n"); + goto err2; + } + + if (cpu_is_omap44xx()) + ret = omap4plus_init_static_deps(omap4_static_dep_map); + else if (soc_is_omap54xx() || soc_is_dra7xx()) + ret = omap4plus_init_static_deps(omap5_dra7_static_dep_map); + + if (ret) { + pr_err("Failed to initialise static dependencies.\n"); + goto err2; + } + + ret = omap4_mpuss_init(); + if (ret) { + pr_err("Failed to initialise OMAP4 MPUSS\n"); + goto err2; + } + + (void) clkdm_for_each(omap_pm_clkdms_setup, NULL); + + omap_common_suspend_init(omap4_pm_suspend); + + /* Overwrite the default cpu_do_idle() */ + arm_pm_idle = omap_default_idle; + + if (cpu_is_omap44xx() || soc_is_omap54xx()) + omap4_idle_init(); + +err2: + return ret; +} diff --git a/arch/arm/mach-omap2/pmic-cpcap.c b/arch/arm/mach-omap2/pmic-cpcap.c new file mode 100644 index 000000000..668dc84fd --- /dev/null +++ b/arch/arm/mach-omap2/pmic-cpcap.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * pmic-cpcap.c - CPCAP-specific functions for the OPP code + * + * Adapted from Motorola Mapphone Android Linux kernel + * Copyright (C) 2011 Motorola, Inc. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> + +#include "soc.h" +#include "pm.h" +#include "voltage.h" + +#include <linux/init.h> +#include "vc.h" + +/** + * omap_cpcap_vsel_to_vdc - convert CPCAP VSEL value to microvolts DC + * @vsel: CPCAP VSEL value to convert + * + * Returns the microvolts DC that the CPCAP PMIC should generate when + * programmed with @vsel. + */ +static unsigned long omap_cpcap_vsel_to_uv(unsigned char vsel) +{ + if (vsel > 0x44) + vsel = 0x44; + return (((vsel * 125) + 6000)) * 100; +} + +/** + * omap_cpcap_uv_to_vsel - convert microvolts DC to CPCAP VSEL value + * @uv: microvolts DC to convert + * + * Returns the VSEL value necessary for the CPCAP PMIC to + * generate an output voltage equal to or greater than @uv microvolts DC. + */ +static unsigned char omap_cpcap_uv_to_vsel(unsigned long uv) +{ + if (uv < 600000) + uv = 600000; + else if (uv > 1450000) + uv = 1450000; + return DIV_ROUND_UP(uv - 600000, 12500); +} + +static struct omap_voltdm_pmic omap_cpcap_core = { + .slew_rate = 4000, + .step_size = 12500, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 900000, + .vddmax = 1350000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = 0x02, + .volt_reg_addr = 0x00, + .cmd_reg_addr = 0x01, + .i2c_high_speed = false, + .vsel_to_uv = omap_cpcap_vsel_to_uv, + .uv_to_vsel = omap_cpcap_uv_to_vsel, +}; + +static struct omap_voltdm_pmic omap_cpcap_iva = { + .slew_rate = 4000, + .step_size = 12500, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 900000, + .vddmax = 1375000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = 0x44, + .volt_reg_addr = 0x0, + .cmd_reg_addr = 0x01, + .i2c_high_speed = false, + .vsel_to_uv = omap_cpcap_vsel_to_uv, + .uv_to_vsel = omap_cpcap_uv_to_vsel, +}; + +/** + * omap_max8952_vsel_to_vdc - convert MAX8952 VSEL value to microvolts DC + * @vsel: MAX8952 VSEL value to convert + * + * Returns the microvolts DC that the MAX8952 Regulator should generate when + * programmed with @vsel. + */ +static unsigned long omap_max8952_vsel_to_uv(unsigned char vsel) +{ + if (vsel > 0x3F) + vsel = 0x3F; + return (((vsel * 100) + 7700)) * 100; +} + +/** + * omap_max8952_uv_to_vsel - convert microvolts DC to MAX8952 VSEL value + * @uv: microvolts DC to convert + * + * Returns the VSEL value necessary for the MAX8952 Regulator to + * generate an output voltage equal to or greater than @uv microvolts DC. + */ +static unsigned char omap_max8952_uv_to_vsel(unsigned long uv) +{ + if (uv < 770000) + uv = 770000; + else if (uv > 1400000) + uv = 1400000; + return DIV_ROUND_UP(uv - 770000, 10000); +} + +static struct omap_voltdm_pmic omap443x_max8952_mpu = { + .slew_rate = 16000, + .step_size = 10000, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 900000, + .vddmax = 1400000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = 0x60, + .volt_reg_addr = 0x03, + .cmd_reg_addr = 0x03, + .i2c_high_speed = false, + .vsel_to_uv = omap_max8952_vsel_to_uv, + .uv_to_vsel = omap_max8952_uv_to_vsel, +}; + +/** + * omap_fan5355_vsel_to_vdc - convert FAN535503 VSEL value to microvolts DC + * @vsel: FAN535503 VSEL value to convert + * + * Returns the microvolts DC that the FAN535503 Regulator should generate when + * programmed with @vsel. + */ +static unsigned long omap_fan535503_vsel_to_uv(unsigned char vsel) +{ + /* Extract bits[5:0] */ + vsel &= 0x3F; + + return (((vsel * 125) + 7500)) * 100; +} + +/** + * omap_fan535508_vsel_to_vdc - convert FAN535508 VSEL value to microvolts DC + * @vsel: FAN535508 VSEL value to convert + * + * Returns the microvolts DC that the FAN535508 Regulator should generate when + * programmed with @vsel. + */ +static unsigned long omap_fan535508_vsel_to_uv(unsigned char vsel) +{ + /* Extract bits[5:0] */ + vsel &= 0x3F; + + if (vsel > 0x37) + vsel = 0x37; + return (((vsel * 125) + 7500)) * 100; +} + + +/** + * omap_fan535503_uv_to_vsel - convert microvolts DC to FAN535503 VSEL value + * @uv: microvolts DC to convert + * + * Returns the VSEL value necessary for the MAX8952 Regulator to + * generate an output voltage equal to or greater than @uv microvolts DC. + */ +static unsigned char omap_fan535503_uv_to_vsel(unsigned long uv) +{ + unsigned char vsel; + if (uv < 750000) + uv = 750000; + else if (uv > 1537500) + uv = 1537500; + + vsel = DIV_ROUND_UP(uv - 750000, 12500); + return vsel | 0xC0; +} + +/** + * omap_fan535508_uv_to_vsel - convert microvolts DC to FAN535508 VSEL value + * @uv: microvolts DC to convert + * + * Returns the VSEL value necessary for the MAX8952 Regulator to + * generate an output voltage equal to or greater than @uv microvolts DC. + */ +static unsigned char omap_fan535508_uv_to_vsel(unsigned long uv) +{ + unsigned char vsel; + if (uv < 750000) + uv = 750000; + else if (uv > 1437500) + uv = 1437500; + + vsel = DIV_ROUND_UP(uv - 750000, 12500); + return vsel | 0xC0; +} + +/* fan5335-core */ +static struct omap_voltdm_pmic omap4_fan_core = { + .slew_rate = 4000, + .step_size = 12500, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 850000, + .vddmax = 1375000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = 0x4A, + .i2c_high_speed = false, + .volt_reg_addr = 0x01, + .cmd_reg_addr = 0x01, + .vsel_to_uv = omap_fan535508_vsel_to_uv, + .uv_to_vsel = omap_fan535508_uv_to_vsel, +}; + +/* fan5335 iva */ +static struct omap_voltdm_pmic omap4_fan_iva = { + .slew_rate = 4000, + .step_size = 12500, + .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET, + .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN, + .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX, + .vddmin = 850000, + .vddmax = 1375000, + .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US, + .i2c_slave_addr = 0x48, + .volt_reg_addr = 0x01, + .cmd_reg_addr = 0x01, + .i2c_high_speed = false, + .vsel_to_uv = omap_fan535503_vsel_to_uv, + .uv_to_vsel = omap_fan535503_uv_to_vsel, +}; + +int __init omap4_cpcap_init(void) +{ + struct voltagedomain *voltdm; + + if (!of_find_compatible_node(NULL, NULL, "motorola,cpcap")) + return -ENODEV; + + voltdm = voltdm_lookup("mpu"); + omap_voltage_register_pmic(voltdm, &omap443x_max8952_mpu); + + if (of_machine_is_compatible("motorola,droid-bionic")) { + voltdm = voltdm_lookup("core"); + omap_voltage_register_pmic(voltdm, &omap_cpcap_core); + + voltdm = voltdm_lookup("iva"); + omap_voltage_register_pmic(voltdm, &omap_cpcap_iva); + } else { + voltdm = voltdm_lookup("core"); + omap_voltage_register_pmic(voltdm, &omap4_fan_core); + + voltdm = voltdm_lookup("iva"); + omap_voltage_register_pmic(voltdm, &omap4_fan_iva); + } + + return 0; +} + +static int __init cpcap_late_init(void) +{ + omap4_vc_set_pmic_signaling(PWRDM_POWER_RET); + + return 0; +} +omap_late_initcall(cpcap_late_init); diff --git a/arch/arm/mach-omap2/powerdomain-common.c b/arch/arm/mach-omap2/powerdomain-common.c new file mode 100644 index 000000000..7e2c65fe7 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomain-common.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Common powerdomain framework functions + * + * Copyright (C) 2010-2011 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * + * Derived from mach-omap2/powerdomain.c written by Paul Walmsley + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/bug.h> +#include "pm.h" +#include "cm.h" +#include "cm-regbits-34xx.h" +#include "prm-regbits-34xx.h" +#include "prm-regbits-44xx.h" + +/* + * OMAP3 and OMAP4 specific register bit initialisations + * Notice that the names here are not according to each power + * domain but the bit mapping used applies to all of them + */ +/* OMAP3 and OMAP4 Memory Onstate Masks (common across all power domains) */ +#define OMAP_MEM0_ONSTATE_MASK OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK +#define OMAP_MEM1_ONSTATE_MASK OMAP3430_L1FLATMEMONSTATE_MASK +#define OMAP_MEM2_ONSTATE_MASK OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK +#define OMAP_MEM3_ONSTATE_MASK OMAP3430_L2FLATMEMONSTATE_MASK +#define OMAP_MEM4_ONSTATE_MASK OMAP4430_OCP_NRET_BANK_ONSTATE_MASK + +/* OMAP3 and OMAP4 Memory Retstate Masks (common across all power domains) */ +#define OMAP_MEM0_RETSTATE_MASK OMAP3430_SHAREDL1CACHEFLATRETSTATE_MASK +#define OMAP_MEM1_RETSTATE_MASK OMAP3430_L1FLATMEMRETSTATE_MASK +#define OMAP_MEM2_RETSTATE_MASK OMAP3430_SHAREDL2CACHEFLATRETSTATE_MASK +#define OMAP_MEM3_RETSTATE_MASK OMAP3430_L2FLATMEMRETSTATE_MASK +#define OMAP_MEM4_RETSTATE_MASK OMAP4430_OCP_NRET_BANK_RETSTATE_MASK + +/* OMAP3 and OMAP4 Memory Status bits */ +#define OMAP_MEM0_STATEST_MASK OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK +#define OMAP_MEM1_STATEST_MASK OMAP3430_L1FLATMEMSTATEST_MASK +#define OMAP_MEM2_STATEST_MASK OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK +#define OMAP_MEM3_STATEST_MASK OMAP3430_L2FLATMEMSTATEST_MASK +#define OMAP_MEM4_STATEST_MASK OMAP4430_OCP_NRET_BANK_STATEST_MASK + +/* Common Internal functions used across OMAP rev's*/ +u32 omap2_pwrdm_get_mem_bank_onstate_mask(u8 bank) +{ + switch (bank) { + case 0: + return OMAP_MEM0_ONSTATE_MASK; + case 1: + return OMAP_MEM1_ONSTATE_MASK; + case 2: + return OMAP_MEM2_ONSTATE_MASK; + case 3: + return OMAP_MEM3_ONSTATE_MASK; + case 4: + return OMAP_MEM4_ONSTATE_MASK; + default: + WARN_ON(1); /* should never happen */ + return -EEXIST; + } + return 0; +} + +u32 omap2_pwrdm_get_mem_bank_retst_mask(u8 bank) +{ + switch (bank) { + case 0: + return OMAP_MEM0_RETSTATE_MASK; + case 1: + return OMAP_MEM1_RETSTATE_MASK; + case 2: + return OMAP_MEM2_RETSTATE_MASK; + case 3: + return OMAP_MEM3_RETSTATE_MASK; + case 4: + return OMAP_MEM4_RETSTATE_MASK; + default: + WARN_ON(1); /* should never happen */ + return -EEXIST; + } + return 0; +} + +u32 omap2_pwrdm_get_mem_bank_stst_mask(u8 bank) +{ + switch (bank) { + case 0: + return OMAP_MEM0_STATEST_MASK; + case 1: + return OMAP_MEM1_STATEST_MASK; + case 2: + return OMAP_MEM2_STATEST_MASK; + case 3: + return OMAP_MEM3_STATEST_MASK; + case 4: + return OMAP_MEM4_STATEST_MASK; + default: + WARN_ON(1); /* should never happen */ + return -EEXIST; + } + return 0; +} + diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c new file mode 100644 index 000000000..0eca44fc1 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomain.c @@ -0,0 +1,1285 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP powerdomain control + * + * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc. + * Copyright (C) 2007-2011 Nokia Corporation + * + * Written by Paul Walmsley + * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> + * State counting code by Tero Kristo <tero.kristo@nokia.com> + */ +#undef DEBUG + +#include <linux/cpu_pm.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <trace/events/power.h> + +#include "cm2xxx_3xxx.h" +#include "prcm44xx.h" +#include "cm44xx.h" +#include "prm2xxx_3xxx.h" +#include "prm44xx.h" + +#include <asm/cpu.h> + +#include "powerdomain.h" +#include "clockdomain.h" +#include "voltage.h" + +#include "soc.h" +#include "pm.h" + +#define PWRDM_TRACE_STATES_FLAG (1<<31) + +void pwrdms_save_context(void); +void pwrdms_restore_context(void); + +enum { + PWRDM_STATE_NOW = 0, + PWRDM_STATE_PREV, +}; + +/* + * Types of sleep_switch used internally in omap_set_pwrdm_state() + * and its associated static functions + * + * XXX Better documentation is needed here + */ +#define ALREADYACTIVE_SWITCH 0 +#define FORCEWAKEUP_SWITCH 1 +#define LOWPOWERSTATE_SWITCH 2 + +/* pwrdm_list contains all registered struct powerdomains */ +static LIST_HEAD(pwrdm_list); + +static struct pwrdm_ops *arch_pwrdm; + +/* Private functions */ + +static struct powerdomain *_pwrdm_lookup(const char *name) +{ + struct powerdomain *pwrdm, *temp_pwrdm; + + pwrdm = NULL; + + list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { + if (!strcmp(name, temp_pwrdm->name)) { + pwrdm = temp_pwrdm; + break; + } + } + + return pwrdm; +} + +/** + * _pwrdm_register - register a powerdomain + * @pwrdm: struct powerdomain * to register + * + * Adds a powerdomain to the internal powerdomain list. Returns + * -EINVAL if given a null pointer, -EEXIST if a powerdomain is + * already registered by the provided name, or 0 upon success. + */ +static int _pwrdm_register(struct powerdomain *pwrdm) +{ + int i; + struct voltagedomain *voltdm; + + if (!pwrdm || !pwrdm->name) + return -EINVAL; + + if (cpu_is_omap44xx() && + pwrdm->prcm_partition == OMAP4430_INVALID_PRCM_PARTITION) { + pr_err("powerdomain: %s: missing OMAP4 PRCM partition ID\n", + pwrdm->name); + return -EINVAL; + } + + if (_pwrdm_lookup(pwrdm->name)) + return -EEXIST; + + if (arch_pwrdm && arch_pwrdm->pwrdm_has_voltdm) + if (!arch_pwrdm->pwrdm_has_voltdm()) + goto skip_voltdm; + + voltdm = voltdm_lookup(pwrdm->voltdm.name); + if (!voltdm) { + pr_err("powerdomain: %s: voltagedomain %s does not exist\n", + pwrdm->name, pwrdm->voltdm.name); + return -EINVAL; + } + pwrdm->voltdm.ptr = voltdm; + INIT_LIST_HEAD(&pwrdm->voltdm_node); +skip_voltdm: + spin_lock_init(&pwrdm->_lock); + + list_add(&pwrdm->node, &pwrdm_list); + + /* Initialize the powerdomain's state counter */ + for (i = 0; i < PWRDM_MAX_PWRSTS; i++) + pwrdm->state_counter[i] = 0; + + pwrdm->ret_logic_off_counter = 0; + for (i = 0; i < pwrdm->banks; i++) + pwrdm->ret_mem_off_counter[i] = 0; + + if (arch_pwrdm && arch_pwrdm->pwrdm_wait_transition) + arch_pwrdm->pwrdm_wait_transition(pwrdm); + pwrdm->state = pwrdm_read_pwrst(pwrdm); + pwrdm->state_counter[pwrdm->state] = 1; + + pr_debug("powerdomain: registered %s\n", pwrdm->name); + + return 0; +} + +static void _update_logic_membank_counters(struct powerdomain *pwrdm) +{ + int i; + u8 prev_logic_pwrst, prev_mem_pwrst; + + prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm); + if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) && + (prev_logic_pwrst == PWRDM_POWER_OFF)) + pwrdm->ret_logic_off_counter++; + + for (i = 0; i < pwrdm->banks; i++) { + prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i); + + if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) && + (prev_mem_pwrst == PWRDM_POWER_OFF)) + pwrdm->ret_mem_off_counter[i]++; + } +} + +static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) +{ + + int prev, next, state, trace_state = 0; + + if (pwrdm == NULL) + return -EINVAL; + + state = pwrdm_read_pwrst(pwrdm); + + switch (flag) { + case PWRDM_STATE_NOW: + prev = pwrdm->state; + break; + case PWRDM_STATE_PREV: + prev = pwrdm_read_prev_pwrst(pwrdm); + if (prev >= 0 && pwrdm->state != prev) + pwrdm->state_counter[prev]++; + if (prev == PWRDM_POWER_RET) + _update_logic_membank_counters(pwrdm); + /* + * If the power domain did not hit the desired state, + * generate a trace event with both the desired and hit states + */ + next = pwrdm_read_next_pwrst(pwrdm); + if (next != prev) { + trace_state = (PWRDM_TRACE_STATES_FLAG | + ((next & OMAP_POWERSTATE_MASK) << 8) | + ((prev & OMAP_POWERSTATE_MASK) << 0)); + trace_power_domain_target_rcuidle(pwrdm->name, + trace_state, + raw_smp_processor_id()); + } + break; + default: + return -EINVAL; + } + + if (state != prev) + pwrdm->state_counter[state]++; + + pm_dbg_update_time(pwrdm, prev); + + pwrdm->state = state; + + return 0; +} + +static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused) +{ + pwrdm_clear_all_prev_pwrst(pwrdm); + _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); + return 0; +} + +static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused) +{ + _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV); + return 0; +} + +/** + * _pwrdm_save_clkdm_state_and_activate - prepare for power state change + * @pwrdm: struct powerdomain * to operate on + * @curr_pwrst: current power state of @pwrdm + * @pwrst: power state to switch to + * + * Determine whether the powerdomain needs to be turned on before + * attempting to switch power states. Called by + * omap_set_pwrdm_state(). NOTE that if the powerdomain contains + * multiple clockdomains, this code assumes that the first clockdomain + * supports software-supervised wakeup mode - potentially a problem. + * Returns the power state switch mode currently in use (see the + * "Types of sleep_switch" comment above). + */ +static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm, + u8 curr_pwrst, u8 pwrst) +{ + u8 sleep_switch; + + if (curr_pwrst < PWRDM_POWER_ON) { + if (curr_pwrst > pwrst && + pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE && + arch_pwrdm->pwrdm_set_lowpwrstchange) { + sleep_switch = LOWPOWERSTATE_SWITCH; + } else { + clkdm_deny_idle_nolock(pwrdm->pwrdm_clkdms[0]); + sleep_switch = FORCEWAKEUP_SWITCH; + } + } else { + sleep_switch = ALREADYACTIVE_SWITCH; + } + + return sleep_switch; +} + +/** + * _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change + * @pwrdm: struct powerdomain * to operate on + * @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate() + * + * Restore the clockdomain state perturbed by + * _pwrdm_save_clkdm_state_and_activate(), and call the power state + * bookkeeping code. Called by omap_set_pwrdm_state(). NOTE that if + * the powerdomain contains multiple clockdomains, this assumes that + * the first associated clockdomain supports either + * hardware-supervised idle control in the register, or + * software-supervised sleep. No return value. + */ +static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm, + u8 sleep_switch) +{ + switch (sleep_switch) { + case FORCEWAKEUP_SWITCH: + clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]); + break; + case LOWPOWERSTATE_SWITCH: + if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE && + arch_pwrdm->pwrdm_set_lowpwrstchange) + arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm); + pwrdm_state_switch_nolock(pwrdm); + break; + } +} + +/* Public functions */ + +/** + * pwrdm_register_platform_funcs - register powerdomain implementation fns + * @po: func pointers for arch specific implementations + * + * Register the list of function pointers used to implement the + * powerdomain functions on different OMAP SoCs. Should be called + * before any other pwrdm_register*() function. Returns -EINVAL if + * @po is null, -EEXIST if platform functions have already been + * registered, or 0 upon success. + */ +int pwrdm_register_platform_funcs(struct pwrdm_ops *po) +{ + if (!po) + return -EINVAL; + + if (arch_pwrdm) + return -EEXIST; + + arch_pwrdm = po; + + return 0; +} + +/** + * pwrdm_register_pwrdms - register SoC powerdomains + * @ps: pointer to an array of struct powerdomain to register + * + * Register the powerdomains available on a particular OMAP SoC. Must + * be called after pwrdm_register_platform_funcs(). May be called + * multiple times. Returns -EACCES if called before + * pwrdm_register_platform_funcs(); -EINVAL if the argument @ps is + * null; or 0 upon success. + */ +int pwrdm_register_pwrdms(struct powerdomain **ps) +{ + struct powerdomain **p = NULL; + + if (!arch_pwrdm) + return -EEXIST; + + if (!ps) + return -EINVAL; + + for (p = ps; *p; p++) + _pwrdm_register(*p); + + return 0; +} + +static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + if (enable_off_mode) + pwrdms_save_context(); + break; + case CPU_CLUSTER_PM_EXIT: + if (enable_off_mode) + pwrdms_restore_context(); + break; + } + + return NOTIFY_OK; +} + +/** + * pwrdm_complete_init - set up the powerdomain layer + * + * Do whatever is necessary to initialize registered powerdomains and + * powerdomain code. Currently, this programs the next power state + * for each powerdomain to ON. This prevents powerdomains from + * unexpectedly losing context or entering high wakeup latency modes + * with non-power-management-enabled kernels. Must be called after + * pwrdm_register_pwrdms(). Returns -EACCES if called before + * pwrdm_register_pwrdms(), or 0 upon success. + */ +int pwrdm_complete_init(void) +{ + struct powerdomain *temp_p; + static struct notifier_block nb; + + if (list_empty(&pwrdm_list)) + return -EACCES; + + list_for_each_entry(temp_p, &pwrdm_list, node) + pwrdm_set_next_pwrst(temp_p, PWRDM_POWER_ON); + + /* Only AM43XX can lose pwrdm context during rtc-ddr suspend */ + if (soc_is_am43xx()) { + nb.notifier_call = cpu_notifier; + cpu_pm_register_notifier(&nb); + } + + return 0; +} + +/** + * pwrdm_lock - acquire a Linux spinlock on a powerdomain + * @pwrdm: struct powerdomain * to lock + * + * Acquire the powerdomain spinlock on @pwrdm. No return value. + */ +void pwrdm_lock(struct powerdomain *pwrdm) + __acquires(&pwrdm->_lock) +{ + spin_lock_irqsave(&pwrdm->_lock, pwrdm->_lock_flags); +} + +/** + * pwrdm_unlock - release a Linux spinlock on a powerdomain + * @pwrdm: struct powerdomain * to unlock + * + * Release the powerdomain spinlock on @pwrdm. No return value. + */ +void pwrdm_unlock(struct powerdomain *pwrdm) + __releases(&pwrdm->_lock) +{ + spin_unlock_irqrestore(&pwrdm->_lock, pwrdm->_lock_flags); +} + +/** + * pwrdm_lookup - look up a powerdomain by name, return a pointer + * @name: name of powerdomain + * + * Find a registered powerdomain by its name @name. Returns a pointer + * to the struct powerdomain if found, or NULL otherwise. + */ +struct powerdomain *pwrdm_lookup(const char *name) +{ + struct powerdomain *pwrdm; + + if (!name) + return NULL; + + pwrdm = _pwrdm_lookup(name); + + return pwrdm; +} + +/** + * pwrdm_for_each - call function on each registered clockdomain + * @fn: callback function * + * + * Call the supplied function @fn for each registered powerdomain. + * The callback function @fn can return anything but 0 to bail out + * early from the iterator. Returns the last return value of the + * callback function, which should be 0 for success or anything else + * to indicate failure; or -EINVAL if the function pointer is null. + */ +int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user) +{ + struct powerdomain *temp_pwrdm; + int ret = 0; + + if (!fn) + return -EINVAL; + + list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { + ret = (*fn)(temp_pwrdm, user); + if (ret) + break; + } + + return ret; +} + +/** + * pwrdm_add_clkdm - add a clockdomain to a powerdomain + * @pwrdm: struct powerdomain * to add the clockdomain to + * @clkdm: struct clockdomain * to associate with a powerdomain + * + * Associate the clockdomain @clkdm with a powerdomain @pwrdm. This + * enables the use of pwrdm_for_each_clkdm(). Returns -EINVAL if + * presented with invalid pointers; -ENOMEM if memory could not be allocated; + * or 0 upon success. + */ +int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) +{ + int i; + int ret = -EINVAL; + + if (!pwrdm || !clkdm) + return -EINVAL; + + pr_debug("powerdomain: %s: associating clockdomain %s\n", + pwrdm->name, clkdm->name); + + for (i = 0; i < PWRDM_MAX_CLKDMS; i++) { + if (!pwrdm->pwrdm_clkdms[i]) + break; +#ifdef DEBUG + if (pwrdm->pwrdm_clkdms[i] == clkdm) { + ret = -EINVAL; + goto pac_exit; + } +#endif + } + + if (i == PWRDM_MAX_CLKDMS) { + pr_debug("powerdomain: %s: increase PWRDM_MAX_CLKDMS for clkdm %s\n", + pwrdm->name, clkdm->name); + WARN_ON(1); + ret = -ENOMEM; + goto pac_exit; + } + + pwrdm->pwrdm_clkdms[i] = clkdm; + + ret = 0; + +pac_exit: + return ret; +} + +/** + * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain + * @pwrdm: struct powerdomain * + * + * Return the number of controllable memory banks in powerdomain @pwrdm, + * starting with 1. Returns -EINVAL if the powerdomain pointer is null. + */ +int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm) +{ + if (!pwrdm) + return -EINVAL; + + return pwrdm->banks; +} + +/** + * pwrdm_set_next_pwrst - set next powerdomain power state + * @pwrdm: struct powerdomain * to set + * @pwrst: one of the PWRDM_POWER_* macros + * + * Set the powerdomain @pwrdm's next power state to @pwrst. The powerdomain + * may not enter this state immediately if the preconditions for this state + * have not been satisfied. Returns -EINVAL if the powerdomain pointer is + * null or if the power state is invalid for the powerdomin, or returns 0 + * upon success. + */ +int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (!(pwrdm->pwrsts & (1 << pwrst))) + return -EINVAL; + + pr_debug("powerdomain: %s: setting next powerstate to %0x\n", + pwrdm->name, pwrst); + + if (arch_pwrdm && arch_pwrdm->pwrdm_set_next_pwrst) { + /* Trace the pwrdm desired target state */ + trace_power_domain_target_rcuidle(pwrdm->name, pwrst, + raw_smp_processor_id()); + /* Program the pwrdm desired target state */ + ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst); + } + + return ret; +} + +/** + * pwrdm_read_next_pwrst - get next powerdomain power state + * @pwrdm: struct powerdomain * to get power state + * + * Return the powerdomain @pwrdm's next power state. Returns -EINVAL + * if the powerdomain pointer is null or returns the next power state + * upon success. + */ +int pwrdm_read_next_pwrst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_next_pwrst) + ret = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm); + + return ret; +} + +/** + * pwrdm_read_pwrst - get current powerdomain power state + * @pwrdm: struct powerdomain * to get power state + * + * Return the powerdomain @pwrdm's current power state. Returns -EINVAL + * if the powerdomain pointer is null or returns the current power state + * upon success. Note that if the power domain only supports the ON state + * then just return ON as the current state. + */ +int pwrdm_read_pwrst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (pwrdm->pwrsts == PWRSTS_ON) + return PWRDM_POWER_ON; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_pwrst) + ret = arch_pwrdm->pwrdm_read_pwrst(pwrdm); + + return ret; +} + +/** + * pwrdm_read_prev_pwrst - get previous powerdomain power state + * @pwrdm: struct powerdomain * to get previous power state + * + * Return the powerdomain @pwrdm's previous power state. Returns -EINVAL + * if the powerdomain pointer is null or returns the previous power state + * upon success. + */ +int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_pwrst) + ret = arch_pwrdm->pwrdm_read_prev_pwrst(pwrdm); + + return ret; +} + +/** + * pwrdm_set_logic_retst - set powerdomain logic power state upon retention + * @pwrdm: struct powerdomain * to set + * @pwrst: one of the PWRDM_POWER_* macros + * + * Set the next power state @pwrst that the logic portion of the + * powerdomain @pwrdm will enter when the powerdomain enters retention. + * This will be either RETENTION or OFF, if supported. Returns + * -EINVAL if the powerdomain pointer is null or the target power + * state is not supported, or returns 0 upon success. + */ +int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst))) + return -EINVAL; + + pr_debug("powerdomain: %s: setting next logic powerstate to %0x\n", + pwrdm->name, pwrst); + + if (arch_pwrdm && arch_pwrdm->pwrdm_set_logic_retst) + ret = arch_pwrdm->pwrdm_set_logic_retst(pwrdm, pwrst); + + return ret; +} + +/** + * pwrdm_set_mem_onst - set memory power state while powerdomain ON + * @pwrdm: struct powerdomain * to set + * @bank: memory bank number to set (0-3) + * @pwrst: one of the PWRDM_POWER_* macros + * + * Set the next power state @pwrst that memory bank @bank of the + * powerdomain @pwrdm will enter when the powerdomain enters the ON + * state. @bank will be a number from 0 to 3, and represents different + * types of memory, depending on the powerdomain. Returns -EINVAL if + * the powerdomain pointer is null or the target power state is not + * supported for this memory bank, -EEXIST if the target memory + * bank does not exist or is not controllable, or returns 0 upon + * success. + */ +int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (pwrdm->banks < (bank + 1)) + return -EEXIST; + + if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst))) + return -EINVAL; + + pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-ON to %0x\n", + pwrdm->name, bank, pwrst); + + if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_onst) + ret = arch_pwrdm->pwrdm_set_mem_onst(pwrdm, bank, pwrst); + + return ret; +} + +/** + * pwrdm_set_mem_retst - set memory power state while powerdomain in RET + * @pwrdm: struct powerdomain * to set + * @bank: memory bank number to set (0-3) + * @pwrst: one of the PWRDM_POWER_* macros + * + * Set the next power state @pwrst that memory bank @bank of the + * powerdomain @pwrdm will enter when the powerdomain enters the + * RETENTION state. Bank will be a number from 0 to 3, and represents + * different types of memory, depending on the powerdomain. @pwrst + * will be either RETENTION or OFF, if supported. Returns -EINVAL if + * the powerdomain pointer is null or the target power state is not + * supported for this memory bank, -EEXIST if the target memory + * bank does not exist or is not controllable, or returns 0 upon + * success. + */ +int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (pwrdm->banks < (bank + 1)) + return -EEXIST; + + if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst))) + return -EINVAL; + + pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-RET to %0x\n", + pwrdm->name, bank, pwrst); + + if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_retst) + ret = arch_pwrdm->pwrdm_set_mem_retst(pwrdm, bank, pwrst); + + return ret; +} + +/** + * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state + * @pwrdm: struct powerdomain * to get current logic retention power state + * + * Return the power state that the logic portion of powerdomain @pwrdm + * will enter when the powerdomain enters retention. Returns -EINVAL + * if the powerdomain pointer is null or returns the logic retention + * power state upon success. + */ +int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_pwrst) + ret = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm); + + return ret; +} + +/** + * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state + * @pwrdm: struct powerdomain * to get previous logic power state + * + * Return the powerdomain @pwrdm's previous logic power state. Returns + * -EINVAL if the powerdomain pointer is null or returns the previous + * logic power state upon success. + */ +int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_logic_pwrst) + ret = arch_pwrdm->pwrdm_read_prev_logic_pwrst(pwrdm); + + return ret; +} + +/** + * pwrdm_read_logic_retst - get next powerdomain logic power state + * @pwrdm: struct powerdomain * to get next logic power state + * + * Return the powerdomain pwrdm's logic power state. Returns -EINVAL + * if the powerdomain pointer is null or returns the next logic + * power state upon success. + */ +int pwrdm_read_logic_retst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_retst) + ret = arch_pwrdm->pwrdm_read_logic_retst(pwrdm); + + return ret; +} + +/** + * pwrdm_read_mem_pwrst - get current memory bank power state + * @pwrdm: struct powerdomain * to get current memory bank power state + * @bank: memory bank number (0-3) + * + * Return the powerdomain @pwrdm's current memory power state for bank + * @bank. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if + * the target memory bank does not exist or is not controllable, or + * returns the current memory power state upon success. + */ +int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + int ret = -EINVAL; + + if (!pwrdm) + return ret; + + if (pwrdm->banks < (bank + 1)) + return ret; + + if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK) + bank = 1; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_pwrst) + ret = arch_pwrdm->pwrdm_read_mem_pwrst(pwrdm, bank); + + return ret; +} + +/** + * pwrdm_read_prev_mem_pwrst - get previous memory bank power state + * @pwrdm: struct powerdomain * to get previous memory bank power state + * @bank: memory bank number (0-3) + * + * Return the powerdomain @pwrdm's previous memory power state for + * bank @bank. Returns -EINVAL if the powerdomain pointer is null, + * -EEXIST if the target memory bank does not exist or is not + * controllable, or returns the previous memory power state upon + * success. + */ +int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + int ret = -EINVAL; + + if (!pwrdm) + return ret; + + if (pwrdm->banks < (bank + 1)) + return ret; + + if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK) + bank = 1; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_mem_pwrst) + ret = arch_pwrdm->pwrdm_read_prev_mem_pwrst(pwrdm, bank); + + return ret; +} + +/** + * pwrdm_read_mem_retst - get next memory bank power state + * @pwrdm: struct powerdomain * to get mext memory bank power state + * @bank: memory bank number (0-3) + * + * Return the powerdomain pwrdm's next memory power state for bank + * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if + * the target memory bank does not exist or is not controllable, or + * returns the next memory power state upon success. + */ +int pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank) +{ + int ret = -EINVAL; + + if (!pwrdm) + return ret; + + if (pwrdm->banks < (bank + 1)) + return ret; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_retst) + ret = arch_pwrdm->pwrdm_read_mem_retst(pwrdm, bank); + + return ret; +} + +/** + * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm + * @pwrdm: struct powerdomain * to clear + * + * Clear the powerdomain's previous power state register @pwrdm. + * Clears the entire register, including logic and memory bank + * previous power states. Returns -EINVAL if the powerdomain pointer + * is null, or returns 0 upon success. + */ +int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return ret; + + /* + * XXX should get the powerdomain's current state here; + * warn & fail if it is not ON. + */ + + pr_debug("powerdomain: %s: clearing previous power state reg\n", + pwrdm->name); + + if (arch_pwrdm && arch_pwrdm->pwrdm_clear_all_prev_pwrst) + ret = arch_pwrdm->pwrdm_clear_all_prev_pwrst(pwrdm); + + return ret; +} + +/** + * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm + * @pwrdm: struct powerdomain * + * + * Enable automatic context save-and-restore upon power state change + * for some devices in the powerdomain @pwrdm. Warning: this only + * affects a subset of devices in a powerdomain; check the TRM + * closely. Returns -EINVAL if the powerdomain pointer is null or if + * the powerdomain does not support automatic save-and-restore, or + * returns 0 upon success. + */ +int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return ret; + + if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) + return ret; + + pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", pwrdm->name); + + if (arch_pwrdm && arch_pwrdm->pwrdm_enable_hdwr_sar) + ret = arch_pwrdm->pwrdm_enable_hdwr_sar(pwrdm); + + return ret; +} + +/** + * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm + * @pwrdm: struct powerdomain * + * + * Disable automatic context save-and-restore upon power state change + * for some devices in the powerdomain @pwrdm. Warning: this only + * affects a subset of devices in a powerdomain; check the TRM + * closely. Returns -EINVAL if the powerdomain pointer is null or if + * the powerdomain does not support automatic save-and-restore, or + * returns 0 upon success. + */ +int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return ret; + + if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) + return ret; + + pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", pwrdm->name); + + if (arch_pwrdm && arch_pwrdm->pwrdm_disable_hdwr_sar) + ret = arch_pwrdm->pwrdm_disable_hdwr_sar(pwrdm); + + return ret; +} + +/** + * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR + * @pwrdm: struct powerdomain * + * + * Returns 1 if powerdomain @pwrdm supports hardware save-and-restore + * for some devices, or 0 if it does not. + */ +bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm) +{ + return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0; +} + +int pwrdm_state_switch_nolock(struct powerdomain *pwrdm) +{ + int ret; + + if (!pwrdm || !arch_pwrdm) + return -EINVAL; + + ret = arch_pwrdm->pwrdm_wait_transition(pwrdm); + if (!ret) + ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); + + return ret; +} + +int __deprecated pwrdm_state_switch(struct powerdomain *pwrdm) +{ + int ret; + + pwrdm_lock(pwrdm); + ret = pwrdm_state_switch_nolock(pwrdm); + pwrdm_unlock(pwrdm); + + return ret; +} + +int pwrdm_pre_transition(struct powerdomain *pwrdm) +{ + if (pwrdm) + _pwrdm_pre_transition_cb(pwrdm, NULL); + else + pwrdm_for_each(_pwrdm_pre_transition_cb, NULL); + + return 0; +} + +int pwrdm_post_transition(struct powerdomain *pwrdm) +{ + if (pwrdm) + _pwrdm_post_transition_cb(pwrdm, NULL); + else + pwrdm_for_each(_pwrdm_post_transition_cb, NULL); + + return 0; +} + +/** + * pwrdm_get_valid_lp_state() - Find best match deep power state + * @pwrdm: power domain for which we want to find best match + * @is_logic_state: Are we looking for logic state match here? Should + * be one of PWRDM_xxx macro values + * @req_state: requested power state + * + * Returns: closest match for requested power state. default fallback + * is RET for logic state and ON for power state. + * + * This does a search from the power domain data looking for the + * closest valid power domain state that the hardware can achieve. + * PRCM definitions for PWRSTCTRL allows us to program whatever + * configuration we'd like, and PRCM will actually attempt such + * a transition, however if the powerdomain does not actually support it, + * we endup with a hung system. The valid power domain states are already + * available in our powerdomain data files. So this function tries to do + * the following: + * a) find if we have an exact match to the request - no issues. + * b) else find if a deeper power state is possible. + * c) failing which, it tries to find closest higher power state for the + * request. + */ +u8 pwrdm_get_valid_lp_state(struct powerdomain *pwrdm, + bool is_logic_state, u8 req_state) +{ + u8 pwrdm_states = is_logic_state ? pwrdm->pwrsts_logic_ret : + pwrdm->pwrsts; + /* For logic, ret is highest and others, ON is highest */ + u8 default_pwrst = is_logic_state ? PWRDM_POWER_RET : PWRDM_POWER_ON; + u8 new_pwrst; + bool found; + + /* If it is already supported, nothing to search */ + if (pwrdm_states & BIT(req_state)) + return req_state; + + if (!req_state) + goto up_search; + + /* + * So, we dont have a exact match + * Can we get a deeper power state match? + */ + new_pwrst = req_state - 1; + found = true; + while (!(pwrdm_states & BIT(new_pwrst))) { + /* No match even at OFF? Not available */ + if (new_pwrst == PWRDM_POWER_OFF) { + found = false; + break; + } + new_pwrst--; + } + + if (found) + goto done; + +up_search: + /* OK, no deeper ones, can we get a higher match? */ + new_pwrst = req_state + 1; + while (!(pwrdm_states & BIT(new_pwrst))) { + if (new_pwrst > PWRDM_POWER_ON) { + WARN(1, "powerdomain: %s: Fix max powerstate to ON\n", + pwrdm->name); + return PWRDM_POWER_ON; + } + + if (new_pwrst == default_pwrst) + break; + new_pwrst++; + } +done: + return new_pwrst; +} + +/** + * omap_set_pwrdm_state - change a powerdomain's current power state + * @pwrdm: struct powerdomain * to change the power state of + * @pwrst: power state to change to + * + * Change the current hardware power state of the powerdomain + * represented by @pwrdm to the power state represented by @pwrst. + * Returns -EINVAL if @pwrdm is null or invalid or if the + * powerdomain's current power state could not be read, or returns 0 + * upon success or if @pwrdm does not support @pwrst or any + * lower-power state. XXX Should not return 0 if the @pwrdm does not + * support @pwrst or any lower-power state: this should be an error. + */ +int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst) +{ + u8 next_pwrst, sleep_switch; + int curr_pwrst; + int ret = 0; + + if (!pwrdm || IS_ERR(pwrdm)) + return -EINVAL; + + while (!(pwrdm->pwrsts & (1 << pwrst))) { + if (pwrst == PWRDM_POWER_OFF) + return ret; + pwrst--; + } + + pwrdm_lock(pwrdm); + + curr_pwrst = pwrdm_read_pwrst(pwrdm); + if (curr_pwrst < 0) { + ret = -EINVAL; + goto osps_out; + } + + next_pwrst = pwrdm_read_next_pwrst(pwrdm); + if (curr_pwrst == pwrst && next_pwrst == pwrst) + goto osps_out; + + sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst, + pwrst); + + ret = pwrdm_set_next_pwrst(pwrdm, pwrst); + if (ret) + pr_err("%s: unable to set power state of powerdomain: %s\n", + __func__, pwrdm->name); + + _pwrdm_restore_clkdm_state(pwrdm, sleep_switch); + +osps_out: + pwrdm_unlock(pwrdm); + + return ret; +} + +/** + * pwrdm_get_context_loss_count - get powerdomain's context loss count + * @pwrdm: struct powerdomain * to wait for + * + * Context loss count is the sum of powerdomain off-mode counter, the + * logic off counter and the per-bank memory off counter. Returns negative + * (and WARNs) upon error, otherwise, returns the context loss count. + */ +int pwrdm_get_context_loss_count(struct powerdomain *pwrdm) +{ + int i, count; + + if (!pwrdm) { + WARN(1, "powerdomain: %s: pwrdm is null\n", __func__); + return -ENODEV; + } + + count = pwrdm->state_counter[PWRDM_POWER_OFF]; + count += pwrdm->ret_logic_off_counter; + + for (i = 0; i < pwrdm->banks; i++) + count += pwrdm->ret_mem_off_counter[i]; + + /* + * Context loss count has to be a non-negative value. Clear the sign + * bit to get a value range from 0 to INT_MAX. + */ + count &= INT_MAX; + + pr_debug("powerdomain: %s: context loss count = %d\n", + pwrdm->name, count); + + return count; +} + +/** + * pwrdm_can_ever_lose_context - can this powerdomain ever lose context? + * @pwrdm: struct powerdomain * + * + * Given a struct powerdomain * @pwrdm, returns 1 if the powerdomain + * can lose either memory or logic context or if @pwrdm is invalid, or + * returns 0 otherwise. This function is not concerned with how the + * powerdomain registers are programmed (i.e., to go off or not); it's + * concerned with whether it's ever possible for this powerdomain to + * go off while some other part of the chip is active. This function + * assumes that every powerdomain can go to either ON or INACTIVE. + */ +bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm) +{ + int i; + + if (!pwrdm) { + pr_debug("powerdomain: %s: invalid powerdomain pointer\n", + __func__); + return true; + } + + if (pwrdm->pwrsts & PWRSTS_OFF) + return true; + + if (pwrdm->pwrsts & PWRSTS_RET) { + if (pwrdm->pwrsts_logic_ret & PWRSTS_OFF) + return true; + + for (i = 0; i < pwrdm->banks; i++) + if (pwrdm->pwrsts_mem_ret[i] & PWRSTS_OFF) + return true; + } + + for (i = 0; i < pwrdm->banks; i++) + if (pwrdm->pwrsts_mem_on[i] & PWRSTS_OFF) + return true; + + return false; +} + +/** + * pwrdm_save_context - save powerdomain registers + * + * Register state is going to be lost due to a suspend or hibernate + * event. Save the powerdomain registers. + */ +static int pwrdm_save_context(struct powerdomain *pwrdm, void *unused) +{ + if (arch_pwrdm && arch_pwrdm->pwrdm_save_context) + arch_pwrdm->pwrdm_save_context(pwrdm); + return 0; +} + +/** + * pwrdm_save_context - restore powerdomain registers + * + * Restore powerdomain control registers after a suspend or resume + * event. + */ +static int pwrdm_restore_context(struct powerdomain *pwrdm, void *unused) +{ + if (arch_pwrdm && arch_pwrdm->pwrdm_restore_context) + arch_pwrdm->pwrdm_restore_context(pwrdm); + return 0; +} + +static int pwrdm_lost_power(struct powerdomain *pwrdm, void *unused) +{ + int state; + + /* + * Power has been lost across all powerdomains, increment the + * counter. + */ + + state = pwrdm_read_pwrst(pwrdm); + if (state != PWRDM_POWER_OFF) { + pwrdm->state_counter[state]++; + pwrdm->state_counter[PWRDM_POWER_OFF]++; + } + pwrdm->state = state; + + return 0; +} + +void pwrdms_save_context(void) +{ + pwrdm_for_each(pwrdm_save_context, NULL); +} + +void pwrdms_restore_context(void) +{ + pwrdm_for_each(pwrdm_restore_context, NULL); +} + +void pwrdms_lost_power(void) +{ + pwrdm_for_each(pwrdm_lost_power, NULL); +} diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h new file mode 100644 index 000000000..907cc659f --- /dev/null +++ b/arch/arm/mach-omap2/powerdomain.h @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2/3/4 powerdomain control + * + * Copyright (C) 2007-2008, 2010 Texas Instruments, Inc. + * Copyright (C) 2007-2011 Nokia Corporation + * + * Paul Walmsley + * + * XXX This should be moved to the mach-omap2/ directory at the earliest + * opportunity. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_POWERDOMAIN_H +#define __ARCH_ARM_MACH_OMAP2_POWERDOMAIN_H + +#include <linux/types.h> +#include <linux/list.h> +#include <linux/spinlock.h> + +/* Powerdomain basic power states */ +#define PWRDM_POWER_OFF 0x0 +#define PWRDM_POWER_RET 0x1 +#define PWRDM_POWER_INACTIVE 0x2 +#define PWRDM_POWER_ON 0x3 + +#define PWRDM_MAX_PWRSTS 4 + +/* Powerdomain allowable state bitfields */ +#define PWRSTS_ON (1 << PWRDM_POWER_ON) +#define PWRSTS_INACTIVE (1 << PWRDM_POWER_INACTIVE) +#define PWRSTS_RET (1 << PWRDM_POWER_RET) +#define PWRSTS_OFF (1 << PWRDM_POWER_OFF) + +#define PWRSTS_OFF_ON (PWRSTS_OFF | PWRSTS_ON) +#define PWRSTS_OFF_RET (PWRSTS_OFF | PWRSTS_RET) +#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON) +#define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | PWRSTS_ON) +#define PWRSTS_INA_ON (PWRSTS_INACTIVE | PWRSTS_ON) + + +/* + * Powerdomain flags (struct powerdomain.flags) + * + * PWRDM_HAS_HDWR_SAR - powerdomain has hardware save-and-restore support + * + * PWRDM_HAS_MPU_QUIRK - MPU pwr domain has MEM bank 0 bits in MEM + * bank 1 position. This is true for OMAP3430 + * + * PWRDM_HAS_LOWPOWERSTATECHANGE - can transition from a sleep state + * to a lower sleep state without waking up the powerdomain + */ +#define PWRDM_HAS_HDWR_SAR BIT(0) +#define PWRDM_HAS_MPU_QUIRK BIT(1) +#define PWRDM_HAS_LOWPOWERSTATECHANGE BIT(2) + +/* + * Number of memory banks that are power-controllable. On OMAP4430, the + * maximum is 5. + */ +#define PWRDM_MAX_MEM_BANKS 5 + +/* + * Maximum number of clockdomains that can be associated with a powerdomain. + * PER powerdomain on AM33XX is the worst case + */ +#define PWRDM_MAX_CLKDMS 11 + +/* XXX A completely arbitrary number. What is reasonable here? */ +#define PWRDM_TRANSITION_BAILOUT 100000 + +struct clockdomain; +struct powerdomain; +struct voltagedomain; + +/** + * struct powerdomain - OMAP powerdomain + * @name: Powerdomain name + * @voltdm: voltagedomain containing this powerdomain + * @prcm_offs: the address offset from CM_BASE/PRM_BASE + * @prcm_partition: (OMAP4 only) the PRCM partition ID containing @prcm_offs + * @pwrsts: Possible powerdomain power states + * @pwrsts_logic_ret: Possible logic power states when pwrdm in RETENTION + * @flags: Powerdomain flags + * @banks: Number of software-controllable memory banks in this powerdomain + * @pwrsts_mem_ret: Possible memory bank pwrstates when pwrdm in RETENTION + * @pwrsts_mem_on: Possible memory bank pwrstates when pwrdm in ON + * @pwrdm_clkdms: Clockdomains in this powerdomain + * @node: list_head linking all powerdomains + * @voltdm_node: list_head linking all powerdomains in a voltagedomain + * @pwrstctrl_offs: (AM33XX only) XXX_PWRSTCTRL reg offset from prcm_offs + * @pwrstst_offs: (AM33XX only) XXX_PWRSTST reg offset from prcm_offs + * @logicretstate_mask: (AM33XX only) mask for logic retention bitfield + * in @pwrstctrl_offs + * @mem_on_mask: (AM33XX only) mask for mem on bitfield in @pwrstctrl_offs + * @mem_ret_mask: (AM33XX only) mask for mem ret bitfield in @pwrstctrl_offs + * @mem_pwrst_mask: (AM33XX only) mask for mem state bitfield in @pwrstst_offs + * @mem_retst_mask: (AM33XX only) mask for mem retention state bitfield + * in @pwrstctrl_offs + * @state: + * @state_counter: + * @timer: + * @state_timer: + * @_lock: spinlock used to serialize powerdomain and some clockdomain ops + * @_lock_flags: stored flags when @_lock is taken + * + * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h. + */ +struct powerdomain { + const char *name; + union { + const char *name; + struct voltagedomain *ptr; + } voltdm; + const s16 prcm_offs; + const u8 pwrsts; + const u8 pwrsts_logic_ret; + const u8 flags; + const u8 banks; + const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS]; + const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS]; + const u8 prcm_partition; + struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS]; + struct list_head node; + struct list_head voltdm_node; + int state; + unsigned state_counter[PWRDM_MAX_PWRSTS]; + unsigned ret_logic_off_counter; + unsigned ret_mem_off_counter[PWRDM_MAX_MEM_BANKS]; + spinlock_t _lock; + unsigned long _lock_flags; + const u8 pwrstctrl_offs; + const u8 pwrstst_offs; + const u32 logicretstate_mask; + const u32 mem_on_mask[PWRDM_MAX_MEM_BANKS]; + const u32 mem_ret_mask[PWRDM_MAX_MEM_BANKS]; + const u32 mem_pwrst_mask[PWRDM_MAX_MEM_BANKS]; + const u32 mem_retst_mask[PWRDM_MAX_MEM_BANKS]; + +#ifdef CONFIG_PM_DEBUG + s64 timer; + s64 state_timer[PWRDM_MAX_PWRSTS]; +#endif + u32 context; +}; + +/** + * struct pwrdm_ops - Arch specific function implementations + * @pwrdm_set_next_pwrst: Set the target power state for a pd + * @pwrdm_read_next_pwrst: Read the target power state set for a pd + * @pwrdm_read_pwrst: Read the current power state of a pd + * @pwrdm_read_prev_pwrst: Read the prev power state entered by the pd + * @pwrdm_set_logic_retst: Set the logic state in RET for a pd + * @pwrdm_set_mem_onst: Set the Memory state in ON for a pd + * @pwrdm_set_mem_retst: Set the Memory state in RET for a pd + * @pwrdm_read_logic_pwrst: Read the current logic state of a pd + * @pwrdm_read_prev_logic_pwrst: Read the previous logic state entered by a pd + * @pwrdm_read_logic_retst: Read the logic state in RET for a pd + * @pwrdm_read_mem_pwrst: Read the current memory state of a pd + * @pwrdm_read_prev_mem_pwrst: Read the previous memory state entered by a pd + * @pwrdm_read_mem_retst: Read the memory state in RET for a pd + * @pwrdm_clear_all_prev_pwrst: Clear all previous power states logged for a pd + * @pwrdm_enable_hdwr_sar: Enable Hardware Save-Restore feature for the pd + * @pwrdm_disable_hdwr_sar: Disable Hardware Save-Restore feature for a pd + * @pwrdm_set_lowpwrstchange: Enable pd transitions from a shallow to deep sleep + * @pwrdm_wait_transition: Wait for a pd state transition to complete + * @pwrdm_has_voltdm: Check if a voltdm association is needed + * + * Regarding @pwrdm_set_lowpwrstchange: On the OMAP2 and 3-family + * chips, a powerdomain's power state is not allowed to directly + * transition from one low-power state (e.g., CSWR) to another + * low-power state (e.g., OFF) without first waking up the + * powerdomain. This wastes energy. So OMAP4 chips support the + * ability to transition a powerdomain power state directly from one + * low-power state to another. The function pointed to by + * @pwrdm_set_lowpwrstchange is intended to configure the OMAP4 + * hardware powerdomain state machine to enable this feature. + */ +struct pwrdm_ops { + int (*pwrdm_set_next_pwrst)(struct powerdomain *pwrdm, u8 pwrst); + int (*pwrdm_read_next_pwrst)(struct powerdomain *pwrdm); + int (*pwrdm_read_pwrst)(struct powerdomain *pwrdm); + int (*pwrdm_read_prev_pwrst)(struct powerdomain *pwrdm); + int (*pwrdm_set_logic_retst)(struct powerdomain *pwrdm, u8 pwrst); + int (*pwrdm_set_mem_onst)(struct powerdomain *pwrdm, u8 bank, u8 pwrst); + int (*pwrdm_set_mem_retst)(struct powerdomain *pwrdm, u8 bank, u8 pwrst); + int (*pwrdm_read_logic_pwrst)(struct powerdomain *pwrdm); + int (*pwrdm_read_prev_logic_pwrst)(struct powerdomain *pwrdm); + int (*pwrdm_read_logic_retst)(struct powerdomain *pwrdm); + int (*pwrdm_read_mem_pwrst)(struct powerdomain *pwrdm, u8 bank); + int (*pwrdm_read_prev_mem_pwrst)(struct powerdomain *pwrdm, u8 bank); + int (*pwrdm_read_mem_retst)(struct powerdomain *pwrdm, u8 bank); + int (*pwrdm_clear_all_prev_pwrst)(struct powerdomain *pwrdm); + int (*pwrdm_enable_hdwr_sar)(struct powerdomain *pwrdm); + int (*pwrdm_disable_hdwr_sar)(struct powerdomain *pwrdm); + int (*pwrdm_set_lowpwrstchange)(struct powerdomain *pwrdm); + int (*pwrdm_wait_transition)(struct powerdomain *pwrdm); + int (*pwrdm_has_voltdm)(void); + void (*pwrdm_save_context)(struct powerdomain *pwrdm); + void (*pwrdm_restore_context)(struct powerdomain *pwrdm); +}; + +int pwrdm_register_platform_funcs(struct pwrdm_ops *custom_funcs); +int pwrdm_register_pwrdms(struct powerdomain **pwrdm_list); +int pwrdm_complete_init(void); + +struct powerdomain *pwrdm_lookup(const char *name); + +int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user); +int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user); + +int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm); + +int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm); + +u8 pwrdm_get_valid_lp_state(struct powerdomain *pwrdm, + bool is_logic_state, u8 req_state); + +int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst); +int pwrdm_read_next_pwrst(struct powerdomain *pwrdm); +int pwrdm_read_pwrst(struct powerdomain *pwrdm); +int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm); +int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm); + +int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst); +int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst); +int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst); + +int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm); +int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm); +int pwrdm_read_logic_retst(struct powerdomain *pwrdm); +int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank); +int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank); +int pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank); + +int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm); +int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm); +bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm); + +int pwrdm_state_switch_nolock(struct powerdomain *pwrdm); +int pwrdm_state_switch(struct powerdomain *pwrdm); +int pwrdm_pre_transition(struct powerdomain *pwrdm); +int pwrdm_post_transition(struct powerdomain *pwrdm); +int pwrdm_get_context_loss_count(struct powerdomain *pwrdm); +bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm); + +extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 state); + +extern void omap242x_powerdomains_init(void); +extern void omap243x_powerdomains_init(void); +extern void omap3xxx_powerdomains_init(void); +extern void am33xx_powerdomains_init(void); +extern void omap44xx_powerdomains_init(void); +extern void omap54xx_powerdomains_init(void); +extern void dra7xx_powerdomains_init(void); +void am43xx_powerdomains_init(void); + +extern struct pwrdm_ops omap2_pwrdm_operations; +extern struct pwrdm_ops omap3_pwrdm_operations; +extern struct pwrdm_ops am33xx_pwrdm_operations; +extern struct pwrdm_ops omap4_pwrdm_operations; + +/* Common Internal functions used across OMAP rev's */ +extern u32 omap2_pwrdm_get_mem_bank_onstate_mask(u8 bank); +extern u32 omap2_pwrdm_get_mem_bank_retst_mask(u8 bank); +extern u32 omap2_pwrdm_get_mem_bank_stst_mask(u8 bank); + +extern struct powerdomain wkup_omap2_pwrdm; +extern struct powerdomain gfx_omap2_pwrdm; + +extern void pwrdm_lock(struct powerdomain *pwrdm); +extern void pwrdm_unlock(struct powerdomain *pwrdm); + +extern void pwrdms_save_context(void); +extern void pwrdms_restore_context(void); + +extern void pwrdms_lost_power(void); +#endif diff --git a/arch/arm/mach-omap2/powerdomains2xxx_3xxx_data.c b/arch/arm/mach-omap2/powerdomains2xxx_3xxx_data.c new file mode 100644 index 000000000..7374c8904 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains2xxx_3xxx_data.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2/3 common powerdomain definitions + * + * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc. + * Copyright (C) 2007-2011 Nokia Corporation + * + * Paul Walmsley, Jouni Högander + */ + +/* + * The names for the DSP/IVA2 powerdomains are confusing. + * + * Most OMAP chips have an on-board DSP. + * + * On the 2420, this is a 'C55 DSP called, simply, the DSP. Its + * powerdomain is called the "DSP power domain." On the 2430, the + * on-board DSP is a 'C64 DSP, now called (along with its hardware + * accelerators) the IVA2 or IVA2.1. Its powerdomain is still called + * the "DSP power domain." On the 3430, the DSP is a 'C64 DSP like the + * 2430, also known as the IVA2; but its powerdomain is now called the + * "IVA2 power domain." + * + * The 2420 also has something called the IVA, which is a separate ARM + * core, and has nothing to do with the DSP/IVA2. + * + * Ideally the DSP/IVA2 could just be the same powerdomain, but the PRCM + * address offset is different between the C55 and C64 DSPs. + */ + +#include "powerdomain.h" + +#include "prcm-common.h" +#include "prm.h" + +/* OMAP2/3-common powerdomains */ + +/* + * The GFX powerdomain is not present on 3430ES2, but currently we do not + * have a macro to filter it out at compile-time. + */ +struct powerdomain gfx_omap2_pwrdm = { + .name = "gfx_pwrdm", + .prcm_offs = GFX_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +struct powerdomain wkup_omap2_pwrdm = { + .name = "wkup_pwrdm", + .prcm_offs = WKUP_MOD, + .pwrsts = PWRSTS_ON, + .voltdm = { .name = "wakeup" }, +}; diff --git a/arch/arm/mach-omap2/powerdomains2xxx_3xxx_data.h b/arch/arm/mach-omap2/powerdomains2xxx_3xxx_data.h new file mode 100644 index 000000000..d7cc26af5 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains2xxx_3xxx_data.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2/3 common powerdomains - prototypes + * + * Copyright (C) 2008 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * + * Paul Walmsley + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_POWERDOMAINS2XXX_3XXX_DATA_H +#define __ARCH_ARM_MACH_OMAP2_POWERDOMAINS2XXX_3XXX_DATA_H + +#include "powerdomain.h" + +extern struct powerdomain gfx_omap2_pwrdm; +extern struct powerdomain wkup_omap2_pwrdm; + +#endif diff --git a/arch/arm/mach-omap2/powerdomains2xxx_data.c b/arch/arm/mach-omap2/powerdomains2xxx_data.c new file mode 100644 index 000000000..fa259adbd --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains2xxx_data.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2XXX powerdomain definitions + * + * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc. + * Copyright (C) 2007-2011 Nokia Corporation + * + * Paul Walmsley, Jouni Högander + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include "soc.h" +#include "powerdomain.h" +#include "powerdomains2xxx_3xxx_data.h" + +#include "prcm-common.h" +#include "prm2xxx_3xxx.h" +#include "prm-regbits-24xx.h" + +/* 24XX powerdomains and dependencies */ + +/* Powerdomains */ + +static struct powerdomain dsp_pwrdm = { + .name = "dsp_pwrdm", + .prcm_offs = OMAP24XX_DSP_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain mpu_24xx_pwrdm = { + .name = "mpu_pwrdm", + .prcm_offs = MPU_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain core_24xx_pwrdm = { + .name = "core_pwrdm", + .prcm_offs = CORE_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 3, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */ + [1] = PWRSTS_OFF_RET, /* MEM2RETSTATE */ + [2] = PWRSTS_OFF_RET, /* MEM3RETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */ + [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */ + [2] = PWRSTS_OFF_RET_ON, /* MEM3ONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + + +/* + * 2430-specific powerdomains + */ + +/* XXX 2430 KILLDOMAINWKUP bit? No current users apparently */ + +static struct powerdomain mdm_pwrdm = { + .name = "mdm_pwrdm", + .prcm_offs = OMAP2430_MDM_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +/* + * + */ + +static struct powerdomain *powerdomains_omap24xx[] __initdata = { + &wkup_omap2_pwrdm, + &gfx_omap2_pwrdm, + &dsp_pwrdm, + &mpu_24xx_pwrdm, + &core_24xx_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_omap2430[] __initdata = { + &mdm_pwrdm, + NULL +}; + +void __init omap242x_powerdomains_init(void) +{ + if (!cpu_is_omap2420()) + return; + + pwrdm_register_platform_funcs(&omap2_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_omap24xx); + pwrdm_complete_init(); +} + +void __init omap243x_powerdomains_init(void) +{ + if (!cpu_is_omap2430()) + return; + + pwrdm_register_platform_funcs(&omap2_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_omap24xx); + pwrdm_register_pwrdms(powerdomains_omap2430); + pwrdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/powerdomains33xx_data.c b/arch/arm/mach-omap2/powerdomains33xx_data.c new file mode 100644 index 000000000..1d58fd1a2 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains33xx_data.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AM33XX Power domain data + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include "powerdomain.h" +#include "prcm-common.h" +#include "prm-regbits-33xx.h" +#include "prm33xx.h" + +static struct powerdomain gfx_33xx_pwrdm = { + .name = "gfx_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM33XX_PRM_GFX_MOD, + .pwrstctrl_offs = AM33XX_PM_GFX_PWRSTCTRL_OFFSET, + .pwrstst_offs = AM33XX_PM_GFX_PWRSTST_OFFSET, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, + .banks = 1, + .logicretstate_mask = AM33XX_LOGICRETSTATE_MASK, + .mem_on_mask = { + [0] = AM33XX_GFX_MEM_ONSTATE_MASK, /* gfx_mem */ + }, + .mem_ret_mask = { + [0] = AM33XX_GFX_MEM_RETSTATE_MASK, /* gfx_mem */ + }, + .mem_pwrst_mask = { + [0] = AM33XX_GFX_MEM_STATEST_MASK, /* gfx_mem */ + }, + .mem_retst_mask = { + [0] = AM33XX_GFX_MEM_RETSTATE_MASK, /* gfx_mem */ + }, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* gfx_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* gfx_mem */ + }, +}; + +static struct powerdomain rtc_33xx_pwrdm = { + .name = "rtc_pwrdm", + .voltdm = { .name = "rtc" }, + .prcm_offs = AM33XX_PRM_RTC_MOD, + .pwrstctrl_offs = AM33XX_PM_RTC_PWRSTCTRL_OFFSET, + .pwrstst_offs = AM33XX_PM_RTC_PWRSTST_OFFSET, + .pwrsts = PWRSTS_ON, + .logicretstate_mask = AM33XX_LOGICRETSTATE_MASK, +}; + +static struct powerdomain wkup_33xx_pwrdm = { + .name = "wkup_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM33XX_PRM_WKUP_MOD, + .pwrstctrl_offs = AM33XX_PM_WKUP_PWRSTCTRL_OFFSET, + .pwrstst_offs = AM33XX_PM_WKUP_PWRSTST_OFFSET, + .pwrsts = PWRSTS_ON, + .logicretstate_mask = AM33XX_LOGICRETSTATE_3_3_MASK, +}; + +static struct powerdomain per_33xx_pwrdm = { + .name = "per_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM33XX_PRM_PER_MOD, + .pwrstctrl_offs = AM33XX_PM_PER_PWRSTCTRL_OFFSET, + .pwrstst_offs = AM33XX_PM_PER_PWRSTST_OFFSET, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, + .banks = 3, + .logicretstate_mask = AM33XX_LOGICRETSTATE_3_3_MASK, + .mem_on_mask = { + [0] = AM33XX_PRUSS_MEM_ONSTATE_MASK, /* pruss_mem */ + [1] = AM33XX_PER_MEM_ONSTATE_MASK, /* per_mem */ + [2] = AM33XX_RAM_MEM_ONSTATE_MASK, /* ram_mem */ + }, + .mem_ret_mask = { + [0] = AM33XX_PRUSS_MEM_RETSTATE_MASK, /* pruss_mem */ + [1] = AM33XX_PER_MEM_RETSTATE_MASK, /* per_mem */ + [2] = AM33XX_RAM_MEM_RETSTATE_MASK, /* ram_mem */ + }, + .mem_pwrst_mask = { + [0] = AM33XX_PRUSS_MEM_STATEST_MASK, /* pruss_mem */ + [1] = AM33XX_PER_MEM_STATEST_MASK, /* per_mem */ + [2] = AM33XX_RAM_MEM_STATEST_MASK, /* ram_mem */ + }, + .mem_retst_mask = { + [0] = AM33XX_PRUSS_MEM_RETSTATE_MASK, /* pruss_mem */ + [1] = AM33XX_PER_MEM_RETSTATE_MASK, /* per_mem */ + [2] = AM33XX_RAM_MEM_RETSTATE_MASK, /* ram_mem */ + }, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* pruss_mem */ + [1] = PWRSTS_OFF_RET, /* per_mem */ + [2] = PWRSTS_OFF_RET, /* ram_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* pruss_mem */ + [1] = PWRSTS_ON, /* per_mem */ + [2] = PWRSTS_ON, /* ram_mem */ + }, +}; + +static struct powerdomain mpu_33xx_pwrdm = { + .name = "mpu_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = AM33XX_PRM_MPU_MOD, + .pwrstctrl_offs = AM33XX_PM_MPU_PWRSTCTRL_OFFSET, + .pwrstst_offs = AM33XX_PM_MPU_PWRSTST_OFFSET, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, + .banks = 3, + .logicretstate_mask = AM33XX_LOGICRETSTATE_MASK, + .mem_on_mask = { + [0] = AM33XX_MPU_L1_ONSTATE_MASK, /* mpu_l1 */ + [1] = AM33XX_MPU_L2_ONSTATE_MASK, /* mpu_l2 */ + [2] = AM33XX_MPU_RAM_ONSTATE_MASK, /* mpu_ram */ + }, + .mem_ret_mask = { + [0] = AM33XX_MPU_L1_RETSTATE_MASK, /* mpu_l1 */ + [1] = AM33XX_MPU_L2_RETSTATE_MASK, /* mpu_l2 */ + [2] = AM33XX_MPU_RAM_RETSTATE_MASK, /* mpu_ram */ + }, + .mem_pwrst_mask = { + [0] = AM33XX_MPU_L1_STATEST_MASK, /* mpu_l1 */ + [1] = AM33XX_MPU_L2_STATEST_MASK, /* mpu_l2 */ + [2] = AM33XX_MPU_RAM_STATEST_MASK, /* mpu_ram */ + }, + .mem_retst_mask = { + [0] = AM33XX_MPU_L1_RETSTATE_MASK, /* mpu_l1 */ + [1] = AM33XX_MPU_L2_RETSTATE_MASK, /* mpu_l2 */ + [2] = AM33XX_MPU_RAM_RETSTATE_MASK, /* mpu_ram */ + }, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* mpu_l1 */ + [1] = PWRSTS_OFF_RET, /* mpu_l2 */ + [2] = PWRSTS_OFF_RET, /* mpu_ram */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* mpu_l1 */ + [1] = PWRSTS_ON, /* mpu_l2 */ + [2] = PWRSTS_ON, /* mpu_ram */ + }, +}; + +static struct powerdomain cefuse_33xx_pwrdm = { + .name = "cefuse_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM33XX_PRM_CEFUSE_MOD, + .pwrstctrl_offs = AM33XX_PM_CEFUSE_PWRSTCTRL_OFFSET, + .pwrstst_offs = AM33XX_PM_CEFUSE_PWRSTST_OFFSET, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct powerdomain *powerdomains_am33xx[] __initdata = { + &gfx_33xx_pwrdm, + &rtc_33xx_pwrdm, + &wkup_33xx_pwrdm, + &per_33xx_pwrdm, + &mpu_33xx_pwrdm, + &cefuse_33xx_pwrdm, + NULL, +}; + +void __init am33xx_powerdomains_init(void) +{ + pwrdm_register_platform_funcs(&am33xx_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_am33xx); + pwrdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c new file mode 100644 index 000000000..3564fade6 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 powerdomain definitions + * + * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc. + * Copyright (C) 2007-2011 Nokia Corporation + * + * Paul Walmsley, Jouni Högander + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/bug.h> + +#include "soc.h" +#include "powerdomain.h" +#include "powerdomains2xxx_3xxx_data.h" +#include "prcm-common.h" +#include "prm2xxx_3xxx.h" +#include "prm-regbits-34xx.h" +#include "cm2xxx_3xxx.h" +#include "cm-regbits-34xx.h" + +/* + * 34XX-specific powerdomains, dependencies + */ + +/* + * Powerdomains + */ + +static struct powerdomain iva2_pwrdm = { + .name = "iva2_pwrdm", + .prcm_offs = OMAP3430_IVA2_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 4, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, + [1] = PWRSTS_OFF_RET, + [2] = PWRSTS_OFF_RET, + [3] = PWRSTS_OFF_RET, + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, + [1] = PWRSTS_ON, + [2] = PWRSTS_OFF_ON, + [3] = PWRSTS_ON, + }, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain mpu_3xxx_pwrdm = { + .name = "mpu_pwrdm", + .prcm_offs = MPU_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .flags = PWRDM_HAS_MPU_QUIRK, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_ON, + }, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain mpu_am35x_pwrdm = { + .name = "mpu_pwrdm", + .prcm_offs = MPU_MOD, + .pwrsts = PWRSTS_ON, + .pwrsts_logic_ret = PWRSTS_ON, + .flags = PWRDM_HAS_MPU_QUIRK, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_ON, + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, + }, + .voltdm = { .name = "mpu_iva" }, +}; + +/* + * The USBTLL Save-and-Restore mechanism is broken on + * 3430s up to ES3.0 and 3630ES1.0. Hence this feature + * needs to be disabled on these chips. + * Refer: 3430 errata ID i459 and 3630 errata ID i579 + * + * Note: setting the SAR flag could help for errata ID i478 + * which applies to 3430 <= ES3.1, but since the SAR feature + * is broken, do not use it. + */ +static struct powerdomain core_3xxx_pre_es3_1_pwrdm = { + .name = "core_pwrdm", + .prcm_offs = CORE_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */ + [1] = PWRSTS_OFF_RET, /* MEM2RETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */ + [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain core_3xxx_es3_1_pwrdm = { + .name = "core_pwrdm", + .prcm_offs = CORE_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + /* + * Setting the SAR flag for errata ID i478 which applies + * to 3430 <= ES3.1 + */ + .flags = PWRDM_HAS_HDWR_SAR, /* for USBTLL only */ + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */ + [1] = PWRSTS_OFF_RET, /* MEM2RETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */ + [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain core_am35x_pwrdm = { + .name = "core_pwrdm", + .prcm_offs = CORE_MOD, + .pwrsts = PWRSTS_ON, + .pwrsts_logic_ret = PWRSTS_ON, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_ON, /* MEM1RETSTATE */ + [1] = PWRSTS_ON, /* MEM2RETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEM1ONSTATE */ + [1] = PWRSTS_ON, /* MEM2ONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain dss_pwrdm = { + .name = "dss_pwrdm", + .prcm_offs = OMAP3430_DSS_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain dss_am35x_pwrdm = { + .name = "dss_pwrdm", + .prcm_offs = OMAP3430_DSS_MOD, + .pwrsts = PWRSTS_ON, + .pwrsts_logic_ret = PWRSTS_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_ON, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +/* + * Although the 34XX TRM Rev K Table 4-371 notes that retention is a + * possible SGX powerstate, the SGX device itself does not support + * retention. + */ +static struct powerdomain sgx_pwrdm = { + .name = "sgx_pwrdm", + .prcm_offs = OMAP3430ES2_SGX_MOD, + /* XXX This is accurate for 3430 SGX, but what about GFX? */ + .pwrsts = PWRSTS_OFF_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain sgx_am35x_pwrdm = { + .name = "sgx_pwrdm", + .prcm_offs = OMAP3430ES2_SGX_MOD, + .pwrsts = PWRSTS_ON, + .pwrsts_logic_ret = PWRSTS_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_ON, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain cam_pwrdm = { + .name = "cam_pwrdm", + .prcm_offs = OMAP3430_CAM_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain per_pwrdm = { + .name = "per_pwrdm", + .prcm_offs = OMAP3430_PER_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain per_am35x_pwrdm = { + .name = "per_pwrdm", + .prcm_offs = OMAP3430_PER_MOD, + .pwrsts = PWRSTS_ON, + .pwrsts_logic_ret = PWRSTS_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_ON, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain emu_pwrdm = { + .name = "emu_pwrdm", + .prcm_offs = OMAP3430_EMU_MOD, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain neon_pwrdm = { + .name = "neon_pwrdm", + .prcm_offs = OMAP3430_NEON_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain neon_am35x_pwrdm = { + .name = "neon_pwrdm", + .prcm_offs = OMAP3430_NEON_MOD, + .pwrsts = PWRSTS_ON, + .pwrsts_logic_ret = PWRSTS_ON, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain usbhost_pwrdm = { + .name = "usbhost_pwrdm", + .prcm_offs = OMAP3430ES2_USBHOST_MOD, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + /* + * REVISIT: Enabling usb host save and restore mechanism seems to + * leave the usb host domain permanently in ACTIVE mode after + * changing the usb host power domain state from OFF to active once. + * Disabling for now. + */ + /*.flags = PWRDM_HAS_HDWR_SAR,*/ /* for USBHOST ctrlr only */ + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* MEMRETSTATE */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* MEMONSTATE */ + }, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain dpll1_pwrdm = { + .name = "dpll1_pwrdm", + .prcm_offs = MPU_MOD, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain dpll2_pwrdm = { + .name = "dpll2_pwrdm", + .prcm_offs = OMAP3430_IVA2_MOD, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain dpll3_pwrdm = { + .name = "dpll3_pwrdm", + .prcm_offs = PLL_MOD, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain dpll4_pwrdm = { + .name = "dpll4_pwrdm", + .prcm_offs = PLL_MOD, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain dpll5_pwrdm = { + .name = "dpll5_pwrdm", + .prcm_offs = PLL_MOD, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain alwon_81xx_pwrdm = { + .name = "alwon_pwrdm", + .prcm_offs = TI81XX_PRM_ALWON_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain device_81xx_pwrdm = { + .name = "device_pwrdm", + .prcm_offs = TI81XX_PRM_DEVICE_MOD, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain gem_814x_pwrdm = { + .name = "gem_pwrdm", + .prcm_offs = TI814X_PRM_DSP_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "dsp" }, +}; + +static struct powerdomain ivahd_814x_pwrdm = { + .name = "ivahd_pwrdm", + .prcm_offs = TI814X_PRM_HDVICP_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "iva" }, +}; + +static struct powerdomain hdvpss_814x_pwrdm = { + .name = "hdvpss_pwrdm", + .prcm_offs = TI814X_PRM_HDVPSS_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "dsp" }, +}; + +static struct powerdomain sgx_814x_pwrdm = { + .name = "sgx_pwrdm", + .prcm_offs = TI814X_PRM_GFX_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain isp_814x_pwrdm = { + .name = "isp_pwrdm", + .prcm_offs = TI814X_PRM_ISP_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain active_81xx_pwrdm = { + .name = "active_pwrdm", + .prcm_offs = TI816X_PRM_ACTIVE_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain default_81xx_pwrdm = { + .name = "default_pwrdm", + .prcm_offs = TI81XX_PRM_DEFAULT_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "core" }, +}; + +static struct powerdomain ivahd0_816x_pwrdm = { + .name = "ivahd0_pwrdm", + .prcm_offs = TI816X_PRM_IVAHD0_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain ivahd1_816x_pwrdm = { + .name = "ivahd1_pwrdm", + .prcm_offs = TI816X_PRM_IVAHD1_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain ivahd2_816x_pwrdm = { + .name = "ivahd2_pwrdm", + .prcm_offs = TI816X_PRM_IVAHD2_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "mpu_iva" }, +}; + +static struct powerdomain sgx_816x_pwrdm = { + .name = "sgx_pwrdm", + .prcm_offs = TI816X_PRM_SGX_MOD, + .pwrsts = PWRSTS_OFF_ON, + .voltdm = { .name = "core" }, +}; + +/* As powerdomains are added or removed above, this list must also be changed */ +static struct powerdomain *powerdomains_omap3430_common[] __initdata = { + &wkup_omap2_pwrdm, + &iva2_pwrdm, + &mpu_3xxx_pwrdm, + &neon_pwrdm, + &cam_pwrdm, + &dss_pwrdm, + &per_pwrdm, + &emu_pwrdm, + &dpll1_pwrdm, + &dpll2_pwrdm, + &dpll3_pwrdm, + &dpll4_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_omap3430es1[] __initdata = { + &gfx_omap2_pwrdm, + &core_3xxx_pre_es3_1_pwrdm, + NULL +}; + +/* also includes 3630ES1.0 */ +static struct powerdomain *powerdomains_omap3430es2_es3_0[] __initdata = { + &core_3xxx_pre_es3_1_pwrdm, + &sgx_pwrdm, + &usbhost_pwrdm, + &dpll5_pwrdm, + NULL +}; + +/* also includes 3630ES1.1+ */ +static struct powerdomain *powerdomains_omap3430es3_1plus[] __initdata = { + &core_3xxx_es3_1_pwrdm, + &sgx_pwrdm, + &usbhost_pwrdm, + &dpll5_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_am35x[] __initdata = { + &wkup_omap2_pwrdm, + &mpu_am35x_pwrdm, + &neon_am35x_pwrdm, + &core_am35x_pwrdm, + &sgx_am35x_pwrdm, + &dss_am35x_pwrdm, + &per_am35x_pwrdm, + &emu_pwrdm, + &dpll1_pwrdm, + &dpll3_pwrdm, + &dpll4_pwrdm, + &dpll5_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_ti814x[] __initdata = { + &alwon_81xx_pwrdm, + &device_81xx_pwrdm, + &active_81xx_pwrdm, + &default_81xx_pwrdm, + &gem_814x_pwrdm, + &ivahd_814x_pwrdm, + &hdvpss_814x_pwrdm, + &sgx_814x_pwrdm, + &isp_814x_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_ti816x[] __initdata = { + &alwon_81xx_pwrdm, + &device_81xx_pwrdm, + &active_81xx_pwrdm, + &default_81xx_pwrdm, + &ivahd0_816x_pwrdm, + &ivahd1_816x_pwrdm, + &ivahd2_816x_pwrdm, + &sgx_816x_pwrdm, + NULL +}; + +/* TI81XX specific ops */ +#define TI81XX_PM_PWSTCTRL 0x0000 +#define TI81XX_RM_RSTCTRL 0x0010 +#define TI81XX_PM_PWSTST 0x0004 + +static int ti81xx_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) +{ + omap2_prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK, + (pwrst << OMAP_POWERSTATE_SHIFT), + pwrdm->prcm_offs, TI81XX_PM_PWSTCTRL); + return 0; +} + +static int ti81xx_pwrdm_read_next_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + TI81XX_PM_PWSTCTRL, + OMAP_POWERSTATE_MASK); +} + +static int ti81xx_pwrdm_read_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + (pwrdm->prcm_offs == TI814X_PRM_GFX_MOD) ? TI81XX_RM_RSTCTRL : + TI81XX_PM_PWSTST, + OMAP_POWERSTATEST_MASK); +} + +static int ti81xx_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + (pwrdm->prcm_offs == TI814X_PRM_GFX_MOD) ? TI81XX_RM_RSTCTRL : + TI81XX_PM_PWSTST, + OMAP3430_LOGICSTATEST_MASK); +} + +static int ti81xx_pwrdm_wait_transition(struct powerdomain *pwrdm) +{ + u32 c = 0; + + while ((omap2_prm_read_mod_reg(pwrdm->prcm_offs, + (pwrdm->prcm_offs == TI814X_PRM_GFX_MOD) ? TI81XX_RM_RSTCTRL : + TI81XX_PM_PWSTST) & + OMAP_INTRANSITION_MASK) && + (c++ < PWRDM_TRANSITION_BAILOUT)) + udelay(1); + + if (c > PWRDM_TRANSITION_BAILOUT) { + pr_err("powerdomain: %s timeout waiting for transition\n", + pwrdm->name); + return -EAGAIN; + } + + pr_debug("powerdomain: completed transition in %d loops\n", c); + + return 0; +} + +/* For dm814x we need to fix up fix GFX pwstst and rstctrl reg offsets */ +static struct pwrdm_ops ti81xx_pwrdm_operations = { + .pwrdm_set_next_pwrst = ti81xx_pwrdm_set_next_pwrst, + .pwrdm_read_next_pwrst = ti81xx_pwrdm_read_next_pwrst, + .pwrdm_read_pwrst = ti81xx_pwrdm_read_pwrst, + .pwrdm_read_logic_pwrst = ti81xx_pwrdm_read_logic_pwrst, + .pwrdm_wait_transition = ti81xx_pwrdm_wait_transition, +}; + +void __init omap3xxx_powerdomains_init(void) +{ + unsigned int rev; + + if (!cpu_is_omap34xx() && !cpu_is_ti81xx()) + return; + + /* Only 81xx needs custom pwrdm_operations */ + if (!cpu_is_ti81xx()) + pwrdm_register_platform_funcs(&omap3_pwrdm_operations); + + rev = omap_rev(); + + if (rev == AM35XX_REV_ES1_0 || rev == AM35XX_REV_ES1_1) { + pwrdm_register_pwrdms(powerdomains_am35x); + } else if (rev == TI8148_REV_ES1_0 || rev == TI8148_REV_ES2_0 || + rev == TI8148_REV_ES2_1) { + pwrdm_register_platform_funcs(&ti81xx_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_ti814x); + } else if (rev == TI8168_REV_ES1_0 || rev == TI8168_REV_ES1_1 + || rev == TI8168_REV_ES2_0 || rev == TI8168_REV_ES2_1) { + pwrdm_register_platform_funcs(&ti81xx_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_ti816x); + } else { + pwrdm_register_pwrdms(powerdomains_omap3430_common); + + switch (rev) { + case OMAP3430_REV_ES1_0: + pwrdm_register_pwrdms(powerdomains_omap3430es1); + break; + case OMAP3430_REV_ES2_0: + case OMAP3430_REV_ES2_1: + case OMAP3430_REV_ES3_0: + case OMAP3630_REV_ES1_0: + pwrdm_register_pwrdms(powerdomains_omap3430es2_es3_0); + break; + case OMAP3430_REV_ES3_1: + case OMAP3430_REV_ES3_1_2: + case OMAP3630_REV_ES1_1: + case OMAP3630_REV_ES1_2: + pwrdm_register_pwrdms(powerdomains_omap3430es3_1plus); + break; + default: + WARN(1, "OMAP3 powerdomain init: unknown chip type\n"); + } + } + + pwrdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/powerdomains43xx_data.c b/arch/arm/mach-omap2/powerdomains43xx_data.c new file mode 100644 index 000000000..5255f7e92 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains43xx_data.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AM43xx Power domains framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include "powerdomain.h" + +#include "prcm-common.h" +#include "prcm44xx.h" +#include "prcm43xx.h" + +static struct powerdomain gfx_43xx_pwrdm = { + .name = "gfx_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM43XX_PRM_GFX_INST, + .prcm_partition = AM43XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* gfx_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +static struct powerdomain mpu_43xx_pwrdm = { + .name = "mpu_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = AM43XX_PRM_MPU_INST, + .prcm_partition = AM43XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 3, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* mpu_l1 */ + [1] = PWRSTS_OFF_RET, /* mpu_l2 */ + [2] = PWRSTS_OFF_RET, /* mpu_ram */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* mpu_l1 */ + [1] = PWRSTS_ON, /* mpu_l2 */ + [2] = PWRSTS_ON, /* mpu_ram */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +static struct powerdomain rtc_43xx_pwrdm = { + .name = "rtc_pwrdm", + .voltdm = { .name = "rtc" }, + .prcm_offs = AM43XX_PRM_RTC_INST, + .prcm_partition = AM43XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, +}; + +static struct powerdomain wkup_43xx_pwrdm = { + .name = "wkup_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM43XX_PRM_WKUP_INST, + .prcm_partition = AM43XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* debugss_mem */ + }, +}; + +static struct powerdomain tamper_43xx_pwrdm = { + .name = "tamper_pwrdm", + .voltdm = { .name = "tamper" }, + .prcm_offs = AM43XX_PRM_TAMPER_INST, + .prcm_partition = AM43XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, +}; + +static struct powerdomain cefuse_43xx_pwrdm = { + .name = "cefuse_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM43XX_PRM_CEFUSE_INST, + .prcm_partition = AM43XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +static struct powerdomain per_43xx_pwrdm = { + .name = "per_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = AM43XX_PRM_PER_INST, + .prcm_partition = AM43XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 4, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* icss_mem */ + [1] = PWRSTS_OFF_RET, /* per_mem */ + [2] = PWRSTS_OFF_RET, /* ram1_mem */ + [3] = PWRSTS_OFF_RET, /* ram2_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* icss_mem */ + [1] = PWRSTS_ON, /* per_mem */ + [2] = PWRSTS_ON, /* ram1_mem */ + [3] = PWRSTS_ON, /* ram2_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +static struct powerdomain *powerdomains_am43xx[] __initdata = { + &gfx_43xx_pwrdm, + &mpu_43xx_pwrdm, + &rtc_43xx_pwrdm, + &wkup_43xx_pwrdm, + &tamper_43xx_pwrdm, + &cefuse_43xx_pwrdm, + &per_43xx_pwrdm, + NULL +}; + +static int am43xx_check_vcvp(void) +{ + return 0; +} + +void __init am43xx_powerdomains_init(void) +{ + omap4_pwrdm_operations.pwrdm_has_voltdm = am43xx_check_vcvp; + pwrdm_register_platform_funcs(&omap4_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_am43xx); + pwrdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/powerdomains44xx_data.c b/arch/arm/mach-omap2/powerdomains44xx_data.c new file mode 100644 index 000000000..37fd1a567 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains44xx_data.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 Power domains framework + * + * Copyright (C) 2009-2011 Texas Instruments, Inc. + * Copyright (C) 2009-2011 Nokia Corporation + * + * Abhijit Pagare (abhijitpagare@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * Paul Walmsley (paul@pwsan.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include "powerdomain.h" + +#include "prcm-common.h" +#include "prcm44xx.h" +#include "prm-regbits-44xx.h" +#include "prm44xx.h" +#include "prcm_mpu44xx.h" + +/* core_44xx_pwrdm: CORE power domain */ +static struct powerdomain core_44xx_pwrdm = { + .name = "core_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_CORE_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 5, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* core_nret_bank */ + [1] = PWRSTS_RET, /* core_ocmram */ + [2] = PWRSTS_RET, /* core_other_bank */ + [3] = PWRSTS_OFF_RET, /* ducati_l2ram */ + [4] = PWRSTS_OFF_RET, /* ducati_unicache */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* core_nret_bank */ + [1] = PWRSTS_ON, /* core_ocmram */ + [2] = PWRSTS_ON, /* core_other_bank */ + [3] = PWRSTS_ON, /* ducati_l2ram */ + [4] = PWRSTS_ON, /* ducati_unicache */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* gfx_44xx_pwrdm: 3D accelerator power domain */ +static struct powerdomain gfx_44xx_pwrdm = { + .name = "gfx_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_GFX_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* gfx_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* gfx_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* abe_44xx_pwrdm: Audio back end power domain */ +static struct powerdomain abe_44xx_pwrdm = { + .name = "abe_pwrdm", + .voltdm = { .name = "iva" }, + .prcm_offs = OMAP4430_PRM_ABE_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* aessmem */ + [1] = PWRSTS_OFF, /* periphmem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* aessmem */ + [1] = PWRSTS_ON, /* periphmem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* dss_44xx_pwrdm: Display subsystem power domain */ +static struct powerdomain dss_44xx_pwrdm = { + .name = "dss_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_DSS_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* dss_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* dss_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* tesla_44xx_pwrdm: Tesla processor power domain */ +static struct powerdomain tesla_44xx_pwrdm = { + .name = "tesla_pwrdm", + .voltdm = { .name = "iva" }, + .prcm_offs = OMAP4430_PRM_TESLA_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 3, + .pwrsts_mem_ret = { + [0] = PWRSTS_RET, /* tesla_edma */ + [1] = PWRSTS_OFF_RET, /* tesla_l1 */ + [2] = PWRSTS_OFF_RET, /* tesla_l2 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* tesla_edma */ + [1] = PWRSTS_ON, /* tesla_l1 */ + [2] = PWRSTS_ON, /* tesla_l2 */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* wkup_44xx_pwrdm: Wake-up power domain */ +static struct powerdomain wkup_44xx_pwrdm = { + .name = "wkup_pwrdm", + .voltdm = { .name = "wakeup" }, + .prcm_offs = OMAP4430_PRM_WKUP_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* wkup_bank */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* wkup_bank */ + }, +}; + +/* cpu0_44xx_pwrdm: MPU0 processor and Neon coprocessor power domain */ +static struct powerdomain cpu0_44xx_pwrdm = { + .name = "cpu0_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = OMAP4430_PRCM_MPU_CPU0_INST, + .prcm_partition = OMAP4430_PRCM_MPU_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* cpu0_l1 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* cpu0_l1 */ + }, +}; + +/* cpu1_44xx_pwrdm: MPU1 processor and Neon coprocessor power domain */ +static struct powerdomain cpu1_44xx_pwrdm = { + .name = "cpu1_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = OMAP4430_PRCM_MPU_CPU1_INST, + .prcm_partition = OMAP4430_PRCM_MPU_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* cpu1_l1 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* cpu1_l1 */ + }, +}; + +/* emu_44xx_pwrdm: Emulation power domain */ +static struct powerdomain emu_44xx_pwrdm = { + .name = "emu_pwrdm", + .voltdm = { .name = "wakeup" }, + .prcm_offs = OMAP4430_PRM_EMU_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* emu_bank */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* emu_bank */ + }, +}; + +/* mpu_44xx_pwrdm: Modena processor and the Neon coprocessor power domain */ +static struct powerdomain mpu_44xx_pwrdm = { + .name = "mpu_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = OMAP4430_PRM_MPU_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 3, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* mpu_l1 */ + [1] = PWRSTS_OFF_RET, /* mpu_l2 */ + [2] = PWRSTS_RET, /* mpu_ram */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* mpu_l1 */ + [1] = PWRSTS_ON, /* mpu_l2 */ + [2] = PWRSTS_ON, /* mpu_ram */ + }, +}; + +/* ivahd_44xx_pwrdm: IVA-HD power domain */ +static struct powerdomain ivahd_44xx_pwrdm = { + .name = "ivahd_pwrdm", + .voltdm = { .name = "iva" }, + .prcm_offs = OMAP4430_PRM_IVAHD_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF, + .banks = 4, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* hwa_mem */ + [1] = PWRSTS_OFF_RET, /* sl2_mem */ + [2] = PWRSTS_OFF_RET, /* tcm1_mem */ + [3] = PWRSTS_OFF_RET, /* tcm2_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* hwa_mem */ + [1] = PWRSTS_ON, /* sl2_mem */ + [2] = PWRSTS_ON, /* tcm1_mem */ + [3] = PWRSTS_ON, /* tcm2_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* cam_44xx_pwrdm: Camera subsystem power domain */ +static struct powerdomain cam_44xx_pwrdm = { + .name = "cam_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_CAM_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* cam_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* cam_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* l3init_44xx_pwrdm: L3 initators pheripherals power domain */ +static struct powerdomain l3init_44xx_pwrdm = { + .name = "l3init_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_L3INIT_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* l3init_bank1 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* l3init_bank1 */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* l4per_44xx_pwrdm: Target peripherals power domain */ +static struct powerdomain l4per_44xx_pwrdm = { + .name = "l4per_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_L4PER_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF, /* nonretained_bank */ + [1] = PWRSTS_RET, /* retained_bank */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* nonretained_bank */ + [1] = PWRSTS_ON, /* retained_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* + * always_on_core_44xx_pwrdm: Always ON logic that sits in VDD_CORE voltage + * domain + */ +static struct powerdomain always_on_core_44xx_pwrdm = { + .name = "always_on_core_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_ALWAYS_ON_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_ON, +}; + +/* cefuse_44xx_pwrdm: Customer efuse controller power domain */ +static struct powerdomain cefuse_44xx_pwrdm = { + .name = "cefuse_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP4430_PRM_CEFUSE_INST, + .prcm_partition = OMAP4430_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* + * The following power domains are not under SW control + * + * always_on_iva + * always_on_mpu + * stdefuse + */ + +/* As powerdomains are added or removed above, this list must also be changed */ +static struct powerdomain *powerdomains_omap44xx[] __initdata = { + &core_44xx_pwrdm, + &gfx_44xx_pwrdm, + &abe_44xx_pwrdm, + &dss_44xx_pwrdm, + &tesla_44xx_pwrdm, + &wkup_44xx_pwrdm, + &cpu0_44xx_pwrdm, + &cpu1_44xx_pwrdm, + &emu_44xx_pwrdm, + &mpu_44xx_pwrdm, + &ivahd_44xx_pwrdm, + &cam_44xx_pwrdm, + &l3init_44xx_pwrdm, + &l4per_44xx_pwrdm, + &always_on_core_44xx_pwrdm, + &cefuse_44xx_pwrdm, + NULL +}; + +void __init omap44xx_powerdomains_init(void) +{ + pwrdm_register_platform_funcs(&omap4_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_omap44xx); + pwrdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/powerdomains54xx_data.c b/arch/arm/mach-omap2/powerdomains54xx_data.c new file mode 100644 index 000000000..5506da2ce --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains54xx_data.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP54XX Power domains framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + * + * Abhijit Pagare (abhijitpagare@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * Paul Walmsley (paul@pwsan.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include "powerdomain.h" + +#include "prcm-common.h" +#include "prcm44xx.h" +#include "prm54xx.h" +#include "prcm_mpu54xx.h" + +/* core_54xx_pwrdm: CORE power domain */ +static struct powerdomain core_54xx_pwrdm = { + .name = "core_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP54XX_PRM_CORE_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 5, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* core_nret_bank */ + [1] = PWRSTS_OFF_RET, /* core_ocmram */ + [2] = PWRSTS_OFF_RET, /* core_other_bank */ + [3] = PWRSTS_OFF_RET, /* ipu_l2ram */ + [4] = PWRSTS_OFF_RET, /* ipu_unicache */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* core_nret_bank */ + [1] = PWRSTS_OFF_RET, /* core_ocmram */ + [2] = PWRSTS_OFF_RET, /* core_other_bank */ + [3] = PWRSTS_OFF_RET, /* ipu_l2ram */ + [4] = PWRSTS_OFF_RET, /* ipu_unicache */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* abe_54xx_pwrdm: Audio back end power domain */ +static struct powerdomain abe_54xx_pwrdm = { + .name = "abe_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP54XX_PRM_ABE_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* aessmem */ + [1] = PWRSTS_OFF_RET, /* periphmem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* aessmem */ + [1] = PWRSTS_OFF_RET, /* periphmem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* coreaon_54xx_pwrdm: Always ON logic that sits in VDD_CORE voltage domain */ +static struct powerdomain coreaon_54xx_pwrdm = { + .name = "coreaon_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP54XX_PRM_COREAON_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, +}; + +/* dss_54xx_pwrdm: Display subsystem power domain */ +static struct powerdomain dss_54xx_pwrdm = { + .name = "dss_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP54XX_PRM_DSS_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* dss_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* dss_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* cpu0_54xx_pwrdm: MPU0 processor and Neon coprocessor power domain */ +static struct powerdomain cpu0_54xx_pwrdm = { + .name = "cpu0_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = OMAP54XX_PRCM_MPU_PRM_C0_INST, + .prcm_partition = OMAP54XX_PRCM_MPU_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* cpu0_l1 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* cpu0_l1 */ + }, +}; + +/* cpu1_54xx_pwrdm: MPU1 processor and Neon coprocessor power domain */ +static struct powerdomain cpu1_54xx_pwrdm = { + .name = "cpu1_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = OMAP54XX_PRCM_MPU_PRM_C1_INST, + .prcm_partition = OMAP54XX_PRCM_MPU_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* cpu1_l1 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* cpu1_l1 */ + }, +}; + +/* emu_54xx_pwrdm: Emulation power domain */ +static struct powerdomain emu_54xx_pwrdm = { + .name = "emu_pwrdm", + .voltdm = { .name = "wkup" }, + .prcm_offs = OMAP54XX_PRM_EMU_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* emu_bank */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* emu_bank */ + }, +}; + +/* mpu_54xx_pwrdm: Modena processor and the Neon coprocessor power domain */ +static struct powerdomain mpu_54xx_pwrdm = { + .name = "mpu_pwrdm", + .voltdm = { .name = "mpu" }, + .prcm_offs = OMAP54XX_PRM_MPU_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* mpu_l2 */ + [1] = PWRSTS_RET, /* mpu_ram */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* mpu_l2 */ + [1] = PWRSTS_OFF_RET, /* mpu_ram */ + }, +}; + +/* custefuse_54xx_pwrdm: Customer efuse controller power domain */ +static struct powerdomain custefuse_54xx_pwrdm = { + .name = "custefuse_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP54XX_PRM_CUSTEFUSE_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* dsp_54xx_pwrdm: Tesla processor power domain */ +static struct powerdomain dsp_54xx_pwrdm = { + .name = "dsp_pwrdm", + .voltdm = { .name = "mm" }, + .prcm_offs = OMAP54XX_PRM_DSP_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 3, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* dsp_edma */ + [1] = PWRSTS_OFF_RET, /* dsp_l1 */ + [2] = PWRSTS_OFF_RET, /* dsp_l2 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* dsp_edma */ + [1] = PWRSTS_OFF_RET, /* dsp_l1 */ + [2] = PWRSTS_OFF_RET, /* dsp_l2 */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* cam_54xx_pwrdm: Camera subsystem power domain */ +static struct powerdomain cam_54xx_pwrdm = { + .name = "cam_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP54XX_PRM_CAM_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* cam_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* cam_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* l3init_54xx_pwrdm: L3 initators pheripherals power domain */ +static struct powerdomain l3init_54xx_pwrdm = { + .name = "l3init_pwrdm", + .voltdm = { .name = "core" }, + .prcm_offs = OMAP54XX_PRM_L3INIT_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF_RET, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* l3init_bank1 */ + [1] = PWRSTS_OFF_RET, /* l3init_bank2 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* l3init_bank1 */ + [1] = PWRSTS_OFF_RET, /* l3init_bank2 */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* gpu_54xx_pwrdm: 3D accelerator power domain */ +static struct powerdomain gpu_54xx_pwrdm = { + .name = "gpu_pwrdm", + .voltdm = { .name = "mm" }, + .prcm_offs = OMAP54XX_PRM_GPU_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* gpu_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* gpu_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* wkupaon_54xx_pwrdm: Wake-up power domain */ +static struct powerdomain wkupaon_54xx_pwrdm = { + .name = "wkupaon_pwrdm", + .voltdm = { .name = "wkup" }, + .prcm_offs = OMAP54XX_PRM_WKUPAON_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, + .banks = 1, + .pwrsts_mem_ret = { + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* wkup_bank */ + }, +}; + +/* iva_54xx_pwrdm: IVA-HD power domain */ +static struct powerdomain iva_54xx_pwrdm = { + .name = "iva_pwrdm", + .voltdm = { .name = "mm" }, + .prcm_offs = OMAP54XX_PRM_IVA_INST, + .prcm_partition = OMAP54XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_RET_ON, + .pwrsts_logic_ret = PWRSTS_OFF, + .banks = 4, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* hwa_mem */ + [1] = PWRSTS_OFF_RET, /* sl2_mem */ + [2] = PWRSTS_OFF_RET, /* tcm1_mem */ + [3] = PWRSTS_OFF_RET, /* tcm2_mem */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_OFF_RET, /* hwa_mem */ + [1] = PWRSTS_OFF_RET, /* sl2_mem */ + [2] = PWRSTS_OFF_RET, /* tcm1_mem */ + [3] = PWRSTS_OFF_RET, /* tcm2_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* + * The following power domains are not under SW control + * + * mpuaon + * mmaon + */ + +/* As powerdomains are added or removed above, this list must also be changed */ +static struct powerdomain *powerdomains_omap54xx[] __initdata = { + &core_54xx_pwrdm, + &abe_54xx_pwrdm, + &coreaon_54xx_pwrdm, + &dss_54xx_pwrdm, + &cpu0_54xx_pwrdm, + &cpu1_54xx_pwrdm, + &emu_54xx_pwrdm, + &mpu_54xx_pwrdm, + &custefuse_54xx_pwrdm, + &dsp_54xx_pwrdm, + &cam_54xx_pwrdm, + &l3init_54xx_pwrdm, + &gpu_54xx_pwrdm, + &wkupaon_54xx_pwrdm, + &iva_54xx_pwrdm, + NULL +}; + +void __init omap54xx_powerdomains_init(void) +{ + pwrdm_register_platform_funcs(&omap4_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_omap54xx); + pwrdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/powerdomains7xx_data.c b/arch/arm/mach-omap2/powerdomains7xx_data.c new file mode 100644 index 000000000..921cb1b29 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomains7xx_data.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRA7xx Power domains framework + * + * Copyright (C) 2009-2013 Texas Instruments, Inc. + * Copyright (C) 2009-2011 Nokia Corporation + * + * Generated by code originally written by: + * Abhijit Pagare (abhijitpagare@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * Paul Walmsley (paul@pwsan.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include "powerdomain.h" + +#include "prcm-common.h" +#include "prcm44xx.h" +#include "prm7xx.h" +#include "prcm_mpu7xx.h" +#include "soc.h" + +/* iva_7xx_pwrdm: IVA-HD power domain */ +static struct powerdomain iva_7xx_pwrdm = { + .name = "iva_pwrdm", + .prcm_offs = DRA7XX_PRM_IVA_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 4, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* hwa_mem */ + [1] = PWRSTS_ON, /* sl2_mem */ + [2] = PWRSTS_ON, /* tcm1_mem */ + [3] = PWRSTS_ON, /* tcm2_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* rtc_7xx_pwrdm: */ +static struct powerdomain rtc_7xx_pwrdm = { + .name = "rtc_pwrdm", + .prcm_offs = DRA7XX_PRM_RTC_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, +}; + +/* custefuse_7xx_pwrdm: Customer efuse controller power domain */ +static struct powerdomain custefuse_7xx_pwrdm = { + .name = "custefuse_pwrdm", + .prcm_offs = DRA7XX_PRM_CUSTEFUSE_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* custefuse_aon_7xx_pwrdm: Customer efuse controller power domain */ +static struct powerdomain custefuse_aon_7xx_pwrdm = { + .name = "custefuse_pwrdm", + .prcm_offs = DRA7XX_PRM_CUSTEFUSE_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, +}; + +/* ipu_7xx_pwrdm: Audio back end power domain */ +static struct powerdomain ipu_7xx_pwrdm = { + .name = "ipu_pwrdm", + .prcm_offs = DRA7XX_PRM_IPU_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 2, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* aessmem */ + [1] = PWRSTS_ON, /* periphmem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* dss_7xx_pwrdm: Display subsystem power domain */ +static struct powerdomain dss_7xx_pwrdm = { + .name = "dss_pwrdm", + .prcm_offs = DRA7XX_PRM_DSS_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* dss_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* l4per_7xx_pwrdm: Target peripherals power domain */ +static struct powerdomain l4per_7xx_pwrdm = { + .name = "l4per_pwrdm", + .prcm_offs = DRA7XX_PRM_L4PER_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, + .banks = 2, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* nonretained_bank */ + [1] = PWRSTS_ON, /* retained_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* gpu_7xx_pwrdm: 3D accelerator power domain */ +static struct powerdomain gpu_7xx_pwrdm = { + .name = "gpu_pwrdm", + .prcm_offs = DRA7XX_PRM_GPU_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* gpu_mem */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* wkupaon_7xx_pwrdm: Wake-up power domain */ +static struct powerdomain wkupaon_7xx_pwrdm = { + .name = "wkupaon_pwrdm", + .prcm_offs = DRA7XX_PRM_WKUPAON_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* wkup_bank */ + }, +}; + +/* core_7xx_pwrdm: CORE power domain */ +static struct powerdomain core_7xx_pwrdm = { + .name = "core_pwrdm", + .prcm_offs = DRA7XX_PRM_CORE_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, + .banks = 5, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* core_nret_bank */ + [1] = PWRSTS_ON, /* core_ocmram */ + [2] = PWRSTS_ON, /* core_other_bank */ + [3] = PWRSTS_ON, /* ipu_l2ram */ + [4] = PWRSTS_ON, /* ipu_unicache */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* coreaon_7xx_pwrdm: Always ON logic that sits in VDD_CORE voltage domain */ +static struct powerdomain coreaon_7xx_pwrdm = { + .name = "coreaon_pwrdm", + .prcm_offs = DRA7XX_PRM_COREAON_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, +}; + +/* cpu0_7xx_pwrdm: MPU0 processor and Neon coprocessor power domain */ +static struct powerdomain cpu0_7xx_pwrdm = { + .name = "cpu0_pwrdm", + .prcm_offs = DRA7XX_MPU_PRCM_PRM_C0_INST, + .prcm_partition = DRA7XX_MPU_PRCM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* cpu0_l1 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* cpu0_l1 */ + }, +}; + +/* cpu1_7xx_pwrdm: MPU1 processor and Neon coprocessor power domain */ +static struct powerdomain cpu1_7xx_pwrdm = { + .name = "cpu1_pwrdm", + .prcm_offs = DRA7XX_MPU_PRCM_PRM_C1_INST, + .prcm_partition = DRA7XX_MPU_PRCM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 1, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* cpu1_l1 */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* cpu1_l1 */ + }, +}; + +/* vpe_7xx_pwrdm: */ +static struct powerdomain vpe_7xx_pwrdm = { + .name = "vpe_pwrdm", + .prcm_offs = DRA7XX_PRM_VPE_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* vpe_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* mpu_7xx_pwrdm: Modena processor and the Neon coprocessor power domain */ +static struct powerdomain mpu_7xx_pwrdm = { + .name = "mpu_pwrdm", + .prcm_offs = DRA7XX_PRM_MPU_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_RET_ON, + .pwrsts_logic_ret = PWRSTS_RET, + .banks = 2, + .pwrsts_mem_ret = { + [0] = PWRSTS_OFF_RET, /* mpu_l2 */ + [1] = PWRSTS_RET, /* mpu_ram */ + }, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* mpu_l2 */ + [1] = PWRSTS_ON, /* mpu_ram */ + }, +}; + +/* l3init_7xx_pwrdm: L3 initators pheripherals power domain */ +static struct powerdomain l3init_7xx_pwrdm = { + .name = "l3init_pwrdm", + .prcm_offs = DRA7XX_PRM_L3INIT_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_ON, + .banks = 3, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* gmac_bank */ + [1] = PWRSTS_ON, /* l3init_bank1 */ + [2] = PWRSTS_ON, /* l3init_bank2 */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* eve3_7xx_pwrdm: */ +static struct powerdomain eve3_7xx_pwrdm = { + .name = "eve3_pwrdm", + .prcm_offs = DRA7XX_PRM_EVE3_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* eve3_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* emu_7xx_pwrdm: Emulation power domain */ +static struct powerdomain emu_7xx_pwrdm = { + .name = "emu_pwrdm", + .prcm_offs = DRA7XX_PRM_EMU_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* emu_bank */ + }, +}; + +/* dsp2_7xx_pwrdm: */ +static struct powerdomain dsp2_7xx_pwrdm = { + .name = "dsp2_pwrdm", + .prcm_offs = DRA7XX_PRM_DSP2_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 3, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* dsp2_edma */ + [1] = PWRSTS_ON, /* dsp2_l1 */ + [2] = PWRSTS_ON, /* dsp2_l2 */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* dsp1_7xx_pwrdm: Tesla processor power domain */ +static struct powerdomain dsp1_7xx_pwrdm = { + .name = "dsp1_pwrdm", + .prcm_offs = DRA7XX_PRM_DSP1_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 3, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* dsp1_edma */ + [1] = PWRSTS_ON, /* dsp1_l1 */ + [2] = PWRSTS_ON, /* dsp1_l2 */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* cam_7xx_pwrdm: Camera subsystem power domain */ +static struct powerdomain cam_7xx_pwrdm = { + .name = "cam_pwrdm", + .prcm_offs = DRA7XX_PRM_CAM_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* vip_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* eve4_7xx_pwrdm: */ +static struct powerdomain eve4_7xx_pwrdm = { + .name = "eve4_pwrdm", + .prcm_offs = DRA7XX_PRM_EVE4_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* eve4_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* eve2_7xx_pwrdm: */ +static struct powerdomain eve2_7xx_pwrdm = { + .name = "eve2_pwrdm", + .prcm_offs = DRA7XX_PRM_EVE2_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* eve2_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* eve1_7xx_pwrdm: */ +static struct powerdomain eve1_7xx_pwrdm = { + .name = "eve1_pwrdm", + .prcm_offs = DRA7XX_PRM_EVE1_INST, + .prcm_partition = DRA7XX_PRM_PARTITION, + .pwrsts = PWRSTS_OFF_ON, + .banks = 1, + .pwrsts_mem_on = { + [0] = PWRSTS_ON, /* eve1_bank */ + }, + .flags = PWRDM_HAS_LOWPOWERSTATECHANGE, +}; + +/* + * The following power domains are not under SW control + * + * mpuaon + * mmaon + */ + +/* As powerdomains are added or removed above, this list must also be changed */ +static struct powerdomain *powerdomains_dra7xx[] __initdata = { + &iva_7xx_pwrdm, + &rtc_7xx_pwrdm, + &ipu_7xx_pwrdm, + &dss_7xx_pwrdm, + &l4per_7xx_pwrdm, + &gpu_7xx_pwrdm, + &wkupaon_7xx_pwrdm, + &core_7xx_pwrdm, + &coreaon_7xx_pwrdm, + &cpu0_7xx_pwrdm, + &cpu1_7xx_pwrdm, + &vpe_7xx_pwrdm, + &mpu_7xx_pwrdm, + &l3init_7xx_pwrdm, + &eve3_7xx_pwrdm, + &emu_7xx_pwrdm, + &dsp2_7xx_pwrdm, + &dsp1_7xx_pwrdm, + &cam_7xx_pwrdm, + &eve4_7xx_pwrdm, + &eve2_7xx_pwrdm, + &eve1_7xx_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_dra76x[] __initdata = { + &custefuse_aon_7xx_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_dra74x[] __initdata = { + &custefuse_7xx_pwrdm, + NULL +}; + +static struct powerdomain *powerdomains_dra72x[] __initdata = { + &custefuse_aon_7xx_pwrdm, + NULL +}; + +void __init dra7xx_powerdomains_init(void) +{ + pwrdm_register_platform_funcs(&omap4_pwrdm_operations); + pwrdm_register_pwrdms(powerdomains_dra7xx); + + if (soc_is_dra76x()) + pwrdm_register_pwrdms(powerdomains_dra76x); + else if (soc_is_dra74x()) + pwrdm_register_pwrdms(powerdomains_dra74x); + else if (soc_is_dra72x()) + pwrdm_register_pwrdms(powerdomains_dra72x); + + pwrdm_complete_init(); +} diff --git a/arch/arm/mach-omap2/prcm-common.h b/arch/arm/mach-omap2/prcm-common.h new file mode 100644 index 000000000..48e804c93 --- /dev/null +++ b/arch/arm/mach-omap2/prcm-common.h @@ -0,0 +1,563 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARCH_ASM_MACH_OMAP2_PRCM_COMMON_H +#define __ARCH_ASM_MACH_OMAP2_PRCM_COMMON_H + +/* + * OMAP2/3 PRCM base and module definitions + * + * Copyright (C) 2007-2009, 2011 Texas Instruments, Inc. + * Copyright (C) 2007-2009 Nokia Corporation + * + * Written by Paul Walmsley + */ + +/* Module offsets from both CM_BASE & PRM_BASE */ + +/* + * Offsets that are the same on 24xx and 34xx + * + * Technically, in terms of the TRM, OCP_MOD is 34xx only; PLL_MOD is + * CCR_MOD on 3430; and GFX_MOD only exists < 3430ES2. + */ +#define OCP_MOD 0x000 +#define MPU_MOD 0x100 +#define CORE_MOD 0x200 +#define GFX_MOD 0x300 +#define WKUP_MOD 0x400 +#define PLL_MOD 0x500 + + +/* Chip-specific module offsets */ +#define OMAP24XX_GR_MOD OCP_MOD +#define OMAP24XX_DSP_MOD 0x800 + +#define OMAP2430_MDM_MOD 0xc00 + +/* IVA2 module is < base on 3430 */ +#define OMAP3430_IVA2_MOD -0x800 +#define OMAP3430ES2_SGX_MOD GFX_MOD +#define OMAP3430_CCR_MOD PLL_MOD +#define OMAP3430_DSS_MOD 0x600 +#define OMAP3430_CAM_MOD 0x700 +#define OMAP3430_PER_MOD 0x800 +#define OMAP3430_EMU_MOD 0x900 +#define OMAP3430_GR_MOD 0xa00 +#define OMAP3430_NEON_MOD 0xb00 +#define OMAP3430ES2_USBHOST_MOD 0xc00 + +/* + * TI81XX PRM module offsets + */ +#define TI814X_PRM_DSP_MOD 0x0a00 +#define TI814X_PRM_HDVICP_MOD 0x0c00 +#define TI814X_PRM_ISP_MOD 0x0d00 +#define TI814X_PRM_HDVPSS_MOD 0x0e00 +#define TI814X_PRM_GFX_MOD 0x0f00 + +#define TI81XX_PRM_DEVICE_MOD 0x0000 +#define TI816X_PRM_ACTIVE_MOD 0x0a00 +#define TI81XX_PRM_DEFAULT_MOD 0x0b00 +#define TI816X_PRM_IVAHD0_MOD 0x0c00 +#define TI816X_PRM_IVAHD1_MOD 0x0d00 +#define TI816X_PRM_IVAHD2_MOD 0x0e00 +#define TI816X_PRM_SGX_MOD 0x0f00 +#define TI81XX_PRM_ALWON_MOD 0x1800 + +/* 24XX register bits shared between CM & PRM registers */ + +/* CM_FCLKEN1_CORE, CM_ICLKEN1_CORE, PM_WKEN1_CORE shared bits */ +#define OMAP2420_EN_MMC_SHIFT 26 +#define OMAP2420_EN_MMC_MASK (1 << 26) +#define OMAP24XX_EN_UART2_SHIFT 22 +#define OMAP24XX_EN_UART2_MASK (1 << 22) +#define OMAP24XX_EN_UART1_SHIFT 21 +#define OMAP24XX_EN_UART1_MASK (1 << 21) +#define OMAP24XX_EN_MCSPI2_SHIFT 18 +#define OMAP24XX_EN_MCSPI2_MASK (1 << 18) +#define OMAP24XX_EN_MCSPI1_SHIFT 17 +#define OMAP24XX_EN_MCSPI1_MASK (1 << 17) +#define OMAP24XX_EN_MCBSP2_SHIFT 16 +#define OMAP24XX_EN_MCBSP2_MASK (1 << 16) +#define OMAP24XX_EN_MCBSP1_SHIFT 15 +#define OMAP24XX_EN_MCBSP1_MASK (1 << 15) +#define OMAP24XX_EN_GPT12_SHIFT 14 +#define OMAP24XX_EN_GPT12_MASK (1 << 14) +#define OMAP24XX_EN_GPT11_SHIFT 13 +#define OMAP24XX_EN_GPT11_MASK (1 << 13) +#define OMAP24XX_EN_GPT10_SHIFT 12 +#define OMAP24XX_EN_GPT10_MASK (1 << 12) +#define OMAP24XX_EN_GPT9_SHIFT 11 +#define OMAP24XX_EN_GPT9_MASK (1 << 11) +#define OMAP24XX_EN_GPT8_SHIFT 10 +#define OMAP24XX_EN_GPT8_MASK (1 << 10) +#define OMAP24XX_EN_GPT7_SHIFT 9 +#define OMAP24XX_EN_GPT7_MASK (1 << 9) +#define OMAP24XX_EN_GPT6_SHIFT 8 +#define OMAP24XX_EN_GPT6_MASK (1 << 8) +#define OMAP24XX_EN_GPT5_SHIFT 7 +#define OMAP24XX_EN_GPT5_MASK (1 << 7) +#define OMAP24XX_EN_GPT4_SHIFT 6 +#define OMAP24XX_EN_GPT4_MASK (1 << 6) +#define OMAP24XX_EN_GPT3_SHIFT 5 +#define OMAP24XX_EN_GPT3_MASK (1 << 5) +#define OMAP24XX_EN_GPT2_SHIFT 4 +#define OMAP24XX_EN_GPT2_MASK (1 << 4) +#define OMAP2420_EN_VLYNQ_SHIFT 3 +#define OMAP2420_EN_VLYNQ_MASK (1 << 3) + +/* CM_FCLKEN2_CORE, CM_ICLKEN2_CORE, PM_WKEN2_CORE shared bits */ +#define OMAP2430_EN_GPIO5_SHIFT 10 +#define OMAP2430_EN_GPIO5_MASK (1 << 10) +#define OMAP2430_EN_MCSPI3_SHIFT 9 +#define OMAP2430_EN_MCSPI3_MASK (1 << 9) +#define OMAP2430_EN_MMCHS2_SHIFT 8 +#define OMAP2430_EN_MMCHS2_MASK (1 << 8) +#define OMAP2430_EN_MMCHS1_SHIFT 7 +#define OMAP2430_EN_MMCHS1_MASK (1 << 7) +#define OMAP24XX_EN_UART3_SHIFT 2 +#define OMAP24XX_EN_UART3_MASK (1 << 2) +#define OMAP24XX_EN_USB_SHIFT 0 +#define OMAP24XX_EN_USB_MASK (1 << 0) + +/* CM_ICLKEN2_CORE, PM_WKEN2_CORE shared bits */ +#define OMAP2430_EN_MDM_INTC_SHIFT 11 +#define OMAP2430_EN_MDM_INTC_MASK (1 << 11) +#define OMAP2430_EN_USBHS_SHIFT 6 +#define OMAP2430_EN_USBHS_MASK (1 << 6) +#define OMAP24XX_EN_GPMC_SHIFT 1 +#define OMAP24XX_EN_GPMC_MASK (1 << 1) + +/* CM_IDLEST1_CORE, PM_WKST1_CORE shared bits */ +#define OMAP2420_ST_MMC_SHIFT 26 +#define OMAP2420_ST_MMC_MASK (1 << 26) +#define OMAP24XX_ST_UART2_SHIFT 22 +#define OMAP24XX_ST_UART2_MASK (1 << 22) +#define OMAP24XX_ST_UART1_SHIFT 21 +#define OMAP24XX_ST_UART1_MASK (1 << 21) +#define OMAP24XX_ST_MCSPI2_SHIFT 18 +#define OMAP24XX_ST_MCSPI2_MASK (1 << 18) +#define OMAP24XX_ST_MCSPI1_SHIFT 17 +#define OMAP24XX_ST_MCSPI1_MASK (1 << 17) +#define OMAP24XX_ST_MCBSP2_SHIFT 16 +#define OMAP24XX_ST_MCBSP2_MASK (1 << 16) +#define OMAP24XX_ST_MCBSP1_SHIFT 15 +#define OMAP24XX_ST_MCBSP1_MASK (1 << 15) +#define OMAP24XX_ST_GPT12_SHIFT 14 +#define OMAP24XX_ST_GPT12_MASK (1 << 14) +#define OMAP24XX_ST_GPT11_SHIFT 13 +#define OMAP24XX_ST_GPT11_MASK (1 << 13) +#define OMAP24XX_ST_GPT10_SHIFT 12 +#define OMAP24XX_ST_GPT10_MASK (1 << 12) +#define OMAP24XX_ST_GPT9_SHIFT 11 +#define OMAP24XX_ST_GPT9_MASK (1 << 11) +#define OMAP24XX_ST_GPT8_SHIFT 10 +#define OMAP24XX_ST_GPT8_MASK (1 << 10) +#define OMAP24XX_ST_GPT7_SHIFT 9 +#define OMAP24XX_ST_GPT7_MASK (1 << 9) +#define OMAP24XX_ST_GPT6_SHIFT 8 +#define OMAP24XX_ST_GPT6_MASK (1 << 8) +#define OMAP24XX_ST_GPT5_SHIFT 7 +#define OMAP24XX_ST_GPT5_MASK (1 << 7) +#define OMAP24XX_ST_GPT4_SHIFT 6 +#define OMAP24XX_ST_GPT4_MASK (1 << 6) +#define OMAP24XX_ST_GPT3_SHIFT 5 +#define OMAP24XX_ST_GPT3_MASK (1 << 5) +#define OMAP24XX_ST_GPT2_SHIFT 4 +#define OMAP24XX_ST_GPT2_MASK (1 << 4) +#define OMAP2420_ST_VLYNQ_SHIFT 3 +#define OMAP2420_ST_VLYNQ_MASK (1 << 3) + +/* CM_IDLEST2_CORE, PM_WKST2_CORE shared bits */ +#define OMAP2430_ST_MDM_INTC_SHIFT 11 +#define OMAP2430_ST_MDM_INTC_MASK (1 << 11) +#define OMAP2430_ST_GPIO5_SHIFT 10 +#define OMAP2430_ST_GPIO5_MASK (1 << 10) +#define OMAP2430_ST_MCSPI3_SHIFT 9 +#define OMAP2430_ST_MCSPI3_MASK (1 << 9) +#define OMAP2430_ST_MMCHS2_SHIFT 8 +#define OMAP2430_ST_MMCHS2_MASK (1 << 8) +#define OMAP2430_ST_MMCHS1_SHIFT 7 +#define OMAP2430_ST_MMCHS1_MASK (1 << 7) +#define OMAP2430_ST_USBHS_SHIFT 6 +#define OMAP2430_ST_USBHS_MASK (1 << 6) +#define OMAP24XX_ST_UART3_SHIFT 2 +#define OMAP24XX_ST_UART3_MASK (1 << 2) +#define OMAP24XX_ST_USB_SHIFT 0 +#define OMAP24XX_ST_USB_MASK (1 << 0) + +/* CM_FCLKEN_WKUP, CM_ICLKEN_WKUP, PM_WKEN_WKUP shared bits */ +#define OMAP24XX_EN_GPIOS_SHIFT 2 +#define OMAP24XX_EN_GPIOS_MASK (1 << 2) +#define OMAP24XX_EN_GPT1_SHIFT 0 +#define OMAP24XX_EN_GPT1_MASK (1 << 0) + +/* PM_WKST_WKUP, CM_IDLEST_WKUP shared bits */ +#define OMAP24XX_ST_GPIOS_SHIFT 2 +#define OMAP24XX_ST_GPIOS_MASK (1 << 2) +#define OMAP24XX_ST_32KSYNC_SHIFT 1 +#define OMAP24XX_ST_32KSYNC_MASK (1 << 1) +#define OMAP24XX_ST_GPT1_SHIFT 0 +#define OMAP24XX_ST_GPT1_MASK (1 << 0) + +/* CM_IDLEST_MDM and PM_WKST_MDM shared bits */ +#define OMAP2430_ST_MDM_SHIFT 0 +#define OMAP2430_ST_MDM_MASK (1 << 0) + + +/* 3430 register bits shared between CM & PRM registers */ + +/* CM_REVISION, PRM_REVISION shared bits */ +#define OMAP3430_REV_SHIFT 0 +#define OMAP3430_REV_MASK (0xff << 0) + +/* CM_SYSCONFIG, PRM_SYSCONFIG shared bits */ +#define OMAP3430_AUTOIDLE_MASK (1 << 0) + +/* CM_FCLKEN1_CORE, CM_ICLKEN1_CORE, PM_WKEN1_CORE shared bits */ +#define OMAP3430_EN_MMC3_MASK (1 << 30) +#define OMAP3430_EN_MMC3_SHIFT 30 +#define OMAP3430_EN_MMC2_MASK (1 << 25) +#define OMAP3430_EN_MMC2_SHIFT 25 +#define OMAP3430_EN_MMC1_MASK (1 << 24) +#define OMAP3430_EN_MMC1_SHIFT 24 +#define AM35XX_EN_UART4_MASK (1 << 23) +#define AM35XX_EN_UART4_SHIFT 23 +#define OMAP3430_EN_MCSPI4_MASK (1 << 21) +#define OMAP3430_EN_MCSPI4_SHIFT 21 +#define OMAP3430_EN_MCSPI3_MASK (1 << 20) +#define OMAP3430_EN_MCSPI3_SHIFT 20 +#define OMAP3430_EN_MCSPI2_MASK (1 << 19) +#define OMAP3430_EN_MCSPI2_SHIFT 19 +#define OMAP3430_EN_MCSPI1_MASK (1 << 18) +#define OMAP3430_EN_MCSPI1_SHIFT 18 +#define OMAP3430_EN_I2C3_MASK (1 << 17) +#define OMAP3430_EN_I2C3_SHIFT 17 +#define OMAP3430_EN_I2C2_MASK (1 << 16) +#define OMAP3430_EN_I2C2_SHIFT 16 +#define OMAP3430_EN_I2C1_MASK (1 << 15) +#define OMAP3430_EN_I2C1_SHIFT 15 +#define OMAP3430_EN_UART2_MASK (1 << 14) +#define OMAP3430_EN_UART2_SHIFT 14 +#define OMAP3430_EN_UART1_MASK (1 << 13) +#define OMAP3430_EN_UART1_SHIFT 13 +#define OMAP3430_EN_GPT11_MASK (1 << 12) +#define OMAP3430_EN_GPT11_SHIFT 12 +#define OMAP3430_EN_GPT10_MASK (1 << 11) +#define OMAP3430_EN_GPT10_SHIFT 11 +#define OMAP3430_EN_MCBSP5_MASK (1 << 10) +#define OMAP3430_EN_MCBSP5_SHIFT 10 +#define OMAP3430_EN_MCBSP1_MASK (1 << 9) +#define OMAP3430_EN_MCBSP1_SHIFT 9 +#define OMAP3430_EN_FSHOSTUSB_MASK (1 << 5) +#define OMAP3430_EN_FSHOSTUSB_SHIFT 5 +#define OMAP3430_EN_D2D_MASK (1 << 3) +#define OMAP3430_EN_D2D_SHIFT 3 + +/* CM_ICLKEN1_CORE, PM_WKEN1_CORE shared bits */ +#define OMAP3430_EN_HSOTGUSB_MASK (1 << 4) +#define OMAP3430_EN_HSOTGUSB_SHIFT 4 + +/* PM_WKST1_CORE, CM_IDLEST1_CORE shared bits */ +#define OMAP3430_ST_MMC3_SHIFT 30 +#define OMAP3430_ST_MMC3_MASK (1 << 30) +#define OMAP3430_ST_MMC2_SHIFT 25 +#define OMAP3430_ST_MMC2_MASK (1 << 25) +#define OMAP3430_ST_MMC1_SHIFT 24 +#define OMAP3430_ST_MMC1_MASK (1 << 24) +#define OMAP3430_ST_MCSPI4_SHIFT 21 +#define OMAP3430_ST_MCSPI4_MASK (1 << 21) +#define OMAP3430_ST_MCSPI3_SHIFT 20 +#define OMAP3430_ST_MCSPI3_MASK (1 << 20) +#define OMAP3430_ST_MCSPI2_SHIFT 19 +#define OMAP3430_ST_MCSPI2_MASK (1 << 19) +#define OMAP3430_ST_MCSPI1_SHIFT 18 +#define OMAP3430_ST_MCSPI1_MASK (1 << 18) +#define OMAP3430_ST_I2C3_SHIFT 17 +#define OMAP3430_ST_I2C3_MASK (1 << 17) +#define OMAP3430_ST_I2C2_SHIFT 16 +#define OMAP3430_ST_I2C2_MASK (1 << 16) +#define OMAP3430_ST_I2C1_SHIFT 15 +#define OMAP3430_ST_I2C1_MASK (1 << 15) +#define OMAP3430_ST_UART2_SHIFT 14 +#define OMAP3430_ST_UART2_MASK (1 << 14) +#define OMAP3430_ST_UART1_SHIFT 13 +#define OMAP3430_ST_UART1_MASK (1 << 13) +#define OMAP3430_ST_GPT11_SHIFT 12 +#define OMAP3430_ST_GPT11_MASK (1 << 12) +#define OMAP3430_ST_GPT10_SHIFT 11 +#define OMAP3430_ST_GPT10_MASK (1 << 11) +#define OMAP3430_ST_MCBSP5_SHIFT 10 +#define OMAP3430_ST_MCBSP5_MASK (1 << 10) +#define OMAP3430_ST_MCBSP1_SHIFT 9 +#define OMAP3430_ST_MCBSP1_MASK (1 << 9) +#define OMAP3430ES1_ST_FSHOSTUSB_SHIFT 5 +#define OMAP3430ES1_ST_FSHOSTUSB_MASK (1 << 5) +#define OMAP3430ES1_ST_HSOTGUSB_SHIFT 4 +#define OMAP3430ES1_ST_HSOTGUSB_MASK (1 << 4) +#define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 +#define OMAP3430ES2_ST_HSOTGUSB_IDLE_MASK (1 << 5) +#define OMAP3430ES2_ST_HSOTGUSB_STDBY_SHIFT 4 +#define OMAP3430ES2_ST_HSOTGUSB_STDBY_MASK (1 << 4) +#define OMAP3430_ST_D2D_SHIFT 3 +#define OMAP3430_ST_D2D_MASK (1 << 3) + +/* CM_FCLKEN_WKUP, CM_ICLKEN_WKUP, PM_WKEN_WKUP shared bits */ +#define OMAP3430_EN_GPIO1_MASK (1 << 3) +#define OMAP3430_EN_GPIO1_SHIFT 3 +#define OMAP3430_EN_GPT12_MASK (1 << 1) +#define OMAP3430_EN_GPT12_SHIFT 1 +#define OMAP3430_EN_GPT1_MASK (1 << 0) +#define OMAP3430_EN_GPT1_SHIFT 0 + +/* CM_FCLKEN_WKUP, PM_WKEN_WKUP shared bits */ +#define OMAP3430_EN_SR2_MASK (1 << 7) +#define OMAP3430_EN_SR2_SHIFT 7 +#define OMAP3430_EN_SR1_MASK (1 << 6) +#define OMAP3430_EN_SR1_SHIFT 6 + +/* CM_ICLKEN_WKUP, PM_WKEN_WKUP shared bits */ +#define OMAP3430_EN_GPT12_MASK (1 << 1) +#define OMAP3430_EN_GPT12_SHIFT 1 + +/* CM_IDLEST_WKUP, PM_WKST_WKUP shared bits */ +#define OMAP3430_ST_SR2_SHIFT 7 +#define OMAP3430_ST_SR2_MASK (1 << 7) +#define OMAP3430_ST_SR1_SHIFT 6 +#define OMAP3430_ST_SR1_MASK (1 << 6) +#define OMAP3430_ST_GPIO1_SHIFT 3 +#define OMAP3430_ST_GPIO1_MASK (1 << 3) +#define OMAP3430_ST_32KSYNC_SHIFT 2 +#define OMAP3430_ST_32KSYNC_MASK (1 << 2) +#define OMAP3430_ST_GPT12_SHIFT 1 +#define OMAP3430_ST_GPT12_MASK (1 << 1) +#define OMAP3430_ST_GPT1_SHIFT 0 +#define OMAP3430_ST_GPT1_MASK (1 << 0) + +/* + * CM_SLEEPDEP_GFX, CM_SLEEPDEP_DSS, CM_SLEEPDEP_CAM, + * CM_SLEEPDEP_PER, PM_WKDEP_IVA2, PM_WKDEP_GFX, + * PM_WKDEP_DSS, PM_WKDEP_CAM, PM_WKDEP_PER, PM_WKDEP_NEON shared bits + */ +#define OMAP3430_EN_MPU_MASK (1 << 1) +#define OMAP3430_EN_MPU_SHIFT 1 + +/* CM_FCLKEN_PER, CM_ICLKEN_PER, PM_WKEN_PER shared bits */ + +#define OMAP3630_EN_UART4_MASK (1 << 18) +#define OMAP3630_EN_UART4_SHIFT 18 +#define OMAP3430_EN_GPIO6_MASK (1 << 17) +#define OMAP3430_EN_GPIO6_SHIFT 17 +#define OMAP3430_EN_GPIO5_MASK (1 << 16) +#define OMAP3430_EN_GPIO5_SHIFT 16 +#define OMAP3430_EN_GPIO4_MASK (1 << 15) +#define OMAP3430_EN_GPIO4_SHIFT 15 +#define OMAP3430_EN_GPIO3_MASK (1 << 14) +#define OMAP3430_EN_GPIO3_SHIFT 14 +#define OMAP3430_EN_GPIO2_MASK (1 << 13) +#define OMAP3430_EN_GPIO2_SHIFT 13 +#define OMAP3430_EN_UART3_MASK (1 << 11) +#define OMAP3430_EN_UART3_SHIFT 11 +#define OMAP3430_EN_GPT9_MASK (1 << 10) +#define OMAP3430_EN_GPT9_SHIFT 10 +#define OMAP3430_EN_GPT8_MASK (1 << 9) +#define OMAP3430_EN_GPT8_SHIFT 9 +#define OMAP3430_EN_GPT7_MASK (1 << 8) +#define OMAP3430_EN_GPT7_SHIFT 8 +#define OMAP3430_EN_GPT6_MASK (1 << 7) +#define OMAP3430_EN_GPT6_SHIFT 7 +#define OMAP3430_EN_GPT5_MASK (1 << 6) +#define OMAP3430_EN_GPT5_SHIFT 6 +#define OMAP3430_EN_GPT4_MASK (1 << 5) +#define OMAP3430_EN_GPT4_SHIFT 5 +#define OMAP3430_EN_GPT3_MASK (1 << 4) +#define OMAP3430_EN_GPT3_SHIFT 4 +#define OMAP3430_EN_GPT2_MASK (1 << 3) +#define OMAP3430_EN_GPT2_SHIFT 3 + +/* CM_FCLKEN_PER, CM_ICLKEN_PER, PM_WKEN_PER, PM_WKST_PER shared bits */ +/* XXX Possible TI documentation bug: should the PM_WKST_PER EN_* bits + * be ST_* bits instead? */ +#define OMAP3430_EN_MCBSP4_MASK (1 << 2) +#define OMAP3430_EN_MCBSP4_SHIFT 2 +#define OMAP3430_EN_MCBSP3_MASK (1 << 1) +#define OMAP3430_EN_MCBSP3_SHIFT 1 +#define OMAP3430_EN_MCBSP2_MASK (1 << 0) +#define OMAP3430_EN_MCBSP2_SHIFT 0 + +/* CM_IDLEST_PER, PM_WKST_PER shared bits */ +#define OMAP3630_ST_UART4_SHIFT 18 +#define OMAP3630_ST_UART4_MASK (1 << 18) +#define OMAP3430_ST_GPIO6_SHIFT 17 +#define OMAP3430_ST_GPIO6_MASK (1 << 17) +#define OMAP3430_ST_GPIO5_SHIFT 16 +#define OMAP3430_ST_GPIO5_MASK (1 << 16) +#define OMAP3430_ST_GPIO4_SHIFT 15 +#define OMAP3430_ST_GPIO4_MASK (1 << 15) +#define OMAP3430_ST_GPIO3_SHIFT 14 +#define OMAP3430_ST_GPIO3_MASK (1 << 14) +#define OMAP3430_ST_GPIO2_SHIFT 13 +#define OMAP3430_ST_GPIO2_MASK (1 << 13) +#define OMAP3430_ST_UART3_SHIFT 11 +#define OMAP3430_ST_UART3_MASK (1 << 11) +#define OMAP3430_ST_GPT9_SHIFT 10 +#define OMAP3430_ST_GPT9_MASK (1 << 10) +#define OMAP3430_ST_GPT8_SHIFT 9 +#define OMAP3430_ST_GPT8_MASK (1 << 9) +#define OMAP3430_ST_GPT7_SHIFT 8 +#define OMAP3430_ST_GPT7_MASK (1 << 8) +#define OMAP3430_ST_GPT6_SHIFT 7 +#define OMAP3430_ST_GPT6_MASK (1 << 7) +#define OMAP3430_ST_GPT5_SHIFT 6 +#define OMAP3430_ST_GPT5_MASK (1 << 6) +#define OMAP3430_ST_GPT4_SHIFT 5 +#define OMAP3430_ST_GPT4_MASK (1 << 5) +#define OMAP3430_ST_GPT3_SHIFT 4 +#define OMAP3430_ST_GPT3_MASK (1 << 4) +#define OMAP3430_ST_GPT2_SHIFT 3 +#define OMAP3430_ST_GPT2_MASK (1 << 3) + +/* CM_SLEEPDEP_PER, PM_WKDEP_IVA2, PM_WKDEP_MPU, PM_WKDEP_PER shared bits */ +#define OMAP3430_EN_CORE_SHIFT 0 +#define OMAP3430_EN_CORE_MASK (1 << 0) + + + +/* + * Maximum time(us) it takes to output the signal WUCLKOUT of the last + * pad of the I/O ring after asserting WUCLKIN high. Tero measured + * the actual time at 7 to 8 microseconds on OMAP3 and 2 to 4 + * microseconds on OMAP4, so this timeout may be too high. + */ +#define MAX_IOPAD_LATCH_TIME 100 +# ifndef __ASSEMBLER__ + +#include <linux/delay.h> + +/** + * omap_test_timeout - busy-loop, testing a condition + * @cond: condition to test until it evaluates to true + * @timeout: maximum number of microseconds in the timeout + * @index: loop index (integer) + * + * Loop waiting for @cond to become true or until at least @timeout + * microseconds have passed. To use, define some integer @index in the + * calling code. After running, if @index == @timeout, then the loop has + * timed out. + */ +#define omap_test_timeout(cond, timeout, index) \ +({ \ + for (index = 0; index < timeout; index++) { \ + if (cond) \ + break; \ + udelay(1); \ + } \ +}) + +/** + * struct omap_prcm_irq - describes a PRCM interrupt bit + * @name: a short name describing the interrupt type, e.g. "wkup" or "io" + * @offset: the bit shift of the interrupt inside the IRQ{ENABLE,STATUS} regs + * @priority: should this interrupt be handled before @priority=false IRQs? + * + * Describes interrupt bits inside the PRM_IRQ{ENABLE,STATUS}_MPU* registers. + * On systems with multiple PRM MPU IRQ registers, the bitfields read from + * the registers are concatenated, so @offset could be > 31 on these systems - + * see omap_prm_irq_handler() for more details. I/O ring interrupts should + * have @priority set to true. + */ +struct omap_prcm_irq { + const char *name; + unsigned int offset; + bool priority; +}; + +/** + * struct omap_prcm_irq_setup - PRCM interrupt controller details + * @ack: PRM register offset for the first PRM_IRQSTATUS_MPU register + * @mask: PRM register offset for the first PRM_IRQENABLE_MPU register + * @pm_ctrl: PRM register offset for the PRM_IO_PMCTRL register + * @nr_regs: number of PRM_IRQ{STATUS,ENABLE}_MPU* registers + * @nr_irqs: number of entries in the @irqs array + * @irqs: ptr to an array of PRCM interrupt bits (see @nr_irqs) + * @irq: MPU IRQ asserted when a PRCM interrupt arrives + * @read_pending_irqs: fn ptr to determine if any PRCM IRQs are pending + * @ocp_barrier: fn ptr to force buffered PRM writes to complete + * @save_and_clear_irqen: fn ptr to save and clear IRQENABLE regs + * @restore_irqen: fn ptr to save and clear IRQENABLE regs + * @reconfigure_io_chain: fn ptr to reconfigure IO chain + * @saved_mask: IRQENABLE regs are saved here during suspend + * @priority_mask: 1 bit per IRQ, set to 1 if omap_prcm_irq.priority = true + * @base_irq: base dynamic IRQ number, returned from irq_alloc_descs() in init + * @suspended: set to true after Linux suspend code has called our ->prepare() + * @suspend_save_flag: set to true after IRQ masks have been saved and disabled + * + * @saved_mask, @priority_mask, @base_irq, @suspended, and + * @suspend_save_flag are populated dynamically, and are not to be + * specified in static initializers. + */ +struct omap_prcm_irq_setup { + u16 ack; + u16 mask; + u16 pm_ctrl; + u8 nr_regs; + u8 nr_irqs; + const struct omap_prcm_irq *irqs; + int irq; + void (*read_pending_irqs)(unsigned long *events); + void (*ocp_barrier)(void); + void (*save_and_clear_irqen)(u32 *saved_mask); + void (*restore_irqen)(u32 *saved_mask); + void (*reconfigure_io_chain)(void); + u32 *saved_mask; + u32 *priority_mask; + int base_irq; + bool suspended; + bool suspend_save_flag; +}; + +/* OMAP_PRCM_IRQ: convenience macro for creating struct omap_prcm_irq records */ +#define OMAP_PRCM_IRQ(_name, _offset, _priority) { \ + .name = _name, \ + .offset = _offset, \ + .priority = _priority \ + } + +struct omap_domain_base { + u32 pa; + void __iomem *va; + s16 offset; +}; + +/** + * struct omap_prcm_init_data - PRCM driver init data + * @index: clock memory mapping index to be used + * @mem: IO mem pointer for this module + * @phys: IO mem physical base address for this module + * @offset: module base address offset from the IO base + * @flags: PRCM module init flags + * @device_inst_offset: device instance offset within the module address space + * @init: low level PRCM init function for this module + * @np: device node for this PRCM module + */ +struct omap_prcm_init_data { + int index; + void __iomem *mem; + u32 phys; + s16 offset; + u16 flags; + s32 device_inst_offset; + int (*init)(const struct omap_prcm_init_data *data); + struct device_node *np; +}; + +extern void omap_prcm_irq_cleanup(void); +extern int omap_prcm_register_chain_handler( + struct omap_prcm_irq_setup *irq_setup); +extern int omap_prcm_event_to_irq(const char *event); +extern void omap_prcm_irq_prepare(void); +extern void omap_prcm_irq_complete(void); + +# endif + +#endif + diff --git a/arch/arm/mach-omap2/prcm43xx.h b/arch/arm/mach-omap2/prcm43xx.h new file mode 100644 index 000000000..38ed69b15 --- /dev/null +++ b/arch/arm/mach-omap2/prcm43xx.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AM43x PRCM defines + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM_43XX_H +#define __ARCH_ARM_MACH_OMAP2_PRCM_43XX_H + +#define AM43XX_PRM_PARTITION 1 +#define AM43XX_CM_PARTITION 1 + +/* PRM instances */ +#define AM43XX_PRM_OCP_SOCKET_INST 0x0000 +#define AM43XX_PRM_MPU_INST 0x0300 +#define AM43XX_PRM_GFX_INST 0x0400 +#define AM43XX_PRM_RTC_INST 0x0500 +#define AM43XX_PRM_TAMPER_INST 0x0600 +#define AM43XX_PRM_CEFUSE_INST 0x0700 +#define AM43XX_PRM_PER_INST 0x0800 +#define AM43XX_PRM_WKUP_INST 0x2000 +#define AM43XX_PRM_DEVICE_INST 0x4000 + +/* PRM_IRQ offsets */ +#define AM43XX_PRM_IRQSTATUS_MPU_OFFSET 0x0004 +#define AM43XX_PRM_IRQENABLE_MPU_OFFSET 0x0008 + +/* Other PRM offsets */ +#define AM43XX_PRM_IO_PMCTRL_OFFSET 0x0024 + +/* CM instances */ +#define AM43XX_CM_WKUP_INST 0x2800 +#define AM43XX_CM_MPU_INST 0x8300 +#define AM43XX_CM_GFX_INST 0x8400 +#define AM43XX_CM_RTC_INST 0x8500 +#define AM43XX_CM_TAMPER_INST 0x8600 +#define AM43XX_CM_CEFUSE_INST 0x8700 +#define AM43XX_CM_PER_INST 0x8800 + +/* CD offsets */ +#define AM43XX_CM_WKUP_L3_AON_CDOFFS 0x0000 +#define AM43XX_CM_WKUP_L3S_TSC_CDOFFS 0x0100 +#define AM43XX_CM_WKUP_L4_WKUP_AON_CDOFFS 0x0200 +#define AM43XX_CM_WKUP_WKUP_CDOFFS 0x0300 +#define AM43XX_CM_MPU_MPU_CDOFFS 0x0000 +#define AM43XX_CM_GFX_GFX_L3_CDOFFS 0x0000 +#define AM43XX_CM_RTC_RTC_CDOFFS 0x0000 +#define AM43XX_CM_TAMPER_TAMPER_CDOFFS 0x0000 +#define AM43XX_CM_CEFUSE_CEFUSE_CDOFFS 0x0000 +#define AM43XX_CM_PER_L3_CDOFFS 0x0000 +#define AM43XX_CM_PER_L3S_CDOFFS 0x0200 +#define AM43XX_CM_PER_ICSS_CDOFFS 0x0300 +#define AM43XX_CM_PER_L4LS_CDOFFS 0x0400 +#define AM43XX_CM_PER_EMIF_CDOFFS 0x0700 +#define AM43XX_CM_PER_LCDC_CDOFFS 0x0800 +#define AM43XX_CM_PER_DSS_CDOFFS 0x0a00 +#define AM43XX_CM_PER_CPSW_CDOFFS 0x0b00 +#define AM43XX_CM_PER_OCPWP_L3_CDOFFS 0x0c00 + +/* CLK CTRL offsets */ +#define AM43XX_CM_MPU_MPU_CLKCTRL_OFFSET 0x0020 +#define AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET 0x0720 + +#endif diff --git a/arch/arm/mach-omap2/prcm44xx.h b/arch/arm/mach-omap2/prcm44xx.h new file mode 100644 index 000000000..b1d4c89a4 --- /dev/null +++ b/arch/arm/mach-omap2/prcm44xx.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP4 PRCM definitions + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * + * Paul Walmsley + * + * This file contains macros and functions that are common to all of + * the PRM/CM/PRCM blocks on the OMAP4 devices: PRM, CM1, CM2, + * PRCM_MPU, SCRM + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM44XX_H +#define __ARCH_ARM_MACH_OMAP2_PRCM44XX_H + +/* + * OMAP4 PRCM partition IDs + * + * The numbers and order are arbitrary, but 0 is reserved for the + * 'invalid' partition in case someone forgets to add a + * .prcm_partition field. + */ +#define OMAP4430_INVALID_PRCM_PARTITION 0 +#define OMAP4430_PRM_PARTITION 1 +#define OMAP4430_CM1_PARTITION 2 +#define OMAP4430_CM2_PARTITION 3 +#define OMAP4430_SCRM_PARTITION 4 +#define OMAP4430_PRCM_MPU_PARTITION 5 + +#define OMAP54XX_PRM_PARTITION 1 +#define OMAP54XX_CM_CORE_AON_PARTITION 2 +#define OMAP54XX_CM_CORE_PARTITION 3 +#define OMAP54XX_SCRM_PARTITION 4 +#define OMAP54XX_PRCM_MPU_PARTITION 5 + +#define DRA7XX_PRM_PARTITION 1 +#define DRA7XX_CM_CORE_AON_PARTITION 2 +#define DRA7XX_CM_CORE_PARTITION 3 +#define DRA7XX_MPU_PRCM_PARTITION 5 + +/* + * OMAP4_MAX_PRCM_PARTITIONS: set to the highest value of the PRCM partition + * IDs, plus one + */ +#define OMAP4_MAX_PRCM_PARTITIONS 6 + + +#endif diff --git a/arch/arm/mach-omap2/prcm_mpu44xx.c b/arch/arm/mach-omap2/prcm_mpu44xx.c new file mode 100644 index 000000000..5add541e3 --- /dev/null +++ b/arch/arm/mach-omap2/prcm_mpu44xx.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 PRCM_MPU module functions + * + * Copyright (C) 2009 Nokia Corporation + * Paul Walmsley + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "iomap.h" +#include "common.h" +#include "prcm_mpu44xx.h" +#include "cm-regbits-44xx.h" + +/* + * prcm_mpu_base: the virtual address of the start of the PRCM_MPU IP + * block registers + */ +struct omap_domain_base prcm_mpu_base; + +/* PRCM_MPU low-level functions */ + +u32 omap4_prcm_mpu_read_inst_reg(s16 inst, u16 reg) +{ + return readl_relaxed(OMAP44XX_PRCM_MPU_REGADDR(inst, reg)); +} + +void omap4_prcm_mpu_write_inst_reg(u32 val, s16 inst, u16 reg) +{ + writel_relaxed(val, OMAP44XX_PRCM_MPU_REGADDR(inst, reg)); +} + +u32 omap4_prcm_mpu_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 reg) +{ + u32 v; + + v = omap4_prcm_mpu_read_inst_reg(inst, reg); + v &= ~mask; + v |= bits; + omap4_prcm_mpu_write_inst_reg(v, inst, reg); + + return v; +} + +/** + * omap2_set_globals_prcm_mpu - set the MPU PRCM base address (for early use) + * @prcm_mpu: PRCM_MPU base virtual address + * + * XXX Will be replaced when the PRM/CM drivers are completed. + */ +void __init omap2_set_globals_prcm_mpu(void __iomem *prcm_mpu) +{ + prcm_mpu_base.va = prcm_mpu; +} diff --git a/arch/arm/mach-omap2/prcm_mpu44xx.h b/arch/arm/mach-omap2/prcm_mpu44xx.h new file mode 100644 index 000000000..ff3484b04 --- /dev/null +++ b/arch/arm/mach-omap2/prcm_mpu44xx.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx PRCM MPU instance offset macros + * + * Copyright (C) 2010, 2012 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + * + * XXX This file needs to be updated to align on one of "OMAP4", "OMAP44XX", + * or "OMAP4430". + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM_MPU44XX_H +#define __ARCH_ARM_MACH_OMAP2_PRCM_MPU44XX_H + +#include "prcm_mpu_44xx_54xx.h" + +#define OMAP4430_PRCM_MPU_BASE 0x48243000 + +#define OMAP44XX_PRCM_MPU_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP4430_PRCM_MPU_BASE + (inst) + (reg)) + +/* PRCM_MPU instances */ +#define OMAP4430_PRCM_MPU_OCP_SOCKET_PRCM_INST 0x0000 +#define OMAP4430_PRCM_MPU_DEVICE_PRM_INST 0x0200 +#define OMAP4430_PRCM_MPU_CPU0_INST 0x0400 +#define OMAP4430_PRCM_MPU_CPU1_INST 0x0800 + +/* PRCM_MPU clockdomain register offsets (from instance start) */ +#define OMAP4430_PRCM_MPU_CPU0_CPU0_CDOFFS 0x0018 +#define OMAP4430_PRCM_MPU_CPU1_CPU1_CDOFFS 0x0018 + + +/* + * PRCM_MPU + * + * The PRCM_MPU is a local PRCM inside the MPU subsystem. For the PRCM (global) + * point of view the PRCM_MPU is a single entity. It shares the same + * programming model as the global PRCM and thus can be assimilate as two new + * MOD inside the PRCM + */ + +/* PRCM_MPU.OCP_SOCKET_PRCM register offsets */ +#define OMAP4_REVISION_PRCM_OFFSET 0x0000 +#define OMAP4430_REVISION_PRCM OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_OCP_SOCKET_PRCM_INST, 0x0000) + +/* PRCM_MPU.DEVICE_PRM register offsets */ +#define OMAP4_PRCM_MPU_PRM_RSTST_OFFSET 0x0000 +#define OMAP4430_PRCM_MPU_PRM_RSTST OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_DEVICE_PRM_INST, 0x0000) +#define OMAP4_PRCM_MPU_PRM_PSCON_COUNT_OFFSET 0x0004 +#define OMAP4430_PRCM_MPU_PRM_PSCON_COUNT OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_DEVICE_PRM_INST, 0x0004) + +/* PRCM_MPU.CPU0 register offsets */ +#define OMAP4_PM_CPU0_PWRSTCTRL_OFFSET 0x0000 +#define OMAP4430_PM_CPU0_PWRSTCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU0_INST, 0x0000) +#define OMAP4_PM_CPU0_PWRSTST_OFFSET 0x0004 +#define OMAP4430_PM_CPU0_PWRSTST OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU0_INST, 0x0004) +#define OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET 0x0008 +#define OMAP4430_RM_CPU0_CPU0_CONTEXT OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU0_INST, 0x0008) +#define OMAP4_RM_CPU0_CPU0_RSTCTRL_OFFSET 0x000c +#define OMAP4430_RM_CPU0_CPU0_RSTCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU0_INST, 0x000c) +#define OMAP4_RM_CPU0_CPU0_RSTST_OFFSET 0x0010 +#define OMAP4430_RM_CPU0_CPU0_RSTST OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU0_INST, 0x0010) +#define OMAP4_CM_CPU0_CPU0_CLKCTRL_OFFSET 0x0014 +#define OMAP4430_CM_CPU0_CPU0_CLKCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU0_INST, 0x0014) +#define OMAP4_CM_CPU0_CLKSTCTRL_OFFSET 0x0018 +#define OMAP4430_CM_CPU0_CLKSTCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU0_INST, 0x0018) + +/* PRCM_MPU.CPU1 register offsets */ +#define OMAP4_PM_CPU1_PWRSTCTRL_OFFSET 0x0000 +#define OMAP4430_PM_CPU1_PWRSTCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU1_INST, 0x0000) +#define OMAP4_PM_CPU1_PWRSTST_OFFSET 0x0004 +#define OMAP4430_PM_CPU1_PWRSTST OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU1_INST, 0x0004) +#define OMAP4_RM_CPU1_CPU1_CONTEXT_OFFSET 0x0008 +#define OMAP4430_RM_CPU1_CPU1_CONTEXT OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU1_INST, 0x0008) +#define OMAP4_RM_CPU1_CPU1_RSTCTRL_OFFSET 0x000c +#define OMAP4430_RM_CPU1_CPU1_RSTCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU1_INST, 0x000c) +#define OMAP4_RM_CPU1_CPU1_RSTST_OFFSET 0x0010 +#define OMAP4430_RM_CPU1_CPU1_RSTST OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU1_INST, 0x0010) +#define OMAP4_CM_CPU1_CPU1_CLKCTRL_OFFSET 0x0014 +#define OMAP4430_CM_CPU1_CPU1_CLKCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU1_INST, 0x0014) +#define OMAP4_CM_CPU1_CLKSTCTRL_OFFSET 0x0018 +#define OMAP4430_CM_CPU1_CLKSTCTRL OMAP44XX_PRCM_MPU_REGADDR(OMAP4430_PRCM_MPU_CPU1_INST, 0x0018) + +#endif diff --git a/arch/arm/mach-omap2/prcm_mpu54xx.h b/arch/arm/mach-omap2/prcm_mpu54xx.h new file mode 100644 index 000000000..bdbfa070b --- /dev/null +++ b/arch/arm/mach-omap2/prcm_mpu54xx.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP54xx PRCM MPU instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM_MPU54XX_H +#define __ARCH_ARM_MACH_OMAP2_PRCM_MPU54XX_H + +#include "prcm_mpu_44xx_54xx.h" +#include "common.h" + +#define OMAP54XX_PRCM_MPU_BASE 0x48243000 + +#define OMAP54XX_PRCM_MPU_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP54XX_PRCM_MPU_BASE + (inst) + (reg)) + +/* PRCM_MPU instances */ +#define OMAP54XX_PRCM_MPU_OCP_SOCKET_INST 0x0000 +#define OMAP54XX_PRCM_MPU_DEVICE_INST 0x0200 +#define OMAP54XX_PRCM_MPU_PRM_C0_INST 0x0400 +#define OMAP54XX_PRCM_MPU_CM_C0_INST 0x0600 +#define OMAP54XX_PRCM_MPU_PRM_C1_INST 0x0800 +#define OMAP54XX_PRCM_MPU_CM_C1_INST 0x0a00 + +/* PRCM_MPU clockdomain register offsets (from instance start) */ +#define OMAP54XX_PRCM_MPU_CM_C0_CPU0_CDOFFS 0x0000 +#define OMAP54XX_PRCM_MPU_CM_C1_CPU1_CDOFFS 0x0000 + + +/* + * PRCM_MPU + * + * The PRCM_MPU is a local PRCM inside the MPU subsystem. For the PRCM (global) + * point of view the PRCM_MPU is a single entity. It shares the same + * programming model as the global PRCM and thus can be assimilate as two new + * MOD inside the PRCM + */ + +/* PRCM_MPU.PRCM_MPU_OCP_SOCKET register offsets */ +#define OMAP54XX_REVISION_PRCM_MPU_OFFSET 0x0000 + +/* PRCM_MPU.PRCM_MPU_DEVICE register offsets */ +#define OMAP54XX_PRCM_MPU_PRM_RSTST_OFFSET 0x0000 +#define OMAP54XX_PRCM_MPU_PRM_PSCON_COUNT_OFFSET 0x0004 +#define OMAP54XX_PRM_FRAC_INCREMENTER_NUMERATOR_OFFSET 0x0010 +#define OMAP54XX_PRM_FRAC_INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x0014 + +/* PRCM_MPU.PRCM_MPU_PRM_C0 register offsets */ +#define OMAP54XX_PM_CPU0_PWRSTCTRL_OFFSET 0x0000 +#define OMAP54XX_PM_CPU0_PWRSTST_OFFSET 0x0004 +#define OMAP54XX_RM_CPU0_CPU0_RSTCTRL_OFFSET 0x0010 +#define OMAP54XX_RM_CPU0_CPU0_RSTST_OFFSET 0x0014 +#define OMAP54XX_RM_CPU0_CPU0_CONTEXT_OFFSET 0x0024 + +/* PRCM_MPU.PRCM_MPU_CM_C0 register offsets */ +#define OMAP54XX_CM_CPU0_CLKSTCTRL_OFFSET 0x0000 +#define OMAP54XX_CM_CPU0_CPU0_CLKCTRL_OFFSET 0x0020 +#define OMAP54XX_CM_CPU0_CPU0_CLKCTRL OMAP54XX_PRCM_MPU_REGADDR(OMAP54XX_PRCM_MPU_CM_C0_INST, 0x0020) + +/* PRCM_MPU.PRCM_MPU_PRM_C1 register offsets */ +#define OMAP54XX_PM_CPU1_PWRSTCTRL_OFFSET 0x0000 +#define OMAP54XX_PM_CPU1_PWRSTST_OFFSET 0x0004 +#define OMAP54XX_RM_CPU1_CPU1_RSTCTRL_OFFSET 0x0010 +#define OMAP54XX_RM_CPU1_CPU1_RSTST_OFFSET 0x0014 +#define OMAP54XX_RM_CPU1_CPU1_CONTEXT_OFFSET 0x0024 + +/* PRCM_MPU.PRCM_MPU_CM_C1 register offsets */ +#define OMAP54XX_CM_CPU1_CLKSTCTRL_OFFSET 0x0000 +#define OMAP54XX_CM_CPU1_CPU1_CLKCTRL_OFFSET 0x0020 +#define OMAP54XX_CM_CPU1_CPU1_CLKCTRL OMAP54XX_PRCM_MPU_REGADDR(OMAP54XX_PRCM_MPU_CM_C1_INST, 0x0020) + +#endif diff --git a/arch/arm/mach-omap2/prcm_mpu7xx.h b/arch/arm/mach-omap2/prcm_mpu7xx.h new file mode 100644 index 000000000..2e3032440 --- /dev/null +++ b/arch/arm/mach-omap2/prcm_mpu7xx.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * DRA7xx PRCM MPU instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Generated by code originally written by: + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM_MPU7XX_H +#define __ARCH_ARM_MACH_OMAP2_PRCM_MPU7XX_H + +#include "prcm_mpu_44xx_54xx.h" + +#define DRA7XX_PRCM_MPU_BASE 0x48243000 + +#define DRA7XX_PRCM_MPU_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(DRA7XX_PRCM_MPU_BASE + (inst) + (reg)) + +/* MPU_PRCM instances */ +#define DRA7XX_MPU_PRCM_OCP_SOCKET_INST 0x0000 +#define DRA7XX_MPU_PRCM_DEVICE_INST 0x0200 +#define DRA7XX_MPU_PRCM_PRM_C0_INST 0x0400 +#define DRA7XX_MPU_PRCM_CM_C0_INST 0x0600 +#define DRA7XX_MPU_PRCM_PRM_C1_INST 0x0800 +#define DRA7XX_MPU_PRCM_CM_C1_INST 0x0a00 + +/* PRCM_MPU clockdomain register offsets (from instance start) */ +#define DRA7XX_MPU_PRCM_CM_C0_CPU0_CDOFFS 0x0000 +#define DRA7XX_MPU_PRCM_CM_C1_CPU1_CDOFFS 0x0000 + + +/* MPU_PRCM */ + +/* MPU_PRCM.PRCM_MPU_OCP_SOCKET register offsets */ +#define DRA7XX_REVISION_PRCM_MPU_OFFSET 0x0000 + +/* MPU_PRCM.PRCM_MPU_DEVICE register offsets */ +#define DRA7XX_PRM_FRAC_INCREMENTER_NUMERATOR_OFFSET 0x0010 +#define DRA7XX_PRM_FRAC_INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x0014 + +/* MPU_PRCM.PRCM_MPU_PRM_C0 register offsets */ +#define DRA7XX_PM_CPU0_PWRSTCTRL_OFFSET 0x0000 +#define DRA7XX_PM_CPU0_PWRSTST_OFFSET 0x0004 +#define DRA7XX_RM_CPU0_CPU0_RSTCTRL_OFFSET 0x0010 +#define DRA7XX_RM_CPU0_CPU0_RSTST_OFFSET 0x0014 +#define DRA7XX_RM_CPU0_CPU0_CONTEXT_OFFSET 0x0024 + +/* MPU_PRCM.PRCM_MPU_CM_C0 register offsets */ +#define DRA7XX_CM_CPU0_CLKSTCTRL_OFFSET 0x0000 +#define DRA7XX_CM_CPU0_CPU0_CLKCTRL_OFFSET 0x0020 +#define DRA7XX_CM_CPU0_CPU0_CLKCTRL DRA7XX_MPU_PRCM_REGADDR(DRA7XX_MPU_PRCM_CM_C0_INST, 0x0020) + +/* MPU_PRCM.PRCM_MPU_PRM_C1 register offsets */ +#define DRA7XX_PM_CPU1_PWRSTCTRL_OFFSET 0x0000 +#define DRA7XX_PM_CPU1_PWRSTST_OFFSET 0x0004 +#define DRA7XX_RM_CPU1_CPU1_RSTCTRL_OFFSET 0x0010 +#define DRA7XX_RM_CPU1_CPU1_RSTST_OFFSET 0x0014 +#define DRA7XX_RM_CPU1_CPU1_CONTEXT_OFFSET 0x0024 + +/* MPU_PRCM.PRCM_MPU_CM_C1 register offsets */ +#define DRA7XX_CM_CPU1_CLKSTCTRL_OFFSET 0x0000 +#define DRA7XX_CM_CPU1_CPU1_CLKCTRL_OFFSET 0x0020 +#define DRA7XX_CM_CPU1_CPU1_CLKCTRL DRA7XX_MPU_PRCM_REGADDR(DRA7XX_MPU_PRCM_CM_C1_INST, 0x0020) + +#endif diff --git a/arch/arm/mach-omap2/prcm_mpu_44xx_54xx.h b/arch/arm/mach-omap2/prcm_mpu_44xx_54xx.h new file mode 100644 index 000000000..7c6377566 --- /dev/null +++ b/arch/arm/mach-omap2/prcm_mpu_44xx_54xx.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx and OMAP54xx PRCM MPU function prototypes + * + * Copyright (C) 2010, 2013 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM_MPU_44XX_54XX_H +#define __ARCH_ARM_MACH_OMAP2_PRCM_MPU_44XX_54XX_H + +#ifndef __ASSEMBLER__ +#include "prcm-common.h" + +extern struct omap_domain_base prcm_mpu_base; + +extern u32 omap4_prcm_mpu_read_inst_reg(s16 inst, u16 idx); +extern void omap4_prcm_mpu_write_inst_reg(u32 val, s16 inst, u16 idx); +extern u32 omap4_prcm_mpu_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, + s16 idx); +extern void __init omap2_set_globals_prcm_mpu(void __iomem *prcm_mpu); +#endif + +#endif diff --git a/arch/arm/mach-omap2/prm-regbits-24xx.h b/arch/arm/mach-omap2/prm-regbits-24xx.h new file mode 100644 index 000000000..ff83801f6 --- /dev/null +++ b/arch/arm/mach-omap2/prm-regbits-24xx.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_24XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_24XX_H + +/* + * OMAP24XX Power/Reset Management register bits + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Copyright (C) 2007 Nokia Corporation + * + * Written by Paul Walmsley + */ + +#include "prm2xxx.h" + +#define OMAP24XX_EN_CORE_SHIFT 0 +#define OMAP24XX_FORCESTATE_MASK (1 << 18) +#define OMAP24XX_AUTOIDLE_MASK (1 << 0) +#define OMAP24XX_AUTO_EXTVOLT_MASK (1 << 15) +#define OMAP24XX_SETOFF_LEVEL_SHIFT 12 +#define OMAP24XX_MEMRETCTRL_MASK (1 << 8) +#define OMAP24XX_SETRET_LEVEL_SHIFT 6 +#define OMAP24XX_VOLT_LEVEL_SHIFT 0 +#define OMAP2420_CLKOUT2_EN_SHIFT 15 +#define OMAP2420_CLKOUT2_DIV_SHIFT 11 +#define OMAP2420_CLKOUT2_DIV_WIDTH 3 +#define OMAP2420_CLKOUT2_SOURCE_MASK (0x3 << 8) +#define OMAP24XX_CLKOUT_EN_SHIFT 7 +#define OMAP24XX_CLKOUT_DIV_SHIFT 3 +#define OMAP24XX_CLKOUT_DIV_WIDTH 3 +#define OMAP24XX_CLKOUT_SOURCE_MASK (0x3 << 0) +#define OMAP24XX_EMULATION_EN_SHIFT 0 +#define OMAP2430_PM_WKDEP_MPU_EN_MDM_SHIFT 5 +#define OMAP24XX_PM_WKDEP_MPU_EN_DSP_SHIFT 2 +#define OMAP24XX_EXTWMPU_RST_SHIFT 6 +#define OMAP24XX_SECU_WD_RST_SHIFT 5 +#define OMAP24XX_MPU_WD_RST_SHIFT 4 +#define OMAP24XX_SECU_VIOL_RST_SHIFT 3 +#endif diff --git a/arch/arm/mach-omap2/prm-regbits-33xx.h b/arch/arm/mach-omap2/prm-regbits-33xx.h new file mode 100644 index 000000000..3748c5266 --- /dev/null +++ b/arch/arm/mach-omap2/prm-regbits-33xx.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AM33XX PRM_XXX register bits + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_33XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_33XX_H + +#include "prm.h" + +#define AM33XX_GFX_MEM_ONSTATE_MASK (0x3 << 17) +#define AM33XX_GFX_MEM_RETSTATE_MASK (1 << 6) +#define AM33XX_GFX_MEM_STATEST_MASK (0x3 << 4) +#define AM33XX_GLOBAL_WARM_SW_RST_MASK (1 << 1) +#define AM33XX_RST_GLOBAL_WARM_SW_MASK (1 << 0) +#define AM33XX_PRUSS_MEM_ONSTATE_MASK (0x3 << 5) +#define AM33XX_PRUSS_MEM_RETSTATE_MASK (1 << 7) +#define AM33XX_PRUSS_MEM_STATEST_MASK (0x3 << 23) +#define AM33XX_LASTPOWERSTATEENTERED_SHIFT 24 +#define AM33XX_LASTPOWERSTATEENTERED_MASK (0x3 << 24) +#define AM33XX_LOGICRETSTATE_MASK (1 << 2) +#define AM33XX_LOGICRETSTATE_3_3_MASK (1 << 3) +#define AM33XX_LOGICSTATEST_SHIFT 2 +#define AM33XX_LOGICSTATEST_MASK (1 << 2) +#define AM33XX_LOWPOWERSTATECHANGE_SHIFT 4 +#define AM33XX_LOWPOWERSTATECHANGE_MASK (1 << 4) +#define AM33XX_MPU_L1_ONSTATE_MASK (0x3 << 18) +#define AM33XX_MPU_L1_RETSTATE_MASK (1 << 22) +#define AM33XX_MPU_L1_STATEST_MASK (0x3 << 6) +#define AM33XX_MPU_L2_ONSTATE_MASK (0x3 << 20) +#define AM33XX_MPU_L2_RETSTATE_MASK (1 << 23) +#define AM33XX_MPU_L2_STATEST_MASK (0x3 << 8) +#define AM33XX_MPU_RAM_ONSTATE_MASK (0x3 << 16) +#define AM33XX_MPU_RAM_RETSTATE_MASK (1 << 24) +#define AM33XX_MPU_RAM_STATEST_MASK (0x3 << 4) +#define AM33XX_PER_MEM_ONSTATE_MASK (0x3 << 25) +#define AM33XX_PER_MEM_RETSTATE_MASK (1 << 29) +#define AM33XX_PER_MEM_STATEST_MASK (0x3 << 17) +#define AM33XX_RAM_MEM_ONSTATE_MASK (0x3 << 30) +#define AM33XX_RAM_MEM_RETSTATE_MASK (1 << 27) +#define AM33XX_RAM_MEM_STATEST_MASK (0x3 << 21) +#endif diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h b/arch/arm/mach-omap2/prm-regbits-34xx.h new file mode 100644 index 000000000..4b94fd11a --- /dev/null +++ b/arch/arm/mach-omap2/prm-regbits-34xx.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP3430 Power/Reset Management register bits + * + * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Copyright (C) 2007-2008 Nokia Corporation + * + * Written by Paul Walmsley + */ +#ifndef __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_34XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_34XX_H + + +#include "prm3xxx.h" + +#define OMAP3430_ERROROFFSET_MASK (0xff << 24) +#define OMAP3430_ERRORGAIN_MASK (0xff << 16) +#define OMAP3430_INITVOLTAGE_MASK (0xff << 8) +#define OMAP3430_TIMEOUTEN_MASK (1 << 3) +#define OMAP3430_INITVDD_MASK (1 << 2) +#define OMAP3430_FORCEUPDATE_MASK (1 << 1) +#define OMAP3430_VPENABLE_MASK (1 << 0) +#define OMAP3430_SMPSWAITTIMEMIN_SHIFT 8 +#define OMAP3430_VSTEPMIN_SHIFT 0 +#define OMAP3430_SMPSWAITTIMEMAX_SHIFT 8 +#define OMAP3430_VSTEPMAX_SHIFT 0 +#define OMAP3430_VDDMAX_SHIFT 24 +#define OMAP3430_VDDMIN_SHIFT 16 +#define OMAP3430_TIMEOUT_SHIFT 0 +#define OMAP3430_VPVOLTAGE_MASK (0xff << 0) +#define OMAP3430_EN_PER_SHIFT 7 +#define OMAP3430_LOGICSTATEST_MASK (1 << 2) +#define OMAP3430_LASTLOGICSTATEENTERED_MASK (1 << 2) +#define OMAP3430_LASTPOWERSTATEENTERED_MASK (0x3 << 0) +#define OMAP3430_GRPSEL_MCBSP5_MASK (1 << 10) +#define OMAP3430_GRPSEL_MCBSP1_MASK (1 << 9) +#define OMAP3630_GRPSEL_UART4_MASK (1 << 18) +#define OMAP3430_GRPSEL_GPIO6_MASK (1 << 17) +#define OMAP3430_GRPSEL_GPIO5_MASK (1 << 16) +#define OMAP3430_GRPSEL_GPIO4_MASK (1 << 15) +#define OMAP3430_GRPSEL_GPIO3_MASK (1 << 14) +#define OMAP3430_GRPSEL_GPIO2_MASK (1 << 13) +#define OMAP3430_GRPSEL_UART3_MASK (1 << 11) +#define OMAP3430_GRPSEL_GPT8_MASK (1 << 9) +#define OMAP3430_GRPSEL_GPT7_MASK (1 << 8) +#define OMAP3430_GRPSEL_GPT6_MASK (1 << 7) +#define OMAP3430_GRPSEL_GPT5_MASK (1 << 6) +#define OMAP3430_GRPSEL_MCBSP4_MASK (1 << 2) +#define OMAP3430_GRPSEL_MCBSP3_MASK (1 << 1) +#define OMAP3430_GRPSEL_MCBSP2_MASK (1 << 0) +#define OMAP3430_GRPSEL_GPIO1_MASK (1 << 3) +#define OMAP3430_GRPSEL_GPT12_MASK (1 << 1) +#define OMAP3430_GRPSEL_GPT1_MASK (1 << 0) +#define OMAP3430_RST3_IVA2_MASK (1 << 2) +#define OMAP3430_RST2_IVA2_MASK (1 << 1) +#define OMAP3430_RST1_IVA2_MASK (1 << 0) +#define OMAP3430_L2FLATMEMONSTATE_MASK (0x3 << 22) +#define OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK (0x3 << 20) +#define OMAP3430_L1FLATMEMONSTATE_MASK (0x3 << 18) +#define OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK (0x3 << 16) +#define OMAP3430_L2FLATMEMRETSTATE_MASK (1 << 11) +#define OMAP3430_SHAREDL2CACHEFLATRETSTATE_MASK (1 << 10) +#define OMAP3430_L1FLATMEMRETSTATE_MASK (1 << 9) +#define OMAP3430_SHAREDL1CACHEFLATRETSTATE_MASK (1 << 8) +#define OMAP3430_L2FLATMEMSTATEST_MASK (0x3 << 10) +#define OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK (0x3 << 8) +#define OMAP3430_L1FLATMEMSTATEST_MASK (0x3 << 6) +#define OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK (0x3 << 4) +#define OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK (0x3 << 10) +#define OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK (0x3 << 8) +#define OMAP3430ES2_SND_PERIPH_DPLL_ST_SHIFT 25 +#define OMAP3430_VP2_TRANXDONE_ST_MASK (1 << 21) +#define OMAP3430_VP1_TRANXDONE_ST_MASK (1 << 15) +#define OMAP3430_PRM_IRQSTATUS_MPU_IVA2_DPLL_ST_SHIFT 8 +#define OMAP3430_MPU_DPLL_ST_SHIFT 7 +#define OMAP3430_PERIPH_DPLL_ST_SHIFT 6 +#define OMAP3430_CORE_DPLL_ST_SHIFT 5 +#define OMAP3430ES2_SND_PERIPH_DPLL_RECAL_EN_SHIFT 25 +#define OMAP3430_PRM_IRQENABLE_MPU_IVA2_DPLL_RECAL_EN_SHIFT 8 +#define OMAP3430_MPU_DPLL_RECAL_EN_SHIFT 7 +#define OMAP3430_PERIPH_DPLL_RECAL_EN_SHIFT 6 +#define OMAP3430_CORE_DPLL_RECAL_EN_SHIFT 5 +#define OMAP3430_PM_WKDEP_MPU_EN_DSS_SHIFT 5 +#define OMAP3430_PM_WKDEP_MPU_EN_IVA2_SHIFT 2 +#define OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RSTPWRON_MASK (1 << 1) +#define OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RST_MASK (1 << 0) +#define OMAP3430_LASTMEM2STATEENTERED_MASK (0x3 << 6) +#define OMAP3430_LASTMEM1STATEENTERED_MASK (0x3 << 4) +#define OMAP3430_EN_IO_CHAIN_MASK (1 << 16) +#define OMAP3430_EN_IO_MASK (1 << 8) +#define OMAP3430_EN_GPIO1_MASK (1 << 3) +#define OMAP3430_ST_IO_CHAIN_MASK (1 << 16) +#define OMAP3430_ST_IO_MASK (1 << 8) +#define OMAP3430_SYS_CLKIN_SEL_SHIFT 0 +#define OMAP3430_SYS_CLKIN_SEL_WIDTH 3 +#define OMAP3430_CLKOUT_EN_SHIFT 7 +#define OMAP3430_PM_WKEN_DSS_EN_DSS_MASK (1 << 0) +#define OMAP3430ES2_SAVEANDRESTORE_SHIFT 4 +#define OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT 16 +#define OMAP3430_PRM_VC_SMPS_SA_SA1_MASK (0x7f << 16) +#define OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT 0 +#define OMAP3430_PRM_VC_SMPS_SA_SA0_MASK (0x7f << 0) +#define OMAP3430_VOLRA1_MASK (0xff << 16) +#define OMAP3430_VOLRA0_MASK (0xff << 0) +#define OMAP3430_CMDRA1_MASK (0xff << 16) +#define OMAP3430_CMDRA0_MASK (0xff << 0) +#define OMAP3430_VC_CMD_ON_SHIFT 24 +#define OMAP3430_VC_CMD_ON_MASK (0xFF << 24) +#define OMAP3430_VC_CMD_ONLP_SHIFT 16 +#define OMAP3430_VC_CMD_RET_SHIFT 8 +#define OMAP3430_VC_CMD_OFF_SHIFT 0 +#define OMAP3430_SREN_MASK (1 << 4) +#define OMAP3430_HSEN_MASK (1 << 3) +#define OMAP3430_MCODE_MASK (0x7 << 0) +#define OMAP3430_VALID_MASK (1 << 24) +#define OMAP3430_DATA_SHIFT 16 +#define OMAP3430_REGADDR_SHIFT 8 +#define OMAP3430_SLAVEADDR_SHIFT 0 +#define OMAP3430_ICECRUSHER_RST_SHIFT 10 +#define OMAP3430_ICEPICK_RST_SHIFT 9 +#define OMAP3430_VDD2_VOLTAGE_MANAGER_RST_SHIFT 8 +#define OMAP3430_VDD1_VOLTAGE_MANAGER_RST_SHIFT 7 +#define OMAP3430_EXTERNAL_WARM_RST_SHIFT 6 +#define OMAP3430_SECURE_WD_RST_SHIFT 5 +#define OMAP3430_MPU_WD_RST_SHIFT 4 +#define OMAP3430_SECURITY_VIOL_RST_SHIFT 3 +#define OMAP3430_GLOBAL_SW_RST_SHIFT 1 +#define OMAP3430_GLOBAL_COLD_RST_SHIFT 0 +#define OMAP3430_GLOBAL_COLD_RST_MASK (1 << 0) +#define OMAP3430_PRM_VOLTCTRL_SEL_VMODE (1 << 4) +#define OMAP3430_PRM_VOLTCTRL_SEL_OFF (1 << 3) +#define OMAP3430_PRM_VOLTCTRL_AUTO_OFF (1 << 2) +#define OMAP3430_PRM_VOLTCTRL_AUTO_RET (1 << 1) +#define OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP (1 << 0) +#define OMAP3430_SETUP_TIME2_MASK (0xffff << 16) +#define OMAP3430_SETUP_TIME1_MASK (0xffff << 0) +#define OMAP3430_PRM_POLCTRL_OFFMODE_POL (1 << 3) +#define OMAP3430_PRM_POLCTRL_CLKOUT_POL (1 << 2) +#define OMAP3430_PRM_POLCTRL_CLKREQ_POL (1 << 1) +#define OMAP3430_PRM_POLCTRL_EXTVOL_POL (1 << 0) +#endif diff --git a/arch/arm/mach-omap2/prm-regbits-44xx.h b/arch/arm/mach-omap2/prm-regbits-44xx.h new file mode 100644 index 000000000..55d84d0d7 --- /dev/null +++ b/arch/arm/mach-omap2/prm-regbits-44xx.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx Power Management register bits + * + * Copyright (C) 2009-2010 Texas Instruments, Inc. + * Copyright (C) 2009-2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_44XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_44XX_H + +#define OMAP4430_C2C_RST_SHIFT 10 +#define OMAP4430_CMDRA_VDD_CORE_L_MASK (0xff << 0) +#define OMAP4430_CMDRA_VDD_IVA_L_MASK (0xff << 8) +#define OMAP4430_CMDRA_VDD_MPU_L_MASK (0xff << 16) +#define OMAP4430_DATA_SHIFT 16 +#define OMAP4430_ERRORGAIN_MASK (0xff << 16) +#define OMAP4430_ERROROFFSET_MASK (0xff << 24) +#define OMAP4430_EXTERNAL_WARM_RST_SHIFT 5 +#define OMAP4430_FORCEUPDATE_MASK (1 << 1) +#define OMAP4430_GLOBAL_COLD_RST_SHIFT 0 +#define OMAP4430_GLOBAL_WARM_SW_RST_SHIFT 1 +#define OMAP4430_GLOBAL_WUEN_MASK (1 << 16) +#define OMAP4430_HSMCODE_MASK (0x7 << 0) +#define OMAP4430_SRMODEEN_MASK (1 << 4) +#define OMAP4430_HSMODEEN_MASK (1 << 3) +#define OMAP4430_HSSCLL_SHIFT 24 +#define OMAP4430_ICEPICK_RST_SHIFT 9 +#define OMAP4430_INITVDD_MASK (1 << 2) +#define OMAP4430_INITVOLTAGE_MASK (0xff << 8) +#define OMAP4430_LASTPOWERSTATEENTERED_SHIFT 24 +#define OMAP4430_LASTPOWERSTATEENTERED_MASK (0x3 << 24) +#define OMAP4430_LOGICRETSTATE_SHIFT 2 +#define OMAP4430_LOGICRETSTATE_MASK (1 << 2) +#define OMAP4430_LOGICSTATEST_SHIFT 2 +#define OMAP4430_LOGICSTATEST_MASK (1 << 2) +#define OMAP4430_LOSTCONTEXT_DFF_MASK (1 << 0) +#define OMAP4430_LOSTMEM_AESSMEM_MASK (1 << 8) +#define OMAP4430_LOWPOWERSTATECHANGE_SHIFT 4 +#define OMAP4430_LOWPOWERSTATECHANGE_MASK (1 << 4) +#define OMAP4430_MPU_SECURITY_VIOL_RST_SHIFT 2 +#define OMAP4430_MPU_WDT_RST_SHIFT 3 +#define OMAP4430_OCP_NRET_BANK_ONSTATE_MASK (0x3 << 24) +#define OMAP4430_OCP_NRET_BANK_RETSTATE_MASK (1 << 12) +#define OMAP4430_OCP_NRET_BANK_STATEST_MASK (0x3 << 12) +#define OMAP4430_OFF_SHIFT 0 +#define OMAP4430_ON_SHIFT 24 +#define OMAP4430_ON_MASK (0xff << 24) +#define OMAP4430_ONLP_SHIFT 16 +#define OMAP4430_RAMP_DOWN_COUNT_SHIFT 16 +#define OMAP4430_RAMP_UP_COUNT_SHIFT 0 +#define OMAP4430_RAMP_UP_PRESCAL_SHIFT 8 +#define OMAP4430_REGADDR_SHIFT 8 +#define OMAP4430_RET_SHIFT 8 +#define OMAP4430_RST_GLOBAL_WARM_SW_MASK (1 << 0) +#define OMAP4430_SA_VDD_CORE_L_SHIFT 0 +#define OMAP4430_SA_VDD_CORE_L_0_6_MASK (0x7f << 0) +#define OMAP4430_SA_VDD_IVA_L_SHIFT 8 +#define OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_MASK (0x7f << 8) +#define OMAP4430_SA_VDD_MPU_L_SHIFT 16 +#define OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_MASK (0x7f << 16) +#define OMAP4430_SCLH_SHIFT 0 +#define OMAP4430_SCLL_SHIFT 8 +#define OMAP4430_SECURE_WDT_RST_SHIFT 4 +#define OMAP4430_SLAVEADDR_SHIFT 0 +#define OMAP4430_SMPSWAITTIMEMAX_SHIFT 8 +#define OMAP4430_SMPSWAITTIMEMIN_SHIFT 8 +#define OMAP4430_TIMEOUT_SHIFT 0 +#define OMAP4430_TIMEOUTEN_MASK (1 << 3) +#define OMAP4430_VALID_MASK (1 << 24) +#define OMAP4430_VDDMAX_SHIFT 24 +#define OMAP4430_VDDMIN_SHIFT 16 +#define OMAP4430_VDD_CORE_VOLT_MGR_RST_SHIFT 8 +#define OMAP4430_VDD_IVA_VOLT_MGR_RST_SHIFT 7 +#define OMAP4430_VDD_MPU_VOLT_MGR_RST_SHIFT 6 +#define OMAP4430_VOLRA_VDD_CORE_L_MASK (0xff << 0) +#define OMAP4430_VOLRA_VDD_IVA_L_MASK (0xff << 8) +#define OMAP4430_VOLRA_VDD_MPU_L_MASK (0xff << 16) +#define OMAP4430_VPENABLE_MASK (1 << 0) +#define OMAP4430_VPVOLTAGE_MASK (0xff << 0) +#define OMAP4430_VP_CORE_TRANXDONE_ST_MASK (1 << 21) +#define OMAP4430_VP_IVA_TRANXDONE_ST_MASK (1 << 29) +#define OMAP4430_VP_MPU_TRANXDONE_ST_MASK (1 << 5) +#define OMAP4430_VSTEPMAX_SHIFT 0 +#define OMAP4430_VSTEPMIN_SHIFT 0 +#define OMAP4430_WUCLK_CTRL_MASK (1 << 8) +#define OMAP4430_WUCLK_STATUS_SHIFT 9 +#define OMAP4430_WUCLK_STATUS_MASK (1 << 9) +#endif diff --git a/arch/arm/mach-omap2/prm.h b/arch/arm/mach-omap2/prm.h new file mode 100644 index 000000000..08df78810 --- /dev/null +++ b/arch/arm/mach-omap2/prm.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2/3/4 Power/Reset Management (PRM) bitfield definitions + * + * Copyright (C) 2007-2009, 2012 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * + * Paul Walmsley + */ +#ifndef __ARCH_ARM_MACH_OMAP2_PRM_H +#define __ARCH_ARM_MACH_OMAP2_PRM_H + +#include "prcm-common.h" + +# ifndef __ASSEMBLER__ +extern struct omap_domain_base prm_base; +extern u16 prm_features; +extern void omap2_set_globals_prm(void __iomem *prm); +int omap_prcm_init(void); +int omap2_prm_base_init(void); +int omap2_prcm_base_init(void); +# endif + +/* + * prm_features flag values + * + * PRM_HAS_IO_WAKEUP: has IO wakeup capability + * PRM_HAS_VOLTAGE: has voltage domains + */ +#define PRM_HAS_IO_WAKEUP BIT(0) +#define PRM_HAS_VOLTAGE BIT(1) + +/* + * MAX_MODULE_SOFTRESET_WAIT: Maximum microseconds to wait for OMAP + * module to softreset + */ +#define MAX_MODULE_SOFTRESET_WAIT 10000 + +/* + * MAX_MODULE_HARDRESET_WAIT: Maximum microseconds to wait for an OMAP + * submodule to exit hardreset + */ +#define MAX_MODULE_HARDRESET_WAIT 10000 + +/* + * Register bitfields + */ + +/* + * 24XX: PM_PWSTST_CORE, PM_PWSTST_GFX, PM_PWSTST_MPU, PM_PWSTST_DSP + * + * 2430: PM_PWSTST_MDM + * + * 3430: PM_PWSTST_IVA2, PM_PWSTST_MPU, PM_PWSTST_CORE, PM_PWSTST_GFX, + * PM_PWSTST_DSS, PM_PWSTST_CAM, PM_PWSTST_PER, PM_PWSTST_EMU, + * PM_PWSTST_NEON + */ +#define OMAP_INTRANSITION_MASK (1 << 20) + + +/* + * 24XX: PM_PWSTST_GFX, PM_PWSTST_DSP + * + * 2430: PM_PWSTST_MDM + * + * 3430: PM_PWSTST_IVA2, PM_PWSTST_MPU, PM_PWSTST_CORE, PM_PWSTST_GFX, + * PM_PWSTST_DSS, PM_PWSTST_CAM, PM_PWSTST_PER, PM_PWSTST_EMU, + * PM_PWSTST_NEON + */ +#define OMAP_POWERSTATEST_SHIFT 0 +#define OMAP_POWERSTATEST_MASK (0x3 << 0) + +/* + * 24XX: PM_PWSTCTRL_MPU, PM_PWSTCTRL_CORE, PM_PWSTCTRL_GFX, + * PM_PWSTCTRL_DSP, PM_PWSTST_MPU + * + * 2430: PM_PWSTCTRL_MDM shared bits + * + * 3430: PM_PWSTCTRL_IVA2, PM_PWSTCTRL_MPU, PM_PWSTCTRL_CORE, + * PM_PWSTCTRL_GFX, PM_PWSTCTRL_DSS, PM_PWSTCTRL_CAM, PM_PWSTCTRL_PER, + * PM_PWSTCTRL_NEON shared bits + */ +#define OMAP_POWERSTATE_SHIFT 0 +#define OMAP_POWERSTATE_MASK (0x3 << 0) + +/* + * Standardized OMAP reset source bits + * + * To the extent these happen to match the hardware register bit + * shifts, it's purely coincidental. Used by omap-wdt.c. + * OMAP_UNKNOWN_RST_SRC_ID_SHIFT is a special value, used whenever + * there are any bits remaining in the global PRM_RSTST register that + * haven't been identified, or when the PRM code for the current SoC + * doesn't know how to interpret the register. + */ +#define OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT 0 +#define OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT 1 +#define OMAP_SECU_VIOL_RST_SRC_ID_SHIFT 2 +#define OMAP_MPU_WD_RST_SRC_ID_SHIFT 3 +#define OMAP_SECU_WD_RST_SRC_ID_SHIFT 4 +#define OMAP_EXTWARM_RST_SRC_ID_SHIFT 5 +#define OMAP_VDD_MPU_VM_RST_SRC_ID_SHIFT 6 +#define OMAP_VDD_IVA_VM_RST_SRC_ID_SHIFT 7 +#define OMAP_VDD_CORE_VM_RST_SRC_ID_SHIFT 8 +#define OMAP_ICEPICK_RST_SRC_ID_SHIFT 9 +#define OMAP_ICECRUSHER_RST_SRC_ID_SHIFT 10 +#define OMAP_C2C_RST_SRC_ID_SHIFT 11 +#define OMAP_UNKNOWN_RST_SRC_ID_SHIFT 12 + +#ifndef __ASSEMBLER__ + +/** + * struct prm_reset_src_map - map register bitshifts to standard bitshifts + * @reg_shift: bitshift in the PRM reset source register + * @std_shift: bitshift equivalent in the standard reset source list + * + * The fields are signed because -1 is used as a terminator. + */ +struct prm_reset_src_map { + s8 reg_shift; + s8 std_shift; +}; + +/** + * struct prm_ll_data - fn ptrs to per-SoC PRM function implementations + * @read_reset_sources: ptr to the SoC PRM-specific get_reset_source impl + * @was_any_context_lost_old: ptr to the SoC PRM context loss test fn + * @clear_context_loss_flags_old: ptr to the SoC PRM context loss flag clear fn + * @late_init: ptr to the late init function + * @assert_hardreset: ptr to the SoC PRM hardreset assert impl + * @deassert_hardreset: ptr to the SoC PRM hardreset deassert impl + * + * XXX @was_any_context_lost_old and @clear_context_loss_flags_old are + * deprecated. + */ +struct prm_ll_data { + u32 (*read_reset_sources)(void); + bool (*was_any_context_lost_old)(u8 part, s16 inst, u16 idx); + void (*clear_context_loss_flags_old)(u8 part, s16 inst, u16 idx); + int (*late_init)(void); + int (*assert_hardreset)(u8 shift, u8 part, s16 prm_mod, u16 offset); + int (*deassert_hardreset)(u8 shift, u8 st_shift, u8 part, s16 prm_mod, + u16 offset, u16 st_offset); + int (*is_hardreset_asserted)(u8 shift, u8 part, s16 prm_mod, + u16 offset); + void (*reset_system)(void); + int (*clear_mod_irqs)(s16 module, u8 regs, u32 wkst_mask); + u32 (*vp_check_txdone)(u8 vp_id); + void (*vp_clear_txdone)(u8 vp_id); +}; + +extern int prm_register(struct prm_ll_data *pld); +extern int prm_unregister(struct prm_ll_data *pld); + +int omap_prm_assert_hardreset(u8 shift, u8 part, s16 prm_mod, u16 offset); +int omap_prm_deassert_hardreset(u8 shift, u8 st_shift, u8 part, s16 prm_mod, + u16 offset, u16 st_offset); +int omap_prm_is_hardreset_asserted(u8 shift, u8 part, s16 prm_mod, u16 offset); +extern u32 prm_read_reset_sources(void); +extern bool prm_was_any_context_lost_old(u8 part, s16 inst, u16 idx); +extern void prm_clear_context_loss_flags_old(u8 part, s16 inst, u16 idx); +void omap_prm_reset_system(void); + +void omap_prm_reconfigure_io_chain(void); +int omap_prm_clear_mod_irqs(s16 module, u8 regs, u32 wkst_mask); + +/* + * Voltage Processor (VP) identifiers + */ +#define OMAP3_VP_VDD_MPU_ID 0 +#define OMAP3_VP_VDD_CORE_ID 1 +#define OMAP4_VP_VDD_CORE_ID 0 +#define OMAP4_VP_VDD_IVA_ID 1 +#define OMAP4_VP_VDD_MPU_ID 2 + +u32 omap_prm_vp_check_txdone(u8 vp_id); +void omap_prm_vp_clear_txdone(u8 vp_id); + +#endif + + +#endif diff --git a/arch/arm/mach-omap2/prm2xxx.c b/arch/arm/mach-omap2/prm2xxx.c new file mode 100644 index 000000000..35a58f54b --- /dev/null +++ b/arch/arm/mach-omap2/prm2xxx.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2xxx PRM module functions + * + * Copyright (C) 2010-2012 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * Benoît Cousson + * Paul Walmsley + * Rajendra Nayak <rnayak@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/irq.h> + +#include "powerdomain.h" +#include "clockdomain.h" +#include "prm2xxx.h" +#include "cm2xxx_3xxx.h" +#include "prm-regbits-24xx.h" + +/* + * OMAP24xx PM_PWSTCTRL_*.POWERSTATE and PM_PWSTST_*.LASTSTATEENTERED bits - + * these are reversed from the bits used on OMAP3+ + */ +#define OMAP24XX_PWRDM_POWER_ON 0x0 +#define OMAP24XX_PWRDM_POWER_RET 0x1 +#define OMAP24XX_PWRDM_POWER_OFF 0x3 + +/* + * omap2xxx_prm_reset_src_map - map from bits in the PRM_RSTST_WKUP + * hardware register (which are specific to the OMAP2xxx SoCs) to + * reset source ID bit shifts (which is an OMAP SoC-independent + * enumeration) + */ +static struct prm_reset_src_map omap2xxx_prm_reset_src_map[] = { + { OMAP_GLOBALCOLD_RST_SHIFT, OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT }, + { OMAP_GLOBALWARM_RST_SHIFT, OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT }, + { OMAP24XX_SECU_VIOL_RST_SHIFT, OMAP_SECU_VIOL_RST_SRC_ID_SHIFT }, + { OMAP24XX_MPU_WD_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT }, + { OMAP24XX_SECU_WD_RST_SHIFT, OMAP_SECU_WD_RST_SRC_ID_SHIFT }, + { OMAP24XX_EXTWMPU_RST_SHIFT, OMAP_EXTWARM_RST_SRC_ID_SHIFT }, + { -1, -1 }, +}; + +/** + * omap2xxx_prm_read_reset_sources - return the last SoC reset source + * + * Return a u32 representing the last reset sources of the SoC. The + * returned reset source bits are standardized across OMAP SoCs. + */ +static u32 omap2xxx_prm_read_reset_sources(void) +{ + struct prm_reset_src_map *p; + u32 r = 0; + u32 v; + + v = omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTST); + + p = omap2xxx_prm_reset_src_map; + while (p->reg_shift >= 0 && p->std_shift >= 0) { + if (v & (1 << p->reg_shift)) + r |= 1 << p->std_shift; + p++; + } + + return r; +} + +/** + * omap2xxx_pwrst_to_common_pwrst - convert OMAP2xxx pwrst to common pwrst + * @omap2xxx_pwrst: OMAP2xxx hardware power state to convert + * + * Return the common power state bits corresponding to the OMAP2xxx + * hardware power state bits @omap2xxx_pwrst, or -EINVAL upon error. + */ +static int omap2xxx_pwrst_to_common_pwrst(u8 omap2xxx_pwrst) +{ + u8 pwrst; + + switch (omap2xxx_pwrst) { + case OMAP24XX_PWRDM_POWER_OFF: + pwrst = PWRDM_POWER_OFF; + break; + case OMAP24XX_PWRDM_POWER_RET: + pwrst = PWRDM_POWER_RET; + break; + case OMAP24XX_PWRDM_POWER_ON: + pwrst = PWRDM_POWER_ON; + break; + default: + return -EINVAL; + } + + return pwrst; +} + +/** + * omap2xxx_prm_dpll_reset - use DPLL reset to reboot the OMAP SoC + * + * Set the DPLL reset bit, which should reboot the SoC. This is the + * recommended way to restart the SoC. No return value. + */ +static void omap2xxx_prm_dpll_reset(void) +{ + omap2_prm_set_mod_reg_bits(OMAP_RST_DPLL3_MASK, WKUP_MOD, + OMAP2_RM_RSTCTRL); + /* OCP barrier */ + omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTCTRL); +} + +/** + * omap2xxx_prm_clear_mod_irqs - clear wakeup status bits for a module + * @module: PRM module to clear wakeups from + * @regs: register offset to clear + * @wkst_mask: wakeup status mask to clear + * + * Clears wakeup status bits for a given module, so that the device can + * re-enter idle. + */ +static int omap2xxx_prm_clear_mod_irqs(s16 module, u8 regs, u32 wkst_mask) +{ + u32 wkst; + + wkst = omap2_prm_read_mod_reg(module, regs); + wkst &= wkst_mask; + omap2_prm_write_mod_reg(wkst, module, regs); + return 0; +} + +int omap2xxx_clkdm_sleep(struct clockdomain *clkdm) +{ + omap2_prm_set_mod_reg_bits(OMAP24XX_FORCESTATE_MASK, + clkdm->pwrdm.ptr->prcm_offs, + OMAP2_PM_PWSTCTRL); + return 0; +} + +int omap2xxx_clkdm_wakeup(struct clockdomain *clkdm) +{ + omap2_prm_clear_mod_reg_bits(OMAP24XX_FORCESTATE_MASK, + clkdm->pwrdm.ptr->prcm_offs, + OMAP2_PM_PWSTCTRL); + return 0; +} + +static int omap2xxx_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) +{ + u8 omap24xx_pwrst; + + switch (pwrst) { + case PWRDM_POWER_OFF: + omap24xx_pwrst = OMAP24XX_PWRDM_POWER_OFF; + break; + case PWRDM_POWER_RET: + omap24xx_pwrst = OMAP24XX_PWRDM_POWER_RET; + break; + case PWRDM_POWER_ON: + omap24xx_pwrst = OMAP24XX_PWRDM_POWER_ON; + break; + default: + return -EINVAL; + } + + omap2_prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK, + (omap24xx_pwrst << OMAP_POWERSTATE_SHIFT), + pwrdm->prcm_offs, OMAP2_PM_PWSTCTRL); + return 0; +} + +static int omap2xxx_pwrdm_read_next_pwrst(struct powerdomain *pwrdm) +{ + u8 omap2xxx_pwrst; + + omap2xxx_pwrst = omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL, + OMAP_POWERSTATE_MASK); + + return omap2xxx_pwrst_to_common_pwrst(omap2xxx_pwrst); +} + +static int omap2xxx_pwrdm_read_pwrst(struct powerdomain *pwrdm) +{ + u8 omap2xxx_pwrst; + + omap2xxx_pwrst = omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP2_PM_PWSTST, + OMAP_POWERSTATEST_MASK); + + return omap2xxx_pwrst_to_common_pwrst(omap2xxx_pwrst); +} + +struct pwrdm_ops omap2_pwrdm_operations = { + .pwrdm_set_next_pwrst = omap2xxx_pwrdm_set_next_pwrst, + .pwrdm_read_next_pwrst = omap2xxx_pwrdm_read_next_pwrst, + .pwrdm_read_pwrst = omap2xxx_pwrdm_read_pwrst, + .pwrdm_set_logic_retst = omap2_pwrdm_set_logic_retst, + .pwrdm_set_mem_onst = omap2_pwrdm_set_mem_onst, + .pwrdm_set_mem_retst = omap2_pwrdm_set_mem_retst, + .pwrdm_read_mem_pwrst = omap2_pwrdm_read_mem_pwrst, + .pwrdm_read_mem_retst = omap2_pwrdm_read_mem_retst, + .pwrdm_wait_transition = omap2_pwrdm_wait_transition, +}; + +/* + * + */ + +static struct prm_ll_data omap2xxx_prm_ll_data = { + .read_reset_sources = &omap2xxx_prm_read_reset_sources, + .assert_hardreset = &omap2_prm_assert_hardreset, + .deassert_hardreset = &omap2_prm_deassert_hardreset, + .is_hardreset_asserted = &omap2_prm_is_hardreset_asserted, + .reset_system = &omap2xxx_prm_dpll_reset, + .clear_mod_irqs = &omap2xxx_prm_clear_mod_irqs, +}; + +int __init omap2xxx_prm_init(const struct omap_prcm_init_data *data) +{ + return prm_register(&omap2xxx_prm_ll_data); +} + +static void __exit omap2xxx_prm_exit(void) +{ + prm_unregister(&omap2xxx_prm_ll_data); +} +__exitcall(omap2xxx_prm_exit); diff --git a/arch/arm/mach-omap2/prm2xxx.h b/arch/arm/mach-omap2/prm2xxx.h new file mode 100644 index 000000000..659b91734 --- /dev/null +++ b/arch/arm/mach-omap2/prm2xxx.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2xxx Power/Reset Management (PRM) register definitions + * + * Copyright (C) 2007-2009, 2011-2012 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * Paul Walmsley + * + * The PRM hardware modules on the OMAP2/3 are quite similar to each + * other. The PRM on OMAP4 has a new register layout, and is handled + * in a separate file. + */ +#ifndef __ARCH_ARM_MACH_OMAP2_PRM2XXX_H +#define __ARCH_ARM_MACH_OMAP2_PRM2XXX_H + +#include "prcm-common.h" +#include "prm.h" +#include "prm2xxx_3xxx.h" + +#define OMAP2420_PRM_REGADDR(module, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP2420_PRM_BASE + (module) + (reg)) +#define OMAP2430_PRM_REGADDR(module, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP2430_PRM_BASE + (module) + (reg)) + +/* + * OMAP2-specific global PRM registers + * Use {read,write}l_relaxed() with these registers. + * + * With a few exceptions, these are the register names beginning with + * PRCM_* on 24xx. (The exceptions are the IRQSTATUS and IRQENABLE + * bits.) + * + */ + +#define OMAP2_PRCM_REVISION_OFFSET 0x0000 +#define OMAP2420_PRCM_REVISION OMAP2420_PRM_REGADDR(OCP_MOD, 0x0000) +#define OMAP2_PRCM_SYSCONFIG_OFFSET 0x0010 +#define OMAP2420_PRCM_SYSCONFIG OMAP2420_PRM_REGADDR(OCP_MOD, 0x0010) + +#define OMAP2_PRCM_IRQSTATUS_MPU_OFFSET 0x0018 +#define OMAP2420_PRCM_IRQSTATUS_MPU OMAP2420_PRM_REGADDR(OCP_MOD, 0x0018) +#define OMAP2_PRCM_IRQENABLE_MPU_OFFSET 0x001c +#define OMAP2420_PRCM_IRQENABLE_MPU OMAP2420_PRM_REGADDR(OCP_MOD, 0x001c) + +#define OMAP2_PRCM_VOLTCTRL_OFFSET 0x0050 +#define OMAP2420_PRCM_VOLTCTRL OMAP2420_PRM_REGADDR(OCP_MOD, 0x0050) +#define OMAP2_PRCM_VOLTST_OFFSET 0x0054 +#define OMAP2420_PRCM_VOLTST OMAP2420_PRM_REGADDR(OCP_MOD, 0x0054) +#define OMAP2_PRCM_CLKSRC_CTRL_OFFSET 0x0060 +#define OMAP2420_PRCM_CLKSRC_CTRL OMAP2420_PRM_REGADDR(OCP_MOD, 0x0060) +#define OMAP2_PRCM_CLKOUT_CTRL_OFFSET 0x0070 +#define OMAP2420_PRCM_CLKOUT_CTRL OMAP2420_PRM_REGADDR(OCP_MOD, 0x0070) +#define OMAP2_PRCM_CLKEMUL_CTRL_OFFSET 0x0078 +#define OMAP2420_PRCM_CLKEMUL_CTRL OMAP2420_PRM_REGADDR(OCP_MOD, 0x0078) +#define OMAP2_PRCM_CLKCFG_CTRL_OFFSET 0x0080 +#define OMAP2420_PRCM_CLKCFG_CTRL OMAP2420_PRM_REGADDR(OCP_MOD, 0x0080) +#define OMAP2_PRCM_CLKCFG_STATUS_OFFSET 0x0084 +#define OMAP2420_PRCM_CLKCFG_STATUS OMAP2420_PRM_REGADDR(OCP_MOD, 0x0084) +#define OMAP2_PRCM_VOLTSETUP_OFFSET 0x0090 +#define OMAP2420_PRCM_VOLTSETUP OMAP2420_PRM_REGADDR(OCP_MOD, 0x0090) +#define OMAP2_PRCM_CLKSSETUP_OFFSET 0x0094 +#define OMAP2420_PRCM_CLKSSETUP OMAP2420_PRM_REGADDR(OCP_MOD, 0x0094) +#define OMAP2_PRCM_POLCTRL_OFFSET 0x0098 +#define OMAP2420_PRCM_POLCTRL OMAP2420_PRM_REGADDR(OCP_MOD, 0x0098) + +#define OMAP2430_PRCM_REVISION OMAP2430_PRM_REGADDR(OCP_MOD, 0x0000) +#define OMAP2430_PRCM_SYSCONFIG OMAP2430_PRM_REGADDR(OCP_MOD, 0x0010) + +#define OMAP2430_PRCM_IRQSTATUS_MPU OMAP2430_PRM_REGADDR(OCP_MOD, 0x0018) +#define OMAP2430_PRCM_IRQENABLE_MPU OMAP2430_PRM_REGADDR(OCP_MOD, 0x001c) + +#define OMAP2430_PRCM_VOLTCTRL OMAP2430_PRM_REGADDR(OCP_MOD, 0x0050) +#define OMAP2430_PRCM_VOLTST OMAP2430_PRM_REGADDR(OCP_MOD, 0x0054) +#define OMAP2430_PRCM_CLKSRC_CTRL OMAP2430_PRM_REGADDR(OCP_MOD, 0x0060) +#define OMAP2430_PRCM_CLKOUT_CTRL OMAP2430_PRM_REGADDR(OCP_MOD, 0x0070) +#define OMAP2430_PRCM_CLKEMUL_CTRL OMAP2430_PRM_REGADDR(OCP_MOD, 0x0078) +#define OMAP2430_PRCM_CLKCFG_CTRL OMAP2430_PRM_REGADDR(OCP_MOD, 0x0080) +#define OMAP2430_PRCM_CLKCFG_STATUS OMAP2430_PRM_REGADDR(OCP_MOD, 0x0084) +#define OMAP2430_PRCM_VOLTSETUP OMAP2430_PRM_REGADDR(OCP_MOD, 0x0090) +#define OMAP2430_PRCM_CLKSSETUP OMAP2430_PRM_REGADDR(OCP_MOD, 0x0094) +#define OMAP2430_PRCM_POLCTRL OMAP2430_PRM_REGADDR(OCP_MOD, 0x0098) + +/* + * Module specific PRM register offsets from PRM_BASE + domain offset + * + * Use prm_{read,write}_mod_reg() with these registers. + * + * With a few exceptions, these are the register names beginning with + * {PM,RM}_* on both OMAP2/3 SoC families.. (The exceptions are the + * IRQSTATUS and IRQENABLE bits.) + */ + +/* Register offsets appearing on both OMAP2 and OMAP3 */ + +#define OMAP2_RM_RSTCTRL 0x0050 +#define OMAP2_RM_RSTTIME 0x0054 +#define OMAP2_RM_RSTST 0x0058 +#define OMAP2_PM_PWSTCTRL 0x00e0 +#define OMAP2_PM_PWSTST 0x00e4 + +#define PM_WKEN 0x00a0 +#define PM_WKEN1 PM_WKEN +#define PM_WKST 0x00b0 +#define PM_WKST1 PM_WKST +#define PM_WKDEP 0x00c8 +#define PM_EVGENCTRL 0x00d4 +#define PM_EVGENONTIM 0x00d8 +#define PM_EVGENOFFTIM 0x00dc + +/* OMAP2xxx specific register offsets */ +#define OMAP24XX_PM_WKEN2 0x00a4 +#define OMAP24XX_PM_WKST2 0x00b4 + +#define OMAP24XX_PRCM_IRQSTATUS_DSP 0x00f0 /* IVA mod */ +#define OMAP24XX_PRCM_IRQENABLE_DSP 0x00f4 /* IVA mod */ +#define OMAP24XX_PRCM_IRQSTATUS_IVA 0x00f8 +#define OMAP24XX_PRCM_IRQENABLE_IVA 0x00fc + +#ifndef __ASSEMBLER__ +/* Function prototypes */ +extern int omap2xxx_clkdm_sleep(struct clockdomain *clkdm); +extern int omap2xxx_clkdm_wakeup(struct clockdomain *clkdm); + +int __init omap2xxx_prm_init(const struct omap_prcm_init_data *data); + +#endif + +#endif diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.c b/arch/arm/mach-omap2/prm2xxx_3xxx.c new file mode 100644 index 000000000..d983efac6 --- /dev/null +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2/3 PRM module functions + * + * Copyright (C) 2010-2011 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * Benoît Cousson + * Paul Walmsley + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "powerdomain.h" +#include "prm2xxx_3xxx.h" +#include "prm-regbits-24xx.h" +#include "clockdomain.h" + +/** + * omap2_prm_is_hardreset_asserted - read the HW reset line state of + * submodules contained in the hwmod module + * @shift: register bit shift corresponding to the reset line to check + * @part: PRM partition, ignored for OMAP2 + * @prm_mod: PRM submodule base (e.g. CORE_MOD) + * @offset: register offset, ignored for OMAP2 + * + * Returns 1 if the (sub)module hardreset line is currently asserted, + * 0 if the (sub)module hardreset line is not currently asserted, or + * -EINVAL if called while running on a non-OMAP2/3 chip. + */ +int omap2_prm_is_hardreset_asserted(u8 shift, u8 part, s16 prm_mod, u16 offset) +{ + return omap2_prm_read_mod_bits_shift(prm_mod, OMAP2_RM_RSTCTRL, + (1 << shift)); +} + +/** + * omap2_prm_assert_hardreset - assert the HW reset line of a submodule + * @shift: register bit shift corresponding to the reset line to assert + * @part: PRM partition, ignored for OMAP2 + * @prm_mod: PRM submodule base (e.g. CORE_MOD) + * @offset: register offset, ignored for OMAP2 + * + * Some IPs like dsp or iva contain processors that require an HW + * reset line to be asserted / deasserted in order to fully enable the + * IP. These modules may have multiple hard-reset lines that reset + * different 'submodules' inside the IP block. This function will + * place the submodule into reset. Returns 0 upon success or -EINVAL + * upon an argument error. + */ +int omap2_prm_assert_hardreset(u8 shift, u8 part, s16 prm_mod, u16 offset) +{ + u32 mask; + + mask = 1 << shift; + omap2_prm_rmw_mod_reg_bits(mask, mask, prm_mod, OMAP2_RM_RSTCTRL); + + return 0; +} + +/** + * omap2_prm_deassert_hardreset - deassert a submodule hardreset line and wait + * @prm_mod: PRM submodule base (e.g. CORE_MOD) + * @rst_shift: register bit shift corresponding to the reset line to deassert + * @st_shift: register bit shift for the status of the deasserted submodule + * @part: PRM partition, not used for OMAP2 + * @prm_mod: PRM submodule base (e.g. CORE_MOD) + * @rst_offset: reset register offset, not used for OMAP2 + * @st_offset: reset status register offset, not used for OMAP2 + * + * Some IPs like dsp or iva contain processors that require an HW + * reset line to be asserted / deasserted in order to fully enable the + * IP. These modules may have multiple hard-reset lines that reset + * different 'submodules' inside the IP block. This function will + * take the submodule out of reset and wait until the PRCM indicates + * that the reset has completed before returning. Returns 0 upon success or + * -EINVAL upon an argument error, -EEXIST if the submodule was already out + * of reset, or -EBUSY if the submodule did not exit reset promptly. + */ +int omap2_prm_deassert_hardreset(u8 rst_shift, u8 st_shift, u8 part, + s16 prm_mod, u16 rst_offset, u16 st_offset) +{ + u32 rst, st; + int c; + + rst = 1 << rst_shift; + st = 1 << st_shift; + + /* Check the current status to avoid de-asserting the line twice */ + if (omap2_prm_read_mod_bits_shift(prm_mod, OMAP2_RM_RSTCTRL, rst) == 0) + return -EEXIST; + + /* Clear the reset status by writing 1 to the status bit */ + omap2_prm_rmw_mod_reg_bits(0xffffffff, st, prm_mod, OMAP2_RM_RSTST); + /* de-assert the reset control line */ + omap2_prm_rmw_mod_reg_bits(rst, 0, prm_mod, OMAP2_RM_RSTCTRL); + /* wait the status to be set */ + omap_test_timeout(omap2_prm_read_mod_bits_shift(prm_mod, OMAP2_RM_RSTST, + st), + MAX_MODULE_HARDRESET_WAIT, c); + + return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0; +} + + +/* Powerdomain low-level functions */ + +/* Common functions across OMAP2 and OMAP3 */ +int omap2_pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst) +{ + u32 m; + + m = omap2_pwrdm_get_mem_bank_onstate_mask(bank); + + omap2_prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL); + + return 0; +} + +int omap2_pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst) +{ + u32 m; + + m = omap2_pwrdm_get_mem_bank_retst_mask(bank); + + omap2_prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL); + + return 0; +} + +int omap2_pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m; + + m = omap2_pwrdm_get_mem_bank_stst_mask(bank); + + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP2_PM_PWSTST, + m); +} + +int omap2_pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m; + + m = omap2_pwrdm_get_mem_bank_retst_mask(bank); + + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL, m); +} + +int omap2_pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) +{ + u32 v; + + v = pwrst << __ffs(OMAP_LOGICRETSTATE_MASK); + omap2_prm_rmw_mod_reg_bits(OMAP_LOGICRETSTATE_MASK, v, pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL); + + return 0; +} + +int omap2_pwrdm_wait_transition(struct powerdomain *pwrdm) +{ + u32 c = 0; + + /* + * REVISIT: pwrdm_wait_transition() may be better implemented + * via a callback and a periodic timer check -- how long do we expect + * powerdomain transitions to take? + */ + + /* XXX Is this udelay() value meaningful? */ + while ((omap2_prm_read_mod_reg(pwrdm->prcm_offs, OMAP2_PM_PWSTST) & + OMAP_INTRANSITION_MASK) && + (c++ < PWRDM_TRANSITION_BAILOUT)) + udelay(1); + + if (c > PWRDM_TRANSITION_BAILOUT) { + pr_err("powerdomain: %s: waited too long to complete transition\n", + pwrdm->name); + return -EAGAIN; + } + + pr_debug("powerdomain: completed transition in %d loops\n", c); + + return 0; +} + +int omap2_clkdm_add_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + omap2_prm_set_mod_reg_bits((1 << clkdm2->dep_bit), + clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); + return 0; +} + +int omap2_clkdm_del_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + omap2_prm_clear_mod_reg_bits((1 << clkdm2->dep_bit), + clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); + return 0; +} + +int omap2_clkdm_read_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2) +{ + return omap2_prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, + PM_WKDEP, (1 << clkdm2->dep_bit)); +} + +/* XXX Caller must hold the clkdm's powerdomain lock */ +int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm) +{ + struct clkdm_dep *cd; + u32 mask = 0; + + for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) { + if (!cd->clkdm) + continue; /* only happens if data is erroneous */ + + /* PRM accesses are slow, so minimize them */ + mask |= 1 << cd->clkdm->dep_bit; + cd->wkdep_usecount = 0; + } + + omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, + PM_WKDEP); + return 0; +} + diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.h b/arch/arm/mach-omap2/prm2xxx_3xxx.h new file mode 100644 index 000000000..3d803f718 --- /dev/null +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.h @@ -0,0 +1,245 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP2xxx/3xxx-common Power/Reset Management (PRM) register definitions + * + * Copyright (C) 2007-2009, 2011-2012 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * Paul Walmsley + * + * The PRM hardware modules on the OMAP2/3 are quite similar to each + * other. The PRM on OMAP4 has a new register layout, and is handled + * in a separate file. + */ +#ifndef __ARCH_ARM_MACH_OMAP2_PRM2XXX_3XXX_H +#define __ARCH_ARM_MACH_OMAP2_PRM2XXX_3XXX_H + +#include "prcm-common.h" +#include "prm.h" + +/* + * Module specific PRM register offsets from PRM_BASE + domain offset + * + * Use prm_{read,write}_mod_reg() with these registers. + * + * With a few exceptions, these are the register names beginning with + * {PM,RM}_* on both OMAP2/3 SoC families.. (The exceptions are the + * IRQSTATUS and IRQENABLE bits.) + */ + +/* Register offsets appearing on both OMAP2 and OMAP3 */ + +#define OMAP2_RM_RSTCTRL 0x0050 +#define OMAP2_RM_RSTTIME 0x0054 +#define OMAP2_RM_RSTST 0x0058 +#define OMAP2_PM_PWSTCTRL 0x00e0 +#define OMAP2_PM_PWSTST 0x00e4 + +#define PM_WKEN 0x00a0 +#define PM_WKEN1 PM_WKEN +#define PM_WKST 0x00b0 +#define PM_WKST1 PM_WKST +#define PM_WKDEP 0x00c8 +#define PM_EVGENCTRL 0x00d4 +#define PM_EVGENONTIM 0x00d8 +#define PM_EVGENOFFTIM 0x00dc + + +#ifndef __ASSEMBLER__ + +#include <linux/io.h> +#include "powerdomain.h" + +/* Power/reset management domain register get/set */ +static inline u32 omap2_prm_read_mod_reg(s16 module, u16 idx) +{ + return readl_relaxed(prm_base.va + module + idx); +} + +static inline void omap2_prm_write_mod_reg(u32 val, s16 module, u16 idx) +{ + writel_relaxed(val, prm_base.va + module + idx); +} + +/* Read-modify-write a register in a PRM module. Caller must lock */ +static inline u32 omap2_prm_rmw_mod_reg_bits(u32 mask, u32 bits, s16 module, + s16 idx) +{ + u32 v; + + v = omap2_prm_read_mod_reg(module, idx); + v &= ~mask; + v |= bits; + omap2_prm_write_mod_reg(v, module, idx); + + return v; +} + +/* Read a PRM register, AND it, and shift the result down to bit 0 */ +static inline u32 omap2_prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask) +{ + u32 v; + + v = omap2_prm_read_mod_reg(domain, idx); + v &= mask; + v >>= __ffs(mask); + + return v; +} + +static inline u32 omap2_prm_set_mod_reg_bits(u32 bits, s16 module, s16 idx) +{ + return omap2_prm_rmw_mod_reg_bits(bits, bits, module, idx); +} + +static inline u32 omap2_prm_clear_mod_reg_bits(u32 bits, s16 module, s16 idx) +{ + return omap2_prm_rmw_mod_reg_bits(bits, 0x0, module, idx); +} + +/* These omap2_ PRM functions apply to both OMAP2 and 3 */ +int omap2_prm_is_hardreset_asserted(u8 shift, u8 part, s16 prm_mod, u16 offset); +int omap2_prm_assert_hardreset(u8 shift, u8 part, s16 prm_mod, + u16 offset); +int omap2_prm_deassert_hardreset(u8 rst_shift, u8 st_shift, u8 part, + s16 prm_mod, u16 reset_offset, + u16 st_offset); + +extern int omap2_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst); +extern int omap2_pwrdm_read_next_pwrst(struct powerdomain *pwrdm); +extern int omap2_pwrdm_read_pwrst(struct powerdomain *pwrdm); +extern int omap2_pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst); +extern int omap2_pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst); +extern int omap2_pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank); +extern int omap2_pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank); +extern int omap2_pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst); +extern int omap2_pwrdm_wait_transition(struct powerdomain *pwrdm); + +extern int omap2_clkdm_add_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2); +extern int omap2_clkdm_del_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2); +extern int omap2_clkdm_read_wkdep(struct clockdomain *clkdm1, + struct clockdomain *clkdm2); +extern int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm); + +#endif /* __ASSEMBLER */ + +/* + * Bits common to specific registers + * + * The 3430 register and bit names are generally used, + * since they tend to make more sense + */ + +/* PM_EVGENONTIM_MPU */ +/* Named PM_EVEGENONTIM_MPU on the 24XX */ +#define OMAP_ONTIMEVAL_SHIFT 0 +#define OMAP_ONTIMEVAL_MASK (0xffffffff << 0) + +/* PM_EVGENOFFTIM_MPU */ +/* Named PM_EVEGENOFFTIM_MPU on the 24XX */ +#define OMAP_OFFTIMEVAL_SHIFT 0 +#define OMAP_OFFTIMEVAL_MASK (0xffffffff << 0) + +/* PRM_CLKSETUP and PRCM_VOLTSETUP */ +/* Named PRCM_CLKSSETUP on the 24XX */ +#define OMAP_SETUP_TIME_SHIFT 0 +#define OMAP_SETUP_TIME_MASK (0xffff << 0) + +/* PRM_CLKSRC_CTRL */ +/* Named PRCM_CLKSRC_CTRL on the 24XX */ +#define OMAP_SYSCLKDIV_SHIFT 6 +#define OMAP_SYSCLKDIV_MASK (0x3 << 6) +#define OMAP_SYSCLKDIV_WIDTH 2 +#define OMAP_AUTOEXTCLKMODE_SHIFT 3 +#define OMAP_AUTOEXTCLKMODE_MASK (0x3 << 3) +#define OMAP_SYSCLKSEL_SHIFT 0 +#define OMAP_SYSCLKSEL_MASK (0x3 << 0) + +/* PM_EVGENCTRL_MPU */ +#define OMAP_OFFLOADMODE_SHIFT 3 +#define OMAP_OFFLOADMODE_MASK (0x3 << 3) +#define OMAP_ONLOADMODE_SHIFT 1 +#define OMAP_ONLOADMODE_MASK (0x3 << 1) +#define OMAP_ENABLE_MASK (1 << 0) + +/* PRM_RSTTIME */ +/* Named RM_RSTTIME_WKUP on the 24xx */ +#define OMAP_RSTTIME2_SHIFT 8 +#define OMAP_RSTTIME2_MASK (0x1f << 8) +#define OMAP_RSTTIME1_SHIFT 0 +#define OMAP_RSTTIME1_MASK (0xff << 0) + +/* PRM_RSTCTRL */ +/* Named RM_RSTCTRL_WKUP on the 24xx */ +/* 2420 calls RST_DPLL3 'RST_DPLL' */ +#define OMAP_RST_DPLL3_MASK (1 << 2) +#define OMAP_RST_GS_MASK (1 << 1) + + +/* + * Bits common to module-shared registers + * + * Not all registers of a particular type support all of these bits - + * check TRM if you are unsure + */ + +/* + * 24XX: RM_RSTST_MPU and RM_RSTST_DSP - on 24XX, 'COREDOMAINWKUP_RST' is + * called 'COREWKUP_RST' + * + * 3430: RM_RSTST_IVA2, RM_RSTST_MPU, RM_RSTST_GFX, RM_RSTST_DSS, + * RM_RSTST_CAM, RM_RSTST_PER, RM_RSTST_NEON + */ +#define OMAP_COREDOMAINWKUP_RST_MASK (1 << 3) + +/* + * 24XX: RM_RSTST_MPU, RM_RSTST_GFX, RM_RSTST_DSP + * + * 2430: RM_RSTST_MDM + * + * 3430: RM_RSTST_CORE, RM_RSTST_EMU + */ +#define OMAP_DOMAINWKUP_RST_MASK (1 << 2) + +/* + * 24XX: RM_RSTST_MPU, RM_RSTST_WKUP, RM_RSTST_DSP + * On 24XX, 'GLOBALWARM_RST' is called 'GLOBALWMPU_RST'. + * + * 2430: RM_RSTST_MDM + * + * 3430: RM_RSTST_CORE, RM_RSTST_EMU + */ +#define OMAP_GLOBALWARM_RST_SHIFT 1 +#define OMAP_GLOBALWARM_RST_MASK (1 << 1) +#define OMAP_GLOBALCOLD_RST_SHIFT 0 +#define OMAP_GLOBALCOLD_RST_MASK (1 << 0) + +/* + * 24XX: PM_WKDEP_GFX, PM_WKDEP_MPU, PM_WKDEP_CORE, PM_WKDEP_DSP + * 2420 TRM sometimes uses "EN_WAKEUP" instead of "EN_WKUP" + * + * 2430: PM_WKDEP_MDM + * + * 3430: PM_WKDEP_IVA2, PM_WKDEP_GFX, PM_WKDEP_DSS, PM_WKDEP_CAM, + * PM_WKDEP_PER + */ +#define OMAP_EN_WKUP_SHIFT 4 +#define OMAP_EN_WKUP_MASK (1 << 4) + +/* + * 24XX: PM_PWSTCTRL_MPU, PM_PWSTCTRL_CORE, PM_PWSTCTRL_GFX, + * PM_PWSTCTRL_DSP + * + * 2430: PM_PWSTCTRL_MDM + * + * 3430: PM_PWSTCTRL_IVA2, PM_PWSTCTRL_CORE, PM_PWSTCTRL_GFX, + * PM_PWSTCTRL_DSS, PM_PWSTCTRL_CAM, PM_PWSTCTRL_PER, + * PM_PWSTCTRL_NEON + */ +#define OMAP_LOGICRETSTATE_MASK (1 << 2) + + +#endif diff --git a/arch/arm/mach-omap2/prm33xx.c b/arch/arm/mach-omap2/prm33xx.c new file mode 100644 index 000000000..4b65a0f9c --- /dev/null +++ b/arch/arm/mach-omap2/prm33xx.c @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AM33XX PRM functions + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "powerdomain.h" +#include "prm33xx.h" +#include "prm-regbits-33xx.h" + +#define AM33XX_PRM_RSTCTRL_OFFSET 0x0000 + +#define AM33XX_RST_GLOBAL_WARM_SW_MASK (1 << 0) + +/* Read a register in a PRM instance */ +static u32 am33xx_prm_read_reg(s16 inst, u16 idx) +{ + return readl_relaxed(prm_base.va + inst + idx); +} + +/* Write into a register in a PRM instance */ +static void am33xx_prm_write_reg(u32 val, s16 inst, u16 idx) +{ + writel_relaxed(val, prm_base.va + inst + idx); +} + +/* Read-modify-write a register in PRM. Caller must lock */ +static u32 am33xx_prm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx) +{ + u32 v; + + v = am33xx_prm_read_reg(inst, idx); + v &= ~mask; + v |= bits; + am33xx_prm_write_reg(v, inst, idx); + + return v; +} + +/** + * am33xx_prm_is_hardreset_asserted - read the HW reset line state of + * submodules contained in the hwmod module + * @shift: register bit shift corresponding to the reset line to check + * @part: PRM partition, ignored for AM33xx + * @inst: CM instance register offset (*_INST macro) + * @rstctrl_offs: RM_RSTCTRL register address offset for this module + * + * Returns 1 if the (sub)module hardreset line is currently asserted, + * 0 if the (sub)module hardreset line is not currently asserted, or + * -EINVAL upon parameter error. + */ +static int am33xx_prm_is_hardreset_asserted(u8 shift, u8 part, s16 inst, + u16 rstctrl_offs) +{ + u32 v; + + v = am33xx_prm_read_reg(inst, rstctrl_offs); + v &= 1 << shift; + v >>= shift; + + return v; +} + +/** + * am33xx_prm_assert_hardreset - assert the HW reset line of a submodule + * @shift: register bit shift corresponding to the reset line to assert + * @part: CM partition, ignored for AM33xx + * @inst: CM instance register offset (*_INST macro) + * @rstctrl_reg: RM_RSTCTRL register address for this module + * + * Some IPs like dsp, ipu or iva contain processors that require an HW + * reset line to be asserted / deasserted in order to fully enable the + * IP. These modules may have multiple hard-reset lines that reset + * different 'submodules' inside the IP block. This function will + * place the submodule into reset. Returns 0 upon success or -EINVAL + * upon an argument error. + */ +static int am33xx_prm_assert_hardreset(u8 shift, u8 part, s16 inst, + u16 rstctrl_offs) +{ + u32 mask = 1 << shift; + + am33xx_prm_rmw_reg_bits(mask, mask, inst, rstctrl_offs); + + return 0; +} + +/** + * am33xx_prm_deassert_hardreset - deassert a submodule hardreset line and + * wait + * @shift: register bit shift corresponding to the reset line to deassert + * @st_shift: reset status register bit shift corresponding to the reset line + * @part: PRM partition, not used for AM33xx + * @inst: CM instance register offset (*_INST macro) + * @rstctrl_reg: RM_RSTCTRL register address for this module + * @rstst_reg: RM_RSTST register address for this module + * + * Some IPs like dsp, ipu or iva contain processors that require an HW + * reset line to be asserted / deasserted in order to fully enable the + * IP. These modules may have multiple hard-reset lines that reset + * different 'submodules' inside the IP block. This function will + * take the submodule out of reset and wait until the PRCM indicates + * that the reset has completed before returning. Returns 0 upon success or + * -EINVAL upon an argument error, -EEXIST if the submodule was already out + * of reset, or -EBUSY if the submodule did not exit reset promptly. + */ +static int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, u8 part, + s16 inst, u16 rstctrl_offs, + u16 rstst_offs) +{ + int c; + u32 mask = 1 << st_shift; + + /* Check the current status to avoid de-asserting the line twice */ + if (am33xx_prm_is_hardreset_asserted(shift, 0, inst, rstctrl_offs) == 0) + return -EEXIST; + + /* Clear the reset status by writing 1 to the status bit */ + am33xx_prm_rmw_reg_bits(0xffffffff, mask, inst, rstst_offs); + + /* de-assert the reset control line */ + mask = 1 << shift; + + am33xx_prm_rmw_reg_bits(mask, 0, inst, rstctrl_offs); + + /* wait the status to be set */ + omap_test_timeout(am33xx_prm_is_hardreset_asserted(st_shift, 0, inst, + rstst_offs), + MAX_MODULE_HARDRESET_WAIT, c); + + return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0; +} + +static int am33xx_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) +{ + am33xx_prm_rmw_reg_bits(OMAP_POWERSTATE_MASK, + (pwrst << OMAP_POWERSTATE_SHIFT), + pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + return 0; +} + +static int am33xx_pwrdm_read_next_pwrst(struct powerdomain *pwrdm) +{ + u32 v; + + v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + v &= OMAP_POWERSTATE_MASK; + v >>= OMAP_POWERSTATE_SHIFT; + + return v; +} + +static int am33xx_pwrdm_read_pwrst(struct powerdomain *pwrdm) +{ + u32 v; + + v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs); + v &= OMAP_POWERSTATEST_MASK; + v >>= OMAP_POWERSTATEST_SHIFT; + + return v; +} + +static int am33xx_pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm) +{ + am33xx_prm_rmw_reg_bits(AM33XX_LOWPOWERSTATECHANGE_MASK, + (1 << AM33XX_LOWPOWERSTATECHANGE_SHIFT), + pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + return 0; +} + +static int am33xx_pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) +{ + am33xx_prm_rmw_reg_bits(AM33XX_LASTPOWERSTATEENTERED_MASK, + AM33XX_LASTPOWERSTATEENTERED_MASK, + pwrdm->prcm_offs, pwrdm->pwrstst_offs); + return 0; +} + +static int am33xx_pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) +{ + u32 m; + + m = pwrdm->logicretstate_mask; + if (!m) + return -EINVAL; + + am33xx_prm_rmw_reg_bits(m, (pwrst << __ffs(m)), + pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + + return 0; +} + +static int am33xx_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) +{ + u32 v; + + v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs); + v &= AM33XX_LOGICSTATEST_MASK; + v >>= AM33XX_LOGICSTATEST_SHIFT; + + return v; +} + +static int am33xx_pwrdm_read_logic_retst(struct powerdomain *pwrdm) +{ + u32 v, m; + + m = pwrdm->logicretstate_mask; + if (!m) + return -EINVAL; + + v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + v &= m; + v >>= __ffs(m); + + return v; +} + +static int am33xx_pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst) +{ + u32 m; + + m = pwrdm->mem_on_mask[bank]; + if (!m) + return -EINVAL; + + am33xx_prm_rmw_reg_bits(m, (pwrst << __ffs(m)), + pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + + return 0; +} + +static int am33xx_pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst) +{ + u32 m; + + m = pwrdm->mem_ret_mask[bank]; + if (!m) + return -EINVAL; + + am33xx_prm_rmw_reg_bits(m, (pwrst << __ffs(m)), + pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + + return 0; +} + +static int am33xx_pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m, v; + + m = pwrdm->mem_pwrst_mask[bank]; + if (!m) + return -EINVAL; + + v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs); + v &= m; + v >>= __ffs(m); + + return v; +} + +static int am33xx_pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m, v; + + m = pwrdm->mem_retst_mask[bank]; + if (!m) + return -EINVAL; + + v = am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstctrl_offs); + v &= m; + v >>= __ffs(m); + + return v; +} + +static int am33xx_pwrdm_wait_transition(struct powerdomain *pwrdm) +{ + u32 c = 0; + + /* + * REVISIT: pwrdm_wait_transition() may be better implemented + * via a callback and a periodic timer check -- how long do we expect + * powerdomain transitions to take? + */ + + /* XXX Is this udelay() value meaningful? */ + while ((am33xx_prm_read_reg(pwrdm->prcm_offs, pwrdm->pwrstst_offs) + & OMAP_INTRANSITION_MASK) && + (c++ < PWRDM_TRANSITION_BAILOUT)) + udelay(1); + + if (c > PWRDM_TRANSITION_BAILOUT) { + pr_err("powerdomain: %s: waited too long to complete transition\n", + pwrdm->name); + return -EAGAIN; + } + + pr_debug("powerdomain: completed transition in %d loops\n", c); + + return 0; +} + +static int am33xx_check_vcvp(void) +{ + /* No VC/VP on am33xx devices */ + return 0; +} + +/** + * am33xx_prm_global_warm_sw_reset - reboot the device via warm reset + * + * Immediately reboots the device through warm reset. + */ +static void am33xx_prm_global_warm_sw_reset(void) +{ + am33xx_prm_rmw_reg_bits(AM33XX_RST_GLOBAL_WARM_SW_MASK, + AM33XX_RST_GLOBAL_WARM_SW_MASK, + AM33XX_PRM_DEVICE_MOD, + AM33XX_PRM_RSTCTRL_OFFSET); + + /* OCP barrier */ + (void)am33xx_prm_read_reg(AM33XX_PRM_DEVICE_MOD, + AM33XX_PRM_RSTCTRL_OFFSET); +} + +static void am33xx_pwrdm_save_context(struct powerdomain *pwrdm) +{ + pwrdm->context = am33xx_prm_read_reg(pwrdm->prcm_offs, + pwrdm->pwrstctrl_offs); + /* + * Do not save LOWPOWERSTATECHANGE, writing a 1 indicates a request, + * reading back a 1 indicates a request in progress. + */ + pwrdm->context &= ~AM33XX_LOWPOWERSTATECHANGE_MASK; +} + +static void am33xx_pwrdm_restore_context(struct powerdomain *pwrdm) +{ + int st, ctrl; + + st = am33xx_prm_read_reg(pwrdm->prcm_offs, + pwrdm->pwrstst_offs); + + am33xx_prm_write_reg(pwrdm->context, pwrdm->prcm_offs, + pwrdm->pwrstctrl_offs); + + /* Make sure we only wait for a transition if there is one */ + st &= OMAP_POWERSTATEST_MASK; + ctrl = OMAP_POWERSTATEST_MASK & pwrdm->context; + + if (st != ctrl) + am33xx_pwrdm_wait_transition(pwrdm); +} + +struct pwrdm_ops am33xx_pwrdm_operations = { + .pwrdm_set_next_pwrst = am33xx_pwrdm_set_next_pwrst, + .pwrdm_read_next_pwrst = am33xx_pwrdm_read_next_pwrst, + .pwrdm_read_pwrst = am33xx_pwrdm_read_pwrst, + .pwrdm_set_logic_retst = am33xx_pwrdm_set_logic_retst, + .pwrdm_read_logic_pwrst = am33xx_pwrdm_read_logic_pwrst, + .pwrdm_read_logic_retst = am33xx_pwrdm_read_logic_retst, + .pwrdm_clear_all_prev_pwrst = am33xx_pwrdm_clear_all_prev_pwrst, + .pwrdm_set_lowpwrstchange = am33xx_pwrdm_set_lowpwrstchange, + .pwrdm_read_mem_pwrst = am33xx_pwrdm_read_mem_pwrst, + .pwrdm_read_mem_retst = am33xx_pwrdm_read_mem_retst, + .pwrdm_set_mem_onst = am33xx_pwrdm_set_mem_onst, + .pwrdm_set_mem_retst = am33xx_pwrdm_set_mem_retst, + .pwrdm_wait_transition = am33xx_pwrdm_wait_transition, + .pwrdm_has_voltdm = am33xx_check_vcvp, + .pwrdm_save_context = am33xx_pwrdm_save_context, + .pwrdm_restore_context = am33xx_pwrdm_restore_context, +}; + +static struct prm_ll_data am33xx_prm_ll_data = { + .assert_hardreset = am33xx_prm_assert_hardreset, + .deassert_hardreset = am33xx_prm_deassert_hardreset, + .is_hardreset_asserted = am33xx_prm_is_hardreset_asserted, + .reset_system = am33xx_prm_global_warm_sw_reset, +}; + +int __init am33xx_prm_init(const struct omap_prcm_init_data *data) +{ + return prm_register(&am33xx_prm_ll_data); +} + +static void __exit am33xx_prm_exit(void) +{ + prm_unregister(&am33xx_prm_ll_data); +} +__exitcall(am33xx_prm_exit); diff --git a/arch/arm/mach-omap2/prm33xx.h b/arch/arm/mach-omap2/prm33xx.h new file mode 100644 index 000000000..3081f3deb --- /dev/null +++ b/arch/arm/mach-omap2/prm33xx.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AM33XX PRM instance offset macros + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRM33XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM33XX_H + +#include "prcm-common.h" +#include "prm.h" + +#define AM33XX_PRM_BASE 0x44E00000 + +#define AM33XX_PRM_REGADDR(inst, reg) \ + AM33XX_L4_WK_IO_ADDRESS(AM33XX_PRM_BASE + (inst) + (reg)) + + +/* PRM instances */ +#define AM33XX_PRM_OCP_SOCKET_MOD 0x0B00 +#define AM33XX_PRM_PER_MOD 0x0C00 +#define AM33XX_PRM_WKUP_MOD 0x0D00 +#define AM33XX_PRM_MPU_MOD 0x0E00 +#define AM33XX_PRM_DEVICE_MOD 0x0F00 +#define AM33XX_PRM_RTC_MOD 0x1000 +#define AM33XX_PRM_GFX_MOD 0x1100 +#define AM33XX_PRM_CEFUSE_MOD 0x1200 + +/* PRM.PER_PRM register offsets */ +#define AM33XX_PM_PER_PWRSTST_OFFSET 0x0008 +#define AM33XX_PM_PER_PWRSTST AM33XX_PRM_REGADDR(AM33XX_PRM_PER_MOD, 0x0008) +#define AM33XX_PM_PER_PWRSTCTRL_OFFSET 0x000c +#define AM33XX_PM_PER_PWRSTCTRL AM33XX_PRM_REGADDR(AM33XX_PRM_PER_MOD, 0x000c) + +/* PRM.WKUP_PRM register offsets */ +#define AM33XX_PM_WKUP_PWRSTCTRL_OFFSET 0x0004 +#define AM33XX_PM_WKUP_PWRSTCTRL AM33XX_PRM_REGADDR(AM33XX_PRM_WKUP_MOD, 0x0004) +#define AM33XX_PM_WKUP_PWRSTST_OFFSET 0x0008 +#define AM33XX_PM_WKUP_PWRSTST AM33XX_PRM_REGADDR(AM33XX_PRM_WKUP_MOD, 0x0008) + +/* PRM.MPU_PRM register offsets */ +#define AM33XX_PM_MPU_PWRSTCTRL_OFFSET 0x0000 +#define AM33XX_PM_MPU_PWRSTCTRL AM33XX_PRM_REGADDR(AM33XX_PRM_MPU_MOD, 0x0000) +#define AM33XX_PM_MPU_PWRSTST_OFFSET 0x0004 +#define AM33XX_PM_MPU_PWRSTST AM33XX_PRM_REGADDR(AM33XX_PRM_MPU_MOD, 0x0004) + +/* PRM.DEVICE_PRM register offsets */ +#define AM33XX_PRM_RSTCTRL_OFFSET 0x0000 +#define AM33XX_PRM_RSTCTRL AM33XX_PRM_REGADDR(AM33XX_PRM_DEVICE_MOD, 0x0000) + +/* PRM.RTC_PRM register offsets */ +#define AM33XX_PM_RTC_PWRSTCTRL_OFFSET 0x0000 +#define AM33XX_PM_RTC_PWRSTCTRL AM33XX_PRM_REGADDR(AM33XX_PRM_RTC_MOD, 0x0000) +#define AM33XX_PM_RTC_PWRSTST_OFFSET 0x0004 +#define AM33XX_PM_RTC_PWRSTST AM33XX_PRM_REGADDR(AM33XX_PRM_RTC_MOD, 0x0004) + +/* PRM.GFX_PRM register offsets */ +#define AM33XX_PM_GFX_PWRSTCTRL_OFFSET 0x0000 +#define AM33XX_PM_GFX_PWRSTCTRL AM33XX_PRM_REGADDR(AM33XX_PRM_GFX_MOD, 0x0000) +#define AM33XX_PM_GFX_PWRSTST_OFFSET 0x0010 +#define AM33XX_PM_GFX_PWRSTST AM33XX_PRM_REGADDR(AM33XX_PRM_GFX_MOD, 0x0010) + +/* PRM.CEFUSE_PRM register offsets */ +#define AM33XX_PM_CEFUSE_PWRSTCTRL_OFFSET 0x0000 +#define AM33XX_PM_CEFUSE_PWRSTCTRL AM33XX_PRM_REGADDR(AM33XX_PRM_CEFUSE_MOD, 0x0000) +#define AM33XX_PM_CEFUSE_PWRSTST_OFFSET 0x0004 +#define AM33XX_PM_CEFUSE_PWRSTST AM33XX_PRM_REGADDR(AM33XX_PRM_CEFUSE_MOD, 0x0004) + +#ifndef __ASSEMBLER__ +int am33xx_prm_init(const struct omap_prcm_init_data *data); + +#endif /* ASSEMBLER */ +#endif diff --git a/arch/arm/mach-omap2/prm3xxx.c b/arch/arm/mach-omap2/prm3xxx.c new file mode 100644 index 000000000..63e73e9b8 --- /dev/null +++ b/arch/arm/mach-omap2/prm3xxx.c @@ -0,0 +1,726 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3xxx PRM module functions + * + * Copyright (C) 2010-2012 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * Benoît Cousson + * Paul Walmsley + * Rajendra Nayak <rnayak@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of_irq.h> + +#include "soc.h" +#include "common.h" +#include "vp.h" +#include "powerdomain.h" +#include "prm3xxx.h" +#include "prm2xxx_3xxx.h" +#include "cm2xxx_3xxx.h" +#include "prm-regbits-34xx.h" +#include "cm3xxx.h" +#include "cm-regbits-34xx.h" +#include "clock.h" + +static void omap3xxx_prm_read_pending_irqs(unsigned long *events); +static void omap3xxx_prm_ocp_barrier(void); +static void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask); +static void omap3xxx_prm_restore_irqen(u32 *saved_mask); + +static const struct omap_prcm_irq omap3_prcm_irqs[] = { + OMAP_PRCM_IRQ("wkup", 0, 0), + OMAP_PRCM_IRQ("io", 9, 1), +}; + +static struct omap_prcm_irq_setup omap3_prcm_irq_setup = { + .ack = OMAP3_PRM_IRQSTATUS_MPU_OFFSET, + .mask = OMAP3_PRM_IRQENABLE_MPU_OFFSET, + .nr_regs = 1, + .irqs = omap3_prcm_irqs, + .nr_irqs = ARRAY_SIZE(omap3_prcm_irqs), + .irq = 11 + OMAP_INTC_START, + .read_pending_irqs = &omap3xxx_prm_read_pending_irqs, + .ocp_barrier = &omap3xxx_prm_ocp_barrier, + .save_and_clear_irqen = &omap3xxx_prm_save_and_clear_irqen, + .restore_irqen = &omap3xxx_prm_restore_irqen, + .reconfigure_io_chain = NULL, +}; + +/* + * omap3_prm_reset_src_map - map from bits in the PRM_RSTST hardware + * register (which are specific to OMAP3xxx SoCs) to reset source ID + * bit shifts (which is an OMAP SoC-independent enumeration) + */ +static struct prm_reset_src_map omap3xxx_prm_reset_src_map[] = { + { OMAP3430_GLOBAL_COLD_RST_SHIFT, OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT }, + { OMAP3430_GLOBAL_SW_RST_SHIFT, OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT }, + { OMAP3430_SECURITY_VIOL_RST_SHIFT, OMAP_SECU_VIOL_RST_SRC_ID_SHIFT }, + { OMAP3430_MPU_WD_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT }, + { OMAP3430_SECURE_WD_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT }, + { OMAP3430_EXTERNAL_WARM_RST_SHIFT, OMAP_EXTWARM_RST_SRC_ID_SHIFT }, + { OMAP3430_VDD1_VOLTAGE_MANAGER_RST_SHIFT, + OMAP_VDD_MPU_VM_RST_SRC_ID_SHIFT }, + { OMAP3430_VDD2_VOLTAGE_MANAGER_RST_SHIFT, + OMAP_VDD_CORE_VM_RST_SRC_ID_SHIFT }, + { OMAP3430_ICEPICK_RST_SHIFT, OMAP_ICEPICK_RST_SRC_ID_SHIFT }, + { OMAP3430_ICECRUSHER_RST_SHIFT, OMAP_ICECRUSHER_RST_SRC_ID_SHIFT }, + { -1, -1 }, +}; + +/* PRM VP */ + +/* + * struct omap3_vp - OMAP3 VP register access description. + * @tranxdone_status: VP_TRANXDONE_ST bitmask in PRM_IRQSTATUS_MPU reg + */ +struct omap3_vp { + u32 tranxdone_status; +}; + +static struct omap3_vp omap3_vp[] = { + [OMAP3_VP_VDD_MPU_ID] = { + .tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK, + }, + [OMAP3_VP_VDD_CORE_ID] = { + .tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK, + }, +}; + +#define MAX_VP_ID ARRAY_SIZE(omap3_vp); + +static u32 omap3_prm_vp_check_txdone(u8 vp_id) +{ + struct omap3_vp *vp = &omap3_vp[vp_id]; + u32 irqstatus; + + irqstatus = omap2_prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQSTATUS_MPU_OFFSET); + return irqstatus & vp->tranxdone_status; +} + +static void omap3_prm_vp_clear_txdone(u8 vp_id) +{ + struct omap3_vp *vp = &omap3_vp[vp_id]; + + omap2_prm_write_mod_reg(vp->tranxdone_status, + OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); +} + +u32 omap3_prm_vcvp_read(u8 offset) +{ + return omap2_prm_read_mod_reg(OMAP3430_GR_MOD, offset); +} + +void omap3_prm_vcvp_write(u32 val, u8 offset) +{ + omap2_prm_write_mod_reg(val, OMAP3430_GR_MOD, offset); +} + +u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset) +{ + return omap2_prm_rmw_mod_reg_bits(mask, bits, OMAP3430_GR_MOD, offset); +} + +/** + * omap3xxx_prm_dpll3_reset - use DPLL3 reset to reboot the OMAP SoC + * + * Set the DPLL3 reset bit, which should reboot the SoC. This is the + * recommended way to restart the SoC, considering Errata i520. No + * return value. + */ +static void omap3xxx_prm_dpll3_reset(void) +{ + omap2_prm_set_mod_reg_bits(OMAP_RST_DPLL3_MASK, OMAP3430_GR_MOD, + OMAP2_RM_RSTCTRL); + /* OCP barrier */ + omap2_prm_read_mod_reg(OMAP3430_GR_MOD, OMAP2_RM_RSTCTRL); +} + +/** + * omap3xxx_prm_read_pending_irqs - read pending PRM MPU IRQs into @events + * @events: ptr to a u32, preallocated by caller + * + * Read PRM_IRQSTATUS_MPU bits, AND'ed with the currently-enabled PRM + * MPU IRQs, and store the result into the u32 pointed to by @events. + * No return value. + */ +static void omap3xxx_prm_read_pending_irqs(unsigned long *events) +{ + u32 mask, st; + + /* XXX Can the mask read be avoided (e.g., can it come from RAM?) */ + mask = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); + st = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); + + events[0] = mask & st; +} + +/** + * omap3xxx_prm_ocp_barrier - force buffered MPU writes to the PRM to complete + * + * Force any buffered writes to the PRM IP block to complete. Needed + * by the PRM IRQ handler, which reads and writes directly to the IP + * block, to avoid race conditions after acknowledging or clearing IRQ + * bits. No return value. + */ +static void omap3xxx_prm_ocp_barrier(void) +{ + omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); +} + +/** + * omap3xxx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU reg + * @saved_mask: ptr to a u32 array to save IRQENABLE bits + * + * Save the PRM_IRQENABLE_MPU register to @saved_mask. @saved_mask + * must be allocated by the caller. Intended to be used in the PRM + * interrupt handler suspend callback. The OCP barrier is needed to + * ensure the write to disable PRM interrupts reaches the PRM before + * returning; otherwise, spurious interrupts might occur. No return + * value. + */ +static void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask) +{ + saved_mask[0] = omap2_prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQENABLE_MPU_OFFSET); + omap2_prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); + + /* OCP barrier */ + omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); +} + +/** + * omap3xxx_prm_restore_irqen - set PRM_IRQENABLE_MPU register from args + * @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously + * + * Restore the PRM_IRQENABLE_MPU register from @saved_mask. Intended + * to be used in the PRM interrupt handler resume callback to restore + * values saved by omap3xxx_prm_save_and_clear_irqen(). No OCP + * barrier should be needed here; any pending PRM interrupts will fire + * once the writes reach the PRM. No return value. + */ +static void omap3xxx_prm_restore_irqen(u32 *saved_mask) +{ + omap2_prm_write_mod_reg(saved_mask[0], OCP_MOD, + OMAP3_PRM_IRQENABLE_MPU_OFFSET); +} + +/** + * omap3xxx_prm_clear_mod_irqs - clear wake-up events from PRCM interrupt + * @module: PRM module to clear wakeups from + * @regs: register set to clear, 1 or 3 + * @wkst_mask: wkst bits to clear + * + * The purpose of this function is to clear any wake-up events latched + * in the PRCM PM_WKST_x registers. It is possible that a wake-up event + * may occur whilst attempting to clear a PM_WKST_x register and thus + * set another bit in this register. A while loop is used to ensure + * that any peripheral wake-up events occurring while attempting to + * clear the PM_WKST_x are detected and cleared. + */ +static int omap3xxx_prm_clear_mod_irqs(s16 module, u8 regs, u32 wkst_mask) +{ + u32 wkst, fclk, iclk, clken; + u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; + u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1; + u16 iclk_off = (regs == 3) ? CM_ICLKEN3 : CM_ICLKEN1; + u16 grpsel_off = (regs == 3) ? + OMAP3430ES2_PM_MPUGRPSEL3 : OMAP3430_PM_MPUGRPSEL; + int c = 0; + + wkst = omap2_prm_read_mod_reg(module, wkst_off); + wkst &= omap2_prm_read_mod_reg(module, grpsel_off); + wkst &= wkst_mask; + if (wkst) { + iclk = omap2_cm_read_mod_reg(module, iclk_off); + fclk = omap2_cm_read_mod_reg(module, fclk_off); + while (wkst) { + clken = wkst; + omap2_cm_set_mod_reg_bits(clken, module, iclk_off); + /* + * For USBHOST, we don't know whether HOST1 or + * HOST2 woke us up, so enable both f-clocks + */ + if (module == OMAP3430ES2_USBHOST_MOD) + clken |= 1 << OMAP3430ES2_EN_USBHOST2_SHIFT; + omap2_cm_set_mod_reg_bits(clken, module, fclk_off); + omap2_prm_write_mod_reg(wkst, module, wkst_off); + wkst = omap2_prm_read_mod_reg(module, wkst_off); + wkst &= wkst_mask; + c++; + } + omap2_cm_write_mod_reg(iclk, module, iclk_off); + omap2_cm_write_mod_reg(fclk, module, fclk_off); + } + + return c; +} + +/** + * omap3_prm_reset_modem - toggle reset signal for modem + * + * Toggles the reset signal to modem IP block. Required to allow + * OMAP3430 without stacked modem to idle properly. + */ +void __init omap3_prm_reset_modem(void) +{ + omap2_prm_write_mod_reg( + OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RSTPWRON_MASK | + OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RST_MASK, + CORE_MOD, OMAP2_RM_RSTCTRL); + omap2_prm_write_mod_reg(0, CORE_MOD, OMAP2_RM_RSTCTRL); +} + +/** + * omap3_prm_init_pm - initialize PM related registers for PRM + * @has_uart4: SoC has UART4 + * @has_iva: SoC has IVA + * + * Initializes PRM registers for PM use. Called from PM init. + */ +void __init omap3_prm_init_pm(bool has_uart4, bool has_iva) +{ + u32 en_uart4_mask; + u32 grpsel_uart4_mask; + + /* + * Enable control of expternal oscillator through + * sys_clkreq. In the long run clock framework should + * take care of this. + */ + omap2_prm_rmw_mod_reg_bits(OMAP_AUTOEXTCLKMODE_MASK, + 1 << OMAP_AUTOEXTCLKMODE_SHIFT, + OMAP3430_GR_MOD, + OMAP3_PRM_CLKSRC_CTRL_OFFSET); + + /* setup wakup source */ + omap2_prm_write_mod_reg(OMAP3430_EN_IO_MASK | OMAP3430_EN_GPIO1_MASK | + OMAP3430_EN_GPT1_MASK | OMAP3430_EN_GPT12_MASK, + WKUP_MOD, PM_WKEN); + /* No need to write EN_IO, that is always enabled */ + omap2_prm_write_mod_reg(OMAP3430_GRPSEL_GPIO1_MASK | + OMAP3430_GRPSEL_GPT1_MASK | + OMAP3430_GRPSEL_GPT12_MASK, + WKUP_MOD, OMAP3430_PM_MPUGRPSEL); + + /* Enable PM_WKEN to support DSS LPR */ + omap2_prm_write_mod_reg(OMAP3430_PM_WKEN_DSS_EN_DSS_MASK, + OMAP3430_DSS_MOD, PM_WKEN); + + if (has_uart4) { + en_uart4_mask = OMAP3630_EN_UART4_MASK; + grpsel_uart4_mask = OMAP3630_GRPSEL_UART4_MASK; + } else { + en_uart4_mask = 0; + grpsel_uart4_mask = 0; + } + + /* Enable wakeups in PER */ + omap2_prm_write_mod_reg(en_uart4_mask | + OMAP3430_EN_GPIO2_MASK | + OMAP3430_EN_GPIO3_MASK | + OMAP3430_EN_GPIO4_MASK | + OMAP3430_EN_GPIO5_MASK | + OMAP3430_EN_GPIO6_MASK | + OMAP3430_EN_UART3_MASK | + OMAP3430_EN_MCBSP2_MASK | + OMAP3430_EN_MCBSP3_MASK | + OMAP3430_EN_MCBSP4_MASK, + OMAP3430_PER_MOD, PM_WKEN); + + /* and allow them to wake up MPU */ + omap2_prm_write_mod_reg(grpsel_uart4_mask | + OMAP3430_GRPSEL_GPIO2_MASK | + OMAP3430_GRPSEL_GPIO3_MASK | + OMAP3430_GRPSEL_GPIO4_MASK | + OMAP3430_GRPSEL_GPIO5_MASK | + OMAP3430_GRPSEL_GPIO6_MASK | + OMAP3430_GRPSEL_UART3_MASK | + OMAP3430_GRPSEL_MCBSP2_MASK | + OMAP3430_GRPSEL_MCBSP3_MASK | + OMAP3430_GRPSEL_MCBSP4_MASK, + OMAP3430_PER_MOD, OMAP3430_PM_MPUGRPSEL); + + /* Don't attach IVA interrupts */ + if (has_iva) { + omap2_prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL); + omap2_prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1); + omap2_prm_write_mod_reg(0, CORE_MOD, OMAP3430ES2_PM_IVAGRPSEL3); + omap2_prm_write_mod_reg(0, OMAP3430_PER_MOD, + OMAP3430_PM_IVAGRPSEL); + } + + /* Clear any pending 'reset' flags */ + omap2_prm_write_mod_reg(0xffffffff, MPU_MOD, OMAP2_RM_RSTST); + omap2_prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP2_RM_RSTST); + omap2_prm_write_mod_reg(0xffffffff, OMAP3430_PER_MOD, OMAP2_RM_RSTST); + omap2_prm_write_mod_reg(0xffffffff, OMAP3430_EMU_MOD, OMAP2_RM_RSTST); + omap2_prm_write_mod_reg(0xffffffff, OMAP3430_NEON_MOD, OMAP2_RM_RSTST); + omap2_prm_write_mod_reg(0xffffffff, OMAP3430_DSS_MOD, OMAP2_RM_RSTST); + omap2_prm_write_mod_reg(0xffffffff, OMAP3430ES2_USBHOST_MOD, + OMAP2_RM_RSTST); + + /* Clear any pending PRCM interrupts */ + omap2_prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); + + /* We need to idle iva2_pwrdm even on am3703 with no iva2. */ + omap3xxx_prm_iva_idle(); + + omap3_prm_reset_modem(); +} + +/** + * omap3430_pre_es3_1_reconfigure_io_chain - restart wake-up daisy chain + * + * The ST_IO_CHAIN bit does not exist in 3430 before es3.1. The only + * thing we can do is toggle EN_IO bit for earlier omaps. + */ +static void omap3430_pre_es3_1_reconfigure_io_chain(void) +{ + omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD, + PM_WKEN); + omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD, + PM_WKEN); + omap2_prm_read_mod_reg(WKUP_MOD, PM_WKEN); +} + +/** + * omap3_prm_reconfigure_io_chain - clear latches and reconfigure I/O chain + * + * Clear any previously-latched I/O wakeup events and ensure that the + * I/O wakeup gates are aligned with the current mux settings. Works + * by asserting WUCLKIN, waiting for WUCLKOUT to be asserted, and then + * deasserting WUCLKIN and clearing the ST_IO_CHAIN WKST bit. No + * return value. These registers are only available in 3430 es3.1 and later. + */ +static void omap3_prm_reconfigure_io_chain(void) +{ + int i = 0; + + omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD, + PM_WKEN); + + omap_test_timeout(omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST) & + OMAP3430_ST_IO_CHAIN_MASK, + MAX_IOPAD_LATCH_TIME, i); + if (i == MAX_IOPAD_LATCH_TIME) + pr_warn("PRM: I/O chain clock line assertion timed out\n"); + + omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD, + PM_WKEN); + + omap2_prm_set_mod_reg_bits(OMAP3430_ST_IO_CHAIN_MASK, WKUP_MOD, + PM_WKST); + + omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST); +} + +/** + * omap3xxx_prm_enable_io_wakeup - enable wakeup events from I/O wakeup latches + * + * Activates the I/O wakeup event latches and allows events logged by + * those latches to signal a wakeup event to the PRCM. For I/O + * wakeups to occur, WAKEUPENABLE bits must be set in the pad mux + * registers, and omap3xxx_prm_reconfigure_io_chain() must be called. + * No return value. + */ +static void omap3xxx_prm_enable_io_wakeup(void) +{ + if (prm_features & PRM_HAS_IO_WAKEUP) + omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD, + PM_WKEN); +} + +/** + * omap3xxx_prm_read_reset_sources - return the last SoC reset source + * + * Return a u32 representing the last reset sources of the SoC. The + * returned reset source bits are standardized across OMAP SoCs. + */ +static u32 omap3xxx_prm_read_reset_sources(void) +{ + struct prm_reset_src_map *p; + u32 r = 0; + u32 v; + + v = omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTST); + + p = omap3xxx_prm_reset_src_map; + while (p->reg_shift >= 0 && p->std_shift >= 0) { + if (v & (1 << p->reg_shift)) + r |= 1 << p->std_shift; + p++; + } + + return r; +} + +/** + * omap3xxx_prm_iva_idle - ensure IVA is in idle so it can be put into retention + * + * In cases where IVA2 is activated by bootcode, it may prevent + * full-chip retention or off-mode because it is not idle. This + * function forces the IVA2 into idle state so it can go + * into retention/off and thus allow full-chip retention/off. + */ +void omap3xxx_prm_iva_idle(void) +{ + /* ensure IVA2 clock is disabled */ + omap2_cm_write_mod_reg(0, OMAP3430_IVA2_MOD, CM_FCLKEN); + + /* if no clock activity, nothing else to do */ + if (!(omap2_cm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP3430_CM_CLKSTST) & + OMAP3430_CLKACTIVITY_IVA2_MASK)) + return; + + /* Reset IVA2 */ + omap2_prm_write_mod_reg(OMAP3430_RST1_IVA2_MASK | + OMAP3430_RST2_IVA2_MASK | + OMAP3430_RST3_IVA2_MASK, + OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); + + /* Enable IVA2 clock */ + omap2_cm_write_mod_reg(OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_MASK, + OMAP3430_IVA2_MOD, CM_FCLKEN); + + /* Un-reset IVA2 */ + omap2_prm_write_mod_reg(0, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); + + /* Disable IVA2 clock */ + omap2_cm_write_mod_reg(0, OMAP3430_IVA2_MOD, CM_FCLKEN); + + /* Reset IVA2 */ + omap2_prm_write_mod_reg(OMAP3430_RST1_IVA2_MASK | + OMAP3430_RST2_IVA2_MASK | + OMAP3430_RST3_IVA2_MASK, + OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); +} + +/** + * omap3xxx_prm_clear_global_cold_reset - checks the global cold reset status + * and clears it if asserted + * + * Checks if cold-reset has occurred and clears the status bit if yes. Returns + * 1 if cold-reset has occurred, 0 otherwise. + */ +int omap3xxx_prm_clear_global_cold_reset(void) +{ + if (omap2_prm_read_mod_reg(OMAP3430_GR_MOD, OMAP3_PRM_RSTST_OFFSET) & + OMAP3430_GLOBAL_COLD_RST_MASK) { + omap2_prm_set_mod_reg_bits(OMAP3430_GLOBAL_COLD_RST_MASK, + OMAP3430_GR_MOD, + OMAP3_PRM_RSTST_OFFSET); + return 1; + } + + return 0; +} + +void omap3_prm_save_scratchpad_contents(u32 *ptr) +{ + *ptr++ = omap2_prm_read_mod_reg(OMAP3430_GR_MOD, + OMAP3_PRM_CLKSRC_CTRL_OFFSET); + + *ptr++ = omap2_prm_read_mod_reg(OMAP3430_GR_MOD, + OMAP3_PRM_CLKSEL_OFFSET); +} + +/* Powerdomain low-level functions */ + +static int omap3_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) +{ + omap2_prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK, + (pwrst << OMAP_POWERSTATE_SHIFT), + pwrdm->prcm_offs, OMAP2_PM_PWSTCTRL); + return 0; +} + +static int omap3_pwrdm_read_next_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL, + OMAP_POWERSTATE_MASK); +} + +static int omap3_pwrdm_read_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP2_PM_PWSTST, + OMAP_POWERSTATEST_MASK); +} + +/* Applicable only for OMAP3. Not supported on OMAP2 */ +static int omap3_pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP3430_PM_PREPWSTST, + OMAP3430_LASTPOWERSTATEENTERED_MASK); +} + +static int omap3_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP2_PM_PWSTST, + OMAP3430_LOGICSTATEST_MASK); +} + +static int omap3_pwrdm_read_logic_retst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL, + OMAP3430_LOGICSTATEST_MASK); +} + +static int omap3_pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) +{ + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP3430_PM_PREPWSTST, + OMAP3430_LASTLOGICSTATEENTERED_MASK); +} + +static int omap3_get_mem_bank_lastmemst_mask(u8 bank) +{ + switch (bank) { + case 0: + return OMAP3430_LASTMEM1STATEENTERED_MASK; + case 1: + return OMAP3430_LASTMEM2STATEENTERED_MASK; + case 2: + return OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK; + case 3: + return OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK; + default: + WARN_ON(1); /* should never happen */ + return -EEXIST; + } + return 0; +} + +static int omap3_pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m; + + m = omap3_get_mem_bank_lastmemst_mask(bank); + + return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, + OMAP3430_PM_PREPWSTST, m); +} + +static int omap3_pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) +{ + omap2_prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST); + return 0; +} + +static int omap3_pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) +{ + return omap2_prm_rmw_mod_reg_bits(0, + 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, + pwrdm->prcm_offs, OMAP2_PM_PWSTCTRL); +} + +static int omap3_pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm) +{ + return omap2_prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, + 0, pwrdm->prcm_offs, + OMAP2_PM_PWSTCTRL); +} + +struct pwrdm_ops omap3_pwrdm_operations = { + .pwrdm_set_next_pwrst = omap3_pwrdm_set_next_pwrst, + .pwrdm_read_next_pwrst = omap3_pwrdm_read_next_pwrst, + .pwrdm_read_pwrst = omap3_pwrdm_read_pwrst, + .pwrdm_read_prev_pwrst = omap3_pwrdm_read_prev_pwrst, + .pwrdm_set_logic_retst = omap2_pwrdm_set_logic_retst, + .pwrdm_read_logic_pwrst = omap3_pwrdm_read_logic_pwrst, + .pwrdm_read_logic_retst = omap3_pwrdm_read_logic_retst, + .pwrdm_read_prev_logic_pwrst = omap3_pwrdm_read_prev_logic_pwrst, + .pwrdm_set_mem_onst = omap2_pwrdm_set_mem_onst, + .pwrdm_set_mem_retst = omap2_pwrdm_set_mem_retst, + .pwrdm_read_mem_pwrst = omap2_pwrdm_read_mem_pwrst, + .pwrdm_read_mem_retst = omap2_pwrdm_read_mem_retst, + .pwrdm_read_prev_mem_pwrst = omap3_pwrdm_read_prev_mem_pwrst, + .pwrdm_clear_all_prev_pwrst = omap3_pwrdm_clear_all_prev_pwrst, + .pwrdm_enable_hdwr_sar = omap3_pwrdm_enable_hdwr_sar, + .pwrdm_disable_hdwr_sar = omap3_pwrdm_disable_hdwr_sar, + .pwrdm_wait_transition = omap2_pwrdm_wait_transition, +}; + +/* + * + */ + +static int omap3xxx_prm_late_init(void); + +static struct prm_ll_data omap3xxx_prm_ll_data = { + .read_reset_sources = &omap3xxx_prm_read_reset_sources, + .late_init = &omap3xxx_prm_late_init, + .assert_hardreset = &omap2_prm_assert_hardreset, + .deassert_hardreset = &omap2_prm_deassert_hardreset, + .is_hardreset_asserted = &omap2_prm_is_hardreset_asserted, + .reset_system = &omap3xxx_prm_dpll3_reset, + .clear_mod_irqs = &omap3xxx_prm_clear_mod_irqs, + .vp_check_txdone = &omap3_prm_vp_check_txdone, + .vp_clear_txdone = &omap3_prm_vp_clear_txdone, +}; + +int __init omap3xxx_prm_init(const struct omap_prcm_init_data *data) +{ + omap2_clk_legacy_provider_init(TI_CLKM_PRM, + prm_base.va + OMAP3430_IVA2_MOD); + if (omap3_has_io_wakeup()) + prm_features |= PRM_HAS_IO_WAKEUP; + + return prm_register(&omap3xxx_prm_ll_data); +} + +static const struct of_device_id omap3_prm_dt_match_table[] = { + { .compatible = "ti,omap3-prm" }, + { } +}; + +static int omap3xxx_prm_late_init(void) +{ + struct device_node *np; + int irq_num; + + if (!(prm_features & PRM_HAS_IO_WAKEUP)) + return 0; + + if (omap3_has_io_chain_ctrl()) + omap3_prcm_irq_setup.reconfigure_io_chain = + omap3_prm_reconfigure_io_chain; + else + omap3_prcm_irq_setup.reconfigure_io_chain = + omap3430_pre_es3_1_reconfigure_io_chain; + + np = of_find_matching_node(NULL, omap3_prm_dt_match_table); + if (!np) { + pr_err("PRM: no device tree node for interrupt?\n"); + + return -ENODEV; + } + + irq_num = of_irq_get(np, 0); + of_node_put(np); + if (irq_num == -EPROBE_DEFER) + return irq_num; + + omap3_prcm_irq_setup.irq = irq_num; + + omap3xxx_prm_enable_io_wakeup(); + + return omap_prcm_register_chain_handler(&omap3_prcm_irq_setup); +} + +static void __exit omap3xxx_prm_exit(void) +{ + prm_unregister(&omap3xxx_prm_ll_data); +} +__exitcall(omap3xxx_prm_exit); diff --git a/arch/arm/mach-omap2/prm3xxx.h b/arch/arm/mach-omap2/prm3xxx.h new file mode 100644 index 000000000..ed7c389aa --- /dev/null +++ b/arch/arm/mach-omap2/prm3xxx.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP3xxx Power/Reset Management (PRM) register definitions + * + * Copyright (C) 2007-2009, 2011-2012 Texas Instruments, Inc. + * Copyright (C) 2008-2010 Nokia Corporation + * Paul Walmsley + * + * The PRM hardware modules on the OMAP2/3 are quite similar to each + * other. The PRM on OMAP4 has a new register layout, and is handled + * in a separate file. + */ +#ifndef __ARCH_ARM_MACH_OMAP2_PRM3XXX_H +#define __ARCH_ARM_MACH_OMAP2_PRM3XXX_H + +#include "prcm-common.h" +#include "prm.h" +#include "prm2xxx_3xxx.h" + +#define OMAP34XX_PRM_REGADDR(module, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP3430_PRM_BASE + (module) + (reg)) + + +/* + * OMAP3-specific global PRM registers + * Use {read,write}l_relaxed() with these registers. + * + * With a few exceptions, these are the register names beginning with + * PRM_* on 34xx. (The exceptions are the IRQSTATUS and IRQENABLE + * bits.) + */ + +#define OMAP3_PRM_REVISION_OFFSET 0x0004 +#define OMAP3430_PRM_REVISION OMAP34XX_PRM_REGADDR(OCP_MOD, 0x0004) +#define OMAP3_PRM_SYSCONFIG_OFFSET 0x0014 +#define OMAP3430_PRM_SYSCONFIG OMAP34XX_PRM_REGADDR(OCP_MOD, 0x0014) + +#define OMAP3_PRM_IRQSTATUS_MPU_OFFSET 0x0018 +#define OMAP3430_PRM_IRQSTATUS_MPU OMAP34XX_PRM_REGADDR(OCP_MOD, 0x0018) +#define OMAP3_PRM_IRQENABLE_MPU_OFFSET 0x001c +#define OMAP3430_PRM_IRQENABLE_MPU OMAP34XX_PRM_REGADDR(OCP_MOD, 0x001c) + + +#define OMAP3_PRM_VC_SMPS_SA_OFFSET 0x0020 +#define OMAP3430_PRM_VC_SMPS_SA OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0020) +#define OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET 0x0024 +#define OMAP3430_PRM_VC_SMPS_VOL_RA OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0024) +#define OMAP3_PRM_VC_SMPS_CMD_RA_OFFSET 0x0028 +#define OMAP3430_PRM_VC_SMPS_CMD_RA OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0028) +#define OMAP3_PRM_VC_CMD_VAL_0_OFFSET 0x002c +#define OMAP3430_PRM_VC_CMD_VAL_0 OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x002c) +#define OMAP3_PRM_VC_CMD_VAL_1_OFFSET 0x0030 +#define OMAP3430_PRM_VC_CMD_VAL_1 OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0030) +#define OMAP3_PRM_VC_CH_CONF_OFFSET 0x0034 +#define OMAP3430_PRM_VC_CH_CONF OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0034) +#define OMAP3_PRM_VC_I2C_CFG_OFFSET 0x0038 +#define OMAP3430_PRM_VC_I2C_CFG OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0038) +#define OMAP3_PRM_VC_BYPASS_VAL_OFFSET 0x003c +#define OMAP3430_PRM_VC_BYPASS_VAL OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x003c) +#define OMAP3_PRM_RSTCTRL_OFFSET 0x0050 +#define OMAP3430_PRM_RSTCTRL OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0050) +#define OMAP3_PRM_RSTTIME_OFFSET 0x0054 +#define OMAP3430_PRM_RSTTIME OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0054) +#define OMAP3_PRM_RSTST_OFFSET 0x0058 +#define OMAP3430_PRM_RSTST OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0058) +#define OMAP3_PRM_VOLTCTRL_OFFSET 0x0060 +#define OMAP3430_PRM_VOLTCTRL OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0060) +#define OMAP3_PRM_SRAM_PCHARGE_OFFSET 0x0064 +#define OMAP3430_PRM_SRAM_PCHARGE OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0064) +#define OMAP3_PRM_CLKSRC_CTRL_OFFSET 0x0070 +#define OMAP3430_PRM_CLKSRC_CTRL OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0070) +#define OMAP3_PRM_VOLTSETUP1_OFFSET 0x0090 +#define OMAP3430_PRM_VOLTSETUP1 OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0090) +#define OMAP3_PRM_VOLTOFFSET_OFFSET 0x0094 +#define OMAP3430_PRM_VOLTOFFSET OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0094) +#define OMAP3_PRM_CLKSETUP_OFFSET 0x0098 +#define OMAP3430_PRM_CLKSETUP OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x0098) +#define OMAP3_PRM_POLCTRL_OFFSET 0x009c +#define OMAP3430_PRM_POLCTRL OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x009c) +#define OMAP3_PRM_VOLTSETUP2_OFFSET 0x00a0 +#define OMAP3430_PRM_VOLTSETUP2 OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00a0) +#define OMAP3_PRM_VP1_CONFIG_OFFSET 0x00b0 +#define OMAP3430_PRM_VP1_CONFIG OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00b0) +#define OMAP3_PRM_VP1_VSTEPMIN_OFFSET 0x00b4 +#define OMAP3430_PRM_VP1_VSTEPMIN OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00b4) +#define OMAP3_PRM_VP1_VSTEPMAX_OFFSET 0x00b8 +#define OMAP3430_PRM_VP1_VSTEPMAX OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00b8) +#define OMAP3_PRM_VP1_VLIMITTO_OFFSET 0x00bc +#define OMAP3430_PRM_VP1_VLIMITTO OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00bc) +#define OMAP3_PRM_VP1_VOLTAGE_OFFSET 0x00c0 +#define OMAP3430_PRM_VP1_VOLTAGE OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00c0) +#define OMAP3_PRM_VP1_STATUS_OFFSET 0x00c4 +#define OMAP3430_PRM_VP1_STATUS OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00c4) +#define OMAP3_PRM_VP2_CONFIG_OFFSET 0x00d0 +#define OMAP3430_PRM_VP2_CONFIG OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00d0) +#define OMAP3_PRM_VP2_VSTEPMIN_OFFSET 0x00d4 +#define OMAP3430_PRM_VP2_VSTEPMIN OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00d4) +#define OMAP3_PRM_VP2_VSTEPMAX_OFFSET 0x00d8 +#define OMAP3430_PRM_VP2_VSTEPMAX OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00d8) +#define OMAP3_PRM_VP2_VLIMITTO_OFFSET 0x00dc +#define OMAP3430_PRM_VP2_VLIMITTO OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00dc) +#define OMAP3_PRM_VP2_VOLTAGE_OFFSET 0x00e0 +#define OMAP3430_PRM_VP2_VOLTAGE OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00e0) +#define OMAP3_PRM_VP2_STATUS_OFFSET 0x00e4 +#define OMAP3430_PRM_VP2_STATUS OMAP34XX_PRM_REGADDR(OMAP3430_GR_MOD, 0x00e4) + +#define OMAP3_PRM_CLKSEL_OFFSET 0x0040 +#define OMAP3430_PRM_CLKSEL OMAP34XX_PRM_REGADDR(OMAP3430_CCR_MOD, 0x0040) +#define OMAP3_PRM_CLKOUT_CTRL_OFFSET 0x0070 +#define OMAP3430_PRM_CLKOUT_CTRL OMAP34XX_PRM_REGADDR(OMAP3430_CCR_MOD, 0x0070) + +/* OMAP3 specific register offsets */ +#define OMAP3430ES2_PM_WKEN3 0x00f0 +#define OMAP3430ES2_PM_WKST3 0x00b8 + +#define OMAP3430_PM_MPUGRPSEL 0x00a4 +#define OMAP3430_PM_MPUGRPSEL1 OMAP3430_PM_MPUGRPSEL +#define OMAP3430ES2_PM_MPUGRPSEL3 0x00f8 + +#define OMAP3430_PM_IVAGRPSEL 0x00a8 +#define OMAP3430_PM_IVAGRPSEL1 OMAP3430_PM_IVAGRPSEL +#define OMAP3430ES2_PM_IVAGRPSEL3 0x00f4 + +#define OMAP3430_PM_PREPWSTST 0x00e8 + +#define OMAP3430_PRM_IRQSTATUS_IVA2 0x00f8 +#define OMAP3430_PRM_IRQENABLE_IVA2 0x00fc + + +#ifndef __ASSEMBLER__ + +/* + * OMAP3 access functions for voltage controller (VC) and + * voltage proccessor (VP) in the PRM. + */ +extern u32 omap3_prm_vcvp_read(u8 offset); +extern void omap3_prm_vcvp_write(u32 val, u8 offset); +extern u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); + +int __init omap3xxx_prm_init(const struct omap_prcm_init_data *data); +void omap3xxx_prm_iva_idle(void); +void omap3_prm_reset_modem(void); +int omap3xxx_prm_clear_global_cold_reset(void); +void omap3_prm_save_scratchpad_contents(u32 *ptr); +void omap3_prm_init_pm(bool has_uart4, bool has_iva); + +#endif /* __ASSEMBLER */ + + +#endif diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c new file mode 100644 index 000000000..25093c1e5 --- /dev/null +++ b/arch/arm/mach-omap2/prm44xx.c @@ -0,0 +1,859 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 PRM module functions + * + * Copyright (C) 2011-2012 Texas Instruments, Inc. + * Copyright (C) 2010 Nokia Corporation + * Benoît Cousson + * Paul Walmsley + * Rajendra Nayak <rnayak@ti.com> + */ + +#include <linux/cpu_pm.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of_irq.h> +#include <linux/of.h> + +#include "soc.h" +#include "iomap.h" +#include "common.h" +#include "vp.h" +#include "prm44xx.h" +#include "prcm43xx.h" +#include "prm-regbits-44xx.h" +#include "prcm44xx.h" +#include "prminst44xx.h" +#include "powerdomain.h" +#include "pm.h" + +/* Static data */ + +static void omap44xx_prm_read_pending_irqs(unsigned long *events); +static void omap44xx_prm_ocp_barrier(void); +static void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask); +static void omap44xx_prm_restore_irqen(u32 *saved_mask); +static void omap44xx_prm_reconfigure_io_chain(void); + +static const struct omap_prcm_irq omap4_prcm_irqs[] = { + OMAP_PRCM_IRQ("io", 9, 1), +}; + +static struct omap_prcm_irq_setup omap4_prcm_irq_setup = { + .ack = OMAP4_PRM_IRQSTATUS_MPU_OFFSET, + .mask = OMAP4_PRM_IRQENABLE_MPU_OFFSET, + .pm_ctrl = OMAP4_PRM_IO_PMCTRL_OFFSET, + .nr_regs = 2, + .irqs = omap4_prcm_irqs, + .nr_irqs = ARRAY_SIZE(omap4_prcm_irqs), + .read_pending_irqs = &omap44xx_prm_read_pending_irqs, + .ocp_barrier = &omap44xx_prm_ocp_barrier, + .save_and_clear_irqen = &omap44xx_prm_save_and_clear_irqen, + .restore_irqen = &omap44xx_prm_restore_irqen, + .reconfigure_io_chain = &omap44xx_prm_reconfigure_io_chain, +}; + +struct omap_prm_irq_context { + unsigned long irq_enable; + unsigned long pm_ctrl; +}; + +static struct omap_prm_irq_context omap_prm_context; + +/* + * omap44xx_prm_reset_src_map - map from bits in the PRM_RSTST + * hardware register (which are specific to OMAP44xx SoCs) to reset + * source ID bit shifts (which is an OMAP SoC-independent + * enumeration) + */ +static struct prm_reset_src_map omap44xx_prm_reset_src_map[] = { + { OMAP4430_GLOBAL_WARM_SW_RST_SHIFT, + OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT }, + { OMAP4430_GLOBAL_COLD_RST_SHIFT, + OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT }, + { OMAP4430_MPU_SECURITY_VIOL_RST_SHIFT, + OMAP_SECU_VIOL_RST_SRC_ID_SHIFT }, + { OMAP4430_MPU_WDT_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT }, + { OMAP4430_SECURE_WDT_RST_SHIFT, OMAP_SECU_WD_RST_SRC_ID_SHIFT }, + { OMAP4430_EXTERNAL_WARM_RST_SHIFT, OMAP_EXTWARM_RST_SRC_ID_SHIFT }, + { OMAP4430_VDD_MPU_VOLT_MGR_RST_SHIFT, + OMAP_VDD_MPU_VM_RST_SRC_ID_SHIFT }, + { OMAP4430_VDD_IVA_VOLT_MGR_RST_SHIFT, + OMAP_VDD_IVA_VM_RST_SRC_ID_SHIFT }, + { OMAP4430_VDD_CORE_VOLT_MGR_RST_SHIFT, + OMAP_VDD_CORE_VM_RST_SRC_ID_SHIFT }, + { OMAP4430_ICEPICK_RST_SHIFT, OMAP_ICEPICK_RST_SRC_ID_SHIFT }, + { OMAP4430_C2C_RST_SHIFT, OMAP_C2C_RST_SRC_ID_SHIFT }, + { -1, -1 }, +}; + +/* PRM low-level functions */ + +/* Read a register in a CM/PRM instance in the PRM module */ +static u32 omap4_prm_read_inst_reg(s16 inst, u16 reg) +{ + return readl_relaxed(prm_base.va + inst + reg); +} + +/* Write into a register in a CM/PRM instance in the PRM module */ +static void omap4_prm_write_inst_reg(u32 val, s16 inst, u16 reg) +{ + writel_relaxed(val, prm_base.va + inst + reg); +} + +/* Read-modify-write a register in a PRM module. Caller must lock */ +static u32 omap4_prm_rmw_inst_reg_bits(u32 mask, u32 bits, s16 inst, s16 reg) +{ + u32 v; + + v = omap4_prm_read_inst_reg(inst, reg); + v &= ~mask; + v |= bits; + omap4_prm_write_inst_reg(v, inst, reg); + + return v; +} + +/* PRM VP */ + +/* + * struct omap4_vp - OMAP4 VP register access description. + * @irqstatus_mpu: offset to IRQSTATUS_MPU register for VP + * @tranxdone_status: VP_TRANXDONE_ST bitmask in PRM_IRQSTATUS_MPU reg + */ +struct omap4_vp { + u32 irqstatus_mpu; + u32 tranxdone_status; +}; + +static struct omap4_vp omap4_vp[] = { + [OMAP4_VP_VDD_MPU_ID] = { + .irqstatus_mpu = OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET, + .tranxdone_status = OMAP4430_VP_MPU_TRANXDONE_ST_MASK, + }, + [OMAP4_VP_VDD_IVA_ID] = { + .irqstatus_mpu = OMAP4_PRM_IRQSTATUS_MPU_OFFSET, + .tranxdone_status = OMAP4430_VP_IVA_TRANXDONE_ST_MASK, + }, + [OMAP4_VP_VDD_CORE_ID] = { + .irqstatus_mpu = OMAP4_PRM_IRQSTATUS_MPU_OFFSET, + .tranxdone_status = OMAP4430_VP_CORE_TRANXDONE_ST_MASK, + }, +}; + +static u32 omap4_prm_vp_check_txdone(u8 vp_id) +{ + struct omap4_vp *vp = &omap4_vp[vp_id]; + u32 irqstatus; + + irqstatus = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, + OMAP4430_PRM_OCP_SOCKET_INST, + vp->irqstatus_mpu); + return irqstatus & vp->tranxdone_status; +} + +static void omap4_prm_vp_clear_txdone(u8 vp_id) +{ + struct omap4_vp *vp = &omap4_vp[vp_id]; + + omap4_prminst_write_inst_reg(vp->tranxdone_status, + OMAP4430_PRM_PARTITION, + OMAP4430_PRM_OCP_SOCKET_INST, + vp->irqstatus_mpu); +}; + +u32 omap4_prm_vcvp_read(u8 offset) +{ + s32 inst = omap4_prmst_get_prm_dev_inst(); + + if (inst == PRM_INSTANCE_UNKNOWN) + return 0; + + return omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, + inst, offset); +} + +void omap4_prm_vcvp_write(u32 val, u8 offset) +{ + s32 inst = omap4_prmst_get_prm_dev_inst(); + + if (inst == PRM_INSTANCE_UNKNOWN) + return; + + omap4_prminst_write_inst_reg(val, OMAP4430_PRM_PARTITION, + inst, offset); +} + +u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset) +{ + s32 inst = omap4_prmst_get_prm_dev_inst(); + + if (inst == PRM_INSTANCE_UNKNOWN) + return 0; + + return omap4_prminst_rmw_inst_reg_bits(mask, bits, + OMAP4430_PRM_PARTITION, + inst, + offset); +} + +static inline u32 _read_pending_irq_reg(u16 irqen_offs, u16 irqst_offs) +{ + u32 mask, st; + + /* XXX read mask from RAM? */ + mask = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, + irqen_offs); + st = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, irqst_offs); + + return mask & st; +} + +/** + * omap44xx_prm_read_pending_irqs - read pending PRM MPU IRQs into @events + * @events: ptr to two consecutive u32s, preallocated by caller + * + * Read PRM_IRQSTATUS_MPU* bits, AND'ed with the currently-enabled PRM + * MPU IRQs, and store the result into the two u32s pointed to by @events. + * No return value. + */ +static void omap44xx_prm_read_pending_irqs(unsigned long *events) +{ + int i; + + for (i = 0; i < omap4_prcm_irq_setup.nr_regs; i++) + events[i] = _read_pending_irq_reg(omap4_prcm_irq_setup.mask + + i * 4, omap4_prcm_irq_setup.ack + i * 4); +} + +/** + * omap44xx_prm_ocp_barrier - force buffered MPU writes to the PRM to complete + * + * Force any buffered writes to the PRM IP block to complete. Needed + * by the PRM IRQ handler, which reads and writes directly to the IP + * block, to avoid race conditions after acknowledging or clearing IRQ + * bits. No return value. + */ +static void omap44xx_prm_ocp_barrier(void) +{ + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, + OMAP4_REVISION_PRM_OFFSET); +} + +/** + * omap44xx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU* regs + * @saved_mask: ptr to a u32 array to save IRQENABLE bits + * + * Save the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers to + * @saved_mask. @saved_mask must be allocated by the caller. + * Intended to be used in the PRM interrupt handler suspend callback. + * The OCP barrier is needed to ensure the write to disable PRM + * interrupts reaches the PRM before returning; otherwise, spurious + * interrupts might occur. No return value. + */ +static void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask) +{ + int i; + u16 reg; + + for (i = 0; i < omap4_prcm_irq_setup.nr_regs; i++) { + reg = omap4_prcm_irq_setup.mask + i * 4; + + saved_mask[i] = + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, + reg); + omap4_prm_write_inst_reg(0, OMAP4430_PRM_OCP_SOCKET_INST, reg); + } + + /* OCP barrier */ + omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, + OMAP4_REVISION_PRM_OFFSET); +} + +/** + * omap44xx_prm_restore_irqen - set PRM_IRQENABLE_MPU* registers from args + * @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously + * + * Restore the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers from + * @saved_mask. Intended to be used in the PRM interrupt handler resume + * callback to restore values saved by omap44xx_prm_save_and_clear_irqen(). + * No OCP barrier should be needed here; any pending PRM interrupts will fire + * once the writes reach the PRM. No return value. + */ +static void omap44xx_prm_restore_irqen(u32 *saved_mask) +{ + int i; + + for (i = 0; i < omap4_prcm_irq_setup.nr_regs; i++) + omap4_prm_write_inst_reg(saved_mask[i], + OMAP4430_PRM_OCP_SOCKET_INST, + omap4_prcm_irq_setup.mask + i * 4); +} + +/** + * omap44xx_prm_reconfigure_io_chain - clear latches and reconfigure I/O chain + * + * Clear any previously-latched I/O wakeup events and ensure that the + * I/O wakeup gates are aligned with the current mux settings. Works + * by asserting WUCLKIN, waiting for WUCLKOUT to be asserted, and then + * deasserting WUCLKIN and waiting for WUCLKOUT to be deasserted. + * No return value. XXX Are the final two steps necessary? + */ +static void omap44xx_prm_reconfigure_io_chain(void) +{ + int i = 0; + s32 inst = omap4_prmst_get_prm_dev_inst(); + + if (inst == PRM_INSTANCE_UNKNOWN) + return; + + /* Trigger WUCLKIN enable */ + omap4_prm_rmw_inst_reg_bits(OMAP4430_WUCLK_CTRL_MASK, + OMAP4430_WUCLK_CTRL_MASK, + inst, + omap4_prcm_irq_setup.pm_ctrl); + omap_test_timeout( + (((omap4_prm_read_inst_reg(inst, + omap4_prcm_irq_setup.pm_ctrl) & + OMAP4430_WUCLK_STATUS_MASK) >> + OMAP4430_WUCLK_STATUS_SHIFT) == 1), + MAX_IOPAD_LATCH_TIME, i); + if (i == MAX_IOPAD_LATCH_TIME) + pr_warn("PRM: I/O chain clock line assertion timed out\n"); + + /* Trigger WUCLKIN disable */ + omap4_prm_rmw_inst_reg_bits(OMAP4430_WUCLK_CTRL_MASK, 0x0, + inst, + omap4_prcm_irq_setup.pm_ctrl); + omap_test_timeout( + (((omap4_prm_read_inst_reg(inst, + omap4_prcm_irq_setup.pm_ctrl) & + OMAP4430_WUCLK_STATUS_MASK) >> + OMAP4430_WUCLK_STATUS_SHIFT) == 0), + MAX_IOPAD_LATCH_TIME, i); + if (i == MAX_IOPAD_LATCH_TIME) + pr_warn("PRM: I/O chain clock line deassertion timed out\n"); + + return; +} + +/** + * omap44xx_prm_enable_io_wakeup - enable wakeup events from I/O wakeup latches + * + * Activates the I/O wakeup event latches and allows events logged by + * those latches to signal a wakeup event to the PRCM. For I/O wakeups + * to occur, WAKEUPENABLE bits must be set in the pad mux registers, and + * omap44xx_prm_reconfigure_io_chain() must be called. No return value. + */ +static void omap44xx_prm_enable_io_wakeup(void) +{ + s32 inst = omap4_prmst_get_prm_dev_inst(); + + if (inst == PRM_INSTANCE_UNKNOWN) + return; + + omap4_prm_rmw_inst_reg_bits(OMAP4430_GLOBAL_WUEN_MASK, + OMAP4430_GLOBAL_WUEN_MASK, + inst, + omap4_prcm_irq_setup.pm_ctrl); +} + +/** + * omap44xx_prm_read_reset_sources - return the last SoC reset source + * + * Return a u32 representing the last reset sources of the SoC. The + * returned reset source bits are standardized across OMAP SoCs. + */ +static u32 omap44xx_prm_read_reset_sources(void) +{ + struct prm_reset_src_map *p; + u32 r = 0; + u32 v; + s32 inst = omap4_prmst_get_prm_dev_inst(); + + if (inst == PRM_INSTANCE_UNKNOWN) + return 0; + + + v = omap4_prm_read_inst_reg(inst, + OMAP4_RM_RSTST); + + p = omap44xx_prm_reset_src_map; + while (p->reg_shift >= 0 && p->std_shift >= 0) { + if (v & (1 << p->reg_shift)) + r |= 1 << p->std_shift; + p++; + } + + return r; +} + +/** + * omap44xx_prm_was_any_context_lost_old - was module hardware context lost? + * @part: PRM partition ID (e.g., OMAP4430_PRM_PARTITION) + * @inst: PRM instance offset (e.g., OMAP4430_PRM_MPU_INST) + * @idx: CONTEXT register offset + * + * Return 1 if any bits were set in the *_CONTEXT_* register + * identified by (@part, @inst, @idx), which means that some context + * was lost for that module; otherwise, return 0. + */ +static bool omap44xx_prm_was_any_context_lost_old(u8 part, s16 inst, u16 idx) +{ + return (omap4_prminst_read_inst_reg(part, inst, idx)) ? 1 : 0; +} + +/** + * omap44xx_prm_clear_context_lost_flags_old - clear context loss flags + * @part: PRM partition ID (e.g., OMAP4430_PRM_PARTITION) + * @inst: PRM instance offset (e.g., OMAP4430_PRM_MPU_INST) + * @idx: CONTEXT register offset + * + * Clear hardware context loss bits for the module identified by + * (@part, @inst, @idx). No return value. XXX Writes to reserved bits; + * is there a way to avoid this? + */ +static void omap44xx_prm_clear_context_loss_flags_old(u8 part, s16 inst, + u16 idx) +{ + omap4_prminst_write_inst_reg(0xffffffff, part, inst, idx); +} + +/* Powerdomain low-level functions */ + +static int omap4_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) +{ + omap4_prminst_rmw_inst_reg_bits(OMAP_POWERSTATE_MASK, + (pwrst << OMAP_POWERSTATE_SHIFT), + pwrdm->prcm_partition, + pwrdm->prcm_offs, OMAP4_PM_PWSTCTRL); + return 0; +} + +static int omap4_pwrdm_read_next_pwrst(struct powerdomain *pwrdm) +{ + u32 v; + + v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTCTRL); + v &= OMAP_POWERSTATE_MASK; + v >>= OMAP_POWERSTATE_SHIFT; + + return v; +} + +static int omap4_pwrdm_read_pwrst(struct powerdomain *pwrdm) +{ + u32 v; + + v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTST); + v &= OMAP_POWERSTATEST_MASK; + v >>= OMAP_POWERSTATEST_SHIFT; + + return v; +} + +static int omap4_pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) +{ + u32 v; + + v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTST); + v &= OMAP4430_LASTPOWERSTATEENTERED_MASK; + v >>= OMAP4430_LASTPOWERSTATEENTERED_SHIFT; + + return v; +} + +static int omap4_pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm) +{ + omap4_prminst_rmw_inst_reg_bits(OMAP4430_LOWPOWERSTATECHANGE_MASK, + (1 << OMAP4430_LOWPOWERSTATECHANGE_SHIFT), + pwrdm->prcm_partition, + pwrdm->prcm_offs, OMAP4_PM_PWSTCTRL); + return 0; +} + +static int omap4_pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) +{ + omap4_prminst_rmw_inst_reg_bits(OMAP4430_LASTPOWERSTATEENTERED_MASK, + OMAP4430_LASTPOWERSTATEENTERED_MASK, + pwrdm->prcm_partition, + pwrdm->prcm_offs, OMAP4_PM_PWSTST); + return 0; +} + +static int omap4_pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) +{ + u32 v; + + v = pwrst << __ffs(OMAP4430_LOGICRETSTATE_MASK); + omap4_prminst_rmw_inst_reg_bits(OMAP4430_LOGICRETSTATE_MASK, v, + pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTCTRL); + + return 0; +} + +static int omap4_pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst) +{ + u32 m; + + m = omap2_pwrdm_get_mem_bank_onstate_mask(bank); + + omap4_prminst_rmw_inst_reg_bits(m, (pwrst << __ffs(m)), + pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTCTRL); + + return 0; +} + +static int omap4_pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, + u8 pwrst) +{ + u32 m; + + m = omap2_pwrdm_get_mem_bank_retst_mask(bank); + + omap4_prminst_rmw_inst_reg_bits(m, (pwrst << __ffs(m)), + pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTCTRL); + + return 0; +} + +static int omap4_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) +{ + u32 v; + + v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTST); + v &= OMAP4430_LOGICSTATEST_MASK; + v >>= OMAP4430_LOGICSTATEST_SHIFT; + + return v; +} + +static int omap4_pwrdm_read_logic_retst(struct powerdomain *pwrdm) +{ + u32 v; + + v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTCTRL); + v &= OMAP4430_LOGICRETSTATE_MASK; + v >>= OMAP4430_LOGICRETSTATE_SHIFT; + + return v; +} + +/** + * omap4_pwrdm_read_prev_logic_pwrst - read the previous logic powerstate + * @pwrdm: struct powerdomain * to read the state for + * + * Reads the previous logic powerstate for a powerdomain. This + * function must determine the previous logic powerstate by first + * checking the previous powerstate for the domain. If that was OFF, + * then logic has been lost. If previous state was RETENTION, the + * function reads the setting for the next retention logic state to + * see the actual value. In every other case, the logic is + * retained. Returns either PWRDM_POWER_OFF or PWRDM_POWER_RET + * depending whether the logic was retained or not. + */ +static int omap4_pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) +{ + int state; + + state = omap4_pwrdm_read_prev_pwrst(pwrdm); + + if (state == PWRDM_POWER_OFF) + return PWRDM_POWER_OFF; + + if (state != PWRDM_POWER_RET) + return PWRDM_POWER_RET; + + return omap4_pwrdm_read_logic_retst(pwrdm); +} + +static int omap4_pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m, v; + + m = omap2_pwrdm_get_mem_bank_stst_mask(bank); + + v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTST); + v &= m; + v >>= __ffs(m); + + return v; +} + +static int omap4_pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank) +{ + u32 m, v; + + m = omap2_pwrdm_get_mem_bank_retst_mask(bank); + + v = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, pwrdm->prcm_offs, + OMAP4_PM_PWSTCTRL); + v &= m; + v >>= __ffs(m); + + return v; +} + +/** + * omap4_pwrdm_read_prev_mem_pwrst - reads the previous memory powerstate + * @pwrdm: struct powerdomain * to read mem powerstate for + * @bank: memory bank index + * + * Reads the previous memory powerstate for a powerdomain. This + * function must determine the previous memory powerstate by first + * checking the previous powerstate for the domain. If that was OFF, + * then logic has been lost. If previous state was RETENTION, the + * function reads the setting for the next memory retention state to + * see the actual value. In every other case, the logic is + * retained. Returns either PWRDM_POWER_OFF or PWRDM_POWER_RET + * depending whether logic was retained or not. + */ +static int omap4_pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) +{ + int state; + + state = omap4_pwrdm_read_prev_pwrst(pwrdm); + + if (state == PWRDM_POWER_OFF) + return PWRDM_POWER_OFF; + + if (state != PWRDM_POWER_RET) + return PWRDM_POWER_RET; + + return omap4_pwrdm_read_mem_retst(pwrdm, bank); +} + +static int omap4_pwrdm_wait_transition(struct powerdomain *pwrdm) +{ + u32 c = 0; + + /* + * REVISIT: pwrdm_wait_transition() may be better implemented + * via a callback and a periodic timer check -- how long do we expect + * powerdomain transitions to take? + */ + + /* XXX Is this udelay() value meaningful? */ + while ((omap4_prminst_read_inst_reg(pwrdm->prcm_partition, + pwrdm->prcm_offs, + OMAP4_PM_PWSTST) & + OMAP_INTRANSITION_MASK) && + (c++ < PWRDM_TRANSITION_BAILOUT)) + udelay(1); + + if (c > PWRDM_TRANSITION_BAILOUT) { + pr_err("powerdomain: %s: waited too long to complete transition\n", + pwrdm->name); + return -EAGAIN; + } + + pr_debug("powerdomain: completed transition in %d loops\n", c); + + return 0; +} + +static int omap4_check_vcvp(void) +{ + if (prm_features & PRM_HAS_VOLTAGE) + return 1; + + return 0; +} + +/** + * omap4_pwrdm_save_context - Saves the powerdomain state + * @pwrdm: pointer to individual powerdomain + * + * The function saves the powerdomain state control information. + * This is needed in rtc+ddr modes where we lose powerdomain context. + */ +static void omap4_pwrdm_save_context(struct powerdomain *pwrdm) +{ + pwrdm->context = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, + pwrdm->prcm_offs, + pwrdm->pwrstctrl_offs); + + /* + * Do not save LOWPOWERSTATECHANGE, writing a 1 indicates a request, + * reading back a 1 indicates a request in progress. + */ + pwrdm->context &= ~OMAP4430_LOWPOWERSTATECHANGE_MASK; +} + +/** + * omap4_pwrdm_restore_context - Restores the powerdomain state + * @pwrdm: pointer to individual powerdomain + * + * The function restores the powerdomain state control information. + * This is needed in rtc+ddr modes where we lose powerdomain context. + */ +static void omap4_pwrdm_restore_context(struct powerdomain *pwrdm) +{ + int st, ctrl; + + st = omap4_prminst_read_inst_reg(pwrdm->prcm_partition, + pwrdm->prcm_offs, + pwrdm->pwrstctrl_offs); + + omap4_prminst_write_inst_reg(pwrdm->context, + pwrdm->prcm_partition, + pwrdm->prcm_offs, + pwrdm->pwrstctrl_offs); + + /* Make sure we only wait for a transition if there is one */ + st &= OMAP_POWERSTATEST_MASK; + ctrl = OMAP_POWERSTATEST_MASK & pwrdm->context; + + if (st != ctrl) + omap4_pwrdm_wait_transition(pwrdm); +} + +struct pwrdm_ops omap4_pwrdm_operations = { + .pwrdm_set_next_pwrst = omap4_pwrdm_set_next_pwrst, + .pwrdm_read_next_pwrst = omap4_pwrdm_read_next_pwrst, + .pwrdm_read_pwrst = omap4_pwrdm_read_pwrst, + .pwrdm_read_prev_pwrst = omap4_pwrdm_read_prev_pwrst, + .pwrdm_set_lowpwrstchange = omap4_pwrdm_set_lowpwrstchange, + .pwrdm_clear_all_prev_pwrst = omap4_pwrdm_clear_all_prev_pwrst, + .pwrdm_set_logic_retst = omap4_pwrdm_set_logic_retst, + .pwrdm_read_logic_pwrst = omap4_pwrdm_read_logic_pwrst, + .pwrdm_read_prev_logic_pwrst = omap4_pwrdm_read_prev_logic_pwrst, + .pwrdm_read_logic_retst = omap4_pwrdm_read_logic_retst, + .pwrdm_read_mem_pwrst = omap4_pwrdm_read_mem_pwrst, + .pwrdm_read_mem_retst = omap4_pwrdm_read_mem_retst, + .pwrdm_read_prev_mem_pwrst = omap4_pwrdm_read_prev_mem_pwrst, + .pwrdm_set_mem_onst = omap4_pwrdm_set_mem_onst, + .pwrdm_set_mem_retst = omap4_pwrdm_set_mem_retst, + .pwrdm_wait_transition = omap4_pwrdm_wait_transition, + .pwrdm_has_voltdm = omap4_check_vcvp, + .pwrdm_save_context = omap4_pwrdm_save_context, + .pwrdm_restore_context = omap4_pwrdm_restore_context, +}; + +static int omap44xx_prm_late_init(void); + +static void prm_save_context(void) +{ + omap_prm_context.irq_enable = + omap4_prm_read_inst_reg(AM43XX_PRM_OCP_SOCKET_INST, + omap4_prcm_irq_setup.mask); + + omap_prm_context.pm_ctrl = + omap4_prm_read_inst_reg(AM43XX_PRM_DEVICE_INST, + omap4_prcm_irq_setup.pm_ctrl); +} + +static void prm_restore_context(void) +{ + omap4_prm_write_inst_reg(omap_prm_context.irq_enable, + OMAP4430_PRM_OCP_SOCKET_INST, + omap4_prcm_irq_setup.mask); + + omap4_prm_write_inst_reg(omap_prm_context.pm_ctrl, + AM43XX_PRM_DEVICE_INST, + omap4_prcm_irq_setup.pm_ctrl); +} + +static int cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + if (enable_off_mode) + prm_save_context(); + break; + case CPU_CLUSTER_PM_EXIT: + if (enable_off_mode) + prm_restore_context(); + break; + } + + return NOTIFY_OK; +} + +/* + * XXX document + */ +static struct prm_ll_data omap44xx_prm_ll_data = { + .read_reset_sources = &omap44xx_prm_read_reset_sources, + .was_any_context_lost_old = &omap44xx_prm_was_any_context_lost_old, + .clear_context_loss_flags_old = &omap44xx_prm_clear_context_loss_flags_old, + .late_init = &omap44xx_prm_late_init, + .assert_hardreset = omap4_prminst_assert_hardreset, + .deassert_hardreset = omap4_prminst_deassert_hardreset, + .is_hardreset_asserted = omap4_prminst_is_hardreset_asserted, + .reset_system = omap4_prminst_global_warm_sw_reset, + .vp_check_txdone = omap4_prm_vp_check_txdone, + .vp_clear_txdone = omap4_prm_vp_clear_txdone, +}; + +static const struct omap_prcm_init_data *prm_init_data; + +int __init omap44xx_prm_init(const struct omap_prcm_init_data *data) +{ + static struct notifier_block nb; + omap_prm_base_init(); + + prm_init_data = data; + + if (data->flags & PRM_HAS_IO_WAKEUP) + prm_features |= PRM_HAS_IO_WAKEUP; + + if (data->flags & PRM_HAS_VOLTAGE) + prm_features |= PRM_HAS_VOLTAGE; + + omap4_prminst_set_prm_dev_inst(data->device_inst_offset); + + /* Add AM437X specific differences */ + if (of_device_is_compatible(data->np, "ti,am4-prcm")) { + omap4_prcm_irq_setup.nr_irqs = 1; + omap4_prcm_irq_setup.nr_regs = 1; + omap4_prcm_irq_setup.pm_ctrl = AM43XX_PRM_IO_PMCTRL_OFFSET; + omap4_prcm_irq_setup.ack = AM43XX_PRM_IRQSTATUS_MPU_OFFSET; + omap4_prcm_irq_setup.mask = AM43XX_PRM_IRQENABLE_MPU_OFFSET; + } + + /* Only AM43XX can lose prm context during rtc-ddr suspend */ + if (soc_is_am43xx()) { + nb.notifier_call = cpu_notifier; + cpu_pm_register_notifier(&nb); + } + + return prm_register(&omap44xx_prm_ll_data); +} + +static int omap44xx_prm_late_init(void) +{ + int irq_num; + + if (!(prm_features & PRM_HAS_IO_WAKEUP)) + return 0; + + irq_num = of_irq_get(prm_init_data->np, 0); + if (irq_num == -EPROBE_DEFER) + return irq_num; + + omap4_prcm_irq_setup.irq = irq_num; + + omap44xx_prm_enable_io_wakeup(); + + return omap_prcm_register_chain_handler(&omap4_prcm_irq_setup); +} + +static void __exit omap44xx_prm_exit(void) +{ + prm_unregister(&omap44xx_prm_ll_data); +} +__exitcall(omap44xx_prm_exit); diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h new file mode 100644 index 000000000..fc7d4ed0b --- /dev/null +++ b/arch/arm/mach-omap2/prm44xx.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx PRM instance offset macros + * + * Copyright (C) 2009-2011 Texas Instruments, Inc. + * Copyright (C) 2009-2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + * + * XXX This file needs to be updated to align on one of "OMAP4", "OMAP44XX", + * or "OMAP4430". + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRM44XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM44XX_H + +#include "prm44xx_54xx.h" +#include "prm.h" + +#define OMAP4430_PRM_BASE 0x4a306000 + +#define OMAP44XX_PRM_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP4430_PRM_BASE + (inst) + (reg)) + + +/* PRM instances */ +#define OMAP4430_PRM_OCP_SOCKET_INST 0x0000 +#define OMAP4430_PRM_CKGEN_INST 0x0100 +#define OMAP4430_PRM_MPU_INST 0x0300 +#define OMAP4430_PRM_TESLA_INST 0x0400 +#define OMAP4430_PRM_ABE_INST 0x0500 +#define OMAP4430_PRM_ALWAYS_ON_INST 0x0600 +#define OMAP4430_PRM_CORE_INST 0x0700 +#define OMAP4430_PRM_IVAHD_INST 0x0f00 +#define OMAP4430_PRM_CAM_INST 0x1000 +#define OMAP4430_PRM_DSS_INST 0x1100 +#define OMAP4430_PRM_GFX_INST 0x1200 +#define OMAP4430_PRM_L3INIT_INST 0x1300 +#define OMAP4430_PRM_L4PER_INST 0x1400 +#define OMAP4430_PRM_CEFUSE_INST 0x1600 +#define OMAP4430_PRM_WKUP_INST 0x1700 +#define OMAP4430_PRM_WKUP_CM_INST 0x1800 +#define OMAP4430_PRM_EMU_INST 0x1900 +#define OMAP4430_PRM_EMU_CM_INST 0x1a00 +#define OMAP4430_PRM_DEVICE_INST 0x1b00 + +/* PRM clockdomain register offsets (from instance start) */ +#define OMAP4430_PRM_WKUP_CM_WKUP_CDOFFS 0x0000 +#define OMAP4430_PRM_EMU_CM_EMU_CDOFFS 0x0000 + +/* OMAP4 specific register offsets */ +#define OMAP4_RM_RSTST 0x0004 +#define OMAP4_PM_PWSTCTRL 0x0000 +#define OMAP4_PM_PWSTST 0x0004 + +/* PRM.OCP_SOCKET_PRM register offsets */ +#define OMAP4_REVISION_PRM_OFFSET 0x0000 +#define OMAP4_PRM_IRQSTATUS_MPU_OFFSET 0x0010 +#define OMAP4430_PRM_IRQSTATUS_MPU OMAP44XX_PRM_REGADDR(OMAP4430_PRM_OCP_SOCKET_INST, 0x0010) +#define OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET 0x0014 +#define OMAP4_PRM_IRQENABLE_MPU_OFFSET 0x0018 +#define OMAP4430_PRM_IRQENABLE_MPU OMAP44XX_PRM_REGADDR(OMAP4430_PRM_OCP_SOCKET_INST, 0x0018) + +/* PRM.MPU_PRM register offsets */ +#define OMAP4_RM_MPU_MPU_CONTEXT_OFFSET 0x0024 + +/* PRM.DEVICE_PRM register offsets */ +#define OMAP4_PRM_RSTCTRL_OFFSET 0x0000 +#define OMAP4_PRM_VOLTCTRL_OFFSET 0x0010 +#define OMAP4_PRM_IO_PMCTRL_OFFSET 0x0020 +#define OMAP4_PRM_VOLTSETUP_CORE_OFF_OFFSET 0x0028 +#define OMAP4_PRM_VOLTSETUP_MPU_OFF_OFFSET 0x002c +#define OMAP4_PRM_VOLTSETUP_IVA_OFF_OFFSET 0x0030 +#define OMAP4_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET 0x0034 +#define OMAP4_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET 0x0038 +#define OMAP4_PRM_VOLTSETUP_IVA_RET_SLEEP_OFFSET 0x003c +#define OMAP4_PRM_VP_CORE_CONFIG_OFFSET 0x0040 +#define OMAP4_PRM_VP_CORE_STATUS_OFFSET 0x0044 +#define OMAP4_PRM_VP_CORE_VLIMITTO_OFFSET 0x0048 +#define OMAP4_PRM_VP_CORE_VOLTAGE_OFFSET 0x004c +#define OMAP4_PRM_VP_CORE_VSTEPMAX_OFFSET 0x0050 +#define OMAP4_PRM_VP_CORE_VSTEPMIN_OFFSET 0x0054 +#define OMAP4_PRM_VP_MPU_CONFIG_OFFSET 0x0058 +#define OMAP4_PRM_VP_MPU_STATUS_OFFSET 0x005c +#define OMAP4_PRM_VP_MPU_VLIMITTO_OFFSET 0x0060 +#define OMAP4_PRM_VP_MPU_VOLTAGE_OFFSET 0x0064 +#define OMAP4_PRM_VP_MPU_VSTEPMAX_OFFSET 0x0068 +#define OMAP4_PRM_VP_MPU_VSTEPMIN_OFFSET 0x006c +#define OMAP4_PRM_VP_IVA_CONFIG_OFFSET 0x0070 +#define OMAP4_PRM_VP_IVA_STATUS_OFFSET 0x0074 +#define OMAP4_PRM_VP_IVA_VLIMITTO_OFFSET 0x0078 +#define OMAP4_PRM_VP_IVA_VOLTAGE_OFFSET 0x007c +#define OMAP4_PRM_VP_IVA_VSTEPMAX_OFFSET 0x0080 +#define OMAP4_PRM_VP_IVA_VSTEPMIN_OFFSET 0x0084 +#define OMAP4_PRM_VC_SMPS_SA_OFFSET 0x0088 +#define OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET 0x008c +#define OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET 0x0090 +#define OMAP4_PRM_VC_VAL_CMD_VDD_CORE_L_OFFSET 0x0094 +#define OMAP4_PRM_VC_VAL_CMD_VDD_MPU_L_OFFSET 0x0098 +#define OMAP4_PRM_VC_VAL_CMD_VDD_IVA_L_OFFSET 0x009c +#define OMAP4_PRM_VC_VAL_BYPASS_OFFSET 0x00a0 +#define OMAP4_PRM_VC_CFG_CHANNEL_OFFSET 0x00a4 +#define OMAP4_PRM_VC_CFG_I2C_MODE_OFFSET 0x00a8 +#define OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET 0x00ac + +#endif diff --git a/arch/arm/mach-omap2/prm44xx_54xx.h b/arch/arm/mach-omap2/prm44xx_54xx.h new file mode 100644 index 000000000..0915a1092 --- /dev/null +++ b/arch/arm/mach-omap2/prm44xx_54xx.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx and 54xx PRM common functions + * + * Copyright (C) 2009-2013 Texas Instruments, Inc. + * Copyright (C) 2009-2010 Nokia Corporation + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRM44XX_54XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM44XX_54XX_H + +#include "prcm-common.h" + +/* Function prototypes */ +#ifndef __ASSEMBLER__ + +/* + * OMAP4/OMAP5 access functions for voltage controller (VC) and + * voltage proccessor (VP) in the PRM. + */ +extern u32 omap4_prm_vcvp_read(u8 offset); +extern void omap4_prm_vcvp_write(u32 val, u8 offset); +extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); + +int __init omap44xx_prm_init(const struct omap_prcm_init_data *data); + +#endif + +#endif diff --git a/arch/arm/mach-omap2/prm54xx.h b/arch/arm/mach-omap2/prm54xx.h new file mode 100644 index 000000000..0b59eeda7 --- /dev/null +++ b/arch/arm/mach-omap2/prm54xx.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP54xx PRM instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRM54XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM54XX_H + +#include "prm44xx_54xx.h" +#include "prm.h" + +#define OMAP54XX_PRM_BASE 0x4ae06000 + +#define OMAP54XX_PRM_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(OMAP54XX_PRM_BASE + (inst) + (reg)) + + +/* PRM instances */ +#define OMAP54XX_PRM_OCP_SOCKET_INST 0x0000 +#define OMAP54XX_PRM_CKGEN_INST 0x0100 +#define OMAP54XX_PRM_MPU_INST 0x0300 +#define OMAP54XX_PRM_DSP_INST 0x0400 +#define OMAP54XX_PRM_ABE_INST 0x0500 +#define OMAP54XX_PRM_COREAON_INST 0x0600 +#define OMAP54XX_PRM_CORE_INST 0x0700 +#define OMAP54XX_PRM_IVA_INST 0x1200 +#define OMAP54XX_PRM_CAM_INST 0x1300 +#define OMAP54XX_PRM_DSS_INST 0x1400 +#define OMAP54XX_PRM_GPU_INST 0x1500 +#define OMAP54XX_PRM_L3INIT_INST 0x1600 +#define OMAP54XX_PRM_CUSTEFUSE_INST 0x1700 +#define OMAP54XX_PRM_WKUPAON_INST 0x1800 +#define OMAP54XX_PRM_WKUPAON_CM_INST 0x1900 +#define OMAP54XX_PRM_EMU_INST 0x1a00 +#define OMAP54XX_PRM_EMU_CM_INST 0x1b00 +#define OMAP54XX_PRM_DEVICE_INST 0x1c00 + +/* PRM clockdomain register offsets (from instance start) */ +#define OMAP54XX_PRM_WKUPAON_CM_WKUPAON_CDOFFS 0x0000 +#define OMAP54XX_PRM_EMU_CM_EMU_CDOFFS 0x0000 + +/* PRM.DEVICE_PRM register offsets */ +#define OMAP54XX_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET 0x0034 +#define OMAP54XX_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET 0x0038 +#define OMAP54XX_PRM_VOLTSETUP_MM_RET_SLEEP_OFFSET 0x003c + +#endif diff --git a/arch/arm/mach-omap2/prm7xx.h b/arch/arm/mach-omap2/prm7xx.h new file mode 100644 index 000000000..0ad1deba3 --- /dev/null +++ b/arch/arm/mach-omap2/prm7xx.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * DRA7xx PRM instance offset macros + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + * + * Generated by code originally written by: + * Paul Walmsley (paul@pwsan.com) + * Rajendra Nayak (rnayak@ti.com) + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_PRM7XX_H +#define __ARCH_ARM_MACH_OMAP2_PRM7XX_H + +#include "prcm-common.h" +#include "prm44xx_54xx.h" +#include "prm.h" + +#define DRA7XX_PRM_BASE 0x4ae06000 + +#define DRA7XX_PRM_REGADDR(inst, reg) \ + OMAP2_L4_IO_ADDRESS(DRA7XX_PRM_BASE + (inst) + (reg)) + + +/* PRM instances */ +#define DRA7XX_PRM_OCP_SOCKET_INST 0x0000 +#define DRA7XX_PRM_CKGEN_INST 0x0100 +#define DRA7XX_PRM_MPU_INST 0x0300 +#define DRA7XX_PRM_DSP1_INST 0x0400 +#define DRA7XX_PRM_IPU_INST 0x0500 +#define DRA7XX_PRM_COREAON_INST 0x0628 +#define DRA7XX_PRM_CORE_INST 0x0700 +#define DRA7XX_PRM_IVA_INST 0x0f00 +#define DRA7XX_PRM_CAM_INST 0x1000 +#define DRA7XX_PRM_DSS_INST 0x1100 +#define DRA7XX_PRM_GPU_INST 0x1200 +#define DRA7XX_PRM_L3INIT_INST 0x1300 +#define DRA7XX_PRM_L4PER_INST 0x1400 +#define DRA7XX_PRM_CUSTEFUSE_INST 0x1600 +#define DRA7XX_PRM_WKUPAON_INST 0x1724 +#define DRA7XX_PRM_WKUPAON_CM_INST 0x1800 +#define DRA7XX_PRM_EMU_INST 0x1900 +#define DRA7XX_PRM_EMU_CM_INST 0x1a00 +#define DRA7XX_PRM_DSP2_INST 0x1b00 +#define DRA7XX_PRM_EVE1_INST 0x1b40 +#define DRA7XX_PRM_EVE2_INST 0x1b80 +#define DRA7XX_PRM_EVE3_INST 0x1bc0 +#define DRA7XX_PRM_EVE4_INST 0x1c00 +#define DRA7XX_PRM_RTC_INST 0x1c60 +#define DRA7XX_PRM_VPE_INST 0x1c80 +#define DRA7XX_PRM_DEVICE_INST 0x1d00 + +/* PRM clockdomain register offsets (from instance start) */ +#define DRA7XX_PRM_WKUPAON_CM_WKUPAON_CDOFFS 0x0000 +#define DRA7XX_PRM_EMU_CM_EMU_CDOFFS 0x0000 + +/* PRM.CKGEN_PRM register offsets */ +#define DRA7XX_CM_CLKSEL_SYS DRA7XX_PRM_REGADDR(DRA7XX_PRM_CKGEN_INST, 0x0010) + +#endif diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c new file mode 100644 index 000000000..fb2d48cfe --- /dev/null +++ b/arch/arm/mach-omap2/prm_common.c @@ -0,0 +1,821 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP2+ common Power & Reset Management (PRM) IP block functions + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Tero Kristo <t-kristo@ti.com> + * + * For historical purposes, the API used to configure the PRM + * interrupt handler refers to it as the "PRCM interrupt." The + * underlying registers are located in the PRM on OMAP3/4. + * + * XXX This code should eventually be moved to a PRM driver. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/clk-provider.h> +#include <linux/clk/ti.h> + +#include "soc.h" +#include "prm2xxx_3xxx.h" +#include "prm2xxx.h" +#include "prm3xxx.h" +#include "prm33xx.h" +#include "prm44xx.h" +#include "prm54xx.h" +#include "prm7xx.h" +#include "prcm43xx.h" +#include "common.h" +#include "clock.h" +#include "cm.h" +#include "control.h" + +/* + * OMAP_PRCM_MAX_NR_PENDING_REG: maximum number of PRM_IRQ*_MPU regs + * XXX this is technically not needed, since + * omap_prcm_register_chain_handler() could allocate this based on the + * actual amount of memory needed for the SoC + */ +#define OMAP_PRCM_MAX_NR_PENDING_REG 2 + +/* + * prcm_irq_chips: an array of all of the "generic IRQ chips" in use + * by the PRCM interrupt handler code. There will be one 'chip' per + * PRM_{IRQSTATUS,IRQENABLE}_MPU register pair. (So OMAP3 will have + * one "chip" and OMAP4 will have two.) + */ +static struct irq_chip_generic **prcm_irq_chips; + +/* + * prcm_irq_setup: the PRCM IRQ parameters for the hardware the code + * is currently running on. Defined and passed by initialization code + * that calls omap_prcm_register_chain_handler(). + */ +static struct omap_prcm_irq_setup *prcm_irq_setup; + +/* prm_base: base virtual address of the PRM IP block */ +struct omap_domain_base prm_base; + +u16 prm_features; + +/* + * prm_ll_data: function pointers to SoC-specific implementations of + * common PRM functions + */ +static struct prm_ll_data null_prm_ll_data; +static struct prm_ll_data *prm_ll_data = &null_prm_ll_data; + +/* Private functions */ + +/* + * Move priority events from events to priority_events array + */ +static void omap_prcm_events_filter_priority(unsigned long *events, + unsigned long *priority_events) +{ + int i; + + for (i = 0; i < prcm_irq_setup->nr_regs; i++) { + priority_events[i] = + events[i] & prcm_irq_setup->priority_mask[i]; + events[i] ^= priority_events[i]; + } +} + +/* + * PRCM Interrupt Handler + * + * This is a common handler for the OMAP PRCM interrupts. Pending + * interrupts are detected by a call to prcm_pending_events and + * dispatched accordingly. Clearing of the wakeup events should be + * done by the SoC specific individual handlers. + */ +static void omap_prcm_irq_handler(struct irq_desc *desc) +{ + unsigned long pending[OMAP_PRCM_MAX_NR_PENDING_REG]; + unsigned long priority_pending[OMAP_PRCM_MAX_NR_PENDING_REG]; + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int virtirq; + int nr_irq = prcm_irq_setup->nr_regs * 32; + + /* + * If we are suspended, mask all interrupts from PRCM level, + * this does not ack them, and they will be pending until we + * re-enable the interrupts, at which point the + * omap_prcm_irq_handler will be executed again. The + * _save_and_clear_irqen() function must ensure that the PRM + * write to disable all IRQs has reached the PRM before + * returning, or spurious PRCM interrupts may occur during + * suspend. + */ + if (prcm_irq_setup->suspended) { + prcm_irq_setup->save_and_clear_irqen(prcm_irq_setup->saved_mask); + prcm_irq_setup->suspend_save_flag = true; + } + + /* + * Loop until all pending irqs are handled, since + * generic_handle_irq() can cause new irqs to come + */ + while (!prcm_irq_setup->suspended) { + prcm_irq_setup->read_pending_irqs(pending); + + /* No bit set, then all IRQs are handled */ + if (find_first_bit(pending, nr_irq) >= nr_irq) + break; + + omap_prcm_events_filter_priority(pending, priority_pending); + + /* + * Loop on all currently pending irqs so that new irqs + * cannot starve previously pending irqs + */ + + /* Serve priority events first */ + for_each_set_bit(virtirq, priority_pending, nr_irq) + generic_handle_irq(prcm_irq_setup->base_irq + virtirq); + + /* Serve normal events next */ + for_each_set_bit(virtirq, pending, nr_irq) + generic_handle_irq(prcm_irq_setup->base_irq + virtirq); + } + if (chip->irq_ack) + chip->irq_ack(&desc->irq_data); + if (chip->irq_eoi) + chip->irq_eoi(&desc->irq_data); + chip->irq_unmask(&desc->irq_data); + + prcm_irq_setup->ocp_barrier(); /* avoid spurious IRQs */ +} + +/* Public functions */ + +/** + * omap_prcm_event_to_irq - given a PRCM event name, returns the + * corresponding IRQ on which the handler should be registered + * @name: name of the PRCM interrupt bit to look up - see struct omap_prcm_irq + * + * Returns the Linux internal IRQ ID corresponding to @name upon success, + * or -ENOENT upon failure. + */ +int omap_prcm_event_to_irq(const char *name) +{ + int i; + + if (!prcm_irq_setup || !name) + return -ENOENT; + + for (i = 0; i < prcm_irq_setup->nr_irqs; i++) + if (!strcmp(prcm_irq_setup->irqs[i].name, name)) + return prcm_irq_setup->base_irq + + prcm_irq_setup->irqs[i].offset; + + return -ENOENT; +} + +/** + * omap_prcm_irq_cleanup - reverses memory allocated and other steps + * done by omap_prcm_register_chain_handler() + * + * No return value. + */ +void omap_prcm_irq_cleanup(void) +{ + unsigned int irq; + int i; + + if (!prcm_irq_setup) { + pr_err("PRCM: IRQ handler not initialized; cannot cleanup\n"); + return; + } + + if (prcm_irq_chips) { + for (i = 0; i < prcm_irq_setup->nr_regs; i++) { + if (prcm_irq_chips[i]) + irq_remove_generic_chip(prcm_irq_chips[i], + 0xffffffff, 0, 0); + prcm_irq_chips[i] = NULL; + } + kfree(prcm_irq_chips); + prcm_irq_chips = NULL; + } + + kfree(prcm_irq_setup->saved_mask); + prcm_irq_setup->saved_mask = NULL; + + kfree(prcm_irq_setup->priority_mask); + prcm_irq_setup->priority_mask = NULL; + + irq = prcm_irq_setup->irq; + irq_set_chained_handler(irq, NULL); + + if (prcm_irq_setup->base_irq > 0) + irq_free_descs(prcm_irq_setup->base_irq, + prcm_irq_setup->nr_regs * 32); + prcm_irq_setup->base_irq = 0; +} + +void omap_prcm_irq_prepare(void) +{ + prcm_irq_setup->suspended = true; +} + +void omap_prcm_irq_complete(void) +{ + prcm_irq_setup->suspended = false; + + /* If we have not saved the masks, do not attempt to restore */ + if (!prcm_irq_setup->suspend_save_flag) + return; + + prcm_irq_setup->suspend_save_flag = false; + + /* + * Re-enable all masked PRCM irq sources, this causes the PRCM + * interrupt to fire immediately if the events were masked + * previously in the chain handler + */ + prcm_irq_setup->restore_irqen(prcm_irq_setup->saved_mask); +} + +/** + * omap_prcm_register_chain_handler - initializes the prcm chained interrupt + * handler based on provided parameters + * @irq_setup: hardware data about the underlying PRM/PRCM + * + * Set up the PRCM chained interrupt handler on the PRCM IRQ. Sets up + * one generic IRQ chip per PRM interrupt status/enable register pair. + * Returns 0 upon success, -EINVAL if called twice or if invalid + * arguments are passed, or -ENOMEM on any other error. + */ +int omap_prcm_register_chain_handler(struct omap_prcm_irq_setup *irq_setup) +{ + int nr_regs; + u32 mask[OMAP_PRCM_MAX_NR_PENDING_REG]; + int offset, i, irq; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + if (!irq_setup) + return -EINVAL; + + nr_regs = irq_setup->nr_regs; + + if (prcm_irq_setup) { + pr_err("PRCM: already initialized; won't reinitialize\n"); + return -EINVAL; + } + + if (nr_regs > OMAP_PRCM_MAX_NR_PENDING_REG) { + pr_err("PRCM: nr_regs too large\n"); + return -EINVAL; + } + + prcm_irq_setup = irq_setup; + + prcm_irq_chips = kcalloc(nr_regs, sizeof(void *), GFP_KERNEL); + prcm_irq_setup->saved_mask = kcalloc(nr_regs, sizeof(u32), + GFP_KERNEL); + prcm_irq_setup->priority_mask = kcalloc(nr_regs, sizeof(u32), + GFP_KERNEL); + + if (!prcm_irq_chips || !prcm_irq_setup->saved_mask || + !prcm_irq_setup->priority_mask) + goto err; + + memset(mask, 0, sizeof(mask)); + + for (i = 0; i < irq_setup->nr_irqs; i++) { + offset = irq_setup->irqs[i].offset; + mask[offset >> 5] |= 1 << (offset & 0x1f); + if (irq_setup->irqs[i].priority) + irq_setup->priority_mask[offset >> 5] |= + 1 << (offset & 0x1f); + } + + irq = irq_setup->irq; + irq_set_chained_handler(irq, omap_prcm_irq_handler); + + irq_setup->base_irq = irq_alloc_descs(-1, 0, irq_setup->nr_regs * 32, + 0); + + if (irq_setup->base_irq < 0) { + pr_err("PRCM: failed to allocate irq descs: %d\n", + irq_setup->base_irq); + goto err; + } + + for (i = 0; i < irq_setup->nr_regs; i++) { + gc = irq_alloc_generic_chip("PRCM", 1, + irq_setup->base_irq + i * 32, prm_base.va, + handle_level_irq); + + if (!gc) { + pr_err("PRCM: failed to allocate generic chip\n"); + goto err; + } + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_ack_set_bit; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + + ct->regs.ack = irq_setup->ack + i * 4; + ct->regs.mask = irq_setup->mask + i * 4; + + irq_setup_generic_chip(gc, mask[i], 0, IRQ_NOREQUEST, 0); + prcm_irq_chips[i] = gc; + } + + irq = omap_prcm_event_to_irq("io"); + omap_pcs_legacy_init(irq, irq_setup->reconfigure_io_chain); + + return 0; + +err: + omap_prcm_irq_cleanup(); + return -ENOMEM; +} + +/** + * omap2_set_globals_prm - set the PRM base address (for early use) + * @prm: PRM base virtual address + * + * XXX Will be replaced when the PRM/CM drivers are completed. + */ +void __init omap2_set_globals_prm(void __iomem *prm) +{ + prm_base.va = prm; +} + +/** + * prm_read_reset_sources - return the sources of the SoC's last reset + * + * Return a u32 bitmask representing the reset sources that caused the + * SoC to reset. The low-level per-SoC functions called by this + * function remap the SoC-specific reset source bits into an + * OMAP-common set of reset source bits, defined in + * arch/arm/mach-omap2/prm.h. Returns the standardized reset source + * u32 bitmask from the hardware upon success, or returns (1 << + * OMAP_UNKNOWN_RST_SRC_ID_SHIFT) if no low-level read_reset_sources() + * function was registered. + */ +u32 prm_read_reset_sources(void) +{ + u32 ret = 1 << OMAP_UNKNOWN_RST_SRC_ID_SHIFT; + + if (prm_ll_data->read_reset_sources) + ret = prm_ll_data->read_reset_sources(); + else + WARN_ONCE(1, "prm: %s: no mapping function defined for reset sources\n", __func__); + + return ret; +} + +/** + * prm_was_any_context_lost_old - was device context lost? (old API) + * @part: PRM partition ID (e.g., OMAP4430_PRM_PARTITION) + * @inst: PRM instance offset (e.g., OMAP4430_PRM_MPU_INST) + * @idx: CONTEXT register offset + * + * Return 1 if any bits were set in the *_CONTEXT_* register + * identified by (@part, @inst, @idx), which means that some context + * was lost for that module; otherwise, return 0. XXX Deprecated; + * callers need to use a less-SoC-dependent way to identify hardware + * IP blocks. + */ +bool prm_was_any_context_lost_old(u8 part, s16 inst, u16 idx) +{ + bool ret = true; + + if (prm_ll_data->was_any_context_lost_old) + ret = prm_ll_data->was_any_context_lost_old(part, inst, idx); + else + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + + return ret; +} + +/** + * prm_clear_context_lost_flags_old - clear context loss flags (old API) + * @part: PRM partition ID (e.g., OMAP4430_PRM_PARTITION) + * @inst: PRM instance offset (e.g., OMAP4430_PRM_MPU_INST) + * @idx: CONTEXT register offset + * + * Clear hardware context loss bits for the module identified by + * (@part, @inst, @idx). No return value. XXX Deprecated; callers + * need to use a less-SoC-dependent way to identify hardware IP + * blocks. + */ +void prm_clear_context_loss_flags_old(u8 part, s16 inst, u16 idx) +{ + if (prm_ll_data->clear_context_loss_flags_old) + prm_ll_data->clear_context_loss_flags_old(part, inst, idx); + else + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); +} + +/** + * omap_prm_assert_hardreset - assert hardreset for an IP block + * @shift: register bit shift corresponding to the reset line + * @part: PRM partition + * @prm_mod: PRM submodule base or instance offset + * @offset: register offset + * + * Asserts a hardware reset line for an IP block. + */ +int omap_prm_assert_hardreset(u8 shift, u8 part, s16 prm_mod, u16 offset) +{ + if (!prm_ll_data->assert_hardreset) { + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + return -EINVAL; + } + + return prm_ll_data->assert_hardreset(shift, part, prm_mod, offset); +} + +/** + * omap_prm_deassert_hardreset - deassert hardreset for an IP block + * @shift: register bit shift corresponding to the reset line + * @st_shift: reset status bit shift corresponding to the reset line + * @part: PRM partition + * @prm_mod: PRM submodule base or instance offset + * @offset: register offset + * @st_offset: status register offset + * + * Deasserts a hardware reset line for an IP block. + */ +int omap_prm_deassert_hardreset(u8 shift, u8 st_shift, u8 part, s16 prm_mod, + u16 offset, u16 st_offset) +{ + if (!prm_ll_data->deassert_hardreset) { + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + return -EINVAL; + } + + return prm_ll_data->deassert_hardreset(shift, st_shift, part, prm_mod, + offset, st_offset); +} + +/** + * omap_prm_is_hardreset_asserted - check the hardreset status for an IP block + * @shift: register bit shift corresponding to the reset line + * @part: PRM partition + * @prm_mod: PRM submodule base or instance offset + * @offset: register offset + * + * Checks if a hardware reset line for an IP block is enabled or not. + */ +int omap_prm_is_hardreset_asserted(u8 shift, u8 part, s16 prm_mod, u16 offset) +{ + if (!prm_ll_data->is_hardreset_asserted) { + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + return -EINVAL; + } + + return prm_ll_data->is_hardreset_asserted(shift, part, prm_mod, offset); +} + +/** + * omap_prm_reconfigure_io_chain - clear latches and reconfigure I/O chain + * + * Clear any previously-latched I/O wakeup events and ensure that the + * I/O wakeup gates are aligned with the current mux settings. + * Calls SoC specific I/O chain reconfigure function if available, + * otherwise does nothing. + */ +void omap_prm_reconfigure_io_chain(void) +{ + if (!prcm_irq_setup || !prcm_irq_setup->reconfigure_io_chain) + return; + + prcm_irq_setup->reconfigure_io_chain(); +} + +/** + * omap_prm_reset_system - trigger global SW reset + * + * Triggers SoC specific global warm reset to reboot the device. + */ +void omap_prm_reset_system(void) +{ + if (!prm_ll_data->reset_system) { + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + return; + } + + prm_ll_data->reset_system(); + + while (1) { + cpu_relax(); + wfe(); + } +} + +/** + * omap_prm_clear_mod_irqs - clear wake-up events from PRCM interrupt + * @module: PRM module to clear wakeups from + * @regs: register to clear + * @wkst_mask: wkst bits to clear + * + * Clears any wakeup events for the module and register set defined. + * Uses SoC specific implementation to do the actual wakeup status + * clearing. + */ +int omap_prm_clear_mod_irqs(s16 module, u8 regs, u32 wkst_mask) +{ + if (!prm_ll_data->clear_mod_irqs) { + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + return -EINVAL; + } + + return prm_ll_data->clear_mod_irqs(module, regs, wkst_mask); +} + +/** + * omap_prm_vp_check_txdone - check voltage processor TX done status + * + * Checks if voltage processor transmission has been completed. + * Returns non-zero if a transmission has completed, 0 otherwise. + */ +u32 omap_prm_vp_check_txdone(u8 vp_id) +{ + if (!prm_ll_data->vp_check_txdone) { + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + return 0; + } + + return prm_ll_data->vp_check_txdone(vp_id); +} + +/** + * omap_prm_vp_clear_txdone - clears voltage processor TX done status + * + * Clears the status bit for completed voltage processor transmission + * returned by prm_vp_check_txdone. + */ +void omap_prm_vp_clear_txdone(u8 vp_id) +{ + if (!prm_ll_data->vp_clear_txdone) { + WARN_ONCE(1, "prm: %s: no mapping function defined\n", + __func__); + return; + } + + prm_ll_data->vp_clear_txdone(vp_id); +} + +/** + * prm_register - register per-SoC low-level data with the PRM + * @pld: low-level per-SoC OMAP PRM data & function pointers to register + * + * Register per-SoC low-level OMAP PRM data and function pointers with + * the OMAP PRM common interface. The caller must keep the data + * pointed to by @pld valid until it calls prm_unregister() and + * it returns successfully. Returns 0 upon success, -EINVAL if @pld + * is NULL, or -EEXIST if prm_register() has already been called + * without an intervening prm_unregister(). + */ +int prm_register(struct prm_ll_data *pld) +{ + if (!pld) + return -EINVAL; + + if (prm_ll_data != &null_prm_ll_data) + return -EEXIST; + + prm_ll_data = pld; + + return 0; +} + +/** + * prm_unregister - unregister per-SoC low-level data & function pointers + * @pld: low-level per-SoC OMAP PRM data & function pointers to unregister + * + * Unregister per-SoC low-level OMAP PRM data and function pointers + * that were previously registered with prm_register(). The + * caller may not destroy any of the data pointed to by @pld until + * this function returns successfully. Returns 0 upon success, or + * -EINVAL if @pld is NULL or if @pld does not match the struct + * prm_ll_data * previously registered by prm_register(). + */ +int prm_unregister(struct prm_ll_data *pld) +{ + if (!pld || prm_ll_data != pld) + return -EINVAL; + + prm_ll_data = &null_prm_ll_data; + + return 0; +} + +#ifdef CONFIG_ARCH_OMAP2 +static struct omap_prcm_init_data omap2_prm_data __initdata = { + .index = TI_CLKM_PRM, + .init = omap2xxx_prm_init, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP3 +static struct omap_prcm_init_data omap3_prm_data __initdata = { + .index = TI_CLKM_PRM, + .init = omap3xxx_prm_init, + + /* + * IVA2 offset is a negative value, must offset the prm_base + * address by this to get it to positive + */ + .offset = -OMAP3430_IVA2_MOD, +}; +#endif + +#if defined(CONFIG_SOC_AM33XX) || defined(CONFIG_SOC_TI81XX) +static struct omap_prcm_init_data am3_prm_data __initdata = { + .index = TI_CLKM_PRM, + .init = am33xx_prm_init, +}; +#endif + +#ifdef CONFIG_SOC_TI81XX +static struct omap_prcm_init_data dm814_pllss_data __initdata = { + .index = TI_CLKM_PLLSS, + .init = am33xx_prm_init, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP4 +static struct omap_prcm_init_data omap4_prm_data __initdata = { + .index = TI_CLKM_PRM, + .init = omap44xx_prm_init, + .device_inst_offset = OMAP4430_PRM_DEVICE_INST, + .flags = PRM_HAS_IO_WAKEUP | PRM_HAS_VOLTAGE, +}; +#endif + +#ifdef CONFIG_SOC_OMAP5 +static struct omap_prcm_init_data omap5_prm_data __initdata = { + .index = TI_CLKM_PRM, + .init = omap44xx_prm_init, + .device_inst_offset = OMAP54XX_PRM_DEVICE_INST, + .flags = PRM_HAS_IO_WAKEUP | PRM_HAS_VOLTAGE, +}; +#endif + +#ifdef CONFIG_SOC_DRA7XX +static struct omap_prcm_init_data dra7_prm_data __initdata = { + .index = TI_CLKM_PRM, + .init = omap44xx_prm_init, + .device_inst_offset = DRA7XX_PRM_DEVICE_INST, + .flags = PRM_HAS_IO_WAKEUP, +}; +#endif + +#ifdef CONFIG_SOC_AM43XX +static struct omap_prcm_init_data am4_prm_data __initdata = { + .index = TI_CLKM_PRM, + .init = omap44xx_prm_init, + .device_inst_offset = AM43XX_PRM_DEVICE_INST, + .flags = PRM_HAS_IO_WAKEUP, +}; +#endif + +#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5) +static struct omap_prcm_init_data scrm_data __initdata = { + .index = TI_CLKM_SCRM, +}; +#endif + +static const struct of_device_id omap_prcm_dt_match_table[] __initconst = { +#ifdef CONFIG_SOC_AM33XX + { .compatible = "ti,am3-prcm", .data = &am3_prm_data }, +#endif +#ifdef CONFIG_SOC_AM43XX + { .compatible = "ti,am4-prcm", .data = &am4_prm_data }, +#endif +#ifdef CONFIG_SOC_TI81XX + { .compatible = "ti,dm814-prcm", .data = &am3_prm_data }, + { .compatible = "ti,dm814-pllss", .data = &dm814_pllss_data }, + { .compatible = "ti,dm816-prcm", .data = &am3_prm_data }, +#endif +#ifdef CONFIG_ARCH_OMAP2 + { .compatible = "ti,omap2-prcm", .data = &omap2_prm_data }, +#endif +#ifdef CONFIG_ARCH_OMAP3 + { .compatible = "ti,omap3-prm", .data = &omap3_prm_data }, +#endif +#ifdef CONFIG_ARCH_OMAP4 + { .compatible = "ti,omap4-prm", .data = &omap4_prm_data }, + { .compatible = "ti,omap4-scrm", .data = &scrm_data }, +#endif +#ifdef CONFIG_SOC_OMAP5 + { .compatible = "ti,omap5-prm", .data = &omap5_prm_data }, + { .compatible = "ti,omap5-scrm", .data = &scrm_data }, +#endif +#ifdef CONFIG_SOC_DRA7XX + { .compatible = "ti,dra7-prm", .data = &dra7_prm_data }, +#endif + { } +}; + +/** + * omap2_prm_base_init - initialize iomappings for the PRM driver + * + * Detects and initializes the iomappings for the PRM driver, based + * on the DT data. Returns 0 in success, negative error value + * otherwise. + */ +int __init omap2_prm_base_init(void) +{ + struct device_node *np; + const struct of_device_id *match; + struct omap_prcm_init_data *data; + struct resource res; + int ret; + + for_each_matching_node_and_match(np, omap_prcm_dt_match_table, &match) { + data = (struct omap_prcm_init_data *)match->data; + + ret = of_address_to_resource(np, 0, &res); + if (ret) { + of_node_put(np); + return ret; + } + + data->mem = ioremap(res.start, resource_size(&res)); + + if (data->index == TI_CLKM_PRM) { + prm_base.va = data->mem + data->offset; + prm_base.pa = res.start + data->offset; + } + + data->np = np; + + if (data->init) + data->init(data); + } + + return 0; +} + +int __init omap2_prcm_base_init(void) +{ + int ret; + + ret = omap2_prm_base_init(); + if (ret) + return ret; + + return omap2_cm_base_init(); +} + +/** + * omap_prcm_init - low level init for the PRCM drivers + * + * Initializes the low level clock infrastructure for PRCM drivers. + * Returns 0 in success, negative error value in failure. + */ +int __init omap_prcm_init(void) +{ + struct device_node *np; + const struct of_device_id *match; + const struct omap_prcm_init_data *data; + int ret; + + for_each_matching_node_and_match(np, omap_prcm_dt_match_table, &match) { + data = match->data; + + ret = omap2_clk_provider_init(np, data->index, NULL, data->mem); + if (ret) { + of_node_put(np); + return ret; + } + } + + omap_cm_init(); + + return 0; +} + +static int __init prm_late_init(void) +{ + if (prm_ll_data->late_init) + return prm_ll_data->late_init(); + return 0; +} +subsys_initcall(prm_late_init); diff --git a/arch/arm/mach-omap2/prminst44xx.c b/arch/arm/mach-omap2/prminst44xx.c new file mode 100644 index 000000000..fc086d8b9 --- /dev/null +++ b/arch/arm/mach-omap2/prminst44xx.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 PRM instance functions + * + * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2011 Texas Instruments, Inc. + * Paul Walmsley + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/io.h> + +#include "iomap.h" +#include "common.h" +#include "prcm-common.h" +#include "prm44xx.h" +#include "prm54xx.h" +#include "prm7xx.h" +#include "prminst44xx.h" +#include "prm-regbits-44xx.h" +#include "prcm44xx.h" +#include "prcm43xx.h" +#include "prcm_mpu44xx.h" +#include "soc.h" + +static struct omap_domain_base _prm_bases[OMAP4_MAX_PRCM_PARTITIONS]; + +static s32 prm_dev_inst = PRM_INSTANCE_UNKNOWN; + +/** + * omap_prm_base_init - Populates the prm partitions + * + * Populates the base addresses of the _prm_bases + * array used for read/write of prm module registers. + */ +void omap_prm_base_init(void) +{ + memcpy(&_prm_bases[OMAP4430_PRM_PARTITION], &prm_base, + sizeof(prm_base)); + memcpy(&_prm_bases[OMAP4430_PRCM_MPU_PARTITION], &prcm_mpu_base, + sizeof(prcm_mpu_base)); +} + +s32 omap4_prmst_get_prm_dev_inst(void) +{ + return prm_dev_inst; +} + +void omap4_prminst_set_prm_dev_inst(s32 dev_inst) +{ + prm_dev_inst = dev_inst; +} + +/* Read a register in a PRM instance */ +u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx) +{ + BUG_ON(part >= OMAP4_MAX_PRCM_PARTITIONS || + part == OMAP4430_INVALID_PRCM_PARTITION || + !_prm_bases[part].va); + return readl_relaxed(_prm_bases[part].va + inst + idx); +} + +/* Write into a register in a PRM instance */ +void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx) +{ + BUG_ON(part >= OMAP4_MAX_PRCM_PARTITIONS || + part == OMAP4430_INVALID_PRCM_PARTITION || + !_prm_bases[part].va); + writel_relaxed(val, _prm_bases[part].va + inst + idx); +} + +/* Read-modify-write a register in PRM. Caller must lock */ +u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, s16 inst, + u16 idx) +{ + u32 v; + + v = omap4_prminst_read_inst_reg(part, inst, idx); + v &= ~mask; + v |= bits; + omap4_prminst_write_inst_reg(v, part, inst, idx); + + return v; +} + +/** + * omap4_prminst_is_hardreset_asserted - read the HW reset line state of + * submodules contained in the hwmod module + * @rstctrl_reg: RM_RSTCTRL register address for this module + * @shift: register bit shift corresponding to the reset line to check + * + * Returns 1 if the (sub)module hardreset line is currently asserted, + * 0 if the (sub)module hardreset line is not currently asserted, or + * -EINVAL upon parameter error. + */ +int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst, + u16 rstctrl_offs) +{ + u32 v; + + v = omap4_prminst_read_inst_reg(part, inst, rstctrl_offs); + v &= 1 << shift; + v >>= shift; + + return v; +} + +/** + * omap4_prminst_assert_hardreset - assert the HW reset line of a submodule + * @rstctrl_reg: RM_RSTCTRL register address for this module + * @shift: register bit shift corresponding to the reset line to assert + * + * Some IPs like dsp, ipu or iva contain processors that require an HW + * reset line to be asserted / deasserted in order to fully enable the + * IP. These modules may have multiple hard-reset lines that reset + * different 'submodules' inside the IP block. This function will + * place the submodule into reset. Returns 0 upon success or -EINVAL + * upon an argument error. + */ +int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst, + u16 rstctrl_offs) +{ + u32 mask = 1 << shift; + + omap4_prminst_rmw_inst_reg_bits(mask, mask, part, inst, rstctrl_offs); + + return 0; +} + +/** + * omap4_prminst_deassert_hardreset - deassert a submodule hardreset line and + * wait + * @shift: register bit shift corresponding to the reset line to deassert + * @st_shift: status bit offset corresponding to the reset line + * @part: PRM partition + * @inst: PRM instance offset + * @rstctrl_offs: reset register offset + * @rstst_offs: reset status register offset + * + * Some IPs like dsp, ipu or iva contain processors that require an HW + * reset line to be asserted / deasserted in order to fully enable the + * IP. These modules may have multiple hard-reset lines that reset + * different 'submodules' inside the IP block. This function will + * take the submodule out of reset and wait until the PRCM indicates + * that the reset has completed before returning. Returns 0 upon success or + * -EINVAL upon an argument error, -EEXIST if the submodule was already out + * of reset, or -EBUSY if the submodule did not exit reset promptly. + */ +int omap4_prminst_deassert_hardreset(u8 shift, u8 st_shift, u8 part, s16 inst, + u16 rstctrl_offs, u16 rstst_offs) +{ + int c; + u32 mask = 1 << shift; + u32 st_mask = 1 << st_shift; + + /* Check the current status to avoid de-asserting the line twice */ + if (omap4_prminst_is_hardreset_asserted(shift, part, inst, + rstctrl_offs) == 0) + return -EEXIST; + + /* Clear the reset status by writing 1 to the status bit */ + omap4_prminst_rmw_inst_reg_bits(0xffffffff, st_mask, part, inst, + rstst_offs); + /* de-assert the reset control line */ + omap4_prminst_rmw_inst_reg_bits(mask, 0, part, inst, rstctrl_offs); + /* wait the status to be set */ + omap_test_timeout(omap4_prminst_is_hardreset_asserted(st_shift, part, + inst, rstst_offs), + MAX_MODULE_HARDRESET_WAIT, c); + + return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0; +} + + +void omap4_prminst_global_warm_sw_reset(void) +{ + u32 v; + s32 inst = omap4_prmst_get_prm_dev_inst(); + + if (inst == PRM_INSTANCE_UNKNOWN) + return; + + v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, inst, + OMAP4_PRM_RSTCTRL_OFFSET); + v |= OMAP4430_RST_GLOBAL_WARM_SW_MASK; + omap4_prminst_write_inst_reg(v, OMAP4430_PRM_PARTITION, + inst, OMAP4_PRM_RSTCTRL_OFFSET); + + /* OCP barrier */ + v = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, + inst, OMAP4_PRM_RSTCTRL_OFFSET); +} diff --git a/arch/arm/mach-omap2/prminst44xx.h b/arch/arm/mach-omap2/prminst44xx.h new file mode 100644 index 000000000..11365a08e --- /dev/null +++ b/arch/arm/mach-omap2/prminst44xx.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP4 Power/Reset Management (PRM) function prototypes + * + * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2011 Texas Instruments, Inc. + * Paul Walmsley + */ +#ifndef __ARCH_ASM_MACH_OMAP2_PRMINST44XX_H +#define __ARCH_ASM_MACH_OMAP2_PRMINST44XX_H + +#define PRM_INSTANCE_UNKNOWN -1 +extern s32 omap4_prmst_get_prm_dev_inst(void); +void omap4_prminst_set_prm_dev_inst(s32 dev_inst); + +/* + * In an ideal world, we would not export these low-level functions, + * but this will probably take some time to fix properly + */ +extern u32 omap4_prminst_read_inst_reg(u8 part, s16 inst, u16 idx); +extern void omap4_prminst_write_inst_reg(u32 val, u8 part, s16 inst, u16 idx); +extern u32 omap4_prminst_rmw_inst_reg_bits(u32 mask, u32 bits, u8 part, + s16 inst, u16 idx); + +extern void omap4_prminst_global_warm_sw_reset(void); + +extern int omap4_prminst_is_hardreset_asserted(u8 shift, u8 part, s16 inst, + u16 rstctrl_offs); +extern int omap4_prminst_assert_hardreset(u8 shift, u8 part, s16 inst, + u16 rstctrl_offs); +int omap4_prminst_deassert_hardreset(u8 shift, u8 st_shift, u8 part, + s16 inst, u16 rstctrl_offs, + u16 rstst_offs); + +extern void omap_prm_base_init(void); + +#endif diff --git a/arch/arm/mach-omap2/scrm44xx.h b/arch/arm/mach-omap2/scrm44xx.h new file mode 100644 index 000000000..2e861aa95 --- /dev/null +++ b/arch/arm/mach-omap2/scrm44xx.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx SCRM registers and bitfields + * + * Copyright (C) 2010 Texas Instruments, Inc. + * + * Benoit Cousson (b-cousson@ti.com) + * + * This file is automatically generated from the OMAP hardware databases. + * We respectfully ask that any modifications to this file be coordinated + * with the public linux-omap@vger.kernel.org mailing list and the + * authors above to ensure that the autogeneration scripts are kept + * up-to-date with the file contents. + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_SCRM_44XX_H +#define __ARCH_ARM_MACH_OMAP2_SCRM_44XX_H + +#define OMAP4_SCRM_BASE 0x4a30a000 + +#define OMAP44XX_SCRM_REGADDR(reg) \ + OMAP2_L4_IO_ADDRESS(OMAP4_SCRM_BASE + (reg)) + +/* Registers offset */ +#define OMAP4_SCRM_CLKSETUPTIME OMAP44XX_SCRM_REGADDR(0x0100) + +/* CLKSETUPTIME */ +#define OMAP4_DOWNTIME_SHIFT 16 +#define OMAP4_DOWNTIME_MASK (0x3f << 16) +#define OMAP4_SETUPTIME_SHIFT 0 +#define OMAP4_SETUPTIME_MASK (0xfff << 0) + +#endif diff --git a/arch/arm/mach-omap2/sdrc.c b/arch/arm/mach-omap2/sdrc.c new file mode 100644 index 000000000..2be4106d0 --- /dev/null +++ b/arch/arm/mach-omap2/sdrc.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SMS/SDRC (SDRAM controller) common code for OMAP2/3 + * + * Copyright (C) 2005, 2008 Texas Instruments Inc. + * Copyright (C) 2005, 2008 Nokia Corporation + * + * Tony Lindgren <tony@atomide.com> + * Paul Walmsley + * Richard Woodruff <r-woodruff2@ti.com> + */ +#undef DEBUG + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include "common.h" +#include "clock.h" +#include "sdrc.h" + +static struct omap_sdrc_params *sdrc_init_params_cs0, *sdrc_init_params_cs1; + +void __iomem *omap2_sdrc_base; +void __iomem *omap2_sms_base; + +struct omap2_sms_regs { + u32 sms_sysconfig; +}; + +static struct omap2_sms_regs sms_context; + +/* SDRC_POWER register bits */ +#define SDRC_POWER_EXTCLKDIS_SHIFT 3 +#define SDRC_POWER_PWDENA_SHIFT 2 +#define SDRC_POWER_PAGEPOLICY_SHIFT 0 + +/** + * omap2_sms_save_context - Save SMS registers + * + * Save SMS registers that need to be restored after off mode. + */ +void omap2_sms_save_context(void) +{ + sms_context.sms_sysconfig = sms_read_reg(SMS_SYSCONFIG); +} + +/** + * omap2_sms_restore_context - Restore SMS registers + * + * Restore SMS registers that need to be Restored after off mode. + */ +void omap2_sms_restore_context(void) +{ + sms_write_reg(sms_context.sms_sysconfig, SMS_SYSCONFIG); +} + +/** + * omap2_sdrc_get_params - return SDRC register values for a given clock rate + * @r: SDRC clock rate (in Hz) + * @sdrc_cs0: chip select 0 ram timings ** + * @sdrc_cs1: chip select 1 ram timings ** + * + * Return pre-calculated values for the SDRC_ACTIM_CTRLA, + * SDRC_ACTIM_CTRLB, SDRC_RFR_CTRL and SDRC_MR registers in sdrc_cs[01] + * structs,for a given SDRC clock rate 'r'. + * These parameters control various timing delays in the SDRAM controller + * that are expressed in terms of the number of SDRC clock cycles to + * wait; hence the clock rate dependency. + * + * Supports 2 different timing parameters for both chip selects. + * + * Note 1: the sdrc_init_params_cs[01] must be sorted rate descending. + * Note 2: If sdrc_init_params_cs_1 is not NULL it must be of same size + * as sdrc_init_params_cs_0. + * + * Fills in the struct omap_sdrc_params * for each chip select. + * Returns 0 upon success or -1 upon failure. + */ +int omap2_sdrc_get_params(unsigned long r, + struct omap_sdrc_params **sdrc_cs0, + struct omap_sdrc_params **sdrc_cs1) +{ + struct omap_sdrc_params *sp0, *sp1; + + if (!sdrc_init_params_cs0) + return -1; + + sp0 = sdrc_init_params_cs0; + sp1 = sdrc_init_params_cs1; + + while (sp0->rate && sp0->rate != r) { + sp0++; + if (sdrc_init_params_cs1) + sp1++; + } + + if (!sp0->rate) + return -1; + + *sdrc_cs0 = sp0; + *sdrc_cs1 = sp1; + return 0; +} + + +void __init omap2_set_globals_sdrc(void __iomem *sdrc, void __iomem *sms) +{ + omap2_sdrc_base = sdrc; + omap2_sms_base = sms; +} + +/** + * omap2_sdrc_init - initialize SMS, SDRC devices on boot + * @sdrc_cs[01]: pointers to a null-terminated list of struct omap_sdrc_params + * Support for 2 chip selects timings + * + * Turn on smart idle modes for SDRAM scheduler and controller. + * Program a known-good configuration for the SDRC to deal with buggy + * bootloaders. + */ +void __init omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0, + struct omap_sdrc_params *sdrc_cs1) +{ + u32 l; + + l = sms_read_reg(SMS_SYSCONFIG); + l &= ~(0x3 << 3); + l |= (0x2 << 3); + sms_write_reg(l, SMS_SYSCONFIG); + + l = sdrc_read_reg(SDRC_SYSCONFIG); + l &= ~(0x3 << 3); + l |= (0x2 << 3); + sdrc_write_reg(l, SDRC_SYSCONFIG); + + sdrc_init_params_cs0 = sdrc_cs0; + sdrc_init_params_cs1 = sdrc_cs1; + + /* XXX Enable SRFRONIDLEREQ here also? */ + /* + * PWDENA should not be set due to 34xx erratum 1.150 - PWDENA + * can cause random memory corruption + */ + l = (1 << SDRC_POWER_EXTCLKDIS_SHIFT) | + (1 << SDRC_POWER_PAGEPOLICY_SHIFT); + sdrc_write_reg(l, SDRC_POWER); + omap2_sms_save_context(); +} diff --git a/arch/arm/mach-omap2/sdrc.h b/arch/arm/mach-omap2/sdrc.h new file mode 100644 index 000000000..5bdb83266 --- /dev/null +++ b/arch/arm/mach-omap2/sdrc.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ARCH_ARM_MACH_OMAP2_SDRC_H +#define __ARCH_ARM_MACH_OMAP2_SDRC_H + +/* + * OMAP2/3 SDRC/SMS macros and prototypes + * + * Copyright (C) 2007-2008, 2012 Texas Instruments, Inc. + * Copyright (C) 2007-2008 Nokia Corporation + * + * Paul Walmsley + * Tony Lindgren + * Richard Woodruff + */ +#undef DEBUG + +#ifndef __ASSEMBLER__ + +#include <linux/io.h> + +extern void __iomem *omap2_sdrc_base; +extern void __iomem *omap2_sms_base; + +#define OMAP_SDRC_REGADDR(reg) (omap2_sdrc_base + (reg)) +#define OMAP_SMS_REGADDR(reg) (omap2_sms_base + (reg)) + +/* SDRC global register get/set */ + +static inline void sdrc_write_reg(u32 val, u16 reg) +{ + writel_relaxed(val, OMAP_SDRC_REGADDR(reg)); +} + +static inline u32 sdrc_read_reg(u16 reg) +{ + return readl_relaxed(OMAP_SDRC_REGADDR(reg)); +} + +/* SMS global register get/set */ + +static inline void sms_write_reg(u32 val, u16 reg) +{ + writel_relaxed(val, OMAP_SMS_REGADDR(reg)); +} + +static inline u32 sms_read_reg(u16 reg) +{ + return readl_relaxed(OMAP_SMS_REGADDR(reg)); +} + +extern void omap2_set_globals_sdrc(void __iomem *sdrc, void __iomem *sms); + + +/** + * struct omap_sdrc_params - SDRC parameters for a given SDRC clock rate + * @rate: SDRC clock rate (in Hz) + * @actim_ctrla: Value to program to SDRC_ACTIM_CTRLA for this rate + * @actim_ctrlb: Value to program to SDRC_ACTIM_CTRLB for this rate + * @rfr_ctrl: Value to program to SDRC_RFR_CTRL for this rate + * @mr: Value to program to SDRC_MR for this rate + * + * This structure holds a pre-computed set of register values for the + * SDRC for a given SDRC clock rate and SDRAM chip. These are + * intended to be pre-computed and specified in an array in the board-*.c + * files. The structure is keyed off the 'rate' field. + */ +struct omap_sdrc_params { + unsigned long rate; + u32 actim_ctrla; + u32 actim_ctrlb; + u32 rfr_ctrl; + u32 mr; +}; + +#ifdef CONFIG_SOC_HAS_OMAP2_SDRC +void omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0, + struct omap_sdrc_params *sdrc_cs1); +#else +static inline void __init omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0, + struct omap_sdrc_params *sdrc_cs1) {}; +#endif + +int omap2_sdrc_get_params(unsigned long r, + struct omap_sdrc_params **sdrc_cs0, + struct omap_sdrc_params **sdrc_cs1); +void omap2_sms_save_context(void); +void omap2_sms_restore_context(void); + +struct memory_timings { + u32 m_type; /* ddr = 1, sdr = 0 */ + u32 dll_mode; /* use lock mode = 1, unlock mode = 0 */ + u32 slow_dll_ctrl; /* unlock mode, dll value for slow speed */ + u32 fast_dll_ctrl; /* unlock mode, dll value for fast speed */ + u32 base_cs; /* base chip select to use for calculations */ +}; + +extern void omap2xxx_sdrc_init_params(u32 force_lock_to_unlock_mode); +struct omap_sdrc_params *rx51_get_sdram_timings(void); + +u32 omap2xxx_sdrc_dll_is_unlocked(void); +u32 omap2xxx_sdrc_reprogram(u32 level, u32 force); + + +#else +#define OMAP242X_SDRC_REGADDR(reg) \ + OMAP2_L3_IO_ADDRESS(OMAP2420_SDRC_BASE + (reg)) +#define OMAP243X_SDRC_REGADDR(reg) \ + OMAP2_L3_IO_ADDRESS(OMAP243X_SDRC_BASE + (reg)) +#define OMAP34XX_SDRC_REGADDR(reg) \ + OMAP2_L3_IO_ADDRESS(OMAP343X_SDRC_BASE + (reg)) + +#endif /* __ASSEMBLER__ */ + +/* Minimum frequency that the SDRC DLL can lock at */ +#define MIN_SDRC_DLL_LOCK_FREQ 83000000 + +/* Scale factor for fixed-point arith in omap3_core_dpll_m2_set_rate() */ +#define SDRC_MPURATE_SCALE 8 + +/* 2^SDRC_MPURATE_BASE_SHIFT: MPU MHz that SDRC_MPURATE_LOOPS is defined for */ +#define SDRC_MPURATE_BASE_SHIFT 9 + +/* + * SDRC_MPURATE_LOOPS: Number of MPU loops to execute at + * 2^MPURATE_BASE_SHIFT MHz for SDRC to stabilize + */ +#define SDRC_MPURATE_LOOPS 96 + +/* SDRC register offsets - read/write with sdrc_{read,write}_reg() */ + +#define SDRC_SYSCONFIG 0x010 +#define SDRC_CS_CFG 0x040 +#define SDRC_SHARING 0x044 +#define SDRC_ERR_TYPE 0x04C +#define SDRC_DLLA_CTRL 0x060 +#define SDRC_DLLA_STATUS 0x064 +#define SDRC_DLLB_CTRL 0x068 +#define SDRC_DLLB_STATUS 0x06C +#define SDRC_POWER 0x070 +#define SDRC_MCFG_0 0x080 +#define SDRC_MR_0 0x084 +#define SDRC_EMR2_0 0x08c +#define SDRC_ACTIM_CTRL_A_0 0x09c +#define SDRC_ACTIM_CTRL_B_0 0x0a0 +#define SDRC_RFR_CTRL_0 0x0a4 +#define SDRC_MANUAL_0 0x0a8 +#define SDRC_MCFG_1 0x0B0 +#define SDRC_MR_1 0x0B4 +#define SDRC_EMR2_1 0x0BC +#define SDRC_ACTIM_CTRL_A_1 0x0C4 +#define SDRC_ACTIM_CTRL_B_1 0x0C8 +#define SDRC_RFR_CTRL_1 0x0D4 +#define SDRC_MANUAL_1 0x0D8 + +#define SDRC_POWER_AUTOCOUNT_SHIFT 8 +#define SDRC_POWER_AUTOCOUNT_MASK (0xffff << SDRC_POWER_AUTOCOUNT_SHIFT) +#define SDRC_POWER_CLKCTRL_SHIFT 4 +#define SDRC_POWER_CLKCTRL_MASK (0x3 << SDRC_POWER_CLKCTRL_SHIFT) +#define SDRC_SELF_REFRESH_ON_AUTOCOUNT (0x2 << SDRC_POWER_CLKCTRL_SHIFT) + +/* + * These values represent the number of memory clock cycles between + * autorefresh initiation. They assume 1 refresh per 64 ms (JEDEC), 8192 + * rows per device, and include a subtraction of a 50 cycle window in the + * event that the autorefresh command is delayed due to other SDRC activity. + * The '| 1' sets the ARE field to send one autorefresh when the autorefresh + * counter reaches 0. + * + * These represent optimal values for common parts, it won't work for all. + * As long as you scale down, most parameters are still work, they just + * become sub-optimal. The RFR value goes in the opposite direction. If you + * don't adjust it down as your clock period increases the refresh interval + * will not be met. Setting all parameters for complete worst case may work, + * but may cut memory performance by 2x. Due to errata the DLLs need to be + * unlocked and their value needs run time calibration. A dynamic call is + * need for that as no single right value exists across production samples. + * + * Only the FULL speed values are given. Current code is such that rate + * changes must be made at DPLLoutx2. The actual value adjustment for low + * frequency operation will be handled by omap_set_performance() + * + * By having the boot loader boot up in the fastest L4 speed available likely + * will result in something which you can switch between. + */ +#define SDRC_RFR_CTRL_165MHz (0x00044c00 | 1) +#define SDRC_RFR_CTRL_133MHz (0x0003de00 | 1) +#define SDRC_RFR_CTRL_100MHz (0x0002da01 | 1) +#define SDRC_RFR_CTRL_110MHz (0x0002da01 | 1) /* Need to calc */ +#define SDRC_RFR_CTRL_BYPASS (0x00005000 | 1) /* Need to calc */ + + +/* + * SMS register access + */ + +#define OMAP242X_SMS_REGADDR(reg) \ + (void __iomem *)OMAP2_L3_IO_ADDRESS(OMAP2420_SMS_BASE + reg) +#define OMAP243X_SMS_REGADDR(reg) \ + (void __iomem *)OMAP2_L3_IO_ADDRESS(OMAP243X_SMS_BASE + reg) +#define OMAP343X_SMS_REGADDR(reg) \ + (void __iomem *)OMAP2_L3_IO_ADDRESS(OMAP343X_SMS_BASE + reg) + +/* SMS register offsets - read/write with sms_{read,write}_reg() */ + +#define SMS_SYSCONFIG 0x010 +/* REVISIT: fill in other SMS registers here */ + + + +#endif diff --git a/arch/arm/mach-omap2/sdrc2xxx.c b/arch/arm/mach-omap2/sdrc2xxx.c new file mode 100644 index 000000000..529d46cfd --- /dev/null +++ b/arch/arm/mach-omap2/sdrc2xxx.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/sdrc2xxx.c + * + * SDRAM timing related functions for OMAP2xxx + * + * Copyright (C) 2005, 2008 Texas Instruments Inc. + * Copyright (C) 2005, 2008 Nokia Corporation + * + * Tony Lindgren <tony@atomide.com> + * Paul Walmsley + * Richard Woodruff <r-woodruff2@ti.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include "soc.h" +#include "iomap.h" +#include "common.h" +#include "prm2xxx.h" +#include "clock.h" +#include "sdrc.h" +#include "sram.h" + +/* Memory timing, DLL mode flags */ +#define M_DDR 1 +#define M_LOCK_CTRL (1 << 2) +#define M_UNLOCK 0 +#define M_LOCK 1 + + +static struct memory_timings mem_timings; +static u32 curr_perf_level = CORE_CLK_SRC_DPLL_X2; + +static u32 omap2xxx_sdrc_get_slow_dll_ctrl(void) +{ + return mem_timings.slow_dll_ctrl; +} + +static u32 omap2xxx_sdrc_get_fast_dll_ctrl(void) +{ + return mem_timings.fast_dll_ctrl; +} + +static u32 omap2xxx_sdrc_get_type(void) +{ + return mem_timings.m_type; +} + +/* + * Check the DLL lock state, and return tue if running in unlock mode. + * This is needed to compensate for the shifted DLL value in unlock mode. + */ +u32 omap2xxx_sdrc_dll_is_unlocked(void) +{ + /* dlla and dllb are a set */ + u32 dll_state = sdrc_read_reg(SDRC_DLLA_CTRL); + + if ((dll_state & (1 << 2)) == (1 << 2)) + return 1; + else + return 0; +} + +/* + * 'level' is the value to store to CM_CLKSEL2_PLL.CORE_CLK_SRC. + * Practical values are CORE_CLK_SRC_DPLL (for CORE_CLK = DPLL_CLK) or + * CORE_CLK_SRC_DPLL_X2 (for CORE_CLK = * DPLL_CLK * 2) + * + * Used by the clock framework during CORE DPLL changes + */ +u32 omap2xxx_sdrc_reprogram(u32 level, u32 force) +{ + u32 dll_ctrl, m_type; + u32 prev = curr_perf_level; + unsigned long flags; + + if ((curr_perf_level == level) && !force) + return prev; + + if (level == CORE_CLK_SRC_DPLL) + dll_ctrl = omap2xxx_sdrc_get_slow_dll_ctrl(); + else if (level == CORE_CLK_SRC_DPLL_X2) + dll_ctrl = omap2xxx_sdrc_get_fast_dll_ctrl(); + else + return prev; + + m_type = omap2xxx_sdrc_get_type(); + + local_irq_save(flags); + /* + * XXX These calls should be abstracted out through a + * prm2xxx.c function + */ + if (cpu_is_omap2420()) + writel_relaxed(0xffff, OMAP2420_PRCM_VOLTSETUP); + else + writel_relaxed(0xffff, OMAP2430_PRCM_VOLTSETUP); + omap2_sram_reprogram_sdrc(level, dll_ctrl, m_type); + curr_perf_level = level; + local_irq_restore(flags); + + return prev; +} + +/* Used by the clock framework during CORE DPLL changes */ +void omap2xxx_sdrc_init_params(u32 force_lock_to_unlock_mode) +{ + unsigned long dll_cnt; + u32 fast_dll = 0; + + /* DDR = 1, SDR = 0 */ + mem_timings.m_type = !((sdrc_read_reg(SDRC_MR_0) & 0x3) == 0x1); + + /* 2422 es2.05 and beyond has a single SIP DDR instead of 2 like others. + * In the case of 2422, its ok to use CS1 instead of CS0. + */ + if (cpu_is_omap2422()) + mem_timings.base_cs = 1; + else + mem_timings.base_cs = 0; + + if (mem_timings.m_type != M_DDR) + return; + + /* With DDR we need to determine the low frequency DLL value */ + if (((mem_timings.fast_dll_ctrl & (1 << 2)) == M_LOCK_CTRL)) + mem_timings.dll_mode = M_UNLOCK; + else + mem_timings.dll_mode = M_LOCK; + + if (mem_timings.base_cs == 0) { + fast_dll = sdrc_read_reg(SDRC_DLLA_CTRL); + dll_cnt = sdrc_read_reg(SDRC_DLLA_STATUS) & 0xff00; + } else { + fast_dll = sdrc_read_reg(SDRC_DLLB_CTRL); + dll_cnt = sdrc_read_reg(SDRC_DLLB_STATUS) & 0xff00; + } + if (force_lock_to_unlock_mode) { + fast_dll &= ~0xff00; + fast_dll |= dll_cnt; /* Current lock mode */ + } + /* set fast timings with DLL filter disabled */ + mem_timings.fast_dll_ctrl = (fast_dll | (3 << 8)); + + /* No disruptions, DDR will be offline & C-ABI not followed */ + omap2_sram_ddr_init(&mem_timings.slow_dll_ctrl, + mem_timings.fast_dll_ctrl, + mem_timings.base_cs, + force_lock_to_unlock_mode); + mem_timings.slow_dll_ctrl &= 0xff00; /* Keep lock value */ + + /* Turn status into unlock ctrl */ + mem_timings.slow_dll_ctrl |= + ((mem_timings.fast_dll_ctrl & 0xF) | (1 << 2)); + + /* 90 degree phase for anything below 133MHz + disable DLL filter */ + mem_timings.slow_dll_ctrl |= ((1 << 1) | (3 << 8)); +} diff --git a/arch/arm/mach-omap2/serial.h b/arch/arm/mach-omap2/serial.h new file mode 100644 index 000000000..7ca1fcff4 --- /dev/null +++ b/arch/arm/mach-omap2/serial.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support- Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* OMAP2 serial ports */ +#define OMAP2_UART1_BASE 0x4806a000 +#define OMAP2_UART2_BASE 0x4806c000 +#define OMAP2_UART3_BASE 0x4806e000 + +/* OMAP3 serial ports */ +#define OMAP3_UART1_BASE OMAP2_UART1_BASE +#define OMAP3_UART2_BASE OMAP2_UART2_BASE +#define OMAP3_UART3_BASE 0x49020000 +#define OMAP3_UART4_BASE 0x49042000 /* Only on 36xx */ +#define OMAP3_UART4_AM35XX_BASE 0x4809E000 /* Only on AM35xx */ + +/* OMAP4 serial ports */ +#define OMAP4_UART1_BASE OMAP2_UART1_BASE +#define OMAP4_UART2_BASE OMAP2_UART2_BASE +#define OMAP4_UART3_BASE 0x48020000 +#define OMAP4_UART4_BASE 0x4806e000 + +/* TI81XX serial ports */ +#define TI81XX_UART1_BASE 0x48020000 +#define TI81XX_UART2_BASE 0x48022000 +#define TI81XX_UART3_BASE 0x48024000 + +/* AM3505/3517 UART4 */ +#define AM35XX_UART4_BASE 0x4809E000 /* Only on AM3505/3517 */ + +/* AM33XX serial port */ +#define AM33XX_UART1_BASE 0x44E09000 + +/* OMAP5 serial ports */ +#define OMAP5_UART1_BASE OMAP2_UART1_BASE +#define OMAP5_UART2_BASE OMAP2_UART2_BASE +#define OMAP5_UART3_BASE OMAP4_UART3_BASE +#define OMAP5_UART4_BASE OMAP4_UART4_BASE +#define OMAP5_UART5_BASE 0x48066000 +#define OMAP5_UART6_BASE 0x48068000 + +/* External port on Zoom2/3 */ +#define ZOOM_UART_BASE 0x10000000 +#define ZOOM_UART_VIRT 0xfa400000 + +#define OMAP_PORT_SHIFT 2 +#define ZOOM_PORT_SHIFT 1 + +#define OMAP24XX_BASE_BAUD (48000000/16) + +#ifndef __ASSEMBLER__ + +struct omap_board_data; +struct omap_uart_port_info; + +extern void omap_serial_init(void); +extern void omap_serial_board_init(struct omap_uart_port_info *platform_data); +extern void omap_serial_init_port(struct omap_board_data *bdata, + struct omap_uart_port_info *platform_data); +#endif diff --git a/arch/arm/mach-omap2/sleep24xx.S b/arch/arm/mach-omap2/sleep24xx.S new file mode 100644 index 000000000..84d8c43ad --- /dev/null +++ b/arch/arm/mach-omap2/sleep24xx.S @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * linux/arch/arm/mach-omap2/sleep.S + * + * (C) Copyright 2004 + * Texas Instruments, <www.ti.com> + * Richard Woodruff <r-woodruff2@ti.com> + * + * (C) Copyright 2006 Nokia Corporation + * Fixed idle loop sleep + * Igor Stoppa <igor.stoppa@nokia.com> + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + +#include "omap24xx.h" +#include "sdrc.h" + +/* First address of reserved address space? apparently valid for OMAP2 & 3 */ +#define A_SDRC0_V (0xC0000000) + + .text + +/* + * omap24xx_cpu_suspend() - Forces OMAP into deep sleep state by completing + * SDRC shutdown then ARM shutdown. Upon wake MPU is back on so just restore + * SDRC. + * + * Input: + * R0 : DLL ctrl value pre-Sleep + * R1 : SDRC_DLLA_CTRL + * R2 : SDRC_POWER + * + * The if the DPLL is going to AutoIdle. It seems like the DPLL may be back on + * when we get called, but the DLL probably isn't. We will wait a bit more in + * case the DPLL isn't quite there yet. The code will wait on DLL for DDR even + * if in unlocked mode. + * + * For less than 242x-ES2.2 upon wake from a sleep mode where the external + * oscillator was stopped, a timing bug exists where a non-stabilized 12MHz + * clock can pass into the PRCM can cause problems at DSP and IVA. + * To work around this the code will switch to the 32kHz source prior to sleep. + * Post sleep we will shift back to using the DPLL. Apparently, + * CM_IDLEST_CLKGEN does not reflect the full clock change so you need to wait + * 3x12MHz + 3x32kHz clocks for a full switch. + * + * The DLL load value is not kept in RETENTION or OFF. It needs to be restored + * at wake + */ + .align 3 +ENTRY(omap24xx_cpu_suspend) + stmfd sp!, {r0 - r12, lr} @ save registers on stack + mov r3, #0x0 @ clear for mcr call + mcr p15, 0, r3, c7, c10, 4 @ memory barrier, hope SDR/DDR finished + nop + nop + ldr r4, [r2] @ read SDRC_POWER + orr r4, r4, #0x40 @ enable self refresh on idle req + mov r5, #0x2000 @ set delay (DPLL relock + DLL relock) + str r4, [r2] @ make it so + nop + mcr p15, 0, r3, c7, c0, 4 @ wait for interrupt + nop +loop: + subs r5, r5, #0x1 @ awake, wait just a bit + bne loop + + /* The DPLL has to be on before we take the DDR out of self refresh */ + bic r4, r4, #0x40 @ now clear self refresh bit. + str r4, [r2] @ write to SDRC_POWER + ldr r4, A_SDRC0 @ make a clock happen + ldr r4, [r4] @ read A_SDRC0 + nop @ start auto refresh only after clk ok + movs r0, r0 @ see if DDR or SDR + strne r0, [r1] @ rewrite DLLA to force DLL reload + addne r1, r1, #0x8 @ move to DLLB + strne r0, [r1] @ rewrite DLLB to force DLL reload + + mov r5, #0x1000 +loop2: + subs r5, r5, #0x1 + bne loop2 + /* resume*/ + ldmfd sp!, {r0 - r12, pc} @ restore regs and return + +A_SDRC0: + .word A_SDRC0_V + +ENTRY(omap24xx_cpu_suspend_sz) + .word . - omap24xx_cpu_suspend diff --git a/arch/arm/mach-omap2/sleep33xx.S b/arch/arm/mach-omap2/sleep33xx.S new file mode 100644 index 000000000..ac3d0b363 --- /dev/null +++ b/arch/arm/mach-omap2/sleep33xx.S @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Low level suspend code for AM33XX SoCs + * + * Copyright (C) 2012-2018 Texas Instruments Incorporated - https://www.ti.com/ + * Dave Gerlach, Vaibhav Bedia + */ + +#include <linux/linkage.h> +#include <linux/platform_data/pm33xx.h> +#include <linux/ti-emif-sram.h> +#include <asm/assembler.h> +#include <asm/memory.h> + +#include "iomap.h" +#include "cm33xx.h" +#include "pm-asm-offsets.h" + +#define AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED 0x00030000 +#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003 +#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002 + +/* replicated define because linux/bitops.h cannot be included in assembly */ +#define BIT(nr) (1 << (nr)) + + .arm + .arch armv7-a + .align 3 + +ENTRY(am33xx_do_wfi) + stmfd sp!, {r4 - r11, lr} @ save registers on stack + + /* Save wfi_flags arg to data space */ + mov r4, r0 + adr r3, am33xx_pm_ro_sram_data + ldr r2, [r3, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET] + str r4, [r2, #AMX3_PM_WFI_FLAGS_OFFSET] + + /* Only flush cache is we know we are losing MPU context */ + tst r4, #WFI_FLAG_FLUSH_CACHE + beq cache_skip_flush + + /* + * Flush all data from the L1 and L2 data cache before disabling + * SCTLR.C bit. + */ + ldr r1, kernel_flush + blx r1 + + /* + * Clear the SCTLR.C bit to prevent further data cache + * allocation. Clearing SCTLR.C would make all the data accesses + * strongly ordered and would not hit the cache. + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + + /* + * Invalidate L1 and L2 data cache. + */ + ldr r1, kernel_flush + blx r1 + + adr r3, am33xx_pm_ro_sram_data + ldr r2, [r3, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET] + ldr r4, [r2, #AMX3_PM_WFI_FLAGS_OFFSET] + +cache_skip_flush: + /* Check if we want self refresh */ + tst r4, #WFI_FLAG_SELF_REFRESH + beq emif_skip_enter_sr + + adr r9, am33xx_emif_sram_table + + ldr r3, [r9, #EMIF_PM_ENTER_SR_OFFSET] + blx r3 + +emif_skip_enter_sr: + /* Only necessary if PER is losing context */ + tst r4, #WFI_FLAG_SAVE_EMIF + beq emif_skip_save + + ldr r3, [r9, #EMIF_PM_SAVE_CONTEXT_OFFSET] + blx r3 + +emif_skip_save: + /* Only can disable EMIF if we have entered self refresh */ + tst r4, #WFI_FLAG_SELF_REFRESH + beq emif_skip_disable + + /* Disable EMIF */ + ldr r1, virt_emif_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + + ldr r1, virt_emif_clkctrl +wait_emif_disable: + ldr r2, [r1] + mov r3, #AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED + cmp r2, r3 + bne wait_emif_disable + +emif_skip_disable: + tst r4, #WFI_FLAG_WAKE_M3 + beq wkup_m3_skip + + /* + * For the MPU WFI to be registered as an interrupt + * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set + * to DISABLED + */ + ldr r1, virt_mpu_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + +wkup_m3_skip: + /* + * Execute an ISB instruction to ensure that all of the + * CP15 register changes have been committed. + */ + isb + + /* + * Execute a barrier instruction to ensure that all cache, + * TLB and branch predictor maintenance operations issued + * have completed. + */ + dsb + dmb + + /* + * Execute a WFI instruction and wait until the + * STANDBYWFI output is asserted to indicate that the + * CPU is in idle and low power state. CPU can specualatively + * prefetch the instructions so add NOPs after WFI. Thirteen + * NOPs as per Cortex-A8 pipeline. + */ + wfi + + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + /* We come here in case of an abort due to a late interrupt */ + + /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */ + ldr r1, virt_mpu_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] + + /* Re-enable EMIF */ + ldr r1, virt_emif_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] +wait_emif_enable: + ldr r3, [r1] + cmp r2, r3 + bne wait_emif_enable + + /* Only necessary if PER is losing context */ + tst r4, #WFI_FLAG_SELF_REFRESH + beq emif_skip_exit_sr_abt + + adr r9, am33xx_emif_sram_table + ldr r1, [r9, #EMIF_PM_ABORT_SR_OFFSET] + blx r1 + +emif_skip_exit_sr_abt: + tst r4, #WFI_FLAG_FLUSH_CACHE + beq cache_skip_restore + + /* + * Set SCTLR.C bit to allow data cache allocation + */ + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 2) @ Enable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + +cache_skip_restore: + /* Let the suspend code know about the abort */ + mov r0, #1 + ldmfd sp!, {r4 - r11, pc} @ restore regs and return +ENDPROC(am33xx_do_wfi) + + .align +ENTRY(am33xx_resume_offset) + .word . - am33xx_do_wfi + +ENTRY(am33xx_resume_from_deep_sleep) + /* Re-enable EMIF */ + ldr r0, phys_emif_clkctrl + mov r1, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r1, [r0] +wait_emif_enable1: + ldr r2, [r0] + cmp r1, r2 + bne wait_emif_enable1 + + adr r9, am33xx_emif_sram_table + + ldr r1, [r9, #EMIF_PM_RESTORE_CONTEXT_OFFSET] + blx r1 + + ldr r1, [r9, #EMIF_PM_EXIT_SR_OFFSET] + blx r1 + +resume_to_ddr: + /* We are back. Branch to the common CPU resume routine */ + mov r0, #0 + ldr pc, resume_addr +ENDPROC(am33xx_resume_from_deep_sleep) + +/* + * Local variables + */ + .align +kernel_flush: + .word v7_flush_dcache_all +virt_mpu_clkctrl: + .word AM33XX_CM_MPU_MPU_CLKCTRL +virt_emif_clkctrl: + .word AM33XX_CM_PER_EMIF_CLKCTRL +phys_emif_clkctrl: + .word (AM33XX_CM_BASE + AM33XX_CM_PER_MOD + \ + AM33XX_CM_PER_EMIF_CLKCTRL_OFFSET) + +.align 3 +/* DDR related defines */ +am33xx_emif_sram_table: + .space EMIF_PM_FUNCTIONS_SIZE + +ENTRY(am33xx_pm_sram) + .word am33xx_do_wfi + .word am33xx_do_wfi_sz + .word am33xx_resume_offset + .word am33xx_emif_sram_table + .word am33xx_pm_ro_sram_data + +resume_addr: +.word cpu_resume - PAGE_OFFSET + 0x80000000 + +.align 3 +ENTRY(am33xx_pm_ro_sram_data) + .space AMX3_PM_RO_SRAM_DATA_SIZE + +ENTRY(am33xx_do_wfi_sz) + .word . - am33xx_do_wfi diff --git a/arch/arm/mach-omap2/sleep34xx.S b/arch/arm/mach-omap2/sleep34xx.S new file mode 100644 index 000000000..c4e97d35c --- /dev/null +++ b/arch/arm/mach-omap2/sleep34xx.S @@ -0,0 +1,569 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * (C) Copyright 2007 + * Texas Instruments + * Karthik Dasu <karthik-dp@ti.com> + * + * (C) Copyright 2004 + * Texas Instruments, <www.ti.com> + * Richard Woodruff <r-woodruff2@ti.com> + */ +#include <linux/linkage.h> + +#include <asm/assembler.h> + +#include "omap34xx.h" +#include "iomap.h" +#include "cm3xxx.h" +#include "prm3xxx.h" +#include "sdrc.h" +#include "sram.h" +#include "control.h" + +/* + * Registers access definitions + */ +#define SDRC_SCRATCHPAD_SEM_OFFS 0xc +#define SDRC_SCRATCHPAD_SEM_V OMAP343X_SCRATCHPAD_REGADDR\ + (SDRC_SCRATCHPAD_SEM_OFFS) +#define PM_PREPWSTST_CORE_P OMAP3430_PRM_BASE + CORE_MOD +\ + OMAP3430_PM_PREPWSTST +#define PM_PWSTCTRL_MPU_P OMAP3430_PRM_BASE + MPU_MOD + OMAP2_PM_PWSTCTRL +#define CM_IDLEST1_CORE_V OMAP34XX_CM_REGADDR(CORE_MOD, CM_IDLEST1) +#define CM_IDLEST_CKGEN_V OMAP34XX_CM_REGADDR(PLL_MOD, CM_IDLEST) +#define SRAM_BASE_P OMAP3_SRAM_PA +#define CONTROL_STAT OMAP343X_CTRL_BASE + OMAP343X_CONTROL_STATUS +#define CONTROL_MEM_RTA_CTRL (OMAP343X_CTRL_BASE +\ + OMAP36XX_CONTROL_MEM_RTA_CTRL) + +/* Move this as correct place is available */ +#define SCRATCHPAD_MEM_OFFS 0x310 +#define SCRATCHPAD_BASE_P (OMAP343X_CTRL_BASE +\ + OMAP343X_CONTROL_MEM_WKUP +\ + SCRATCHPAD_MEM_OFFS) +#define SDRC_POWER_V OMAP34XX_SDRC_REGADDR(SDRC_POWER) +#define SDRC_SYSCONFIG_P (OMAP343X_SDRC_BASE + SDRC_SYSCONFIG) +#define SDRC_MR_0_P (OMAP343X_SDRC_BASE + SDRC_MR_0) +#define SDRC_EMR2_0_P (OMAP343X_SDRC_BASE + SDRC_EMR2_0) +#define SDRC_MANUAL_0_P (OMAP343X_SDRC_BASE + SDRC_MANUAL_0) +#define SDRC_MR_1_P (OMAP343X_SDRC_BASE + SDRC_MR_1) +#define SDRC_EMR2_1_P (OMAP343X_SDRC_BASE + SDRC_EMR2_1) +#define SDRC_MANUAL_1_P (OMAP343X_SDRC_BASE + SDRC_MANUAL_1) +#define SDRC_DLLA_STATUS_V OMAP34XX_SDRC_REGADDR(SDRC_DLLA_STATUS) +#define SDRC_DLLA_CTRL_V OMAP34XX_SDRC_REGADDR(SDRC_DLLA_CTRL) + +/* + * This file needs be built unconditionally as ARM to interoperate correctly + * with non-Thumb-2-capable firmware. + */ + .arm + +/* + * API functions + */ + + .text +/* + * L2 cache needs to be toggled for stable OFF mode functionality on 3630. + * This function sets up a flag that will allow for this toggling to take + * place on 3630. Hopefully some version in the future may not need this. + */ +ENTRY(enable_omap3630_toggle_l2_on_restore) + stmfd sp!, {lr} @ save registers on stack + /* Setup so that we will disable and enable l2 */ + mov r1, #0x1 + adr r3, l2dis_3630_offset + ldr r2, [r3] @ value for offset + str r1, [r2, r3] @ write to l2dis_3630 + ldmfd sp!, {pc} @ restore regs and return +ENDPROC(enable_omap3630_toggle_l2_on_restore) + +/* + * Function to call rom code to save secure ram context. + * + * r0 = physical address of the parameters + */ + .arch armv7-a + .arch_extension sec +ENTRY(save_secure_ram_context) + stmfd sp!, {r4 - r11, lr} @ save registers on stack + mov r3, r0 @ physical address of parameters + mov r0, #25 @ set service ID for PPA + mov r12, r0 @ copy secure service ID in r12 + mov r1, #0 @ set task id for ROM code in r1 + mov r2, #4 @ set some flags in r2, r6 + mov r6, #0xff + dsb @ data write barrier + dmb @ data memory barrier + smc #1 @ call SMI monitor (smi #1) + nop + nop + nop + nop + ldmfd sp!, {r4 - r11, pc} +ENDPROC(save_secure_ram_context) + +/* + * ====================== + * == Idle entry point == + * ====================== + */ + +/* + * Forces OMAP into idle state + * + * omap34xx_cpu_suspend() - This bit of code saves the CPU context if needed + * and executes the WFI instruction. Calling WFI effectively changes the + * power domains states to the desired target power states. + * + * + * Notes: + * - only the minimum set of functions gets copied to internal SRAM at boot + * and after wake-up from OFF mode, cf. omap_push_sram_idle. The function + * pointers in SDRAM or SRAM are called depending on the desired low power + * target state. + * - when the OMAP wakes up it continues at different execution points + * depending on the low power mode (non-OFF vs OFF modes), + * cf. 'Resume path for xxx mode' comments. + */ + .align 3 +ENTRY(omap34xx_cpu_suspend) + stmfd sp!, {r4 - r11, lr} @ save registers on stack + + /* + * r0 contains information about saving context: + * 0 - No context lost + * 1 - Only L1 and logic lost + * 2 - Only L2 lost (Even L1 is retained we clean it along with L2) + * 3 - Both L1 and L2 lost and logic lost + */ + + /* + * For OFF mode: save context and jump to WFI in SDRAM (omap3_do_wfi) + * For non-OFF modes: jump to the WFI code in SRAM (omap3_do_wfi_sram) + */ + ldr r4, omap3_do_wfi_sram_addr + ldr r5, [r4] + cmp r0, #0x0 @ If no context save required, + bxeq r5 @ jump to the WFI code in SRAM + + + /* Otherwise fall through to the save context code */ +save_context_wfi: + /* + * jump out to kernel flush routine + * - reuse that code is better + * - it executes in a cached space so is faster than refetch per-block + * - should be faster and will change with kernel + * - 'might' have to copy address, load and jump to it + * Flush all data from the L1 data cache before disabling + * SCTLR.C bit. + */ + ldr r1, kernel_flush + mov lr, pc + bx r1 + + /* + * Clear the SCTLR.C bit to prevent further data cache + * allocation. Clearing SCTLR.C would make all the data accesses + * strongly ordered and would not hit the cache. + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + + /* + * Invalidate L1 data cache. Even though only invalidate is + * necessary exported flush API is used here. Doing clean + * on already clean cache would be almost NOP. + */ + ldr r1, kernel_flush + blx r1 + b omap3_do_wfi +ENDPROC(omap34xx_cpu_suspend) +omap3_do_wfi_sram_addr: + .word omap3_do_wfi_sram +kernel_flush: + .word v7_flush_dcache_all + +/* =================================== + * == WFI instruction => Enter idle == + * =================================== + */ + +/* + * Do WFI instruction + * Includes the resume path for non-OFF modes + * + * This code gets copied to internal SRAM and is accessible + * from both SDRAM and SRAM: + * - executed from SRAM for non-off modes (omap3_do_wfi_sram), + * - executed from SDRAM for OFF mode (omap3_do_wfi). + */ + .align 3 +ENTRY(omap3_do_wfi) + ldr r4, sdrc_power @ read the SDRC_POWER register + ldr r5, [r4] @ read the contents of SDRC_POWER + orr r5, r5, #0x40 @ enable self refresh on idle req + str r5, [r4] @ write back to SDRC_POWER register + + /* Data memory barrier and Data sync barrier */ + dsb + dmb + +/* + * =================================== + * == WFI instruction => Enter idle == + * =================================== + */ + wfi @ wait for interrupt + +/* + * =================================== + * == Resume path for non-OFF modes == + * =================================== + */ + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + +/* + * This function implements the erratum ID i581 WA: + * SDRC state restore before accessing the SDRAM + * + * Only used at return from non-OFF mode. For OFF + * mode the ROM code configures the SDRC and + * the DPLL before calling the restore code directly + * from DDR. + */ + +/* Make sure SDRC accesses are ok */ +wait_sdrc_ok: + +/* DPLL3 must be locked before accessing the SDRC. Maybe the HW ensures this */ + ldr r4, cm_idlest_ckgen +wait_dpll3_lock: + ldr r5, [r4] + tst r5, #1 + beq wait_dpll3_lock + + ldr r4, cm_idlest1_core +wait_sdrc_ready: + ldr r5, [r4] + tst r5, #0x2 + bne wait_sdrc_ready + /* allow DLL powerdown upon hw idle req */ + ldr r4, sdrc_power + ldr r5, [r4] + bic r5, r5, #0x40 + str r5, [r4] + +is_dll_in_lock_mode: + /* Is dll in lock mode? */ + ldr r4, sdrc_dlla_ctrl + ldr r5, [r4] + tst r5, #0x4 + bne exit_nonoff_modes @ Return if locked + /* wait till dll locks */ +wait_dll_lock_timed: + ldr r4, sdrc_dlla_status + /* Wait 20uS for lock */ + mov r6, #8 +wait_dll_lock: + subs r6, r6, #0x1 + beq kick_dll + ldr r5, [r4] + and r5, r5, #0x4 + cmp r5, #0x4 + bne wait_dll_lock + b exit_nonoff_modes @ Return when locked + + /* disable/reenable DLL if not locked */ +kick_dll: + ldr r4, sdrc_dlla_ctrl + ldr r5, [r4] + mov r6, r5 + bic r6, #(1<<3) @ disable dll + str r6, [r4] + dsb + orr r6, r6, #(1<<3) @ enable dll + str r6, [r4] + dsb + b wait_dll_lock_timed + +exit_nonoff_modes: + /* Re-enable C-bit if needed */ + mrc p15, 0, r0, c1, c0, 0 + tst r0, #(1 << 2) @ Check C bit enabled? + orreq r0, r0, #(1 << 2) @ Enable the C bit if cleared + mcreq p15, 0, r0, c1, c0, 0 + isb + +/* + * =================================== + * == Exit point from non-OFF modes == + * =================================== + */ + ldmfd sp!, {r4 - r11, pc} @ restore regs and return +ENDPROC(omap3_do_wfi) +sdrc_power: + .word SDRC_POWER_V +cm_idlest1_core: + .word CM_IDLEST1_CORE_V +cm_idlest_ckgen: + .word CM_IDLEST_CKGEN_V +sdrc_dlla_status: + .word SDRC_DLLA_STATUS_V +sdrc_dlla_ctrl: + .word SDRC_DLLA_CTRL_V +ENTRY(omap3_do_wfi_sz) + .word . - omap3_do_wfi + + +/* + * ============================== + * == Resume path for OFF mode == + * ============================== + */ + +/* + * The restore_* functions are called by the ROM code + * when back from WFI in OFF mode. + * Cf. the get_*restore_pointer functions. + * + * restore_es3: applies to 34xx >= ES3.0 + * restore_3630: applies to 36xx + * restore: common code for 3xxx + * + * Note: when back from CORE and MPU OFF mode we are running + * from SDRAM, without MMU, without the caches and prediction. + * Also the SRAM content has been cleared. + */ +ENTRY(omap3_restore_es3) + ldr r5, pm_prepwstst_core_p + ldr r4, [r5] + and r4, r4, #0x3 + cmp r4, #0x0 @ Check if previous power state of CORE is OFF + bne omap3_restore @ Fall through to OMAP3 common code + adr r0, es3_sdrc_fix + ldr r1, sram_base + ldr r2, es3_sdrc_fix_sz + mov r2, r2, ror #2 +copy_to_sram: + ldmia r0!, {r3} @ val = *src + stmia r1!, {r3} @ *dst = val + subs r2, r2, #0x1 @ num_words-- + bne copy_to_sram + ldr r1, sram_base + blx r1 + b omap3_restore @ Fall through to OMAP3 common code +ENDPROC(omap3_restore_es3) + +ENTRY(omap3_restore_3630) + ldr r1, pm_prepwstst_core_p + ldr r2, [r1] + and r2, r2, #0x3 + cmp r2, #0x0 @ Check if previous power state of CORE is OFF + bne omap3_restore @ Fall through to OMAP3 common code + /* Disable RTA before giving control */ + ldr r1, control_mem_rta + mov r2, #OMAP36XX_RTA_DISABLE + str r2, [r1] +ENDPROC(omap3_restore_3630) + + /* Fall through to common code for the remaining logic */ + +ENTRY(omap3_restore) + /* + * Read the pwstctrl register to check the reason for mpu reset. + * This tells us what was lost. + */ + ldr r1, pm_pwstctrl_mpu + ldr r2, [r1] + and r2, r2, #0x3 + cmp r2, #0x0 @ Check if target power state was OFF or RET + bne logic_l1_restore + + adr r1, l2dis_3630_offset @ address for offset + ldr r0, [r1] @ value for offset + ldr r0, [r1, r0] @ value at l2dis_3630 + cmp r0, #0x1 @ should we disable L2 on 3630? + bne skipl2dis + mrc p15, 0, r0, c1, c0, 1 + bic r0, r0, #2 @ disable L2 cache + mcr p15, 0, r0, c1, c0, 1 +skipl2dis: + ldr r0, control_stat + ldr r1, [r0] + and r1, #0x700 + cmp r1, #0x300 + beq l2_inv_gp + adr r0, l2_inv_api_params_offset + ldr r3, [r0] + add r3, r3, r0 @ r3 points to dummy parameters + mov r0, #40 @ set service ID for PPA + mov r12, r0 @ copy secure Service ID in r12 + mov r1, #0 @ set task id for ROM code in r1 + mov r2, #4 @ set some flags in r2, r6 + mov r6, #0xff + dsb @ data write barrier + dmb @ data memory barrier + smc #1 @ call SMI monitor (smi #1) + /* Write to Aux control register to set some bits */ + mov r0, #42 @ set service ID for PPA + mov r12, r0 @ copy secure Service ID in r12 + mov r1, #0 @ set task id for ROM code in r1 + mov r2, #4 @ set some flags in r2, r6 + mov r6, #0xff + ldr r4, scratchpad_base + ldr r3, [r4, #0xBC] @ r3 points to parameters + dsb @ data write barrier + dmb @ data memory barrier + smc #1 @ call SMI monitor (smi #1) + +#ifdef CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE + /* Restore L2 aux control register */ + @ set service ID for PPA + mov r0, #CONFIG_OMAP3_L2_AUX_SECURE_SERVICE_SET_ID + mov r12, r0 @ copy service ID in r12 + mov r1, #0 @ set task ID for ROM code in r1 + mov r2, #4 @ set some flags in r2, r6 + mov r6, #0xff + ldr r4, scratchpad_base + ldr r3, [r4, #0xBC] + adds r3, r3, #8 @ r3 points to parameters + dsb @ data write barrier + dmb @ data memory barrier + smc #1 @ call SMI monitor (smi #1) +#endif + b logic_l1_restore + + .align +l2_inv_api_params_offset: + .long l2_inv_api_params - . +l2_inv_gp: + /* Execute smi to invalidate L2 cache */ + mov r12, #0x1 @ set up to invalidate L2 + smc #0 @ Call SMI monitor (smieq) + /* Write to Aux control register to set some bits */ + ldr r4, scratchpad_base + ldr r3, [r4,#0xBC] + ldr r0, [r3,#4] + mov r12, #0x3 + smc #0 @ Call SMI monitor (smieq) + ldr r4, scratchpad_base + ldr r3, [r4,#0xBC] + ldr r0, [r3,#12] + mov r12, #0x2 + smc #0 @ Call SMI monitor (smieq) +logic_l1_restore: + adr r0, l2dis_3630_offset @ adress for offset + ldr r1, [r0] @ value for offset + ldr r1, [r0, r1] @ value at l2dis_3630 + cmp r1, #0x1 @ Test if L2 re-enable needed on 3630 + bne skipl2reen + mrc p15, 0, r1, c1, c0, 1 + orr r1, r1, #2 @ re-enable L2 cache + mcr p15, 0, r1, c1, c0, 1 +skipl2reen: + + /* Now branch to the common CPU resume function */ + b cpu_resume +ENDPROC(omap3_restore) + + .ltorg + +/* + * Local variables + */ +pm_prepwstst_core_p: + .word PM_PREPWSTST_CORE_P +pm_pwstctrl_mpu: + .word PM_PWSTCTRL_MPU_P +scratchpad_base: + .word SCRATCHPAD_BASE_P +sram_base: + .word SRAM_BASE_P + 0x8000 +control_stat: + .word CONTROL_STAT +control_mem_rta: + .word CONTROL_MEM_RTA_CTRL +l2dis_3630_offset: + .long l2dis_3630 - . + + .data + .align 2 +l2dis_3630: + .word 0 + + .data + .align 2 +l2_inv_api_params: + .word 0x1, 0x00 + +/* + * Internal functions + */ + +/* + * This function implements the erratum ID i443 WA, applies to 34xx >= ES3.0 + * Copied to and run from SRAM in order to reconfigure the SDRC parameters. + */ + .text + .align 3 +ENTRY(es3_sdrc_fix) + ldr r4, sdrc_syscfg @ get config addr + ldr r5, [r4] @ get value + tst r5, #0x100 @ is part access blocked + it eq + biceq r5, r5, #0x100 @ clear bit if set + str r5, [r4] @ write back change + ldr r4, sdrc_mr_0 @ get config addr + ldr r5, [r4] @ get value + str r5, [r4] @ write back change + ldr r4, sdrc_emr2_0 @ get config addr + ldr r5, [r4] @ get value + str r5, [r4] @ write back change + ldr r4, sdrc_manual_0 @ get config addr + mov r5, #0x2 @ autorefresh command + str r5, [r4] @ kick off refreshes + ldr r4, sdrc_mr_1 @ get config addr + ldr r5, [r4] @ get value + str r5, [r4] @ write back change + ldr r4, sdrc_emr2_1 @ get config addr + ldr r5, [r4] @ get value + str r5, [r4] @ write back change + ldr r4, sdrc_manual_1 @ get config addr + mov r5, #0x2 @ autorefresh command + str r5, [r4] @ kick off refreshes + bx lr + +/* + * Local variables + */ + .align +sdrc_syscfg: + .word SDRC_SYSCONFIG_P +sdrc_mr_0: + .word SDRC_MR_0_P +sdrc_emr2_0: + .word SDRC_EMR2_0_P +sdrc_manual_0: + .word SDRC_MANUAL_0_P +sdrc_mr_1: + .word SDRC_MR_1_P +sdrc_emr2_1: + .word SDRC_EMR2_1_P +sdrc_manual_1: + .word SDRC_MANUAL_1_P +ENDPROC(es3_sdrc_fix) +ENTRY(es3_sdrc_fix_sz) + .word . - es3_sdrc_fix diff --git a/arch/arm/mach-omap2/sleep43xx.S b/arch/arm/mach-omap2/sleep43xx.S new file mode 100644 index 000000000..832c91327 --- /dev/null +++ b/arch/arm/mach-omap2/sleep43xx.S @@ -0,0 +1,493 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Low level suspend code for AM43XX SoCs + * + * Copyright (C) 2013-2018 Texas Instruments Incorporated - https://www.ti.com/ + * Dave Gerlach, Vaibhav Bedia + */ + +#include <linux/linkage.h> +#include <linux/ti-emif-sram.h> +#include <linux/platform_data/pm33xx.h> +#include <asm/assembler.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/memory.h> + +#include "cm33xx.h" +#include "common.h" +#include "iomap.h" +#include "omap-secure.h" +#include "omap44xx.h" +#include "pm-asm-offsets.h" +#include "prm33xx.h" +#include "prcm43xx.h" + +/* replicated define because linux/bitops.h cannot be included in assembly */ +#define BIT(nr) (1 << (nr)) + +#define AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED 0x00030000 +#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003 +#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002 + +#define AM43XX_EMIF_POWEROFF_ENABLE 0x1 +#define AM43XX_EMIF_POWEROFF_DISABLE 0x0 + +#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP 0x1 +#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO 0x3 + +#define AM43XX_CM_BASE 0x44DF0000 + +#define AM43XX_CM_REGADDR(inst, reg) \ + AM33XX_L4_WK_IO_ADDRESS(AM43XX_CM_BASE + (inst) + (reg)) + +#define AM43XX_CM_MPU_CLKSTCTRL AM43XX_CM_REGADDR(AM43XX_CM_MPU_INST, \ + AM43XX_CM_MPU_MPU_CDOFFS) +#define AM43XX_CM_MPU_MPU_CLKCTRL AM43XX_CM_REGADDR(AM43XX_CM_MPU_INST, \ + AM43XX_CM_MPU_MPU_CLKCTRL_OFFSET) +#define AM43XX_CM_PER_EMIF_CLKCTRL AM43XX_CM_REGADDR(AM43XX_CM_PER_INST, \ + AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET) +#define AM43XX_PRM_EMIF_CTRL_OFFSET 0x0030 + +#define RTC_SECONDS_REG 0x0 +#define RTC_PMIC_REG 0x98 +#define RTC_PMIC_POWER_EN BIT(16) +#define RTC_PMIC_EXT_WAKEUP_STS BIT(12) +#define RTC_PMIC_EXT_WAKEUP_POL BIT(4) +#define RTC_PMIC_EXT_WAKEUP_EN BIT(0) + + .arm + .arch armv7-a + .arch_extension sec + .align 3 + +ENTRY(am43xx_do_wfi) + stmfd sp!, {r4 - r11, lr} @ save registers on stack + + /* Save wfi_flags arg to data space */ + mov r4, r0 + adr r3, am43xx_pm_ro_sram_data + ldr r2, [r3, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET] + str r4, [r2, #AMX3_PM_WFI_FLAGS_OFFSET] + +#ifdef CONFIG_CACHE_L2X0 + /* Retrieve l2 cache virt address BEFORE we shut off EMIF */ + ldr r1, get_l2cache_base + blx r1 + mov r8, r0 +#endif + + /* Only flush cache is we know we are losing MPU context */ + tst r4, #WFI_FLAG_FLUSH_CACHE + beq cache_skip_flush + + /* + * Flush all data from the L1 and L2 data cache before disabling + * SCTLR.C bit. + */ + ldr r1, kernel_flush + blx r1 + + /* + * Clear the SCTLR.C bit to prevent further data cache + * allocation. Clearing SCTLR.C would make all the data accesses + * strongly ordered and would not hit the cache. + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + dsb + + /* + * Invalidate L1 and L2 data cache. + */ + ldr r1, kernel_flush + blx r1 + +#ifdef CONFIG_CACHE_L2X0 + /* + * Clean and invalidate the L2 cache. + */ +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x03 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + dsb + smc #0 + dsb +#endif + mov r0, r8 + adr r4, am43xx_pm_ro_sram_data + ldr r3, [r4, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET] + + mov r2, r0 + ldr r0, [r2, #L2X0_AUX_CTRL] + str r0, [r3, #AMX3_PM_L2_AUX_CTRL_VAL_OFFSET] + ldr r0, [r2, #L310_PREFETCH_CTRL] + str r0, [r3, #AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET] + + ldr r0, l2_val + str r0, [r2, #L2X0_CLEAN_INV_WAY] +wait: + ldr r0, [r2, #L2X0_CLEAN_INV_WAY] + ldr r1, l2_val + ands r0, r0, r1 + bne wait +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x00 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + dsb + smc #0 + dsb +#endif +l2x_sync: + mov r0, r8 + mov r2, r0 + mov r0, #0x0 + str r0, [r2, #L2X0_CACHE_SYNC] +sync: + ldr r0, [r2, #L2X0_CACHE_SYNC] + ands r0, r0, #0x1 + bne sync +#endif + + /* Restore wfi_flags */ + adr r3, am43xx_pm_ro_sram_data + ldr r2, [r3, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET] + ldr r4, [r2, #AMX3_PM_WFI_FLAGS_OFFSET] + +cache_skip_flush: + /* + * If we are trying to enter RTC+DDR mode we must perform + * a read from the rtc address space to ensure translation + * presence in the TLB to avoid page table walk after DDR + * is unavailable. + */ + tst r4, #WFI_FLAG_RTC_ONLY + beq skip_rtc_va_refresh + + adr r3, am43xx_pm_ro_sram_data + ldr r1, [r3, #AMX3_PM_RTC_BASE_VIRT_OFFSET] + ldr r0, [r1] + +skip_rtc_va_refresh: + /* Check if we want self refresh */ + tst r4, #WFI_FLAG_SELF_REFRESH + beq emif_skip_enter_sr + + adr r9, am43xx_emif_sram_table + + ldr r3, [r9, #EMIF_PM_ENTER_SR_OFFSET] + blx r3 + +emif_skip_enter_sr: + /* Only necessary if PER is losing context */ + tst r4, #WFI_FLAG_SAVE_EMIF + beq emif_skip_save + + ldr r3, [r9, #EMIF_PM_SAVE_CONTEXT_OFFSET] + blx r3 + +emif_skip_save: + /* Only can disable EMIF if we have entered self refresh */ + tst r4, #WFI_FLAG_SELF_REFRESH + beq emif_skip_disable + + /* Disable EMIF */ + ldr r1, am43xx_virt_emif_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + +wait_emif_disable: + ldr r2, [r1] + mov r3, #AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED + cmp r2, r3 + bne wait_emif_disable + +emif_skip_disable: + tst r4, #WFI_FLAG_RTC_ONLY + beq skip_rtc_only + + adr r3, am43xx_pm_ro_sram_data + ldr r1, [r3, #AMX3_PM_RTC_BASE_VIRT_OFFSET] + + ldr r0, [r1, #RTC_PMIC_REG] + orr r0, r0, #RTC_PMIC_POWER_EN + orr r0, r0, #RTC_PMIC_EXT_WAKEUP_STS + orr r0, r0, #RTC_PMIC_EXT_WAKEUP_EN + orr r0, r0, #RTC_PMIC_EXT_WAKEUP_POL + str r0, [r1, #RTC_PMIC_REG] + ldr r0, [r1, #RTC_PMIC_REG] + /* Wait for 2 seconds to lose power */ + mov r3, #2 + ldr r2, [r1, #RTC_SECONDS_REG] +rtc_loop: + ldr r0, [r1, #RTC_SECONDS_REG] + cmp r0, r2 + beq rtc_loop + mov r2, r0 + subs r3, r3, #1 + bne rtc_loop + + b re_enable_emif + +skip_rtc_only: + + tst r4, #WFI_FLAG_WAKE_M3 + beq wkup_m3_skip + + /* + * For the MPU WFI to be registered as an interrupt + * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set + * to DISABLED + */ + ldr r1, am43xx_virt_mpu_clkctrl + ldr r2, [r1] + bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE + str r2, [r1] + + /* + * Put MPU CLKDM to SW_SLEEP + */ + ldr r1, am43xx_virt_mpu_clkstctrl + mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP + str r2, [r1] + +wkup_m3_skip: + /* + * Execute a barrier instruction to ensure that all cache, + * TLB and branch predictor maintenance operations issued + * have completed. + */ + dsb + dmb + + /* + * Execute a WFI instruction and wait until the + * STANDBYWFI output is asserted to indicate that the + * CPU is in idle and low power state. CPU can specualatively + * prefetch the instructions so add NOPs after WFI. Sixteen + * NOPs as per Cortex-A9 pipeline. + */ + wfi + + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + /* We come here in case of an abort due to a late interrupt */ + ldr r1, am43xx_virt_mpu_clkstctrl + mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO + str r2, [r1] + + /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */ + ldr r1, am43xx_virt_mpu_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] + +re_enable_emif: + /* Re-enable EMIF */ + ldr r1, am43xx_virt_emif_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] +wait_emif_enable: + ldr r3, [r1] + cmp r2, r3 + bne wait_emif_enable + + tst r4, #WFI_FLAG_FLUSH_CACHE + beq cache_skip_restore + + /* + * Set SCTLR.C bit to allow data cache allocation + */ + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 2) @ Enable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + +cache_skip_restore: + /* Only necessary if PER is losing context */ + tst r4, #WFI_FLAG_SELF_REFRESH + beq emif_skip_exit_sr_abt + + adr r9, am43xx_emif_sram_table + ldr r1, [r9, #EMIF_PM_ABORT_SR_OFFSET] + blx r1 + +emif_skip_exit_sr_abt: + /* Let the suspend code know about the abort */ + mov r0, #1 + ldmfd sp!, {r4 - r11, pc} @ restore regs and return +ENDPROC(am43xx_do_wfi) + + .align +ENTRY(am43xx_resume_offset) + .word . - am43xx_do_wfi + +ENTRY(am43xx_resume_from_deep_sleep) + /* Set MPU CLKSTCTRL to HW AUTO so that CPUidle works properly */ + ldr r1, am43xx_virt_mpu_clkstctrl + mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO + str r2, [r1] + + /* For AM43xx, use EMIF power down until context is restored */ + ldr r2, am43xx_phys_emif_poweroff + mov r1, #AM43XX_EMIF_POWEROFF_ENABLE + str r1, [r2, #0x0] + + /* Re-enable EMIF */ + ldr r1, am43xx_phys_emif_clkctrl + mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE + str r2, [r1] +wait_emif_enable1: + ldr r3, [r1] + cmp r2, r3 + bne wait_emif_enable1 + + adr r9, am43xx_emif_sram_table + + ldr r1, [r9, #EMIF_PM_RESTORE_CONTEXT_OFFSET] + blx r1 + + ldr r1, [r9, #EMIF_PM_EXIT_SR_OFFSET] + blx r1 + + ldr r2, am43xx_phys_emif_poweroff + mov r1, #AM43XX_EMIF_POWEROFF_DISABLE + str r1, [r2, #0x0] + + ldr r1, [r9, #EMIF_PM_RUN_HW_LEVELING] + blx r1 + +#ifdef CONFIG_CACHE_L2X0 + ldr r2, l2_cache_base + ldr r0, [r2, #L2X0_CTRL] + and r0, #0x0f + cmp r0, #1 + beq skip_l2en @ Skip if already enabled + + adr r4, am43xx_pm_ro_sram_data + ldr r3, [r4, #AMX3_PM_RO_SRAM_DATA_PHYS_OFFSET] + ldr r0, [r3, #AMX3_PM_L2_PREFETCH_CTRL_VAL_OFFSET] + + ldr r12, l2_smc1 + dsb + smc #0 + dsb +set_aux_ctrl: + ldr r0, [r3, #AMX3_PM_L2_AUX_CTRL_VAL_OFFSET] + ldr r12, l2_smc2 + dsb + smc #0 + dsb + + /* L2 invalidate on resume */ + ldr r0, l2_val + ldr r2, l2_cache_base + str r0, [r2, #L2X0_INV_WAY] +wait2: + ldr r0, [r2, #L2X0_INV_WAY] + ldr r1, l2_val + ands r0, r0, r1 + bne wait2 +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x00 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + dsb + smc #0 + dsb +#endif +l2x_sync2: + ldr r2, l2_cache_base + mov r0, #0x0 + str r0, [r2, #L2X0_CACHE_SYNC] +sync2: + ldr r0, [r2, #L2X0_CACHE_SYNC] + ands r0, r0, #0x1 + bne sync2 + + mov r0, #0x1 + ldr r12, l2_smc3 + dsb + smc #0 + dsb +#endif +skip_l2en: + /* We are back. Branch to the common CPU resume routine */ + mov r0, #0 + ldr pc, resume_addr +ENDPROC(am43xx_resume_from_deep_sleep) + +/* + * Local variables + */ + .align +kernel_flush: + .word v7_flush_dcache_all +ddr_start: + .word PAGE_OFFSET + +am43xx_phys_emif_poweroff: + .word (AM43XX_CM_BASE + AM43XX_PRM_DEVICE_INST + \ + AM43XX_PRM_EMIF_CTRL_OFFSET) +am43xx_virt_mpu_clkstctrl: + .word (AM43XX_CM_MPU_CLKSTCTRL) +am43xx_virt_mpu_clkctrl: + .word (AM43XX_CM_MPU_MPU_CLKCTRL) +am43xx_virt_emif_clkctrl: + .word (AM43XX_CM_PER_EMIF_CLKCTRL) +am43xx_phys_emif_clkctrl: + .word (AM43XX_CM_BASE + AM43XX_CM_PER_INST + \ + AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET) + +#ifdef CONFIG_CACHE_L2X0 +/* L2 cache related defines for AM437x */ +get_l2cache_base: + .word omap4_get_l2cache_base +l2_cache_base: + .word OMAP44XX_L2CACHE_BASE +l2_smc1: + .word OMAP4_MON_L2X0_PREFETCH_INDEX +l2_smc2: + .word OMAP4_MON_L2X0_AUXCTRL_INDEX +l2_smc3: + .word OMAP4_MON_L2X0_CTRL_INDEX +l2_val: + .word 0xffff +#endif + +.align 3 +/* DDR related defines */ +ENTRY(am43xx_emif_sram_table) + .space EMIF_PM_FUNCTIONS_SIZE + +ENTRY(am43xx_pm_sram) + .word am43xx_do_wfi + .word am43xx_do_wfi_sz + .word am43xx_resume_offset + .word am43xx_emif_sram_table + .word am43xx_pm_ro_sram_data + +resume_addr: + .word cpu_resume - PAGE_OFFSET + 0x80000000 +.align 3 + +ENTRY(am43xx_pm_ro_sram_data) + .space AMX3_PM_RO_SRAM_DATA_SIZE + +ENTRY(am43xx_do_wfi_sz) + .word . - am43xx_do_wfi diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S new file mode 100644 index 000000000..f60f6a9ae --- /dev/null +++ b/arch/arm/mach-omap2/sleep44xx.S @@ -0,0 +1,388 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP44xx sleep code. + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Santosh Shilimkar <santosh.shilimkar@ti.com> + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/smp_scu.h> +#include <asm/memory.h> +#include <asm/hardware/cache-l2x0.h> + +#include "omap-secure.h" + +#include "common.h" +#include "omap44xx.h" +#include "omap4-sar-layout.h" + + .arch armv7-a + +#if defined(CONFIG_SMP) && defined(CONFIG_PM) + + .arch_extension sec +.macro DO_SMC + dsb + smc #0 + dsb +.endm + +#ifdef CONFIG_ARCH_OMAP4 + +/* + * ============================= + * == CPU suspend finisher == + * ============================= + * + * void omap4_finish_suspend(unsigned long cpu_state) + * + * This function code saves the CPU context and performs the CPU + * power down sequence. Calling WFI effectively changes the CPU + * power domains states to the desired target power state. + * + * @cpu_state : contains context save state (r0) + * 0 - No context lost + * 1 - CPUx L1 and logic lost: MPUSS CSWR + * 2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR + * 3 - CPUx L1 and logic lost + GIC + L2 lost: MPUSS OFF + * @return: This function never returns for CPU OFF and DORMANT power states. + * Post WFI, CPU transitions to DORMANT or OFF power state and on wake-up + * from this follows a full CPU reset path via ROM code to CPU restore code. + * The restore function pointer is stored at CPUx_WAKEUP_NS_PA_ADDR_OFFSET. + * It returns to the caller for CPU INACTIVE and ON power states or in case + * CPU failed to transition to targeted OFF/DORMANT state. + * + * omap4_finish_suspend() calls v7_flush_dcache_all() which doesn't save + * stack frame and it expects the caller to take care of it. Hence the entire + * stack frame is saved to avoid possible stack corruption. + */ +ENTRY(omap4_finish_suspend) + stmfd sp!, {r4-r12, lr} + cmp r0, #0x0 + beq do_WFI @ No lowpower state, jump to WFI + + /* + * Flush all data from the L1 data cache before disabling + * SCTLR.C bit. + */ + bl omap4_get_sar_ram_base + ldr r9, [r0, #OMAP_TYPE_OFFSET] + cmp r9, #0x1 @ Check for HS device + bne skip_secure_l1_clean + mov r0, #SCU_PM_NORMAL + mov r1, #0xFF @ clean seucre L1 + stmfd r13!, {r4-r12, r14} + ldr r12, =OMAP4_MON_SCU_PWR_INDEX + DO_SMC + ldmfd r13!, {r4-r12, r14} +skip_secure_l1_clean: + bl v7_flush_dcache_all + + /* + * Clear the SCTLR.C bit to prevent further data cache + * allocation. Clearing SCTLR.C would make all the data accesses + * strongly ordered and would not hit the cache. + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + + bl v7_invalidate_l1 + + /* + * Switch the CPU from Symmetric Multiprocessing (SMP) mode + * to AsymmetricMultiprocessing (AMP) mode by programming + * the SCU power status to DORMANT or OFF mode. + * This enables the CPU to be taken out of coherency by + * preventing the CPU from receiving cache, TLB, or BTB + * maintenance operations broadcast by other CPUs in the cluster. + */ + bl omap4_get_sar_ram_base + mov r8, r0 + ldr r9, [r8, #OMAP_TYPE_OFFSET] + cmp r9, #0x1 @ Check for HS device + bne scu_gp_set + mrc p15, 0, r0, c0, c0, 5 @ Read MPIDR + ands r0, r0, #0x0f + ldreq r0, [r8, #SCU_OFFSET0] + ldrne r0, [r8, #SCU_OFFSET1] + mov r1, #0x00 + stmfd r13!, {r4-r12, r14} + ldr r12, =OMAP4_MON_SCU_PWR_INDEX + DO_SMC + ldmfd r13!, {r4-r12, r14} + b skip_scu_gp_set +scu_gp_set: + mrc p15, 0, r0, c0, c0, 5 @ Read MPIDR + ands r0, r0, #0x0f + ldreq r1, [r8, #SCU_OFFSET0] + ldrne r1, [r8, #SCU_OFFSET1] + bl omap4_get_scu_base + bl scu_power_mode +skip_scu_gp_set: + mrc p15, 0, r0, c1, c1, 2 @ Read NSACR data + tst r0, #(1 << 18) + mrcne p15, 0, r0, c1, c0, 1 + bicne r0, r0, #(1 << 6) @ Disable SMP bit + mcrne p15, 0, r0, c1, c0, 1 + isb + dsb +#ifdef CONFIG_CACHE_L2X0 + /* + * Clean and invalidate the L2 cache. + * Common cache-l2x0.c functions can't be used here since it + * uses spinlocks. We are out of coherency here with data cache + * disabled. The spinlock implementation uses exclusive load/store + * instruction which can fail without data cache being enabled. + * OMAP4 hardware doesn't support exclusive monitor which can + * overcome exclusive access issue. Because of this, CPU can + * lead to deadlock. + */ + bl omap4_get_sar_ram_base + mov r8, r0 + mrc p15, 0, r5, c0, c0, 5 @ Read MPIDR + ands r5, r5, #0x0f + ldreq r0, [r8, #L2X0_SAVE_OFFSET0] @ Retrieve L2 state from SAR + ldrne r0, [r8, #L2X0_SAVE_OFFSET1] @ memory. + cmp r0, #3 + bne do_WFI +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x03 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + DO_SMC +#endif + bl omap4_get_l2cache_base + mov r2, r0 + ldr r0, =0xffff + str r0, [r2, #L2X0_CLEAN_INV_WAY] +wait: + ldr r0, [r2, #L2X0_CLEAN_INV_WAY] + ldr r1, =0xffff + ands r0, r0, r1 + bne wait +#ifdef CONFIG_PL310_ERRATA_727915 + mov r0, #0x00 + mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX + DO_SMC +#endif +l2x_sync: + bl omap4_get_l2cache_base + mov r2, r0 + mov r0, #0x0 + str r0, [r2, #L2X0_CACHE_SYNC] +sync: + ldr r0, [r2, #L2X0_CACHE_SYNC] + ands r0, r0, #0x1 + bne sync +#endif + +do_WFI: + bl omap_do_wfi + + /* + * CPU is here when it failed to enter OFF/DORMANT or + * no low power state was attempted. + */ + mrc p15, 0, r0, c1, c0, 0 + tst r0, #(1 << 2) @ Check C bit enabled? + orreq r0, r0, #(1 << 2) @ Enable the C bit + mcreq p15, 0, r0, c1, c0, 0 + isb + + /* + * Ensure the CPU power state is set to NORMAL in + * SCU power state so that CPU is back in coherency. + * In non-coherent mode CPU can lock-up and lead to + * system deadlock. + */ + mrc p15, 0, r0, c1, c0, 1 + tst r0, #(1 << 6) @ Check SMP bit enabled? + orreq r0, r0, #(1 << 6) + mcreq p15, 0, r0, c1, c0, 1 + isb + bl omap4_get_sar_ram_base + mov r8, r0 + ldr r9, [r8, #OMAP_TYPE_OFFSET] + cmp r9, #0x1 @ Check for HS device + bne scu_gp_clear + mov r0, #SCU_PM_NORMAL + mov r1, #0x00 + stmfd r13!, {r4-r12, r14} + ldr r12, =OMAP4_MON_SCU_PWR_INDEX + DO_SMC + ldmfd r13!, {r4-r12, r14} + b skip_scu_gp_clear +scu_gp_clear: + bl omap4_get_scu_base + mov r1, #SCU_PM_NORMAL + bl scu_power_mode +skip_scu_gp_clear: + isb + dsb + ldmfd sp!, {r4-r12, pc} +ENDPROC(omap4_finish_suspend) + +/* + * ============================ + * == CPU resume entry point == + * ============================ + * + * void omap4_cpu_resume(void) + * + * ROM code jumps to this function while waking up from CPU + * OFF or DORMANT state. Physical address of the function is + * stored in the SAR RAM while entering to OFF or DORMANT mode. + * The restore function pointer is stored at CPUx_WAKEUP_NS_PA_ADDR_OFFSET. + */ +ENTRY(omap4_cpu_resume) + /* + * Configure ACTRL and enable NS SMP bit access on CPU1 on HS device. + * OMAP44XX EMU/HS devices - CPU0 SMP bit access is enabled in PPA + * init and for CPU1, a secure PPA API provided. CPU0 must be ON + * while executing NS_SMP API on CPU1 and PPA version must be 1.4.0+. + * OMAP443X GP devices- SMP bit isn't accessible. + * OMAP446X GP devices - SMP bit access is enabled on both CPUs. + */ + ldr r8, =OMAP44XX_SAR_RAM_BASE + ldr r9, [r8, #OMAP_TYPE_OFFSET] + cmp r9, #0x1 @ Skip if GP device + bne skip_ns_smp_enable + mrc p15, 0, r0, c0, c0, 5 + ands r0, r0, #0x0f + beq skip_ns_smp_enable +ppa_actrl_retry: + mov r0, #OMAP4_PPA_CPU_ACTRL_SMP_INDEX + adr r1, ppa_zero_params_offset + ldr r3, [r1] + add r3, r3, r1 @ Pointer to ppa_zero_params + mov r1, #0x0 @ Process ID + mov r2, #0x4 @ Flag + mov r6, #0xff + mov r12, #0x00 @ Secure Service ID + DO_SMC + cmp r0, #0x0 @ API returns 0 on success. + beq enable_smp_bit + b ppa_actrl_retry +enable_smp_bit: + mrc p15, 0, r0, c1, c0, 1 + tst r0, #(1 << 6) @ Check SMP bit enabled? + orreq r0, r0, #(1 << 6) + mcreq p15, 0, r0, c1, c0, 1 + isb +skip_ns_smp_enable: +#ifdef CONFIG_CACHE_L2X0 + /* + * Restore the L2 AUXCTRL and enable the L2 cache. + * OMAP4_MON_L2X0_AUXCTRL_INDEX = Program the L2X0 AUXCTRL + * OMAP4_MON_L2X0_CTRL_INDEX = Enable the L2 using L2X0 CTRL + * register r0 contains value to be programmed. + * L2 cache is already invalidate by ROM code as part + * of MPUSS OFF wakeup path. + */ + ldr r2, =OMAP44XX_L2CACHE_BASE + ldr r0, [r2, #L2X0_CTRL] + and r0, #0x0f + cmp r0, #1 + beq skip_l2en @ Skip if already enabled + ldr r3, =OMAP44XX_SAR_RAM_BASE + ldr r1, [r3, #OMAP_TYPE_OFFSET] + cmp r1, #0x1 @ Check for HS device + bne set_gp_por + ldr r0, =OMAP4_PPA_L2_POR_INDEX + ldr r1, =OMAP44XX_SAR_RAM_BASE + ldr r4, [r1, #L2X0_PREFETCH_CTRL_OFFSET] + adr r1, ppa_por_params_offset + ldr r3, [r1] + add r3, r3, r1 @ Pointer to ppa_por_params + str r4, [r3, #0x04] + mov r1, #0x0 @ Process ID + mov r2, #0x4 @ Flag + mov r6, #0xff + mov r12, #0x00 @ Secure Service ID + DO_SMC + b set_aux_ctrl +set_gp_por: + ldr r1, =OMAP44XX_SAR_RAM_BASE + ldr r0, [r1, #L2X0_PREFETCH_CTRL_OFFSET] + ldr r12, =OMAP4_MON_L2X0_PREFETCH_INDEX @ Setup L2 PREFETCH + DO_SMC +set_aux_ctrl: + ldr r1, =OMAP44XX_SAR_RAM_BASE + ldr r0, [r1, #L2X0_AUXCTRL_OFFSET] + ldr r12, =OMAP4_MON_L2X0_AUXCTRL_INDEX @ Setup L2 AUXCTRL + DO_SMC + mov r0, #0x1 + ldr r12, =OMAP4_MON_L2X0_CTRL_INDEX @ Enable L2 cache + DO_SMC +skip_l2en: +#endif + + b cpu_resume @ Jump to generic resume +ppa_por_params_offset: + .long ppa_por_params - . +ENDPROC(omap4_cpu_resume) +#endif /* CONFIG_ARCH_OMAP4 */ + +#endif /* defined(CONFIG_SMP) && defined(CONFIG_PM) */ + +ENTRY(omap_do_wfi) + stmfd sp!, {lr} +#ifdef CONFIG_OMAP_INTERCONNECT_BARRIER + /* Drain interconnect write buffers. */ + bl omap_interconnect_sync +#endif + + /* + * Execute an ISB instruction to ensure that all of the + * CP15 register changes have been committed. + */ + isb + + /* + * Execute a barrier instruction to ensure that all cache, + * TLB and branch predictor maintenance operations issued + * by any CPU in the cluster have completed. + */ + dsb + dmb + + /* + * Execute a WFI instruction and wait until the + * STANDBYWFI output is asserted to indicate that the + * CPU is in idle and low power state. CPU can specualatively + * prefetch the instructions so add NOPs after WFI. Sixteen + * NOPs as per Cortex-A9 pipeline. + */ + wfi @ Wait For Interrupt + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + ldmfd sp!, {pc} +ppa_zero_params_offset: + .long ppa_zero_params - . +ENDPROC(omap_do_wfi) + + .data + .align 2 +ppa_zero_params: + .word 0 + +ppa_por_params: + .word 1, 0 diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c new file mode 100644 index 000000000..7c18420c9 --- /dev/null +++ b/arch/arm/mach-omap2/smartreflex-class3.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Smart reflex Class 3 specific implementations + * + * Author: Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Thara Gopinath <thara@ti.com> + */ + +#include <linux/power/smartreflex.h> +#include "soc.h" +#include "voltage.h" + +static int sr_class3_enable(struct omap_sr *sr) +{ + unsigned long volt = voltdm_get_voltage(sr->voltdm); + + if (!volt) { + pr_warn("%s: Curr voltage unknown. Cannot enable %s\n", + __func__, sr->name); + return -ENODATA; + } + + omap_vp_enable(sr->voltdm); + return sr_enable(sr, volt); +} + +static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) +{ + sr_disable_errgen(sr); + omap_vp_disable(sr->voltdm); + sr_disable(sr); + if (is_volt_reset) + voltdm_reset(sr->voltdm); + + return 0; +} + +static int sr_class3_configure(struct omap_sr *sr) +{ + return sr_configure_errgen(sr); +} + +/* SR class3 structure */ +static struct omap_sr_class_data class3_data = { + .enable = sr_class3_enable, + .disable = sr_class3_disable, + .configure = sr_class3_configure, + .class_type = SR_CLASS3, +}; + +/* Smartreflex Class3 init API to be called from board file */ +static int __init sr_class3_init(void) +{ + pr_info("SmartReflex Class3 initialized\n"); + return sr_register_class(&class3_data); +} +omap_late_initcall(sr_class3_init); diff --git a/arch/arm/mach-omap2/soc.h b/arch/arm/mach-omap2/soc.h new file mode 100644 index 000000000..9e3dbb743 --- /dev/null +++ b/arch/arm/mach-omap2/soc.h @@ -0,0 +1,504 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * OMAP cpu type detection + * + * Copyright (C) 2004, 2008 Nokia Corporation + * + * Copyright (C) 2009-11 Texas Instruments. + * + * Written by Tony Lindgren <tony.lindgren@nokia.com> + * + * Added OMAP4/5 specific defines - Santosh Shilimkar<santosh.shilimkar@ti.com> + * Added DRA7xxx specific defines - Sricharan R<r.sricharan@ti.com> + */ + +#include "omap24xx.h" +#include "omap34xx.h" +#include "omap44xx.h" +#include "ti81xx.h" +#include "am33xx.h" +#include "omap54xx.h" + +#ifndef __ASSEMBLY__ + +#include <linux/bitops.h> +#include <linux/of.h> + +/* + * OMAP2+ is always defined as ARCH_MULTIPLATFORM in Kconfig + */ +#undef MULTI_OMAP2 +#define MULTI_OMAP2 + +/* + * Omap device type i.e. EMU/HS/TST/GP/BAD + */ +#define OMAP2_DEVICE_TYPE_TEST 0 +#define OMAP2_DEVICE_TYPE_EMU 1 +#define OMAP2_DEVICE_TYPE_SEC 2 +#define OMAP2_DEVICE_TYPE_GP 3 +#define OMAP2_DEVICE_TYPE_BAD 4 + +int omap_type(void); + +/* + * omap_rev bits: + * SoC id bits (0730, 1510, 1710, 2422...) [31:16] + * SoC revision (See _REV_ defined in cpu.h) [15:08] + * SoC class bits (15xx, 16xx, 24xx, 34xx...) [07:00] + */ +unsigned int omap_rev(void); + +static inline int soc_is_omap(void) +{ + return omap_rev() != 0; +} + +/* + * Get the SoC revision for OMAP devices + */ +#define GET_OMAP_REVISION() ((omap_rev() >> 8) & 0xff) + +/* + * Macros to group OMAP into cpu classes. + * These can be used in most places. + * soc_is_omap24xx(): True for OMAP2420, OMAP2422, OMAP2423, OMAP2430 + * soc_is_omap242x(): True for OMAP2420, OMAP2422, OMAP2423 + * soc_is_omap243x(): True for OMAP2430 + * soc_is_omap343x(): True for OMAP3430 + * soc_is_omap443x(): True for OMAP4430 + * soc_is_omap446x(): True for OMAP4460 + * soc_is_omap447x(): True for OMAP4470 + * soc_is_omap543x(): True for OMAP5430, OMAP5432 + */ +#define GET_OMAP_CLASS (omap_rev() & 0xff) + +#define IS_OMAP_CLASS(class, id) \ +static inline int is_omap ##class (void) \ +{ \ + return (GET_OMAP_CLASS == (id)) ? 1 : 0; \ +} + +#define GET_AM_CLASS ((omap_rev() >> 24) & 0xff) + +#define IS_AM_CLASS(class, id) \ +static inline int is_am ##class (void) \ +{ \ + return (GET_AM_CLASS == (id)) ? 1 : 0; \ +} + +#define GET_TI_CLASS ((omap_rev() >> 24) & 0xff) + +#define IS_TI_CLASS(class, id) \ +static inline int is_ti ##class (void) \ +{ \ + return (GET_TI_CLASS == (id)) ? 1 : 0; \ +} + +#define GET_DRA_CLASS ((omap_rev() >> 24) & 0xff) + +#define IS_DRA_CLASS(class, id) \ +static inline int is_dra ##class (void) \ +{ \ + return (GET_DRA_CLASS == (id)) ? 1 : 0; \ +} + +#define GET_OMAP_SUBCLASS ((omap_rev() >> 20) & 0x0fff) + +#define IS_OMAP_SUBCLASS(subclass, id) \ +static inline int is_omap ##subclass (void) \ +{ \ + return (GET_OMAP_SUBCLASS == (id)) ? 1 : 0; \ +} + +#define IS_TI_SUBCLASS(subclass, id) \ +static inline int is_ti ##subclass (void) \ +{ \ + return (GET_OMAP_SUBCLASS == (id)) ? 1 : 0; \ +} + +#define IS_AM_SUBCLASS(subclass, id) \ +static inline int is_am ##subclass (void) \ +{ \ + return (GET_OMAP_SUBCLASS == (id)) ? 1 : 0; \ +} + +#define IS_DRA_SUBCLASS(subclass, id) \ +static inline int is_dra ##subclass (void) \ +{ \ + return (GET_OMAP_SUBCLASS == (id)) ? 1 : 0; \ +} + +#define GET_DRA_PACKAGE (omap_rev() & 0xff) + +#define IS_DRA_SUBCLASS_PACKAGE(subclass, package, id) \ +static inline int is_dra ##subclass ##_ ##package (void) \ +{ \ + return (is_dra ##subclass () && GET_DRA_PACKAGE == id) ? 1 : 0; \ +} + +IS_OMAP_CLASS(24xx, 0x24) +IS_OMAP_CLASS(34xx, 0x34) +IS_OMAP_CLASS(44xx, 0x44) +IS_AM_CLASS(35xx, 0x35) +IS_OMAP_CLASS(54xx, 0x54) +IS_AM_CLASS(33xx, 0x33) +IS_AM_CLASS(43xx, 0x43) + +IS_TI_CLASS(81xx, 0x81) +IS_DRA_CLASS(7xx, 0x7) + +IS_OMAP_SUBCLASS(242x, 0x242) +IS_OMAP_SUBCLASS(243x, 0x243) +IS_OMAP_SUBCLASS(343x, 0x343) +IS_OMAP_SUBCLASS(363x, 0x363) +IS_OMAP_SUBCLASS(443x, 0x443) +IS_OMAP_SUBCLASS(446x, 0x446) +IS_OMAP_SUBCLASS(447x, 0x447) +IS_OMAP_SUBCLASS(543x, 0x543) + +IS_TI_SUBCLASS(816x, 0x816) +IS_TI_SUBCLASS(814x, 0x814) +IS_AM_SUBCLASS(335x, 0x335) +IS_AM_SUBCLASS(437x, 0x437) +IS_DRA_SUBCLASS(76x, 0x76) +IS_DRA_SUBCLASS_PACKAGE(76x, abz, 2) +IS_DRA_SUBCLASS_PACKAGE(76x, acd, 3) +IS_DRA_SUBCLASS(75x, 0x75) +IS_DRA_SUBCLASS(72x, 0x72) + +#define soc_is_ti81xx() 0 +#define soc_is_ti816x() 0 +#define soc_is_ti814x() 0 +#define soc_is_am35xx() 0 +#define soc_is_am33xx() 0 +#define soc_is_am335x() 0 +#define soc_is_am43xx() 0 +#define soc_is_am437x() 0 +#define soc_is_omap44xx() 0 +#define soc_is_omap443x() 0 +#define soc_is_omap446x() 0 +#define soc_is_omap447x() 0 +#define soc_is_omap54xx() 0 +#define soc_is_omap543x() 0 +#define soc_is_dra7xx() 0 +#define soc_is_dra76x() 0 +#define soc_is_dra74x() 0 +#define soc_is_dra72x() 0 + +#if defined(CONFIG_ARCH_OMAP2) +# define soc_is_omap24xx() is_omap24xx() +#else +# define soc_is_omap24xx() 0 +#endif +#if defined(CONFIG_SOC_OMAP2420) +# define soc_is_omap242x() is_omap242x() +#else +# define soc_is_omap242x() 0 +#endif +#if defined(CONFIG_SOC_OMAP2430) +# define soc_is_omap243x() is_omap243x() +#else +# define soc_is_omap243x() 0 +#endif +#if defined(CONFIG_ARCH_OMAP3) +# define soc_is_omap34xx() is_omap34xx() +# define soc_is_omap343x() is_omap343x() +#else +# define soc_is_omap34xx() 0 +# define soc_is_omap343x() 0 +#endif + +/* + * Macros to detect individual cpu types. + * These are only rarely needed. + * soc_is_omap2420(): True for OMAP2420 + * soc_is_omap2422(): True for OMAP2422 + * soc_is_omap2423(): True for OMAP2423 + * soc_is_omap2430(): True for OMAP2430 + * soc_is_omap3430(): True for OMAP3430 + */ +#define GET_OMAP_TYPE ((omap_rev() >> 16) & 0xffff) + +#define IS_OMAP_TYPE(type, id) \ +static inline int is_omap ##type (void) \ +{ \ + return (GET_OMAP_TYPE == (id)) ? 1 : 0; \ +} + +IS_OMAP_TYPE(2420, 0x2420) +IS_OMAP_TYPE(2422, 0x2422) +IS_OMAP_TYPE(2423, 0x2423) +IS_OMAP_TYPE(2430, 0x2430) +IS_OMAP_TYPE(3430, 0x3430) + +#define soc_is_omap2420() 0 +#define soc_is_omap2422() 0 +#define soc_is_omap2423() 0 +#define soc_is_omap2430() 0 +#define soc_is_omap3430() 0 +#define soc_is_omap3630() 0 +#define soc_is_omap5430() 0 + +/* These are needed for the common code */ +#define soc_is_omap7xx() 0 +#define soc_is_omap15xx() 0 +#define soc_is_omap16xx() 0 +#define soc_is_omap1510() 0 +#define soc_is_omap1610() 0 +#define soc_is_omap1611() 0 +#define soc_is_omap1621() 0 +#define soc_is_omap1710() 0 +#define cpu_class_is_omap1() 0 +#define cpu_class_is_omap2() 1 + +#if defined(CONFIG_ARCH_OMAP2) +# undef soc_is_omap2420 +# undef soc_is_omap2422 +# undef soc_is_omap2423 +# undef soc_is_omap2430 +# define soc_is_omap2420() is_omap2420() +# define soc_is_omap2422() is_omap2422() +# define soc_is_omap2423() is_omap2423() +# define soc_is_omap2430() is_omap2430() +#endif + +#if defined(CONFIG_ARCH_OMAP3) +# undef soc_is_omap3430 +# undef soc_is_ti81xx +# undef soc_is_ti816x +# undef soc_is_ti814x +# undef soc_is_am35xx +# define soc_is_omap3430() is_omap3430() +# undef soc_is_omap3630 +# define soc_is_omap3630() is_omap363x() +# define soc_is_ti81xx() is_ti81xx() +# define soc_is_ti816x() is_ti816x() +# define soc_is_ti814x() is_ti814x() +# define soc_is_am35xx() is_am35xx() +#endif + +# if defined(CONFIG_SOC_AM33XX) +# undef soc_is_am33xx +# undef soc_is_am335x +# define soc_is_am33xx() is_am33xx() +# define soc_is_am335x() is_am335x() +#endif + +#ifdef CONFIG_SOC_AM43XX +# undef soc_is_am43xx +# undef soc_is_am437x +# define soc_is_am43xx() is_am43xx() +# define soc_is_am437x() is_am437x() +#endif + +# if defined(CONFIG_ARCH_OMAP4) +# undef soc_is_omap44xx +# undef soc_is_omap443x +# undef soc_is_omap446x +# undef soc_is_omap447x +# define soc_is_omap44xx() is_omap44xx() +# define soc_is_omap443x() is_omap443x() +# define soc_is_omap446x() is_omap446x() +# define soc_is_omap447x() is_omap447x() +# endif + +# if defined(CONFIG_SOC_OMAP5) +# undef soc_is_omap54xx +# undef soc_is_omap543x +# define soc_is_omap54xx() is_omap54xx() +# define soc_is_omap543x() is_omap543x() +#endif + +#if defined(CONFIG_SOC_DRA7XX) +#undef soc_is_dra7xx +#undef soc_is_dra76x +#undef soc_is_dra76x_abz +#undef soc_is_dra76x_acd +#undef soc_is_dra74x +#undef soc_is_dra72x +#define soc_is_dra7xx() is_dra7xx() +#define soc_is_dra76x() is_dra76x() +#define soc_is_dra76x_abz() is_dra76x_abz() +#define soc_is_dra76x_acd() is_dra76x_acd() +#define soc_is_dra74x() is_dra75x() +#define soc_is_dra72x() is_dra72x() +#endif + +/* Various silicon revisions for omap2 */ +#define OMAP242X_CLASS 0x24200024 +#define OMAP2420_REV_ES1_0 OMAP242X_CLASS +#define OMAP2420_REV_ES2_0 (OMAP242X_CLASS | (0x1 << 8)) + +#define OMAP243X_CLASS 0x24300024 +#define OMAP2430_REV_ES1_0 OMAP243X_CLASS + +#define OMAP343X_CLASS 0x34300034 +#define OMAP3430_REV_ES1_0 OMAP343X_CLASS +#define OMAP3430_REV_ES2_0 (OMAP343X_CLASS | (0x1 << 8)) +#define OMAP3430_REV_ES2_1 (OMAP343X_CLASS | (0x2 << 8)) +#define OMAP3430_REV_ES3_0 (OMAP343X_CLASS | (0x3 << 8)) +#define OMAP3430_REV_ES3_1 (OMAP343X_CLASS | (0x4 << 8)) +#define OMAP3430_REV_ES3_1_2 (OMAP343X_CLASS | (0x5 << 8)) + +#define OMAP363X_CLASS 0x36300034 +#define OMAP3630_REV_ES1_0 OMAP363X_CLASS +#define OMAP3630_REV_ES1_1 (OMAP363X_CLASS | (0x1 << 8)) +#define OMAP3630_REV_ES1_2 (OMAP363X_CLASS | (0x2 << 8)) + +#define TI816X_CLASS 0x81600081 +#define TI8168_REV_ES1_0 TI816X_CLASS +#define TI8168_REV_ES1_1 (TI816X_CLASS | (0x1 << 8)) +#define TI8168_REV_ES2_0 (TI816X_CLASS | (0x2 << 8)) +#define TI8168_REV_ES2_1 (TI816X_CLASS | (0x3 << 8)) + +#define TI814X_CLASS 0x81400081 +#define TI8148_REV_ES1_0 TI814X_CLASS +#define TI8148_REV_ES2_0 (TI814X_CLASS | (0x1 << 8)) +#define TI8148_REV_ES2_1 (TI814X_CLASS | (0x2 << 8)) + +#define AM35XX_CLASS 0x35170034 +#define AM35XX_REV_ES1_0 AM35XX_CLASS +#define AM35XX_REV_ES1_1 (AM35XX_CLASS | (0x1 << 8)) + +#define AM335X_CLASS 0x33500033 +#define AM335X_REV_ES1_0 AM335X_CLASS +#define AM335X_REV_ES2_0 (AM335X_CLASS | (0x1 << 8)) +#define AM335X_REV_ES2_1 (AM335X_CLASS | (0x2 << 8)) + +#define AM437X_CLASS 0x43700000 +#define AM437X_REV_ES1_0 (AM437X_CLASS | (0x10 << 8)) +#define AM437X_REV_ES1_1 (AM437X_CLASS | (0x11 << 8)) +#define AM437X_REV_ES1_2 (AM437X_CLASS | (0x12 << 8)) + +#define OMAP443X_CLASS 0x44300044 +#define OMAP4430_REV_ES1_0 (OMAP443X_CLASS | (0x10 << 8)) +#define OMAP4430_REV_ES2_0 (OMAP443X_CLASS | (0x20 << 8)) +#define OMAP4430_REV_ES2_1 (OMAP443X_CLASS | (0x21 << 8)) +#define OMAP4430_REV_ES2_2 (OMAP443X_CLASS | (0x22 << 8)) +#define OMAP4430_REV_ES2_3 (OMAP443X_CLASS | (0x23 << 8)) + +#define OMAP446X_CLASS 0x44600044 +#define OMAP4460_REV_ES1_0 (OMAP446X_CLASS | (0x10 << 8)) +#define OMAP4460_REV_ES1_1 (OMAP446X_CLASS | (0x11 << 8)) + +#define OMAP447X_CLASS 0x44700044 +#define OMAP4470_REV_ES1_0 (OMAP447X_CLASS | (0x10 << 8)) + +#define OMAP54XX_CLASS 0x54000054 +#define OMAP5430_REV_ES2_0 (OMAP54XX_CLASS | (0x30 << 16) | (0x20 << 8)) +#define OMAP5432_REV_ES2_0 (OMAP54XX_CLASS | (0x32 << 16) | (0x20 << 8)) + +#define DRA7XX_CLASS 0x07000000 +#define DRA762_REV_ES1_0 (DRA7XX_CLASS | (0x62 << 16) | (0x10 << 8)) +#define DRA762_ABZ_REV_ES1_0 (DRA762_REV_ES1_0 | (2 << 0)) +#define DRA762_ACD_REV_ES1_0 (DRA762_REV_ES1_0 | (3 << 0)) +#define DRA752_REV_ES1_0 (DRA7XX_CLASS | (0x52 << 16) | (0x10 << 8)) +#define DRA752_REV_ES1_1 (DRA7XX_CLASS | (0x52 << 16) | (0x11 << 8)) +#define DRA752_REV_ES2_0 (DRA7XX_CLASS | (0x52 << 16) | (0x20 << 8)) +#define DRA722_REV_ES1_0 (DRA7XX_CLASS | (0x22 << 16) | (0x10 << 8)) +#define DRA722_REV_ES2_0 (DRA7XX_CLASS | (0x22 << 16) | (0x20 << 8)) +#define DRA722_REV_ES2_1 (DRA7XX_CLASS | (0x22 << 16) | (0x21 << 8)) + +void omap2xxx_check_revision(void); +void omap3xxx_check_revision(void); +void omap4xxx_check_revision(void); +void omap5xxx_check_revision(void); +void dra7xxx_check_revision(void); +void omap3xxx_check_features(void); +void ti81xx_check_features(void); +void am33xx_check_features(void); +void omap4xxx_check_features(void); + +/* + * Runtime detection of OMAP3 features + * + * OMAP3_HAS_IO_CHAIN_CTRL: Some later members of the OMAP3 chip + * family have OS-level control over the I/O chain clock. This is + * to avoid a window during which wakeups could potentially be lost + * during powerdomain transitions. If this bit is set, it + * indicates that the chip does support OS-level control of this + * feature. + */ +extern u32 omap_features; + +#define OMAP3_HAS_L2CACHE BIT(0) +#define OMAP3_HAS_IVA BIT(1) +#define OMAP3_HAS_SGX BIT(2) +#define OMAP3_HAS_NEON BIT(3) +#define OMAP3_HAS_ISP BIT(4) +#define OMAP3_HAS_192MHZ_CLK BIT(5) +#define OMAP3_HAS_IO_WAKEUP BIT(6) +#define OMAP3_HAS_SDRC BIT(7) +#define OMAP3_HAS_IO_CHAIN_CTRL BIT(8) +#define OMAP4_HAS_PERF_SILICON BIT(9) + + +#define OMAP3_HAS_FEATURE(feat,flag) \ +static inline unsigned int omap3_has_ ##feat(void) \ +{ \ + return omap_features & OMAP3_HAS_ ##flag; \ +} \ + +OMAP3_HAS_FEATURE(l2cache, L2CACHE) +OMAP3_HAS_FEATURE(sgx, SGX) +OMAP3_HAS_FEATURE(iva, IVA) +OMAP3_HAS_FEATURE(neon, NEON) +OMAP3_HAS_FEATURE(isp, ISP) +OMAP3_HAS_FEATURE(192mhz_clk, 192MHZ_CLK) +OMAP3_HAS_FEATURE(io_wakeup, IO_WAKEUP) +OMAP3_HAS_FEATURE(sdrc, SDRC) +OMAP3_HAS_FEATURE(io_chain_ctrl, IO_CHAIN_CTRL) + +/* + * Runtime detection of OMAP4 features + */ +#define OMAP4_HAS_FEATURE(feat, flag) \ +static inline unsigned int omap4_has_ ##feat(void) \ +{ \ + return omap_features & OMAP4_HAS_ ##flag; \ +} \ + +OMAP4_HAS_FEATURE(perf_silicon, PERF_SILICON) + +/* + * We need to make sure omap initcalls don't run when + * multiplatform kernels are booted on other SoCs. + */ +#define omap_initcall(level, fn) \ +static int __init __used __##fn(void) \ +{ \ + if (!soc_is_omap()) \ + return 0; \ + return fn(); \ +} \ +level(__##fn); + +#define omap_early_initcall(fn) omap_initcall(early_initcall, fn) +#define omap_core_initcall(fn) omap_initcall(core_initcall, fn) +#define omap_postcore_initcall(fn) omap_initcall(postcore_initcall, fn) +#define omap_arch_initcall(fn) omap_initcall(arch_initcall, fn) +#define omap_subsys_initcall(fn) omap_initcall(subsys_initcall, fn) +#define omap_device_initcall(fn) omap_initcall(device_initcall, fn) +#define omap_late_initcall(fn) omap_initcall(late_initcall, fn) +#define omap_late_initcall_sync(fn) omap_initcall(late_initcall_sync, fn) + +/* Legacy defines, these can be removed when users are removed */ +#define cpu_is_omap2420() soc_is_omap2420() +#define cpu_is_omap2422() soc_is_omap2422() +#define cpu_is_omap242x() soc_is_omap242x() +#define cpu_is_omap2430() soc_is_omap2430() +#define cpu_is_omap243x() soc_is_omap243x() +#define cpu_is_omap24xx() soc_is_omap24xx() +#define cpu_is_omap3430() soc_is_omap3430() +#define cpu_is_omap343x() soc_is_omap343x() +#define cpu_is_omap34xx() soc_is_omap34xx() +#define cpu_is_omap3630() soc_is_omap3630() +#define cpu_is_omap443x() soc_is_omap443x() +#define cpu_is_omap446x() soc_is_omap446x() +#define cpu_is_omap44xx() soc_is_omap44xx() +#define cpu_is_ti814x() soc_is_ti814x() +#define cpu_is_ti816x() soc_is_ti816x() +#define cpu_is_ti81xx() soc_is_ti81xx() + +#endif /* __ASSEMBLY__ */ diff --git a/arch/arm/mach-omap2/sr_device.c b/arch/arm/mach-omap2/sr_device.c new file mode 100644 index 000000000..db672cf19 --- /dev/null +++ b/arch/arm/mach-omap2/sr_device.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3/OMAP4 smartreflex device file + * + * Author: Thara Gopinath <thara@ti.com> + * + * Based originally on code from smartreflex.c + * Copyright (C) 2010 Texas Instruments, Inc. + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008 Nokia Corporation + * Kalle Jokiniemi + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Lesly A M <x0080970@ti.com> + */ +#include <linux/power/smartreflex.h> + +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include "soc.h" +#include "omap_device.h" +#include "voltage.h" +#include "control.h" +#include "pm.h" + +static bool sr_enable_on_init; + +/* Read EFUSE values from control registers for OMAP3430 */ +static void __init sr_set_nvalues(struct omap_volt_data *volt_data, + struct omap_sr_data *sr_data) +{ + struct omap_sr_nvalue_table *nvalue_table; + int i, j, count = 0; + + sr_data->nvalue_count = 0; + sr_data->nvalue_table = NULL; + + while (volt_data[count].volt_nominal) + count++; + + nvalue_table = kcalloc(count, sizeof(*nvalue_table), GFP_KERNEL); + if (!nvalue_table) + return; + + for (i = 0, j = 0; i < count; i++) { + u32 v; + + /* + * In OMAP4 the efuse registers are 24 bit aligned. + * A readl_relaxed will fail for non-32 bit aligned address + * and hence the 8-bit read and shift. + */ + if (cpu_is_omap44xx()) { + u16 offset = volt_data[i].sr_efuse_offs; + + v = omap_ctrl_readb(offset) | + omap_ctrl_readb(offset + 1) << 8 | + omap_ctrl_readb(offset + 2) << 16; + } else { + v = omap_ctrl_readl(volt_data[i].sr_efuse_offs); + } + + /* + * Many OMAP SoCs don't have the eFuse values set. + * For example, pretty much all OMAP3xxx before + * ES3.something. + * + * XXX There needs to be some way for board files or + * userspace to add these in. + */ + if (v == 0) + continue; + + nvalue_table[j].nvalue = v; + nvalue_table[j].efuse_offs = volt_data[i].sr_efuse_offs; + nvalue_table[j].errminlimit = volt_data[i].sr_errminlimit; + nvalue_table[j].volt_nominal = volt_data[i].volt_nominal; + + j++; + } + + sr_data->nvalue_table = nvalue_table; + sr_data->nvalue_count = j; +} + +extern struct omap_sr_data omap_sr_pdata[]; + +static int __init sr_init_by_name(const char *name, const char *voltdm) +{ + struct omap_sr_data *sr_data = NULL; + struct omap_volt_data *volt_data; + static int i; + + if (!strncmp(name, "smartreflex_mpu_iva", 20) || + !strncmp(name, "smartreflex_mpu", 16)) + sr_data = &omap_sr_pdata[OMAP_SR_MPU]; + else if (!strncmp(name, "smartreflex_core", 17)) + sr_data = &omap_sr_pdata[OMAP_SR_CORE]; + else if (!strncmp(name, "smartreflex_iva", 16)) + sr_data = &omap_sr_pdata[OMAP_SR_IVA]; + + if (!sr_data) { + pr_err("%s: Unknown instance %s\n", __func__, name); + return -EINVAL; + } + + sr_data->name = name; + if (cpu_is_omap343x()) + sr_data->ip_type = 1; + else + sr_data->ip_type = 2; + sr_data->senn_mod = 0x1; + sr_data->senp_mod = 0x1; + + if (cpu_is_omap34xx() || cpu_is_omap44xx()) { + sr_data->err_weight = OMAP3430_SR_ERRWEIGHT; + sr_data->err_maxlimit = OMAP3430_SR_ERRMAXLIMIT; + sr_data->accum_data = OMAP3430_SR_ACCUMDATA; + if (!(strcmp(sr_data->name, "smartreflex_mpu"))) { + sr_data->senn_avgweight = OMAP3430_SR1_SENNAVGWEIGHT; + sr_data->senp_avgweight = OMAP3430_SR1_SENPAVGWEIGHT; + } else { + sr_data->senn_avgweight = OMAP3430_SR2_SENNAVGWEIGHT; + sr_data->senp_avgweight = OMAP3430_SR2_SENPAVGWEIGHT; + } + } + + sr_data->voltdm = voltdm_lookup(voltdm); + if (!sr_data->voltdm) { + pr_err("%s: Unable to get voltage domain pointer for VDD %s\n", + __func__, voltdm); + goto exit; + } + + omap_voltage_get_volttable(sr_data->voltdm, &volt_data); + if (!volt_data) { + pr_err("%s: No Voltage table registered for VDD%d\n", + __func__, i + 1); + goto exit; + } + + sr_set_nvalues(volt_data, sr_data); + + sr_data->enable_on_init = sr_enable_on_init; + +exit: + i++; + + return 0; +} + +#ifdef CONFIG_OMAP_HWMOD +static int __init sr_dev_init(struct omap_hwmod *oh, void *user) +{ + struct omap_smartreflex_dev_attr *sr_dev_attr; + + sr_dev_attr = (struct omap_smartreflex_dev_attr *)oh->dev_attr; + if (!sr_dev_attr || !sr_dev_attr->sensor_voltdm_name) { + pr_err("%s: No voltage domain specified for %s. Cannot initialize\n", + __func__, oh->name); + return 0; + } + + return sr_init_by_name(oh->name, sr_dev_attr->sensor_voltdm_name); +} +#else +static int __init sr_dev_init(struct omap_hwmod *oh, void *user) +{ + return -EINVAL; +} +#endif + +/* + * API to be called from board files to enable smartreflex + * autocompensation at init. + */ +void __init omap_enable_smartreflex_on_init(void) +{ + sr_enable_on_init = true; +} + +static const char * const omap4_sr_instances[] = { + "mpu", + "iva", + "core", +}; + +static const char * const dra7_sr_instances[] = { + "mpu", + "core", +}; + +int __init omap_devinit_smartreflex(void) +{ + const char * const *sr_inst = NULL; + int i, nr_sr = 0; + + if (soc_is_omap44xx()) { + sr_inst = omap4_sr_instances; + nr_sr = ARRAY_SIZE(omap4_sr_instances); + + } else if (soc_is_dra7xx()) { + sr_inst = dra7_sr_instances; + nr_sr = ARRAY_SIZE(dra7_sr_instances); + } + + if (nr_sr) { + const char *name, *voltdm; + + for (i = 0; i < nr_sr; i++) { + name = kasprintf(GFP_KERNEL, "smartreflex_%s", sr_inst[i]); + voltdm = sr_inst[i]; + sr_init_by_name(name, voltdm); + } + + return 0; + } + + return omap_hwmod_for_each_by_class("smartreflex", sr_dev_init, NULL); +} diff --git a/arch/arm/mach-omap2/sram.c b/arch/arm/mach-omap2/sram.c new file mode 100644 index 000000000..39cf270da --- /dev/null +++ b/arch/arm/mach-omap2/sram.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * OMAP SRAM detection and management + * + * Copyright (C) 2005 Nokia Corporation + * Written by Tony Lindgren <tony@atomide.com> + * + * Copyright (C) 2009-2012 Texas Instruments + * Added OMAP4/5 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> + +#include <asm/fncpy.h> +#include <asm/tlb.h> +#include <asm/cacheflush.h> +#include <asm/set_memory.h> + +#include <asm/mach/map.h> + +#include "soc.h" +#include "iomap.h" +#include "prm2xxx_3xxx.h" +#include "sdrc.h" +#include "sram.h" + +#define OMAP2_SRAM_PUB_PA (OMAP2_SRAM_PA + 0xf800) +#define OMAP3_SRAM_PUB_PA (OMAP3_SRAM_PA + 0x8000) + +#define SRAM_BOOTLOADER_SZ 0x00 + +#define OMAP24XX_VA_REQINFOPERM0 OMAP2_L3_IO_ADDRESS(0x68005048) +#define OMAP24XX_VA_READPERM0 OMAP2_L3_IO_ADDRESS(0x68005050) +#define OMAP24XX_VA_WRITEPERM0 OMAP2_L3_IO_ADDRESS(0x68005058) + +#define OMAP34XX_VA_REQINFOPERM0 OMAP2_L3_IO_ADDRESS(0x68012848) +#define OMAP34XX_VA_READPERM0 OMAP2_L3_IO_ADDRESS(0x68012850) +#define OMAP34XX_VA_WRITEPERM0 OMAP2_L3_IO_ADDRESS(0x68012858) +#define OMAP34XX_VA_ADDR_MATCH2 OMAP2_L3_IO_ADDRESS(0x68012880) +#define OMAP34XX_VA_SMS_RG_ATT0 OMAP2_L3_IO_ADDRESS(0x6C000048) + +#define GP_DEVICE 0x300 + +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) + +static unsigned long omap_sram_start; +static unsigned long omap_sram_size; +static void __iomem *omap_sram_base; +static unsigned long omap_sram_skip; +static void __iomem *omap_sram_ceil; + +/* + * Memory allocator for SRAM: calculates the new ceiling address + * for pushing a function using the fncpy API. + * + * Note that fncpy requires the returned address to be aligned + * to an 8-byte boundary. + */ +static void *omap_sram_push_address(unsigned long size) +{ + unsigned long available, new_ceil = (unsigned long)omap_sram_ceil; + + available = omap_sram_ceil - (omap_sram_base + omap_sram_skip); + + if (size > available) { + pr_err("Not enough space in SRAM\n"); + return NULL; + } + + new_ceil -= size; + new_ceil = ROUND_DOWN(new_ceil, FNCPY_ALIGN); + omap_sram_ceil = IOMEM(new_ceil); + + return (void __force *)omap_sram_ceil; +} + +void *omap_sram_push(void *funcp, unsigned long size) +{ + void *sram; + unsigned long base; + int pages; + void *dst = NULL; + + sram = omap_sram_push_address(size); + if (!sram) + return NULL; + + base = (unsigned long)sram & PAGE_MASK; + pages = PAGE_ALIGN(size) / PAGE_SIZE; + + set_memory_rw(base, pages); + + dst = fncpy(sram, funcp, size); + + set_memory_ro(base, pages); + set_memory_x(base, pages); + + return dst; +} + +/* + * The SRAM context is lost during off-idle and stack + * needs to be reset. + */ +static void omap_sram_reset(void) +{ + omap_sram_ceil = omap_sram_base + omap_sram_size; +} + +/* + * Depending on the target RAMFS firewall setup, the public usable amount of + * SRAM varies. The default accessible size for all device types is 2k. A GP + * device allows ARM11 but not other initiators for full size. This + * functionality seems ok until some nice security API happens. + */ +static int is_sram_locked(void) +{ + if (OMAP2_DEVICE_TYPE_GP == omap_type()) { + /* RAMFW: R/W access to all initiators for all qualifier sets */ + if (cpu_is_omap242x()) { + writel_relaxed(0xFF, OMAP24XX_VA_REQINFOPERM0); /* all q-vects */ + writel_relaxed(0xCFDE, OMAP24XX_VA_READPERM0); /* all i-read */ + writel_relaxed(0xCFDE, OMAP24XX_VA_WRITEPERM0); /* all i-write */ + } + if (cpu_is_omap34xx()) { + writel_relaxed(0xFFFF, OMAP34XX_VA_REQINFOPERM0); /* all q-vects */ + writel_relaxed(0xFFFF, OMAP34XX_VA_READPERM0); /* all i-read */ + writel_relaxed(0xFFFF, OMAP34XX_VA_WRITEPERM0); /* all i-write */ + writel_relaxed(0x0, OMAP34XX_VA_ADDR_MATCH2); + writel_relaxed(0xFFFFFFFF, OMAP34XX_VA_SMS_RG_ATT0); + } + return 0; + } else + return 1; /* assume locked with no PPA or security driver */ +} + +/* + * The amount of SRAM depends on the core type. + * Note that we cannot try to test for SRAM here because writes + * to secure SRAM will hang the system. Also the SRAM is not + * yet mapped at this point. + */ +static void __init omap_detect_sram(void) +{ + omap_sram_skip = SRAM_BOOTLOADER_SZ; + if (is_sram_locked()) { + if (cpu_is_omap34xx()) { + omap_sram_start = OMAP3_SRAM_PUB_PA; + if ((omap_type() == OMAP2_DEVICE_TYPE_EMU) || + (omap_type() == OMAP2_DEVICE_TYPE_SEC)) { + omap_sram_size = 0x7000; /* 28K */ + omap_sram_skip += SZ_16K; + } else { + omap_sram_size = 0x8000; /* 32K */ + } + } else { + omap_sram_start = OMAP2_SRAM_PUB_PA; + omap_sram_size = 0x800; /* 2K */ + } + } else { + if (cpu_is_omap34xx()) { + omap_sram_start = OMAP3_SRAM_PA; + omap_sram_size = 0x10000; /* 64K */ + } else { + omap_sram_start = OMAP2_SRAM_PA; + if (cpu_is_omap242x()) + omap_sram_size = 0xa0000; /* 640K */ + else if (cpu_is_omap243x()) + omap_sram_size = 0x10000; /* 64K */ + } + } +} + +/* + * Note that we cannot use ioremap for SRAM, as clock init needs SRAM early. + */ +static void __init omap2_map_sram(void) +{ + unsigned long base; + int pages; + int cached = 1; + + if (cpu_is_omap34xx()) { + /* + * SRAM must be marked as non-cached on OMAP3 since the + * CORE DPLL M2 divider change code (in SRAM) runs with the + * SDRAM controller disabled, and if it is marked cached, + * the ARM may attempt to write cache lines back to SDRAM + * which will cause the system to hang. + */ + cached = 0; + } + + if (omap_sram_size == 0) + return; + + omap_sram_start = ROUND_DOWN(omap_sram_start, PAGE_SIZE); + omap_sram_base = __arm_ioremap_exec(omap_sram_start, omap_sram_size, cached); + if (!omap_sram_base) { + pr_err("SRAM: Could not map\n"); + return; + } + + omap_sram_reset(); + + /* + * Looks like we need to preserve some bootloader code at the + * beginning of SRAM for jumping to flash for reboot to work... + */ + memset_io(omap_sram_base + omap_sram_skip, 0, + omap_sram_size - omap_sram_skip); + + base = (unsigned long)omap_sram_base; + pages = PAGE_ALIGN(omap_sram_size) / PAGE_SIZE; + + set_memory_ro(base, pages); + set_memory_x(base, pages); +} + +static void (*_omap2_sram_ddr_init)(u32 *slow_dll_ctrl, u32 fast_dll_ctrl, + u32 base_cs, u32 force_unlock); + +void omap2_sram_ddr_init(u32 *slow_dll_ctrl, u32 fast_dll_ctrl, + u32 base_cs, u32 force_unlock) +{ + BUG_ON(!_omap2_sram_ddr_init); + _omap2_sram_ddr_init(slow_dll_ctrl, fast_dll_ctrl, + base_cs, force_unlock); +} + +static void (*_omap2_sram_reprogram_sdrc)(u32 perf_level, u32 dll_val, + u32 mem_type); + +void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, u32 mem_type) +{ + BUG_ON(!_omap2_sram_reprogram_sdrc); + _omap2_sram_reprogram_sdrc(perf_level, dll_val, mem_type); +} + +static u32 (*_omap2_set_prcm)(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass); + +u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass) +{ + BUG_ON(!_omap2_set_prcm); + return _omap2_set_prcm(dpll_ctrl_val, sdrc_rfr_val, bypass); +} + +#ifdef CONFIG_SOC_OMAP2420 +static int __init omap242x_sram_init(void) +{ + _omap2_sram_ddr_init = omap_sram_push(omap242x_sram_ddr_init, + omap242x_sram_ddr_init_sz); + + _omap2_sram_reprogram_sdrc = omap_sram_push(omap242x_sram_reprogram_sdrc, + omap242x_sram_reprogram_sdrc_sz); + + _omap2_set_prcm = omap_sram_push(omap242x_sram_set_prcm, + omap242x_sram_set_prcm_sz); + + return 0; +} +#else +static inline int omap242x_sram_init(void) +{ + return 0; +} +#endif + +#ifdef CONFIG_SOC_OMAP2430 +static int __init omap243x_sram_init(void) +{ + _omap2_sram_ddr_init = omap_sram_push(omap243x_sram_ddr_init, + omap243x_sram_ddr_init_sz); + + _omap2_sram_reprogram_sdrc = omap_sram_push(omap243x_sram_reprogram_sdrc, + omap243x_sram_reprogram_sdrc_sz); + + _omap2_set_prcm = omap_sram_push(omap243x_sram_set_prcm, + omap243x_sram_set_prcm_sz); + + return 0; +} +#else +static inline int omap243x_sram_init(void) +{ + return 0; +} +#endif + +#ifdef CONFIG_ARCH_OMAP3 + +void omap3_sram_restore_context(void) +{ + omap_sram_reset(); + + omap_push_sram_idle(); +} + +static inline int omap34xx_sram_init(void) +{ + omap3_sram_restore_context(); + return 0; +} +#else +static inline int omap34xx_sram_init(void) +{ + return 0; +} +#endif /* CONFIG_ARCH_OMAP3 */ + +int __init omap_sram_init(void) +{ + omap_detect_sram(); + omap2_map_sram(); + + if (cpu_is_omap242x()) + omap242x_sram_init(); + else if (cpu_is_omap2430()) + omap243x_sram_init(); + else if (cpu_is_omap34xx()) + omap34xx_sram_init(); + + return 0; +} diff --git a/arch/arm/mach-omap2/sram.h b/arch/arm/mach-omap2/sram.h new file mode 100644 index 000000000..271062f23 --- /dev/null +++ b/arch/arm/mach-omap2/sram.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Interface for functions that need to be run in internal SRAM + */ + +#ifndef __ASSEMBLY__ + +extern void omap2_sram_ddr_init(u32 *slow_dll_ctrl, u32 fast_dll_ctrl, + u32 base_cs, u32 force_unlock); +extern void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, + u32 mem_type); +extern u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass); + +extern void omap3_sram_restore_context(void); + +extern int __init omap_sram_init(void); + +extern void *omap_sram_push(void *funcp, unsigned long size); + +/* Do not use these */ +extern void omap24xx_sram_reprogram_clock(u32 ckctl, u32 dpllctl); +extern unsigned long omap24xx_sram_reprogram_clock_sz; + +extern void omap242x_sram_ddr_init(u32 *slow_dll_ctrl, u32 fast_dll_ctrl, + u32 base_cs, u32 force_unlock); +extern unsigned long omap242x_sram_ddr_init_sz; + +extern u32 omap242x_sram_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, + int bypass); +extern unsigned long omap242x_sram_set_prcm_sz; + +extern void omap242x_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, + u32 mem_type); +extern unsigned long omap242x_sram_reprogram_sdrc_sz; + + +extern void omap243x_sram_ddr_init(u32 *slow_dll_ctrl, u32 fast_dll_ctrl, + u32 base_cs, u32 force_unlock); +extern unsigned long omap243x_sram_ddr_init_sz; + +extern u32 omap243x_sram_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, + int bypass); +extern unsigned long omap243x_sram_set_prcm_sz; + +extern void omap243x_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, + u32 mem_type); +extern unsigned long omap243x_sram_reprogram_sdrc_sz; + +#ifdef CONFIG_PM +extern void omap_push_sram_idle(void); +#else +static inline void omap_push_sram_idle(void) {} +#endif /* CONFIG_PM */ + +#endif /* __ASSEMBLY__ */ + +/* + * OMAP2+: define the SRAM PA addresses. + * Used by the SRAM management code and the idle sleep code. + */ +#define OMAP2_SRAM_PA 0x40200000 +#define OMAP3_SRAM_PA 0x40200000 diff --git a/arch/arm/mach-omap2/sram242x.S b/arch/arm/mach-omap2/sram242x.S new file mode 100644 index 000000000..92ef21ac2 --- /dev/null +++ b/arch/arm/mach-omap2/sram242x.S @@ -0,0 +1,317 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * linux/arch/arm/mach-omap2/sram242x.S + * + * Omap2 specific functions that need to be run in internal SRAM + * + * (C) Copyright 2004 + * Texas Instruments, <www.ti.com> + * Richard Woodruff <r-woodruff2@ti.com> + * + * Richard Woodruff notes that any changes to this code must be carefully + * audited and tested to ensure that they don't cause a TLB miss while + * the SDRAM is inaccessible. Such a situation will crash the system + * since it will cause the ARM MMU to attempt to walk the page tables. + * These crashes may be intermittent. + */ +#include <linux/linkage.h> + +#include <asm/assembler.h> + +#include "soc.h" +#include "iomap.h" +#include "prm2xxx.h" +#include "cm2xxx.h" +#include "sdrc.h" + + .text + + .align 3 +ENTRY(omap242x_sram_ddr_init) + stmfd sp!, {r0 - r12, lr} @ save registers on stack + + mov r12, r2 @ capture CS1 vs CS0 + mov r8, r3 @ capture force parameter + + /* frequency shift down */ + ldr r2, omap242x_sdi_cm_clksel2_pll @ get address of dpllout reg + mov r3, #0x1 @ value for 1x operation + str r3, [r2] @ go to L1-freq operation + + /* voltage shift down */ + mov r9, #0x1 @ set up for L1 voltage call + bl voltage_shift @ go drop voltage + + /* dll lock mode */ + ldr r11, omap242x_sdi_sdrc_dlla_ctrl @ addr of dlla ctrl + ldr r10, [r11] @ get current val + cmp r12, #0x1 @ cs1 base (2422 es2.05/1) + addeq r11, r11, #0x8 @ if cs1 base, move to DLLB + mvn r9, #0x4 @ mask to get clear bit2 + and r10, r10, r9 @ clear bit2 for lock mode. + orr r10, r10, #0x8 @ make sure DLL on (es2 bit pos) + orr r10, r10, #0x2 @ 90 degree phase for all below 133MHz + str r10, [r11] @ commit to DLLA_CTRL + bl i_dll_wait @ wait for dll to lock + + /* get dll value */ + add r11, r11, #0x4 @ get addr of status reg + ldr r10, [r11] @ get locked value + + /* voltage shift up */ + mov r9, #0x0 @ shift back to L0-voltage + bl voltage_shift @ go raise voltage + + /* frequency shift up */ + mov r3, #0x2 @ value for 2x operation + str r3, [r2] @ go to L0-freq operation + + /* reset entry mode for dllctrl */ + sub r11, r11, #0x4 @ move from status to ctrl + cmp r12, #0x1 @ normalize if cs1 based + subeq r11, r11, #0x8 @ possibly back to DLLA + cmp r8, #0x1 @ if forced unlock exit + orreq r1, r1, #0x4 @ make sure exit with unlocked value + str r1, [r11] @ restore DLLA_CTRL high value + add r11, r11, #0x8 @ move to DLLB_CTRL addr + str r1, [r11] @ set value DLLB_CTRL + bl i_dll_wait @ wait for possible lock + + /* set up for return, DDR should be good */ + str r10, [r0] @ write dll_status and return counter + ldmfd sp!, {r0 - r12, pc} @ restore regs and return + + /* ensure the DLL has relocked */ +i_dll_wait: + mov r4, #0x800 @ delay DLL relock, min 0x400 L3 clocks +i_dll_delay: + subs r4, r4, #0x1 + bne i_dll_delay + ret lr + + /* + * shift up or down voltage, use R9 as input to tell level. + * wait for it to finish, use 32k sync counter, 1tick=31uS. + */ +voltage_shift: + ldr r4, omap242x_sdi_prcm_voltctrl @ get addr of volt ctrl. + ldr r5, [r4] @ get value. + ldr r6, prcm_mask_val @ get value of mask + and r5, r5, r6 @ apply mask to clear bits + orr r5, r5, r9 @ bulld value for L0/L1-volt operation. + str r5, [r4] @ set up for change. + mov r3, #0x4000 @ get val for force + orr r5, r5, r3 @ build value for force + str r5, [r4] @ Force transition to L1 + + ldr r3, omap242x_sdi_timer_32ksynct_cr @ get addr of counter + ldr r5, [r3] @ get value + add r5, r5, #0x3 @ give it at most 93uS +volt_delay: + ldr r7, [r3] @ get timer value + cmp r5, r7 @ time up? + bhi volt_delay @ not yet->branch + ret lr @ back to caller. + +omap242x_sdi_cm_clksel2_pll: + .word OMAP2420_CM_REGADDR(PLL_MOD, CM_CLKSEL2) +omap242x_sdi_sdrc_dlla_ctrl: + .word OMAP242X_SDRC_REGADDR(SDRC_DLLA_CTRL) +omap242x_sdi_prcm_voltctrl: + .word OMAP2420_PRCM_VOLTCTRL +prcm_mask_val: + .word 0xFFFF3FFC +omap242x_sdi_timer_32ksynct_cr: + .word OMAP2_L4_IO_ADDRESS(OMAP2420_32KSYNCT_BASE + 0x010) +ENTRY(omap242x_sram_ddr_init_sz) + .word . - omap242x_sram_ddr_init + +/* + * Reprograms memory timings. + * r0 = [PRCM_FULL | PRCM_HALF] r1 = SDRC_DLLA_CTRL value r2 = [DDR | SDR] + * PRCM_FULL = 2, PRCM_HALF = 1, DDR = 1, SDR = 0 + */ + .align 3 +ENTRY(omap242x_sram_reprogram_sdrc) + stmfd sp!, {r0 - r10, lr} @ save registers on stack + mov r3, #0x0 @ clear for mrc call + mcr p15, 0, r3, c7, c10, 4 @ memory barrier, finish ARM SDR/DDR + nop + nop + ldr r6, omap242x_srs_sdrc_rfr_ctrl @ get addr of refresh reg + ldr r5, [r6] @ get value + mov r5, r5, lsr #8 @ isolate rfr field and drop burst + + cmp r0, #0x1 @ going to half speed? + movne r9, #0x0 @ if up set flag up for pre up, hi volt + + blne voltage_shift_c @ adjust voltage + + cmp r0, #0x1 @ going to half speed (post branch link) + moveq r5, r5, lsr #1 @ divide by 2 if to half + movne r5, r5, lsl #1 @ mult by 2 if to full + mov r5, r5, lsl #8 @ put rfr field back into place + add r5, r5, #0x1 @ turn on burst of 1 + ldr r4, omap242x_srs_cm_clksel2_pll @ get address of out reg + ldr r3, [r4] @ get curr value + orr r3, r3, #0x3 + bic r3, r3, #0x3 @ clear lower bits + orr r3, r3, r0 @ new state value + str r3, [r4] @ set new state (pll/x, x=1 or 2) + nop + nop + + moveq r9, #0x1 @ if speed down, post down, drop volt + bleq voltage_shift_c + + mcr p15, 0, r3, c7, c10, 4 @ memory barrier + str r5, [r6] @ set new RFR_1 value + add r6, r6, #0x30 @ get RFR_2 addr + str r5, [r6] @ set RFR_2 + nop + cmp r2, #0x1 @ (SDR or DDR) do we need to adjust DLL + bne freq_out @ leave if SDR, no DLL function + + /* With DDR, we need to take care of the DLL for the frequency change */ + ldr r2, omap242x_srs_sdrc_dlla_ctrl @ addr of dlla ctrl + str r1, [r2] @ write out new SDRC_DLLA_CTRL + add r2, r2, #0x8 @ addr to SDRC_DLLB_CTRL + str r1, [r2] @ commit to SDRC_DLLB_CTRL + mov r1, #0x2000 @ wait DLL relock, min 0x400 L3 clocks +dll_wait: + subs r1, r1, #0x1 + bne dll_wait +freq_out: + ldmfd sp!, {r0 - r10, pc} @ restore regs and return + + /* + * shift up or down voltage, use R9 as input to tell level. + * wait for it to finish, use 32k sync counter, 1tick=31uS. + */ +voltage_shift_c: + ldr r10, omap242x_srs_prcm_voltctrl @ get addr of volt ctrl + ldr r8, [r10] @ get value + ldr r7, ddr_prcm_mask_val @ get value of mask + and r8, r8, r7 @ apply mask to clear bits + orr r8, r8, r9 @ bulld value for L0/L1-volt operation. + str r8, [r10] @ set up for change. + mov r7, #0x4000 @ get val for force + orr r8, r8, r7 @ build value for force + str r8, [r10] @ Force transition to L1 + + ldr r10, omap242x_srs_timer_32ksynct @ get addr of counter + ldr r8, [r10] @ get value + add r8, r8, #0x2 @ give it at most 62uS (min 31+) +volt_delay_c: + ldr r7, [r10] @ get timer value + cmp r8, r7 @ time up? + bhi volt_delay_c @ not yet->branch + ret lr @ back to caller + +omap242x_srs_cm_clksel2_pll: + .word OMAP2420_CM_REGADDR(PLL_MOD, CM_CLKSEL2) +omap242x_srs_sdrc_dlla_ctrl: + .word OMAP242X_SDRC_REGADDR(SDRC_DLLA_CTRL) +omap242x_srs_sdrc_rfr_ctrl: + .word OMAP242X_SDRC_REGADDR(SDRC_RFR_CTRL_0) +omap242x_srs_prcm_voltctrl: + .word OMAP2420_PRCM_VOLTCTRL +ddr_prcm_mask_val: + .word 0xFFFF3FFC +omap242x_srs_timer_32ksynct: + .word OMAP2_L4_IO_ADDRESS(OMAP2420_32KSYNCT_BASE + 0x010) + +ENTRY(omap242x_sram_reprogram_sdrc_sz) + .word . - omap242x_sram_reprogram_sdrc + +/* + * Set dividers and pll. Also recalculate DLL value for DDR and unlock mode. + */ + .align 3 +ENTRY(omap242x_sram_set_prcm) + stmfd sp!, {r0-r12, lr} @ regs to stack + adr r4, pbegin @ addr of preload start + adr r8, pend @ addr of preload end + mcrr p15, 1, r8, r4, c12 @ preload into icache +pbegin: + /* move into fast relock bypass */ + ldr r8, omap242x_ssp_pll_ctl @ get addr + ldr r5, [r8] @ get val + mvn r6, #0x3 @ clear mask + and r5, r5, r6 @ clear field + orr r7, r5, #0x2 @ fast relock val + str r7, [r8] @ go to fast relock + ldr r4, omap242x_ssp_pll_stat @ addr of stat +block: + /* wait for bypass */ + ldr r8, [r4] @ stat value + and r8, r8, #0x3 @ mask for stat + cmp r8, #0x1 @ there yet + bne block @ loop if not + + /* set new dpll dividers _after_ in bypass */ + ldr r4, omap242x_ssp_pll_div @ get addr + str r0, [r4] @ set dpll ctrl val + + ldr r4, omap242x_ssp_set_config @ get addr + mov r8, #1 @ valid cfg msk + str r8, [r4] @ make dividers take + + mov r4, #100 @ dead spin a bit +wait_a_bit: + subs r4, r4, #1 @ dec loop + bne wait_a_bit @ delay done? + + /* check if staying in bypass */ + cmp r2, #0x1 @ stay in bypass? + beq pend @ jump over dpll relock + + /* relock DPLL with new vals */ + ldr r5, omap242x_ssp_pll_stat @ get addr + ldr r4, omap242x_ssp_pll_ctl @ get addr + orr r8, r7, #0x3 @ val for lock dpll + str r8, [r4] @ set val + mov r0, #1000 @ dead spin a bit +wait_more: + subs r0, r0, #1 @ dec loop + bne wait_more @ delay done? +wait_lock: + ldr r8, [r5] @ get lock val + and r8, r8, #3 @ isolate field + cmp r8, #2 @ locked? + bne wait_lock @ wait if not +pend: + /* update memory timings & briefly lock dll */ + ldr r4, omap242x_ssp_sdrc_rfr @ get addr + str r1, [r4] @ update refresh timing + ldr r11, omap242x_ssp_dlla_ctrl @ get addr of DLLA ctrl + ldr r10, [r11] @ get current val + mvn r9, #0x4 @ mask to get clear bit2 + and r10, r10, r9 @ clear bit2 for lock mode + orr r10, r10, #0x8 @ make sure DLL on (es2 bit pos) + str r10, [r11] @ commit to DLLA_CTRL + add r11, r11, #0x8 @ move to dllb + str r10, [r11] @ hit DLLB also + + mov r4, #0x800 @ relock time (min 0x400 L3 clocks) +wait_dll_lock: + subs r4, r4, #0x1 + bne wait_dll_lock + nop + ldmfd sp!, {r0-r12, pc} @ restore regs and return + +omap242x_ssp_set_config: + .word OMAP2420_PRCM_CLKCFG_CTRL +omap242x_ssp_pll_ctl: + .word OMAP2420_CM_REGADDR(PLL_MOD, CM_CLKEN) +omap242x_ssp_pll_stat: + .word OMAP2420_CM_REGADDR(PLL_MOD, CM_IDLEST) +omap242x_ssp_pll_div: + .word OMAP2420_CM_REGADDR(PLL_MOD, CM_CLKSEL1) +omap242x_ssp_sdrc_rfr: + .word OMAP242X_SDRC_REGADDR(SDRC_RFR_CTRL_0) +omap242x_ssp_dlla_ctrl: + .word OMAP242X_SDRC_REGADDR(SDRC_DLLA_CTRL) + +ENTRY(omap242x_sram_set_prcm_sz) + .word . - omap242x_sram_set_prcm diff --git a/arch/arm/mach-omap2/sram243x.S b/arch/arm/mach-omap2/sram243x.S new file mode 100644 index 000000000..faf03b7f0 --- /dev/null +++ b/arch/arm/mach-omap2/sram243x.S @@ -0,0 +1,317 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * linux/arch/arm/mach-omap2/sram243x.S + * + * Omap2 specific functions that need to be run in internal SRAM + * + * (C) Copyright 2004 + * Texas Instruments, <www.ti.com> + * Richard Woodruff <r-woodruff2@ti.com> + * + * Richard Woodruff notes that any changes to this code must be carefully + * audited and tested to ensure that they don't cause a TLB miss while + * the SDRAM is inaccessible. Such a situation will crash the system + * since it will cause the ARM MMU to attempt to walk the page tables. + * These crashes may be intermittent. + */ +#include <linux/linkage.h> + +#include <asm/assembler.h> + +#include "soc.h" +#include "iomap.h" +#include "prm2xxx.h" +#include "cm2xxx.h" +#include "sdrc.h" + + .text + + .align 3 +ENTRY(omap243x_sram_ddr_init) + stmfd sp!, {r0 - r12, lr} @ save registers on stack + + mov r12, r2 @ capture CS1 vs CS0 + mov r8, r3 @ capture force parameter + + /* frequency shift down */ + ldr r2, omap243x_sdi_cm_clksel2_pll @ get address of dpllout reg + mov r3, #0x1 @ value for 1x operation + str r3, [r2] @ go to L1-freq operation + + /* voltage shift down */ + mov r9, #0x1 @ set up for L1 voltage call + bl voltage_shift @ go drop voltage + + /* dll lock mode */ + ldr r11, omap243x_sdi_sdrc_dlla_ctrl @ addr of dlla ctrl + ldr r10, [r11] @ get current val + cmp r12, #0x1 @ cs1 base (2422 es2.05/1) + addeq r11, r11, #0x8 @ if cs1 base, move to DLLB + mvn r9, #0x4 @ mask to get clear bit2 + and r10, r10, r9 @ clear bit2 for lock mode. + orr r10, r10, #0x8 @ make sure DLL on (es2 bit pos) + orr r10, r10, #0x2 @ 90 degree phase for all below 133MHz + str r10, [r11] @ commit to DLLA_CTRL + bl i_dll_wait @ wait for dll to lock + + /* get dll value */ + add r11, r11, #0x4 @ get addr of status reg + ldr r10, [r11] @ get locked value + + /* voltage shift up */ + mov r9, #0x0 @ shift back to L0-voltage + bl voltage_shift @ go raise voltage + + /* frequency shift up */ + mov r3, #0x2 @ value for 2x operation + str r3, [r2] @ go to L0-freq operation + + /* reset entry mode for dllctrl */ + sub r11, r11, #0x4 @ move from status to ctrl + cmp r12, #0x1 @ normalize if cs1 based + subeq r11, r11, #0x8 @ possibly back to DLLA + cmp r8, #0x1 @ if forced unlock exit + orreq r1, r1, #0x4 @ make sure exit with unlocked value + str r1, [r11] @ restore DLLA_CTRL high value + add r11, r11, #0x8 @ move to DLLB_CTRL addr + str r1, [r11] @ set value DLLB_CTRL + bl i_dll_wait @ wait for possible lock + + /* set up for return, DDR should be good */ + str r10, [r0] @ write dll_status and return counter + ldmfd sp!, {r0 - r12, pc} @ restore regs and return + + /* ensure the DLL has relocked */ +i_dll_wait: + mov r4, #0x800 @ delay DLL relock, min 0x400 L3 clocks +i_dll_delay: + subs r4, r4, #0x1 + bne i_dll_delay + ret lr + + /* + * shift up or down voltage, use R9 as input to tell level. + * wait for it to finish, use 32k sync counter, 1tick=31uS. + */ +voltage_shift: + ldr r4, omap243x_sdi_prcm_voltctrl @ get addr of volt ctrl. + ldr r5, [r4] @ get value. + ldr r6, prcm_mask_val @ get value of mask + and r5, r5, r6 @ apply mask to clear bits + orr r5, r5, r9 @ bulld value for L0/L1-volt operation. + str r5, [r4] @ set up for change. + mov r3, #0x4000 @ get val for force + orr r5, r5, r3 @ build value for force + str r5, [r4] @ Force transition to L1 + + ldr r3, omap243x_sdi_timer_32ksynct_cr @ get addr of counter + ldr r5, [r3] @ get value + add r5, r5, #0x3 @ give it at most 93uS +volt_delay: + ldr r7, [r3] @ get timer value + cmp r5, r7 @ time up? + bhi volt_delay @ not yet->branch + ret lr @ back to caller. + +omap243x_sdi_cm_clksel2_pll: + .word OMAP2430_CM_REGADDR(PLL_MOD, CM_CLKSEL2) +omap243x_sdi_sdrc_dlla_ctrl: + .word OMAP243X_SDRC_REGADDR(SDRC_DLLA_CTRL) +omap243x_sdi_prcm_voltctrl: + .word OMAP2430_PRCM_VOLTCTRL +prcm_mask_val: + .word 0xFFFF3FFC +omap243x_sdi_timer_32ksynct_cr: + .word OMAP2_L4_IO_ADDRESS(OMAP2430_32KSYNCT_BASE + 0x010) +ENTRY(omap243x_sram_ddr_init_sz) + .word . - omap243x_sram_ddr_init + +/* + * Reprograms memory timings. + * r0 = [PRCM_FULL | PRCM_HALF] r1 = SDRC_DLLA_CTRL value r2 = [DDR | SDR] + * PRCM_FULL = 2, PRCM_HALF = 1, DDR = 1, SDR = 0 + */ + .align 3 +ENTRY(omap243x_sram_reprogram_sdrc) + stmfd sp!, {r0 - r10, lr} @ save registers on stack + mov r3, #0x0 @ clear for mrc call + mcr p15, 0, r3, c7, c10, 4 @ memory barrier, finish ARM SDR/DDR + nop + nop + ldr r6, omap243x_srs_sdrc_rfr_ctrl @ get addr of refresh reg + ldr r5, [r6] @ get value + mov r5, r5, lsr #8 @ isolate rfr field and drop burst + + cmp r0, #0x1 @ going to half speed? + movne r9, #0x0 @ if up set flag up for pre up, hi volt + + blne voltage_shift_c @ adjust voltage + + cmp r0, #0x1 @ going to half speed (post branch link) + moveq r5, r5, lsr #1 @ divide by 2 if to half + movne r5, r5, lsl #1 @ mult by 2 if to full + mov r5, r5, lsl #8 @ put rfr field back into place + add r5, r5, #0x1 @ turn on burst of 1 + ldr r4, omap243x_srs_cm_clksel2_pll @ get address of out reg + ldr r3, [r4] @ get curr value + orr r3, r3, #0x3 + bic r3, r3, #0x3 @ clear lower bits + orr r3, r3, r0 @ new state value + str r3, [r4] @ set new state (pll/x, x=1 or 2) + nop + nop + + moveq r9, #0x1 @ if speed down, post down, drop volt + bleq voltage_shift_c + + mcr p15, 0, r3, c7, c10, 4 @ memory barrier + str r5, [r6] @ set new RFR_1 value + add r6, r6, #0x30 @ get RFR_2 addr + str r5, [r6] @ set RFR_2 + nop + cmp r2, #0x1 @ (SDR or DDR) do we need to adjust DLL + bne freq_out @ leave if SDR, no DLL function + + /* With DDR, we need to take care of the DLL for the frequency change */ + ldr r2, omap243x_srs_sdrc_dlla_ctrl @ addr of dlla ctrl + str r1, [r2] @ write out new SDRC_DLLA_CTRL + add r2, r2, #0x8 @ addr to SDRC_DLLB_CTRL + str r1, [r2] @ commit to SDRC_DLLB_CTRL + mov r1, #0x2000 @ wait DLL relock, min 0x400 L3 clocks +dll_wait: + subs r1, r1, #0x1 + bne dll_wait +freq_out: + ldmfd sp!, {r0 - r10, pc} @ restore regs and return + + /* + * shift up or down voltage, use R9 as input to tell level. + * wait for it to finish, use 32k sync counter, 1tick=31uS. + */ +voltage_shift_c: + ldr r10, omap243x_srs_prcm_voltctrl @ get addr of volt ctrl + ldr r8, [r10] @ get value + ldr r7, ddr_prcm_mask_val @ get value of mask + and r8, r8, r7 @ apply mask to clear bits + orr r8, r8, r9 @ bulld value for L0/L1-volt operation. + str r8, [r10] @ set up for change. + mov r7, #0x4000 @ get val for force + orr r8, r8, r7 @ build value for force + str r8, [r10] @ Force transition to L1 + + ldr r10, omap243x_srs_timer_32ksynct @ get addr of counter + ldr r8, [r10] @ get value + add r8, r8, #0x2 @ give it at most 62uS (min 31+) +volt_delay_c: + ldr r7, [r10] @ get timer value + cmp r8, r7 @ time up? + bhi volt_delay_c @ not yet->branch + ret lr @ back to caller + +omap243x_srs_cm_clksel2_pll: + .word OMAP2430_CM_REGADDR(PLL_MOD, CM_CLKSEL2) +omap243x_srs_sdrc_dlla_ctrl: + .word OMAP243X_SDRC_REGADDR(SDRC_DLLA_CTRL) +omap243x_srs_sdrc_rfr_ctrl: + .word OMAP243X_SDRC_REGADDR(SDRC_RFR_CTRL_0) +omap243x_srs_prcm_voltctrl: + .word OMAP2430_PRCM_VOLTCTRL +ddr_prcm_mask_val: + .word 0xFFFF3FFC +omap243x_srs_timer_32ksynct: + .word OMAP2_L4_IO_ADDRESS(OMAP2430_32KSYNCT_BASE + 0x010) + +ENTRY(omap243x_sram_reprogram_sdrc_sz) + .word . - omap243x_sram_reprogram_sdrc + +/* + * Set dividers and pll. Also recalculate DLL value for DDR and unlock mode. + */ + .align 3 +ENTRY(omap243x_sram_set_prcm) + stmfd sp!, {r0-r12, lr} @ regs to stack + adr r4, pbegin @ addr of preload start + adr r8, pend @ addr of preload end + mcrr p15, 1, r8, r4, c12 @ preload into icache +pbegin: + /* move into fast relock bypass */ + ldr r8, omap243x_ssp_pll_ctl @ get addr + ldr r5, [r8] @ get val + mvn r6, #0x3 @ clear mask + and r5, r5, r6 @ clear field + orr r7, r5, #0x2 @ fast relock val + str r7, [r8] @ go to fast relock + ldr r4, omap243x_ssp_pll_stat @ addr of stat +block: + /* wait for bypass */ + ldr r8, [r4] @ stat value + and r8, r8, #0x3 @ mask for stat + cmp r8, #0x1 @ there yet + bne block @ loop if not + + /* set new dpll dividers _after_ in bypass */ + ldr r4, omap243x_ssp_pll_div @ get addr + str r0, [r4] @ set dpll ctrl val + + ldr r4, omap243x_ssp_set_config @ get addr + mov r8, #1 @ valid cfg msk + str r8, [r4] @ make dividers take + + mov r4, #100 @ dead spin a bit +wait_a_bit: + subs r4, r4, #1 @ dec loop + bne wait_a_bit @ delay done? + + /* check if staying in bypass */ + cmp r2, #0x1 @ stay in bypass? + beq pend @ jump over dpll relock + + /* relock DPLL with new vals */ + ldr r5, omap243x_ssp_pll_stat @ get addr + ldr r4, omap243x_ssp_pll_ctl @ get addr + orr r8, r7, #0x3 @ val for lock dpll + str r8, [r4] @ set val + mov r0, #1000 @ dead spin a bit +wait_more: + subs r0, r0, #1 @ dec loop + bne wait_more @ delay done? +wait_lock: + ldr r8, [r5] @ get lock val + and r8, r8, #3 @ isolate field + cmp r8, #2 @ locked? + bne wait_lock @ wait if not +pend: + /* update memory timings & briefly lock dll */ + ldr r4, omap243x_ssp_sdrc_rfr @ get addr + str r1, [r4] @ update refresh timing + ldr r11, omap243x_ssp_dlla_ctrl @ get addr of DLLA ctrl + ldr r10, [r11] @ get current val + mvn r9, #0x4 @ mask to get clear bit2 + and r10, r10, r9 @ clear bit2 for lock mode + orr r10, r10, #0x8 @ make sure DLL on (es2 bit pos) + str r10, [r11] @ commit to DLLA_CTRL + add r11, r11, #0x8 @ move to dllb + str r10, [r11] @ hit DLLB also + + mov r4, #0x800 @ relock time (min 0x400 L3 clocks) +wait_dll_lock: + subs r4, r4, #0x1 + bne wait_dll_lock + nop + ldmfd sp!, {r0-r12, pc} @ restore regs and return + +omap243x_ssp_set_config: + .word OMAP2430_PRCM_CLKCFG_CTRL +omap243x_ssp_pll_ctl: + .word OMAP2430_CM_REGADDR(PLL_MOD, CM_CLKEN) +omap243x_ssp_pll_stat: + .word OMAP2430_CM_REGADDR(PLL_MOD, CM_IDLEST) +omap243x_ssp_pll_div: + .word OMAP2430_CM_REGADDR(PLL_MOD, CM_CLKSEL1) +omap243x_ssp_sdrc_rfr: + .word OMAP243X_SDRC_REGADDR(SDRC_RFR_CTRL_0) +omap243x_ssp_dlla_ctrl: + .word OMAP243X_SDRC_REGADDR(SDRC_DLLA_CTRL) + +ENTRY(omap243x_sram_set_prcm_sz) + .word . - omap243x_sram_set_prcm diff --git a/arch/arm/mach-omap2/ti81xx-restart.c b/arch/arm/mach-omap2/ti81xx-restart.c new file mode 100644 index 000000000..d6dc518b1 --- /dev/null +++ b/arch/arm/mach-omap2/ti81xx-restart.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/reboot.h> + +#include "iomap.h" +#include "common.h" +#include "control.h" +#include "prm3xxx.h" + +#define TI81XX_PRM_DEVICE_RSTCTRL 0x00a0 +#define TI81XX_GLOBAL_RST_COLD BIT(1) + +/** + * ti81xx_restart - trigger a software restart of the SoC + * @mode: the "reboot mode", see arch/arm/kernel/{setup,process}.c + * @cmd: passed from the userspace program rebooting the system (if provided) + * + * Resets the SoC. For @cmd, see the 'reboot' syscall in + * kernel/sys.c. No return value. + * + * NOTE: Warm reset does not seem to work, may require resetting + * clocks to bypass mode. + */ +void ti81xx_restart(enum reboot_mode mode, const char *cmd) +{ + omap2_prm_set_mod_reg_bits(TI81XX_GLOBAL_RST_COLD, 0, + TI81XX_PRM_DEVICE_RSTCTRL); + while (1); +} diff --git a/arch/arm/mach-omap2/ti81xx.h b/arch/arm/mach-omap2/ti81xx.h new file mode 100644 index 000000000..fe9f7f388 --- /dev/null +++ b/arch/arm/mach-omap2/ti81xx.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * This file contains the address data for various TI81XX modules. + * + * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ + */ + +#ifndef __ASM_ARCH_TI81XX_H +#define __ASM_ARCH_TI81XX_H + +#define L4_SLOW_TI81XX_BASE 0x48000000 + +#define TI81XX_SCM_BASE 0x48140000 +#define TI81XX_CTRL_BASE TI81XX_SCM_BASE +#define TI81XX_PRCM_BASE 0x48180000 + +/* + * Adjust TAP register base such that omap3_check_revision accesses the correct + * TI81XX register for checking device ID (it adds 0x204 to tap base while + * TI81XX DEVICE ID register is at offset 0x600 from control base). + */ +#define TI81XX_TAP_BASE (TI81XX_CTRL_BASE + \ + TI81XX_CONTROL_DEVICE_ID - 0x204) + + +#define TI81XX_ARM_INTC_BASE 0x48200000 + +#endif /* __ASM_ARCH_TI81XX_H */ diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c new file mode 100644 index 000000000..5677c4a08 --- /dev/null +++ b/arch/arm/mach-omap2/timer.c @@ -0,0 +1,167 @@ +/* + * linux/arch/arm/mach-omap2/timer.c + * + * OMAP2 GP timer support. + * + * Copyright (C) 2009 Nokia Corporation + * + * Update to use new clocksource/clockevent layers + * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Original driver: + * Copyright (C) 2005 Nokia Corporation + * Author: Paul Mundt <paul.mundt@nokia.com> + * Juha Yrjölä <juha.yrjola@nokia.com> + * OMAP Dual-mode timer framework support by Timo Teras + * + * Some parts based off of TI's 24xx code: + * + * Copyright (C) 2004-2009 Texas Instruments, Inc. + * + * Roughly modelled after the OMAP1 MPU timer code. + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/clk.h> +#include <linux/clocksource.h> + +#include "soc.h" +#include "common.h" +#include "control.h" +#include "omap-secure.h" + +#define REALTIME_COUNTER_BASE 0x48243200 +#define INCREMENTER_NUMERATOR_OFFSET 0x10 +#define INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x14 +#define NUMERATOR_DENUMERATOR_MASK 0xfffff000 + +static unsigned long arch_timer_freq; + +void set_cntfreq(void) +{ + omap_smc1(OMAP5_DRA7_MON_SET_CNTFRQ_INDEX, arch_timer_freq); +} + +/* + * The realtime counter also called master counter, is a free-running + * counter, which is related to real time. It produces the count used + * by the CPU local timer peripherals in the MPU cluster. The timer counts + * at a rate of 6.144 MHz. Because the device operates on different clocks + * in different power modes, the master counter shifts operation between + * clocks, adjusting the increment per clock in hardware accordingly to + * maintain a constant count rate. + */ +static void __init realtime_counter_init(void) +{ + void __iomem *base; + static struct clk *sys_clk; + unsigned long rate; + unsigned int reg; + unsigned long long num, den; + + base = ioremap(REALTIME_COUNTER_BASE, SZ_32); + if (!base) { + pr_err("%s: ioremap failed\n", __func__); + return; + } + sys_clk = clk_get(NULL, "sys_clkin"); + if (IS_ERR(sys_clk)) { + pr_err("%s: failed to get system clock handle\n", __func__); + iounmap(base); + return; + } + + rate = clk_get_rate(sys_clk); + clk_put(sys_clk); + + if (soc_is_dra7xx()) { + /* + * Errata i856 says the 32.768KHz crystal does not start at + * power on, so the CPU falls back to an emulated 32KHz clock + * based on sysclk / 610 instead. This causes the master counter + * frequency to not be 6.144MHz but at sysclk / 610 * 375 / 2 + * (OR sysclk * 75 / 244) + * + * This affects at least the DRA7/AM572x 1.0, 1.1 revisions. + * Of course any board built without a populated 32.768KHz + * crystal would also need this fix even if the CPU is fixed + * later. + * + * Either case can be detected by using the two speedselect bits + * If they are not 0, then the 32.768KHz clock driving the + * coarse counter that corrects the fine counter every time it + * ticks is actually rate/610 rather than 32.768KHz and we + * should compensate to avoid the 570ppm (at 20MHz, much worse + * at other rates) too fast system time. + */ + reg = omap_ctrl_readl(DRA7_CTRL_CORE_BOOTSTRAP); + if (reg & DRA7_SPEEDSELECT_MASK) { + num = 75; + den = 244; + goto sysclk1_based; + } + } + + /* Numerator/denumerator values refer TRM Realtime Counter section */ + switch (rate) { + case 12000000: + num = 64; + den = 125; + break; + case 13000000: + num = 768; + den = 1625; + break; + case 19200000: + num = 8; + den = 25; + break; + case 20000000: + num = 192; + den = 625; + break; + case 26000000: + num = 384; + den = 1625; + break; + case 27000000: + num = 256; + den = 1125; + break; + case 38400000: + default: + /* Program it for 38.4 MHz */ + num = 4; + den = 25; + break; + } + +sysclk1_based: + /* Program numerator and denumerator registers */ + reg = readl_relaxed(base + INCREMENTER_NUMERATOR_OFFSET) & + NUMERATOR_DENUMERATOR_MASK; + reg |= num; + writel_relaxed(reg, base + INCREMENTER_NUMERATOR_OFFSET); + + reg = readl_relaxed(base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET) & + NUMERATOR_DENUMERATOR_MASK; + reg |= den; + writel_relaxed(reg, base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET); + + arch_timer_freq = DIV_ROUND_UP_ULL(rate * num, den); + set_cntfreq(); + + iounmap(base); +} + +void __init omap5_realtime_timer_init(void) +{ + omap_clk_init(); + realtime_counter_init(); + + timer_probe(); +} diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c new file mode 100644 index 000000000..a0c4c42e5 --- /dev/null +++ b/arch/arm/mach-omap2/usb-tusb6010.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/arch/arm/mach-omap2/usb-tusb6010.c + * + * Copyright (C) 2006 Nokia Corporation + */ + +#include <linux/err.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/export.h> +#include <linux/platform_data/usb-omap.h> + +#include <linux/usb/musb.h> + +#include "gpmc.h" + +static u8 async_cs, sync_cs; +static unsigned refclk_psec; + +static struct gpmc_settings tusb_async = { + .wait_on_read = true, + .wait_on_write = true, + .device_width = GPMC_DEVWIDTH_16BIT, + .mux_add_data = GPMC_MUX_AD, +}; + +static struct gpmc_settings tusb_sync = { + .burst_read = true, + .burst_write = true, + .sync_read = true, + .sync_write = true, + .wait_on_read = true, + .wait_on_write = true, + .burst_len = GPMC_BURST_16, + .device_width = GPMC_DEVWIDTH_16BIT, + .mux_add_data = GPMC_MUX_AD, +}; + +/* NOTE: timings are from tusb 6010 datasheet Rev 1.8, 12-Sept 2006 */ + +static int tusb_set_async_mode(unsigned sysclk_ps) +{ + struct gpmc_device_timings dev_t; + struct gpmc_timings t; + unsigned t_acsnh_advnh = sysclk_ps + 3000; + + memset(&dev_t, 0, sizeof(dev_t)); + + dev_t.t_ceasu = 8 * 1000; + dev_t.t_avdasu = t_acsnh_advnh - 7000; + dev_t.t_ce_avd = 1000; + dev_t.t_avdp_r = t_acsnh_advnh; + dev_t.t_oeasu = t_acsnh_advnh + 1000; + dev_t.t_oe = 300; + dev_t.t_cez_r = 7000; + dev_t.t_cez_w = dev_t.t_cez_r; + dev_t.t_avdp_w = t_acsnh_advnh; + dev_t.t_weasu = t_acsnh_advnh + 1000; + dev_t.t_wpl = 300; + dev_t.cyc_aavdh_we = 1; + + gpmc_calc_timings(&t, &tusb_async, &dev_t); + + return gpmc_cs_set_timings(async_cs, &t, &tusb_async); +} + +static int tusb_set_sync_mode(unsigned sysclk_ps) +{ + struct gpmc_device_timings dev_t; + struct gpmc_timings t; + unsigned t_scsnh_advnh = sysclk_ps + 3000; + + memset(&dev_t, 0, sizeof(dev_t)); + + dev_t.clk = 11100; + dev_t.t_bacc = 1000; + dev_t.t_ces = 1000; + dev_t.t_ceasu = 8 * 1000; + dev_t.t_avdasu = t_scsnh_advnh - 7000; + dev_t.t_ce_avd = 1000; + dev_t.t_avdp_r = t_scsnh_advnh; + dev_t.cyc_aavdh_oe = 3; + dev_t.cyc_oe = 5; + dev_t.t_ce_rdyz = 7000; + dev_t.t_avdp_w = t_scsnh_advnh; + dev_t.cyc_aavdh_we = 3; + dev_t.cyc_wpl = 6; + + gpmc_calc_timings(&t, &tusb_sync, &dev_t); + + return gpmc_cs_set_timings(sync_cs, &t, &tusb_sync); +} + +/* tusb driver calls this when it changes the chip's clocking */ +int tusb6010_platform_retime(unsigned is_refclk) +{ + static const char error[] = + KERN_ERR "tusb6010 %s retime error %d\n"; + + unsigned sysclk_ps; + int status; + + if (!refclk_psec) + return -ENODEV; + + sysclk_ps = is_refclk ? refclk_psec : TUSB6010_OSCCLK_60; + + status = tusb_set_async_mode(sysclk_ps); + if (status < 0) { + printk(error, "async", status); + goto done; + } + status = tusb_set_sync_mode(sysclk_ps); + if (status < 0) + printk(error, "sync", status); +done: + return status; +} +EXPORT_SYMBOL_GPL(tusb6010_platform_retime); + +static struct resource tusb_resources[] = { + /* Order is significant! The start/end fields + * are updated during setup.. + */ + { /* Asynchronous access */ + .flags = IORESOURCE_MEM, + }, + { /* Synchronous access */ + .flags = IORESOURCE_MEM, + }, + { /* IRQ */ + .name = "mc", + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 tusb_dmamask = ~(u32)0; + +static struct platform_device tusb_device = { + .name = "musb-tusb", + .id = -1, + .dev = { + .dma_mask = &tusb_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(tusb_resources), + .resource = tusb_resources, +}; + + +/* this may be called only from board-*.c setup code */ +int __init +tusb6010_setup_interface(struct musb_hdrc_platform_data *data, + unsigned ps_refclk, unsigned waitpin, + unsigned async, unsigned sync, + unsigned irq, unsigned dmachan) +{ + int status; + static char error[] __initdata = + KERN_ERR "tusb6010 init error %d, %d\n"; + + /* ASYNC region, primarily for PIO */ + status = gpmc_cs_request(async, SZ_16M, (unsigned long *) + &tusb_resources[0].start); + if (status < 0) { + printk(error, 1, status); + return status; + } + tusb_resources[0].end = tusb_resources[0].start + 0x9ff; + tusb_async.wait_pin = waitpin; + async_cs = async; + + status = gpmc_cs_program_settings(async_cs, &tusb_async); + if (status < 0) + return status; + + /* SYNC region, primarily for DMA */ + status = gpmc_cs_request(sync, SZ_16M, (unsigned long *) + &tusb_resources[1].start); + if (status < 0) { + printk(error, 2, status); + return status; + } + tusb_resources[1].end = tusb_resources[1].start + 0x9ff; + tusb_sync.wait_pin = waitpin; + sync_cs = sync; + + status = gpmc_cs_program_settings(sync_cs, &tusb_sync); + if (status < 0) + return status; + + /* IRQ */ + status = gpio_request_one(irq, GPIOF_IN, "TUSB6010 irq"); + if (status < 0) { + printk(error, 3, status); + return status; + } + tusb_resources[2].start = gpio_to_irq(irq); + + /* set up memory timings ... can speed them up later */ + if (!ps_refclk) { + printk(error, 4, status); + return -ENODEV; + } + refclk_psec = ps_refclk; + status = tusb6010_platform_retime(1); + if (status < 0) { + printk(error, 5, status); + return status; + } + + /* finish device setup ... */ + if (!data) { + printk(error, 6, status); + return -ENODEV; + } + tusb_device.dev.platform_data = data; + + /* so far so good ... register the device */ + status = platform_device_register(&tusb_device); + if (status < 0) { + printk(error, 7, status); + return status; + } + return 0; +} diff --git a/arch/arm/mach-omap2/usb.h b/arch/arm/mach-omap2/usb.h new file mode 100644 index 000000000..740a499be --- /dev/null +++ b/arch/arm/mach-omap2/usb.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/platform_data/usb-omap.h> + +/* AM35x */ +/* USB 2.0 PHY Control */ +#define CONF2_PHY_GPIOMODE (1 << 23) +#define CONF2_OTGMODE (3 << 14) +#define CONF2_NO_OVERRIDE (0 << 14) +#define CONF2_FORCE_HOST (1 << 14) +#define CONF2_FORCE_DEVICE (2 << 14) +#define CONF2_FORCE_HOST_VBUS_LOW (3 << 14) +#define CONF2_SESENDEN (1 << 13) +#define CONF2_VBDTCTEN (1 << 12) +#define CONF2_REFFREQ_24MHZ (2 << 8) +#define CONF2_REFFREQ_26MHZ (7 << 8) +#define CONF2_REFFREQ_13MHZ (6 << 8) +#define CONF2_REFFREQ (0xf << 8) +#define CONF2_PHYCLKGD (1 << 7) +#define CONF2_VBUSSENSE (1 << 6) +#define CONF2_PHY_PLLON (1 << 5) +#define CONF2_RESET (1 << 4) +#define CONF2_PHYPWRDN (1 << 3) +#define CONF2_OTGPWRDN (1 << 2) +#define CONF2_DATPOL (1 << 1) + +/* TI81XX specific definitions */ +#define USBCTRL0 0x620 +#define USBSTAT0 0x624 + +/* TI816X PHY controls bits */ +#define TI816X_USBPHY0_NORMAL_MODE (1 << 0) +#define TI816X_USBPHY_REFCLK_OSC (1 << 8) + +/* TI814X PHY controls bits */ +#define USBPHY_CM_PWRDN (1 << 0) +#define USBPHY_OTG_PWRDN (1 << 1) +#define USBPHY_CHGDET_DIS (1 << 2) +#define USBPHY_CHGDET_RSTRT (1 << 3) +#define USBPHY_SRCONDM (1 << 4) +#define USBPHY_SINKONDP (1 << 5) +#define USBPHY_CHGISINK_EN (1 << 6) +#define USBPHY_CHGVSRC_EN (1 << 7) +#define USBPHY_DMPULLUP (1 << 8) +#define USBPHY_DPPULLUP (1 << 9) +#define USBPHY_CDET_EXTCTL (1 << 10) +#define USBPHY_GPIO_MODE (1 << 12) +#define USBPHY_DPOPBUFCTL (1 << 13) +#define USBPHY_DMOPBUFCTL (1 << 14) +#define USBPHY_DPINPUT (1 << 15) +#define USBPHY_DMINPUT (1 << 16) +#define USBPHY_DPGPIO_PD (1 << 17) +#define USBPHY_DMGPIO_PD (1 << 18) +#define USBPHY_OTGVDET_EN (1 << 19) +#define USBPHY_OTGSESSEND_EN (1 << 20) +#define USBPHY_DATA_POLARITY (1 << 23) + +struct usbhs_phy_data { + int port; /* 1 indexed port number */ + int reset_gpio; + int vcc_gpio; + bool vcc_polarity; /* 1 active high, 0 active low */ +}; + +extern void usb_musb_init(struct omap_musb_board_data *board_data); +extern void usbhs_init(struct usbhs_omap_platform_data *pdata); +extern int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys); + +extern void am35x_musb_reset(void); +extern void am35x_musb_phy_power(u8 on); +extern void am35x_musb_clear_irq(void); +extern void am35x_set_mode(u8 musb_mode); diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c new file mode 100644 index 000000000..ea02d4040 --- /dev/null +++ b/arch/arm/mach-omap2/vc.c @@ -0,0 +1,894 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP Voltage Controller (VC) interface + * + * Copyright (C) 2011 Texas Instruments, Inc. + */ +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/bug.h> +#include <linux/io.h> + +#include <asm/div64.h> + +#include "iomap.h" +#include "soc.h" +#include "voltage.h" +#include "vc.h" +#include "prm-regbits-34xx.h" +#include "prm-regbits-44xx.h" +#include "prm44xx.h" +#include "pm.h" +#include "scrm44xx.h" +#include "control.h" + +#define OMAP4430_VDD_IVA_I2C_DISABLE BIT(14) +#define OMAP4430_VDD_MPU_I2C_DISABLE BIT(13) +#define OMAP4430_VDD_CORE_I2C_DISABLE BIT(12) +#define OMAP4430_VDD_IVA_PRESENCE BIT(9) +#define OMAP4430_VDD_MPU_PRESENCE BIT(8) +#define OMAP4430_AUTO_CTRL_VDD_IVA(x) ((x) << 4) +#define OMAP4430_AUTO_CTRL_VDD_MPU(x) ((x) << 2) +#define OMAP4430_AUTO_CTRL_VDD_CORE(x) ((x) << 0) +#define OMAP4430_AUTO_CTRL_VDD_RET 2 + +#define OMAP4430_VDD_I2C_DISABLE_MASK \ + (OMAP4430_VDD_IVA_I2C_DISABLE | \ + OMAP4430_VDD_MPU_I2C_DISABLE | \ + OMAP4430_VDD_CORE_I2C_DISABLE) + +#define OMAP4_VDD_DEFAULT_VAL \ + (OMAP4430_VDD_I2C_DISABLE_MASK | \ + OMAP4430_VDD_IVA_PRESENCE | OMAP4430_VDD_MPU_PRESENCE | \ + OMAP4430_AUTO_CTRL_VDD_IVA(OMAP4430_AUTO_CTRL_VDD_RET) | \ + OMAP4430_AUTO_CTRL_VDD_MPU(OMAP4430_AUTO_CTRL_VDD_RET) | \ + OMAP4430_AUTO_CTRL_VDD_CORE(OMAP4430_AUTO_CTRL_VDD_RET)) + +#define OMAP4_VDD_RET_VAL \ + (OMAP4_VDD_DEFAULT_VAL & ~OMAP4430_VDD_I2C_DISABLE_MASK) + +/** + * struct omap_vc_channel_cfg - describe the cfg_channel bitfield + * @sa: bit for slave address + * @rav: bit for voltage configuration register + * @rac: bit for command configuration register + * @racen: enable bit for RAC + * @cmd: bit for command value set selection + * + * Channel configuration bits, common for OMAP3+ + * OMAP3 register: PRM_VC_CH_CONF + * OMAP4 register: PRM_VC_CFG_CHANNEL + * OMAP5 register: PRM_VC_SMPS_<voltdm>_CONFIG + */ +struct omap_vc_channel_cfg { + u8 sa; + u8 rav; + u8 rac; + u8 racen; + u8 cmd; +}; + +static struct omap_vc_channel_cfg vc_default_channel_cfg = { + .sa = BIT(0), + .rav = BIT(1), + .rac = BIT(2), + .racen = BIT(3), + .cmd = BIT(4), +}; + +/* + * On OMAP3+, all VC channels have the above default bitfield + * configuration, except the OMAP4 MPU channel. This appears + * to be a freak accident as every other VC channel has the + * default configuration, thus creating a mutant channel config. + */ +static struct omap_vc_channel_cfg vc_mutant_channel_cfg = { + .sa = BIT(0), + .rav = BIT(2), + .rac = BIT(3), + .racen = BIT(4), + .cmd = BIT(1), +}; + +static struct omap_vc_channel_cfg *vc_cfg_bits; + +/* Default I2C trace length on pcb, 6.3cm. Used for capacitance calculations. */ +static u32 sr_i2c_pcb_length = 63; +#define CFG_CHANNEL_MASK 0x1f + +/** + * omap_vc_config_channel - configure VC channel to PMIC mappings + * @voltdm: pointer to voltagdomain defining the desired VC channel + * + * Configures the VC channel to PMIC mappings for the following + * PMIC settings + * - i2c slave address (SA) + * - voltage configuration address (RAV) + * - command configuration address (RAC) and enable bit (RACEN) + * - command values for ON, ONLP, RET and OFF (CMD) + * + * This function currently only allows flexible configuration of the + * non-default channel. Starting with OMAP4, there are more than 2 + * channels, with one defined as the default (on OMAP4, it's MPU.) + * Only the non-default channel can be configured. + */ +static int omap_vc_config_channel(struct voltagedomain *voltdm) +{ + struct omap_vc_channel *vc = voltdm->vc; + + /* + * For default channel, the only configurable bit is RACEN. + * All others must stay at zero (see function comment above.) + */ + if (vc->flags & OMAP_VC_CHANNEL_DEFAULT) + vc->cfg_channel &= vc_cfg_bits->racen; + + voltdm->rmw(CFG_CHANNEL_MASK << vc->cfg_channel_sa_shift, + vc->cfg_channel << vc->cfg_channel_sa_shift, + vc->cfg_channel_reg); + + return 0; +} + +/* Voltage scale and accessory APIs */ +int omap_vc_pre_scale(struct voltagedomain *voltdm, + unsigned long target_volt, + u8 *target_vsel, u8 *current_vsel) +{ + struct omap_vc_channel *vc = voltdm->vc; + u32 vc_cmdval; + + /* Check if sufficient pmic info is available for this vdd */ + if (!voltdm->pmic) { + pr_err("%s: Insufficient pmic info to scale the vdd_%s\n", + __func__, voltdm->name); + return -EINVAL; + } + + if (!voltdm->pmic->uv_to_vsel) { + pr_err("%s: PMIC function to convert voltage in uV to vsel not registered. Hence unable to scale voltage for vdd_%s\n", + __func__, voltdm->name); + return -ENODATA; + } + + if (!voltdm->read || !voltdm->write) { + pr_err("%s: No read/write API for accessing vdd_%s regs\n", + __func__, voltdm->name); + return -EINVAL; + } + + *target_vsel = voltdm->pmic->uv_to_vsel(target_volt); + *current_vsel = voltdm->pmic->uv_to_vsel(voltdm->nominal_volt); + + /* Setting the ON voltage to the new target voltage */ + vc_cmdval = voltdm->read(vc->cmdval_reg); + vc_cmdval &= ~vc->common->cmd_on_mask; + vc_cmdval |= (*target_vsel << vc->common->cmd_on_shift); + voltdm->write(vc_cmdval, vc->cmdval_reg); + + voltdm->vc_param->on = target_volt; + + omap_vp_update_errorgain(voltdm, target_volt); + + return 0; +} + +void omap_vc_post_scale(struct voltagedomain *voltdm, + unsigned long target_volt, + u8 target_vsel, u8 current_vsel) +{ + u32 smps_steps = 0, smps_delay = 0; + + smps_steps = abs(target_vsel - current_vsel); + /* SMPS slew rate / step size. 2us added as buffer. */ + smps_delay = ((smps_steps * voltdm->pmic->step_size) / + voltdm->pmic->slew_rate) + 2; + udelay(smps_delay); +} + +/* vc_bypass_scale - VC bypass method of voltage scaling */ +int omap_vc_bypass_scale(struct voltagedomain *voltdm, + unsigned long target_volt) +{ + struct omap_vc_channel *vc = voltdm->vc; + u32 loop_cnt = 0, retries_cnt = 0; + u32 vc_valid, vc_bypass_val_reg, vc_bypass_value; + u8 target_vsel, current_vsel; + int ret; + + ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, ¤t_vsel); + if (ret) + return ret; + + vc_valid = vc->common->valid; + vc_bypass_val_reg = vc->common->bypass_val_reg; + vc_bypass_value = (target_vsel << vc->common->data_shift) | + (vc->volt_reg_addr << vc->common->regaddr_shift) | + (vc->i2c_slave_addr << vc->common->slaveaddr_shift); + + voltdm->write(vc_bypass_value, vc_bypass_val_reg); + voltdm->write(vc_bypass_value | vc_valid, vc_bypass_val_reg); + + vc_bypass_value = voltdm->read(vc_bypass_val_reg); + /* + * Loop till the bypass command is acknowledged from the SMPS. + * NOTE: This is legacy code. The loop count and retry count needs + * to be revisited. + */ + while (!(vc_bypass_value & vc_valid)) { + loop_cnt++; + + if (retries_cnt > 10) { + pr_warn("%s: Retry count exceeded\n", __func__); + return -ETIMEDOUT; + } + + if (loop_cnt > 50) { + retries_cnt++; + loop_cnt = 0; + udelay(10); + } + vc_bypass_value = voltdm->read(vc_bypass_val_reg); + } + + omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel); + return 0; +} + +/* Convert microsecond value to number of 32kHz clock cycles */ +static inline u32 omap_usec_to_32k(u32 usec) +{ + return DIV_ROUND_UP_ULL(32768ULL * (u64)usec, 1000000ULL); +} + +struct omap3_vc_timings { + u32 voltsetup1; + u32 voltsetup2; +}; + +struct omap3_vc { + struct voltagedomain *vd; + u32 voltctrl; + u32 voltsetup1; + u32 voltsetup2; + struct omap3_vc_timings timings[2]; +}; +static struct omap3_vc vc; + +void omap3_vc_set_pmic_signaling(int core_next_state) +{ + struct voltagedomain *vd = vc.vd; + struct omap3_vc_timings *c = vc.timings; + u32 voltctrl, voltsetup1, voltsetup2; + + voltctrl = vc.voltctrl; + voltsetup1 = vc.voltsetup1; + voltsetup2 = vc.voltsetup2; + + switch (core_next_state) { + case PWRDM_POWER_OFF: + voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_RET | + OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP); + voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_OFF; + if (voltctrl & OMAP3430_PRM_VOLTCTRL_SEL_OFF) + voltsetup2 = c->voltsetup2; + else + voltsetup1 = c->voltsetup1; + break; + case PWRDM_POWER_RET: + default: + c++; + voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_OFF | + OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP); + voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_RET; + voltsetup1 = c->voltsetup1; + break; + } + + if (voltctrl != vc.voltctrl) { + vd->write(voltctrl, OMAP3_PRM_VOLTCTRL_OFFSET); + vc.voltctrl = voltctrl; + } + if (voltsetup1 != vc.voltsetup1) { + vd->write(c->voltsetup1, + OMAP3_PRM_VOLTSETUP1_OFFSET); + vc.voltsetup1 = voltsetup1; + } + if (voltsetup2 != vc.voltsetup2) { + vd->write(c->voltsetup2, + OMAP3_PRM_VOLTSETUP2_OFFSET); + vc.voltsetup2 = voltsetup2; + } +} + +void omap4_vc_set_pmic_signaling(int core_next_state) +{ + struct voltagedomain *vd = vc.vd; + u32 val; + + if (!vd) + return; + + switch (core_next_state) { + case PWRDM_POWER_RET: + val = OMAP4_VDD_RET_VAL; + break; + default: + val = OMAP4_VDD_DEFAULT_VAL; + break; + } + + vd->write(val, OMAP4_PRM_VOLTCTRL_OFFSET); +} + +/* + * Configure signal polarity for sys_clkreq and sys_off_mode pins + * as the default values are wrong and can cause the system to hang + * if any twl4030 scripts are loaded. + */ +static void __init omap3_vc_init_pmic_signaling(struct voltagedomain *voltdm) +{ + u32 val; + + if (vc.vd) + return; + + vc.vd = voltdm; + + val = voltdm->read(OMAP3_PRM_POLCTRL_OFFSET); + if (!(val & OMAP3430_PRM_POLCTRL_CLKREQ_POL) || + (val & OMAP3430_PRM_POLCTRL_OFFMODE_POL)) { + val |= OMAP3430_PRM_POLCTRL_CLKREQ_POL; + val &= ~OMAP3430_PRM_POLCTRL_OFFMODE_POL; + pr_debug("PM: fixing sys_clkreq and sys_off_mode polarity to 0x%x\n", + val); + voltdm->write(val, OMAP3_PRM_POLCTRL_OFFSET); + } + + /* + * By default let's use I2C4 signaling for retention idle + * and sys_off_mode pin signaling for off idle. This way we + * have sys_clk_req pin go down for retention and both + * sys_clk_req and sys_off_mode pins will go down for off + * idle. And we can also scale voltages to zero for off-idle. + * Note that no actual voltage scaling during off-idle will + * happen unless the board specific twl4030 PMIC scripts are + * loaded. See also omap_vc_i2c_init for comments regarding + * erratum i531. + */ + val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET); + if (!(val & OMAP3430_PRM_VOLTCTRL_SEL_OFF)) { + val |= OMAP3430_PRM_VOLTCTRL_SEL_OFF; + pr_debug("PM: setting voltctrl sys_off_mode signaling to 0x%x\n", + val); + voltdm->write(val, OMAP3_PRM_VOLTCTRL_OFFSET); + } + vc.voltctrl = val; + + omap3_vc_set_pmic_signaling(PWRDM_POWER_ON); +} + +static void omap3_init_voltsetup1(struct voltagedomain *voltdm, + struct omap3_vc_timings *c, u32 idle) +{ + unsigned long val; + + val = (voltdm->vc_param->on - idle) / voltdm->pmic->slew_rate; + val *= voltdm->sys_clk.rate / 8 / 1000000 + 1; + val <<= __ffs(voltdm->vfsm->voltsetup_mask); + c->voltsetup1 &= ~voltdm->vfsm->voltsetup_mask; + c->voltsetup1 |= val; +} + +/** + * omap3_set_i2c_timings - sets i2c sleep timings for a channel + * @voltdm: channel to configure + * @off_mode: select whether retention or off mode values used + * + * Calculates and sets up voltage controller to use I2C based + * voltage scaling for sleep modes. This can be used for either off mode + * or retention. Off mode has additionally an option to use sys_off_mode + * pad, which uses a global signal to program the whole power IC to + * off-mode. + * + * Note that pmic is not controlling the voltage scaling during + * retention signaled over I2C4, so we can keep voltsetup2 as 0. + * And the oscillator is not shut off over I2C4, so no need to + * set clksetup. + */ +static void omap3_set_i2c_timings(struct voltagedomain *voltdm) +{ + struct omap3_vc_timings *c = vc.timings; + + /* Configure PRWDM_POWER_OFF over I2C4 */ + omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->off); + c++; + /* Configure PRWDM_POWER_RET over I2C4 */ + omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->ret); +} + +/** + * omap3_set_off_timings - sets off-mode timings for a channel + * @voltdm: channel to configure + * + * Calculates and sets up off-mode timings for a channel. Off-mode + * can use either I2C based voltage scaling, or alternatively + * sys_off_mode pad can be used to send a global command to power IC.n, + * sys_off_mode has the additional benefit that voltages can be + * scaled to zero volt level with TWL4030 / TWL5030, I2C can only + * scale to 600mV. + * + * Note that omap is not controlling the voltage scaling during + * off idle signaled by sys_off_mode, so we can keep voltsetup1 + * as 0. + */ +static void omap3_set_off_timings(struct voltagedomain *voltdm) +{ + struct omap3_vc_timings *c = vc.timings; + u32 tstart, tshut, clksetup, voltoffset; + + if (c->voltsetup2) + return; + + omap_pm_get_oscillator(&tstart, &tshut); + if (tstart == ULONG_MAX) { + pr_debug("PM: oscillator start-up time not initialized, using 10ms\n"); + clksetup = omap_usec_to_32k(10000); + } else { + clksetup = omap_usec_to_32k(tstart); + } + + /* + * For twl4030 errata 27, we need to allow minimum ~488.32 us wait to + * switch from HFCLKIN to internal oscillator. That means timings + * have voltoffset fixed to 0xa in rounded up 32 KiHz cycles. And + * that means we can calculate the value based on the oscillator + * start-up time since voltoffset2 = clksetup - voltoffset. + */ + voltoffset = omap_usec_to_32k(488); + c->voltsetup2 = clksetup - voltoffset; + voltdm->write(clksetup, OMAP3_PRM_CLKSETUP_OFFSET); + voltdm->write(voltoffset, OMAP3_PRM_VOLTOFFSET_OFFSET); +} + +static void __init omap3_vc_init_channel(struct voltagedomain *voltdm) +{ + omap3_vc_init_pmic_signaling(voltdm); + omap3_set_off_timings(voltdm); + omap3_set_i2c_timings(voltdm); +} + +/** + * omap4_calc_volt_ramp - calculates voltage ramping delays on omap4 + * @voltdm: channel to calculate values for + * @voltage_diff: voltage difference in microvolts + * + * Calculates voltage ramp prescaler + counter values for a voltage + * difference on omap4. Returns a field value suitable for writing to + * VOLTSETUP register for a channel in following format: + * bits[8:9] prescaler ... bits[0:5] counter. See OMAP4 TRM for reference. + */ +static u32 omap4_calc_volt_ramp(struct voltagedomain *voltdm, u32 voltage_diff) +{ + u32 prescaler; + u32 cycles; + u32 time; + + time = voltage_diff / voltdm->pmic->slew_rate; + + cycles = voltdm->sys_clk.rate / 1000 * time / 1000; + + cycles /= 64; + prescaler = 0; + + /* shift to next prescaler until no overflow */ + + /* scale for div 256 = 64 * 4 */ + if (cycles > 63) { + cycles /= 4; + prescaler++; + } + + /* scale for div 512 = 256 * 2 */ + if (cycles > 63) { + cycles /= 2; + prescaler++; + } + + /* scale for div 2048 = 512 * 4 */ + if (cycles > 63) { + cycles /= 4; + prescaler++; + } + + /* check for overflow => invalid ramp time */ + if (cycles > 63) { + pr_warn("%s: invalid setuptime for vdd_%s\n", __func__, + voltdm->name); + return 0; + } + + cycles++; + + return (prescaler << OMAP4430_RAMP_UP_PRESCAL_SHIFT) | + (cycles << OMAP4430_RAMP_UP_COUNT_SHIFT); +} + +/** + * omap4_usec_to_val_scrm - convert microsecond value to SCRM module bitfield + * @usec: microseconds + * @shift: number of bits to shift left + * @mask: bitfield mask + * + * Converts microsecond value to OMAP4 SCRM bitfield. Bitfield is + * shifted to requested position, and checked agains the mask value. + * If larger, forced to the max value of the field (i.e. the mask itself.) + * Returns the SCRM bitfield value. + */ +static u32 omap4_usec_to_val_scrm(u32 usec, int shift, u32 mask) +{ + u32 val; + + val = omap_usec_to_32k(usec) << shift; + + /* Check for overflow, if yes, force to max value */ + if (val > mask) + val = mask; + + return val; +} + +/** + * omap4_set_timings - set voltage ramp timings for a channel + * @voltdm: channel to configure + * @off_mode: whether off-mode values are used + * + * Calculates and sets the voltage ramp up / down values for a channel. + */ +static void omap4_set_timings(struct voltagedomain *voltdm, bool off_mode) +{ + u32 val; + u32 ramp; + int offset; + u32 tstart, tshut; + + if (off_mode) { + ramp = omap4_calc_volt_ramp(voltdm, + voltdm->vc_param->on - voltdm->vc_param->off); + offset = voltdm->vfsm->voltsetup_off_reg; + } else { + ramp = omap4_calc_volt_ramp(voltdm, + voltdm->vc_param->on - voltdm->vc_param->ret); + offset = voltdm->vfsm->voltsetup_reg; + } + + if (!ramp) + return; + + val = voltdm->read(offset); + + val |= ramp << OMAP4430_RAMP_DOWN_COUNT_SHIFT; + + val |= ramp << OMAP4430_RAMP_UP_COUNT_SHIFT; + + voltdm->write(val, offset); + + omap_pm_get_oscillator(&tstart, &tshut); + + val = omap4_usec_to_val_scrm(tstart, OMAP4_SETUPTIME_SHIFT, + OMAP4_SETUPTIME_MASK); + val |= omap4_usec_to_val_scrm(tshut, OMAP4_DOWNTIME_SHIFT, + OMAP4_DOWNTIME_MASK); + + writel_relaxed(val, OMAP4_SCRM_CLKSETUPTIME); +} + +static void __init omap4_vc_init_pmic_signaling(struct voltagedomain *voltdm) +{ + if (vc.vd) + return; + + vc.vd = voltdm; + voltdm->write(OMAP4_VDD_DEFAULT_VAL, OMAP4_PRM_VOLTCTRL_OFFSET); +} + +/* OMAP4 specific voltage init functions */ +static void __init omap4_vc_init_channel(struct voltagedomain *voltdm) +{ + omap4_vc_init_pmic_signaling(voltdm); + omap4_set_timings(voltdm, true); + omap4_set_timings(voltdm, false); +} + +struct i2c_init_data { + u8 loadbits; + u8 load; + u8 hsscll_38_4; + u8 hsscll_26; + u8 hsscll_19_2; + u8 hsscll_16_8; + u8 hsscll_12; +}; + +static const struct i2c_init_data omap4_i2c_timing_data[] __initconst = { + { + .load = 50, + .loadbits = 0x3, + .hsscll_38_4 = 13, + .hsscll_26 = 11, + .hsscll_19_2 = 9, + .hsscll_16_8 = 9, + .hsscll_12 = 8, + }, + { + .load = 25, + .loadbits = 0x2, + .hsscll_38_4 = 13, + .hsscll_26 = 11, + .hsscll_19_2 = 9, + .hsscll_16_8 = 9, + .hsscll_12 = 8, + }, + { + .load = 12, + .loadbits = 0x1, + .hsscll_38_4 = 11, + .hsscll_26 = 10, + .hsscll_19_2 = 9, + .hsscll_16_8 = 9, + .hsscll_12 = 8, + }, + { + .load = 0, + .loadbits = 0x0, + .hsscll_38_4 = 12, + .hsscll_26 = 10, + .hsscll_19_2 = 9, + .hsscll_16_8 = 8, + .hsscll_12 = 8, + }, +}; + +/** + * omap4_vc_i2c_timing_init - sets up board I2C timing parameters + * @voltdm: voltagedomain pointer to get data from + * + * Use PMIC + board supplied settings for calculating the total I2C + * channel capacitance and set the timing parameters based on this. + * Pre-calculated values are provided in data tables, as it is not + * too straightforward to calculate these runtime. + */ +static void __init omap4_vc_i2c_timing_init(struct voltagedomain *voltdm) +{ + u32 capacitance; + u32 val; + u16 hsscll; + const struct i2c_init_data *i2c_data; + + if (!voltdm->pmic->i2c_high_speed) { + pr_info("%s: using bootloader low-speed timings\n", __func__); + return; + } + + /* PCB trace capacitance, 0.125pF / mm => mm / 8 */ + capacitance = DIV_ROUND_UP(sr_i2c_pcb_length, 8); + + /* OMAP pad capacitance */ + capacitance += 4; + + /* PMIC pad capacitance */ + capacitance += voltdm->pmic->i2c_pad_load; + + /* Search for capacitance match in the table */ + i2c_data = omap4_i2c_timing_data; + + while (i2c_data->load > capacitance) + i2c_data++; + + /* Select proper values based on sysclk frequency */ + switch (voltdm->sys_clk.rate) { + case 38400000: + hsscll = i2c_data->hsscll_38_4; + break; + case 26000000: + hsscll = i2c_data->hsscll_26; + break; + case 19200000: + hsscll = i2c_data->hsscll_19_2; + break; + case 16800000: + hsscll = i2c_data->hsscll_16_8; + break; + case 12000000: + hsscll = i2c_data->hsscll_12; + break; + default: + pr_warn("%s: unsupported sysclk rate: %d!\n", __func__, + voltdm->sys_clk.rate); + return; + } + + /* Loadbits define pull setup for the I2C channels */ + val = i2c_data->loadbits << 25 | i2c_data->loadbits << 29; + + /* Write to SYSCTRL_PADCONF_WKUP_CTRL_I2C_2 to setup I2C pull */ + writel_relaxed(val, OMAP2_L4_IO_ADDRESS(OMAP4_CTRL_MODULE_PAD_WKUP + + OMAP4_CTRL_MODULE_PAD_WKUP_CONTROL_I2C_2)); + + /* HSSCLH can always be zero */ + val = hsscll << OMAP4430_HSSCLL_SHIFT; + val |= (0x28 << OMAP4430_SCLL_SHIFT | 0x2c << OMAP4430_SCLH_SHIFT); + + /* Write setup times to I2C config register */ + voltdm->write(val, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET); +} + + + +/** + * omap_vc_i2c_init - initialize I2C interface to PMIC + * @voltdm: voltage domain containing VC data + * + * Use PMIC supplied settings for I2C high-speed mode and + * master code (if set) and program the VC I2C configuration + * register. + * + * The VC I2C configuration is common to all VC channels, + * so this function only configures I2C for the first VC + * channel registers. All other VC channels will use the + * same configuration. + */ +static void __init omap_vc_i2c_init(struct voltagedomain *voltdm) +{ + struct omap_vc_channel *vc = voltdm->vc; + static bool initialized; + static bool i2c_high_speed; + u8 mcode; + + if (initialized) { + if (voltdm->pmic->i2c_high_speed != i2c_high_speed) + pr_warn("%s: I2C config for vdd_%s does not match other channels (%u).\n", + __func__, voltdm->name, i2c_high_speed); + return; + } + + /* + * Note that for omap3 OMAP3430_SREN_MASK clears SREN to work around + * erratum i531 "Extra Power Consumed When Repeated Start Operation + * Mode Is Enabled on I2C Interface Dedicated for Smart Reflex (I2C4)". + * Otherwise I2C4 eventually leads into about 23mW extra power being + * consumed even during off idle using VMODE. + */ + i2c_high_speed = voltdm->pmic->i2c_high_speed; + if (i2c_high_speed) + voltdm->rmw(vc->common->i2c_cfg_clear_mask, + vc->common->i2c_cfg_hsen_mask, + vc->common->i2c_cfg_reg); + + mcode = voltdm->pmic->i2c_mcode; + if (mcode) + voltdm->rmw(vc->common->i2c_mcode_mask, + mcode << __ffs(vc->common->i2c_mcode_mask), + vc->common->i2c_cfg_reg); + + if (cpu_is_omap44xx()) + omap4_vc_i2c_timing_init(voltdm); + + initialized = true; +} + +/** + * omap_vc_calc_vsel - calculate vsel value for a channel + * @voltdm: channel to calculate value for + * @uvolt: microvolt value to convert to vsel + * + * Converts a microvolt value to vsel value for the used PMIC. + * This checks whether the microvolt value is out of bounds, and + * adjusts the value accordingly. If unsupported value detected, + * warning is thrown. + */ +static u8 omap_vc_calc_vsel(struct voltagedomain *voltdm, u32 uvolt) +{ + if (voltdm->pmic->vddmin > uvolt) + uvolt = voltdm->pmic->vddmin; + if (voltdm->pmic->vddmax < uvolt) { + WARN(1, "%s: voltage not supported by pmic: %u vs max %u\n", + __func__, uvolt, voltdm->pmic->vddmax); + /* Lets try maximum value anyway */ + uvolt = voltdm->pmic->vddmax; + } + + return voltdm->pmic->uv_to_vsel(uvolt); +} + +#ifdef CONFIG_PM +/** + * omap_pm_setup_sr_i2c_pcb_length - set length of SR I2C traces on PCB + * @mm: length of the PCB trace in millimetres + * + * Sets the PCB trace length for the I2C channel. By default uses 63mm. + * This is needed for properly calculating the capacitance value for + * the PCB trace, and for setting the SR I2C channel timing parameters. + */ +void __init omap_pm_setup_sr_i2c_pcb_length(u32 mm) +{ + sr_i2c_pcb_length = mm; +} +#endif + +void __init omap_vc_init_channel(struct voltagedomain *voltdm) +{ + struct omap_vc_channel *vc = voltdm->vc; + u8 on_vsel, onlp_vsel, ret_vsel, off_vsel; + u32 val; + + if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { + pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); + return; + } + + if (!voltdm->read || !voltdm->write) { + pr_err("%s: No read/write API for accessing vdd_%s regs\n", + __func__, voltdm->name); + return; + } + + vc->cfg_channel = 0; + if (vc->flags & OMAP_VC_CHANNEL_CFG_MUTANT) + vc_cfg_bits = &vc_mutant_channel_cfg; + else + vc_cfg_bits = &vc_default_channel_cfg; + + /* get PMIC/board specific settings */ + vc->i2c_slave_addr = voltdm->pmic->i2c_slave_addr; + vc->volt_reg_addr = voltdm->pmic->volt_reg_addr; + vc->cmd_reg_addr = voltdm->pmic->cmd_reg_addr; + + /* Configure the i2c slave address for this VC */ + voltdm->rmw(vc->smps_sa_mask, + vc->i2c_slave_addr << __ffs(vc->smps_sa_mask), + vc->smps_sa_reg); + vc->cfg_channel |= vc_cfg_bits->sa; + + /* + * Configure the PMIC register addresses. + */ + voltdm->rmw(vc->smps_volra_mask, + vc->volt_reg_addr << __ffs(vc->smps_volra_mask), + vc->smps_volra_reg); + vc->cfg_channel |= vc_cfg_bits->rav; + + if (vc->cmd_reg_addr) { + voltdm->rmw(vc->smps_cmdra_mask, + vc->cmd_reg_addr << __ffs(vc->smps_cmdra_mask), + vc->smps_cmdra_reg); + vc->cfg_channel |= vc_cfg_bits->rac; + } + + if (vc->cmd_reg_addr == vc->volt_reg_addr) + vc->cfg_channel |= vc_cfg_bits->racen; + + /* Set up the on, inactive, retention and off voltage */ + on_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->on); + onlp_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->onlp); + ret_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->ret); + off_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->off); + + val = ((on_vsel << vc->common->cmd_on_shift) | + (onlp_vsel << vc->common->cmd_onlp_shift) | + (ret_vsel << vc->common->cmd_ret_shift) | + (off_vsel << vc->common->cmd_off_shift)); + voltdm->write(val, vc->cmdval_reg); + vc->cfg_channel |= vc_cfg_bits->cmd; + + /* Channel configuration */ + omap_vc_config_channel(voltdm); + + omap_vc_i2c_init(voltdm); + + if (cpu_is_omap34xx()) + omap3_vc_init_channel(voltdm); + else if (cpu_is_omap44xx()) + omap4_vc_init_channel(voltdm); +} diff --git a/arch/arm/mach-omap2/vc.h b/arch/arm/mach-omap2/vc.h new file mode 100644 index 000000000..9e861dbc2 --- /dev/null +++ b/arch/arm/mach-omap2/vc.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP3/4 Voltage Controller (VC) structure and macro definitions + * + * Copyright (C) 2007, 2010 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + */ +#ifndef __ARCH_ARM_MACH_OMAP2_VC_H +#define __ARCH_ARM_MACH_OMAP2_VC_H + +#include <linux/kernel.h> + +struct voltagedomain; + +/** + * struct omap_vc_common - per-VC register/bitfield data + * @cmd_on_mask: ON bitmask in PRM_VC_CMD_VAL* register + * @valid: VALID bitmask in PRM_VC_BYPASS_VAL register + * @bypass_val_reg: Offset of PRM_VC_BYPASS_VAL reg from PRM start + * @data_shift: DATA field shift in PRM_VC_BYPASS_VAL register + * @slaveaddr_shift: SLAVEADDR field shift in PRM_VC_BYPASS_VAL register + * @regaddr_shift: REGADDR field shift in PRM_VC_BYPASS_VAL register + * @cmd_on_shift: ON field shift in PRM_VC_CMD_VAL_* register + * @cmd_onlp_shift: ONLP field shift in PRM_VC_CMD_VAL_* register + * @cmd_ret_shift: RET field shift in PRM_VC_CMD_VAL_* register + * @cmd_off_shift: OFF field shift in PRM_VC_CMD_VAL_* register + * @i2c_cfg_reg: I2C configuration register offset + * @i2c_cfg_clear_mask: high-speed mode bit clear mask in I2C config register + * @i2c_cfg_hsen_mask: high-speed mode bit field mask in I2C config register + * @i2c_mcode_mask: MCODE field mask for I2C config register + * + * XXX One of cmd_on_mask and cmd_on_shift are not needed + * XXX VALID should probably be a shift, not a mask + */ +struct omap_vc_common { + u32 cmd_on_mask; + u32 valid; + u8 bypass_val_reg; + u8 data_shift; + u8 slaveaddr_shift; + u8 regaddr_shift; + u8 cmd_on_shift; + u8 cmd_onlp_shift; + u8 cmd_ret_shift; + u8 cmd_off_shift; + u8 i2c_cfg_reg; + u8 i2c_cfg_clear_mask; + u8 i2c_cfg_hsen_mask; + u8 i2c_mcode_mask; +}; + +/* omap_vc_channel.flags values */ +#define OMAP_VC_CHANNEL_DEFAULT BIT(0) +#define OMAP_VC_CHANNEL_CFG_MUTANT BIT(1) + +/** + * struct omap_vc_channel - VC per-instance data + * @i2c_slave_addr: I2C slave address of PMIC for this VC channel + * @volt_reg_addr: voltage configuration register address + * @cmd_reg_addr: command configuration register address + * @setup_time: setup time (in sys_clk cycles) of regulator for this channel + * @cfg_channel: current value of VC channel configuration register + * @i2c_high_speed: whether or not to use I2C high-speed mode + * + * @common: pointer to VC common data for this platform + * @smps_sa_mask: i2c slave address bitmask in the PRM_VC_SMPS_SA register + * @smps_volra_mask: VOLRA* bitmask in the PRM_VC_VOL_RA register + * @smps_cmdra_mask: CMDRA* bitmask in the PRM_VC_CMD_RA register + * @cmdval_reg: register for on/ret/off voltage level values for this channel + * @smps_sa_reg: Offset of PRM_VC_SMPS_SA reg from PRM start + * @smps_volra_reg: Offset of PRM_VC_SMPS_VOL_RA reg from PRM start + * @smps_cmdra_reg: Offset of PRM_VC_SMPS_CMD_RA reg from PRM start + * @cfg_channel_reg: VC channel configuration register + * @cfg_channel_sa_shift: bit shift for slave address cfg_channel register + * @flags: VC channel-specific flags (optional) + */ +struct omap_vc_channel { + /* channel state */ + u16 i2c_slave_addr; + u16 volt_reg_addr; + u16 cmd_reg_addr; + u8 cfg_channel; + bool i2c_high_speed; + + /* register access data */ + const struct omap_vc_common *common; + u32 smps_sa_mask; + u32 smps_volra_mask; + u32 smps_cmdra_mask; + u8 cmdval_reg; + u8 smps_sa_reg; + u8 smps_volra_reg; + u8 smps_cmdra_reg; + u8 cfg_channel_reg; + u8 cfg_channel_sa_shift; + u8 flags; +}; + +extern struct omap_vc_channel omap3_vc_mpu; +extern struct omap_vc_channel omap3_vc_core; + +extern struct omap_vc_channel omap4_vc_mpu; +extern struct omap_vc_channel omap4_vc_iva; +extern struct omap_vc_channel omap4_vc_core; + +extern struct omap_vc_param omap3_mpu_vc_data; +extern struct omap_vc_param omap3_core_vc_data; + +extern struct omap_vc_param omap4_mpu_vc_data; +extern struct omap_vc_param omap4_iva_vc_data; +extern struct omap_vc_param omap4_core_vc_data; + +void omap3_vc_set_pmic_signaling(int core_next_state); +void omap4_vc_set_pmic_signaling(int core_next_state); + +void omap_vc_init_channel(struct voltagedomain *voltdm); +int omap_vc_pre_scale(struct voltagedomain *voltdm, + unsigned long target_volt, + u8 *target_vsel, u8 *current_vsel); +void omap_vc_post_scale(struct voltagedomain *voltdm, + unsigned long target_volt, + u8 target_vsel, u8 current_vsel); +int omap_vc_bypass_scale(struct voltagedomain *voltdm, + unsigned long target_volt); + +#endif + diff --git a/arch/arm/mach-omap2/vc3xxx_data.c b/arch/arm/mach-omap2/vc3xxx_data.c new file mode 100644 index 000000000..227345081 --- /dev/null +++ b/arch/arm/mach-omap2/vc3xxx_data.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 Voltage Controller (VC) data + * + * Copyright (C) 2007, 2010 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + */ +#include <linux/io.h> +#include <linux/err.h> +#include <linux/init.h> + +#include "common.h" + +#include "prm-regbits-34xx.h" +#include "voltage.h" + +#include "vc.h" + +/* + * VC data common to 34xx/36xx chips + * XXX This stuff presumably belongs in the vc3xxx.c or vc.c file. + */ +static struct omap_vc_common omap3_vc_common = { + .bypass_val_reg = OMAP3_PRM_VC_BYPASS_VAL_OFFSET, + .data_shift = OMAP3430_DATA_SHIFT, + .slaveaddr_shift = OMAP3430_SLAVEADDR_SHIFT, + .regaddr_shift = OMAP3430_REGADDR_SHIFT, + .valid = OMAP3430_VALID_MASK, + .cmd_on_shift = OMAP3430_VC_CMD_ON_SHIFT, + .cmd_on_mask = OMAP3430_VC_CMD_ON_MASK, + .cmd_onlp_shift = OMAP3430_VC_CMD_ONLP_SHIFT, + .cmd_ret_shift = OMAP3430_VC_CMD_RET_SHIFT, + .cmd_off_shift = OMAP3430_VC_CMD_OFF_SHIFT, + .i2c_cfg_clear_mask = OMAP3430_SREN_MASK | OMAP3430_HSEN_MASK, + .i2c_cfg_hsen_mask = OMAP3430_HSEN_MASK, + .i2c_cfg_reg = OMAP3_PRM_VC_I2C_CFG_OFFSET, + .i2c_mcode_mask = OMAP3430_MCODE_MASK, +}; + +struct omap_vc_channel omap3_vc_mpu = { + .flags = OMAP_VC_CHANNEL_DEFAULT, + .common = &omap3_vc_common, + .smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET, + .smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET, + .smps_cmdra_reg = OMAP3_PRM_VC_SMPS_CMD_RA_OFFSET, + .cfg_channel_reg = OMAP3_PRM_VC_CH_CONF_OFFSET, + .cmdval_reg = OMAP3_PRM_VC_CMD_VAL_0_OFFSET, + .smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA0_MASK, + .smps_volra_mask = OMAP3430_VOLRA0_MASK, + .smps_cmdra_mask = OMAP3430_CMDRA0_MASK, + .cfg_channel_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT, +}; + +struct omap_vc_channel omap3_vc_core = { + .common = &omap3_vc_common, + .smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET, + .smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET, + .smps_cmdra_reg = OMAP3_PRM_VC_SMPS_CMD_RA_OFFSET, + .cfg_channel_reg = OMAP3_PRM_VC_CH_CONF_OFFSET, + .cmdval_reg = OMAP3_PRM_VC_CMD_VAL_1_OFFSET, + .smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA1_MASK, + .smps_volra_mask = OMAP3430_VOLRA1_MASK, + .smps_cmdra_mask = OMAP3430_CMDRA1_MASK, + .cfg_channel_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT, +}; + +/* + * Voltage levels for different operating modes: on, sleep, retention and off + */ +#define OMAP3_ON_VOLTAGE_UV 1200000 +#define OMAP3_ONLP_VOLTAGE_UV 1000000 +#define OMAP3_RET_VOLTAGE_UV 975000 +#define OMAP3_OFF_VOLTAGE_UV 600000 + +struct omap_vc_param omap3_mpu_vc_data = { + .on = OMAP3_ON_VOLTAGE_UV, + .onlp = OMAP3_ONLP_VOLTAGE_UV, + .ret = OMAP3_RET_VOLTAGE_UV, + .off = OMAP3_OFF_VOLTAGE_UV, +}; + +struct omap_vc_param omap3_core_vc_data = { + .on = OMAP3_ON_VOLTAGE_UV, + .onlp = OMAP3_ONLP_VOLTAGE_UV, + .ret = OMAP3_RET_VOLTAGE_UV, + .off = OMAP3_OFF_VOLTAGE_UV, +}; diff --git a/arch/arm/mach-omap2/vc44xx_data.c b/arch/arm/mach-omap2/vc44xx_data.c new file mode 100644 index 000000000..0c70c0815 --- /dev/null +++ b/arch/arm/mach-omap2/vc44xx_data.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP4 Voltage Controller (VC) data + * + * Copyright (C) 2007, 2010 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + */ +#include <linux/io.h> +#include <linux/err.h> +#include <linux/init.h> + +#include "common.h" + +#include "prm44xx.h" +#include "prm-regbits-44xx.h" +#include "voltage.h" + +#include "vc.h" + +/* + * VC data common to 44xx chips + * XXX This stuff presumably belongs in the vc3xxx.c or vc.c file. + */ +static const struct omap_vc_common omap4_vc_common = { + .bypass_val_reg = OMAP4_PRM_VC_VAL_BYPASS_OFFSET, + .data_shift = OMAP4430_DATA_SHIFT, + .slaveaddr_shift = OMAP4430_SLAVEADDR_SHIFT, + .regaddr_shift = OMAP4430_REGADDR_SHIFT, + .valid = OMAP4430_VALID_MASK, + .cmd_on_shift = OMAP4430_ON_SHIFT, + .cmd_on_mask = OMAP4430_ON_MASK, + .cmd_onlp_shift = OMAP4430_ONLP_SHIFT, + .cmd_ret_shift = OMAP4430_RET_SHIFT, + .cmd_off_shift = OMAP4430_OFF_SHIFT, + .i2c_cfg_reg = OMAP4_PRM_VC_CFG_I2C_MODE_OFFSET, + .i2c_cfg_clear_mask = OMAP4430_SRMODEEN_MASK | OMAP4430_HSMODEEN_MASK, + .i2c_cfg_hsen_mask = OMAP4430_HSMODEEN_MASK, + .i2c_mcode_mask = OMAP4430_HSMCODE_MASK, +}; + +/* VC instance data for each controllable voltage line */ +struct omap_vc_channel omap4_vc_mpu = { + .flags = OMAP_VC_CHANNEL_DEFAULT | OMAP_VC_CHANNEL_CFG_MUTANT, + .common = &omap4_vc_common, + .smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET, + .smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET, + .smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET, + .cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET, + .cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_MPU_L_OFFSET, + .smps_sa_mask = OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_MASK, + .smps_volra_mask = OMAP4430_VOLRA_VDD_MPU_L_MASK, + .smps_cmdra_mask = OMAP4430_CMDRA_VDD_MPU_L_MASK, + .cfg_channel_sa_shift = OMAP4430_SA_VDD_MPU_L_SHIFT, +}; + +struct omap_vc_channel omap4_vc_iva = { + .common = &omap4_vc_common, + .smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET, + .smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET, + .smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET, + .cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET, + .cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_IVA_L_OFFSET, + .smps_sa_mask = OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_MASK, + .smps_volra_mask = OMAP4430_VOLRA_VDD_IVA_L_MASK, + .smps_cmdra_mask = OMAP4430_CMDRA_VDD_IVA_L_MASK, + .cfg_channel_sa_shift = OMAP4430_SA_VDD_IVA_L_SHIFT, +}; + +struct omap_vc_channel omap4_vc_core = { + .common = &omap4_vc_common, + .smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET, + .smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET, + .smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET, + .cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET, + .cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_CORE_L_OFFSET, + .smps_sa_mask = OMAP4430_SA_VDD_CORE_L_0_6_MASK, + .smps_volra_mask = OMAP4430_VOLRA_VDD_CORE_L_MASK, + .smps_cmdra_mask = OMAP4430_CMDRA_VDD_CORE_L_MASK, + .cfg_channel_sa_shift = OMAP4430_SA_VDD_CORE_L_SHIFT, +}; + +/* + * Voltage levels for different operating modes: on, sleep, retention and off + */ +#define OMAP4_ON_VOLTAGE_UV 1375000 +#define OMAP4_ONLP_VOLTAGE_UV 1375000 +#define OMAP4_RET_VOLTAGE_UV 837500 +#define OMAP4_OFF_VOLTAGE_UV 0 + +struct omap_vc_param omap4_mpu_vc_data = { + .on = OMAP4_ON_VOLTAGE_UV, + .onlp = OMAP4_ONLP_VOLTAGE_UV, + .ret = OMAP4_RET_VOLTAGE_UV, + .off = OMAP4_OFF_VOLTAGE_UV, +}; + +struct omap_vc_param omap4_iva_vc_data = { + .on = OMAP4_ON_VOLTAGE_UV, + .onlp = OMAP4_ONLP_VOLTAGE_UV, + .ret = OMAP4_RET_VOLTAGE_UV, + .off = OMAP4_OFF_VOLTAGE_UV, +}; + +struct omap_vc_param omap4_core_vc_data = { + .on = OMAP4_ON_VOLTAGE_UV, + .onlp = OMAP4_ONLP_VOLTAGE_UV, + .ret = OMAP4_RET_VOLTAGE_UV, + .off = OMAP4_OFF_VOLTAGE_UV, +}; diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c new file mode 100644 index 000000000..0a0c771db --- /dev/null +++ b/arch/arm/mach-omap2/voltage.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3/OMAP4 Voltage Management Routines + * + * Author: Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Thara Gopinath <thara@ti.com> + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/debugfs.h> +#include <linux/slab.h> +#include <linux/clk.h> + +#include "common.h" + +#include "prm-regbits-34xx.h" +#include "prm-regbits-44xx.h" +#include "prm44xx.h" +#include "prcm44xx.h" +#include "prminst44xx.h" +#include "control.h" + +#include "voltage.h" +#include "powerdomain.h" + +#include "vc.h" +#include "vp.h" + +static LIST_HEAD(voltdm_list); + +/* Public functions */ +/** + * voltdm_get_voltage() - Gets the current non-auto-compensated voltage + * @voltdm: pointer to the voltdm for which current voltage info is needed + * + * API to get the current non-auto-compensated voltage for a voltage domain. + * Returns 0 in case of error else returns the current voltage. + */ +unsigned long voltdm_get_voltage(struct voltagedomain *voltdm) +{ + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return 0; + } + + return voltdm->nominal_volt; +} + +/** + * voltdm_scale() - API to scale voltage of a particular voltage domain. + * @voltdm: pointer to the voltage domain which is to be scaled. + * @target_volt: The target voltage of the voltage domain + * + * This API should be called by the kernel to do the voltage scaling + * for a particular voltage domain during DVFS. + */ +int voltdm_scale(struct voltagedomain *voltdm, + unsigned long target_volt) +{ + int ret, i; + unsigned long volt = 0; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return -EINVAL; + } + + if (!voltdm->scale) { + pr_err("%s: No voltage scale API registered for vdd_%s\n", + __func__, voltdm->name); + return -ENODATA; + } + + if (!voltdm->volt_data) { + pr_err("%s: No voltage data defined for vdd_%s\n", + __func__, voltdm->name); + return -ENODATA; + } + + /* Adjust voltage to the exact voltage from the OPP table */ + for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { + if (voltdm->volt_data[i].volt_nominal >= target_volt) { + volt = voltdm->volt_data[i].volt_nominal; + break; + } + } + + if (!volt) { + pr_warn("%s: not scaling. OPP voltage for %lu, not found.\n", + __func__, target_volt); + return -EINVAL; + } + + ret = voltdm->scale(voltdm, volt); + if (!ret) + voltdm->nominal_volt = volt; + + return ret; +} + +/** + * voltdm_reset() - Resets the voltage of a particular voltage domain + * to that of the current OPP. + * @voltdm: pointer to the voltage domain whose voltage is to be reset. + * + * This API finds out the correct voltage the voltage domain is supposed + * to be at and resets the voltage to that level. Should be used especially + * while disabling any voltage compensation modules. + */ +void voltdm_reset(struct voltagedomain *voltdm) +{ + unsigned long target_volt; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return; + } + + target_volt = voltdm_get_voltage(voltdm); + if (!target_volt) { + pr_err("%s: unable to find current voltage for vdd_%s\n", + __func__, voltdm->name); + return; + } + + voltdm_scale(voltdm, target_volt); +} + +/** + * omap_voltage_get_volttable() - API to get the voltage table associated with a + * particular voltage domain. + * @voltdm: pointer to the VDD for which the voltage table is required + * @volt_data: the voltage table for the particular vdd which is to be + * populated by this API + * + * This API populates the voltage table associated with a VDD into the + * passed parameter pointer. Returns the count of distinct voltages + * supported by this vdd. + * + */ +void omap_voltage_get_volttable(struct voltagedomain *voltdm, + struct omap_volt_data **volt_data) +{ + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return; + } + + *volt_data = voltdm->volt_data; +} + +/** + * omap_voltage_get_voltdata() - API to get the voltage table entry for a + * particular voltage + * @voltdm: pointer to the VDD whose voltage table has to be searched + * @volt: the voltage to be searched in the voltage table + * + * This API searches through the voltage table for the required voltage + * domain and tries to find a matching entry for the passed voltage volt. + * If a matching entry is found volt_data is populated with that entry. + * This API searches only through the non-compensated voltages int the + * voltage table. + * Returns pointer to the voltage table entry corresponding to volt on + * success. Returns -ENODATA if no voltage table exisits for the passed voltage + * domain or if there is no matching entry. + */ +struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, + unsigned long volt) +{ + int i; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return ERR_PTR(-EINVAL); + } + + if (!voltdm->volt_data) { + pr_warn("%s: voltage table does not exist for vdd_%s\n", + __func__, voltdm->name); + return ERR_PTR(-ENODATA); + } + + for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { + if (voltdm->volt_data[i].volt_nominal == volt) + return &voltdm->volt_data[i]; + } + + pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n", + __func__, voltdm->name); + + return ERR_PTR(-ENODATA); +} + +/** + * omap_voltage_register_pmic() - API to register PMIC specific data + * @voltdm: pointer to the VDD for which the PMIC specific data is + * to be registered + * @pmic: the structure containing pmic info + * + * This API is to be called by the SOC/PMIC file to specify the + * pmic specific info as present in omap_voltdm_pmic structure. + */ +int omap_voltage_register_pmic(struct voltagedomain *voltdm, + struct omap_voltdm_pmic *pmic) +{ + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return -EINVAL; + } + + voltdm->pmic = pmic; + + return 0; +} + +/** + * omap_voltage_late_init() - Init the various voltage parameters + * + * This API is to be called in the later stages of the + * system boot to init the voltage controller and + * voltage processors. + */ +int __init omap_voltage_late_init(void) +{ + struct voltagedomain *voltdm; + + if (list_empty(&voltdm_list)) { + pr_err("%s: Voltage driver support not added\n", + __func__); + return -EINVAL; + } + + list_for_each_entry(voltdm, &voltdm_list, node) { + struct clk *sys_ck; + + if (!voltdm->scalable) + continue; + + sys_ck = clk_get(NULL, voltdm->sys_clk.name); + if (IS_ERR(sys_ck)) { + pr_warn("%s: Could not get sys clk.\n", __func__); + return -EINVAL; + } + voltdm->sys_clk.rate = clk_get_rate(sys_ck); + WARN_ON(!voltdm->sys_clk.rate); + clk_put(sys_ck); + + if (voltdm->vc) { + voltdm->scale = omap_vc_bypass_scale; + omap_vc_init_channel(voltdm); + } + + if (voltdm->vp) { + voltdm->scale = omap_vp_forceupdate_scale; + omap_vp_init(voltdm); + } + } + + return 0; +} + +static struct voltagedomain *_voltdm_lookup(const char *name) +{ + struct voltagedomain *voltdm, *temp_voltdm; + + voltdm = NULL; + + list_for_each_entry(temp_voltdm, &voltdm_list, node) { + if (!strcmp(name, temp_voltdm->name)) { + voltdm = temp_voltdm; + break; + } + } + + return voltdm; +} + +static int _voltdm_register(struct voltagedomain *voltdm) +{ + if (!voltdm || !voltdm->name) + return -EINVAL; + + list_add(&voltdm->node, &voltdm_list); + + pr_debug("voltagedomain: registered %s\n", voltdm->name); + + return 0; +} + +/** + * voltdm_lookup - look up a voltagedomain by name, return a pointer + * @name: name of voltagedomain + * + * Find a registered voltagedomain by its name @name. Returns a pointer + * to the struct voltagedomain if found, or NULL otherwise. + */ +struct voltagedomain *voltdm_lookup(const char *name) +{ + struct voltagedomain *voltdm ; + + if (!name) + return NULL; + + voltdm = _voltdm_lookup(name); + + return voltdm; +} + +/** + * voltdm_init - set up the voltagedomain layer + * @voltdm_list: array of struct voltagedomain pointers to register + * + * Loop through the array of voltagedomains @voltdm_list, registering all + * that are available on the current CPU. If voltdm_list is supplied + * and not null, all of the referenced voltagedomains will be + * registered. No return value. + */ +void voltdm_init(struct voltagedomain **voltdms) +{ + struct voltagedomain **v; + + if (voltdms) { + for (v = voltdms; *v; v++) + _voltdm_register(*v); + } +} diff --git a/arch/arm/mach-omap2/voltage.h b/arch/arm/mach-omap2/voltage.h new file mode 100644 index 000000000..4a225f955 --- /dev/null +++ b/arch/arm/mach-omap2/voltage.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP Voltage Management Routines + * + * Author: Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2009 Texas Instruments, Inc. + * Thara Gopinath <thara@ti.com> + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_VOLTAGE_H +#define __ARCH_ARM_MACH_OMAP2_VOLTAGE_H + +#include <linux/err.h> + +#include <linux/platform_data/voltage-omap.h> + +#include "vc.h" +#include "vp.h" + +struct powerdomain; + +/* + * OMAP3 GENERIC setup times. Revisit to see if these needs to be + * passed from board or PMIC file + */ +#define OMAP3_CLKSETUP 0xff +#define OMAP3_VOLTOFFSET 0xff +#define OMAP3_VOLTSETUP2 0xff + +/** + * struct omap_vfsm_instance - per-voltage manager FSM register/bitfield + * data + * @voltsetup_mask: SETUP_TIME* bitmask in the PRM_VOLTSETUP* register + * @voltsetup_reg: register offset of PRM_VOLTSETUP from PRM base + * @voltsetup_off_reg: register offset of PRM_VOLTSETUP_OFF from PRM base + * + * XXX What about VOLTOFFSET/VOLTCTRL? + */ +struct omap_vfsm_instance { + u32 voltsetup_mask; + u8 voltsetup_reg; + u8 voltsetup_off_reg; +}; + +/** + * struct voltagedomain - omap voltage domain global structure. + * @name: Name of the voltage domain which can be used as a unique identifier. + * @scalable: Whether or not this voltage domain is scalable + * @node: list_head linking all voltage domains + * @vc: pointer to VC channel associated with this voltagedomain + * @vp: pointer to VP associated with this voltagedomain + * @read: read a VC/VP register + * @write: write a VC/VP register + * @read: read-modify-write a VC/VP register + * @sys_clk: system clock name/frequency, used for various timing calculations + * @scale: function used to scale the voltage of the voltagedomain + * @nominal_volt: current nominal voltage for this voltage domain + * @volt_data: voltage table having the distinct voltages supported + * by the domain and other associated per voltage data. + */ +struct voltagedomain { + char *name; + bool scalable; + struct list_head node; + struct omap_vc_channel *vc; + const struct omap_vfsm_instance *vfsm; + struct omap_vp_instance *vp; + struct omap_voltdm_pmic *pmic; + struct omap_vp_param *vp_param; + struct omap_vc_param *vc_param; + + /* VC/VP register access functions: SoC specific */ + u32 (*read) (u8 offset); + void (*write) (u32 val, u8 offset); + u32 (*rmw)(u32 mask, u32 bits, u8 offset); + + union { + const char *name; + u32 rate; + } sys_clk; + + int (*scale) (struct voltagedomain *voltdm, + unsigned long target_volt); + + u32 nominal_volt; + struct omap_volt_data *volt_data; +}; + +/* Min and max voltages from OMAP perspective */ +#define OMAP3430_VP1_VLIMITTO_VDDMIN 850000 +#define OMAP3430_VP1_VLIMITTO_VDDMAX 1425000 +#define OMAP3430_VP2_VLIMITTO_VDDMIN 900000 +#define OMAP3430_VP2_VLIMITTO_VDDMAX 1150000 + +#define OMAP3630_VP1_VLIMITTO_VDDMIN 900000 +#define OMAP3630_VP1_VLIMITTO_VDDMAX 1350000 +#define OMAP3630_VP2_VLIMITTO_VDDMIN 900000 +#define OMAP3630_VP2_VLIMITTO_VDDMAX 1200000 + +#define OMAP4_VP_MPU_VLIMITTO_VDDMIN 830000 +#define OMAP4_VP_MPU_VLIMITTO_VDDMAX 1410000 +#define OMAP4_VP_IVA_VLIMITTO_VDDMIN 830000 +#define OMAP4_VP_IVA_VLIMITTO_VDDMAX 1260000 +#define OMAP4_VP_CORE_VLIMITTO_VDDMIN 830000 +#define OMAP4_VP_CORE_VLIMITTO_VDDMAX 1200000 + +/** + * struct omap_voltdm_pmic - PMIC specific data required by voltage driver. + * @slew_rate: PMIC slew rate (in uv/us) + * @step_size: PMIC voltage step size (in uv) + * @i2c_slave_addr: I2C slave address of PMIC + * @volt_reg_addr: voltage configuration register address + * @cmd_reg_addr: command (on, on-LP, ret, off) configuration register address + * @i2c_high_speed: whether VC uses I2C high-speed mode to PMIC + * @i2c_mcode: master code value for I2C high-speed preamble transmission + * @vsel_to_uv: PMIC API to convert vsel value to actual voltage in uV. + * @uv_to_vsel: PMIC API to convert voltage in uV to vsel value. + */ +struct omap_voltdm_pmic { + int slew_rate; + int step_size; + u16 i2c_slave_addr; + u16 volt_reg_addr; + u16 cmd_reg_addr; + u8 vp_erroroffset; + u8 vp_vstepmin; + u8 vp_vstepmax; + u32 vddmin; + u32 vddmax; + u8 vp_timeout_us; + bool i2c_high_speed; + u32 i2c_pad_load; + u8 i2c_mcode; + unsigned long (*vsel_to_uv) (const u8 vsel); + u8 (*uv_to_vsel) (unsigned long uV); +}; + +struct omap_vp_param { + u32 vddmax; + u32 vddmin; +}; + +struct omap_vc_param { + u32 on; + u32 onlp; + u32 ret; + u32 off; +}; + +void omap_voltage_get_volttable(struct voltagedomain *voltdm, + struct omap_volt_data **volt_data); +struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, + unsigned long volt); +int omap_voltage_register_pmic(struct voltagedomain *voltdm, + struct omap_voltdm_pmic *pmic); +int omap_voltage_late_init(void); + +extern void omap2xxx_voltagedomains_init(void); +extern void omap3xxx_voltagedomains_init(void); +extern void omap44xx_voltagedomains_init(void); +extern void omap54xx_voltagedomains_init(void); + +struct voltagedomain *voltdm_lookup(const char *name); +void voltdm_init(struct voltagedomain **voltdm_list); +int voltdm_add_pwrdm(struct voltagedomain *voltdm, struct powerdomain *pwrdm); +int voltdm_scale(struct voltagedomain *voltdm, unsigned long target_volt); +void voltdm_reset(struct voltagedomain *voltdm); +unsigned long voltdm_get_voltage(struct voltagedomain *voltdm); +#endif diff --git a/arch/arm/mach-omap2/voltagedomains2xxx_data.c b/arch/arm/mach-omap2/voltagedomains2xxx_data.c new file mode 100644 index 000000000..9bc116fe3 --- /dev/null +++ b/arch/arm/mach-omap2/voltagedomains2xxx_data.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 voltage domain data + * + * Copyright (C) 2011 Texas Instruments, Inc. + */ +#include <linux/kernel.h> +#include <linux/init.h> + +#include "voltage.h" + +static struct voltagedomain omap2_voltdm_core = { + .name = "core", +}; + +static struct voltagedomain omap2_voltdm_wkup = { + .name = "wakeup", +}; + +static struct voltagedomain *voltagedomains_omap2[] __initdata = { + &omap2_voltdm_core, + &omap2_voltdm_wkup, + NULL, +}; + +void __init omap2xxx_voltagedomains_init(void) +{ + voltdm_init(voltagedomains_omap2); +} diff --git a/arch/arm/mach-omap2/voltagedomains3xxx_data.c b/arch/arm/mach-omap2/voltagedomains3xxx_data.c new file mode 100644 index 000000000..e98a3f289 --- /dev/null +++ b/arch/arm/mach-omap2/voltagedomains3xxx_data.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 voltage domain data + * + * Copyright (C) 2007, 2010 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + */ +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/init.h> + +#include "soc.h" +#include "common.h" +#include "prm-regbits-34xx.h" +#include "omap_opp_data.h" +#include "voltage.h" +#include "vc.h" +#include "vp.h" + +/* + * VDD data + */ + +/* OMAP3-common voltagedomain data */ + +static struct voltagedomain omap3_voltdm_wkup = { + .name = "wakeup", +}; + +/* 34xx/36xx voltagedomain data */ + +static const struct omap_vfsm_instance omap3_vdd1_vfsm = { + .voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET, + .voltsetup_mask = OMAP3430_SETUP_TIME1_MASK, +}; + +static const struct omap_vfsm_instance omap3_vdd2_vfsm = { + .voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET, + .voltsetup_mask = OMAP3430_SETUP_TIME2_MASK, +}; + +static struct voltagedomain omap3_voltdm_mpu = { + .name = "mpu_iva", + .scalable = true, + .read = omap3_prm_vcvp_read, + .write = omap3_prm_vcvp_write, + .rmw = omap3_prm_vcvp_rmw, + .vc = &omap3_vc_mpu, + .vfsm = &omap3_vdd1_vfsm, + .vp = &omap3_vp_mpu, +}; + +static struct voltagedomain omap3_voltdm_core = { + .name = "core", + .scalable = true, + .read = omap3_prm_vcvp_read, + .write = omap3_prm_vcvp_write, + .rmw = omap3_prm_vcvp_rmw, + .vc = &omap3_vc_core, + .vfsm = &omap3_vdd2_vfsm, + .vp = &omap3_vp_core, +}; + +static struct voltagedomain *voltagedomains_omap3[] __initdata = { + &omap3_voltdm_mpu, + &omap3_voltdm_core, + &omap3_voltdm_wkup, + NULL, +}; + +/* AM35xx voltagedomain data */ + +static struct voltagedomain am35xx_voltdm_mpu = { + .name = "mpu_iva", +}; + +static struct voltagedomain am35xx_voltdm_core = { + .name = "core", +}; + +static struct voltagedomain *voltagedomains_am35xx[] __initdata = { + &am35xx_voltdm_mpu, + &am35xx_voltdm_core, + &omap3_voltdm_wkup, + NULL, +}; + + +static const char *const sys_clk_name __initconst = "sys_ck"; + +void __init omap3xxx_voltagedomains_init(void) +{ + struct voltagedomain *voltdm; + struct voltagedomain **voltdms; + int i; + + /* + * XXX Will depend on the process, validation, and binning + * for the currently-running IC + */ +#ifdef CONFIG_PM_OPP + if (cpu_is_omap3630()) { + omap3_voltdm_mpu.volt_data = omap36xx_vddmpu_volt_data; + omap3_voltdm_core.volt_data = omap36xx_vddcore_volt_data; + } else { + omap3_voltdm_mpu.volt_data = omap34xx_vddmpu_volt_data; + omap3_voltdm_core.volt_data = omap34xx_vddcore_volt_data; + } +#endif + + omap3_voltdm_mpu.vp_param = &omap3_mpu_vp_data; + omap3_voltdm_core.vp_param = &omap3_core_vp_data; + omap3_voltdm_mpu.vc_param = &omap3_mpu_vc_data; + omap3_voltdm_core.vc_param = &omap3_core_vc_data; + + if (soc_is_am35xx()) + voltdms = voltagedomains_am35xx; + else + voltdms = voltagedomains_omap3; + + for (i = 0; voltdm = voltdms[i], voltdm; i++) + voltdm->sys_clk.name = sys_clk_name; + + voltdm_init(voltdms); +}; diff --git a/arch/arm/mach-omap2/voltagedomains44xx_data.c b/arch/arm/mach-omap2/voltagedomains44xx_data.c new file mode 100644 index 000000000..5ea86f16f --- /dev/null +++ b/arch/arm/mach-omap2/voltagedomains44xx_data.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3/OMAP4 Voltage Management Routines + * + * Author: Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * + * Copyright (C) 2008 Nokia Corporation + * Kalle Jokiniemi + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Thara Gopinath <thara@ti.com> + */ +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/init.h> + +#include "common.h" +#include "soc.h" +#include "prm-regbits-44xx.h" +#include "prm44xx.h" +#include "prcm44xx.h" +#include "prminst44xx.h" +#include "voltage.h" +#include "omap_opp_data.h" +#include "vc.h" +#include "vp.h" + +static const struct omap_vfsm_instance omap4_vdd_mpu_vfsm = { + .voltsetup_reg = OMAP4_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET, + .voltsetup_off_reg = OMAP4_PRM_VOLTSETUP_MPU_OFF_OFFSET, +}; + +static const struct omap_vfsm_instance omap4_vdd_iva_vfsm = { + .voltsetup_reg = OMAP4_PRM_VOLTSETUP_IVA_RET_SLEEP_OFFSET, + .voltsetup_off_reg = OMAP4_PRM_VOLTSETUP_IVA_OFF_OFFSET, +}; + +static const struct omap_vfsm_instance omap4_vdd_core_vfsm = { + .voltsetup_reg = OMAP4_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET, + .voltsetup_off_reg = OMAP4_PRM_VOLTSETUP_CORE_OFF_OFFSET, +}; + +static struct voltagedomain omap4_voltdm_mpu = { + .name = "mpu", + .scalable = true, + .read = omap4_prm_vcvp_read, + .write = omap4_prm_vcvp_write, + .rmw = omap4_prm_vcvp_rmw, + .vc = &omap4_vc_mpu, + .vfsm = &omap4_vdd_mpu_vfsm, + .vp = &omap4_vp_mpu, +}; + +static struct voltagedomain omap4_voltdm_iva = { + .name = "iva", + .scalable = true, + .read = omap4_prm_vcvp_read, + .write = omap4_prm_vcvp_write, + .rmw = omap4_prm_vcvp_rmw, + .vc = &omap4_vc_iva, + .vfsm = &omap4_vdd_iva_vfsm, + .vp = &omap4_vp_iva, +}; + +static struct voltagedomain omap4_voltdm_core = { + .name = "core", + .scalable = true, + .read = omap4_prm_vcvp_read, + .write = omap4_prm_vcvp_write, + .rmw = omap4_prm_vcvp_rmw, + .vc = &omap4_vc_core, + .vfsm = &omap4_vdd_core_vfsm, + .vp = &omap4_vp_core, +}; + +static struct voltagedomain omap4_voltdm_wkup = { + .name = "wakeup", +}; + +static struct voltagedomain *voltagedomains_omap4[] __initdata = { + &omap4_voltdm_mpu, + &omap4_voltdm_iva, + &omap4_voltdm_core, + &omap4_voltdm_wkup, + NULL, +}; + +static const char *const sys_clk_name __initconst = "sys_clkin_ck"; + +void __init omap44xx_voltagedomains_init(void) +{ + struct voltagedomain *voltdm; + int i; + + /* + * XXX Will depend on the process, validation, and binning + * for the currently-running IC + */ +#ifdef CONFIG_PM_OPP + if (cpu_is_omap443x()) { + omap4_voltdm_mpu.volt_data = omap443x_vdd_mpu_volt_data; + omap4_voltdm_iva.volt_data = omap443x_vdd_iva_volt_data; + omap4_voltdm_core.volt_data = omap443x_vdd_core_volt_data; + } else if (cpu_is_omap446x()) { + omap4_voltdm_mpu.volt_data = omap446x_vdd_mpu_volt_data; + omap4_voltdm_iva.volt_data = omap446x_vdd_iva_volt_data; + omap4_voltdm_core.volt_data = omap446x_vdd_core_volt_data; + } +#endif + + omap4_voltdm_mpu.vp_param = &omap4_mpu_vp_data; + omap4_voltdm_iva.vp_param = &omap4_iva_vp_data; + omap4_voltdm_core.vp_param = &omap4_core_vp_data; + + omap4_voltdm_mpu.vc_param = &omap4_mpu_vc_data; + omap4_voltdm_iva.vc_param = &omap4_iva_vc_data; + omap4_voltdm_core.vc_param = &omap4_core_vc_data; + + for (i = 0; voltdm = voltagedomains_omap4[i], voltdm; i++) + voltdm->sys_clk.name = sys_clk_name; + + voltdm_init(voltagedomains_omap4); +}; diff --git a/arch/arm/mach-omap2/voltagedomains54xx_data.c b/arch/arm/mach-omap2/voltagedomains54xx_data.c new file mode 100644 index 000000000..e60d76db0 --- /dev/null +++ b/arch/arm/mach-omap2/voltagedomains54xx_data.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP5 Voltage Management Routines + * + * Based on voltagedomains44xx_data.c + * + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com + */ +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/init.h> + +#include "common.h" + +#include "prm54xx.h" +#include "voltage.h" +#include "omap_opp_data.h" +#include "vc.h" +#include "vp.h" + +static const struct omap_vfsm_instance omap5_vdd_mpu_vfsm = { + .voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET, +}; + +static const struct omap_vfsm_instance omap5_vdd_mm_vfsm = { + .voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_MM_RET_SLEEP_OFFSET, +}; + +static const struct omap_vfsm_instance omap5_vdd_core_vfsm = { + .voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET, +}; + +static struct voltagedomain omap5_voltdm_mpu = { + .name = "mpu", + .scalable = true, + .read = omap4_prm_vcvp_read, + .write = omap4_prm_vcvp_write, + .rmw = omap4_prm_vcvp_rmw, + .vc = &omap4_vc_mpu, + .vfsm = &omap5_vdd_mpu_vfsm, + .vp = &omap4_vp_mpu, +}; + +static struct voltagedomain omap5_voltdm_mm = { + .name = "mm", + .scalable = true, + .read = omap4_prm_vcvp_read, + .write = omap4_prm_vcvp_write, + .rmw = omap4_prm_vcvp_rmw, + .vc = &omap4_vc_iva, + .vfsm = &omap5_vdd_mm_vfsm, + .vp = &omap4_vp_iva, +}; + +static struct voltagedomain omap5_voltdm_core = { + .name = "core", + .scalable = true, + .read = omap4_prm_vcvp_read, + .write = omap4_prm_vcvp_write, + .rmw = omap4_prm_vcvp_rmw, + .vc = &omap4_vc_core, + .vfsm = &omap5_vdd_core_vfsm, + .vp = &omap4_vp_core, +}; + +static struct voltagedomain omap5_voltdm_wkup = { + .name = "wkup", +}; + +static struct voltagedomain *voltagedomains_omap5[] __initdata = { + &omap5_voltdm_mpu, + &omap5_voltdm_mm, + &omap5_voltdm_core, + &omap5_voltdm_wkup, + NULL, +}; + +static const char *const sys_clk_name __initconst = "sys_clkin"; + +void __init omap54xx_voltagedomains_init(void) +{ + struct voltagedomain *voltdm; + int i; + + for (i = 0; voltdm = voltagedomains_omap5[i], voltdm; i++) + voltdm->sys_clk.name = sys_clk_name; + + voltdm_init(voltagedomains_omap5); +}; diff --git a/arch/arm/mach-omap2/vp.c b/arch/arm/mach-omap2/vp.c new file mode 100644 index 000000000..a709655b9 --- /dev/null +++ b/arch/arm/mach-omap2/vp.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/init.h> + +#include "common.h" + +#include "voltage.h" +#include "vp.h" +#include "prm-regbits-34xx.h" +#include "prm-regbits-44xx.h" +#include "prm44xx.h" + +static u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt) +{ + struct omap_vp_instance *vp = voltdm->vp; + u32 vpconfig; + char vsel; + + vsel = voltdm->pmic->uv_to_vsel(volt); + + vpconfig = voltdm->read(vp->vpconfig); + vpconfig &= ~(vp->common->vpconfig_initvoltage_mask | + vp->common->vpconfig_forceupdate | + vp->common->vpconfig_initvdd); + vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask); + voltdm->write(vpconfig, vp->vpconfig); + + /* Trigger initVDD value copy to voltage processor */ + voltdm->write((vpconfig | vp->common->vpconfig_initvdd), + vp->vpconfig); + + /* Clear initVDD copy trigger bit */ + voltdm->write(vpconfig, vp->vpconfig); + + return vpconfig; +} + +/* Generic voltage init functions */ +void __init omap_vp_init(struct voltagedomain *voltdm) +{ + struct omap_vp_instance *vp = voltdm->vp; + u32 val, sys_clk_rate, timeout, waittime; + u32 vddmin, vddmax, vstepmin, vstepmax; + + if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { + pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); + return; + } + + if (!voltdm->read || !voltdm->write) { + pr_err("%s: No read/write API for accessing vdd_%s regs\n", + __func__, voltdm->name); + return; + } + + vp->enabled = false; + + /* Divide to avoid overflow */ + sys_clk_rate = voltdm->sys_clk.rate / 1000; + + timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000; + vddmin = max(voltdm->vp_param->vddmin, voltdm->pmic->vddmin); + vddmax = min(voltdm->vp_param->vddmax, voltdm->pmic->vddmax); + vddmin = voltdm->pmic->uv_to_vsel(vddmin); + vddmax = voltdm->pmic->uv_to_vsel(vddmax); + + waittime = DIV_ROUND_UP(voltdm->pmic->step_size * sys_clk_rate, + 1000 * voltdm->pmic->slew_rate); + vstepmin = voltdm->pmic->vp_vstepmin; + vstepmax = voltdm->pmic->vp_vstepmax; + + /* + * VP_CONFIG: error gain is not set here, it will be updated + * on each scale, based on OPP. + */ + val = (voltdm->pmic->vp_erroroffset << + __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) | + vp->common->vpconfig_timeouten; + voltdm->write(val, vp->vpconfig); + + /* VSTEPMIN */ + val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) | + (vstepmin << vp->common->vstepmin_stepmin_shift); + voltdm->write(val, vp->vstepmin); + + /* VSTEPMAX */ + val = (vstepmax << vp->common->vstepmax_stepmax_shift) | + (waittime << vp->common->vstepmax_smpswaittimemax_shift); + voltdm->write(val, vp->vstepmax); + + /* VLIMITTO */ + val = (vddmax << vp->common->vlimitto_vddmax_shift) | + (vddmin << vp->common->vlimitto_vddmin_shift) | + (timeout << vp->common->vlimitto_timeout_shift); + voltdm->write(val, vp->vlimitto); +} + +int omap_vp_update_errorgain(struct voltagedomain *voltdm, + unsigned long target_volt) +{ + struct omap_volt_data *volt_data; + + if (!voltdm->vp) + return -EINVAL; + + /* Get volt_data corresponding to target_volt */ + volt_data = omap_voltage_get_voltdata(voltdm, target_volt); + if (IS_ERR(volt_data)) + return -EINVAL; + + /* Setting vp errorgain based on the voltage */ + voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask, + volt_data->vp_errgain << + __ffs(voltdm->vp->common->vpconfig_errorgain_mask), + voltdm->vp->vpconfig); + + return 0; +} + +/* VP force update method of voltage scaling */ +int omap_vp_forceupdate_scale(struct voltagedomain *voltdm, + unsigned long target_volt) +{ + struct omap_vp_instance *vp = voltdm->vp; + u32 vpconfig; + u8 target_vsel, current_vsel; + int ret, timeout = 0; + + ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, ¤t_vsel); + if (ret) + return ret; + + /* + * Clear all pending TransactionDone interrupt/status. Typical latency + * is <3us + */ + while (timeout++ < VP_TRANXDONE_TIMEOUT) { + vp->common->ops->clear_txdone(vp->id); + if (!vp->common->ops->check_txdone(vp->id)) + break; + udelay(1); + } + if (timeout >= VP_TRANXDONE_TIMEOUT) { + pr_warn("%s: vdd_%s TRANXDONE timeout exceeded. Voltage change aborted\n", + __func__, voltdm->name); + return -ETIMEDOUT; + } + + vpconfig = _vp_set_init_voltage(voltdm, target_volt); + + /* Force update of voltage */ + voltdm->write(vpconfig | vp->common->vpconfig_forceupdate, + voltdm->vp->vpconfig); + + /* + * Wait for TransactionDone. Typical latency is <200us. + * Depends on SMPSWAITTIMEMIN/MAX and voltage change + */ + timeout = 0; + omap_test_timeout(vp->common->ops->check_txdone(vp->id), + VP_TRANXDONE_TIMEOUT, timeout); + if (timeout >= VP_TRANXDONE_TIMEOUT) + pr_err("%s: vdd_%s TRANXDONE timeout exceeded. TRANXDONE never got set after the voltage update\n", + __func__, voltdm->name); + + omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel); + + /* + * Disable TransactionDone interrupt , clear all status, clear + * control registers + */ + timeout = 0; + while (timeout++ < VP_TRANXDONE_TIMEOUT) { + vp->common->ops->clear_txdone(vp->id); + if (!vp->common->ops->check_txdone(vp->id)) + break; + udelay(1); + } + + if (timeout >= VP_TRANXDONE_TIMEOUT) + pr_warn("%s: vdd_%s TRANXDONE timeout exceeded while trying to clear the TRANXDONE status\n", + __func__, voltdm->name); + + /* Clear force bit */ + voltdm->write(vpconfig, vp->vpconfig); + + return 0; +} + +/** + * omap_vp_enable() - API to enable a particular VP + * @voltdm: pointer to the VDD whose VP is to be enabled. + * + * This API enables a particular voltage processor. Needed by the smartreflex + * class drivers. + */ +void omap_vp_enable(struct voltagedomain *voltdm) +{ + struct omap_vp_instance *vp; + u32 vpconfig, volt; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return; + } + + vp = voltdm->vp; + if (!voltdm->read || !voltdm->write) { + pr_err("%s: No read/write API for accessing vdd_%s regs\n", + __func__, voltdm->name); + return; + } + + /* If VP is already enabled, do nothing. Return */ + if (vp->enabled) + return; + + volt = voltdm_get_voltage(voltdm); + if (!volt) { + pr_warn("%s: unable to find current voltage for %s\n", + __func__, voltdm->name); + return; + } + + vpconfig = _vp_set_init_voltage(voltdm, volt); + + /* Enable VP */ + vpconfig |= vp->common->vpconfig_vpenable; + voltdm->write(vpconfig, vp->vpconfig); + + vp->enabled = true; +} + +/** + * omap_vp_disable() - API to disable a particular VP + * @voltdm: pointer to the VDD whose VP is to be disabled. + * + * This API disables a particular voltage processor. Needed by the smartreflex + * class drivers. + */ +void omap_vp_disable(struct voltagedomain *voltdm) +{ + struct omap_vp_instance *vp; + u32 vpconfig; + int timeout; + + if (!voltdm || IS_ERR(voltdm)) { + pr_warn("%s: VDD specified does not exist!\n", __func__); + return; + } + + vp = voltdm->vp; + if (!voltdm->read || !voltdm->write) { + pr_err("%s: No read/write API for accessing vdd_%s regs\n", + __func__, voltdm->name); + return; + } + + /* If VP is already disabled, do nothing. Return */ + if (!vp->enabled) { + pr_warn("%s: Trying to disable VP for vdd_%s when it is already disabled\n", + __func__, voltdm->name); + return; + } + + /* Disable VP */ + vpconfig = voltdm->read(vp->vpconfig); + vpconfig &= ~vp->common->vpconfig_vpenable; + voltdm->write(vpconfig, vp->vpconfig); + + /* + * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us + */ + omap_test_timeout((voltdm->read(vp->vstatus)), + VP_IDLE_TIMEOUT, timeout); + + if (timeout >= VP_IDLE_TIMEOUT) + pr_warn("%s: vdd_%s idle timedout\n", __func__, voltdm->name); + + vp->enabled = false; + + return; +} diff --git a/arch/arm/mach-omap2/vp.h b/arch/arm/mach-omap2/vp.h new file mode 100644 index 000000000..4d7dd204e --- /dev/null +++ b/arch/arm/mach-omap2/vp.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * OMAP3/4 Voltage Processor (VP) structure and macro definitions + * + * Copyright (C) 2007, 2010 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + */ +#ifndef __ARCH_ARM_MACH_OMAP2_VP_H +#define __ARCH_ARM_MACH_OMAP2_VP_H + +#include <linux/kernel.h> + +struct voltagedomain; + +/* XXX document */ +#define VP_IDLE_TIMEOUT 200 +#define VP_TRANXDONE_TIMEOUT 300 + +/** + * struct omap_vp_ops - per-VP operations + * @check_txdone: check for VP transaction done + * @clear_txdone: clear VP transaction done status + */ +struct omap_vp_ops { + u32 (*check_txdone)(u8 vp_id); + void (*clear_txdone)(u8 vp_id); +}; + +/** + * struct omap_vp_common - register data common to all VDDs + * @vpconfig_erroroffset_mask: ERROROFFSET bitmask in the PRM_VP*_CONFIG reg + * @vpconfig_errorgain_mask: ERRORGAIN bitmask in the PRM_VP*_CONFIG reg + * @vpconfig_initvoltage_mask: INITVOLTAGE bitmask in the PRM_VP*_CONFIG reg + * @vpconfig_timeouten: TIMEOUT bitmask in the PRM_VP*_CONFIG reg + * @vpconfig_initvdd: INITVDD bitmask in the PRM_VP*_CONFIG reg + * @vpconfig_forceupdate: FORCEUPDATE bitmask in the PRM_VP*_CONFIG reg + * @vpconfig_vpenable: VPENABLE bitmask in the PRM_VP*_CONFIG reg + * @vpconfig_erroroffset_shift: ERROROFFSET field shift in PRM_VP*_CONFIG reg + * @vpconfig_errorgain_shift: ERRORGAIN field shift in PRM_VP*_CONFIG reg + * @vpconfig_initvoltage_shift: INITVOLTAGE field shift in PRM_VP*_CONFIG reg + * @vstepmin_stepmin_shift: VSTEPMIN field shift in the PRM_VP*_VSTEPMIN reg + * @vstepmin_smpswaittimemin_shift: SMPSWAITTIMEMIN field shift in PRM_VP*_VSTEPMIN reg + * @vstepmax_stepmax_shift: VSTEPMAX field shift in the PRM_VP*_VSTEPMAX reg + * @vstepmax_smpswaittimemax_shift: SMPSWAITTIMEMAX field shift in PRM_VP*_VSTEPMAX reg + * @vlimitto_vddmin_shift: VDDMIN field shift in PRM_VP*_VLIMITTO reg + * @vlimitto_vddmax_shift: VDDMAX field shift in PRM_VP*_VLIMITTO reg + * @vlimitto_timeout_shift: TIMEOUT field shift in PRM_VP*_VLIMITTO reg + * @vpvoltage_mask: VPVOLTAGE field mask in PRM_VP*_VOLTAGE reg + */ +struct omap_vp_common { + u32 vpconfig_erroroffset_mask; + u32 vpconfig_errorgain_mask; + u32 vpconfig_initvoltage_mask; + u8 vpconfig_timeouten; + u8 vpconfig_initvdd; + u8 vpconfig_forceupdate; + u8 vpconfig_vpenable; + u8 vstepmin_stepmin_shift; + u8 vstepmin_smpswaittimemin_shift; + u8 vstepmax_stepmax_shift; + u8 vstepmax_smpswaittimemax_shift; + u8 vlimitto_vddmin_shift; + u8 vlimitto_vddmax_shift; + u8 vlimitto_timeout_shift; + u8 vpvoltage_mask; + + const struct omap_vp_ops *ops; +}; + +/** + * struct omap_vp_instance - VP register offsets (per-VDD) + * @common: pointer to struct omap_vp_common * for this SoC + * @vpconfig: PRM_VP*_CONFIG reg offset from PRM start + * @vstepmin: PRM_VP*_VSTEPMIN reg offset from PRM start + * @vlimitto: PRM_VP*_VLIMITTO reg offset from PRM start + * @vstatus: PRM_VP*_VSTATUS reg offset from PRM start + * @voltage: PRM_VP*_VOLTAGE reg offset from PRM start + * @id: Unique identifier for VP instance. + * @enabled: flag to keep track of whether vp is enabled or not + * + * XXX vp_common is probably not needed since it is per-SoC + */ +struct omap_vp_instance { + const struct omap_vp_common *common; + u8 vpconfig; + u8 vstepmin; + u8 vstepmax; + u8 vlimitto; + u8 vstatus; + u8 voltage; + u8 id; + bool enabled; +}; + +extern struct omap_vp_instance omap3_vp_mpu; +extern struct omap_vp_instance omap3_vp_core; + +extern struct omap_vp_instance omap4_vp_mpu; +extern struct omap_vp_instance omap4_vp_iva; +extern struct omap_vp_instance omap4_vp_core; + +extern struct omap_vp_param omap3_mpu_vp_data; +extern struct omap_vp_param omap3_core_vp_data; + +extern struct omap_vp_param omap4_mpu_vp_data; +extern struct omap_vp_param omap4_iva_vp_data; +extern struct omap_vp_param omap4_core_vp_data; + +void omap_vp_init(struct voltagedomain *voltdm); +void omap_vp_enable(struct voltagedomain *voltdm); +void omap_vp_disable(struct voltagedomain *voltdm); +int omap_vp_forceupdate_scale(struct voltagedomain *voltdm, + unsigned long target_volt); +int omap_vp_update_errorgain(struct voltagedomain *voltdm, + unsigned long target_volt); + +#endif diff --git a/arch/arm/mach-omap2/vp3xxx_data.c b/arch/arm/mach-omap2/vp3xxx_data.c new file mode 100644 index 000000000..9db9dd269 --- /dev/null +++ b/arch/arm/mach-omap2/vp3xxx_data.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 Voltage Processor (VP) data + * + * Copyright (C) 2007, 2010 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + */ + +#include <linux/io.h> +#include <linux/err.h> +#include <linux/init.h> + +#include "common.h" + +#include "prm-regbits-34xx.h" +#include "voltage.h" + +#include "vp.h" +#include "prm2xxx_3xxx.h" + +static const struct omap_vp_ops omap3_vp_ops = { + .check_txdone = omap_prm_vp_check_txdone, + .clear_txdone = omap_prm_vp_clear_txdone, +}; + +/* + * VP data common to 34xx/36xx chips + * XXX This stuff presumably belongs in the vp3xxx.c or vp.c file. + */ +static const struct omap_vp_common omap3_vp_common = { + .vpconfig_erroroffset_mask = OMAP3430_ERROROFFSET_MASK, + .vpconfig_errorgain_mask = OMAP3430_ERRORGAIN_MASK, + .vpconfig_initvoltage_mask = OMAP3430_INITVOLTAGE_MASK, + .vpconfig_timeouten = OMAP3430_TIMEOUTEN_MASK, + .vpconfig_initvdd = OMAP3430_INITVDD_MASK, + .vpconfig_forceupdate = OMAP3430_FORCEUPDATE_MASK, + .vpconfig_vpenable = OMAP3430_VPENABLE_MASK, + .vstepmin_smpswaittimemin_shift = OMAP3430_SMPSWAITTIMEMIN_SHIFT, + .vstepmax_smpswaittimemax_shift = OMAP3430_SMPSWAITTIMEMAX_SHIFT, + .vstepmin_stepmin_shift = OMAP3430_VSTEPMIN_SHIFT, + .vstepmax_stepmax_shift = OMAP3430_VSTEPMAX_SHIFT, + .vlimitto_vddmin_shift = OMAP3430_VDDMIN_SHIFT, + .vlimitto_vddmax_shift = OMAP3430_VDDMAX_SHIFT, + .vlimitto_timeout_shift = OMAP3430_TIMEOUT_SHIFT, + .vpvoltage_mask = OMAP3430_VPVOLTAGE_MASK, + + .ops = &omap3_vp_ops, +}; + +struct omap_vp_instance omap3_vp_mpu = { + .id = OMAP3_VP_VDD_MPU_ID, + .common = &omap3_vp_common, + .vpconfig = OMAP3_PRM_VP1_CONFIG_OFFSET, + .vstepmin = OMAP3_PRM_VP1_VSTEPMIN_OFFSET, + .vstepmax = OMAP3_PRM_VP1_VSTEPMAX_OFFSET, + .vlimitto = OMAP3_PRM_VP1_VLIMITTO_OFFSET, + .vstatus = OMAP3_PRM_VP1_STATUS_OFFSET, + .voltage = OMAP3_PRM_VP1_VOLTAGE_OFFSET, +}; + +struct omap_vp_instance omap3_vp_core = { + .id = OMAP3_VP_VDD_CORE_ID, + .common = &omap3_vp_common, + .vpconfig = OMAP3_PRM_VP2_CONFIG_OFFSET, + .vstepmin = OMAP3_PRM_VP2_VSTEPMIN_OFFSET, + .vstepmax = OMAP3_PRM_VP2_VSTEPMAX_OFFSET, + .vlimitto = OMAP3_PRM_VP2_VLIMITTO_OFFSET, + .vstatus = OMAP3_PRM_VP2_STATUS_OFFSET, + .voltage = OMAP3_PRM_VP2_VOLTAGE_OFFSET, +}; + +struct omap_vp_param omap3_mpu_vp_data = { + .vddmin = OMAP3430_VP1_VLIMITTO_VDDMIN, + .vddmax = OMAP3430_VP1_VLIMITTO_VDDMAX, +}; + +struct omap_vp_param omap3_core_vp_data = { + .vddmin = OMAP3430_VP2_VLIMITTO_VDDMIN, + .vddmax = OMAP3430_VP2_VLIMITTO_VDDMAX, +}; diff --git a/arch/arm/mach-omap2/vp44xx_data.c b/arch/arm/mach-omap2/vp44xx_data.c new file mode 100644 index 000000000..ec7104ec8 --- /dev/null +++ b/arch/arm/mach-omap2/vp44xx_data.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * OMAP3 Voltage Processor (VP) data + * + * Copyright (C) 2007, 2010 Texas Instruments, Inc. + * Rajendra Nayak <rnayak@ti.com> + * Lesly A M <x0080970@ti.com> + * Thara Gopinath <thara@ti.com> + * + * Copyright (C) 2008, 2011 Nokia Corporation + * Kalle Jokiniemi + * Paul Walmsley + */ + +#include <linux/io.h> +#include <linux/err.h> +#include <linux/init.h> + +#include "common.h" + +#include "prm44xx.h" +#include "prm-regbits-44xx.h" +#include "voltage.h" + +#include "vp.h" + +static const struct omap_vp_ops omap4_vp_ops = { + .check_txdone = omap_prm_vp_check_txdone, + .clear_txdone = omap_prm_vp_clear_txdone, +}; + +/* + * VP data common to 44xx chips + * XXX This stuff presumably belongs in the vp44xx.c or vp.c file. + */ +static const struct omap_vp_common omap4_vp_common = { + .vpconfig_erroroffset_mask = OMAP4430_ERROROFFSET_MASK, + .vpconfig_errorgain_mask = OMAP4430_ERRORGAIN_MASK, + .vpconfig_initvoltage_mask = OMAP4430_INITVOLTAGE_MASK, + .vpconfig_timeouten = OMAP4430_TIMEOUTEN_MASK, + .vpconfig_initvdd = OMAP4430_INITVDD_MASK, + .vpconfig_forceupdate = OMAP4430_FORCEUPDATE_MASK, + .vpconfig_vpenable = OMAP4430_VPENABLE_MASK, + .vstepmin_smpswaittimemin_shift = OMAP4430_SMPSWAITTIMEMIN_SHIFT, + .vstepmax_smpswaittimemax_shift = OMAP4430_SMPSWAITTIMEMAX_SHIFT, + .vstepmin_stepmin_shift = OMAP4430_VSTEPMIN_SHIFT, + .vstepmax_stepmax_shift = OMAP4430_VSTEPMAX_SHIFT, + .vlimitto_vddmin_shift = OMAP4430_VDDMIN_SHIFT, + .vlimitto_vddmax_shift = OMAP4430_VDDMAX_SHIFT, + .vlimitto_timeout_shift = OMAP4430_TIMEOUT_SHIFT, + .vpvoltage_mask = OMAP4430_VPVOLTAGE_MASK, + .ops = &omap4_vp_ops, +}; + +struct omap_vp_instance omap4_vp_mpu = { + .id = OMAP4_VP_VDD_MPU_ID, + .common = &omap4_vp_common, + .vpconfig = OMAP4_PRM_VP_MPU_CONFIG_OFFSET, + .vstepmin = OMAP4_PRM_VP_MPU_VSTEPMIN_OFFSET, + .vstepmax = OMAP4_PRM_VP_MPU_VSTEPMAX_OFFSET, + .vlimitto = OMAP4_PRM_VP_MPU_VLIMITTO_OFFSET, + .vstatus = OMAP4_PRM_VP_MPU_STATUS_OFFSET, + .voltage = OMAP4_PRM_VP_MPU_VOLTAGE_OFFSET, +}; + +struct omap_vp_instance omap4_vp_iva = { + .id = OMAP4_VP_VDD_IVA_ID, + .common = &omap4_vp_common, + .vpconfig = OMAP4_PRM_VP_IVA_CONFIG_OFFSET, + .vstepmin = OMAP4_PRM_VP_IVA_VSTEPMIN_OFFSET, + .vstepmax = OMAP4_PRM_VP_IVA_VSTEPMAX_OFFSET, + .vlimitto = OMAP4_PRM_VP_IVA_VLIMITTO_OFFSET, + .vstatus = OMAP4_PRM_VP_IVA_STATUS_OFFSET, + .voltage = OMAP4_PRM_VP_IVA_VOLTAGE_OFFSET, +}; + +struct omap_vp_instance omap4_vp_core = { + .id = OMAP4_VP_VDD_CORE_ID, + .common = &omap4_vp_common, + .vpconfig = OMAP4_PRM_VP_CORE_CONFIG_OFFSET, + .vstepmin = OMAP4_PRM_VP_CORE_VSTEPMIN_OFFSET, + .vstepmax = OMAP4_PRM_VP_CORE_VSTEPMAX_OFFSET, + .vlimitto = OMAP4_PRM_VP_CORE_VLIMITTO_OFFSET, + .vstatus = OMAP4_PRM_VP_CORE_STATUS_OFFSET, + .voltage = OMAP4_PRM_VP_CORE_VOLTAGE_OFFSET, +}; + +struct omap_vp_param omap4_mpu_vp_data = { + .vddmin = OMAP4_VP_MPU_VLIMITTO_VDDMIN, + .vddmax = OMAP4_VP_MPU_VLIMITTO_VDDMAX, +}; + +struct omap_vp_param omap4_iva_vp_data = { + .vddmin = OMAP4_VP_IVA_VLIMITTO_VDDMIN, + .vddmax = OMAP4_VP_IVA_VLIMITTO_VDDMAX, +}; + +struct omap_vp_param omap4_core_vp_data = { + .vddmin = OMAP4_VP_CORE_VLIMITTO_VDDMIN, + .vddmax = OMAP4_VP_CORE_VLIMITTO_VDDMAX, +}; diff --git a/arch/arm/mach-omap2/wd_timer.c b/arch/arm/mach-omap2/wd_timer.c new file mode 100644 index 000000000..d4ea56a5f --- /dev/null +++ b/arch/arm/mach-omap2/wd_timer.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OMAP2+ MPU WD_TIMER-specific code + * + * Copyright (C) 2012 Texas Instruments, Inc. + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/err.h> + +#include <linux/platform_data/omap-wd-timer.h> + +#include "omap_hwmod.h" +#include "omap_device.h" +#include "wd_timer.h" +#include "common.h" +#include "prm.h" +#include "soc.h" + +/* + * In order to avoid any assumptions from bootloader regarding WDT + * settings, WDT module is reset during init. This enables the watchdog + * timer. Hence it is required to disable the watchdog after the WDT reset + * during init. Otherwise the system would reboot as per the default + * watchdog timer registers settings. + */ +#define OMAP_WDT_WPS 0x34 +#define OMAP_WDT_SPR 0x48 + +int omap2_wd_timer_disable(struct omap_hwmod *oh) +{ + void __iomem *base; + + if (!oh) { + pr_err("%s: Could not look up wdtimer_hwmod\n", __func__); + return -EINVAL; + } + + base = omap_hwmod_get_mpu_rt_va(oh); + if (!base) { + pr_err("%s: Could not get the base address for %s\n", + oh->name, __func__); + return -EINVAL; + } + + /* sequence required to disable watchdog */ + writel_relaxed(0xAAAA, base + OMAP_WDT_SPR); + while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10) + cpu_relax(); + + writel_relaxed(0x5555, base + OMAP_WDT_SPR); + while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10) + cpu_relax(); + + return 0; +} + +/** + * omap2_wdtimer_reset - reset and disable the WDTIMER IP block + * @oh: struct omap_hwmod * + * + * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take + * care to execute the special watchdog disable sequence. This is + * because the watchdog is re-armed upon OCP softreset. (On OMAP4, + * this behavior was apparently changed and the watchdog is no longer + * re-armed after an OCP soft-reset.) Returns -ETIMEDOUT if the reset + * did not complete, or 0 upon success. + * + * XXX Most of this code should be moved to the omap_hwmod.c layer + * during a normal merge window. omap_hwmod_softreset() should be + * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset() + * should call the hwmod _ocp_softreset() code. + */ +int omap2_wd_timer_reset(struct omap_hwmod *oh) +{ + int c = 0; + + /* Write to the SOFTRESET bit */ + omap_hwmod_softreset(oh); + + /* Poll on RESETDONE bit */ + omap_test_timeout((omap_hwmod_read(oh, + oh->class->sysc->syss_offs) + & SYSS_RESETDONE_MASK), + MAX_MODULE_SOFTRESET_WAIT, c); + + if (oh->class->sysc->srst_udelay) + udelay(oh->class->sysc->srst_udelay); + + if (c == MAX_MODULE_SOFTRESET_WAIT) + pr_warn("%s: %s: softreset failed (waited %d usec)\n", + __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT); + else + pr_debug("%s: %s: softreset in %d usec\n", __func__, + oh->name, c); + + return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : + omap2_wd_timer_disable(oh); +} diff --git a/arch/arm/mach-omap2/wd_timer.h b/arch/arm/mach-omap2/wd_timer.h new file mode 100644 index 000000000..fa44a07a5 --- /dev/null +++ b/arch/arm/mach-omap2/wd_timer.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * OMAP2+ MPU WD_TIMER-specific function prototypes + */ + +#ifndef __ARCH_ARM_MACH_OMAP2_WD_TIMER_H +#define __ARCH_ARM_MACH_OMAP2_WD_TIMER_H + +#include "omap_hwmod.h" + +extern int omap2_wd_timer_disable(struct omap_hwmod *oh); +extern int omap2_wd_timer_reset(struct omap_hwmod *oh); + +#endif |