summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRabin Vincent <rabin.vincent@stericsson.com>2011-08-25 12:57:55 +0530
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 16:00:16 +0200
commit389b5c26ad9b88b50e1d24de6cfe3427e7d6cef0 (patch)
tree98ebc53bf43e379ebe1604d837300cd88a1d1350
parent4cf5a02d90afba6c02a6ffae5ed0f7919c7bd34a (diff)
ux500: pm: update
commit 1d3e28fa4e85d54434614575f37a4171e32dfe0c Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Date: Tue Aug 23 12:28:26 2011 +0200 [Android]: pm: usecase: limit cpu frequency to 400MHz during voice-call ST Ericsson ID: 357594 commit 70f52392190a111758836d5fca7fb533ad8b4453 Author: Rickard Andersson <rickard.andersson@stericsson.com> Date: Tue Aug 23 11:05:58 2011 +0200 ux500: cpuidle: Correct PRCMU error messages Show correct error messages when PRCMU requested sleep fails. ST-Ericsson ID: - commit 912e987c1e46815c8cf128bce3d7077d3b05e7d6 Author: Rickard Andersson <rickard.andersson@stericsson.com> Date: Tue Aug 23 11:00:01 2011 +0200 ux500: cpuidle: Optimize stack sizes Optimze the stack sizes for arm registers and CP15 registers ST-Ericsson ID: - commit df0c08abadbdbe19c8c00ca09c3197f3fb0ead29 Author: Rickard Andersson <rickard.andersson@stericsson.com> Date: Mon Aug 22 11:19:13 2011 +0200 ux500: cpuidle: GIC freeze delay optimized GIC freeze delay was previously unnecessary long. ST-Ericsson ID: - commit a28fec98e99d5115b62d3d704452f427ca2a20ac Author: Rajagopala V <rajagopala.v@stericsson.com> Date: Wed Aug 24 13:16:39 2011 +0530 u5500: cpuidle: fix coverity warning check for clockevents_program_event return value in cpuidle driver during wakeup ST-Ericsson ID: ER356883 commit b716ed3140664c6b4b80381b18945032b792b853 Author: Rabin Vincent <rabin.vincent@stericsson.com> Date: Fri Aug 19 14:32:29 2011 +0530 u5500: enable PRCMU QoS and debugging Dummy implementations are provided to get it to build. ST-Ericsson ID: 348762 commit 34cd29c2f8cd1ce8fa51be6d33624b441416d478 Author: om prakash <omprakash.pal@stericsson.com> Date: Tue Aug 23 14:08:52 2011 +0530 cpuidle:Removed the CHECKED_RETURN error Removed the CHECKED_RETURN coverity error in cpuidle. ST-Ericsson ID: 354434 commit d9d500b6742ff1fcb12747cb55d7f26c89ef5c96 Author: Rabin Vincent <rabin.vincent@stericsson.com> Date: Mon Aug 22 10:23:36 2011 +0530 u5500: support cpufreq only on v2 Support cpufreq only on 5500v2+ only, since that is where the other power management features will be enabled. ST-Ericsson ID: 355981 commit 74cf0e658bb9abe240d04427d9043f145dd0505b Author: Hemanth Puranik <hemanth.puranik@stericsson.com> Date: Wed Aug 3 10:29:10 2011 +0530 U5500: Print PRCMU firware version ST-Ericsson ID: WP332193 commit bb04cadb6948a3a68f409fa7828457ce64540172 Author: Vijaya Kumar Kilari <vijay.kilari@stericsson.com> Date: Thu Aug 11 11:49:27 2011 +0530 U5500: Add MSP1 and Cable detect clock support MSP1 and CD clocks are managed by PRCMU FW so special handling for these clocks are required ST-Ericsson ID: 332193 commit 0d9b20560a729b4674d9f55c77572db04272edbd Author: Rajagopala V <rajagopala.v@stericsson.com> Date: Fri Aug 5 16:52:41 2011 +0530 u5500: prcmu: add irqs for db5500 temperature sensor add irqs to support db5500 temp sensor high and low interrupts so as to clearly distinguish whether sensor temperature has crossed min/max values ST-Ericsson ID: WP257616 commit 63877225cb09c38d0fbea2dcf01eb6670eb05549 Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Wed Jul 13 09:01:22 2011 +0200 ARM: u8500: pm: Do not touch TPIU registers if JTAG disabled If JTAG is disabled, the Linux cannot touch the TPIU registers. ST-Ericsson ID: 349265 commit 9fcee5876ff138e85356b63d392b2a050a68601e Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Mon Jul 11 15:27:49 2011 +0200 ARM: ux500: prcmu-debug: Add ARM opp to debugfs Add arm opp to debugfs interface, plus some code clean-up/simplification. ST-Ericsson ID: - commit 0e1f18385d2416e03af308cac1e38997f6bbf044 Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Thu Jul 14 12:49:30 2011 +0200 ARM: ux500: context: Do not touch TPIU when not clocked ST-Ericsson ID: 352300 commit 0ebdb6b6b2a7f9924bd2785654f0abfb1473dda0 Author: Jayarami Reddy <jayarami.reddy@stericsson.com> Date: Thu Jul 14 14:39:07 2011 +0530 u5500: fix to boot the kernel in DB5500 commit a20f6a9088ac5d34634cd26207dd5a072f04c37b Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Mon Jul 11 15:26:40 2011 +0200 ARM: ux500: prcmu-qos: Add ARM OPP qos Make it possible to request lowest ARM OPP. Must be bound to cpufreq to actually do something. ST-Ericsson ID: - commit be6842df3ee21a9aa00216c84e93a643b4ddeabe Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Wed Jul 13 09:00:30 2011 +0200 ARM: ux500: Read product settings at boot ST-Ericsson ID: 349265 commit 94843c6131bac39aa8e7d0ec4c9c2b34e2819c92 Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Wed Jul 13 08:56:38 2011 +0200 drivers: tee: Update with product id configuration Add structs and defines needed to detect product settings. ST-Ericsson ID: 349265 commit 4c70d615b782965c064b3f97963eff5a290acf68 Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Wed Jul 6 08:26:41 2011 +0200 ARM: ux500: prcmu-wdog: Remove check for fw bug In prcmu fw version 3.4.4 the issue with longer intervalls than 131 s was fixed. ST-Ericsson ID: - commit f6ccbf262ddebe39584b4a4c01cfa16af1fbfac6 Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Tue Jul 12 08:53:42 2011 +0200 ARM: ux500: cpuidle: Remove duplicated ApIdle state The ARM PLL is handled automatic by the prcmu fw and cpuidle cannot affect it. Remove duplicated ApIdle state that does the same as the other ApIdle state. ST-Ericsson ID: - commit bdf3c3dd8c22ffe0fbd8c674e8e6bdab83a5ec01 Author: Rabin Vincent <rabin.vincent@stericsson.com> Date: Fri Jul 8 14:33:23 2011 +0530 ux500: support ApDeepSleep on 5500 ST-Ericsson ID: 332193 commit 5326d7744cd226e67253774f30a2bb57c9b2badc Author: Rabin Vincent <rabin.vincent@stericsson.com> Date: Mon Jul 11 11:27:45 2011 +0530 u5500: fix prcmu_get_arm_opp() prcmu_set_arm_opp() maps between logical ARM OPP values and PRCMU firmware values, so prcmu_get_arm_opp() should do it too. ST-Ericsson ID: 332226 commit 6422856927e1230197ae674795eff6538df885e5 Author: Jonas Aaberg <jonas.aberg@stericsson.com> Date: Thu Jul 7 08:33:59 2011 +0200 ARM: u8500: prcmu-dbg: display AVS settings Add debugfs node showing AVS settings. root@ME:/ cat /debugfs/prcmu/avs VBB_RET : 0x 0 VBB_MAX_OPP : 0xdb VBB_100_OPP : 0xdb VBB_50_OPP : 0xdb VARM_MAX_OPP : 0x2f VARM_100_OPP : 0x2e VARM_50_OPP : 0x1d VARM_RET : 0x 0 VAPE_100_OPP : 0x2a VAPE_50_OPP : 0x1a VMOD_100_OPP : 0x29 VMOD_50_OPP : 0x1a VSAFE : 0x29 ST-Ericsson ID: - commit 3184873f10bff0c7c54db75d9c2694e21ebc40b0 Author: Pawel Szyszuk <pawel.szyszuk@stericsson.com> Date: Wed Jun 22 16:03:29 2011 +0200 ARM: U5500: PRCMU CLKOUTx configuration API U5500 API for setting the programmable CLKOUTx source and divisor. New API used for setting the sources of camera clocks. ST-Ericsson ID: - commit 98e9cfc32a25f6cf3b5d4c3456bb6ff6a34ff9cb Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Date: Thu Jun 23 16:07:48 2011 +0200 [ANDROID]: pm: usecase: add sysfs interface to disable the governor Move all the code to control the state of the governor in a separate function. ST-Ericsson ID: CR339643 commit 6e9ab8ab3013d0b372a51d94d617d24fd8b38664 Author: Rickard Andersson <rickard.andersson@stericsson.com> Date: Tue Jun 7 13:04:08 2011 +0200 ARM: ux500: pm: Turn off unnecessary GIC IRQs in deep sleep In the sleep state ApDeepSleep turn off all IRQs in the GIC except for the PRCMU IRQs ST-Ericsson ID: ER338876 commit ba5f28731b27386cb90cf87c4e7b1910e09474a9 Author: Rickard Andersson <rickard.andersson@stericsson.com> Date: Fri May 27 08:56:47 2011 +0200 ARM: ux500: pm: Deepsleep bugfix and optimization Fixing deep sleep sync problems. Also cache clean and saves to backup RAM is now only done when really needed. ST-Ericsson ID: ER338876 commit 0415c755958de0d613a9bd52f73b820cb3a2b916 Author: Rabin Vincent <rabin.vincent@stericsson.com> Date: Tue Jun 28 18:18:36 2011 +0530 u5500: handle SDMMC0 clock change on DB5500v2 On DB5500v2, SDMMC0 is parented to SPARE1CLK instead of SDMMCCLK. Also, correct the PRCM_IRDACLK_MGT register address which is wrong even for v1. ST-Ericsson ID: 349062 commit 2036360d62f3c3f0cd722d751ba90a8739034c0f Author: Rabin Vincent <rabin.vincent@stericsson.com> Date: Mon Jun 27 20:25:06 2011 +0530 u5500: cpufreq: DB5500v2 support ST-Ericsson ID: 349062 commit 190b11834463e835de2d792116dfd8673d775752 Author: Rabin Vincent <rabin.vincent@stericsson.com> Date: Wed Jun 22 07:45:00 2011 +0530 u5500: allow SUSPEND_STANDBY and CPUIDLE to be enabled ST-Ericsson ID: 332226 commit 7c4906d6ee888df46baa64b690dfdfaf44502d86 Author: Pawel Szyszuk <pawel.szyszuk@stericsson.com> Date: Mon Jun 20 15:28:34 2011 +0200 ARM: u5500: PRCMU reset API Added API for rebooting the board and for getting the last reboot code. ST-Ericsson ID: 341245 Change-Id: Ibbcd9e3528cd605c724b9c2c88ae3b41a27f2f1c
-rw-r--r--arch/arm/mach-ux500/Kconfig22
-rw-r--r--arch/arm/mach-ux500/Makefile8
-rw-r--r--arch/arm/mach-ux500/cpu-db8500.c1
-rw-r--r--arch/arm/mach-ux500/hotplug.c22
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu-db5500.h46
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu-db8500.h309
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu-fw-api.h16
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu-qos.h88
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu-regs.h113
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu.h251
-rw-r--r--arch/arm/mach-ux500/include/mach/regulator.h88
-rw-r--r--arch/arm/mach-ux500/pm/Kconfig13
-rw-r--r--arch/arm/mach-ux500/pm/Makefile8
-rw-r--r--arch/arm/mach-ux500/pm/context.c85
-rw-r--r--arch/arm/mach-ux500/pm/context.h9
-rw-r--r--[-rwxr-xr-x]arch/arm/mach-ux500/pm/context_arm.S56
-rw-r--r--arch/arm/mach-ux500/pm/cpufreq-db5500.c59
-rw-r--r--arch/arm/mach-ux500/pm/cpufreq-db8500.c150
-rw-r--r--arch/arm/mach-ux500/pm/cpufreq.c141
-rw-r--r--arch/arm/mach-ux500/pm/cpufreq.h14
-rw-r--r--arch/arm/mach-ux500/pm/cpuidle.c185
-rw-r--r--arch/arm/mach-ux500/pm/cpuidle.h6
-rw-r--r--arch/arm/mach-ux500/pm/cpuidle_dbg.c30
-rw-r--r--arch/arm/mach-ux500/pm/pm.c8
-rw-r--r--arch/arm/mach-ux500/pm/pm.h4
-rw-r--r--arch/arm/mach-ux500/pm/runtime.c2
-rw-r--r--arch/arm/mach-ux500/pm/suspend.c12
-rw-r--r--arch/arm/mach-ux500/pm/suspend_dbg.c6
-rw-r--r--arch/arm/mach-ux500/pm/timer.c10
-rw-r--r--arch/arm/mach-ux500/pm/usecase_gov.c167
-rw-r--r--arch/arm/mach-ux500/prcmu-db5500.c1618
-rw-r--r--arch/arm/mach-ux500/prcmu-db8500.c2280
-rw-r--r--arch/arm/mach-ux500/prcmu-debug.c508
-rw-r--r--arch/arm/mach-ux500/prcmu-debug.h1
-rw-r--r--arch/arm/mach-ux500/prcmu-qos-power.c136
-rw-r--r--arch/arm/mach-ux500/prcmu-regs-db5500.h123
-rw-r--r--arch/arm/mach-ux500/prcmu-regs-db8500.h175
-rw-r--r--arch/arm/mach-ux500/product.c92
-rw-r--r--arch/arm/mach-ux500/product.h26
-rw-r--r--arch/arm/mach-ux500/regulator-u5500.c84
-rw-r--r--arch/arm/mach-ux500/regulator-u5500.h20
-rw-r--r--arch/arm/mach-ux500/regulator-u8500.c266
-rw-r--r--arch/arm/mach-ux500/regulator-u8500.h46
-rw-r--r--arch/arm/mach-ux500/regulator-ux500.c416
-rw-r--r--drivers/cpufreq/Makefile4
-rw-r--r--drivers/mfd/Kconfig2
-rw-r--r--drivers/tee/tee_driver.c2
-rw-r--r--include/linux/tee.h36
48 files changed, 6892 insertions, 872 deletions
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index 90104375ffa..389c82e3970 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -16,14 +16,10 @@ menu "Ux500 SoC"
config UX500_SOC_DB5500
bool "DB5500"
- select MFD_DB5500_PRCMU
- select REGULATOR_DB5500_PRCMU
select UX500_SOC_DBX500
config UX500_SOC_DB8500
bool "DB8500"
- select MFD_DB8500_PRCMU
- select REGULATOR_DB8500_PRCMU
select UX500_SOC_DBX500
endmenu
@@ -96,15 +92,31 @@ config UX500_MTU_TIMER
endchoice
+config U8500_PRCMU
+ bool "U8500 PRCMU support"
+ depends on UX500_SOC_DB8500
+ default n
+ help
+ Add support for PRCMU for U8500
+
+config U5500_PRCMU
+ bool "U5500 PRCMU support"
+ depends on UX500_SOC_DB5500
+ default y
+ help
+ Add support for PRCMU for U5500
+
config UX500_PRCMU_QOS_POWER
bool "UX500 PRCMU power QoS support"
+ depends on (U5500_PRCMU || U8500_PRCMU)
default y
help
Add support for PRCMU power Quality of Service
config UX500_PRCMU_DEBUG
bool "PRCMU debug"
- depends on DEBUG_FS
+ depends on ((U5500_PRCMU || U8500_PRCMU) && DEBUG_FS)
+ default n
help
Add support for PRCMU debug
diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile
index d4fff1cdcb3..4e3105040e0 100644
--- a/arch/arm/mach-ux500/Makefile
+++ b/arch/arm/mach-ux500/Makefile
@@ -6,12 +6,17 @@ obj-y := clock.o cpu.o devices.o dcache.o \
devices-common.o id.o pins.o \
timer.o timer-mtu.o usb.o reboot_reasons.o
obj-y += pm/
+
+obj-$(CONFIG_REGULATOR) += regulator-ux500.o
+
ifeq ($(CONFIG_UX500_SOC_DB5500), y)
+obj-$(CONFIG_REGULATOR) += regulator-u5500.o
obj-$(CONFIG_UX500_SOC_DBX500) += cpu-db5500.o dma-db5500.o \
devices-db5500.o clock-db5500.o \
clock-debug.o
endif
ifeq ($(CONFIG_UX500_SOC_DB8500), y)
+obj-$(CONFIG_REGULATOR) += regulator-u8500.o
obj-$(CONFIG_UX500_SOC_DBX500) += cpu-db8500.o devices-db8500.o \
clock-db8500.o dma-db8500.o
endif
@@ -34,11 +39,12 @@ obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
obj-$(CONFIG_U5500_MODEM_IRQ) += modem-irq-db5500.o
-obj-$(CONFIG_TEE_UX500) += tee_ux500.o
+obj-$(CONFIG_TEE_UX500) += tee_ux500.o product.o
obj-$(CONFIG_TEE_SVP) += tee_service_svp.o
obj-$(CONFIG_TEE_SVP) += tee_ta_start_modem_svp.o
obj-$(CONFIG_DB8500_MLOADER) += mloader-db8500.o
obj-$(CONFIG_U5500_MLOADER) += mloader-db5500.o
+obj-$(CONFIG_U8500_PRCMU) += prcmu-db8500.o
obj-$(CONFIG_UX500_PRCMU_TIMER) += timer-prcmu.o
obj-$(CONFIG_U8500_REGULATOR_DEBUG) += virt-regulator-u8500.o
obj-$(CONFIG_UX500_PRCMU_DEBUG) += prcmu-debug.o
diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c
index de0f3cb6072..c05a39cf5ce 100644
--- a/arch/arm/mach-ux500/cpu-db8500.c
+++ b/arch/arm/mach-ux500/cpu-db8500.c
@@ -47,6 +47,7 @@ static struct map_desc u8500_io_desc[] __initdata = {
__IO_DEV_DESC(U8500_RTC_BASE, SZ_4K),
__IO_DEV_DESC(U8500_SCU_BASE, SZ_4K),
__IO_DEV_DESC(U8500_BACKUPRAM0_BASE, SZ_8K),
+ __MEM_DEV_DESC(U8500_BOOT_ROM_BASE, SZ_1M),
__IO_DEV_DESC(U8500_CLKRST1_BASE, SZ_4K),
__IO_DEV_DESC(U8500_CLKRST2_BASE, SZ_4K),
diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c
index 607567f2618..cc88c5309b7 100644
--- a/arch/arm/mach-ux500/hotplug.c
+++ b/arch/arm/mach-ux500/hotplug.c
@@ -26,29 +26,19 @@ static inline void platform_do_lowpower(unsigned int cpu)
flush_cache_all();
for (;;) {
-#ifndef CONFIG_U8500_CPUIDLE
- __asm__ __volatile__("dsb\n\t" "wfi\n\t"
- : : : "memory");
-#endif
-
context_varm_save_core();
context_save_cpu_registers();
- context_save_to_sram_and_wfi(true, false);
+ context_save_to_sram_and_wfi(false);
context_restore_cpu_registers();
context_varm_restore_core();
- /* we put the platform to just WFI */
- for (;;) {
- __asm__ __volatile__("dsb\n\t" "wfi\n\t"
- : : : "memory");
- if (pen_release == cpu) {
- /*
- * OK, proper wakeup, we're done
- */
- break;
- }
+ if (pen_release == cpu) {
+ /*
+ * OK, proper wakeup, we're done
+ */
+ break;
}
}
}
diff --git a/arch/arm/mach-ux500/include/mach/prcmu-db5500.h b/arch/arm/mach-ux500/include/mach/prcmu-db5500.h
new file mode 100644
index 00000000000..ae76fef0e44
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/prcmu-db5500.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * U5500 PRCMU API.
+ */
+#ifndef __MACH_PRCMU_U5500_H
+#define __MACH_PRCMU_U5500_H
+
+#ifdef CONFIG_U5500_PRCMU
+
+int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
+int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
+
+int db5500_prcmu_request_clock(u8 clock, bool enable);
+
+int prcmu_resetout(u8 resoutn, u8 state);
+
+unsigned int prcmu_get_ddr_freq(void);
+
+#else /* !CONFIG_U5500_PRCMU */
+
+static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ return -ENOSYS;
+}
+
+static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ return -ENOSYS;
+}
+
+static inline int prcmu_resetout(u8 resoutn, u8 state)
+{
+ return 0;
+}
+
+static inline unsigned int prcmu_get_ddr_freq(void)
+{
+ return 0;
+}
+
+#endif /* CONFIG_U5500_PRCMU */
+
+#endif /* __MACH_PRCMU_U5500_H */
diff --git a/arch/arm/mach-ux500/include/mach/prcmu-db8500.h b/arch/arm/mach-ux500/include/mach/prcmu-db8500.h
new file mode 100644
index 00000000000..572d68b7213
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/prcmu-db8500.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * U8500 specific PRCMU API.
+ */
+#ifndef __MACH_PRCMU_DB8500_H
+#define __MACH_PRCMU_DB8500_H
+
+/*
+ * Definitions for autonomous power management configuration.
+ */
+
+#define PRCMU_AUTO_PM_OFF 0
+#define PRCMU_AUTO_PM_ON 1
+
+#define PRCMU_AUTO_PM_POWER_ON_HSEM BIT(0)
+#define PRCMU_AUTO_PM_POWER_ON_ABB_FIFO_IT BIT(1)
+
+enum prcmu_auto_pm_policy {
+ PRCMU_AUTO_PM_POLICY_NO_CHANGE,
+ PRCMU_AUTO_PM_POLICY_DSP_OFF_HWP_OFF,
+ PRCMU_AUTO_PM_POLICY_DSP_OFF_RAMRET_HWP_OFF,
+ PRCMU_AUTO_PM_POLICY_DSP_CLK_OFF_HWP_OFF,
+ PRCMU_AUTO_PM_POLICY_DSP_CLK_OFF_HWP_CLK_OFF,
+};
+
+/**
+ * struct prcmu_auto_pm_config - Autonomous power management configuration.
+ * @sia_auto_pm_enable: SIA autonomous pm enable. (PRCMU_AUTO_PM_{OFF,ON})
+ * @sia_power_on: SIA power ON enable. (PRCMU_AUTO_PM_POWER_ON_* bitmask)
+ * @sia_policy: SIA power policy. (enum prcmu_auto_pm_policy)
+ * @sva_auto_pm_enable: SVA autonomous pm enable. (PRCMU_AUTO_PM_{OFF,ON})
+ * @sva_power_on: SVA power ON enable. (PRCMU_AUTO_PM_POWER_ON_* bitmask)
+ * @sva_policy: SVA power policy. (enum prcmu_auto_pm_policy)
+ */
+struct prcmu_auto_pm_config {
+ u8 sia_auto_pm_enable;
+ u8 sia_power_on;
+ u8 sia_policy;
+ u8 sva_auto_pm_enable;
+ u8 sva_power_on;
+ u8 sva_policy;
+};
+
+/**
+ * enum hw_acc_dev - enum for hw accelerators
+ * @HW_ACC_SVAMMDSP: for SVAMMDSP
+ * @HW_ACC_SVAPIPE: for SVAPIPE
+ * @HW_ACC_SIAMMDSP: for SIAMMDSP
+ * @HW_ACC_SIAPIPE: for SIAPIPE
+ * @HW_ACC_SGA: for SGA
+ * @HW_ACC_B2R2: for B2R2
+ * @HW_ACC_MCDE: for MCDE
+ * @HW_ACC_ESRAM1: for ESRAM1
+ * @HW_ACC_ESRAM2: for ESRAM2
+ * @HW_ACC_ESRAM3: for ESRAM3
+ * @HW_ACC_ESRAM4: for ESRAM4
+ * @NUM_HW_ACC: number of hardware accelerators
+ *
+ * Different hw accelerators which can be turned ON/
+ * OFF or put into retention (MMDSPs and ESRAMs).
+ * Used with EPOD API.
+ *
+ * NOTE! Deprecated, to be removed when all users switched over to use the
+ * regulator API.
+ */
+enum hw_acc_dev{
+ HW_ACC_SVAMMDSP,
+ HW_ACC_SVAPIPE,
+ HW_ACC_SIAMMDSP,
+ HW_ACC_SIAPIPE,
+ HW_ACC_SGA,
+ HW_ACC_B2R2,
+ HW_ACC_MCDE,
+ HW_ACC_ESRAM1,
+ HW_ACC_ESRAM2,
+ HW_ACC_ESRAM3,
+ HW_ACC_ESRAM4,
+ NUM_HW_ACC
+};
+
+/**
+ * enum hw_acc_state - State definition for hardware accelerator
+ * @HW_NO_CHANGE: The hardware accelerator state must remain unchanged
+ * @HW_OFF: The hardware accelerator must be switched off
+ * @HW_OFF_RAMRET: The hardware accelerator must be switched off with its
+ * internal RAM in retention
+ * @HW_ON: The hwa hardware accelerator hwa must be switched on
+ *
+ * NOTE! Deprecated, to be removed when all users switched over to use the
+ * regulator API.
+ */
+enum hw_acc_state {
+ HW_NO_CHANGE = 0x00,
+ HW_OFF = 0x01,
+ HW_OFF_RAMRET = 0x02,
+ HW_ON = 0x04
+};
+
+/**
+ * enum romcode_write - Romcode message written by A9 AND read by XP70
+ * @RDY_2_DS: Value set when ApDeepSleep state can be executed by XP70
+ * @RDY_2_XP70_RST: Value set when 0x0F has been successfully polled by the
+ * romcode. The xp70 will go into self-reset
+ */
+enum romcode_write {
+ RDY_2_DS = 0x09,
+ RDY_2_XP70_RST = 0x10
+};
+
+/**
+ * enum romcode_read - Romcode message written by XP70 and read by A9
+ * @INIT: Init value when romcode field is not used
+ * @FS_2_DS: Value set when power state is going from ApExecute to
+ * ApDeepSleep
+ * @END_DS: Value set when ApDeepSleep power state is reached coming from
+ * ApExecute state
+ * @DS_TO_FS: Value set when power state is going from ApDeepSleep to
+ * ApExecute
+ * @END_FS: Value set when ApExecute power state is reached coming from
+ * ApDeepSleep state
+ * @SWR: Value set when power state is going to ApReset
+ * @END_SWR: Value set when the xp70 finished executing ApReset actions and
+ * waits for romcode acknowledgment to go to self-reset
+ */
+enum romcode_read {
+ INIT = 0x00,
+ FS_2_DS = 0x0A,
+ END_DS = 0x0B,
+ DS_TO_FS = 0x0C,
+ END_FS = 0x0D,
+ SWR = 0x0E,
+ END_SWR = 0x0F
+};
+
+/**
+ * enum ap_pwrst - current power states defined in PRCMU firmware
+ * @NO_PWRST: Current power state init
+ * @AP_BOOT: Current power state is apBoot
+ * @AP_EXECUTE: Current power state is apExecute
+ * @AP_DEEP_SLEEP: Current power state is apDeepSleep
+ * @AP_SLEEP: Current power state is apSleep
+ * @AP_IDLE: Current power state is apIdle
+ * @AP_RESET: Current power state is apReset
+ */
+enum ap_pwrst {
+ NO_PWRST = 0x00,
+ AP_BOOT = 0x01,
+ AP_EXECUTE = 0x02,
+ AP_DEEP_SLEEP = 0x03,
+ AP_SLEEP = 0x04,
+ AP_IDLE = 0x05,
+ AP_RESET = 0x06
+};
+
+#ifdef CONFIG_U8500_PRCMU
+
+bool prcmu_is_u8400(void);
+
+int prcmu_request_ape_opp_100_voltage(bool enable);
+
+int prcmu_release_usb_wakeup_state(void);
+
+int prcmu_set_clock_divider(u8 clock, u8 divider);
+
+void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep,
+ struct prcmu_auto_pm_config *idle);
+bool prcmu_is_auto_pm_enabled(void);
+
+/* NOTE! Use regulator framework instead */
+int prcmu_set_hwacc(u16 hw_acc_dev, u8 state);
+
+/* TODO: Check if anyone is using these. */
+int prcmu_set_rc_a2p(enum romcode_write);
+enum romcode_read prcmu_get_rc_p2a(void);
+enum ap_pwrst prcmu_get_xp70_current_state(void);
+
+/* TODO: Common API with DB5500? */
+bool prcmu_has_arm_maxopp(void);
+int prcmu_config_hotdog(u8 threshold);
+int prcmu_config_hotmon(u8 low, u8 high);
+int prcmu_start_temp_sense(u16 cycles32k);
+int prcmu_stop_temp_sense(void);
+void prcmu_enable_spi2(void);
+void prcmu_disable_spi2(void);
+
+int prcmu_config_a9wdog(u8 num, bool sleep_auto_off);
+int prcmu_enable_a9wdog(u8 id);
+int prcmu_disable_a9wdog(u8 id);
+int prcmu_kick_a9wdog(u8 id);
+int prcmu_load_a9wdog(u8 id, u32 val);
+
+#else /* !CONFIG_U8500_PRCMU */
+
+static inline bool prcmu_is_u8400(void)
+{
+ return false;
+}
+
+static inline int prcmu_request_ape_opp_100_voltage(bool enable)
+{
+ return 0;
+}
+
+static inline int prcmu_release_usb_wakeup_state(void)
+{
+ return 0;
+}
+
+static inline int prcmu_set_clock_divider(u8 clock, u8 divider)
+{
+ return 0;
+}
+
+static inline void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep,
+ struct prcmu_auto_pm_config *idle)
+{
+}
+
+static inline bool prcmu_is_auto_pm_enabled(void)
+{
+ return false;
+}
+
+static inline int prcmu_set_hwacc(u16 hw_acc_dev, u8 state)
+{
+ return 0;
+}
+
+static inline int prcmu_set_rc_a2p(enum romcode_write code)
+{
+ return 0;
+}
+
+static inline enum romcode_read prcmu_get_rc_p2a(void)
+{
+ return INIT;
+}
+
+static inline enum ap_pwrst prcmu_get_xp70_current_state(void)
+{
+ return AP_EXECUTE;
+}
+
+static inline bool prcmu_has_arm_maxopp(void)
+{
+ return false;
+}
+
+static inline int prcmu_config_hotdog(u8 threshold)
+{
+ return 0;
+}
+
+static inline int prcmu_config_hotmon(u8 low, u8 high)
+{
+ return 0;
+}
+
+static inline int prcmu_start_temp_sense(u16 cycles32k)
+{
+ return 0;
+}
+
+static inline int prcmu_stop_temp_sense(void)
+{
+ return 0;
+}
+
+static inline int prcmu_enable_spi2(void)
+{
+ return 0;
+}
+
+static inline int prcmu_disable_spi2(void)
+{
+ return 0;
+}
+
+static inline int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+ return 0;
+}
+
+static inline int prcmu_enable_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int prcmu_disable_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int prcmu_kick_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int prcmu_load_a9wdog(u8 id, u32 val)
+{
+ return 0;
+}
+
+#endif /* CONFIG_U8500_PRCMU */
+
+#endif /* __MACH_PRCMU_DB8500_H */
diff --git a/arch/arm/mach-ux500/include/mach/prcmu-fw-api.h b/arch/arm/mach-ux500/include/mach/prcmu-fw-api.h
new file mode 100644
index 00000000000..7995ed1f77f
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/prcmu-fw-api.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
+ *
+ * PRCMU f/w APIs
+ */
+#ifndef __MACH_PRCMU_FW_API_H
+#define __MACH_PRCMU_FW_API_H
+
+#warning <mach/prcmu-fw-api.h> has been replaced by <mach/prcmu.h>
+#include <mach/prcmu.h>
+
+#endif /* __MACH_PRCMU_FW_API_H */
diff --git a/arch/arm/mach-ux500/include/mach/prcmu-qos.h b/arch/arm/mach-ux500/include/mach/prcmu-qos.h
new file mode 100644
index 00000000000..915ed52a593
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/prcmu-qos.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) ST Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * PRCMU QoS
+ */
+#ifndef __MACH_PRCMU_QOS_H
+#define __MACH_PRCMU_QOS_H
+
+#include <linux/notifier.h>
+
+/* PRCMU QoS APE OPP class */
+#define PRCMU_QOS_APE_OPP 1
+#define PRCMU_QOS_DDR_OPP 2
+#define PRCMU_QOS_ARM_OPP 3
+#define PRCMU_QOS_DEFAULT_VALUE -1
+
+#if defined(CONFIG_U8500_PRCMU) || defined(CONFIG_U5500_PRCMU)
+
+unsigned long prcmu_qos_get_cpufreq_opp_delay(void);
+void prcmu_qos_set_cpufreq_opp_delay(unsigned long);
+void prcmu_qos_force_opp(int, s32);
+void prcmu_qos_voice_call_override(bool enable);
+
+#else /* !CONFIG_U8500_PRCMU */
+
+static inline unsigned long prcmu_qos_get_cpufreq_opp_delay(void)
+{
+ return 0;
+}
+
+static inline void prcmu_qos_set_cpufreq_opp_delay(unsigned long n) {}
+
+static inline void prcmu_qos_force_opp(int prcmu_qos_class, s32 i) {}
+
+static inline void prcmu_qos_voice_call_override(bool enable) {}
+
+#endif /* CONFIG_U8500_PRCMU */
+
+#ifdef CONFIG_UX500_PRCMU_QOS_POWER
+
+int prcmu_qos_requirement(int pm_qos_class);
+int prcmu_qos_add_requirement(int pm_qos_class, char *name, s32 value);
+int prcmu_qos_update_requirement(int pm_qos_class, char *name, s32 new_value);
+void prcmu_qos_remove_requirement(int pm_qos_class, char *name);
+int prcmu_qos_add_notifier(int prcmu_qos_class,
+ struct notifier_block *notifier);
+int prcmu_qos_remove_notifier(int prcmu_qos_class,
+ struct notifier_block *notifier);
+
+#else
+
+static inline int prcmu_qos_requirement(int prcmu_qos_class)
+{
+ return 0;
+}
+
+static inline int prcmu_qos_add_requirement(int prcmu_qos_class,
+ char *name, s32 value)
+{
+ return 0;
+}
+
+static inline int prcmu_qos_update_requirement(int prcmu_qos_class,
+ char *name, s32 new_value)
+{
+ return 0;
+}
+
+static inline void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name)
+{
+}
+
+static inline int prcmu_qos_add_notifier(int prcmu_qos_class,
+ struct notifier_block *notifier)
+{
+ return 0;
+}
+static inline int prcmu_qos_remove_notifier(int prcmu_qos_class,
+ struct notifier_block *notifier)
+{
+ return 0;
+}
+
+#endif
+
+#endif /* __MACH_PRCMU_QOS_H */
diff --git a/arch/arm/mach-ux500/include/mach/prcmu-regs.h b/arch/arm/mach-ux500/include/mach/prcmu-regs.h
index 4f6f2f0394c..5478a553d60 100644
--- a/arch/arm/mach-ux500/include/mach/prcmu-regs.h
+++ b/arch/arm/mach-ux500/include/mach/prcmu-regs.h
@@ -1,59 +1,15 @@
/*
- * Copyright (C) STMicroelectronics 2009
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (c) 2009 ST-Ericsson SA
*
- * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
- * Author: Sundar Iyer <sundar.iyer@stericsson.com>
- *
- * License Terms: GNU General Public License v2
- *
- * PRCM Unit registers
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
*/
-
#ifndef __MACH_PRCMU_REGS_H
#define __MACH_PRCMU_REGS_H
#include <mach/hardware.h>
-#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
-
-#define PRCM_ACLK_MGT_OFF 0x004
-#define PRCM_SVACLK_MGT_OFF 0x008
-#define PRCM_SIACLK_MGT_OFF 0x00C
-#define PRCM_SGACLK_MGT_OFF 0x014
-#define PRCM_UARTCLK_MGT_OFF 0x018
-#define PRCM_MSP02CLK_MGT_OFF 0x01C
-#define PRCM_I2CCLK_MGT_OFF 0x020
-#define PRCM_SDMMCCLK_MGT_OFF 0x024
-#define PRCM_SLIMCLK_MGT_OFF 0x028
-#define PRCM_PER1CLK_MGT_OFF 0x02C
-#define PRCM_PER2CLK_MGT_OFF 0x030
-#define PRCM_PER3CLK_MGT_OFF 0x034
-#define PRCM_PER5CLK_MGT_OFF 0x038
-#define PRCM_PER6CLK_MGT_OFF 0x03C
-#define PRCM_PER7CLK_MGT_OFF 0x040
-#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */
-#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */
-#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */
-#define PRCM_LCDCLK_MGT_OFF 0x044
-#define PRCM_BMLCLK_MGT_OFF 0x04C
-#define PRCM_HSITXCLK_MGT_OFF 0x050
-#define PRCM_HSIRXCLK_MGT_OFF 0x054
-#define PRCM_HDMICLK_MGT_OFF 0x058
-#define PRCM_APEATCLK_MGT_OFF 0x05C
-#define PRCM_APETRACECLK_MGT_OFF 0x060
-#define PRCM_MCDECLK_MGT_OFF 0x064
-#define PRCM_IPI2CCLK_MGT_OFF 0x068
-#define PRCM_DSIALTCLK_MGT_OFF 0x06C
-#define PRCM_DMACLK_MGT_OFF 0x074
-#define PRCM_B2R2CLK_MGT_OFF 0x078
-#define PRCM_TVCLK_MGT_OFF 0x07C
-#define PRCM_UNIPROCLK_MGT_OFF 0x278
-#define PRCM_SSPCLK_MGT_OFF 0x280
-#define PRCM_RNGCLK_MGT_OFF 0x284
-#define PRCM_UICCCLK_MGT_OFF 0x27C
-#define PRCM_MSP1CLK_MGT_OFF 0x288
-
#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118)
#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f
#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf
@@ -69,15 +25,11 @@
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100
#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0)
-#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C)
#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4)
#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0)
#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c)
#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308)
-#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0)
-#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1)
-
/* ARM WFI Standby signal register */
#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130)
#define PRCM_IOCR (_PRCMU_BASE + 0x310)
@@ -104,18 +56,12 @@
#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C)
#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334)
-#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
#define ARM_WAKEUP_MODEM 0x1
-#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C)
+#define PRCM_ARM_IT1_CLEAR (_PRCMU_BASE + 0x48C)
#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494)
#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174)
-#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0)
-#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0)
-#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1)
-#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2)
-
#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148)
#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150)
#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158)
@@ -136,21 +82,16 @@
#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500)
#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
-#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF)
-#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF)
-#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF)
-#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF)
+#define PRCM_LCDCLK_MGT (_PRCMU_BASE + 0x044)
+#define PRCM_MCDECLK_MGT (_PRCMU_BASE + 0x064)
+#define PRCM_HDMICLK_MGT (_PRCMU_BASE + 0x058)
+#define PRCM_TVCLK_MGT (_PRCMU_BASE + 0x07c)
#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530)
#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C)
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4)
#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8)
-
#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC)
-#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0)
-#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13)
-#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16)
-#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29)
/* ePOD and memory power signal control registers */
#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410)
@@ -165,41 +106,5 @@
#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800
#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1
-/* PRCMU HW semaphore */
-#define PRCM_SEM (_PRCMU_BASE + 0x400)
-#define PRCM_SEM_PRCM_SEM BIT(0)
-
-#define PRCM_TCR (_PRCMU_BASE + 0x1C8)
-#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
-#define PRCM_TCR_STOP_TIMERS BIT(16)
-#define PRCM_TCR_DOZE_MODE BIT(17)
-
-#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
-#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
-#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
-#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
-#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
-#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
-#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
-#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
-#define PRCM_CLKOCR_CLK1TYPE BIT(28)
-
-#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
-#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
-#define PRCM_CLK_MGT_CLKEN BIT(8)
-
-/* GPIOCR register */
-#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
-
-#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438)
-#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134)
-#define PRCM_CGATING_BYPASS_ICN2 BIT(6)
-
-/* Miscellaneous unit registers */
-#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214)
-#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218)
-
-/* System reset register */
-#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
#endif /* __MACH_PRCMU__REGS_H */
diff --git a/arch/arm/mach-ux500/include/mach/prcmu.h b/arch/arm/mach-ux500/include/mach/prcmu.h
index 92fd7f79a4d..5638b970752 100644
--- a/arch/arm/mach-ux500/include/mach/prcmu.h
+++ b/arch/arm/mach-ux500/include/mach/prcmu.h
@@ -9,8 +9,7 @@
#define __MACH_PRCMU_H
#include <linux/interrupt.h>
-#include <linux/notifier.h>
-#include <asm/mach-types.h>
+#include <mach/prcmu-qos.h>
/* PRCMU Wakeup defines */
enum prcmu_wakeup_index {
@@ -28,6 +27,15 @@ enum prcmu_wakeup_index {
};
#define PRCMU_WAKEUP(_name) (BIT(PRCMU_WAKEUP_INDEX_##_name))
+/* Low power states */
+#define PRCMU_AP_NO_CHANGE 0x00
+#define PRCMU_AP_SLEEP 0x01
+#define PRCMU_AP_DEEP_SLEEP 0x04
+#define PRCMU_AP_IDLE 0x05
+#define PRCMU_AP_DEEP_IDLE 0x07
+/* Legacy names */
+#define APEXECUTE_TO_APSLEEP PRCMU_AP_SLEEP
+
/* EPOD (power domain) IDs */
/*
@@ -80,8 +88,31 @@ enum prcmu_wakeup_index {
#define EPOD_STATE_ON_CLK_OFF 0x03
#define EPOD_STATE_ON 0x04
+/* DB5500 CLKOUT IDs */
+enum {
+ DB5500_CLKOUT0 = 0,
+ DB5500_CLKOUT1,
+};
+
+/* DB5500 CLKOUTx sources */
+enum {
+ DB5500_CLKOUT_REF_CLK_SEL0,
+ DB5500_CLKOUT_RTC_CLK0_SEL0,
+ DB5500_CLKOUT_ULP_CLK_SEL0,
+ DB5500_CLKOUT_STATIC0,
+ DB5500_CLKOUT_REFCLK,
+ DB5500_CLKOUT_ULPCLK,
+ DB5500_CLKOUT_ARMCLK,
+ DB5500_CLKOUT_SYSACC0CLK,
+ DB5500_CLKOUT_SOC0PLLCLK,
+ DB5500_CLKOUT_SOC1PLLCLK,
+ DB5500_CLKOUT_DDRPLLCLK,
+ DB5500_CLKOUT_TVCLK,
+ DB5500_CLKOUT_IRDACLK,
+};
+
/*
- * CLKOUT sources
+ * DB8500 CLKOUT sources
*/
#define PRCMU_CLKSRC_CLK38M 0x00
#define PRCMU_CLKSRC_ACLK 0x01
@@ -111,6 +142,7 @@ enum prcmu_clock {
PRCMU_MSP1CLK,
PRCMU_I2CCLK,
PRCMU_SDMMCCLK,
+ PRCMU_SPARE1CLK,
PRCMU_SLIMCLK,
PRCMU_PER1CLK,
PRCMU_PER2CLK,
@@ -141,6 +173,7 @@ enum prcmu_clock {
PRCMU_SVACLK,
PRCMU_NUM_REG_CLOCKS,
PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS,
+ PRCMU_CDCLK,
PRCMU_TIMCLK,
PRCMU_PLLSOC0,
PRCMU_PLLSOC1,
@@ -215,159 +248,52 @@ enum ddr_pwrst {
DDR_PWR_STATE_OFFHIGHLAT = 0x03
};
-#include <linux/mfd/db8500-prcmu.h>
-#include <linux/mfd/db5500-prcmu.h>
+#include <mach/prcmu-db8500.h>
+#include <mach/prcmu-db5500.h>
-#if defined(CONFIG_UX500_SOC_DB8500) || defined(CONFIG_UX500_SOC_DB5500)
+#if defined(CONFIG_U8500_PRCMU) || defined(CONFIG_U5500_PRCMU)
-static inline void __init prcmu_early_init(void)
-{
- if (machine_is_u5500())
- return db5500_prcmu_early_init();
- else
- return db8500_prcmu_early_init();
-}
+void __init prcmu_early_init(void);
-static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk,
- bool keep_ap_pll)
-{
- if (machine_is_u5500())
- return db5500_prcmu_set_power_state(state, keep_ulp_clk,
- keep_ap_pll);
- else
- return db8500_prcmu_set_power_state(state, keep_ulp_clk,
- keep_ap_pll);
-}
+int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll);
-static inline int prcmu_set_epod(u16 epod_id, u8 epod_state)
-{
- if (machine_is_u5500())
- return db5500_prcmu_set_epod(epod_id, epod_state);
- else
- return db8500_prcmu_set_epod(epod_id, epod_state);
-}
-
-static inline void prcmu_enable_wakeups(u32 wakeups)
-{
- if (machine_is_u5500())
- db5500_prcmu_enable_wakeups(wakeups);
- else
- db8500_prcmu_enable_wakeups(wakeups);
-}
+int prcmu_set_epod(u16 epod_id, u8 epod_state);
+void prcmu_enable_wakeups(u32 wakeups);
static inline void prcmu_disable_wakeups(void)
{
prcmu_enable_wakeups(0);
}
-
-static inline void prcmu_config_abb_event_readout(u32 abb_events)
-{
- if (machine_is_u5500())
- db5500_prcmu_config_abb_event_readout(abb_events);
- else
- db8500_prcmu_config_abb_event_readout(abb_events);
-}
-
-static inline void prcmu_get_abb_event_buffer(void __iomem **buf)
-{
- if (machine_is_u5500())
- db5500_prcmu_get_abb_event_buffer(buf);
- else
- db8500_prcmu_get_abb_event_buffer(buf);
-}
+void prcmu_config_abb_event_readout(u32 abb_events);
+void prcmu_get_abb_event_buffer(void __iomem **buf);
int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
int prcmu_config_clkout(u8 clkout, u8 source, u8 div);
-static inline int prcmu_request_clock(u8 clock, bool enable)
-{
- if (machine_is_u5500())
- return db5500_prcmu_request_clock(clock, enable);
- else
- return db8500_prcmu_request_clock(clock, enable);
-}
+int prcmu_request_clock(u8 clock, bool enable);
int prcmu_set_ape_opp(u8 opp);
int prcmu_get_ape_opp(void);
+int prcmu_set_arm_opp(u8 opp);
+int prcmu_get_arm_opp(void);
int prcmu_set_ddr_opp(u8 opp);
int prcmu_get_ddr_opp(void);
-static inline int prcmu_set_arm_opp(u8 opp)
-{
- if (machine_is_u5500())
- return db5500_prcmu_set_arm_opp(opp);
- else
- return db8500_prcmu_set_arm_opp(opp);
-}
-
-static inline int prcmu_get_arm_opp(void)
-{
- if (machine_is_u5500())
- return db5500_prcmu_get_arm_opp();
- else
- return db8500_prcmu_get_arm_opp();
-}
-
-static inline void prcmu_system_reset(u16 reset_code)
-{
- if (machine_is_u5500())
- return db5500_prcmu_system_reset(reset_code);
- else
- return db8500_prcmu_system_reset(reset_code);
-}
-
-static inline u16 prcmu_get_reset_code(void)
-{
- if (machine_is_u5500())
- return db5500_prcmu_get_reset_code();
- else
- return db8500_prcmu_get_reset_code();
-}
+void prcmu_system_reset(u16 reset_code);
+u16 prcmu_get_reset_code(void);
void prcmu_ac_wake_req(void);
void prcmu_ac_sleep_req(void);
void prcmu_modem_reset(void);
-static inline bool prcmu_is_ac_wake_requested(void)
-{
- if (machine_is_u5500())
- return db5500_prcmu_is_ac_wake_requested();
- else
- return db8500_prcmu_is_ac_wake_requested();
-}
+bool prcmu_is_ac_wake_requested(void);
-static inline int prcmu_set_display_clocks(void)
-{
- if (machine_is_u5500())
- return db5500_prcmu_set_display_clocks();
- else
- return db8500_prcmu_set_display_clocks();
-}
+int prcmu_set_display_clocks(void);
+int prcmu_disable_dsipll(void);
+int prcmu_enable_dsipll(void);
+int prcmu_config_esram0_deep_sleep(u8 state);
-static inline int prcmu_disable_dsipll(void)
-{
- if (machine_is_u5500())
- return db5500_prcmu_disable_dsipll();
- else
- return db8500_prcmu_disable_dsipll();
-}
-
-static inline int prcmu_enable_dsipll(void)
-{
- if (machine_is_u5500())
- return db5500_prcmu_enable_dsipll();
- else
- return db8500_prcmu_enable_dsipll();
-}
-
-static inline int prcmu_config_esram0_deep_sleep(u8 state)
-{
- if (machine_is_u5500())
- return db5500_prcmu_config_esram0_deep_sleep(state);
- else
- return db8500_prcmu_config_esram0_deep_sleep(state);
-}
#else
static inline void __init prcmu_early_init(void) {}
@@ -484,71 +410,4 @@ static inline void prcmu_get_abb_event_buffer(void __iomem **buf)
#endif
-/* PRCMU QoS APE OPP class */
-#define PRCMU_QOS_APE_OPP 1
-#define PRCMU_QOS_DDR_OPP 2
-#define PRCMU_QOS_DEFAULT_VALUE -1
-
-#ifdef CONFIG_UX500_PRCMU_QOS_POWER
-
-unsigned long prcmu_qos_get_cpufreq_opp_delay(void);
-void prcmu_qos_set_cpufreq_opp_delay(unsigned long);
-void prcmu_qos_force_opp(int, s32);
-void prcmu_qos_voice_call_override(bool enable);
-int prcmu_qos_requirement(int pm_qos_class);
-int prcmu_qos_add_requirement(int pm_qos_class, char *name, s32 value);
-int prcmu_qos_update_requirement(int pm_qos_class, char *name, s32 new_value);
-void prcmu_qos_remove_requirement(int pm_qos_class, char *name);
-int prcmu_qos_add_notifier(int prcmu_qos_class,
- struct notifier_block *notifier);
-int prcmu_qos_remove_notifier(int prcmu_qos_class,
- struct notifier_block *notifier);
-
-#else
-
-static inline unsigned long prcmu_qos_get_cpufreq_opp_delay(void)
-{
- return 0;
-}
-
-static inline void prcmu_qos_set_cpufreq_opp_delay(unsigned long n) {}
-
-static inline void prcmu_qos_force_opp(int prcmu_qos_class, s32 i) {}
-
-static inline void prcmu_qos_voice_call_override(bool enable) {}
-
-static inline int prcmu_qos_requirement(int prcmu_qos_class)
-{
- return 0;
-}
-
-static inline int prcmu_qos_add_requirement(int prcmu_qos_class,
- char *name, s32 value)
-{
- return 0;
-}
-
-static inline int prcmu_qos_update_requirement(int prcmu_qos_class,
- char *name, s32 new_value)
-{
- return 0;
-}
-
-static inline void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name)
-{
-}
-
-static inline int prcmu_qos_add_notifier(int prcmu_qos_class,
- struct notifier_block *notifier)
-{
- return 0;
-}
-static inline int prcmu_qos_remove_notifier(int prcmu_qos_class,
- struct notifier_block *notifier)
-{
- return 0;
-}
-
-#endif
-
#endif /* __MACH_PRCMU_H */
diff --git a/arch/arm/mach-ux500/include/mach/regulator.h b/arch/arm/mach-ux500/include/mach/regulator.h
new file mode 100644
index 00000000000..75ff3340359
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/regulator.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Bengt Jonsson <bengt.jonsson@stericsson.com> for ST-Ericsson,
+ * Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License Terms: GNU General Public License v2
+ *
+ */
+
+#ifndef MACH_UX500_REGULATOR_H
+#define MACH_UX500_REGULATOR_H
+
+#include <linux/device.h>
+
+struct ux500_regulator;
+
+#ifdef CONFIG_REGULATOR
+/*
+ * NOTE! The device will be connected to the correct regulator by this
+ * new framework. A list with connections will match up dev_name(dev)
+ * to the specific regulator. This follows the same principle as the
+ * normal regulator framework.
+ *
+ * This framework shall only be used in special cases when a regulator
+ * has to be enabled/disabled in atomic context.
+ */
+
+/**
+ * ux500_regulator_get()
+ *
+ * @dev: Drivers device struct
+ *
+ * Returns a ux500_regulator struct. Shall be used as argument for
+ * ux500_regulator_atomic_enable/disable calls.
+ * Return ERR_PTR(-EINVAL) upon no matching regulator found.
+ */
+struct ux500_regulator *__must_check ux500_regulator_get(struct device *dev);
+
+/**
+ * ux500_regulator_atomic_enable()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ *
+ * The enable/disable functions keep an internal counter, so every
+ * enable must be paired with an disable in order to turn off regulator.
+ */
+int ux500_regulator_atomic_enable(struct ux500_regulator *regulator);
+
+/**
+ * ux500_regulator_atomic_disable()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ *
+ */
+int ux500_regulator_atomic_disable(struct ux500_regulator *regulator);
+
+/**
+ * ux500_regulator_put()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ */
+void ux500_regulator_put(struct ux500_regulator *regulator);
+#else
+static inline struct ux500_regulator *__must_check
+ux500_regulator_get(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int
+ux500_regulator_atomic_enable(struct ux500_regulator *regulator)
+{
+ return -EINVAL;
+}
+
+static inline int
+ux500_regulator_atomic_disable(struct ux500_regulator *regulator)
+{
+ return -EINVAL;
+}
+
+static inline void ux500_regulator_put(struct ux500_regulator *regulator)
+{
+}
+#endif
+
+#endif
diff --git a/arch/arm/mach-ux500/pm/Kconfig b/arch/arm/mach-ux500/pm/Kconfig
index 47727b60775..26dbe19d558 100644
--- a/arch/arm/mach-ux500/pm/Kconfig
+++ b/arch/arm/mach-ux500/pm/Kconfig
@@ -1,6 +1,15 @@
+config UX500_CPUFREQ
+ tristate "CPUFreq support"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && CPU_FREQ && PM
+ default y
+ help
+ Add support for CPU Frequency scaling for Ux500 SOCs.
+ ARM power domains operating points can be set
+ dynamically. It depends on CPUfreq and PM subsystem.
+
config U8500_CPUIDLE
tristate "CPUIdle support"
- depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && CPU_IDLE && !RTC_DRV_PL031 && PM
+ depends on CPU_IDLE && (U5500_PRCMU || U8500_PRCMU) && !RTC_DRV_PL031 && PM
default y
select GENERIC_CLOCKEVENTS_BROADCAST
select UX500_CONTEXT
@@ -44,7 +53,7 @@ config UX500_SUSPEND_STANDBY
config UX500_SUSPEND_MEM
bool "Suspend Mem goes to ApDeepSleep"
- depends on UX500_SUSPEND && UX500_SOC_DB8500
+ depends on UX500_SUSPEND
help
If yes, echo mem > /sys/power/state puts the system into ApDeepSleep else
it will do the same as echo standby > /sys/power/state.
diff --git a/arch/arm/mach-ux500/pm/Makefile b/arch/arm/mach-ux500/pm/Makefile
index b237ec6fa16..3d56e345159 100644
--- a/arch/arm/mach-ux500/pm/Makefile
+++ b/arch/arm/mach-ux500/pm/Makefile
@@ -6,7 +6,15 @@ obj-y := pm.o runtime.o
obj-$(CONFIG_U8500_CPUIDLE) += cpuidle.o timer.o
obj-$(CONFIG_U8500_CPUIDLE_DEBUG) += cpuidle_dbg.o
obj-$(CONFIG_UX500_CONTEXT) += context.o context_arm.o context-db8500.o context-db5500.o
+obj-$(CONFIG_UX500_CPUFREQ) += cpufreq.o
obj-$(CONFIG_UX500_SUSPEND) += suspend.o
obj-$(CONFIG_UX500_SUSPEND_DBG) += suspend_dbg.o
obj-$(CONFIG_UX500_USECASE_GOVERNOR) += usecase_gov.o
+ifeq ($(CONFIG_UX500_SOC_DB8500), y)
+obj-$(CONFIG_UX500_CPUFREQ) += cpufreq-db8500.o
+endif
+
+ifeq ($(CONFIG_UX500_SOC_DB5500), y)
+obj-$(CONFIG_UX500_CPUFREQ) += cpufreq-db5500.o
+endif
diff --git a/arch/arm/mach-ux500/pm/context.c b/arch/arm/mach-ux500/pm/context.c
index a78f2d9dbb9..7e4687eb6f4 100644
--- a/arch/arm/mach-ux500/pm/context.c
+++ b/arch/arm/mach-ux500/pm/context.c
@@ -18,18 +18,16 @@
#include <linux/notifier.h>
#include <linux/clk.h>
#include <linux/err.h>
-
+#include <linux/gpio/nomadik.h>
#include <asm/hardware/gic.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/scu.h>
-#include <linux/gpio/nomadik.h>
-#include <linux/gpio.h>
-
#include "context.h"
#include "pm.h"
+#include "../product.h"
#define GPIO_NUM_BANKS 9
#define GPIO_NUM_SAVE_REGISTERS 7
@@ -42,17 +40,16 @@
#define U8500_BACKUPRAM_SIZE SZ_64K
#define U8500_PUBLIC_BOOT_ROM_BASE (U8500_BOOT_ROM_BASE + 0x17000)
+#define U5500_PUBLIC_BOOT_ROM_BASE (U5500_BOOT_ROM_BASE + 0x18000)
-/* Special dedicated addresses in backup RAM */
+/*
+ * Special dedicated addresses in backup RAM. The 5500 addresses are identical
+ * to the 8500 ones.
+ */
#define U8500_EXT_RAM_LOC_BACKUPRAM_ADDR 0x80151FDC
#define U8500_CPU0_CP15_CR_BACKUPRAM_ADDR 0x80151F80
#define U8500_CPU1_CP15_CR_BACKUPRAM_ADDR 0x80151FA0
-/* For v1.x */
-#define U8500_CPU0_BACKUPRAM_ADDR_BACKUPRAM_LOG_ADDR 0x80151FD8
-#define U8500_CPU1_BACKUPRAM_ADDR_BACKUPRAM_LOG_ADDR 0x80151FE0
-
-/* For v2.0 and later */
#define U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR 0x80151FD8
#define U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR 0x80151FE0
@@ -65,6 +62,12 @@
#define GIC_DIST_ENABLE_SET_SPI0 GIC_DIST_ENABLE_SET
#define GIC_DIST_ENABLE_SET_SPI32 (GIC_DIST_ENABLE_SET + IRQ_SHPI_START / 8)
+#define GIC_DIST_ENABLE_CLEAR_0 GIC_DIST_ENABLE_CLEAR
+#define GIC_DIST_ENABLE_CLEAR_32 (GIC_DIST_ENABLE_CLEAR + 4)
+#define GIC_DIST_ENABLE_CLEAR_64 (GIC_DIST_ENABLE_CLEAR + 8)
+#define GIC_DIST_ENABLE_CLEAR_96 (GIC_DIST_ENABLE_CLEAR + 12)
+#define GIC_DIST_ENABLE_CLEAR_128 (GIC_DIST_ENABLE_CLEAR + 16)
+
#define GIC_DIST_PRI_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - IRQ_SHPI_START) / 4)
#define GIC_DIST_PRI_CPU_NUM (IRQ_SHPI_START / 4)
#define GIC_DIST_PRI_SPI0 GIC_DIST_PRI
@@ -198,10 +201,10 @@ static u32 gpio_save[GPIO_NUM_BANKS][GPIO_NUM_SAVE_REGISTERS];
/*
* Stacks and stack pointers
*/
-static DEFINE_PER_CPU(u32, varm_registers_backup_stack[1024]);
+static DEFINE_PER_CPU(u32[128], varm_registers_backup_stack);
static DEFINE_PER_CPU(u32 *, varm_registers_pointer);
-static DEFINE_PER_CPU(u32, varm_cp15_backup_stack[1024]);
+static DEFINE_PER_CPU(u32[128], varm_cp15_backup_stack);
static DEFINE_PER_CPU(u32 *, varm_cp15_pointer);
@@ -307,12 +310,18 @@ static void restore_stm_ape(void)
static bool tpiu_clocked(void)
{
- if (cpu_is_u5500()) {
+#ifdef CONFIG_UX500_DEBUG_NO_LAUTERBACH
+ return false;
+#else
+ if (cpu_is_u5500())
return readl_relaxed(__io_address(U5500_PRCMU_DBG_PWRCTRL))
& PRCMU_DBG_PWRCTRL_A9DBGCLKEN;
- }
+
+ if (cpu_is_u8500())
+ return ux500_jtag_enabled();
return true;
+#endif
}
/*
@@ -533,6 +542,37 @@ static void restore_gic_dist_cpu(struct context_gic_dist_cpu *c_gic)
c_gic->base +
GIC_DIST_ENABLE_SET_SPI0 + i * 4);
}
+
+/*
+ * Disable interrupts that are not necessary
+ * to have turned on during ApDeepSleep.
+ */
+void context_gic_dist_disable_unneeded_irqs(void)
+{
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_0);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_32);
+
+ /* Leave PRCMU IRQ 0 and 1 enabled */
+ writel(0xffff3fff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_64);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_96);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_128);
+
+}
+
static void save_scu(void)
{
context_scu.ctrl =
@@ -841,17 +881,12 @@ void context_restore_cpu_registers(void)
* and return address for the PC in backup SRAM and
* does wait for interrupt.
*/
-void context_save_to_sram_and_wfi(bool cleanL1cache,
- bool cleanL2cache)
+void context_save_to_sram_and_wfi(bool cleanL2cache)
{
int cpu = smp_processor_id();
- if (cpu_is_u8500())
- context_save_to_sram_and_wfi_internal(backup_sram_storage[cpu],
- cleanL1cache,
- cleanL2cache);
- else if (cpu_is_u5500())
- __asm__ __volatile__("wfi\n" : : : "memory");
+ context_save_to_sram_and_wfi_internal(backup_sram_storage[cpu],
+ cleanL2cache);
}
static int __init context_init(void)
@@ -877,6 +912,12 @@ static int __init context_init(void)
if (cpu_is_u5500()) {
+ writel(IO_ADDRESS(U5500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ writel(IO_ADDRESS(U5500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
context_tpiu.base = ioremap(U5500_TPIU_BASE, SZ_4K);
context_stm_ape.base = ioremap(U5500_STM_REG_BASE, SZ_4K);
context_scu.base = ioremap(U5500_SCU_BASE, SZ_4K);
diff --git a/arch/arm/mach-ux500/pm/context.h b/arch/arm/mach-ux500/pm/context.h
index 5b7522b4c0a..22b56351284 100644
--- a/arch/arm/mach-ux500/pm/context.h
+++ b/arch/arm/mach-ux500/pm/context.h
@@ -41,6 +41,8 @@ void context_gpio_restore(void);
void context_gpio_restore_mux(void);
void context_gpio_mux_safe_switch(bool begin);
+void context_gic_dist_disable_unneeded_irqs(void);
+
void context_varm_save_common(void);
void context_varm_restore_common(void);
@@ -50,8 +52,7 @@ void context_varm_restore_core(void);
void context_save_cpu_registers(void);
void context_restore_cpu_registers(void);
-void context_save_to_sram_and_wfi(bool cleanL1cache,
- bool cleanL2cache);
+void context_save_to_sram_and_wfi(bool cleanL2cache);
void context_clean_l1_cache_all(void);
void context_save_arm_registers(u32 **backup_stack);
@@ -61,7 +62,6 @@ void context_save_cp15_registers(u32 **backup_stack);
void context_restore_cp15_registers(u32 **backup_stack);
void context_save_to_sram_and_wfi_internal(u32 backup_sram_storage,
- bool cleanL1cache,
bool cleanL2cache);
/* DB specific functions in either context-db8500 or context-db5500 */
@@ -77,8 +77,7 @@ void u5500_context_init(void);
static inline void context_varm_save_core(void) {}
static inline void context_save_cpu_registers(void) {}
-static inline void context_save_to_sram_and_wfi(bool cleanL1cache,
- bool cleanL2cache) {}
+static inline void context_save_to_sram_and_wfi(bool cleanL2cache) {}
static inline void context_restore_cpu_registers(void) {}
static inline void context_varm_restore_core(void) {}
diff --git a/arch/arm/mach-ux500/pm/context_arm.S b/arch/arm/mach-ux500/pm/context_arm.S
index d9afbcbf929..69db730c1fc 100755..100644
--- a/arch/arm/mach-ux500/pm/context_arm.S
+++ b/arch/arm/mach-ux500/pm/context_arm.S
@@ -162,7 +162,8 @@ ENTRY(context_restore_arm_registers)
.align 4
.section ".text", "ax"
ENTRY(context_save_cp15_registers)
- stmfd sp!, {r1, r2, lr} @ Save on stack
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack (r3 is saved due
+ @ to 8 byte aligned stack)
ldr r1, [r0] @ Read backup stack pointer
mrc p15, 0, r2, c12, c0, 0 @ Read Non-secure Vector Base
@@ -217,7 +218,7 @@ ENTRY(context_save_cp15_registers)
SAVE_AND_INCREMENT r2 r1
str r1, [r0] @ Write backup stack pointer
- ldmfd sp!, {r1, r2, pc} @ Restore registers and return
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
@@ -231,7 +232,8 @@ ENTRY(context_save_cp15_registers)
.align 4
.section ".text", "ax"
ENTRY(context_restore_cp15_registers)
- stmfd sp!, {r1, r2, lr} @ Save on stack
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack (r3 is saved due
+ @ to 8 byte aligned stack)
ldr r1, [r0] @ Read backup stack pointer
DECREMENT_AND_RESTORE r1 r2 @ Write CPACR register
@@ -284,7 +286,7 @@ ENTRY(context_restore_cp15_registers)
@ Address Register
str r1, [r0] @ Write backup stack pointer
- ldmfd sp!, {r1, r2, pc} @ Restore registers and return
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
/*
@@ -365,51 +367,47 @@ ENTRY(ux500_clean_l2_cache_all)
/*
- * Last saves and WFI
+ * Last saves to backup RAM, cache clean and WFI
*
* r0 = address to backup_sram_storage base adress
- * r1 = indicate whether L1 cache should be cleaned
- * r2 = indicate whether L2 cache should be cleaned
+ * r1 = indicate whether also L2 cache should be cleaned
*/
.align 4
.section ".text", "ax"
ENTRY(context_save_to_sram_and_wfi_internal)
- stmfd sp!, {r3-r12, lr} @ save on stack.
+ stmfd sp!, {r2-r12, lr} @ save on stack.
- mrc p15, 0, r3, c1, c0, 0 @ read cp15 system control
+ mrc p15, 0, r2, c1, c0, 0 @ read cp15 system control
@ register
- str r3, [r0, #0x00]
- mrc p15, 0, r3, c2, c0, 0 @ read cp15 ttb0 register
- str r3, [r0, #0x04]
- mrc p15, 0, r3, c2, c0, 1 @ read cp15 ttb1 register
- str r3, [r0, #0x08]
- mrc p15, 0, r3, c2, c0, 2 @ read cp15 ttb control register
- str r3, [r0, #0x0C]
- mrc p15, 0, r3, c3, c0, 0 @ read domain access control
+ str r2, [r0, #0x00]
+ mrc p15, 0, r2, c2, c0, 0 @ read cp15 ttb0 register
+ str r2, [r0, #0x04]
+ mrc p15, 0, r2, c2, c0, 1 @ read cp15 ttb1 register
+ str r2, [r0, #0x08]
+ mrc p15, 0, r2, c2, c0, 2 @ read cp15 ttb control register
+ str r2, [r0, #0x0C]
+ mrc p15, 0, r2, c3, c0, 0 @ read domain access control
@ register
- str r3, [r0, #0x10]
+ str r2, [r0, #0x10]
- ldr r3, =return_here
- str r3, [r0, #0x14] @ save program counter restore
+ ldr r2, =return_here
+ str r2, [r0, #0x14] @ save program counter restore
@ value to backup_sram_storage
- mrs r3, cpsr
- str r3, [r0, #0x18] @ save cpsr to
+ mrs r2, cpsr
+ str r2, [r0, #0x18] @ save cpsr to
@ backup_sram_storage
str sp, [r0, #0x1c] @ save sp to backup_sram_storage
- mov r4, r2 @ Set r5 = cleanL2cache, r2
+ mov r4, r1 @ Set r4 = cleanL2cache, r1
@ will be destroyed by
@ v7_clean_l1_cache_all
- dsb
- cmp r1, #0
- blne context_clean_l1_cache_all @ Commit all dirty data in L1
+ bl context_clean_l1_cache_all @ Commit all dirty data in L1
@ cache to L2 without
@ invalidating
- dsb
- cmp r4, #0
+ cmp r4, #0
blne ux500_clean_l2_cache_all @ If r4 != FALSE then clean all
@ dirty data in L2 cache, no
@ invalidate
@@ -424,5 +422,5 @@ return_here: @ both cores return here
@ with all the context lost
@ except pc, sp and cpsr
- ldmfd sp!, {r3-r12, pc} @ restore from stack
+ ldmfd sp!, {r2-r12, pc} @ restore from stack
diff --git a/arch/arm/mach-ux500/pm/cpufreq-db5500.c b/arch/arm/mach-ux500/pm/cpufreq-db5500.c
new file mode 100644
index 00000000000..e058eb6bf58
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/cpufreq-db5500.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+
+#include <mach/prcmu.h>
+
+#include "cpufreq.h"
+
+static struct cpufreq_frequency_table freq_table[] = {
+ [0] = {
+ .index = 0,
+ .frequency = 200000,
+ },
+ [1] = {
+ .index = 1,
+ .frequency = 396500,
+ },
+ [2] = {
+ .index = 2,
+ .frequency = 793000,
+ },
+ [3] = {
+ .index = 3,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+};
+
+static enum arm_opp idx2opp[] = {
+ ARM_EXTCLK,
+ ARM_50_OPP,
+ ARM_100_OPP,
+};
+
+static int __init u5500_cpufreq_register(void)
+{
+ int i = 0;
+
+ BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
+
+ if (cpu_is_u5500v1())
+ return -ENODEV;
+
+ if (prcmu_get_ddr_freq() < 333) {
+ freq_table[1].frequency = 325000;
+ freq_table[2].frequency = 650000;
+ }
+
+ pr_info("u5500-cpufreq : Available frequencies:\n");
+ while (freq_table[i].frequency != CPUFREQ_TABLE_END)
+ pr_info(" %d Mhz\n", freq_table[i++].frequency/1000);
+
+ return ux500_cpufreq_register(freq_table, idx2opp);
+}
+device_initcall(u5500_cpufreq_register);
diff --git a/arch/arm/mach-ux500/pm/cpufreq-db8500.c b/arch/arm/mach-ux500/pm/cpufreq-db8500.c
new file mode 100644
index 00000000000..e36497a7365
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/cpufreq-db8500.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+
+#include <mach/prcmu.h>
+
+#include "cpufreq.h"
+
+static struct cpufreq_frequency_table freq_table[] = {
+ [0] = {
+ .index = 0,
+ .frequency = 200000,
+ },
+ [1] = {
+ .index = 1,
+ .frequency = 300000,
+ },
+ [2] = {
+ .index = 2,
+ .frequency = 600000,
+ },
+ [3] = {
+ /* Used for MAX_OPP, if available */
+ .index = 3,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+ [4] = {
+ .index = 4,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+};
+
+static enum arm_opp idx2opp[] = {
+ ARM_EXTCLK,
+ ARM_50_OPP,
+ ARM_100_OPP,
+ ARM_MAX_OPP
+};
+
+/*
+ * Below is a temporary workaround for wlan performance issues
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/cpu.h>
+
+#include <mach/irqs.h>
+
+#define WLAN_PROBE_DELAY 3000 /* 3 seconds */
+#define WLAN_LIMIT (3000/3) /* If we have more than 1000 irqs per second */
+static struct delayed_work work_wlan_workaround;
+bool wlan_mode_on;
+
+#define USB_PROBE_DELAY 1000 /* 1 seconds */
+#define USB_LIMIT (200) /* If we have more than 200 irqs per second */
+static struct delayed_work work_usb_workaround;
+bool usb_mode_on;
+
+static void wlan_load(struct work_struct *work)
+{
+ int cpu;
+ unsigned int num_irqs = 0;
+ static unsigned int old_num_irqs = UINT_MAX;
+
+ for_each_online_cpu(cpu)
+ num_irqs += kstat_irqs_cpu(IRQ_DB8500_SDMMC1, cpu);
+
+ if ((num_irqs > old_num_irqs) &&
+ (num_irqs - old_num_irqs) > WLAN_LIMIT)
+ wlan_mode_on = true;
+ else
+ wlan_mode_on = false;
+
+ old_num_irqs = num_irqs;
+
+ schedule_delayed_work_on(0,
+ &work_wlan_workaround,
+ msecs_to_jiffies(WLAN_PROBE_DELAY));
+}
+
+static void usb_load(struct work_struct *work)
+{
+ int cpu;
+ unsigned int num_irqs = 0;
+ static unsigned int old_num_irqs = UINT_MAX;
+
+ for_each_online_cpu(cpu)
+ num_irqs += kstat_irqs_cpu(IRQ_DB8500_USBOTG, cpu);
+
+ if ((num_irqs > old_num_irqs) &&
+ (num_irqs - old_num_irqs) > USB_LIMIT)
+ usb_mode_on = true;
+ else
+ usb_mode_on = false;
+
+ old_num_irqs = num_irqs;
+
+ schedule_delayed_work_on(0,
+ &work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+}
+
+void cpufreq_usb_connect_notify(bool connect)
+{
+ if (connect) {
+ schedule_delayed_work_on(0,
+ &work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+ } else {
+ cancel_delayed_work_sync(&work_usb_workaround);
+ usb_mode_on = false;
+ }
+}
+
+static int __init u8500_cpufreq_register(void)
+{
+ int i = 0;
+
+ BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
+
+ if (!prcmu_is_u8400()) {
+ freq_table[1].frequency = 400000;
+ freq_table[2].frequency = 800000;
+ if (prcmu_has_arm_maxopp())
+ freq_table[3].frequency = 1000000;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&work_wlan_workaround,
+ wlan_load);
+
+ INIT_DELAYED_WORK_DEFERRABLE(&work_usb_workaround,
+ usb_load);
+
+ schedule_delayed_work_on(0,
+ &work_wlan_workaround,
+ msecs_to_jiffies(WLAN_PROBE_DELAY));
+
+ pr_info("u8500-cpufreq : Available frequencies:\n");
+ while (freq_table[i].frequency != CPUFREQ_TABLE_END)
+ pr_info(" %d Mhz\n", freq_table[i++].frequency/1000);
+
+ return ux500_cpufreq_register(freq_table, idx2opp);
+}
+device_initcall(u8500_cpufreq_register);
diff --git a/arch/arm/mach-ux500/pm/cpufreq.c b/arch/arm/mach-ux500/pm/cpufreq.c
new file mode 100644
index 00000000000..a411f95d3ce
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/cpufreq.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/prcmu.h>
+#include <mach/prcmu-regs.h>
+
+static struct cpufreq_frequency_table *freq_table;
+static enum arm_opp *idx2opp;
+
+static struct freq_attr *ux500_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static int ux500_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, freq_table);
+}
+
+static int ux500_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ unsigned int idx;
+
+ /* scale the target frequency to one of the extremes supported */
+ if (target_freq < policy->cpuinfo.min_freq)
+ target_freq = policy->cpuinfo.min_freq;
+ if (target_freq > policy->cpuinfo.max_freq)
+ target_freq = policy->cpuinfo.max_freq;
+
+ /* Lookup the next frequency */
+ if (cpufreq_frequency_table_target
+ (policy, freq_table, target_freq, relation, &idx)) {
+ return -EINVAL;
+ }
+
+ freqs.old = policy->cur;
+ freqs.new = freq_table[idx].frequency;
+ freqs.cpu = policy->cpu;
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ /* pre-change notification */
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ /* request the PRCM unit for opp change */
+ if (prcmu_set_arm_opp(idx2opp[idx])) {
+ pr_err("ux500-cpufreq: Failed to set OPP level\n");
+ return -EINVAL;
+ }
+
+ /* post change notification */
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return 0;
+}
+
+static unsigned int ux500_cpufreq_getspeed(unsigned int cpu)
+{
+ int i;
+ /* request the prcm to get the current ARM opp */
+ for (i = 0; prcmu_get_arm_opp() != idx2opp[i]; i++)
+ ;
+ return freq_table[i].frequency;
+}
+
+static int __cpuinit ux500_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int res;
+ int i;
+
+ /* get policy fields based on the table */
+ res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+ if (!res)
+ cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
+ else {
+ pr_err("ux500-cpufreq : Failed to read policy table\n");
+ return res;
+ }
+
+ policy->min = policy->cpuinfo.min_freq;
+ policy->max = policy->cpuinfo.max_freq;
+ policy->cur = ux500_cpufreq_getspeed(policy->cpu);
+
+ for (i = 0; freq_table[i].frequency != policy->cur; i++)
+ ;
+
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+ /*
+ * FIXME : Need to take time measurement across the target()
+ * function with no/some/all drivers in the notification
+ * list.
+ */
+ policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
+
+ /* policy sharing between dual CPUs */
+ cpumask_copy(policy->cpus, &cpu_present_map);
+
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+
+ return 0;
+}
+
+static struct cpufreq_driver ux500_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = ux500_cpufreq_verify_speed,
+ .target = ux500_cpufreq_target,
+ .get = ux500_cpufreq_getspeed,
+ .init = ux500_cpufreq_init,
+ .name = "UX500",
+ .attr = ux500_cpufreq_attr,
+};
+
+int __init
+ux500_cpufreq_register(struct cpufreq_frequency_table *table,
+ enum arm_opp *idx2opplist)
+{
+ freq_table = table;
+ idx2opp = idx2opplist;
+
+ if (ux500_is_svp())
+ return -ENODEV;
+
+ pr_info("cpufreq for ux500 started\n");
+ return cpufreq_register_driver(&ux500_cpufreq_driver);
+}
diff --git a/arch/arm/mach-ux500/pm/cpufreq.h b/arch/arm/mach-ux500/pm/cpufreq.h
new file mode 100644
index 00000000000..bdaba6c464e
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/cpufreq.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+#ifndef __UX500_PM_CPUFREQ_H
+#define __UX500_PM_CPUFREQ_H
+
+extern int ux500_cpufreq_register(struct cpufreq_frequency_table *freq_table,
+ enum arm_opp *idx2opp);
+
+void cpufreq_usb_connect_notify(bool connect);
+
+#endif
diff --git a/arch/arm/mach-ux500/pm/cpuidle.c b/arch/arm/mach-ux500/pm/cpuidle.c
index cc1a4169110..eddd2849a16 100644
--- a/arch/arm/mach-ux500/pm/cpuidle.c
+++ b/arch/arm/mach-ux500/pm/cpuidle.c
@@ -15,8 +15,7 @@
#include <linux/sched.h>
#include <linux/tick.h>
#include <linux/clockchips.h>
-#include <linux/gpio.h>
-#include <linux/regulator/db8500-prcmu.h>
+#include <linux/gpio/nomadik.h>
#include <mach/prcmu.h>
@@ -25,6 +24,7 @@
#include "context.h"
#include "pm.h"
#include "timer.h"
+#include "../regulator-u8500.h"
/*
* All measurements are with two cpus online (worst case) and at
@@ -57,7 +57,6 @@ static struct cstate cstates[] = {
.power_usage = 1000,
.APE = APE_ON,
.ARM = ARM_ON,
- .ARM_PLL = ARM_PLL_ON,
.UL_PLL = UL_PLL_ON,
.ESRAM = ESRAM_RET,
.pwrst = PRCMU_AP_NO_CHANGE,
@@ -72,7 +71,6 @@ static struct cstate cstates[] = {
.power_usage = 10,
.APE = APE_ON,
.ARM = ARM_ON,
- .ARM_PLL = ARM_PLL_ON,
.UL_PLL = UL_PLL_ON,
.ESRAM = ESRAM_RET,
.pwrst = PRCMU_AP_NO_CHANGE,
@@ -81,47 +79,30 @@ static struct cstate cstates[] = {
.desc = "Wait for interrupt ",
},
{
- .enter_latency = 60,
- .exit_latency = 60,
- .threshold = 150,
- .power_usage = 5,
- .APE = APE_ON,
- .ARM = ARM_RET,
- .ARM_PLL = ARM_PLL_ON,
- .UL_PLL = UL_PLL_ON,
- .ESRAM = ESRAM_RET,
- .pwrst = PRCMU_AP_IDLE,
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .state = CI_IDLE,
- .desc = "ApIdle ",
- },
- {
- .enter_latency = 70,
+ .enter_latency = 170,
.exit_latency = 70,
- .threshold = 160,
+ .threshold = 260,
.power_usage = 4,
.APE = APE_ON,
.ARM = ARM_RET,
- .ARM_PLL = ARM_PLL_OFF,
.UL_PLL = UL_PLL_ON,
.ESRAM = ESRAM_RET,
.pwrst = PRCMU_AP_IDLE,
.flags = CPUIDLE_FLAG_TIME_VALID,
.state = CI_IDLE,
- .desc = "ApIdle, ARM PLL off ",
+ .desc = "ApIdle ",
},
{
- .enter_latency = 250,
+ .enter_latency = 350,
.exit_latency = MAX_SLEEP_WAKE_UP_LATENCY + 200,
/*
* Note: Sleep time must be longer than 120 us or else
* there might be issues with the RTC-RTT block.
*/
- .threshold = MAX_SLEEP_WAKE_UP_LATENCY + 250 + 200,
+ .threshold = MAX_SLEEP_WAKE_UP_LATENCY + 350 + 200,
.power_usage = 3,
.APE = APE_OFF,
.ARM = ARM_RET,
- .ARM_PLL = ARM_PLL_OFF,
.UL_PLL = UL_PLL_ON,
.ESRAM = ESRAM_RET,
.pwrst = PRCMU_AP_SLEEP,
@@ -130,15 +111,14 @@ static struct cstate cstates[] = {
.desc = "ApSleep ",
},
{
- .enter_latency = 250,
+ .enter_latency = 350,
.exit_latency = (MAX_SLEEP_WAKE_UP_LATENCY +
UL_PLL_START_UP_LATENCY + 200),
.threshold = (MAX_SLEEP_WAKE_UP_LATENCY +
- UL_PLL_START_UP_LATENCY + 250 + 200),
+ UL_PLL_START_UP_LATENCY + 350 + 200),
.power_usage = 2,
.APE = APE_OFF,
.ARM = ARM_RET,
- .ARM_PLL = ARM_PLL_OFF,
.UL_PLL = UL_PLL_OFF,
.ESRAM = ESRAM_RET,
.pwrst = PRCMU_AP_SLEEP,
@@ -148,13 +128,12 @@ static struct cstate cstates[] = {
},
#ifdef CONFIG_U8500_CPUIDLE_APDEEPIDLE
{
- .enter_latency = 300,
+ .enter_latency = 400,
.exit_latency = DEEP_SLEEP_WAKE_UP_LATENCY + 400,
- .threshold = DEEP_SLEEP_WAKE_UP_LATENCY + 300 + 400,
+ .threshold = DEEP_SLEEP_WAKE_UP_LATENCY + 400 + 400,
.power_usage = 2,
.APE = APE_ON,
.ARM = ARM_OFF,
- .ARM_PLL = ARM_PLL_OFF,
.UL_PLL = UL_PLL_ON,
.ESRAM = ESRAM_RET,
.pwrst = PRCMU_AP_DEEP_IDLE,
@@ -164,13 +143,12 @@ static struct cstate cstates[] = {
},
#endif
{
- .enter_latency = 310,
+ .enter_latency = 410,
.exit_latency = DEEP_SLEEP_WAKE_UP_LATENCY + 420,
- .threshold = DEEP_SLEEP_WAKE_UP_LATENCY + 310 + 420,
+ .threshold = DEEP_SLEEP_WAKE_UP_LATENCY + 410 + 420,
.power_usage = 1,
.APE = APE_OFF,
.ARM = ARM_OFF,
- .ARM_PLL = ARM_PLL_OFF,
.UL_PLL = UL_PLL_OFF,
.ESRAM = ESRAM_RET,
.pwrst = PRCMU_AP_DEEP_SLEEP,
@@ -184,6 +162,7 @@ struct cpu_state {
int gov_cstate;
ktime_t sched_wake_up;
struct cpuidle_device dev;
+ bool restore_arm_core;
};
static DEFINE_PER_CPU(struct cpu_state, *cpu_state);
@@ -196,6 +175,7 @@ static ktime_t time_next; /* protected by cpuidle_lock */
extern struct clock_event_device u8500_mtu_clkevt;
static atomic_t idle_cpus_counter = ATOMIC_INIT(0);
+static atomic_t master_counter = ATOMIC_INIT(0);
struct cstate *ux500_ci_get_cstates(int *len)
{
@@ -204,11 +184,20 @@ struct cstate *ux500_ci_get_cstates(int *len)
return cstates;
}
-static void restore_sequence(ktime_t now)
+static void restore_sequence(struct cpu_state *state, ktime_t now)
{
spin_lock(&cpuidle_lock);
smp_rmb();
+ if (state->restore_arm_core) {
+ state->restore_arm_core = false;
+ smp_wmb();
+
+ context_restore_cpu_registers();
+ context_varm_restore_core();
+ }
+
+ smp_rmb();
if (restore_arm) {
restore_arm = false;
@@ -242,13 +231,13 @@ static void restore_sequence(ktime_t now)
* If we're returning from ApSleep and the RTC timer
* caused the wake up, program the MTU to trigger.
*/
- if ((ktime_to_us(now) > ktime_to_us(time_next)))
+ if ((ktime_to_us(now) >= ktime_to_us(time_next)))
time_next = ktime_add(now, ktime_set(0, 1000));
/* Make sure have an MTU interrupt waiting for us */
- clockevents_program_event(&u8500_mtu_clkevt,
+ WARN_ON(clockevents_program_event(&u8500_mtu_clkevt,
time_next,
- now);
+ now));
}
spin_unlock(&cpuidle_lock);
@@ -290,6 +279,7 @@ static u32 get_remaining_sleep_time(ktime_t *next, int *on_cpu)
static bool is_last_cpu_running(void)
{
+ smp_rmb();
return atomic_read(&idle_cpus_counter) == num_online_cpus();
}
@@ -369,8 +359,9 @@ static int enter_sleep(struct cpuidle_device *dev,
int target;
struct cpu_state *state;
bool slept_well = false;
- bool restore_local_arm = false;
int this_cpu = smp_processor_id();
+ bool migrate_timer;
+ bool master = false;
local_irq_disable();
@@ -391,6 +382,11 @@ static int enter_sleep(struct cpuidle_device *dev,
if (state->gov_cstate > ux500_ci_dbg_deepest_state())
state->gov_cstate = ux500_ci_dbg_deepest_state();
+ if (cstates[state->gov_cstate].ARM != ARM_ON)
+ migrate_timer = true;
+ else
+ migrate_timer = false;
+
spin_unlock(&cpuidle_lock);
atomic_inc(&idle_cpus_counter);
@@ -405,36 +401,55 @@ static int enter_sleep(struct cpuidle_device *dev,
/* "target" will be last_state in the cpuidle framework */
goto exit_fast;
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &this_cpu);
-
+ /* Only one CPU should master the sleeping sequence */
if (cstates[target].ARM != ARM_ON) {
+ smp_mb();
+ if (atomic_inc_return(&master_counter) == 1)
+ master = true;
+ else
+ atomic_dec(&master_counter);
+ smp_mb();
+ }
+
+ if (migrate_timer)
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
+ &this_cpu);
+
+
+ if (master && (cstates[target].ARM != ARM_ON)) {
- /* Decouple GIC from the interrupt bus */
ux500_pm_gic_decouple();
- if (!ux500_pm_other_cpu_wfi())
- /* Other CPU was not in WFI => abort */
- goto exit;
/*
- * Check if we have a pending interrupt or if sleep
- * state has changed after GIC has been frozen
+ * Check if sleep state has changed after GIC has been frozen
*/
- if (ux500_pm_gic_pending_interrupt())
+ if (target != determine_sleep_state(&sleep_time)) {
+ atomic_dec(&master_counter);
goto exit;
+ }
- if (ux500_pm_prcmu_pending_interrupt())
+ /* Copy GIC interrupt settings to PRCMU interrupt settings */
+ ux500_pm_prcmu_copy_gic_settings();
+
+ if (ux500_pm_gic_pending_interrupt()) {
+ /* An interrupt found => abort */
+ atomic_dec(&master_counter);
+ goto exit;
+ }
+
+ if (ux500_pm_prcmu_pending_interrupt()) {
/* An interrupt found => abort */
+ atomic_dec(&master_counter);
goto exit;
+
+ }
/*
* No PRCMU interrupt was pending => continue the
* sleeping stages
*/
-
- /* Copy GIC interrupt settings to PRCMU interrupt settings */
- ux500_pm_prcmu_copy_gic_settings();
}
- if (cstates[target].APE == APE_OFF) {
+ if (master && (cstates[target].APE == APE_OFF)) {
ktime_t est_wake_time;
int wake_cpu;
@@ -445,18 +460,20 @@ static int enter_sleep(struct cpuidle_device *dev,
sleep_time = get_remaining_sleep_time(&est_wake_time,
&wake_cpu);
- if (sleep_time == UINT_MAX)
+ if (sleep_time == UINT_MAX) {
+ atomic_dec(&master_counter);
goto exit;
+ }
if (cstates[target].UL_PLL == UL_PLL_OFF)
/* Compensate for ULPLL start up time */
sleep_time -= UL_PLL_START_UP_LATENCY;
- /*
- * Not checking for negative sleep time since
- * determine_sleep_state has already checked that
- * there is enough time.
- */
+ /*
+ * Not checking for negative sleep time since
+ * determine_sleep_state has already checked that
+ * there is enough time.
+ */
/* Adjust for exit latency */
sleep_time -= MIN_SLEEP_WAKE_UP_LATENCY;
@@ -480,17 +497,25 @@ static int enter_sleep(struct cpuidle_device *dev,
spin_unlock(&cpuidle_lock);
}
- if (cstates[target].ARM == ARM_OFF) {
+ if (master && (cstates[target].ARM == ARM_OFF)) {
+ int cpu;
+
context_varm_save_common();
+
spin_lock(&cpuidle_lock);
restore_arm = true;
+ for_each_possible_cpu(cpu) {
+ (per_cpu(cpu_state, cpu))->restore_arm_core = true;
+ }
spin_unlock(&cpuidle_lock);
}
- if (cstates[target].ARM == ARM_OFF ||
- cstates[state->gov_cstate].ARM == ARM_OFF) {
- restore_local_arm = true;
+ if (cstates[state->gov_cstate].ARM == ARM_OFF) {
context_varm_save_core();
+
+ if (master && (cstates[target].ARM == ARM_OFF))
+ context_gic_dist_disable_unneeded_irqs();
+
context_save_cpu_registers();
/*
@@ -500,43 +525,38 @@ static int enter_sleep(struct cpuidle_device *dev,
* has as little as possible to do.
*/
context_clean_l1_cache_all();
-
}
ux500_ci_dbg_log(target, time_enter);
- if (is_last_cpu_running() && cstates[target].ARM != ARM_ON)
+ if (master && cstates[target].ARM != ARM_ON)
prcmu_set_power_state(cstates[target].pwrst,
cstates[target].UL_PLL,
/* Is actually the AP PLL */
cstates[target].UL_PLL);
+ if (master)
+ atomic_dec(&master_counter);
+
/*
* If deepsleep/deepidle, Save return address to SRAM and set
* this CPU in WFI. This is last core to enter sleep, so we need to
* clean both L2 and L1 caches
*/
- if (cstates[state->gov_cstate].ARM == ARM_ON)
+ if (cstates[state->gov_cstate].ARM == ARM_OFF)
+ context_save_to_sram_and_wfi(cstates[target].ARM == ARM_OFF);
+ else
__asm__ __volatile__
("dsb\n\t" "wfi\n\t" : : : "memory");
- else
- context_save_to_sram_and_wfi(cstates[state->gov_cstate].ARM == ARM_OFF,
- cstates[target].ARM == ARM_OFF);
if (is_last_cpu_running())
ux500_ci_dbg_wake_latency(target, sleep_time);
time_wake = ktime_get();
- /* The PRCMU restores ARM PLL and recouples the GIC */
- if (restore_local_arm) {
- context_restore_cpu_registers();
- context_varm_restore_core();
- }
-
slept_well = true;
- restore_sequence(time_wake);
+ restore_sequence(state, time_wake);
exit:
if (!slept_well)
@@ -544,10 +564,13 @@ exit:
ux500_pm_gic_recouple();
/* Use the ARM local timer for this cpu */
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
- &this_cpu);
+ if (migrate_timer)
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
+ &this_cpu);
exit_fast:
+ atomic_dec(&idle_cpus_counter);
+
if (target < 0)
target = CI_RUNNING;
@@ -584,8 +607,6 @@ exit_fast:
ux500_ci_dbg_log(CI_RUNNING, time_exit);
- atomic_dec(&idle_cpus_counter);
-
local_irq_enable();
ux500_ci_dbg_console();
@@ -681,7 +702,7 @@ static void __exit cpuidle_driver_exit(void)
cpuidle_unregister_driver(&cpuidle_drv);
}
-module_init(cpuidle_driver_init);
+late_initcall(cpuidle_driver_init);
module_exit(cpuidle_driver_exit);
MODULE_DESCRIPTION("U8500 cpuidle driver");
diff --git a/arch/arm/mach-ux500/pm/cpuidle.h b/arch/arm/mach-ux500/pm/cpuidle.h
index f15338aa48b..265cce7de59 100644
--- a/arch/arm/mach-ux500/pm/cpuidle.h
+++ b/arch/arm/mach-ux500/pm/cpuidle.h
@@ -22,11 +22,6 @@ enum APE {
APE_ON
};
-enum ARM_PLL {
- ARM_PLL_OFF = 0,
- ARM_PLL_ON = 1
-};
-
enum UL_PLL {
UL_PLL_OFF,
UL_PLL_ON
@@ -50,7 +45,6 @@ struct cstate {
/* Required state of different hardwares */
enum ARM ARM;
enum APE APE;
- enum ARM_PLL ARM_PLL;
enum UL_PLL UL_PLL;
/* ESRAM = ESRAM_RET means that ESRAM context to be kept */
enum ESRAM ESRAM;
diff --git a/arch/arm/mach-ux500/pm/cpuidle_dbg.c b/arch/arm/mach-ux500/pm/cpuidle_dbg.c
index 85c05d42f81..c5e6e6fbdaa 100644
--- a/arch/arm/mach-ux500/pm/cpuidle_dbg.c
+++ b/arch/arm/mach-ux500/pm/cpuidle_dbg.c
@@ -18,9 +18,9 @@
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/gpio/nomadik.h>
#include <linux/amba/serial.h>
-#include <linux/gpio.h>
#include <asm/hardware/gic.h>
#include "cpuidle.h"
@@ -48,8 +48,8 @@ struct state_history_state {
u32 hit_rate;
u32 state_ok;
u32 state_error;
- u32 state_int;
- u32 sga;
+ u32 prcmu_int;
+ u32 pending_int;
u32 latency_count[NUM_LATENCY];
ktime_t latency_sum[NUM_LATENCY];
@@ -114,8 +114,8 @@ void ux500_ci_dbg_console_handle_ape_suspend(void)
if (!dbg_console_enable)
return;
- set_irq_wake(NOMADIK_GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 1);
- set_irq_type(NOMADIK_GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN),
+ irq_set_irq_wake(GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 1);
+ irq_set_irq_type(GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN),
IRQ_TYPE_EDGE_BOTH);
}
@@ -134,7 +134,7 @@ void ux500_ci_dbg_console_handle_ape_resume(void)
reset_timer = true;
spin_unlock_irqrestore(&dbg_lock, flags);
}
- set_irq_wake(NOMADIK_GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 0);
+ irq_set_irq_wake(GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 0);
}
@@ -258,11 +258,11 @@ void ux500_ci_dbg_exit_latency(int ctarget, ktime_t now, ktime_t exit,
if (cstates[ctarget].state == CI_DEEP_IDLE)
sh->states[ctarget].state_ok++;
break;
- case ARM2PRCMUPENDINGIT_ER:
- sh->states[ctarget].state_int++;
+ case PRCMU2ARMPENDINGIT_ER:
+ sh->states[ctarget].prcmu_int++;
break;
- case HWACFCT_IN_SGA:
- sh->states[ctarget].sga++;
+ case ARMPENDINGIT_ER:
+ sh->states[ctarget].pending_int++;
break;
default:
pr_info("cpuidle: unknown prcmu exit code: 0x%x state: %d\n",
@@ -438,8 +438,8 @@ static void state_history_reset(void)
sh->states[i].hit_rate = 0;
sh->states[i].state_ok = 0;
sh->states[i].state_error = 0;
- sh->states[i].state_int = 0;
- sh->states[i].sga = 0;
+ sh->states[i].prcmu_int = 0;
+ sh->states[i].pending_int = 0;
sh->states[i].time = ktime_set(0, 0);
@@ -596,10 +596,10 @@ static void stats_disp_one(struct seq_file *s, struct state_history *sh,
return;
if (i > CI_WFI && verbose)
- seq_printf(s, " (%u int:%u sga: %u err:%u)",
+ seq_printf(s, " (%u prcmu_int:%u int:%u err:%u)",
sh->states[i].state_ok,
- sh->states[i].state_int,
- sh->states[i].sga,
+ sh->states[i].prcmu_int,
+ sh->states[i].pending_int,
sh->states[i].state_error);
seq_printf(s, " in %d ms %d%%",
diff --git a/arch/arm/mach-ux500/pm/pm.c b/arch/arm/mach-ux500/pm/pm.c
index 4617cff1594..ae6951ede45 100644
--- a/arch/arm/mach-ux500/pm/pm.c
+++ b/arch/arm/mach-ux500/pm/pm.c
@@ -12,9 +12,9 @@
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/nomadik.h>
-#include <asm/processor.h>
#include <asm/hardware/gic.h>
+#include <asm/processor.h>
#include <mach/hardware.h>
#include <mach/prcmu-regs.h>
@@ -22,6 +22,7 @@
#include "pm.h"
#define STABILIZATION_TIME 30 /* us */
+#define GIC_FREEZE_DELAY 1 /* us */
#define PRCM_ARM_WFI_STANDBY_CPU0_WFI 0x8
#define PRCM_ARM_WFI_STANDBY_CPU1_WFI 0x10
@@ -62,6 +63,7 @@ void ux500_pm_gic_decouple(void)
cpu_relax();
/* TODO: Use the ack bit when possible */
+ udelay(GIC_FREEZE_DELAY); /* Wait for the GIC to freeze */
}
/* Recouple GIC with the interrupt bus */
@@ -148,8 +150,12 @@ void ux500_pm_gpio_save_wake_up_status(void)
banks = u8500_gpio_banks;
}
+ nmk_gpio_clocks_enable();
+
for (i = 0; i < num_banks; i++)
ux500_gpio_wks[i] = readl(__io_address(banks[i]) + NMK_GPIO_WKS);
+
+ nmk_gpio_clocks_disable();
}
u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_num)
diff --git a/arch/arm/mach-ux500/pm/pm.h b/arch/arm/mach-ux500/pm/pm.h
index 5448047b61e..22625d6dc7d 100644
--- a/arch/arm/mach-ux500/pm/pm.h
+++ b/arch/arm/mach-ux500/pm/pm.h
@@ -16,8 +16,8 @@ enum prcmu_idle_stat {
DEEP_SLEEP_OK = 0xf6,
IDLE_OK = 0xf0,
DEEPIDLE_OK = 0xe3,
- ARM2PRCMUPENDINGIT_ER = 0x91,
- HWACFCT_IN_SGA = 0x93,
+ PRCMU2ARMPENDINGIT_ER = 0x91,
+ ARMPENDINGIT_ER = 0x93,
};
/**
diff --git a/arch/arm/mach-ux500/pm/runtime.c b/arch/arm/mach-ux500/pm/runtime.c
index 265faf3085d..40b360b684b 100644
--- a/arch/arm/mach-ux500/pm/runtime.c
+++ b/arch/arm/mach-ux500/pm/runtime.c
@@ -19,7 +19,7 @@
#include <linux/amba/bus.h>
#include <linux/clk.h>
#include <plat/pincfg.h>
-#include <linux/regulator/dbx500-prcmu.h>
+#include <mach/regulator.h>
#include "../pins.h"
diff --git a/arch/arm/mach-ux500/pm/suspend.c b/arch/arm/mach-ux500/pm/suspend.c
index 35bce047754..4a4f0071a6e 100644
--- a/arch/arm/mach-ux500/pm/suspend.c
+++ b/arch/arm/mach-ux500/pm/suspend.c
@@ -12,9 +12,9 @@
#include <linux/suspend.h>
#include <linux/gpio.h>
-#include <linux/gpio/nomadik.h>
#include <linux/delay.h>
#include <linux/regulator/ab8500-debug.h>
+#include <linux/gpio/nomadik.h>
#include <mach/prcmu.h>
#include <mach/prcmu-regs.h>
@@ -59,6 +59,8 @@ static int suspend(bool do_deepsleep)
return -EBUSY;
}
+ nmk_gpio_clocks_enable();
+
ux500_suspend_dbg_add_wake_on_uart();
nmk_gpio_wakeups_suspend();
@@ -85,9 +87,6 @@ static int suspend(bool do_deepsleep)
ux500_pm_gic_decouple();
- /* TODO: decouple gic should look at status bit.*/
- udelay(100);
-
if (ux500_pm_gic_pending_interrupt()) {
pr_info("suspend/resume: pending interrupt\n");
@@ -102,6 +101,7 @@ static int suspend(bool do_deepsleep)
if (do_deepsleep) {
context_varm_save_common();
context_varm_save_core();
+ context_gic_dist_disable_unneeded_irqs();
context_save_cpu_registers();
/*
@@ -114,7 +114,7 @@ static int suspend(bool do_deepsleep)
(void) prcmu_set_power_state(PRCMU_AP_DEEP_SLEEP,
false, false);
- context_save_to_sram_and_wfi(true, true);
+ context_save_to_sram_and_wfi(true);
context_restore_cpu_registers();
context_varm_restore_core();
@@ -155,6 +155,8 @@ exit:
nmk_gpio_wakeups_resume();
ux500_suspend_dbg_remove_wake_on_uart();
+ nmk_gpio_clocks_disable();
+
return ret;
}
diff --git a/arch/arm/mach-ux500/pm/suspend_dbg.c b/arch/arm/mach-ux500/pm/suspend_dbg.c
index ff6bb829539..d89e280c9dd 100644
--- a/arch/arm/mach-ux500/pm/suspend_dbg.c
+++ b/arch/arm/mach-ux500/pm/suspend_dbg.c
@@ -42,14 +42,14 @@ static u32 suspend_count;
#ifdef CONFIG_UX500_SUSPEND_DBG_WAKE_ON_UART
void ux500_suspend_dbg_add_wake_on_uart(void)
{
- set_irq_wake(NOMADIK_GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 1);
- set_irq_type(NOMADIK_GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN),
+ irq_set_irq_wake(GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 1);
+ irq_set_irq_type(GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN),
IRQ_TYPE_EDGE_BOTH);
}
void ux500_suspend_dbg_remove_wake_on_uart(void)
{
- set_irq_wake(NOMADIK_GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 0);
+ irq_set_irq_wake(GPIO_TO_IRQ(CONFIG_UX500_CONSOLE_UART_GPIO_PIN), 0);
}
#endif
diff --git a/arch/arm/mach-ux500/pm/timer.c b/arch/arm/mach-ux500/pm/timer.c
index 76c590a420f..12553a3351b 100644
--- a/arch/arm/mach-ux500/pm/timer.c
+++ b/arch/arm/mach-ux500/pm/timer.c
@@ -72,11 +72,10 @@
#define US_TO_TICKS(x) ((u32)((1000 * x) / 30512))
static void __iomem *rtc_base;
+static bool measure_latency;
#ifdef CONFIG_U8500_CPUIDLE_DEBUG
-static bool measure_latency;
-
/*
* The plan here is to be able to measure the ApSleep/ApDeepSleep exit latency
* by having a know timer pattern.
@@ -159,12 +158,9 @@ static inline void ux500_rtcrtt_measure_latency(bool enable) { }
void ux500_rtcrtt_off(void)
{
-#ifdef CONFIG_U8500_CPUIDLE_DEBUG
if (measure_latency) {
measure_latency_start();
- } else
-#endif
- {
+ } else {
/* Clear eventual interrupts */
if (readl(rtc_base + RTC_MIS) & RTC_MIS_RTCTMIS)
writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
@@ -189,9 +185,7 @@ static int __init ux500_rtcrtt_init(void)
pr_err("timer-rtt: Unknown DB Asic!\n");
return -EINVAL;
}
-#ifdef CONFIG_U8500_CPUIDLE_DEBUG
ux500_rtcrtt_measure_latency(false);
-#endif
return 0;
}
subsys_initcall(ux500_rtcrtt_init);
diff --git a/arch/arm/mach-ux500/pm/usecase_gov.c b/arch/arm/mach-ux500/pm/usecase_gov.c
index 78f157d7f60..2a6b36cfaef 100644
--- a/arch/arm/mach-ux500/pm/usecase_gov.c
+++ b/arch/arm/mach-ux500/pm/usecase_gov.c
@@ -67,11 +67,12 @@ static unsigned long max_instant = 85;
static u32 exit_irq_per_s = 1000;
static u64 old_num_irqs;
-static DEFINE_MUTEX(user_config_mutex);
+static DEFINE_MUTEX(usecase_mutex);
static bool user_config_updated;
static enum ux500_uc current_uc = UX500_UC_MAX;
static bool is_work_scheduled;
static bool is_early_suspend;
+static bool uc_master_enable = true;
static unsigned int cpuidle_deepest_state;
@@ -112,8 +113,8 @@ static struct usecase_config usecase_conf[UX500_UC_MAX] = {
},
[UX500_UC_VC] = {
.name = "voice-call",
- .max_freq = 200000,
- .min_freq = 200000,
+ .max_freq = 400000,
+ .min_freq = 400000,
.cpuidle_multiplier = 0,
.second_cpu_online = false,
.l2_prefetch_en = false,
@@ -404,6 +405,44 @@ exit:
user_config_updated = false;
}
+void usecase_update_governor_state(void)
+{
+ bool cancel_work = false;
+
+ mutex_lock(&usecase_mutex);
+
+ if (uc_master_enable && (usecase_conf[UX500_UC_AUTO].enable ||
+ usecase_conf[UX500_UC_USER].enable)) {
+ /*
+ * Usecases are enabled. If we are in early suspend put
+ * governor to work.
+ */
+ if (is_early_suspend && !is_work_scheduled) {
+ schedule_delayed_work_on(0, &work_usecase,
+ msecs_to_jiffies(CPULOAD_MEAS_DELAY));
+ is_work_scheduled = true;
+ } else if (!is_early_suspend && is_work_scheduled) {
+ /* Exiting from early suspend. */
+ cancel_work = true;
+ }
+
+ } else if (is_work_scheduled) {
+ /* No usecase enabled or governor is not enabled. */
+ cancel_work = true;
+ }
+
+ if (cancel_work) {
+ cancel_delayed_work_sync(&work_usecase);
+ is_work_scheduled = false;
+
+ /* Set the default settings before exiting. */
+ set_cpu_config(UX500_UC_NORMAL);
+ }
+
+ mutex_unlock(&usecase_mutex);
+
+}
+
/*
* Start load measurment every 6 s in order detrmine if can unplug one CPU.
* In order to not corrupt measurment, the first load average is not done
@@ -413,37 +452,17 @@ static void usecase_earlysuspend_callback(struct early_suspend *h)
{
init_cpu_load_trend();
- mutex_lock(&user_config_mutex);
-
is_early_suspend = true;
- if (usecase_conf[UX500_UC_AUTO].enable ||
- usecase_conf[UX500_UC_USER].enable) {
-
- is_work_scheduled = true;
-
- schedule_delayed_work_on(0, &work_usecase,
- msecs_to_jiffies(CPULOAD_MEAS_DELAY));
- }
-
- mutex_unlock(&user_config_mutex);
+ usecase_update_governor_state();
}
/* Stop measurement, call LCD early resume */
static void usecase_lateresume_callback(struct early_suspend *h)
{
- mutex_lock(&user_config_mutex);
-
- if (is_work_scheduled) {
- cancel_delayed_work_sync(&work_usecase);
- is_work_scheduled = false;
- }
-
is_early_suspend = false;
- set_cpu_config(UX500_UC_NORMAL);
-
- mutex_unlock(&user_config_mutex);
+ usecase_update_governor_state();
}
static void delayed_usecase_work(struct work_struct *work)
@@ -475,7 +494,7 @@ static void delayed_usecase_work(struct work_struct *work)
irqs_per_s = get_num_interrupts_per_s();
/* Dont let configuration change in the middle of our calculations. */
- mutex_lock(&user_config_mutex);
+ mutex_lock(&usecase_mutex);
/* detect "instant" load increase */
if (load > max_instant || irqs_per_s > exit_irq_per_s) {
@@ -516,7 +535,7 @@ static void delayed_usecase_work(struct work_struct *work)
set_cpu_config(UX500_UC_NORMAL);
}
- mutex_unlock(&user_config_mutex);
+ mutex_unlock(&usecase_mutex);
/* reprogramm scheduled work */
schedule_delayed_work_on(0, &work_usecase,
@@ -663,7 +682,7 @@ static void usecase_update_user_config(void)
bool config_enable = false;
struct usecase_config *user_conf = &usecase_conf[UX500_UC_USER];
- mutex_lock(&user_config_mutex);
+ mutex_lock(&usecase_mutex);
user_conf->max_freq = 0;
user_conf->min_freq = 0;
@@ -711,7 +730,7 @@ static void usecase_update_user_config(void)
user_conf->enable = config_enable;
user_config_updated = true;
- mutex_unlock(&user_config_mutex);
+ mutex_unlock(&usecase_mutex);
}
struct usecase_devclass_attr {
@@ -719,9 +738,14 @@ struct usecase_devclass_attr {
u32 index;
};
-static struct usecase_devclass_attr usecase_dc_attr[UX500_UC_MAX];
+/* One for each usecase except "user" + current + enable */
+#define UX500_NUM_SYSFS_NODES (UX500_UC_USER + 2)
+#define UX500_CURRENT_NODE_INDEX (UX500_NUM_SYSFS_NODES - 1)
+#define UX500_ENABLE_NODE_INDEX (UX500_NUM_SYSFS_NODES - 2)
+
+static struct usecase_devclass_attr usecase_dc_attr[UX500_NUM_SYSFS_NODES];
-static struct attribute *dbs_attributes[UX500_UC_MAX + 1] = {NULL};
+static struct attribute *dbs_attributes[UX500_NUM_SYSFS_NODES + 1] = {NULL};
static struct attribute_group dbs_attr_group = {
.attrs = dbs_attributes,
@@ -735,12 +759,14 @@ static ssize_t show_current(struct sysdev_class *class,
UX500_UC_NORMAL : current_uc;
return sprintf(buf, "max_freq: %ld\n"
+ "min_freq: %ld\n"
"cpuidle_multiplier: %ld\n"
"second_cpu_online: %s\n"
"l2_prefetch_en: %s\n"
"forced_state: %d\n"
"vc_override: %s\n",
usecase_conf[display_uc].max_freq,
+ usecase_conf[display_uc].min_freq,
usecase_conf[display_uc].cpuidle_multiplier,
usecase_conf[display_uc].second_cpu_online ? "true" : "false",
usecase_conf[display_uc].l2_prefetch_en ? "true" : "false",
@@ -748,6 +774,30 @@ static ssize_t show_current(struct sysdev_class *class,
usecase_conf[display_uc].vc_override ? "true" : "false");
}
+static ssize_t show_enable(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", uc_master_enable);
+}
+
+static ssize_t store_enable(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+
+ ret = sscanf(buf, "%u", &input);
+ if (ret != 1)
+ return -EINVAL;
+
+ uc_master_enable = (bool) input;
+
+ usecase_update_governor_state();
+
+ return count;
+}
+
static ssize_t show_dc_attr(struct sysdev_class *class,
struct sysdev_class_attribute *attr, char *buf)
{
@@ -778,22 +828,7 @@ static ssize_t store_dc_attr(struct sysdev_class *class,
usecase_update_user_config();
- mutex_lock(&user_config_mutex);
-
- if (usecase_conf[UX500_UC_AUTO].enable ||
- usecase_conf[UX500_UC_USER].enable) {
- if (is_early_suspend && !is_work_scheduled) {
- schedule_delayed_work_on(0, &work_usecase,
- msecs_to_jiffies(CPULOAD_MEAS_DELAY));
- is_work_scheduled = true;
- }
- } else if (is_work_scheduled) {
- cancel_delayed_work_sync(&work_usecase);
- is_work_scheduled = false;
- set_cpu_config(UX500_UC_NORMAL);
- }
-
- mutex_unlock(&user_config_mutex);
+ usecase_update_governor_state();
return count;
}
@@ -803,7 +838,8 @@ static int usecase_sysfs_init(void)
int err;
int i;
- for (i = 0; i < (ARRAY_SIZE(usecase_conf) - 1); i++) {
+ /* Last two nodes are not based on usecase configurations */
+ for (i = 0; i < (UX500_NUM_SYSFS_NODES - 2); i++) {
usecase_dc_attr[i].class_attr.attr.name = usecase_conf[i].name;
usecase_dc_attr[i].class_attr.attr.mode = 0644;
usecase_dc_attr[i].class_attr.show = show_dc_attr;
@@ -812,13 +848,34 @@ static int usecase_sysfs_init(void)
dbs_attributes[i] = &(usecase_dc_attr[i].class_attr.attr);
}
- usecase_dc_attr[UX500_UC_USER].class_attr.attr.name = "current";
- usecase_dc_attr[UX500_UC_USER].class_attr.attr.mode = 0644;
- usecase_dc_attr[UX500_UC_USER].class_attr.show = show_current;
- usecase_dc_attr[UX500_UC_USER].class_attr.store = NULL;
- usecase_dc_attr[UX500_UC_USER].index = UX500_UC_USER;
- dbs_attributes[UX500_UC_USER] =
- &(usecase_dc_attr[UX500_UC_USER].class_attr.attr);
+
+ /* sysfs current */
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr.name =
+ "current";
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr.mode =
+ 0644;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.show =
+ show_current;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.store =
+ NULL;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].index =
+ 0;
+ dbs_attributes[UX500_CURRENT_NODE_INDEX] =
+ &(usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr);
+
+ /* sysfs enable */
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr.name =
+ "enable";
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr.mode =
+ 0644;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.show =
+ show_enable;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.store =
+ store_enable;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].index =
+ 0;
+ dbs_attributes[UX500_ENABLE_NODE_INDEX] =
+ &(usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr);
err = sysfs_create_group(&(cpu_sysdev_class.kset.kobj),
&dbs_attr_group);
diff --git a/arch/arm/mach-ux500/prcmu-db5500.c b/arch/arm/mach-ux500/prcmu-db5500.c
new file mode 100644
index 00000000000..a0d5fa83b52
--- /dev/null
+++ b/arch/arm/mach-ux500/prcmu-db5500.c
@@ -0,0 +1,1618 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ *
+ * U5500 PRCM Unit interface driver
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/prcmu.h>
+#include <mach/db5500-regs.h>
+
+#include "prcmu-debug.h"
+#include "prcmu-regs-db5500.h"
+
+#define PRCMU_FW_VERSION_OFFSET 0xA4
+#define PRCM_DDR_FREQUENCY (tcdm_base + 0x268) /* 4 bytes */
+#define PRCM_SW_RST_REASON (tcdm_base + 0xFF8) /* 2 bytes */
+
+#define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
+#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
+#define PRCM_REQ_MB1_HEADER (_PRCM_MB_HEADER + 0x1)
+#define PRCM_REQ_MB2_HEADER (_PRCM_MB_HEADER + 0x2)
+#define PRCM_REQ_MB3_HEADER (_PRCM_MB_HEADER + 0x3)
+#define PRCM_REQ_MB4_HEADER (_PRCM_MB_HEADER + 0x4)
+#define PRCM_REQ_MB5_HEADER (_PRCM_MB_HEADER + 0x5)
+#define PRCM_REQ_MB6_HEADER (_PRCM_MB_HEADER + 0x6)
+#define PRCM_REQ_MB7_HEADER (_PRCM_MB_HEADER + 0x7)
+#define PRCM_ACK_MB0_HEADER (_PRCM_MB_HEADER + 0x8)
+#define PRCM_ACK_MB1_HEADER (_PRCM_MB_HEADER + 0x9)
+#define PRCM_ACK_MB2_HEADER (_PRCM_MB_HEADER + 0xa)
+#define PRCM_ACK_MB3_HEADER (_PRCM_MB_HEADER + 0xb)
+#define PRCM_ACK_MB4_HEADER (_PRCM_MB_HEADER + 0xc)
+#define PRCM_ACK_MB5_HEADER (_PRCM_MB_HEADER + 0xd)
+#define PRCM_ACK_MB6_HEADER (_PRCM_MB_HEADER + 0xe)
+#define PRCM_ACK_MB7_HEADER (_PRCM_MB_HEADER + 0xf)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0 (tcdm_base + 0xFD8)
+#define PRCM_REQ_MB1 (tcdm_base + 0xFCC)
+#define PRCM_REQ_MB2 (tcdm_base + 0xFC4)
+#define PRCM_REQ_MB3 (tcdm_base + 0xFC0)
+#define PRCM_REQ_MB4 (tcdm_base + 0xF98)
+#define PRCM_REQ_MB5 (tcdm_base + 0xF90)
+#define PRCM_REQ_MB6 (tcdm_base + 0xF8C)
+#define PRCM_REQ_MB7 (tcdm_base + 0xF84)
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0 (tcdm_base + 0xF38)
+#define PRCM_ACK_MB1 (tcdm_base + 0xF30)
+#define PRCM_ACK_MB2 (tcdm_base + 0xF24)
+#define PRCM_ACK_MB3 (tcdm_base + 0xF20)
+#define PRCM_ACK_MB4 (tcdm_base + 0xF1C)
+#define PRCM_ACK_MB5 (tcdm_base + 0xF14)
+#define PRCM_ACK_MB6 (tcdm_base + 0xF0C)
+#define PRCM_ACK_MB7 (tcdm_base + 0xF08)
+
+/* Mailbox 0 REQs */
+#define PRCM_REQ_MB0_AP_POWER_STATE (PRCM_REQ_MB0 + 0x0)
+#define PRCM_REQ_MB0_ULP_CLOCK_STATE (PRCM_REQ_MB0 + 0x1)
+#define PRCM_REQ_MB0_AP_PLL_STATE (PRCM_REQ_MB0 + 0x2)
+#define PRCM_REQ_MB0_DDR_STATE (PRCM_REQ_MB0 + 0x3)
+#define PRCM_REQ_MB0_ESRAM0_STATE (PRCM_REQ_MB0 + 0x4)
+#define PRCM_REQ_MB0_WAKEUP_DBB (PRCM_REQ_MB0 + 0x8)
+#define PRCM_REQ_MB0_WAKEUP_ABB (PRCM_REQ_MB0 + 0xC)
+
+/* Mailbox 0 ACKs */
+#define PRCM_ACK_MB0_AP_PWRSTTR_STATUS (PRCM_ACK_MB0 + 0x0)
+#define PRCM_ACK_MB0_READ_POINTER (PRCM_ACK_MB0 + 0x1)
+#define PRCM_ACK_MB0_WAKEUP_0_DBB (PRCM_ACK_MB0 + 0x4)
+#define PRCM_ACK_MB0_WAKEUP_0_ABB (PRCM_ACK_MB0 + 0x8)
+#define PRCM_ACK_MB0_WAKEUP_1_DBB (PRCM_ACK_MB0 + 0x28)
+#define PRCM_ACK_MB0_WAKEUP_1_ABB (PRCM_ACK_MB0 + 0x2C)
+#define PRCM_ACK_MB0_EVENT_ABB_NUMBERS 20
+
+/* Request mailbox 1 fields. */
+#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0)
+#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1)
+
+/* Mailbox 1 ACKs */
+#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0)
+#define PRCM_ACK_MB1_CURRENT_APE_OPP (PRCM_ACK_MB1 + 0x1)
+#define PRCM_ACK_MB1_ARM_VOLT_STATUS (PRCM_ACK_MB1 + 0x2)
+#define PRCM_ACK_MB1_APE_VOLT_STATUS (PRCM_ACK_MB1 + 0x3)
+
+/* Mailbox 2 REQs */
+#define PRCM_REQ_MB2_EPOD_CLIENT (PRCM_REQ_MB2 + 0x0)
+#define PRCM_REQ_MB2_EPOD_STATE (PRCM_REQ_MB2 + 0x1)
+#define PRCM_REQ_MB2_CLK_CLIENT (PRCM_REQ_MB2 + 0x2)
+#define PRCM_REQ_MB2_CLK_STATE (PRCM_REQ_MB2 + 0x3)
+#define PRCM_REQ_MB2_PLL_CLIENT (PRCM_REQ_MB2 + 0x4)
+#define PRCM_REQ_MB2_PLL_STATE (PRCM_REQ_MB2 + 0x5)
+
+/* Mailbox 2 ACKs */
+#define PRCM_ACK_MB2_EPOD_STATUS (PRCM_ACK_MB2 + 0x2)
+#define PRCM_ACK_MB2_CLK_STATUS (PRCM_ACK_MB2 + 0x6)
+#define PRCM_ACK_MB2_PLL_STATUS (PRCM_ACK_MB2 + 0xA)
+
+enum mb_return_code {
+ RC_SUCCESS,
+ RC_FAIL,
+};
+
+/* Mailbox 0 headers. */
+enum mb0_header {
+ /* acknowledge */
+ MB0H_WAKE_UP = 0,
+ /* request */
+ MB0H_PWR_STATE_TRANS,
+ MB0H_WAKE_UP_CFG,
+ MB0H_RD_WAKE_UP_ACK,
+};
+
+/* Mailbox 1 headers.*/
+enum mb1_header {
+ MB1H_ARM_OPP = 1,
+ MB1H_APE_OPP,
+ MB1H_ARM_APE_OPP,
+};
+
+/* Mailbox 2 headers. */
+enum mb2_header {
+ MB2H_EPOD_REQUEST = 1,
+ MB2H_CLK_REQUEST,
+ MB2H_PLL_REQUEST,
+};
+
+/* Mailbox 3 headers */
+enum mb3_header {
+ MB3H_REFCLK_REQUEST = 1,
+};
+
+enum sysclk_state {
+ SYSCLK_OFF,
+ SYSCLK_ON,
+};
+
+/* Mailbox 5 headers. */
+enum mb5_header {
+ MB5H_I2C_WRITE = 1,
+ MB5H_I2C_READ,
+};
+
+enum db5500_arm_opp {
+ DB5500_ARM_100_OPP = 1,
+ DB5500_ARM_50_OPP,
+ DB5500_ARM_EXT_OPP,
+};
+
+enum epod_state {
+ EPOD_OFF,
+ EPOD_ON,
+};
+enum epod_onoffret_state {
+ EPOD_OOR_OFF,
+ EPOD_OOR_RET,
+ EPOD_OOR_ON,
+};
+enum db5500_prcmu_pll {
+ DB5500_PLL_SOC0,
+ DB5500_PLL_SOC1,
+ DB5500_PLL_DDR,
+ DB5500_NUM_PLL_ID,
+};
+
+enum db5500_prcmu_clk {
+ DB5500_MSP1CLK,
+ DB5500_CDCLK,
+ DB5500_IRDACLK,
+ DB5500_TVCLK,
+ DB5500_NUM_CLK_CLIENTS,
+};
+
+enum on_off_ret {
+ OFF_ST,
+ RET_ST,
+ ON_ST,
+};
+
+enum db5500_ap_pwr_state {
+ DB5500_AP_SLEEP = 2,
+ DB5500_AP_DEEP_SLEEP,
+ DB5500_AP_IDLE,
+};
+
+/* Request mailbox 3 fields */
+#define PRCM_REQ_MB3_REFCLK_MGT (PRCM_REQ_MB3 + 0x0)
+
+/* Ack. mailbox 3 fields */
+#define PRCM_ACK_MB3_REFCLK_REQ (PRCM_ACK_MB3 + 0x0)
+
+/* Request mailbox 5 fields. */
+#define PRCM_REQ_MB5_I2C_SLAVE (PRCM_REQ_MB5 + 0)
+#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 1)
+#define PRCM_REQ_MB5_I2C_SIZE (PRCM_REQ_MB5 + 2)
+#define PRCM_REQ_MB5_I2C_DATA (PRCM_REQ_MB5 + 4)
+
+/* Acknowledge mailbox 5 fields. */
+#define PRCM_ACK_MB5_RETURN_CODE (PRCM_ACK_MB5 + 0)
+#define PRCM_ACK_MB5_I2C_DATA (PRCM_ACK_MB5 + 4)
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/*
+* Used by MCDE to setup all necessary PRCMU registers
+*/
+#define PRCMU_RESET_DSIPLL 0x00004000
+#define PRCMU_UNCLAMP_DSIPLL 0x00400800
+
+/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0x8, = 50 Mhz*/
+#define PRCMU_DSI_CLOCK_SETTING 0x00000128
+/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
+#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
+#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
+#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
+#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
+#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
+
+#define PRCMU_ENABLE_PLLDSI 0x00000001
+#define PRCMU_DISABLE_PLLDSI 0x00000000
+
+#define PRCMU_DSI_RESET_SW 0x00000003
+#define PRCMU_RESOUTN0_PIN 0x00000001
+#define PRCMU_RESOUTN1_PIN 0x00000002
+#define PRCMU_RESOUTN2_PIN 0x00000004
+
+#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
+
+/*
+ * Wakeups/IRQs
+ */
+
+#define WAKEUP_BIT_RTC BIT(0)
+#define WAKEUP_BIT_RTT0 BIT(1)
+#define WAKEUP_BIT_RTT1 BIT(2)
+#define WAKEUP_BIT_CD_IRQ BIT(3)
+#define WAKEUP_BIT_SRP_TIM BIT(4)
+#define WAKEUP_BIT_APE_REQ BIT(5)
+#define WAKEUP_BIT_USB BIT(6)
+#define WAKEUP_BIT_ABB BIT(7)
+#define WAKEUP_BIT_LOW_POWER_AUDIO BIT(8)
+#define WAKEUP_BIT_TEMP_SENSOR_LOW BIT(9)
+#define WAKEUP_BIT_ARM BIT(10)
+#define WAKEUP_BIT_AC_WAKE_ACK BIT(11)
+#define WAKEUP_BIT_TEMP_SENSOR_HIGH BIT(12)
+#define WAKEUP_BIT_MODEM_SW_RESET_REQ BIT(20)
+#define WAKEUP_BIT_GPIO0 BIT(23)
+#define WAKEUP_BIT_GPIO1 BIT(24)
+#define WAKEUP_BIT_GPIO2 BIT(25)
+#define WAKEUP_BIT_GPIO3 BIT(26)
+#define WAKEUP_BIT_GPIO4 BIT(27)
+#define WAKEUP_BIT_GPIO5 BIT(28)
+#define WAKEUP_BIT_GPIO6 BIT(29)
+#define WAKEUP_BIT_GPIO7 BIT(30)
+#define WAKEUP_BIT_AC_REL_ACK BIT(30)
+
+/*
+ * This vector maps irq numbers to the bits in the bit field used in
+ * communication with the PRCMU firmware.
+ *
+ * The reason for having this is to keep the irq numbers contiguous even though
+ * the bits in the bit field are not. (The bits also have a tendency to move
+ * around, to further complicate matters.)
+ */
+#define IRQ_INDEX(_name) ((IRQ_PRCMU_##_name) - IRQ_PRCMU_BASE)
+#define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name)
+static u32 prcmu_irq_bit[NUM_PRCMU_WAKEUPS] = {
+ IRQ_ENTRY(RTC),
+ IRQ_ENTRY(RTT0),
+ IRQ_ENTRY(RTT1),
+ IRQ_ENTRY(CD_IRQ),
+ IRQ_ENTRY(SRP_TIM),
+ IRQ_ENTRY(APE_REQ),
+ IRQ_ENTRY(USB),
+ IRQ_ENTRY(ABB),
+ IRQ_ENTRY(LOW_POWER_AUDIO),
+ IRQ_ENTRY(TEMP_SENSOR_LOW),
+ IRQ_ENTRY(TEMP_SENSOR_HIGH),
+ IRQ_ENTRY(ARM),
+ IRQ_ENTRY(AC_WAKE_ACK),
+ IRQ_ENTRY(MODEM_SW_RESET_REQ),
+ IRQ_ENTRY(GPIO0),
+ IRQ_ENTRY(GPIO1),
+ IRQ_ENTRY(GPIO2),
+ IRQ_ENTRY(GPIO3),
+ IRQ_ENTRY(GPIO4),
+ IRQ_ENTRY(GPIO5),
+ IRQ_ENTRY(GPIO6),
+ IRQ_ENTRY(GPIO7),
+ IRQ_ENTRY(AC_REL_ACK),
+};
+
+#define VALID_WAKEUPS (BIT(NUM_PRCMU_WAKEUP_INDICES) - 1)
+#define WAKEUP_ENTRY(_name)[PRCMU_WAKEUP_INDEX_##_name] = (WAKEUP_BIT_##_name)
+static u32 prcmu_wakeup_bit[NUM_PRCMU_WAKEUP_INDICES] = {
+ WAKEUP_ENTRY(RTC),
+ WAKEUP_ENTRY(RTT0),
+ WAKEUP_ENTRY(RTT1),
+ WAKEUP_ENTRY(CD_IRQ),
+ WAKEUP_ENTRY(USB),
+ WAKEUP_ENTRY(ABB),
+ WAKEUP_ENTRY(ARM)
+};
+
+/*
+ * mb0_transfer - state needed for mailbox 0 communication.
+ * @lock The transaction lock.
+ * @dbb_irqs_lock lock used for (un)masking DBB wakeup interrupts
+ * @mask_work: Work structure used for (un)masking wakeup interrupts.
+ * @req: Request data that need to persist between requests.
+ */
+static struct {
+ spinlock_t lock;
+ spinlock_t dbb_irqs_lock;
+ struct work_struct mask_work;
+ struct {
+ u32 dbb_irqs;
+ u32 dbb_wakeups;
+ u32 abb_events;
+ } req;
+} mb0_transfer;
+
+
+/*
+ * mb1_transfer - state needed for mailbox 1 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @req_arm_opp Requested arm opp
+ * @req_ape_opp Requested ape opp
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ u8 req_arm_opp;
+ u8 req_ape_opp;
+ struct {
+ u8 header;
+ u8 arm_opp;
+ u8 ape_opp;
+ u8 arm_voltage_st;
+ u8 ape_voltage_st;
+ } ack;
+} mb1_transfer;
+
+/*
+ * mb2_transfer - state needed for mailbox 2 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @req: Request data that need to persist between requests.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 epod_st[DB5500_NUM_EPOD_ID];
+ u8 pll_st[DB5500_NUM_PLL_ID];
+ } req;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb2_transfer;
+
+/*
+ * mb3_transfer - state needed for mailbox 3 communication.
+ * @sysclk_lock: A lock used to handle concurrent sysclk requests.
+ * @sysclk_work: Work structure used for sysclk requests.
+ * @req_st: Requested clock state.
+ * @ack: Acknowledgement data
+ */
+static struct {
+ struct mutex sysclk_lock;
+ struct completion sysclk_work;
+ enum sysclk_state req_st;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb3_transfer;
+
+/*
+ * mb5_transfer - state needed for mailbox 5 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 header;
+ u8 status;
+ u8 value[4];
+ } ack;
+} mb5_transfer;
+
+/* Spinlocks */
+static DEFINE_SPINLOCK(clkout_lock);
+
+/* PRCMU TCDM base IO address. */
+static __iomem void *tcdm_base;
+
+struct clk_mgt {
+ unsigned int offset;
+ u32 pllsw;
+};
+
+/* PRCMU Firmware Details */
+static struct {
+ u16 board;
+ u8 fw_version;
+ u8 api_version;
+} prcmu_version;
+
+static DEFINE_SPINLOCK(clk_mgt_lock);
+
+#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { \
+ (DB5500_PRCM_##_name##_MGT), 0 \
+}
+static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
+ CLK_MGT_ENTRY(SGACLK),
+ CLK_MGT_ENTRY(UARTCLK),
+ CLK_MGT_ENTRY(MSP02CLK),
+ CLK_MGT_ENTRY(I2CCLK),
+ CLK_MGT_ENTRY(SDMMCCLK),
+ CLK_MGT_ENTRY(SPARE1CLK),
+ CLK_MGT_ENTRY(PER1CLK),
+ CLK_MGT_ENTRY(PER2CLK),
+ CLK_MGT_ENTRY(PER3CLK),
+ CLK_MGT_ENTRY(PER5CLK),
+ CLK_MGT_ENTRY(PER6CLK),
+ CLK_MGT_ENTRY(PWMCLK),
+ CLK_MGT_ENTRY(IRDACLK),
+ CLK_MGT_ENTRY(IRRCCLK),
+ CLK_MGT_ENTRY(HDMICLK),
+ CLK_MGT_ENTRY(APEATCLK),
+ CLK_MGT_ENTRY(APETRACECLK),
+ CLK_MGT_ENTRY(MCDECLK),
+ CLK_MGT_ENTRY(DSIALTCLK),
+ CLK_MGT_ENTRY(DMACLK),
+ CLK_MGT_ENTRY(B2R2CLK),
+ CLK_MGT_ENTRY(TVCLK),
+ CLK_MGT_ENTRY(RNGCLK),
+ CLK_MGT_ENTRY(SIACLK),
+ CLK_MGT_ENTRY(SVACLK),
+};
+
+bool prcmu_is_ac_wake_requested(void)
+{
+ return false;
+}
+
+/**
+ * prcmu_config_clkout - Configure one of the programmable clock outputs.
+ * @clkout: The CLKOUT number (0 or 1).
+ * @source: Clock source.
+ * @div: The divider to be applied.
+ *
+ * Configures one of the programmable clock outputs (CLKOUTs).
+ */
+int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
+{
+ static bool configured[2] = {false, false};
+ int r = 0;
+ unsigned long flags;
+ u32 sel_val;
+ u32 div_val;
+ u32 sel_bits;
+ u32 div_bits;
+ u32 sel_mask;
+ u32 div_mask;
+ u8 sel0 = CLKOUT_SEL0_SEL_CLK;
+ u16 sel = 0;
+
+ BUG_ON(clkout > DB5500_CLKOUT1);
+ BUG_ON(source > DB5500_CLKOUT_IRDACLK);
+ BUG_ON(div > 7);
+
+ switch (source) {
+ case DB5500_CLKOUT_REF_CLK_SEL0:
+ sel0 = CLKOUT_SEL0_REF_CLK;
+ break;
+ case DB5500_CLKOUT_RTC_CLK0_SEL0:
+ sel0 = CLKOUT_SEL0_RTC_CLK0;
+ break;
+ case DB5500_CLKOUT_ULP_CLK_SEL0:
+ sel0 = CLKOUT_SEL0_ULP_CLK;
+ break;
+ case DB5500_CLKOUT_STATIC0:
+ sel = CLKOUT_SEL_STATIC0;
+ break;
+ case DB5500_CLKOUT_REFCLK:
+ sel = CLKOUT_SEL_REFCLK;
+ break;
+ case DB5500_CLKOUT_ULPCLK:
+ sel = CLKOUT_SEL_ULPCLK;
+ break;
+ case DB5500_CLKOUT_ARMCLK:
+ sel = CLKOUT_SEL_ARMCLK;
+ break;
+ case DB5500_CLKOUT_SYSACC0CLK:
+ sel = CLKOUT_SEL_SYSACC0CLK;
+ break;
+ case DB5500_CLKOUT_SOC0PLLCLK:
+ sel = CLKOUT_SEL_SOC0PLLCLK;
+ break;
+ case DB5500_CLKOUT_SOC1PLLCLK:
+ sel = CLKOUT_SEL_SOC1PLLCLK;
+ break;
+ case DB5500_CLKOUT_DDRPLLCLK:
+ sel = CLKOUT_SEL_DDRPLLCLK;
+ break;
+ case DB5500_CLKOUT_TVCLK:
+ sel = CLKOUT_SEL_TVCLK;
+ break;
+ case DB5500_CLKOUT_IRDACLK:
+ sel = CLKOUT_SEL_IRDACLK;
+ break;
+ }
+
+ switch (clkout) {
+ case DB5500_CLKOUT0:
+ sel_mask = PRCM_CLKOCR_CLKOUT0_SEL0_MASK |
+ PRCM_CLKOCR_CLKOUT0_SEL_MASK;
+ sel_bits = ((sel0 << PRCM_CLKOCR_CLKOUT0_SEL0_SHIFT) |
+ (sel << PRCM_CLKOCR_CLKOUT0_SEL_SHIFT));
+ div_mask = PRCM_CLKODIV_CLKOUT0_DIV_MASK;
+ div_bits = div << PRCM_CLKODIV_CLKOUT0_DIV_SHIFT;
+ break;
+ case DB5500_CLKOUT1:
+ sel_mask = PRCM_CLKOCR_CLKOUT1_SEL0_MASK |
+ PRCM_CLKOCR_CLKOUT1_SEL_MASK;
+ sel_bits = ((sel0 << PRCM_CLKOCR_CLKOUT1_SEL0_SHIFT) |
+ (sel << PRCM_CLKOCR_CLKOUT1_SEL_SHIFT));
+ div_mask = PRCM_CLKODIV_CLKOUT1_DIV_MASK;
+ div_bits = div << PRCM_CLKODIV_CLKOUT1_DIV_SHIFT;
+ break;
+ }
+
+ spin_lock_irqsave(&clkout_lock, flags);
+
+ if (configured[clkout]) {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+
+ sel_val = readl(_PRCMU_BASE + PRCM_CLKOCR);
+ writel((sel_bits | (sel_val & ~sel_mask)),
+ (_PRCMU_BASE + PRCM_CLKOCR));
+
+ div_val = readl(_PRCMU_BASE + PRCM_CLKODIV);
+ writel((div_bits | (div_val & ~div_mask)),
+ (_PRCMU_BASE + PRCM_CLKODIV));
+
+ configured[clkout] = true;
+
+unlock_and_return:
+ spin_unlock_irqrestore(&clkout_lock, flags);
+
+ return r;
+}
+
+static int request_sysclk(bool enable)
+{
+ int r;
+
+ r = 0;
+ mutex_lock(&mb3_transfer.sysclk_lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
+ cpu_relax();
+
+ if (enable)
+ mb3_transfer.req_st = SYSCLK_ON;
+ else
+ mb3_transfer.req_st = SYSCLK_OFF;
+
+ writeb(mb3_transfer.req_st, (PRCM_REQ_MB3_REFCLK_MGT));
+
+ writeb(MB3H_REFCLK_REQUEST, (PRCM_REQ_MB3_HEADER));
+ writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ /*
+ * The firmware only sends an ACK if we want to enable the
+ * SysClk, and it succeeds.
+ */
+ if (!wait_for_completion_timeout(&mb3_transfer.sysclk_work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ __func__);
+ r = -EIO;
+ WARN(1, "Failed to set sysclk");
+ goto unlock_and_return;
+ }
+
+ if ((mb3_transfer.ack.header != MB3H_REFCLK_REQUEST) ||
+ (mb3_transfer.ack.status != mb3_transfer.req_st)) {
+ r = -EIO;
+ }
+
+unlock_and_return:
+ mutex_unlock(&mb3_transfer.sysclk_lock);
+
+ return r;
+}
+
+static int request_timclk(bool enable)
+{
+ u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
+
+ if (!enable)
+ val |= PRCM_TCR_STOP_TIMERS;
+ writel(val, (_PRCMU_BASE + PRCM_TCR));
+
+ return 0;
+}
+
+static int request_clk(u8 clock, bool enable)
+{
+ int r = 0;
+
+ BUG_ON(clock >= DB5500_NUM_CLK_CLIENTS);
+
+ mutex_lock(&mb2_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* fill in mailbox */
+ writeb(clock, PRCM_REQ_MB2_CLK_CLIENT);
+ writeb(enable, PRCM_REQ_MB2_CLK_STATE);
+
+ writeb(MB2H_CLK_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(500))) {
+ pr_err("prcmu: request_clk() failed.\n");
+ r = -EIO;
+ WARN(1, "Failed in request_clk");
+ goto unlock_and_return;
+ }
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_CLK_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
+static int request_reg_clock(u8 clock, bool enable)
+{
+ u32 val;
+ unsigned long flags;
+
+ WARN_ON(!clk_mgt[clock].offset);
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
+ if (enable) {
+ val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
+ } else {
+ clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+ val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
+ }
+ writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
+
+ /* Release the HW semaphore. */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ return 0;
+}
+
+/*
+ * request_pll() - Request for a pll to be enabled or disabled.
+ * @pll: The pll for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+static int request_pll(u8 pll, bool enable)
+{
+ int r = 0;
+
+ BUG_ON(pll >= DB5500_NUM_PLL_ID);
+ mutex_lock(&mb2_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ mb2_transfer.req.pll_st[pll] = enable;
+
+ /* fill in mailbox */
+ writeb(pll, PRCM_REQ_MB2_PLL_CLIENT);
+ writeb(mb2_transfer.req.pll_st[pll], PRCM_REQ_MB2_PLL_STATE);
+
+ writeb(MB2H_PLL_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(500))) {
+ pr_err("prcmu: set_pll() failed.\n"
+ "prcmu: Please check your firmware version.\n");
+ r = -EIO;
+ WARN(1, "Failed to set pll");
+ goto unlock_and_return;
+ }
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_PLL_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+
+ return r;
+}
+
+/**
+ * prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * @clock: The clock for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+int prcmu_request_clock(u8 clock, bool enable)
+{
+ /* MSP1 & CD clocks are handled by FW */
+ if (clock == PRCMU_MSP1CLK)
+ return request_clk(DB5500_MSP1CLK, enable);
+ else if (clock == PRCMU_CDCLK)
+ return request_clk(DB5500_CDCLK, enable);
+ else if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_reg_clock(clock, enable);
+ else if (clock == PRCMU_TIMCLK)
+ return request_timclk(enable);
+ else if (clock == PRCMU_PLLSOC0)
+ return request_pll(DB5500_PLL_SOC0, enable);
+ else if (clock == PRCMU_PLLSOC1)
+ return request_pll(DB5500_PLL_SOC1, enable);
+ else if (clock == PRCMU_PLLDDR)
+ return request_pll(DB5500_PLL_DDR, enable);
+ else if (clock == PRCMU_SYSCLK)
+ return request_sysclk(enable);
+ else
+ return -EINVAL;
+}
+
+/* This function should only be called while mb0_transfer.lock is held. */
+static void config_wakeups(void)
+{
+ static u32 last_dbb_events;
+ static u32 last_abb_events;
+ u32 dbb_events;
+ u32 abb_events;
+
+ dbb_events = mb0_transfer.req.dbb_irqs | mb0_transfer.req.dbb_wakeups;
+
+ abb_events = mb0_transfer.req.abb_events;
+
+ if ((dbb_events == last_dbb_events) && (abb_events == last_abb_events))
+ return;
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ writel(dbb_events, PRCM_REQ_MB0_WAKEUP_DBB);
+ writel(abb_events, PRCM_REQ_MB0_WAKEUP_ABB);
+ writeb(MB0H_WAKE_UP_CFG, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ last_dbb_events = dbb_events;
+ last_abb_events = abb_events;
+}
+
+int prcmu_config_esram0_deep_sleep(u8 state)
+{
+ unsigned long flags;
+
+ if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
+ (state < ESRAM0_DEEP_SLEEP_STATE_OFF))
+ return -EINVAL;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ if (state == ESRAM0_DEEP_SLEEP_STATE_RET)
+ writeb(RET_ST, PRCM_REQ_MB0_ESRAM0_STATE);
+ else
+ writeb(OFF_ST, PRCM_REQ_MB0_ESRAM0_STATE);
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+ return 0;
+}
+
+int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
+{
+ int r = 0;
+ unsigned long flags;
+
+ /* Deep Idle is not supported in U5500 */
+ BUG_ON((state < PRCMU_AP_SLEEP) || (state >= PRCMU_AP_DEEP_IDLE));
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ switch (state) {
+ case PRCMU_AP_IDLE:
+ writeb(DB5500_AP_IDLE, PRCM_REQ_MB0_AP_POWER_STATE);
+ /* TODO: Can be high latency */
+ writeb(DDR_PWR_STATE_UNCHANGED, PRCM_REQ_MB0_DDR_STATE);
+ break;
+ case PRCMU_AP_SLEEP:
+ writeb(DB5500_AP_SLEEP, PRCM_REQ_MB0_AP_POWER_STATE);
+ break;
+ case PRCMU_AP_DEEP_SLEEP:
+ writeb(DB5500_AP_DEEP_SLEEP, PRCM_REQ_MB0_AP_POWER_STATE);
+ break;
+ default:
+ r = -EINVAL;
+ goto unlock_return;
+ }
+ writeb((keep_ap_pll ? 1 : 0), PRCM_REQ_MB0_AP_PLL_STATE);
+ writeb((keep_ulp_clk ? 1 : 0), PRCM_REQ_MB0_ULP_CLOCK_STATE);
+
+ writeb(MB0H_PWR_STATE_TRANS, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+unlock_return:
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+ return r;
+}
+
+void prcmu_enable_wakeups(u32 wakeups)
+{
+ unsigned long flags;
+ u32 bits;
+ int i;
+
+ BUG_ON(wakeups != (wakeups & VALID_WAKEUPS));
+
+ for (i = 0, bits = 0; i < NUM_PRCMU_WAKEUP_INDICES; i++) {
+ if (wakeups & BIT(i)) {
+ if (prcmu_wakeup_bit[i] == 0)
+ WARN(1, "WAKEUP NOT SUPPORTED");
+ else
+ bits |= prcmu_wakeup_bit[i];
+ }
+ }
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.dbb_wakeups = bits;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void prcmu_config_abb_event_readout(u32 abb_events)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.abb_events = abb_events;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void prcmu_get_abb_event_buffer(void __iomem **buf)
+{
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ *buf = (PRCM_ACK_MB0_WAKEUP_1_ABB);
+ else
+ *buf = (PRCM_ACK_MB0_WAKEUP_0_ABB);
+}
+
+/**
+ * db5500_prcmu_abb_read() - Read register value(s) from the ABB.
+ * @slave: The I2C slave address.
+ * @reg: The (start) register address.
+ * @value: The read out value(s).
+ * @size: The number of registers to read.
+ *
+ * Reads register value(s) from the ABB.
+ * @size has to be <= 4.
+ */
+int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ int r;
+
+ if ((size < 1) || (4 < size))
+ return -EINVAL;
+
+ mutex_lock(&mb5_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ cpu_relax();
+ writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
+ writeb(reg, PRCM_REQ_MB5_I2C_REG);
+ writeb(size, PRCM_REQ_MB5_I2C_SIZE);
+ writeb(MB5H_I2C_READ, PRCM_REQ_MB5_HEADER);
+
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb5_transfer.work);
+
+ r = 0;
+ if ((mb5_transfer.ack.header == MB5H_I2C_READ) &&
+ (mb5_transfer.ack.status == RC_SUCCESS))
+ memcpy(value, mb5_transfer.ack.value, (size_t)size);
+ else
+ r = -EIO;
+
+ mutex_unlock(&mb5_transfer.lock);
+
+ return r;
+}
+
+/**
+ * db5500_prcmu_abb_write() - Write register value(s) to the ABB.
+ * @slave: The I2C slave address.
+ * @reg: The (start) register address.
+ * @value: The value(s) to write.
+ * @size: The number of registers to write.
+ *
+ * Writes register value(s) to the ABB.
+ * @size has to be <= 4.
+ */
+int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ int r;
+
+ if ((size < 1) || (4 < size))
+ return -EINVAL;
+
+ mutex_lock(&mb5_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ cpu_relax();
+ writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
+ writeb(reg, PRCM_REQ_MB5_I2C_REG);
+ writeb(size, PRCM_REQ_MB5_I2C_SIZE);
+ memcpy_toio(PRCM_REQ_MB5_I2C_DATA, value, size);
+ writeb(MB5H_I2C_WRITE, PRCM_REQ_MB5_HEADER);
+
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb5_transfer.work);
+
+ if ((mb5_transfer.ack.header == MB5H_I2C_WRITE) &&
+ (mb5_transfer.ack.status == RC_SUCCESS))
+ r = 0;
+ else
+ r = -EIO;
+
+ mutex_unlock(&mb5_transfer.lock);
+
+ return r;
+}
+
+/**
+ * prcmu_set_arm_opp - set the appropriate ARM OPP
+ * @opp: The new ARM operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the the operating point of the ARM.
+ */
+int prcmu_set_arm_opp(u8 opp)
+{
+ int r;
+ u8 db5500_opp;
+
+ r = 0;
+
+ switch (opp) {
+ case ARM_EXTCLK:
+ db5500_opp = DB5500_ARM_EXT_OPP;
+ break;
+ case ARM_50_OPP:
+ db5500_opp = DB5500_ARM_50_OPP;
+ break;
+ case ARM_100_OPP:
+ db5500_opp = DB5500_ARM_100_OPP;
+ break;
+ default:
+ pr_err("prcmu: %s() received wrong opp value: %d\n",
+ __func__, opp);
+ r = -EINVAL;
+ goto bailout;
+ }
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_ARM_OPP, PRCM_REQ_MB1_HEADER);
+
+ writeb(db5500_opp, PRCM_REQ_MB1_ARM_OPP);
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb1_transfer.work,
+ msecs_to_jiffies(500))) {
+ r = -EIO;
+ WARN(1, "prcmu: failed to set arm opp");
+ goto unlock_and_return;
+ }
+
+ if (mb1_transfer.ack.header != MB1H_ARM_OPP ||
+ (mb1_transfer.ack.arm_opp != db5500_opp) ||
+ (mb1_transfer.ack.arm_voltage_st != RC_SUCCESS))
+ r = -EIO;
+
+ prcmu_debug_arm_opp_log(opp);
+
+unlock_and_return:
+ mutex_unlock(&mb1_transfer.lock);
+bailout:
+ return r;
+}
+
+int prcmu_set_ape_opp(u8 opp)
+{
+ return opp == APE_100_OPP ? 0 : -EINVAL;
+}
+
+int prcmu_get_ape_opp(void)
+{
+ return APE_100_OPP;
+}
+
+int prcmu_get_ddr_opp(void)
+{
+ return DDR_100_OPP;
+}
+
+int prcmu_set_ddr_opp(u8 opp)
+{
+ return opp == DDR_100_OPP ? 0 : -EINVAL;
+}
+
+/**
+ * prcmu_get_arm_opp - get the current ARM OPP
+ *
+ * Returns: the current ARM OPP
+ */
+int prcmu_get_arm_opp(void)
+{
+ u8 opp = readb(PRCM_ACK_MB1_CURRENT_ARM_OPP);
+
+ switch (opp) {
+ case DB5500_ARM_EXT_OPP:
+ return ARM_EXTCLK;
+ case DB5500_ARM_50_OPP:
+ return ARM_50_OPP;
+ case DB5500_ARM_100_OPP:
+ return ARM_100_OPP;
+ default:
+ pr_err("prcmu: %s() read unknown opp value: %d\n",
+ __func__, opp);
+ return ARM_100_OPP;
+ }
+}
+
+unsigned int prcmu_get_ddr_freq(void)
+{
+ return readl(PRCM_DDR_FREQUENCY);
+}
+
+int prcmu_resetout(u8 resoutn, u8 state)
+{
+ int offset;
+ int pin = -1;
+
+ offset = state > 0 ? PRCM_RESOUTN_SET_OFFSET : PRCM_RESOUTN_CLR_OFFSET;
+
+ switch (resoutn) {
+ case 0:
+ pin = PRCMU_RESOUTN0_PIN;
+ break;
+ case 1:
+ pin = PRCMU_RESOUTN1_PIN;
+ break;
+ case 2:
+ pin = PRCMU_RESOUTN2_PIN;
+ default:
+ break;
+ }
+
+ if (pin > 0)
+ writel(pin, _PRCMU_BASE + offset);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+int prcmu_enable_dsipll(void)
+{
+ int i;
+
+ /* Enable DSIPLL_RESETN resets */
+ writel(PRCMU_RESET_DSIPLL, _PRCMU_BASE + PRCM_APE_RESETN_CLR);
+ /* Unclamp DSIPLL in/out */
+ writel(PRCMU_UNCLAMP_DSIPLL, _PRCMU_BASE + PRCM_MMIP_LS_CLAMP_CLR);
+ /* Set DSI PLL FREQ */
+ writel(PRCMU_PLLDSI_FREQ_SETTING, _PRCMU_BASE + PRCM_PLLDSI_FREQ);
+ writel(PRCMU_DSI_PLLOUT_SEL_SETTING,
+ _PRCMU_BASE + PRCM_DSI_PLLOUT_SEL);
+ /* Enable Escape clocks */
+ writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, _PRCMU_BASE + PRCM_DSITVCLK_DIV);
+
+ /* Start DSI PLL */
+ writel(PRCMU_ENABLE_PLLDSI, _PRCMU_BASE + PRCM_PLLDSI_ENABLE);
+ /* Reset DSI PLL */
+ writel(PRCMU_DSI_RESET_SW, _PRCMU_BASE + PRCM_DSI_SW_RESET);
+ for (i = 0; i < 10; i++) {
+ if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
+ PRCMU_PLLDSI_LOCKP_LOCKED) == PRCMU_PLLDSI_LOCKP_LOCKED)
+ break;
+ udelay(100);
+ }
+ /* Release DSIPLL_RESETN */
+ writel(PRCMU_RESET_DSIPLL, _PRCMU_BASE + PRCM_APE_RESETN_SET);
+ return 0;
+}
+
+int prcmu_disable_dsipll(void)
+{
+ /* Disable dsi pll */
+ writel(PRCMU_DISABLE_PLLDSI, _PRCMU_BASE + PRCM_PLLDSI_ENABLE);
+ /* Disable escapeclock */
+ writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV,
+ _PRCMU_BASE + PRCM_DSITVCLK_DIV);
+ return 0;
+}
+
+int prcmu_set_display_clocks(void)
+{
+ writel(PRCMU_DSI_CLOCK_SETTING, _PRCMU_BASE + DB5500_PRCM_HDMICLK_MGT);
+ writel(PRCMU_DSI_LP_CLOCK_SETTING, _PRCMU_BASE + DB5500_PRCM_TVCLK_MGT);
+ return 0;
+}
+
+/**
+ * prcmu_system_reset - System reset
+ *
+ * Saves the reset reason code and then sets the APE_SOFTRST register which
+ * fires an interrupt to fw
+ */
+void prcmu_system_reset(u16 reset_code)
+{
+ writew(reset_code, PRCM_SW_RST_REASON);
+ writel(1, (_PRCMU_BASE + PRCM_APE_SOFTRST));
+}
+
+/**
+ * prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 prcmu_get_reset_code(void)
+{
+ return readw(PRCM_SW_RST_REASON);
+}
+
+static void ack_dbb_wakeup(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ writeb(MB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+int prcmu_set_epod(u16 epod, u8 epod_state)
+{
+ int r = 0;
+ bool ram_retention = false;
+
+ /* check argument */
+ BUG_ON(epod < DB5500_EPOD_ID_BASE);
+ BUG_ON(epod_state > EPOD_STATE_ON);
+ BUG_ON((epod - DB5500_EPOD_ID_BASE) >= DB5500_NUM_EPOD_ID);
+
+ if (epod == DB5500_EPOD_ID_ESRAM12)
+ ram_retention = true;
+
+ /* check argument */
+ BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention);
+
+ /* get lock */
+ mutex_lock(&mb2_transfer.lock);
+
+ /* wait for mailbox */
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* Retention is allowed only for ESRAM12 */
+ if (epod == DB5500_EPOD_ID_ESRAM12) {
+ switch (epod_state) {
+ case EPOD_STATE_ON:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_ON;
+ break;
+ case EPOD_STATE_OFF:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_OFF;
+ break;
+ case EPOD_STATE_RAMRET:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_RET;
+ break;
+ default:
+ r = -EINVAL;
+ goto unlock_and_return;
+ break;
+ }
+ } else {
+ if (epod_state == EPOD_STATE_ON)
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_ON;
+ else if (epod_state == EPOD_STATE_OFF)
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OFF;
+ else {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+ }
+ /* fill in mailbox */
+ writeb((epod - DB5500_EPOD_ID_BASE), PRCM_REQ_MB2_EPOD_CLIENT);
+ writeb(mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE],
+ PRCM_REQ_MB2_EPOD_STATE);
+
+ writeb(MB2H_EPOD_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(500))) {
+ pr_err("prcmu: set_epod() failed.\n"
+ "prcmu: Please check your firmware version.\n");
+ r = -EIO;
+ WARN(1, "Failed to set epod");
+ goto unlock_and_return;
+ }
+
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_EPOD_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
+static inline void print_unknown_header_warning(u8 n, u8 header)
+{
+ pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
+ header, n);
+}
+
+static bool read_mailbox_0(void)
+{
+ bool r;
+ u32 ev;
+ unsigned int n;
+
+ u8 header;
+
+ header = readb(PRCM_ACK_MB0_HEADER);
+ switch (header) {
+ case MB0H_WAKE_UP:
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ ev = readl(PRCM_ACK_MB0_WAKEUP_1_DBB);
+ else
+ ev = readl(PRCM_ACK_MB0_WAKEUP_0_DBB);
+
+ ev &= mb0_transfer.req.dbb_irqs;
+
+ for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
+ if (ev & prcmu_irq_bit[n])
+ generic_handle_irq(IRQ_PRCMU_BASE + n);
+ }
+ r = true;
+ break;
+ default:
+ print_unknown_header_warning(0, header);
+ r = false;
+ break;
+ }
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ return r;
+}
+
+static bool read_mailbox_1(void)
+{
+ u8 header;
+ bool do_complete = true;
+
+ header = mb1_transfer.ack.header = readb(PRCM_ACK_MB1_HEADER);
+
+ switch (header) {
+ case MB1H_ARM_OPP:
+ mb1_transfer.ack.arm_opp = readb(PRCM_ACK_MB1_CURRENT_ARM_OPP);
+ mb1_transfer.ack.arm_voltage_st =
+ readb(PRCM_ACK_MB1_ARM_VOLT_STATUS);
+ break;
+ case MB1H_ARM_APE_OPP:
+ mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+ mb1_transfer.ack.ape_voltage_st =
+ readb(PRCM_ACK_MB1_APE_VOLT_STATUS);
+ break;
+ default:
+ print_unknown_header_warning(1, header);
+ do_complete = false;
+ break;
+ }
+
+ writel(MBOX_BIT(1), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+
+ if (do_complete)
+ complete(&mb1_transfer.work);
+
+ return false;
+}
+
+static bool read_mailbox_2(void)
+{
+ u8 header;
+
+ header = readb(PRCM_ACK_MB2_HEADER);
+ mb2_transfer.ack.header = header;
+ switch (header) {
+ case MB2H_EPOD_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_EPOD_STATUS);
+ break;
+ case MB2H_CLK_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_CLK_STATUS);
+ break;
+ case MB2H_PLL_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_PLL_STATUS);
+ break;
+ default:
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ pr_err("prcmu: Wrong ACK received for MB2 request\n");
+ return false;
+ break;
+ }
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ complete(&mb2_transfer.work);
+ return false;
+}
+
+static bool read_mailbox_3(void)
+{
+ u8 header;
+
+ header = readb(PRCM_ACK_MB3_HEADER);
+ mb3_transfer.ack.header = header;
+ switch (header) {
+ case MB3H_REFCLK_REQUEST:
+ mb3_transfer.ack.status = readb(PRCM_ACK_MB3_REFCLK_REQ);
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ complete(&mb3_transfer.sysclk_work);
+ break;
+ default:
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ pr_err("prcmu: wrong MB3 header\n");
+ break;
+ }
+
+ return false;
+}
+
+static bool read_mailbox_4(void)
+{
+ writel(MBOX_BIT(4), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ return false;
+}
+
+static bool read_mailbox_5(void)
+{
+ u8 header;
+
+ header = readb(PRCM_ACK_MB5_HEADER);
+ switch (header) {
+ case MB5H_I2C_READ:
+ memcpy_fromio(mb5_transfer.ack.value, PRCM_ACK_MB5_I2C_DATA, 4);
+ case MB5H_I2C_WRITE:
+ mb5_transfer.ack.header = header;
+ mb5_transfer.ack.status = readb(PRCM_ACK_MB5_RETURN_CODE);
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ complete(&mb5_transfer.work);
+ break;
+ default:
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ print_unknown_header_warning(5, header);
+ break;
+ }
+ return false;
+}
+
+static bool read_mailbox_6(void)
+{
+ writel(MBOX_BIT(6), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ return false;
+}
+
+static bool read_mailbox_7(void)
+{
+ writel(MBOX_BIT(7), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ return false;
+}
+
+static bool (* const read_mailbox[NUM_MB])(void) = {
+ read_mailbox_0,
+ read_mailbox_1,
+ read_mailbox_2,
+ read_mailbox_3,
+ read_mailbox_4,
+ read_mailbox_5,
+ read_mailbox_6,
+ read_mailbox_7
+};
+
+static irqreturn_t prcmu_irq_handler(int irq, void *data)
+{
+ u32 bits;
+ u8 n;
+ irqreturn_t r;
+
+ bits = (readl(_PRCMU_BASE + PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ if (unlikely(!bits))
+ return IRQ_NONE;
+
+ r = IRQ_HANDLED;
+ for (n = 0; bits; n++) {
+ if (bits & MBOX_BIT(n)) {
+ bits -= MBOX_BIT(n);
+ if (read_mailbox[n]())
+ r = IRQ_WAKE_THREAD;
+ }
+ }
+ return r;
+}
+
+static irqreturn_t prcmu_irq_thread_fn(int irq, void *data)
+{
+ ack_dbb_wakeup();
+ return IRQ_HANDLED;
+}
+
+static void prcmu_mask_work(struct work_struct *work)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static void prcmu_irq_mask(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[irq - IRQ_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void prcmu_irq_unmask(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[irq - IRQ_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void noop(unsigned int irq)
+{
+}
+
+static struct irq_chip prcmu_irq_chip = {
+ .name = "prcmu",
+ .disable = prcmu_irq_mask,
+ .ack = noop,
+ .mask = prcmu_irq_mask,
+ .unmask = prcmu_irq_unmask,
+};
+void __init prcmu_early_init(void)
+{
+ unsigned int i;
+ void *tcpm_base = ioremap_nocache(U5500_PRCMU_TCPM_BASE, SZ_4K);
+
+ if (tcpm_base != NULL) {
+ int version_high , version_low;
+
+ version_high = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
+ version_low = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET + 4);
+ prcmu_version.board = (version_high >> 24) & 0xFF;
+ prcmu_version.fw_version = version_high & 0xFF;
+ prcmu_version.api_version = (version_low) & 0xFF;
+
+ if (prcmu_version.board == 0x20)
+ pr_info("Board Version : V2\n");
+ else
+ pr_info("Board Version : V1\n");
+
+ pr_info("PRCMU Firmware Version : 0x%x\n",
+ prcmu_version.fw_version);
+ pr_info("PRCMU API Version : 0x%x\n",
+ prcmu_version.api_version);
+ iounmap(tcpm_base);
+ }
+
+ tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE);
+ spin_lock_init(&mb0_transfer.lock);
+ spin_lock_init(&mb0_transfer.dbb_irqs_lock);
+ mutex_init(&mb1_transfer.lock);
+ init_completion(&mb1_transfer.work);
+ mutex_init(&mb2_transfer.lock);
+ init_completion(&mb2_transfer.work);
+ mutex_init(&mb3_transfer.sysclk_lock);
+ init_completion(&mb3_transfer.sysclk_work);
+ mutex_init(&mb5_transfer.lock);
+ init_completion(&mb5_transfer.work);
+
+ INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
+
+ /* Initalize irqs. */
+ for (i = 0; i < NUM_PRCMU_WAKEUPS; i++) {
+ unsigned int irq;
+
+ irq = IRQ_PRCMU_BASE + i;
+ set_irq_chip(irq, &prcmu_irq_chip);
+ set_irq_flags(irq, IRQF_VALID);
+ set_irq_handler(irq, handle_simple_irq);
+ }
+}
+
+/**
+ * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
+ *
+ */
+int __init prcmu_init(void)
+{
+ int r = 0;
+
+ if (ux500_is_svp() || !cpu_is_u5500())
+ return -ENODEV;
+
+ /* Clean up the mailbox interrupts after pre-kernel code. */
+ writel(ALL_MBOX_BITS, _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+
+ r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
+ prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
+ if (r < 0) {
+ pr_err("prcmu: Failed to allocate IRQ_DB5500_PRCMU1.\n");
+ return -EBUSY;
+ }
+
+ if (prcmu_debug_init())
+ pr_err("prcmu: Failed to initialize debugfs\n");
+
+ return 0;
+}
+
+arch_initcall(prcmu_init);
diff --git a/arch/arm/mach-ux500/prcmu-db8500.c b/arch/arm/mach-ux500/prcmu-db8500.c
new file mode 100644
index 00000000000..5c7c7b71adc
--- /dev/null
+++ b/arch/arm/mach-ux500/prcmu-db8500.c
@@ -0,0 +1,2280 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ *
+ * U8500 PRCM Unit interface driver
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/bitops.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+
+/*
+ * NOTE! Temporary until all users of set_hwacc() are using the regulator
+ * framework API
+ */
+#include <linux/regulator/consumer.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include "prcmu-regs-db8500.h"
+#include "prcmu-debug.h"
+#include <mach/prcmu.h>
+#include <mach/db8500-regs.h>
+
+/* Offset for the firmware version within the TCPM */
+#define PRCMU_FW_VERSION_OFFSET 0xA4
+
+/* PRCMU project numbers, defined by PRCMU FW */
+#define PRCMU_PROJECT_ID_8500V1_0 1
+#define PRCMU_PROJECT_ID_8500V2_0 2
+#define PRCMU_PROJECT_ID_8400V2_0 3
+
+/* Index of different voltages to be used when accessing AVSData */
+#define PRCM_AVS_BASE 0x2FC
+#define PRCM_AVS_VBB_RET (PRCM_AVS_BASE + 0x0)
+#define PRCM_AVS_VBB_MAX_OPP (PRCM_AVS_BASE + 0x1)
+#define PRCM_AVS_VBB_100_OPP (PRCM_AVS_BASE + 0x2)
+#define PRCM_AVS_VBB_50_OPP (PRCM_AVS_BASE + 0x3)
+#define PRCM_AVS_VARM_MAX_OPP (PRCM_AVS_BASE + 0x4)
+#define PRCM_AVS_VARM_100_OPP (PRCM_AVS_BASE + 0x5)
+#define PRCM_AVS_VARM_50_OPP (PRCM_AVS_BASE + 0x6)
+#define PRCM_AVS_VARM_RET (PRCM_AVS_BASE + 0x7)
+#define PRCM_AVS_VAPE_100_OPP (PRCM_AVS_BASE + 0x8)
+#define PRCM_AVS_VAPE_50_OPP (PRCM_AVS_BASE + 0x9)
+#define PRCM_AVS_VMOD_100_OPP (PRCM_AVS_BASE + 0xA)
+#define PRCM_AVS_VMOD_50_OPP (PRCM_AVS_BASE + 0xB)
+#define PRCM_AVS_VSAFE (PRCM_AVS_BASE + 0xC)
+#define PRCM_AVS_SIZE 14
+
+#define PRCM_AVS_VOLTAGE 0
+#define PRCM_AVS_VOLTAGE_MASK 0x3f
+#define PRCM_AVS_ISSLOWSTARTUP 6
+#define PRCM_AVS_ISSLOWSTARTUP_MASK (1 << PRCM_AVS_ISSLOWSTARTUP)
+#define PRCM_AVS_ISMODEENABLE 7
+#define PRCM_AVS_ISMODEENABLE_MASK (1 << PRCM_AVS_ISMODEENABLE)
+
+#define PRCM_BOOT_STATUS 0xFFF
+#define PRCM_ROMCODE_A2P 0xFFE
+#define PRCM_ROMCODE_P2A 0xFFD
+#define PRCM_XP70_CUR_PWR_STATE 0xFFC /* 4 BYTES */
+
+#define PRCM_SW_RST_REASON 0xFF8 /* 2 bytes */
+
+#define _PRCM_MBOX_HEADER 0xFE8 /* 16 bytes */
+#define PRCM_MBOX_HEADER_REQ_MB0 (_PRCM_MBOX_HEADER + 0x0)
+#define PRCM_MBOX_HEADER_REQ_MB1 (_PRCM_MBOX_HEADER + 0x1)
+#define PRCM_MBOX_HEADER_REQ_MB2 (_PRCM_MBOX_HEADER + 0x2)
+#define PRCM_MBOX_HEADER_REQ_MB3 (_PRCM_MBOX_HEADER + 0x3)
+#define PRCM_MBOX_HEADER_REQ_MB4 (_PRCM_MBOX_HEADER + 0x4)
+#define PRCM_MBOX_HEADER_REQ_MB5 (_PRCM_MBOX_HEADER + 0x5)
+#define PRCM_MBOX_HEADER_ACK_MB0 (_PRCM_MBOX_HEADER + 0x8)
+
+/* Req Mailboxes */
+#define PRCM_REQ_MB0 0xFDC /* 12 bytes */
+#define PRCM_REQ_MB1 0xFD0 /* 12 bytes */
+#define PRCM_REQ_MB2 0xFC0 /* 16 bytes */
+#define PRCM_REQ_MB3 0xE4C /* 372 bytes */
+#define PRCM_REQ_MB4 0xE48 /* 4 bytes */
+#define PRCM_REQ_MB5 0xE44 /* 4 bytes */
+
+/* Ack Mailboxes */
+#define PRCM_ACK_MB0 0xE08 /* 52 bytes */
+#define PRCM_ACK_MB1 0xE04 /* 4 bytes */
+#define PRCM_ACK_MB2 0xE00 /* 4 bytes */
+#define PRCM_ACK_MB3 0xDFC /* 4 bytes */
+#define PRCM_ACK_MB4 0xDF8 /* 4 bytes */
+#define PRCM_ACK_MB5 0xDF4 /* 4 bytes */
+
+/* Mailbox 0 headers */
+#define MB0H_POWER_STATE_TRANS 0
+#define MB0H_CONFIG_WAKEUPS_EXE 1
+#define MB0H_READ_WAKEUP_ACK 3
+#define MB0H_CONFIG_WAKEUPS_SLEEP 4
+
+#define MB0H_WAKEUP_EXE 2
+#define MB0H_WAKEUP_SLEEP 5
+
+/* Mailbox 0 REQs */
+#define PRCM_REQ_MB0_AP_POWER_STATE (PRCM_REQ_MB0 + 0x0)
+#define PRCM_REQ_MB0_AP_PLL_STATE (PRCM_REQ_MB0 + 0x1)
+#define PRCM_REQ_MB0_ULP_CLOCK_STATE (PRCM_REQ_MB0 + 0x2)
+#define PRCM_REQ_MB0_DO_NOT_WFI (PRCM_REQ_MB0 + 0x3)
+#define PRCM_REQ_MB0_WAKEUP_8500 (PRCM_REQ_MB0 + 0x4)
+#define PRCM_REQ_MB0_WAKEUP_4500 (PRCM_REQ_MB0 + 0x8)
+
+/* Mailbox 0 ACKs */
+#define PRCM_ACK_MB0_AP_PWRSTTR_STATUS (PRCM_ACK_MB0 + 0x0)
+#define PRCM_ACK_MB0_READ_POINTER (PRCM_ACK_MB0 + 0x1)
+#define PRCM_ACK_MB0_WAKEUP_0_8500 (PRCM_ACK_MB0 + 0x4)
+#define PRCM_ACK_MB0_WAKEUP_0_4500 (PRCM_ACK_MB0 + 0x8)
+#define PRCM_ACK_MB0_WAKEUP_1_8500 (PRCM_ACK_MB0 + 0x1C)
+#define PRCM_ACK_MB0_WAKEUP_1_4500 (PRCM_ACK_MB0 + 0x20)
+#define PRCM_ACK_MB0_EVENT_4500_NUMBERS 20
+
+/* Mailbox 1 headers */
+#define MB1H_ARM_APE_OPP 0x0
+#define MB1H_RESET_MODEM 0x2
+#define MB1H_REQUEST_APE_OPP_100_VOLT 0x3
+#define MB1H_RELEASE_APE_OPP_100_VOLT 0x4
+#define MB1H_RELEASE_USB_WAKEUP 0x5
+#define MB1H_PLL_ON_OFF 0x6
+
+/* Mailbox 1 Requests */
+#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0)
+#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1)
+#define PRCM_REQ_MB1_APE_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x4)
+#define PRCM_REQ_MB1_ARM_OPP_100_RESTORE (PRCM_REQ_MB1 + 0x8)
+#define PRCM_REQ_MB1_PLL_ON_OFF (PRCM_REQ_MB1 + 0x4)
+#define PLL_SOC1_OFF 0x4
+#define PLL_SOC1_ON 0x8
+
+/* Mailbox 1 ACKs */
+#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0)
+#define PRCM_ACK_MB1_CURRENT_APE_OPP (PRCM_ACK_MB1 + 0x1)
+#define PRCM_ACK_MB1_APE_VOLTAGE_STATUS (PRCM_ACK_MB1 + 0x2)
+#define PRCM_ACK_MB1_DVFS_STATUS (PRCM_ACK_MB1 + 0x3)
+
+/* Mailbox 2 headers */
+#define MB2H_DPS 0x0
+#define MB2H_AUTO_PWR 0x1
+
+/* Mailbox 2 REQs */
+#define PRCM_REQ_MB2_SVA_MMDSP (PRCM_REQ_MB2 + 0x0)
+#define PRCM_REQ_MB2_SVA_PIPE (PRCM_REQ_MB2 + 0x1)
+#define PRCM_REQ_MB2_SIA_MMDSP (PRCM_REQ_MB2 + 0x2)
+#define PRCM_REQ_MB2_SIA_PIPE (PRCM_REQ_MB2 + 0x3)
+#define PRCM_REQ_MB2_SGA (PRCM_REQ_MB2 + 0x4)
+#define PRCM_REQ_MB2_B2R2_MCDE (PRCM_REQ_MB2 + 0x5)
+#define PRCM_REQ_MB2_ESRAM12 (PRCM_REQ_MB2 + 0x6)
+#define PRCM_REQ_MB2_ESRAM34 (PRCM_REQ_MB2 + 0x7)
+#define PRCM_REQ_MB2_AUTO_PM_SLEEP (PRCM_REQ_MB2 + 0x8)
+#define PRCM_REQ_MB2_AUTO_PM_IDLE (PRCM_REQ_MB2 + 0xC)
+
+/* Mailbox 2 ACKs */
+#define PRCM_ACK_MB2_DPS_STATUS (PRCM_ACK_MB2 + 0x0)
+#define HWACC_PWR_ST_OK 0xFE
+
+/* Mailbox 3 headers */
+#define MB3H_ANC 0x0
+#define MB3H_SIDETONE 0x1
+#define MB3H_SYSCLK 0xE
+
+/* Mailbox 3 Requests */
+#define PRCM_REQ_MB3_ANC_FIR_COEFF (PRCM_REQ_MB3 + 0x0)
+#define PRCM_REQ_MB3_ANC_IIR_COEFF (PRCM_REQ_MB3 + 0x20)
+#define PRCM_REQ_MB3_ANC_SHIFTER (PRCM_REQ_MB3 + 0x60)
+#define PRCM_REQ_MB3_ANC_WARP (PRCM_REQ_MB3 + 0x64)
+#define PRCM_REQ_MB3_SIDETONE_FIR_GAIN (PRCM_REQ_MB3 + 0x68)
+#define PRCM_REQ_MB3_SIDETONE_FIR_COEFF (PRCM_REQ_MB3 + 0x6C)
+#define PRCM_REQ_MB3_SYSCLK_MGT (PRCM_REQ_MB3 + 0x16C)
+
+/* Mailbox 4 headers */
+#define MB4H_DDR_INIT 0x0
+#define MB4H_MEM_ST 0x1
+#define MB4H_HOTDOG 0x12
+#define MB4H_HOTMON 0x13
+#define MB4H_HOT_PERIOD 0x14
+#define MB4H_A9WDOG_CONF 0x16
+#define MB4H_A9WDOG_EN 0x17
+#define MB4H_A9WDOG_DIS 0x18
+#define MB4H_A9WDOG_LOAD 0x19
+#define MB4H_A9WDOG_KICK 0x20
+
+/* Mailbox 4 Requests */
+#define PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE (PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE (PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_ESRAM0_ST (PRCM_REQ_MB4 + 0x3)
+#define PRCM_REQ_MB4_HOTDOG_THRESHOLD (PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_HOTMON_LOW (PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_HOTMON_HIGH (PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_HOTMON_CONFIG (PRCM_REQ_MB4 + 0x2)
+#define PRCM_REQ_MB4_HOT_PERIOD (PRCM_REQ_MB4 + 0x0)
+#define HOTMON_CONFIG_LOW BIT(0)
+#define HOTMON_CONFIG_HIGH BIT(1)
+#define PRCM_REQ_MB4_A9WDOG_0 (PRCM_REQ_MB4 + 0x0)
+#define PRCM_REQ_MB4_A9WDOG_1 (PRCM_REQ_MB4 + 0x1)
+#define PRCM_REQ_MB4_A9WDOG_2 (PRCM_REQ_MB4 + 0x2)
+#define PRCM_REQ_MB4_A9WDOG_3 (PRCM_REQ_MB4 + 0x3)
+#define A9WDOG_AUTO_OFF_EN BIT(7)
+#define A9WDOG_AUTO_OFF_DIS 0
+#define A9WDOG_ID_MASK 0xf
+
+/* Mailbox 5 Requests */
+#define PRCM_REQ_MB5_I2C_SLAVE_OP (PRCM_REQ_MB5 + 0x0)
+#define PRCM_REQ_MB5_I2C_HW_BITS (PRCM_REQ_MB5 + 0x1)
+#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 0x2)
+#define PRCM_REQ_MB5_I2C_VAL (PRCM_REQ_MB5 + 0x3)
+#define PRCMU_I2C_WRITE(slave) (((slave) << 1) | BIT(6))
+#define PRCMU_I2C_READ(slave) (((slave) << 1) | BIT(0) | BIT(6))
+#define PRCMU_I2C_STOP_EN BIT(3)
+
+/* Mailbox 5 ACKs */
+#define PRCM_ACK_MB5_I2C_STATUS (PRCM_ACK_MB5 + 0x1)
+#define PRCM_ACK_MB5_I2C_VAL (PRCM_ACK_MB5 + 0x3)
+#define I2C_WR_OK 0x1
+#define I2C_RD_OK 0x2
+
+#define NUM_MB 8
+#define MBOX_BIT BIT
+#define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
+
+/*
+ * Wakeups/IRQs
+ */
+
+#define WAKEUP_BIT_RTC BIT(0)
+#define WAKEUP_BIT_RTT0 BIT(1)
+#define WAKEUP_BIT_RTT1 BIT(2)
+#define WAKEUP_BIT_HSI0 BIT(3)
+#define WAKEUP_BIT_HSI1 BIT(4)
+#define WAKEUP_BIT_CA_WAKE BIT(5)
+#define WAKEUP_BIT_USB BIT(6)
+#define WAKEUP_BIT_ABB BIT(7)
+#define WAKEUP_BIT_ABB_FIFO BIT(8)
+#define WAKEUP_BIT_SYSCLK_OK BIT(9)
+#define WAKEUP_BIT_CA_SLEEP BIT(10)
+#define WAKEUP_BIT_AC_WAKE_ACK BIT(11)
+#define WAKEUP_BIT_SIDE_TONE_OK BIT(12)
+#define WAKEUP_BIT_ANC_OK BIT(13)
+#define WAKEUP_BIT_SW_ERROR BIT(14)
+#define WAKEUP_BIT_AC_SLEEP_ACK BIT(15)
+#define WAKEUP_BIT_ARM BIT(17)
+#define WAKEUP_BIT_HOTMON_LOW BIT(18)
+#define WAKEUP_BIT_HOTMON_HIGH BIT(19)
+#define WAKEUP_BIT_MODEM_SW_RESET_REQ BIT(20)
+#define WAKEUP_BIT_GPIO0 BIT(23)
+#define WAKEUP_BIT_GPIO1 BIT(24)
+#define WAKEUP_BIT_GPIO2 BIT(25)
+#define WAKEUP_BIT_GPIO3 BIT(26)
+#define WAKEUP_BIT_GPIO4 BIT(27)
+#define WAKEUP_BIT_GPIO5 BIT(28)
+#define WAKEUP_BIT_GPIO6 BIT(29)
+#define WAKEUP_BIT_GPIO7 BIT(30)
+#define WAKEUP_BIT_GPIO8 BIT(31)
+
+/*
+ * This vector maps irq numbers to the bits in the bit field used in
+ * communication with the PRCMU firmware.
+ *
+ * The reason for having this is to keep the irq numbers contiguous even though
+ * the bits in the bit field are not. (The bits also have a tendency to move
+ * around, to further complicate matters.)
+ */
+#define IRQ_INDEX(_name) ((IRQ_PRCMU_##_name) - IRQ_PRCMU_BASE)
+#define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name)
+static u32 prcmu_irq_bit[NUM_PRCMU_WAKEUPS] = {
+ IRQ_ENTRY(RTC),
+ IRQ_ENTRY(RTT0),
+ IRQ_ENTRY(RTT1),
+ IRQ_ENTRY(HSI0),
+ IRQ_ENTRY(HSI1),
+ IRQ_ENTRY(CA_WAKE),
+ IRQ_ENTRY(USB),
+ IRQ_ENTRY(ABB),
+ IRQ_ENTRY(ABB_FIFO),
+ IRQ_ENTRY(CA_SLEEP),
+ IRQ_ENTRY(ARM),
+ IRQ_ENTRY(HOTMON_LOW),
+ IRQ_ENTRY(HOTMON_HIGH),
+ IRQ_ENTRY(MODEM_SW_RESET_REQ),
+ IRQ_ENTRY(GPIO0),
+ IRQ_ENTRY(GPIO1),
+ IRQ_ENTRY(GPIO2),
+ IRQ_ENTRY(GPIO3),
+ IRQ_ENTRY(GPIO4),
+ IRQ_ENTRY(GPIO5),
+ IRQ_ENTRY(GPIO6),
+ IRQ_ENTRY(GPIO7),
+ IRQ_ENTRY(GPIO8)
+};
+
+#define VALID_WAKEUPS (BIT(NUM_PRCMU_WAKEUP_INDICES) - 1)
+#define WAKEUP_ENTRY(_name)[PRCMU_WAKEUP_INDEX_##_name] = (WAKEUP_BIT_##_name)
+static u32 prcmu_wakeup_bit[NUM_PRCMU_WAKEUP_INDICES] = {
+ WAKEUP_ENTRY(RTC),
+ WAKEUP_ENTRY(RTT0),
+ WAKEUP_ENTRY(RTT1),
+ WAKEUP_ENTRY(HSI0),
+ WAKEUP_ENTRY(HSI1),
+ WAKEUP_ENTRY(USB),
+ WAKEUP_ENTRY(ABB),
+ WAKEUP_ENTRY(ABB_FIFO),
+ WAKEUP_ENTRY(ARM)
+};
+
+/*
+ * mb0_transfer - state needed for mailbox 0 communication.
+ * @lock: The transaction lock.
+ * @dbb_events_lock: A lock used to handle concurrent access to (parts of)
+ * the request data.
+ * @mask_work: Work structure used for (un)masking wakeup interrupts.
+ * @req: Request data that need to persist between requests.
+ */
+static struct {
+ spinlock_t lock;
+ spinlock_t dbb_irqs_lock;
+ struct work_struct mask_work;
+ struct mutex ac_wake_lock;
+ struct completion ac_wake_work;
+ struct {
+ u32 dbb_irqs;
+ u32 dbb_wakeups;
+ u32 abb_events;
+ } req;
+} mb0_transfer;
+
+/*
+ * mb1_transfer - state needed for mailbox 1 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @ape_opp: The current APE OPP.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ u8 ape_opp;
+ struct {
+ u8 header;
+ u8 arm_opp;
+ u8 ape_opp;
+ u8 ape_voltage_status;
+ } ack;
+} mb1_transfer;
+
+/*
+ * mb2_transfer - state needed for mailbox 2 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @auto_pm_lock: The autonomous power management configuration lock.
+ * @auto_pm_enabled: A flag indicating whether autonomous PM is enabled.
+ * @req: Request data that need to persist between requests.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ spinlock_t auto_pm_lock;
+ bool auto_pm_enabled;
+ struct {
+ u8 status;
+ } ack;
+} mb2_transfer;
+
+/*
+ * mb3_transfer - state needed for mailbox 3 communication.
+ * @lock: The request lock.
+ * @sysclk_lock: A lock used to handle concurrent sysclk requests.
+ * @sysclk_work: Work structure used for sysclk requests.
+ */
+static struct {
+ spinlock_t lock;
+ struct mutex sysclk_lock;
+ struct completion sysclk_work;
+} mb3_transfer;
+
+/*
+ * mb4_transfer - state needed for mailbox 4 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+} mb4_transfer;
+
+/*
+ * mb5_transfer - state needed for mailbox 5 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 status;
+ u8 value;
+ } ack;
+} mb5_transfer;
+
+static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
+
+/* Spinlocks */
+static DEFINE_SPINLOCK(clkout_lock);
+static DEFINE_SPINLOCK(gpiocr_lock);
+
+/* Global var to runtime determine TCDM base for v2 or v1 */
+static __iomem void *tcdm_base;
+
+struct clk_mgt {
+ unsigned int offset;
+ u32 pllsw;
+};
+
+static DEFINE_SPINLOCK(clk_mgt_lock);
+
+#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT), 0 }
+struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
+ CLK_MGT_ENTRY(SGACLK),
+ CLK_MGT_ENTRY(UARTCLK),
+ CLK_MGT_ENTRY(MSP02CLK),
+ CLK_MGT_ENTRY(MSP1CLK),
+ CLK_MGT_ENTRY(I2CCLK),
+ CLK_MGT_ENTRY(SDMMCCLK),
+ CLK_MGT_ENTRY(SLIMCLK),
+ CLK_MGT_ENTRY(PER1CLK),
+ CLK_MGT_ENTRY(PER2CLK),
+ CLK_MGT_ENTRY(PER3CLK),
+ CLK_MGT_ENTRY(PER5CLK),
+ CLK_MGT_ENTRY(PER6CLK),
+ CLK_MGT_ENTRY(PER7CLK),
+ CLK_MGT_ENTRY(LCDCLK),
+ CLK_MGT_ENTRY(BMLCLK),
+ CLK_MGT_ENTRY(HSITXCLK),
+ CLK_MGT_ENTRY(HSIRXCLK),
+ CLK_MGT_ENTRY(HDMICLK),
+ CLK_MGT_ENTRY(APEATCLK),
+ CLK_MGT_ENTRY(APETRACECLK),
+ CLK_MGT_ENTRY(MCDECLK),
+ CLK_MGT_ENTRY(IPI2CCLK),
+ CLK_MGT_ENTRY(DSIALTCLK),
+ CLK_MGT_ENTRY(DMACLK),
+ CLK_MGT_ENTRY(B2R2CLK),
+ CLK_MGT_ENTRY(TVCLK),
+ CLK_MGT_ENTRY(SSPCLK),
+ CLK_MGT_ENTRY(RNGCLK),
+ CLK_MGT_ENTRY(UICCCLK),
+};
+
+/*
+ * NOTE! Temporary until all users of set_hwacc() are using the regulator
+ * framework API
+ */
+static struct regulator *hwacc_regulator[NUM_HW_ACC];
+static struct regulator *hwacc_ret_regulator[NUM_HW_ACC];
+
+static bool hwacc_enabled[NUM_HW_ACC];
+static bool hwacc_ret_enabled[NUM_HW_ACC];
+
+static const char *hwacc_regulator_name[NUM_HW_ACC] = {
+ [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp",
+ [HW_ACC_SVAPIPE] = "hwacc-sva-pipe",
+ [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp",
+ [HW_ACC_SIAPIPE] = "hwacc-sia-pipe",
+ [HW_ACC_SGA] = "hwacc-sga",
+ [HW_ACC_B2R2] = "hwacc-b2r2",
+ [HW_ACC_MCDE] = "hwacc-mcde",
+ [HW_ACC_ESRAM1] = "hwacc-esram1",
+ [HW_ACC_ESRAM2] = "hwacc-esram2",
+ [HW_ACC_ESRAM3] = "hwacc-esram3",
+ [HW_ACC_ESRAM4] = "hwacc-esram4",
+};
+
+static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {
+ [HW_ACC_SVAMMDSP] = "hwacc-sva-mmdsp-ret",
+ [HW_ACC_SIAMMDSP] = "hwacc-sia-mmdsp-ret",
+ [HW_ACC_ESRAM1] = "hwacc-esram1-ret",
+ [HW_ACC_ESRAM2] = "hwacc-esram2-ret",
+ [HW_ACC_ESRAM3] = "hwacc-esram3-ret",
+ [HW_ACC_ESRAM4] = "hwacc-esram4-ret",
+};
+
+/*
+* Used by MCDE to setup all necessary PRCMU registers
+*/
+#define PRCMU_RESET_DSIPLL 0x00004000
+#define PRCMU_UNCLAMP_DSIPLL 0x00400800
+
+#define PRCMU_CLK_PLL_DIV_SHIFT 0
+#define PRCMU_CLK_PLL_SW_SHIFT 5
+#define PRCMU_CLK_38 (1 << 9)
+#define PRCMU_CLK_38_SRC (1 << 10)
+#define PRCMU_CLK_38_DIV (1 << 11)
+
+/* PLLDIV=12, PLLSW=4 (PLLDDR) */
+#define PRCMU_DSI_CLOCK_SETTING 0x0000008C
+
+/* PLLDIV=8, PLLSW=4 (PLLDDR) */
+#define PRCMU_DSI_CLOCK_SETTING_U8400 0x00000088
+
+/* DPI 50000000 Hz */
+#define PRCMU_DPI_CLOCK_SETTING ((1 << PRCMU_CLK_PLL_SW_SHIFT) | \
+ (16 << PRCMU_CLK_PLL_DIV_SHIFT))
+#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000E00
+
+/* D=101, N=1, R=4, SELDIV2=0 */
+#define PRCMU_PLLDSI_FREQ_SETTING 0x00040165
+
+/* D=70, N=1, R=3, SELDIV2=0 */
+#define PRCMU_PLLDSI_FREQ_SETTING_U8400 0x00030146
+
+#define PRCMU_ENABLE_PLLDSI 0x00000001
+#define PRCMU_DISABLE_PLLDSI 0x00000000
+#define PRCMU_RELEASE_RESET_DSS 0x0000400C
+#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000202
+/* ESC clk, div0=1, div1=1, div2=3 */
+#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x07030101
+#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00030101
+#define PRCMU_DSI_RESET_SW 0x00000007
+
+#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
+
+static struct {
+ u8 project_number;
+ u8 api_version;
+ u8 func_version;
+ u8 errata;
+} prcmu_version;
+
+
+int prcmu_enable_dsipll(void)
+{
+ int i;
+ unsigned int plldsifreq;
+
+ /* Clear DSIPLL_RESETN */
+ writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_CLR));
+ /* Unclamp DSIPLL in/out */
+ writel(PRCMU_UNCLAMP_DSIPLL, (_PRCMU_BASE + PRCM_MMIP_LS_CLAMP_CLR));
+
+ if (prcmu_is_u8400())
+ plldsifreq = PRCMU_PLLDSI_FREQ_SETTING_U8400;
+ else
+ plldsifreq = PRCMU_PLLDSI_FREQ_SETTING;
+ /* Set DSI PLL FREQ */
+ writel(plldsifreq, (_PRCMU_BASE + PRCM_PLLDSI_FREQ));
+ writel(PRCMU_DSI_PLLOUT_SEL_SETTING,
+ (_PRCMU_BASE + PRCM_DSI_PLLOUT_SEL));
+ /* Enable Escape clocks */
+ writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV,
+ (_PRCMU_BASE + PRCM_DSITVCLK_DIV));
+
+ /* Start DSI PLL */
+ writel(PRCMU_ENABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE));
+ /* Reset DSI PLL */
+ writel(PRCMU_DSI_RESET_SW, (_PRCMU_BASE + PRCM_DSI_SW_RESET));
+ for (i = 0; i < 10; i++) {
+ if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
+ PRCMU_PLLDSI_LOCKP_LOCKED)
+ == PRCMU_PLLDSI_LOCKP_LOCKED)
+ break;
+ udelay(100);
+ }
+ /* Set DSIPLL_RESETN */
+ writel(PRCMU_RESET_DSIPLL, (_PRCMU_BASE + PRCM_APE_RESETN_SET));
+ return 0;
+}
+
+int prcmu_disable_dsipll(void)
+{
+ /* Disable dsi pll */
+ writel(PRCMU_DISABLE_PLLDSI, (_PRCMU_BASE + PRCM_PLLDSI_ENABLE));
+ /* Disable escapeclock */
+ writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV,
+ (_PRCMU_BASE + PRCM_DSITVCLK_DIV));
+ return 0;
+}
+
+int prcmu_set_display_clocks(void)
+{
+ unsigned long flags;
+ unsigned int dsiclk;
+
+ if (prcmu_is_u8400())
+ dsiclk = PRCMU_DSI_CLOCK_SETTING_U8400;
+ else
+ dsiclk = PRCMU_DSI_CLOCK_SETTING;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ writel(dsiclk, (_PRCMU_BASE + PRCM_HDMICLK_MGT));
+ writel(PRCMU_DSI_LP_CLOCK_SETTING, (_PRCMU_BASE + PRCM_TVCLK_MGT));
+ writel(PRCMU_DPI_CLOCK_SETTING, (_PRCMU_BASE + PRCM_LCDCLK_MGT));
+
+ /* Release the HW semaphore. */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ return 0;
+}
+
+/**
+ * prcmu_enable_spi2 - Enables pin muxing for SPI2 on OtherAlternateC1.
+ */
+void prcmu_enable_spi2(void)
+{
+ u32 reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpiocr_lock, flags);
+ reg = readl(_PRCMU_BASE + PRCM_GPIOCR);
+ writel(reg | PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR);
+ spin_unlock_irqrestore(&gpiocr_lock, flags);
+}
+
+/**
+ * prcmu_disable_spi2 - Disables pin muxing for SPI2 on OtherAlternateC1.
+ */
+void prcmu_disable_spi2(void)
+{
+ u32 reg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpiocr_lock, flags);
+ reg = readl(_PRCMU_BASE + PRCM_GPIOCR);
+ writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, _PRCMU_BASE + PRCM_GPIOCR);
+ spin_unlock_irqrestore(&gpiocr_lock, flags);
+}
+
+bool prcmu_has_arm_maxopp(void)
+{
+ return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) &
+ PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;
+}
+
+/* This is just for showing AVS settings in debugfs, never use otherwise */
+#ifdef CONFIG_UX500_PRCMU_DEBUG
+void prcmu_dump_avs(u8 *avs, int s)
+{
+ BUG_ON(s != PRCM_AVS_SIZE);
+
+ memcpy_fromio(avs, tcdm_base + PRCM_AVS_BASE,
+ PRCM_AVS_SIZE);
+}
+#endif
+
+bool prcmu_is_u8400(void)
+{
+ return prcmu_version.project_number == PRCMU_PROJECT_ID_8400V2_0;
+}
+
+/**
+ * prcmu_get_boot_status - PRCMU boot status checking
+ * Returns: the current PRCMU boot status
+ */
+int prcmu_get_boot_status(void)
+{
+ return readb(tcdm_base + PRCM_BOOT_STATUS);
+}
+
+/**
+ * prcmu_set_rc_a2p - This function is used to run few power state sequences
+ * @val: Value to be set, i.e. transition requested
+ * Returns: 0 on success, -EINVAL on invalid argument
+ *
+ * This function is used to run the following power state sequences -
+ * any state to ApReset, ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
+ */
+int prcmu_set_rc_a2p(enum romcode_write val)
+{
+ if (val < RDY_2_DS || val > RDY_2_XP70_RST)
+ return -EINVAL;
+ writeb(val, (tcdm_base + PRCM_ROMCODE_A2P));
+ return 0;
+}
+
+/**
+ * prcmu_get_rc_p2a - This function is used to get power state sequences
+ * Returns: the power transition that has last happened
+ *
+ * This function can return the following transitions-
+ * any state to ApReset, ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
+ */
+enum romcode_read prcmu_get_rc_p2a(void)
+{
+ return readb(tcdm_base + PRCM_ROMCODE_P2A);
+}
+
+/**
+ * prcmu_get_current_mode - Return the current XP70 power mode
+ * Returns: Returns the current AP(ARM) power mode: init,
+ * apBoot, apExecute, apDeepSleep, apSleep, apIdle, apReset
+ */
+enum ap_pwrst prcmu_get_xp70_current_state(void)
+{
+ return readb(tcdm_base + PRCM_XP70_CUR_PWR_STATE);
+}
+
+/**
+ * prcmu_config_clkout - Configure one of the programmable clock outputs.
+ * @clkout: The CLKOUT number (0 or 1).
+ * @source: The clock to be used (one of the PRCMU_CLKSRC_*).
+ * @div: The divider to be applied.
+ *
+ * Configures one of the programmable clock outputs (CLKOUTs).
+ * @div should be in the range [1,63] to request a configuration, or 0 to
+ * inform that the configuration is no longer requested.
+ */
+int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
+{
+ static int requests[2];
+ int r = 0;
+ unsigned long flags;
+ u32 val;
+ u32 bits;
+ u32 mask;
+ u32 div_mask;
+
+ BUG_ON(clkout > 1);
+ BUG_ON(div > 63);
+ BUG_ON((clkout == 0) && (source > PRCMU_CLKSRC_CLK009));
+
+ if (!div && !requests[clkout])
+ return -EINVAL;
+
+ switch (clkout) {
+ case 0:
+ div_mask = PRCM_CLKOCR_CLKODIV0_MASK;
+ mask = (PRCM_CLKOCR_CLKODIV0_MASK | PRCM_CLKOCR_CLKOSEL0_MASK);
+ bits = ((source << PRCM_CLKOCR_CLKOSEL0_SHIFT) |
+ (div << PRCM_CLKOCR_CLKODIV0_SHIFT));
+ break;
+ case 1:
+ div_mask = PRCM_CLKOCR_CLKODIV1_MASK;
+ mask = (PRCM_CLKOCR_CLKODIV1_MASK | PRCM_CLKOCR_CLKOSEL1_MASK |
+ PRCM_CLKOCR_CLK1TYPE);
+ bits = ((source << PRCM_CLKOCR_CLKOSEL1_SHIFT) |
+ (div << PRCM_CLKOCR_CLKODIV1_SHIFT));
+ break;
+ }
+ bits &= mask;
+
+ spin_lock_irqsave(&clkout_lock, flags);
+
+ val = readl(_PRCMU_BASE + PRCM_CLKOCR);
+ if (val & div_mask) {
+ if (div) {
+ if ((val & mask) != bits) {
+ r = -EBUSY;
+ goto unlock_and_return;
+ }
+ } else {
+ if ((val & mask & ~div_mask) != bits) {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+ }
+ }
+ writel((bits | (val & ~mask)), (_PRCMU_BASE + PRCM_CLKOCR));
+ requests[clkout] += (div ? 1 : -1);
+
+unlock_and_return:
+ spin_unlock_irqrestore(&clkout_lock, flags);
+
+ return r;
+}
+
+int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
+{
+ unsigned long flags;
+
+ BUG_ON((state < PRCMU_AP_SLEEP) || (PRCMU_AP_DEEP_IDLE < state));
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ writeb(MB0H_POWER_STATE_TRANS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
+ writeb(state, (tcdm_base + PRCM_REQ_MB0_AP_POWER_STATE));
+ writeb((keep_ap_pll ? 1 : 0), (tcdm_base + PRCM_REQ_MB0_AP_PLL_STATE));
+ writeb((keep_ulp_clk ? 1 : 0),
+ (tcdm_base + PRCM_REQ_MB0_ULP_CLOCK_STATE));
+ writeb(0, (tcdm_base + PRCM_REQ_MB0_DO_NOT_WFI));
+ writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+ return 0;
+}
+
+/* This function should only be called while mb0_transfer.lock is held. */
+static void config_wakeups(void)
+{
+ const u8 header[2] = {
+ MB0H_CONFIG_WAKEUPS_EXE,
+ MB0H_CONFIG_WAKEUPS_SLEEP
+ };
+ static u32 last_dbb_events;
+ static u32 last_abb_events;
+ u32 dbb_events;
+ u32 abb_events;
+ unsigned int i;
+
+ dbb_events = mb0_transfer.req.dbb_irqs | mb0_transfer.req.dbb_wakeups;
+ dbb_events |= (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK);
+
+ abb_events = mb0_transfer.req.abb_events;
+
+ if ((dbb_events == last_dbb_events) && (abb_events == last_abb_events))
+ return;
+
+ for (i = 0; i < 2; i++) {
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+ writel(dbb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_8500));
+ writel(abb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_4500));
+ writeb(header[i], (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
+ writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ }
+ last_dbb_events = dbb_events;
+ last_abb_events = abb_events;
+}
+
+void prcmu_enable_wakeups(u32 wakeups)
+{
+ unsigned long flags;
+ u32 bits;
+ int i;
+
+ BUG_ON(wakeups != (wakeups & VALID_WAKEUPS));
+
+ for (i = 0, bits = 0; i < NUM_PRCMU_WAKEUP_INDICES; i++) {
+ if (wakeups & BIT(i))
+ bits |= prcmu_wakeup_bit[i];
+ }
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.dbb_wakeups = bits;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void prcmu_config_abb_event_readout(u32 abb_events)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.abb_events = abb_events;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void prcmu_get_abb_event_buffer(void __iomem **buf)
+{
+ if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
+ *buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500);
+ else
+ *buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_0_4500);
+}
+
+/**
+ * prcmu_set_arm_opp - set the appropriate ARM OPP
+ * @opp: The new ARM operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the the operating point of the ARM.
+ */
+int prcmu_set_arm_opp(u8 opp)
+{
+ int r;
+
+ if (opp < ARM_NO_CHANGE || opp > ARM_EXTCLK)
+ return -EINVAL;
+
+ r = 0;
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+ writeb(opp, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
+ writeb(APE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb1_transfer.work);
+
+ if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
+ (mb1_transfer.ack.arm_opp != opp))
+ r = -EIO;
+
+ mutex_unlock(&mb1_transfer.lock);
+
+ prcmu_debug_arm_opp_log(opp);
+
+ return r;
+}
+
+/**
+ * prcmu_get_arm_opp - get the current ARM OPP
+ *
+ * Returns: the current ARM OPP
+ */
+int prcmu_get_arm_opp(void)
+{
+ return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP);
+}
+
+/**
+ * prcmu_get_ddr_opp - get the current DDR OPP
+ *
+ * Returns: the current DDR OPP
+ */
+int prcmu_get_ddr_opp(void)
+{
+ return readb(_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW);
+}
+
+/**
+ * set_ddr_opp - set the appropriate DDR OPP
+ * @opp: The new DDR operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the operating point of the DDR.
+ */
+int prcmu_set_ddr_opp(u8 opp)
+{
+ if (opp < DDR_100_OPP || opp > DDR_25_OPP)
+ return -EINVAL;
+ /* Changing the DDR OPP can hang the hardware pre-v21 */
+ if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20())
+ writeb(opp, (_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW));
+
+ return 0;
+}
+
+/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
+static void request_even_slower_clocks(bool enable)
+{
+ const u8 clock_reg[] = {
+ PRCM_ACLK_MGT,
+ PRCM_DMACLK_MGT
+ };
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
+ u32 val;
+ u32 div;
+
+ val = readl(_PRCMU_BASE + clock_reg[i]);
+ div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
+ if (enable) {
+ if ((div <= 1) || (div > 15)) {
+ pr_err("prcmu: Bad clock divider %d in %s\n",
+ div, __func__);
+ goto unlock_and_return;
+ }
+ div <<= 1;
+ } else {
+ if (div <= 2)
+ goto unlock_and_return;
+ div >>= 1;
+ }
+ val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
+ (div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
+ writel(val, (_PRCMU_BASE + clock_reg[i]));
+ }
+
+unlock_and_return:
+ /* Release the HW semaphore. */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+
+/**
+ * set_ape_opp - set the appropriate APE OPP
+ * @opp: The new APE operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the operating point of the APE.
+ */
+int prcmu_set_ape_opp(u8 opp)
+{
+ int r = 0;
+
+ if (opp == mb1_transfer.ape_opp)
+ return 0;
+
+ mutex_lock(&mb1_transfer.lock);
+
+ if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
+ request_even_slower_clocks(false);
+
+ if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP))
+ goto skip_message;
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+ writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
+ writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
+ (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb1_transfer.work);
+
+ if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
+ (mb1_transfer.ack.ape_opp != opp))
+ r = -EIO;
+
+skip_message:
+ if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
+ (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
+ request_even_slower_clocks(true);
+ if (!r)
+ mb1_transfer.ape_opp = opp;
+
+ mutex_unlock(&mb1_transfer.lock);
+
+ return r;
+}
+
+/**
+ * prcmu_get_ape_opp - get the current APE OPP
+ *
+ * Returns: the current APE OPP
+ */
+int prcmu_get_ape_opp(void)
+{
+ return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP);
+}
+
+/**
+ * prcmu_request_ape_opp_100_voltage - Request APE OPP 100% voltage
+ * @enable: true to request the higher voltage, false to drop a request.
+ *
+ * Calls to this function to enable and disable requests must be balanced.
+ */
+int prcmu_request_ape_opp_100_voltage(bool enable)
+{
+ int r = 0;
+ u8 header;
+ static unsigned int requests;
+
+ mutex_lock(&mb1_transfer.lock);
+
+ if (enable) {
+ if (0 != requests++)
+ goto unlock_and_return;
+ header = MB1H_REQUEST_APE_OPP_100_VOLT;
+ } else {
+ if (requests == 0) {
+ r = -EIO;
+ goto unlock_and_return;
+ } else if (1 != requests--) {
+ goto unlock_and_return;
+ }
+ header = MB1H_RELEASE_APE_OPP_100_VOLT;
+ }
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(header, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb1_transfer.work);
+
+ if ((mb1_transfer.ack.header != header) ||
+ ((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0))
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb1_transfer.lock);
+
+ return r;
+}
+
+/**
+ * prcmu_release_usb_wakeup_state - release the state required by a USB wakeup
+ *
+ * This function releases the power state requirements of a USB wakeup.
+ */
+int prcmu_release_usb_wakeup_state(void)
+{
+ int r = 0;
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_RELEASE_USB_WAKEUP,
+ (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb1_transfer.work);
+
+ if ((mb1_transfer.ack.header != MB1H_RELEASE_USB_WAKEUP) ||
+ ((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0))
+ r = -EIO;
+
+ mutex_unlock(&mb1_transfer.lock);
+
+ return r;
+}
+
+static int request_pll(u8 clock, bool enable)
+{
+ int r = 0;
+
+ if (clock == PRCMU_PLLSOC1)
+ clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF);
+ else
+ return -EINVAL;
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_PLL_ON_OFF, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+ writeb(clock, (tcdm_base + PRCM_REQ_MB1_PLL_ON_OFF));
+
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb1_transfer.work);
+
+ if (mb1_transfer.ack.header != MB1H_PLL_ON_OFF)
+ r = -EIO;
+
+ mutex_unlock(&mb1_transfer.lock);
+
+ return r;
+}
+
+/**
+ * prcmu_set_hwacc - set the power state of a h/w accelerator
+ * @hwacc_dev: The hardware accelerator (enum hw_acc_dev).
+ * @state: The new power state (enum hw_acc_state).
+ *
+ * This function sets the power state of a hardware accelerator.
+ * This function should not be called from interrupt context.
+ *
+ * NOTE! Deprecated, to be removed when all users switched over to use the
+ * regulator framework API.
+ */
+int prcmu_set_hwacc(u16 hwacc_dev, u8 state)
+{
+ int r = 0;
+ bool ram_retention = false;
+ bool enable, enable_ret;
+
+ /* check argument */
+ BUG_ON(hwacc_dev >= NUM_HW_ACC);
+
+ /* get state of switches */
+ enable = hwacc_enabled[hwacc_dev];
+ enable_ret = hwacc_ret_enabled[hwacc_dev];
+
+ /* set flag if retention is possible */
+ switch (hwacc_dev) {
+ case HW_ACC_SVAMMDSP:
+ case HW_ACC_SIAMMDSP:
+ case HW_ACC_ESRAM1:
+ case HW_ACC_ESRAM2:
+ case HW_ACC_ESRAM3:
+ case HW_ACC_ESRAM4:
+ ram_retention = true;
+ break;
+ }
+
+ /* check argument */
+ BUG_ON(state > HW_ON);
+ BUG_ON(state == HW_OFF_RAMRET && !ram_retention);
+
+ /* modify enable flags */
+ switch (state) {
+ case HW_OFF:
+ enable_ret = false;
+ enable = false;
+ break;
+ case HW_ON:
+ enable = true;
+ break;
+ case HW_OFF_RAMRET:
+ enable_ret = true;
+ enable = false;
+ break;
+ }
+
+ /* get regulator (lazy) */
+ if (hwacc_regulator[hwacc_dev] == NULL) {
+ hwacc_regulator[hwacc_dev] = regulator_get(NULL,
+ hwacc_regulator_name[hwacc_dev]);
+ if (IS_ERR(hwacc_regulator[hwacc_dev])) {
+ pr_err("prcmu: failed to get supply %s\n",
+ hwacc_regulator_name[hwacc_dev]);
+ r = PTR_ERR(hwacc_regulator[hwacc_dev]);
+ goto out;
+ }
+ }
+
+ if (ram_retention) {
+ if (hwacc_ret_regulator[hwacc_dev] == NULL) {
+ hwacc_ret_regulator[hwacc_dev] = regulator_get(NULL,
+ hwacc_ret_regulator_name[hwacc_dev]);
+ if (IS_ERR(hwacc_ret_regulator[hwacc_dev])) {
+ pr_err("prcmu: failed to get supply %s\n",
+ hwacc_ret_regulator_name[hwacc_dev]);
+ r = PTR_ERR(hwacc_ret_regulator[hwacc_dev]);
+ goto out;
+ }
+ }
+ }
+
+ /* set regulators */
+ if (ram_retention) {
+ if (enable_ret && !hwacc_ret_enabled[hwacc_dev]) {
+ r = regulator_enable(hwacc_ret_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: ret enable failed\n");
+ goto out;
+ }
+ hwacc_ret_enabled[hwacc_dev] = true;
+ }
+ }
+
+ if (enable && !hwacc_enabled[hwacc_dev]) {
+ r = regulator_enable(hwacc_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: enable failed\n");
+ goto out;
+ }
+ hwacc_enabled[hwacc_dev] = true;
+ }
+
+ if (!enable && hwacc_enabled[hwacc_dev]) {
+ r = regulator_disable(hwacc_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: disable failed\n");
+ goto out;
+ }
+ hwacc_enabled[hwacc_dev] = false;
+ }
+
+ if (ram_retention) {
+ if (!enable_ret && hwacc_ret_enabled[hwacc_dev]) {
+ r = regulator_disable(hwacc_ret_regulator[hwacc_dev]);
+ if (r < 0) {
+ pr_err("prcmu_set_hwacc: ret disable failed\n");
+ goto out;
+ }
+ hwacc_ret_enabled[hwacc_dev] = false;
+ }
+ }
+
+out:
+ return r;
+}
+EXPORT_SYMBOL(prcmu_set_hwacc);
+
+/**
+ * prcmu_set_epod - set the state of a EPOD (power domain)
+ * @epod_id: The EPOD to set
+ * @epod_state: The new EPOD state
+ *
+ * This function sets the state of a EPOD (power domain). It may not be called
+ * from interrupt context.
+ */
+int prcmu_set_epod(u16 epod_id, u8 epod_state)
+{
+ int r = 0;
+ bool ram_retention = false;
+ int i;
+
+ /* check argument */
+ BUG_ON(epod_id >= NUM_EPOD_ID);
+
+ /* set flag if retention is possible */
+ switch (epod_id) {
+ case EPOD_ID_SVAMMDSP:
+ case EPOD_ID_SIAMMDSP:
+ case EPOD_ID_ESRAM12:
+ case EPOD_ID_ESRAM34:
+ ram_retention = true;
+ break;
+ }
+
+ /* check argument */
+ BUG_ON(epod_state > EPOD_STATE_ON);
+ BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention);
+
+ /* get lock */
+ mutex_lock(&mb2_transfer.lock);
+
+ /* wait for mailbox */
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* fill in mailbox */
+ for (i = 0; i < NUM_EPOD_ID; i++)
+ writeb(EPOD_STATE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB2 + i));
+ writeb(epod_state, (tcdm_base + PRCM_REQ_MB2 + epod_id));
+
+ writeb(MB2H_DPS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB2));
+
+ writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ /*
+ * The current firmware version does not handle errors correctly,
+ * and we cannot recover if there is an error.
+ * This is expected to change when the firmware is updated.
+ */
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ __func__);
+ r = -EIO;
+ goto unlock_and_return;
+ }
+
+ if (mb2_transfer.ack.status != HWACC_PWR_ST_OK)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
+/**
+ * prcmu_configure_auto_pm - Configure autonomous power management.
+ * @sleep: Configuration for ApSleep.
+ * @idle: Configuration for ApIdle.
+ */
+void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep,
+ struct prcmu_auto_pm_config *idle)
+{
+ u32 sleep_cfg;
+ u32 idle_cfg;
+ unsigned long flags;
+
+ BUG_ON((sleep == NULL) || (idle == NULL));
+
+ sleep_cfg = (sleep->sva_auto_pm_enable & 0xF);
+ sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_auto_pm_enable & 0xF));
+ sleep_cfg = ((sleep_cfg << 8) | (sleep->sva_power_on & 0xFF));
+ sleep_cfg = ((sleep_cfg << 8) | (sleep->sia_power_on & 0xFF));
+ sleep_cfg = ((sleep_cfg << 4) | (sleep->sva_policy & 0xF));
+ sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_policy & 0xF));
+
+ idle_cfg = (idle->sva_auto_pm_enable & 0xF);
+ idle_cfg = ((idle_cfg << 4) | (idle->sia_auto_pm_enable & 0xF));
+ idle_cfg = ((idle_cfg << 8) | (idle->sva_power_on & 0xFF));
+ idle_cfg = ((idle_cfg << 8) | (idle->sia_power_on & 0xFF));
+ idle_cfg = ((idle_cfg << 4) | (idle->sva_policy & 0xF));
+ idle_cfg = ((idle_cfg << 4) | (idle->sia_policy & 0xF));
+
+ spin_lock_irqsave(&mb2_transfer.auto_pm_lock, flags);
+
+ /*
+ * The autonomous power management configuration is done through
+ * fields in mailbox 2, but these fields are only used as shared
+ * variables - i.e. there is no need to send a message.
+ */
+ writel(sleep_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_SLEEP));
+ writel(idle_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_IDLE));
+
+ mb2_transfer.auto_pm_enabled =
+ ((sleep->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
+ (sleep->sia_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
+ (idle->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
+ (idle->sia_auto_pm_enable == PRCMU_AUTO_PM_ON));
+
+ spin_unlock_irqrestore(&mb2_transfer.auto_pm_lock, flags);
+}
+EXPORT_SYMBOL(prcmu_configure_auto_pm);
+
+bool prcmu_is_auto_pm_enabled(void)
+{
+ return mb2_transfer.auto_pm_enabled;
+}
+
+static int request_sysclk(bool enable)
+{
+ int r;
+ unsigned long flags;
+
+ r = 0;
+
+ mutex_lock(&mb3_transfer.sysclk_lock);
+
+ spin_lock_irqsave(&mb3_transfer.lock, flags);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
+ cpu_relax();
+
+ writeb((enable ? 1 : 0), (tcdm_base + PRCM_REQ_MB3_SYSCLK_MGT));
+
+ writeb(MB3H_SYSCLK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB3));
+ writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ spin_unlock_irqrestore(&mb3_transfer.lock, flags);
+
+ /*
+ * The firmware only sends an ACK if we want to enable the
+ * SysClk, and it succeeds.
+ */
+ if (enable && !wait_for_completion_timeout(&mb3_transfer.sysclk_work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ __func__);
+ r = -EIO;
+ }
+
+ mutex_unlock(&mb3_transfer.sysclk_lock);
+
+ return r;
+}
+
+static int request_timclk(bool enable)
+{
+ u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
+
+ if (!enable)
+ val |= PRCM_TCR_STOP_TIMERS;
+ writel(val, (_PRCMU_BASE + PRCM_TCR));
+
+ return 0;
+}
+
+static int request_reg_clock(u8 clock, bool enable)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
+ if (enable) {
+ val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
+ } else {
+ clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+ val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
+ }
+ writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
+
+ /* Release the HW semaphore. */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ return 0;
+}
+
+static int request_sga_clock(u8 clock, bool enable)
+{
+ u32 val;
+ int ret;
+
+ if (enable) {
+ val = readl(_PRCMU_BASE + PRCM_CGATING_BYPASS);
+ writel(val | PRCM_CGATING_BYPASS_ICN2,
+ _PRCMU_BASE + PRCM_CGATING_BYPASS);
+ }
+
+ ret = request_reg_clock(clock, enable);
+
+ if (!ret && !enable) {
+ val = readl(_PRCMU_BASE + PRCM_CGATING_BYPASS);
+ writel(val & ~PRCM_CGATING_BYPASS_ICN2,
+ _PRCMU_BASE + PRCM_CGATING_BYPASS);
+ }
+
+ return ret;
+}
+
+/**
+ * prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * @clock: The clock for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+int prcmu_request_clock(u8 clock, bool enable)
+{
+ if (clock == PRCMU_SGACLK)
+ return request_sga_clock(clock, enable);
+ else if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_reg_clock(clock, enable);
+ else if (clock == PRCMU_TIMCLK)
+ return request_timclk(enable);
+ else if (clock == PRCMU_SYSCLK)
+ return request_sysclk(enable);
+ else if (clock == PRCMU_PLLSOC1)
+ return request_pll(clock, enable);
+ else
+ return -EINVAL;
+}
+
+int prcmu_config_esram0_deep_sleep(u8 state)
+{
+ if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
+ (state < ESRAM0_DEEP_SLEEP_STATE_OFF))
+ return -EINVAL;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(MB4H_MEM_ST, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+ writeb(((DDR_PWR_STATE_OFFHIGHLAT << 4) | DDR_PWR_STATE_ON),
+ (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE));
+ writeb(DDR_PWR_STATE_ON,
+ (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE));
+ writeb(state, (tcdm_base + PRCM_REQ_MB4_ESRAM0_ST));
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb4_transfer.work);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return 0;
+}
+
+int prcmu_config_hotdog(u8 threshold)
+{
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(threshold, (tcdm_base + PRCM_REQ_MB4_HOTDOG_THRESHOLD));
+ writeb(MB4H_HOTDOG, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb4_transfer.work);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return 0;
+}
+
+int prcmu_config_hotmon(u8 low, u8 high)
+{
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(low, (tcdm_base + PRCM_REQ_MB4_HOTMON_LOW));
+ writeb(high, (tcdm_base + PRCM_REQ_MB4_HOTMON_HIGH));
+ writeb((HOTMON_CONFIG_LOW | HOTMON_CONFIG_HIGH),
+ (tcdm_base + PRCM_REQ_MB4_HOTMON_CONFIG));
+ writeb(MB4H_HOTMON, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb4_transfer.work);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return 0;
+}
+
+static int config_hot_period(u16 val)
+{
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writew(val, (tcdm_base + PRCM_REQ_MB4_HOT_PERIOD));
+ writeb(MB4H_HOT_PERIOD, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb4_transfer.work);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return 0;
+}
+
+int prcmu_start_temp_sense(u16 cycles32k)
+{
+ if (cycles32k == 0xFFFF)
+ return -EINVAL;
+
+ return config_hot_period(cycles32k);
+}
+
+int prcmu_stop_temp_sense(void)
+{
+ return config_hot_period(0xFFFF);
+}
+
+static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)
+{
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0));
+ writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1));
+ writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2));
+ writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3));
+
+ writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb4_transfer.work);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return 0;
+
+}
+
+int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+ BUG_ON(num == 0 || num > 0xf);
+ return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0,
+ sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
+ A9WDOG_AUTO_OFF_DIS);
+}
+
+int prcmu_enable_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
+}
+
+int prcmu_disable_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
+}
+
+int prcmu_kick_a9wdog(u8 id)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
+}
+
+/*
+ * timeout is 28 bit, in ms.
+ */
+int prcmu_load_a9wdog(u8 id, u32 timeout)
+{
+ return prcmu_a9wdog(MB4H_A9WDOG_LOAD,
+ (id & A9WDOG_ID_MASK) |
+ /*
+ * Put the lowest 28 bits of timeout at
+ * offset 4. Four first bits are used for id.
+ */
+ (u8)((timeout << 4) & 0xf0),
+ (u8)((timeout >> 4) & 0xff),
+ (u8)((timeout >> 12) & 0xff),
+ (u8)((timeout >> 20) & 0xff));
+}
+
+/**
+ * prcmu_set_clock_divider() - Configure the clock divider.
+ * @clock: The clock for which the request is made.
+ * @divider: The clock divider. (< 32)
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+int prcmu_set_clock_divider(u8 clock, u8 divider)
+{
+ u32 val;
+ unsigned long flags;
+
+ if ((clock >= PRCMU_NUM_REG_CLOCKS) || (divider < 1) || (31 < divider))
+ return -EINVAL;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
+ val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK);
+ val |= (u32)divider;
+ writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
+
+ /* Release the HW semaphore. */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ return 0;
+}
+
+/**
+ * prcmu_abb_read() - Read register value(s) from the ABB.
+ * @slave: The I2C slave address.
+ * @reg: The (start) register address.
+ * @value: The read out value(s).
+ * @size: The number of registers to read.
+ *
+ * Reads register value(s) from the ABB.
+ * @size has to be 1 for the current firmware version.
+ */
+int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ int r;
+
+ if (size != 1)
+ return -EINVAL;
+
+ mutex_lock(&mb5_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ cpu_relax();
+
+ writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
+ writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
+ writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
+ writeb(0, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
+
+ writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb5_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ __func__);
+ r = -EIO;
+ } else {
+ r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO);
+ }
+
+ if (!r)
+ *value = mb5_transfer.ack.value;
+
+ mutex_unlock(&mb5_transfer.lock);
+
+ return r;
+}
+
+/**
+ * prcmu_abb_write() - Write register value(s) to the ABB.
+ * @slave: The I2C slave address.
+ * @reg: The (start) register address.
+ * @value: The value(s) to write.
+ * @size: The number of registers to write.
+ *
+ * Reads register value(s) from the ABB.
+ * @size has to be 1 for the current firmware version.
+ */
+int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ int r;
+
+ if (size != 1)
+ return -EINVAL;
+
+ mutex_lock(&mb5_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ cpu_relax();
+
+ writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
+ writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
+ writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
+ writeb(*value, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
+
+ writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb5_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ __func__);
+ r = -EIO;
+ } else {
+ r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO);
+ }
+
+ mutex_unlock(&mb5_transfer.lock);
+
+ return r;
+}
+
+/**
+ * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
+ */
+void prcmu_ac_wake_req(void)
+{
+ u32 val;
+ u32 status;
+
+ mutex_lock(&mb0_transfer.ac_wake_lock);
+
+ val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ if (val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ)
+ goto unlock_and_return;
+
+ atomic_set(&ac_wake_req_state, 1);
+
+retry:
+ writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
+ (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+
+ if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+ msecs_to_jiffies(5000))) {
+ panic("prcmu: %s timed out (5 s) waiting for a reply.\n",
+ __func__);
+ goto unlock_and_return;
+ }
+
+ /*
+ * The modem can generate an AC_WAKE_ACK, and then still go to sleep.
+ * As a workaround, we wait, and then check that the modem is indeed
+ * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS
+ * register, which may not be the whole truth).
+ */
+ udelay(400);
+ status = (readl(_PRCMU_BASE + PRCM_MOD_AWAKE_STATUS) & BITS(0, 2));
+ if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE |
+ PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) {
+ pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n",
+ __func__, status);
+ udelay(1200);
+ writel(val, (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+ if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+ msecs_to_jiffies(5000)))
+ goto retry;
+ panic("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
+ __func__);
+ }
+
+unlock_and_return:
+ mutex_unlock(&mb0_transfer.ac_wake_lock);
+}
+
+/**
+ * prcmu_ac_sleep_req - called when ARM no longer needs to talk to modem
+ */
+void prcmu_ac_sleep_req()
+{
+ u32 val;
+
+ mutex_lock(&mb0_transfer.ac_wake_lock);
+
+ val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ if (!(val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ))
+ goto unlock_and_return;
+
+ writel((val & ~PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
+ (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+
+ if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+ msecs_to_jiffies(5000))) {
+ panic("prcmu: %s timed out (5 s) waiting for a reply.\n",
+ __func__);
+ }
+
+ atomic_set(&ac_wake_req_state, 0);
+
+unlock_and_return:
+ mutex_unlock(&mb0_transfer.ac_wake_lock);
+}
+
+bool prcmu_is_ac_wake_requested(void)
+{
+ return (atomic_read(&ac_wake_req_state) != 0);
+}
+
+/**
+ * prcmu_system_reset - System reset
+ *
+ * Saves the reset reason code and then sets the APE_SOFTRST register which
+ * fires interrupt to fw
+ */
+void prcmu_system_reset(u16 reset_code)
+{
+ writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON));
+ writel(1, (_PRCMU_BASE + PRCM_APE_SOFTRST));
+}
+
+/**
+ * prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 prcmu_get_reset_code(void)
+{
+ return readw(tcdm_base + PRCM_SW_RST_REASON);
+}
+
+/**
+ * prcmu_reset_modem - ask the PRCMU to reset modem
+ */
+void prcmu_modem_reset(void)
+{
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_RESET_MODEM, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+ wait_for_completion(&mb1_transfer.work);
+
+ /*
+ * No need to check return from PRCMU as modem should go in reset state
+ * This state is already managed by upper layer
+ */
+
+ mutex_unlock(&mb1_transfer.lock);
+}
+
+static void ack_dbb_wakeup(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ writeb(MB0H_READ_WAKEUP_ACK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
+ writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static inline void print_unknown_header_warning(u8 n, u8 header)
+{
+ pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
+ header, n);
+}
+
+static bool read_mailbox_0(void)
+{
+ bool r;
+ u32 ev;
+ unsigned int n;
+ u8 header;
+
+ header = readb(tcdm_base + PRCM_MBOX_HEADER_ACK_MB0);
+ switch (header) {
+ case MB0H_WAKEUP_EXE:
+ case MB0H_WAKEUP_SLEEP:
+ if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
+ ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_1_8500);
+ else
+ ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_0_8500);
+
+ if (ev & (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK))
+ complete(&mb0_transfer.ac_wake_work);
+ if (ev & WAKEUP_BIT_SYSCLK_OK)
+ complete(&mb3_transfer.sysclk_work);
+
+ ev &= mb0_transfer.req.dbb_irqs;
+
+ for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
+ if (ev & prcmu_irq_bit[n])
+ generic_handle_irq(IRQ_PRCMU_BASE + n);
+ }
+ r = true;
+ break;
+ default:
+ print_unknown_header_warning(0, header);
+ r = false;
+ break;
+ }
+ writel(MBOX_BIT(0), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ return r;
+}
+
+static bool read_mailbox_1(void)
+{
+ mb1_transfer.ack.header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1);
+ mb1_transfer.ack.arm_opp = readb(tcdm_base +
+ PRCM_ACK_MB1_CURRENT_ARM_OPP);
+ mb1_transfer.ack.ape_opp = readb(tcdm_base +
+ PRCM_ACK_MB1_CURRENT_APE_OPP);
+ mb1_transfer.ack.ape_voltage_status = readb(tcdm_base +
+ PRCM_ACK_MB1_APE_VOLTAGE_STATUS);
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ complete(&mb1_transfer.work);
+ return false;
+}
+
+static bool read_mailbox_2(void)
+{
+ mb2_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB2_DPS_STATUS);
+ writel(MBOX_BIT(2), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ complete(&mb2_transfer.work);
+ return false;
+}
+
+static bool read_mailbox_3(void)
+{
+ writel(MBOX_BIT(3), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ return false;
+}
+
+static bool read_mailbox_4(void)
+{
+ u8 header;
+ bool do_complete = true;
+
+ header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB4);
+ switch (header) {
+ case MB4H_MEM_ST:
+ case MB4H_HOTDOG:
+ case MB4H_HOTMON:
+ case MB4H_HOT_PERIOD:
+ case MB4H_A9WDOG_CONF:
+ case MB4H_A9WDOG_EN:
+ case MB4H_A9WDOG_DIS:
+ case MB4H_A9WDOG_LOAD:
+ case MB4H_A9WDOG_KICK:
+ break;
+ default:
+ print_unknown_header_warning(4, header);
+ do_complete = false;
+ break;
+ }
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+
+ if (do_complete)
+ complete(&mb4_transfer.work);
+
+ return false;
+}
+
+static bool read_mailbox_5(void)
+{
+ mb5_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB5_I2C_STATUS);
+ mb5_transfer.ack.value = readb(tcdm_base + PRCM_ACK_MB5_I2C_VAL);
+ writel(MBOX_BIT(5), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ complete(&mb5_transfer.work);
+ return false;
+}
+
+static bool read_mailbox_6(void)
+{
+ writel(MBOX_BIT(6), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ return false;
+}
+
+static bool read_mailbox_7(void)
+{
+ writel(MBOX_BIT(7), (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+ return false;
+}
+
+static bool (* const read_mailbox[NUM_MB])(void) = {
+ read_mailbox_0,
+ read_mailbox_1,
+ read_mailbox_2,
+ read_mailbox_3,
+ read_mailbox_4,
+ read_mailbox_5,
+ read_mailbox_6,
+ read_mailbox_7
+};
+
+static irqreturn_t prcmu_irq_handler(int irq, void *data)
+{
+ u32 bits;
+ u8 n;
+ irqreturn_t r;
+
+ bits = (readl(_PRCMU_BASE + PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ if (unlikely(!bits))
+ return IRQ_NONE;
+
+ r = IRQ_HANDLED;
+ for (n = 0; bits; n++) {
+ if (bits & MBOX_BIT(n)) {
+ bits -= MBOX_BIT(n);
+ if (read_mailbox[n]())
+ r = IRQ_WAKE_THREAD;
+ }
+ }
+ return r;
+}
+
+static irqreturn_t prcmu_irq_thread_fn(int irq, void *data)
+{
+ ack_dbb_wakeup();
+ return IRQ_HANDLED;
+}
+
+static void prcmu_mask_work(struct work_struct *work)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static void prcmu_irq_mask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->irq - IRQ_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+
+ if (d->irq != IRQ_PRCMU_CA_SLEEP)
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void prcmu_irq_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->irq - IRQ_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+
+ if (d->irq != IRQ_PRCMU_CA_SLEEP)
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void noop(struct irq_data *d)
+{
+}
+
+static struct irq_chip prcmu_irq_chip = {
+ .name = "prcmu",
+ .irq_disable = prcmu_irq_mask,
+ .irq_ack = noop,
+ .irq_mask = prcmu_irq_mask,
+ .irq_unmask = prcmu_irq_unmask,
+};
+
+void __init prcmu_early_init(void)
+{
+ unsigned int i;
+
+ void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
+
+ if (tcpm_base != NULL) {
+ int version;
+ version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
+ prcmu_version.project_number = version & 0xFF;
+ prcmu_version.api_version = (version >> 8) & 0xFF;
+ prcmu_version.func_version = (version >> 16) & 0xFF;
+ prcmu_version.errata = (version >> 24) & 0xFF;
+ pr_info("PRCMU firmware version %d.%d.%d\n",
+ (version >> 8) & 0xFF, (version >> 16) & 0xFF,
+ (version >> 24) & 0xFF);
+ iounmap(tcpm_base);
+ }
+ tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+
+ spin_lock_init(&mb0_transfer.lock);
+ spin_lock_init(&mb0_transfer.dbb_irqs_lock);
+ mutex_init(&mb0_transfer.ac_wake_lock);
+ init_completion(&mb0_transfer.ac_wake_work);
+ mutex_init(&mb1_transfer.lock);
+ init_completion(&mb1_transfer.work);
+ mb1_transfer.ape_opp = APE_NO_CHANGE;
+ mutex_init(&mb2_transfer.lock);
+ init_completion(&mb2_transfer.work);
+ spin_lock_init(&mb2_transfer.auto_pm_lock);
+ spin_lock_init(&mb3_transfer.lock);
+ mutex_init(&mb3_transfer.sysclk_lock);
+ init_completion(&mb3_transfer.sysclk_work);
+ mutex_init(&mb4_transfer.lock);
+ init_completion(&mb4_transfer.work);
+ mutex_init(&mb5_transfer.lock);
+ init_completion(&mb5_transfer.work);
+
+ INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
+
+ /* Initalize irqs. */
+ for (i = 0; i < NUM_PRCMU_WAKEUPS; i++) {
+ unsigned int irq;
+
+ irq = IRQ_PRCMU_BASE + i;
+ set_irq_flags(irq, IRQF_VALID);
+ irq_set_chip_and_handler(irq, &prcmu_irq_chip,
+ handle_simple_irq);
+ }
+}
+
+static void __init init_prcm_registers(void)
+{
+ u32 val;
+
+ val = readl(_PRCMU_BASE + PRCM_A9PL_FORCE_CLKEN);
+ val &= ~(PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN |
+ PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN);
+ writel(val, (_PRCMU_BASE + PRCM_A9PL_FORCE_CLKEN));
+}
+
+/**
+ * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
+ *
+ */
+int __init prcmu_init(void)
+{
+ int err = 0;
+
+ if (ux500_is_svp())
+ return -ENODEV;
+
+ init_prcm_registers();
+
+ /* Clean up the mailbox interrupts after pre-kernel code. */
+ writel(ALL_MBOX_BITS, (_PRCMU_BASE + PRCM_ARM_IT1_CLR));
+
+ err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler,
+ prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
+ if (err < 0) {
+ pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n");
+ err = -EBUSY;
+ goto no_irq_return;
+ }
+
+ prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+
+ if (prcmu_debug_init())
+ pr_err("prcmu: Failed to initialize debugfs\n");
+
+no_irq_return:
+ return err;
+}
+
+arch_initcall(prcmu_init);
diff --git a/arch/arm/mach-ux500/prcmu-debug.c b/arch/arm/mach-ux500/prcmu-debug.c
index 8344ff36ffb..139498033fe 100644
--- a/arch/arm/mach-ux500/prcmu-debug.c
+++ b/arch/arm/mach-ux500/prcmu-debug.c
@@ -18,107 +18,110 @@
#include <mach/prcmu.h>
-enum ape_opp_debug {
- APE_50_OPP_DEBUG,
- APE_100_OPP_DEBUG,
- NUM_APE_OPP_DEBUG,
-};
-
-enum ddr_opp_debug {
- DDR_25_OPP_DEBUG,
- DDR_50_OPP_DEBUG,
- DDR_100_OPP_DEBUG,
- NUM_DDR_OPP_DEBUG,
-};
+#define MAX_STATES 5
+#define MAX_NAMELEN 16
-struct ape_state_history {
+struct state_history {
ktime_t start;
u32 state;
- u32 counter[NUM_APE_OPP_DEBUG];
- ktime_t time[NUM_APE_OPP_DEBUG];
+ u32 counter[MAX_STATES];
+ u8 opps[MAX_STATES];
+ int max_states;
+ int req;
+ bool reqs[MAX_STATES];
+ ktime_t time[MAX_STATES];
+ int state_names[MAX_STATES];
+ char prefix[MAX_NAMELEN];
spinlock_t lock;
};
-struct ddr_state_history {
- ktime_t start;
- u32 state;
- u32 counter[NUM_DDR_OPP_DEBUG];
- ktime_t time[NUM_DDR_OPP_DEBUG];
- spinlock_t lock;
+static struct state_history ape_sh = {
+ .prefix = "APE",
+ .req = PRCMU_QOS_APE_OPP,
+ .opps = {APE_50_OPP, APE_100_OPP},
+ .state_names = {50, 100},
+ .max_states = 2,
+};
+
+static struct state_history ddr_sh = {
+ .prefix = "DDR",
+ .req = PRCMU_QOS_DDR_OPP,
+ .opps = {DDR_25_OPP, DDR_50_OPP, DDR_100_OPP},
+ .state_names = {25, 50, 100},
+ .max_states = 3,
+};
+
+static struct state_history arm_sh = {
+ .prefix = "ARM",
+ .req = PRCMU_QOS_ARM_OPP,
+ .opps = {ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP},
+ .state_names = {25, 50, 100, 125},
+ .max_states = 4,
};
-static struct ape_state_history *ape_sh;
-static struct ddr_state_history *ddr_sh;
static int ape_voltage_count;
-void prcmu_debug_ape_opp_log(u8 opp)
+static void log_set(struct state_history *sh, u8 opp)
{
ktime_t now;
ktime_t dtime;
unsigned long flags;
int state;
- if (opp == APE_50_OPP)
- state = APE_50_OPP_DEBUG;
- else
- state = APE_100_OPP_DEBUG;
-
now = ktime_get();
- spin_lock_irqsave(&ape_sh->lock, flags);
+ spin_lock_irqsave(&sh->lock, flags);
- dtime = ktime_sub(now, ape_sh->start);
- ape_sh->time[state] = ktime_add(ape_sh->time[state], dtime);
- ape_sh->start = now;
- ape_sh->counter[state]++;
- ape_sh->state = state;
+ for (state = 0 ; sh->opps[state] != opp; state++)
+ ;
+ BUG_ON(state >= sh->max_states);
- spin_unlock_irqrestore(&ape_sh->lock, flags);
+ dtime = ktime_sub(now, sh->start);
+ sh->time[sh->state] = ktime_add(sh->time[sh->state], dtime);
+ sh->start = now;
+ sh->counter[sh->state]++;
+ sh->state = state;
+
+ spin_unlock_irqrestore(&sh->lock, flags);
}
-void prcmu_debug_ddr_opp_log(u8 opp)
+void prcmu_debug_ape_opp_log(u8 opp)
{
- ktime_t now;
- ktime_t dtime;
- unsigned long flags;
- int state;
-
- if (opp == DDR_25_OPP)
- state = DDR_25_OPP_DEBUG;
- else if (opp == DDR_50_OPP)
- state = DDR_50_OPP_DEBUG;
- else
- state = DDR_100_OPP_DEBUG;
-
- now = ktime_get();
- spin_lock_irqsave(&ddr_sh->lock, flags);
+ log_set(&ape_sh, opp);
+}
- dtime = ktime_sub(now, ddr_sh->start);
- ddr_sh->time[state] = ktime_add(ddr_sh->time[state], dtime);
- ddr_sh->start = now;
- ddr_sh->counter[state]++;
- ddr_sh->state = state;
+void prcmu_debug_ddr_opp_log(u8 opp)
+{
+ log_set(&ddr_sh, opp);
+}
- spin_unlock_irqrestore(&ddr_sh->lock, flags);
+void prcmu_debug_arm_opp_log(u8 opp)
+{
+ log_set(&arm_sh, opp);
}
-static ssize_t ape_stats_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
+static void log_reset(struct state_history *sh)
{
unsigned long flags;
int i;
- pr_info("/nreset\n");
+ pr_info("reset\n");
- spin_lock_irqsave(&ape_sh->lock, flags);
- for (i = 0; i < NUM_APE_OPP_DEBUG; i++) {
- ape_sh->counter[i] = 0;
- ape_sh->time[i] = ktime_set(0, 0);
+ spin_lock_irqsave(&sh->lock, flags);
+ for (i = 0; i < sh->max_states; i++) {
+ sh->counter[i] = 0;
+ sh->time[i] = ktime_set(0, 0);
}
- ape_sh->start = ktime_get();
- spin_unlock_irqrestore(&ape_sh->lock, flags);
+ sh->start = ktime_get();
+ spin_unlock_irqrestore(&sh->lock, flags);
+
+}
+static ssize_t ape_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&ape_sh);
return count;
}
@@ -126,194 +129,135 @@ static ssize_t ddr_stats_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- unsigned long flags;
- int i;
-
- pr_info("/nreset\n");
-
- spin_lock_irqsave(&ddr_sh->lock, flags);
- for (i = 0; i < NUM_DDR_OPP_DEBUG; i++) {
- ddr_sh->counter[i] = 0;
- ddr_sh->time[i] = ktime_set(0, 0);
- }
-
- ddr_sh->start = ktime_get();
- spin_unlock_irqrestore(&ddr_sh->lock, flags);
+ log_reset(&ddr_sh);
+ return count;
+}
+static ssize_t arm_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&arm_sh);
return count;
}
-static int ape_stats_print(struct seq_file *s, void *p)
+static int log_print(struct seq_file *s, struct state_history *sh)
{
int i;
unsigned long flags;
ktime_t total;
- ktime_t now;
ktime_t dtime;
- s64 t_us;
+ s64 t_ms;
s64 perc;
- s64 total_us;
+ s64 total_ms;
- spin_lock_irqsave(&ape_sh->lock, flags);
- /* Update time in state */
- now = ktime_get();
- dtime = ktime_sub(now, ape_sh->start);
- ape_sh->time[ape_sh->state] =
- ktime_add(ape_sh->time[ape_sh->state], dtime);
- ape_sh->start = now;
-
- /* Now print the stats */
- total = ktime_set(0, 0);
-
- for (i = 0; i < NUM_APE_OPP_DEBUG; i++)
- total = ktime_add(total, ape_sh->time[i]);
- total_us = ktime_to_us(total);
- do_div(total_us, 100);
-
- for (i = 0; i < NUM_APE_OPP_DEBUG; i++) {
- t_us = ktime_to_us(ape_sh->time[i]);
- perc = ktime_to_us(ape_sh->time[i]);
- do_div(t_us, 1000); /* to ms */
- do_div(perc, total_us);
- if (i == APE_50_OPP_DEBUG)
- seq_printf(s, "%s: # %u in %d ms %d%%\n",
- "APE OPP 50% ",
- ape_sh->counter[i],
- (u32) t_us, (u32)perc);
- else
- seq_printf(s, "%s: # %u in %d ms %d%%\n",
- "APE OPP 100%",
- ape_sh->counter[i],
- (u32) t_us, (u32)perc);
+ spin_lock_irqsave(&sh->lock, flags);
- }
- spin_unlock_irqrestore(&ape_sh->lock, flags);
- return 0;
-}
+ dtime = ktime_sub(ktime_get(), sh->start);
-static int ddr_stats_print(struct seq_file *s, void *p)
-{
- int i;
- unsigned long flags;
- ktime_t total;
- ktime_t now;
- ktime_t dtime;
- s64 t_us;
- s64 perc;
- s64 total_us;
+ total = dtime;
- spin_lock_irqsave(&ddr_sh->lock, flags);
- /* Update time in state */
- now = ktime_get();
- dtime = ktime_sub(now, ddr_sh->start);
- ddr_sh->time[ddr_sh->state] =
- ktime_add(ddr_sh->time[ddr_sh->state], dtime);
- ddr_sh->start = now;
-
- /* Now print the stats */
- total = ktime_set(0, 0);
-
- for (i = 0; i < NUM_DDR_OPP_DEBUG; i++)
- total = ktime_add(total, ddr_sh->time[i]);
- total_us = ktime_to_us(total);
- do_div(total_us, 100);
-
- for (i = 0; i < NUM_DDR_OPP_DEBUG; i++) {
- t_us = ktime_to_us(ddr_sh->time[i]);
- perc = ktime_to_us(ddr_sh->time[i]);
- do_div(t_us, 1000); /* to ms */
- do_div(perc, total_us);
- if (i == DDR_25_OPP_DEBUG)
- seq_printf(s, "%s: # %u in %d ms %d%%\n",
- "DDR OPP 25% ",
- ddr_sh->counter[i],
- (u32) t_us, (u32)perc);
- else if (i == DDR_50_OPP_DEBUG)
- seq_printf(s, "%s: # %u in %d ms %d%%\n",
- "DDR OPP 50% ",
- ddr_sh->counter[i],
- (u32) t_us, (u32)perc);
- else
- seq_printf(s, "%s: # %u in %d ms %d%%\n",
- "DDR OPP 100%",
- ddr_sh->counter[i],
- (u32) t_us, (u32)perc);
+ for (i = 0; i < sh->max_states; i++)
+ total = ktime_add(total, sh->time[i]);
+ total_ms = ktime_to_ms(total);
+
+ for (i = 0; i < sh->max_states; i++) {
+ ktime_t t = sh->time[i];
+ if (sh->state == i)
+ t = ktime_add(t, dtime);
+
+ t_ms = ktime_to_ms(t);
+ perc = 100 * t_ms;
+ do_div(perc, total_ms);
+
+ seq_printf(s, "%s OPP %d: # %u in %lld ms %d%%\n",
+ sh->prefix, sh->state_names[i],
+ sh->counter[i] + (int)(sh->state == i),
+ t_ms, (u32)perc);
}
- spin_unlock_irqrestore(&ddr_sh->lock, flags);
+ spin_unlock_irqrestore(&sh->lock, flags);
return 0;
}
-static int arm_opp_read(struct seq_file *s, void *p)
+static int ape_stats_print(struct seq_file *s, void *p)
{
- int opp;
-
- opp = prcmu_get_arm_opp();
- return seq_printf(s, "%s (%d)\n",
- (opp == ARM_MAX_OPP) ? "max" :
- (opp == ARM_MAX_FREQ100OPP) ? "max-freq100" :
- (opp == ARM_100_OPP) ? "100%" :
- (opp == ARM_50_OPP) ? "50%" :
- (opp == ARM_EXTCLK) ? "25% (extclk)" :
- "unknown", opp);
+ log_print(s, &ape_sh);
+ return 0;
}
-static int ape_opp_read(struct seq_file *s, void *p)
+static int ddr_stats_print(struct seq_file *s, void *p)
{
- int opp;
+ log_print(s, &ddr_sh);
+ return 0;
+}
- opp = prcmu_get_ape_opp();
- return seq_printf(s, "%s (%d)\n",
- (opp == APE_100_OPP) ? "100%" :
- (opp == APE_50_OPP) ? "50%" :
- "unknown", opp);
+static int arm_stats_print(struct seq_file *s, void *p)
+{
+ log_print(s, &arm_sh);
+ return 0;
}
-static int ddr_opp_read(struct seq_file *s, void *p)
+static int opp_read(struct seq_file *s, void *p)
{
int opp;
- opp = prcmu_get_ddr_opp();
- return seq_printf(s, "%s (%d)\n",
- (opp == DDR_100_OPP) ? "100%" :
- (opp == DDR_50_OPP) ? "50%" :
- (opp == DDR_25_OPP) ? "25%" :
- "unknown", opp);
+ struct state_history *sh = (struct state_history *)s->private;
+
+ switch (sh->req) {
+ case PRCMU_QOS_DDR_OPP:
+ opp = prcmu_get_ddr_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == DDR_100_OPP) ? "100%" :
+ (opp == DDR_50_OPP) ? "50%" :
+ (opp == DDR_25_OPP) ? "25%" :
+ "unknown", opp);
+ break;
+ case PRCMU_QOS_APE_OPP:
+ opp = prcmu_get_ape_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == APE_100_OPP) ? "100%" :
+ (opp == APE_50_OPP) ? "50%" :
+ "unknown", opp);
+ break;
+ case PRCMU_QOS_ARM_OPP:
+ opp = prcmu_get_arm_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == ARM_MAX_OPP) ? "max" :
+ (opp == ARM_MAX_FREQ100OPP) ? "max-freq100" :
+ (opp == ARM_100_OPP) ? "100%" :
+ (opp == ARM_50_OPP) ? "50%" :
+ (opp == ARM_EXTCLK) ? "25% (extclk)" :
+ "unknown", opp);
+ break;
+ default:
+ break;
+ }
+ return 0;
+
}
static ssize_t opp_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos, int prcmu_qos_class)
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
long unsigned i;
int err;
+ struct state_history *sh = (struct state_history *)
+ ((struct seq_file *)file->private_data)->private;
err = kstrtoul_from_user(user_buf, count, 0, &i);
if (err)
return err;
- prcmu_qos_force_opp(prcmu_qos_class, i);
+ prcmu_qos_force_opp(sh->req, i);
- pr_info("prcmu debug: forced OPP for %d to %d\n", prcmu_qos_class, (int)i);
+ pr_info("prcmu debug: forced OPP for %s to %d\n", sh->prefix, (int)i);
return count;
}
-static ssize_t ddr_opp_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- return opp_write(file, user_buf, count, ppos, PRCMU_QOS_DDR_OPP);
-}
-
-static ssize_t ape_opp_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- return opp_write(file, user_buf, count, ppos, PRCMU_QOS_APE_OPP);
-}
-
static int cpufreq_delay_read(struct seq_file *s, void *p)
{
return seq_printf(s, "%lu\n", prcmu_qos_get_cpufreq_opp_delay());
@@ -383,19 +327,55 @@ static ssize_t cpufreq_delay_write(struct file *file,
return count;
}
-static int arm_opp_open_file(struct inode *inode, struct file *file)
+/* These are just for u8500 */
+#define AVS_VBB_RET 0x0
+#define AVS_VBB_MAX_OPP 0x1
+#define AVS_VBB_100_OPP 0x2
+#define AVS_VBB_50_OPP 0x3
+#define AVS_VARM_MAX_OPP 0x4
+#define AVS_VARM_100_OPP 0x5
+#define AVS_VARM_50_OPP 0x6
+#define AVS_VARM_RET 0x7
+#define AVS_VAPE_100_OPP 0x8
+#define AVS_VAPE_50_OPP 0x9
+#define AVS_VMOD_100_OPP 0xA
+#define AVS_VMOD_50_OPP 0xB
+#define AVS_VSAFE 0xC
+#define AVS_SIZE 14
+
+/* TODO: declare this in proper include file when that exists */
+extern void prcmu_dump_avs(u8 *avs, int s);
+static int avs_read(struct seq_file *s, void *p)
{
- return single_open(file, arm_opp_read, inode->i_private);
-}
-static int ape_opp_open_file(struct inode *inode, struct file *file)
-{
- return single_open(file, ape_opp_read, inode->i_private);
+ u8 avs[AVS_SIZE];
+
+ if (cpu_is_u8500()) {
+ prcmu_dump_avs(avs, AVS_SIZE);
+
+ seq_printf(s, "VBB_RET : 0x%2x\n", avs[AVS_VBB_RET]);
+ seq_printf(s, "VBB_MAX_OPP : 0x%2x\n", avs[AVS_VBB_MAX_OPP]);
+ seq_printf(s, "VBB_100_OPP : 0x%2x\n", avs[AVS_VBB_100_OPP]);
+ seq_printf(s, "VBB_50_OPP : 0x%2x\n", avs[AVS_VBB_50_OPP]);
+ seq_printf(s, "VARM_MAX_OPP : 0x%2x\n", avs[AVS_VARM_MAX_OPP]);
+ seq_printf(s, "VARM_100_OPP : 0x%2x\n", avs[AVS_VARM_100_OPP]);
+ seq_printf(s, "VARM_50_OPP : 0x%2x\n", avs[AVS_VARM_50_OPP]);
+ seq_printf(s, "VARM_RET : 0x%2x\n", avs[AVS_VARM_RET]);
+ seq_printf(s, "VAPE_100_OPP : 0x%2x\n", avs[AVS_VAPE_100_OPP]);
+ seq_printf(s, "VAPE_50_OPP : 0x%2x\n", avs[AVS_VAPE_50_OPP]);
+ seq_printf(s, "VMOD_100_OPP : 0x%2x\n", avs[AVS_VMOD_100_OPP]);
+ seq_printf(s, "VMOD_50_OPP : 0x%2x\n", avs[AVS_VMOD_50_OPP]);
+ seq_printf(s, "VSAFE : 0x%2x\n", avs[AVS_VSAFE]);
+ } else {
+ seq_printf(s, "Only u8500 supported.\n");
+ }
+
+ return 0;
}
-static int ddr_opp_open_file(struct inode *inode, struct file *file)
+static int opp_open_file(struct inode *inode, struct file *file)
{
- return single_open(file, ddr_opp_read, inode->i_private);
+ return single_open(file, opp_read, inode->i_private);
}
static int ape_stats_open_file(struct inode *inode, struct file *file)
@@ -408,6 +388,11 @@ static int ddr_stats_open_file(struct inode *inode, struct file *file)
return single_open(file, ddr_stats_print, inode->i_private);
}
+static int arm_stats_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, arm_stats_print, inode->i_private);
+}
+
static int cpufreq_delay_open_file(struct inode *inode, struct file *file)
{
return single_open(file, cpufreq_delay_read, inode->i_private);
@@ -418,26 +403,14 @@ static int ape_voltage_open_file(struct inode *inode, struct file *file)
return single_open(file, ape_voltage_read, inode->i_private);
}
-static const struct file_operations arm_opp_fops = {
- .open = arm_opp_open_file,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
-
-static const struct file_operations ape_opp_fops = {
- .open = ape_opp_open_file,
- .write = ape_opp_write,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .owner = THIS_MODULE,
-};
+static int avs_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, avs_read, inode->i_private);
+}
-static const struct file_operations ddr_opp_fops = {
- .open = ddr_opp_open_file,
- .write = ddr_opp_write,
+static const struct file_operations opp_fops = {
+ .open = opp_open_file,
+ .write = opp_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
@@ -462,6 +435,15 @@ static const struct file_operations ddr_stats_fops = {
.owner = THIS_MODULE,
};
+static const struct file_operations arm_stats_fops = {
+ .open = arm_stats_open_file,
+ .write = arm_stats_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static const struct file_operations cpufreq_delay_fops = {
.open = cpufreq_delay_open_file,
.write = cpufreq_delay_write,
@@ -480,10 +462,18 @@ static const struct file_operations ape_voltage_fops = {
.owner = THIS_MODULE,
};
+static const struct file_operations avs_fops = {
+ .open = avs_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static int setup_debugfs(void)
{
struct dentry *dir;
- struct dentry *file = NULL;
+ struct dentry *file;
dir = debugfs_create_dir("prcmu", NULL);
if (IS_ERR_OR_NULL(dir))
@@ -499,18 +489,26 @@ static int setup_debugfs(void)
if (IS_ERR_OR_NULL(file))
goto fail;
+ file = debugfs_create_file("arm_stats", (S_IRUGO | S_IWUGO),
+ dir, NULL, &arm_stats_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
file = debugfs_create_file("ape_opp", (S_IRUGO),
- dir, NULL, &ape_opp_fops);
+ dir, (void *)&ape_sh,
+ &opp_fops);
if (IS_ERR_OR_NULL(file))
goto fail;
file = debugfs_create_file("ddr_opp", (S_IRUGO),
- dir, NULL, &ddr_opp_fops);
+ dir, (void *)&ddr_sh,
+ &opp_fops);
if (IS_ERR_OR_NULL(file))
goto fail;
file = debugfs_create_file("arm_opp", (S_IRUGO),
- dir, NULL, &arm_opp_fops);
+ dir, (void *)&arm_sh,
+ &opp_fops);
if (IS_ERR_OR_NULL(file))
goto fail;
@@ -524,9 +522,15 @@ static int setup_debugfs(void)
if (IS_ERR_OR_NULL(file))
goto fail;
+ file = debugfs_create_file("avs",
+ (S_IRUGO),
+ dir, NULL, &avs_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
return 0;
fail:
- if ((file == NULL) && (dir != NULL))
+ if (!IS_ERR_OR_NULL(dir))
debugfs_remove_recursive(dir);
pr_err("prcmu debug: debugfs entry failed\n");
@@ -535,22 +539,12 @@ fail:
int prcmu_debug_init(void)
{
- ape_sh = kzalloc(sizeof(struct ape_state_history), GFP_KERNEL);
- if (ape_sh == NULL) {
- pr_err("prcmu debug: kzalloc failed\n");
- return -ENOMEM;
- }
-
- ddr_sh = kzalloc(sizeof(struct ddr_state_history), GFP_KERNEL);
- if (ddr_sh == NULL) {
- pr_err("prcmu debug: kzalloc failed\n");
- return -ENOMEM;
- }
-
- spin_lock_init(&ape_sh->lock);
- spin_lock_init(&ddr_sh->lock);
- ape_sh->start = ktime_get();
- ddr_sh->start = ktime_get();
+ spin_lock_init(&ape_sh.lock);
+ spin_lock_init(&ddr_sh.lock);
+ spin_lock_init(&arm_sh.lock);
+ ape_sh.start = ktime_get();
+ ddr_sh.start = ktime_get();
+ arm_sh.start = ktime_get();
setup_debugfs();
return 0;
}
diff --git a/arch/arm/mach-ux500/prcmu-debug.h b/arch/arm/mach-ux500/prcmu-debug.h
index 79e03e5b208..7346c60ad8e 100644
--- a/arch/arm/mach-ux500/prcmu-debug.h
+++ b/arch/arm/mach-ux500/prcmu-debug.h
@@ -12,6 +12,7 @@
#define PRCMU_DEBUG_H
#ifdef CONFIG_UX500_PRCMU_DEBUG
+void prcmu_debug_arm_opp_log(u8 opp);
void prcmu_debug_ape_opp_log(u8 opp);
void prcmu_debug_ddr_opp_log(u8 opp);
int prcmu_debug_init(void);
diff --git a/arch/arm/mach-ux500/prcmu-qos-power.c b/arch/arm/mach-ux500/prcmu-qos-power.c
index 082393bd7eb..095705e03a3 100644
--- a/arch/arm/mach-ux500/prcmu-qos-power.c
+++ b/arch/arm/mach-ux500/prcmu-qos-power.c
@@ -90,10 +90,27 @@ static struct prcmu_qos_object ddr_opp_qos = {
.comparitor = max_compare
};
+static struct prcmu_qos_object arm_opp_qos = {
+ .requirements = {
+ LIST_HEAD_INIT(arm_opp_qos.requirements.list)
+ },
+ /*
+ * No notifier on ARM opp qos request, since this won't actually
+ * do anything, except changing limits for cpufreq
+ */
+ .name = "arm_opp",
+ /* Target value in % ARM OPP, note can be 125% */
+ .default_value = 25,
+ .force_value = 0,
+ .target_value = ATOMIC_INIT(0),
+ .comparitor = max_compare
+};
+
static struct prcmu_qos_object *prcmu_qos_array[] = {
&null_qos,
&ape_opp_qos,
- &ddr_opp_qos
+ &ddr_opp_qos,
+ &arm_opp_qos,
};
static DEFINE_MUTEX(prcmu_qos_mutex);
@@ -141,7 +158,7 @@ static void update_target(int target)
s32 extreme_value;
struct requirement_list *node;
unsigned long flags;
- int update = 0;
+ bool update = false;
u8 op;
mutex_lock(&prcmu_qos_mutex);
@@ -151,7 +168,7 @@ static void update_target(int target)
if (prcmu_qos_array[target]->force_value != 0) {
extreme_value = prcmu_qos_array[target]->force_value;
- update = 1;
+ update = true;
} else {
list_for_each_entry(node,
&prcmu_qos_array[target]->requirements.list,
@@ -161,7 +178,7 @@ static void update_target(int target)
}
if (atomic_read(&prcmu_qos_array[target]->target_value)
!= extreme_value) {
- update = 1;
+ update = true;
atomic_set(&prcmu_qos_array[target]->target_value,
extreme_value);
pr_debug("prcmu qos: new target for qos %d is %d\n",
@@ -172,53 +189,65 @@ static void update_target(int target)
}
spin_unlock_irqrestore(&prcmu_qos_lock, flags);
- if (update) {
+ if (!update)
+ goto unlock_and_return;
+
+ if (prcmu_qos_array[target]->notifiers)
blocking_notifier_call_chain(prcmu_qos_array[target]->notifiers,
- (unsigned long)extreme_value, NULL);
-
- if (target == PRCMU_QOS_DDR_OPP) {
- switch (extreme_value) {
- case 25:
- op = DDR_25_OPP;
- pr_debug("prcmu qos: set ddr opp to 25%%\n");
- break;
- case 50:
- op = DDR_50_OPP;
- pr_debug("prcmu qos: set ddr opp to 50%%\n");
- break;
- case 100:
- op = DDR_100_OPP;
- pr_debug("prcmu qos: set ddr opp to 100%%\n");
- break;
- default:
- pr_err("prcmu qos: Incorrect ddr target value (%d)",
- extreme_value);
- goto unlock_and_return;
- }
- prcmu_debug_ddr_opp_log(op);
- prcmu_set_ddr_opp(op);
- } else {
- switch (extreme_value) {
- case 50:
- op = APE_50_OPP;
- pr_debug("prcmu qos: set ape opp to 50%%\n");
- break;
- case 100:
- op = APE_100_OPP;
- pr_debug("prcmu qos: set ape opp to 100%%\n");
- break;
- default:
- pr_err("prcmu qos: Incorrect ape target value (%d)",
- extreme_value);
- goto unlock_and_return;
- }
-
- if (!ape_opp_forced_to_50_partly_25)
- (void)prcmu_set_ape_opp(op);
-
- prcmu_debug_ape_opp_log(op);
+ (unsigned long)extreme_value,
+ NULL);
+
+ switch (target) {
+ case PRCMU_QOS_DDR_OPP:
+ switch (extreme_value) {
+ case 25:
+ op = DDR_25_OPP;
+ pr_debug("prcmu qos: set ddr opp to 25%%\n");
+ break;
+ case 50:
+ op = DDR_50_OPP;
+ pr_debug("prcmu qos: set ddr opp to 50%%\n");
+ break;
+ case 100:
+ op = DDR_100_OPP;
+ pr_debug("prcmu qos: set ddr opp to 100%%\n");
+ break;
+ default:
+ pr_err("prcmu qos: Incorrect ddr target value (%d)",
+ extreme_value);
+ goto unlock_and_return;
}
+ prcmu_debug_ddr_opp_log(op);
+ prcmu_set_ddr_opp(op);
+ break;
+ case PRCMU_QOS_APE_OPP:
+ switch (extreme_value) {
+ case 50:
+ op = APE_50_OPP;
+ pr_debug("prcmu qos: set ape opp to 50%%\n");
+ break;
+ case 100:
+ op = APE_100_OPP;
+ pr_debug("prcmu qos: set ape opp to 100%%\n");
+ break;
+ default:
+ pr_err("prcmu qos: Incorrect ape target value (%d)",
+ extreme_value);
+ goto unlock_and_return;
+ }
+
+ if (!ape_opp_forced_to_50_partly_25)
+ (void)prcmu_set_ape_opp(op);
+ prcmu_debug_ape_opp_log(op);
+ break;
+ case PRCMU_QOS_ARM_OPP:
+ /* TODO: Shall force_value != 0 actually set arm opp? */
+ break;
+ default:
+ pr_err("prcmu qos: Incorrect target\n");
+ break;
}
+
unlock_and_return:
mutex_unlock(&prcmu_qos_mutex);
}
@@ -393,9 +422,10 @@ EXPORT_SYMBOL_GPL(prcmu_qos_remove_requirement);
*/
int prcmu_qos_add_notifier(int prcmu_qos_class, struct notifier_block *notifier)
{
- int retval;
+ int retval = -EINVAL;
- retval = blocking_notifier_chain_register(
+ if (prcmu_qos_array[prcmu_qos_class]->notifiers)
+ retval = blocking_notifier_chain_register(
prcmu_qos_array[prcmu_qos_class]->notifiers, notifier);
return retval;
@@ -413,9 +443,9 @@ EXPORT_SYMBOL_GPL(prcmu_qos_add_notifier);
int prcmu_qos_remove_notifier(int prcmu_qos_class,
struct notifier_block *notifier)
{
- int retval;
-
- retval = blocking_notifier_chain_unregister(
+ int retval = -EINVAL;
+ if (prcmu_qos_array[prcmu_qos_class]->notifiers)
+ retval = blocking_notifier_chain_unregister(
prcmu_qos_array[prcmu_qos_class]->notifiers, notifier);
return retval;
diff --git a/arch/arm/mach-ux500/prcmu-regs-db5500.h b/arch/arm/mach-ux500/prcmu-regs-db5500.h
new file mode 100644
index 00000000000..9e76509b011
--- /dev/null
+++ b/arch/arm/mach-ux500/prcmu-regs-db5500.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __MACH_PRCMU_REGS_DB5500_H
+#define __MACH_PRCMU_REGS_DB5500_H
+
+#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+
+#define PRCM_TCR 0x1C8
+#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
+#define PRCM_TCR_STOP_TIMERS BIT(16)
+#define PRCM_TCR_DOZE_MODE BIT(17)
+
+/* PRCMU HW semaphore */
+#define PRCM_SEM 0x400
+#define PRCM_SEM_PRCM_SEM BIT(0)
+
+#define DB5500_PRCM_SVACLK_MGT 0x008
+#define DB5500_PRCM_SIACLK_MGT 0x00C
+#define DB5500_PRCM_SGACLK_MGT 0x014
+#define DB5500_PRCM_UARTCLK_MGT 0x018
+#define DB5500_PRCM_MSP02CLK_MGT 0x01C
+#define DB5500_PRCM_I2CCLK_MGT 0x020
+#define DB5500_PRCM_SDMMCCLK_MGT 0x024
+#define DB5500_PRCM_PER1CLK_MGT 0x02C
+#define DB5500_PRCM_PER2CLK_MGT 0x030
+#define DB5500_PRCM_PER3CLK_MGT 0x034
+#define DB5500_PRCM_PER5CLK_MGT 0x038
+#define DB5500_PRCM_PER6CLK_MGT 0x03C
+#define DB5500_PRCM_IRDACLK_MGT 0x040
+#define DB5500_PRCM_PWMCLK_MGT 0x044
+#define DB5500_PRCM_SPARE1CLK_MGT 0x048
+#define DB5500_PRCM_IRRCCLK_MGT 0x04C
+#define DB5500_PRCM_HDMICLK_MGT 0x058
+#define DB5500_PRCM_APEATCLK_MGT 0x05C
+#define DB5500_PRCM_APETRACECLK_MGT 0x060
+#define DB5500_PRCM_MCDECLK_MGT 0x064
+#define DB5500_PRCM_DSIALTCLK_MGT 0x06C
+#define DB5500_PRCM_DMACLK_MGT 0x074
+#define DB5500_PRCM_B2R2CLK_MGT 0x078
+#define DB5500_PRCM_TVCLK_MGT 0x07C
+#define DB5500_PRCM_RNGCLK_MGT 0x284
+
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN BIT(8)
+
+#define PRCM_ARM_IT1_CLEAR 0x48C
+#define PRCM_ARM_IT1_VAL 0x494
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL 0x0FC
+#define PRCM_MBOX_CPU_SET 0x100
+
+/* System reset register */
+#define PRCM_APE_SOFTRST 0x228
+
+/* PRCMU clock/PLL/reset registers */
+#define PRCM_PLLDSI_FREQ 0x500
+#define PRCM_PLLDSI_ENABLE 0x504
+#define PRCM_PLLDSI_LOCKP 0x508
+#define PRCM_DSI_PLLOUT_SEL 0x530
+#define PRCM_DSITVCLK_DIV 0x52C
+#define PRCM_APE_RESETN_SET 0x1E4
+#define PRCM_APE_RESETN_CLR 0x1E8
+
+/* CLKOUTx SEL0 settings */
+#define CLKOUT_SEL0_REF_CLK 0x01 /* 0b 0001 */
+#define CLKOUT_SEL0_RTC_CLK0 0x02 /* 0b 0010 */
+#define CLKOUT_SEL0_ULP_CLK 0x04 /* 0b 0100 */
+#define CLKOUT_SEL0_SEL_CLK 0x08 /* 0b 1000 */
+
+/* CLKOUTx SEL settings */
+#define CLKOUT_SEL_STATIC0 0x0001 /* 0b 00 0000 0001 */
+#define CLKOUT_SEL_REFCLK 0x0002 /* 0b 00 0000 0010 */
+#define CLKOUT_SEL_ULPCLK 0x0004 /* 0b 00 0000 0100 */
+#define CLKOUT_SEL_ARMCLK 0x0008 /* 0b 00 0000 1000 */
+#define CLKOUT_SEL_SYSACC0CLK 0x0010 /* 0b 00 0001 0000 */
+#define CLKOUT_SEL_SOC0PLLCLK 0x0020 /* 0b 00 0010 0000 */
+#define CLKOUT_SEL_SOC1PLLCLK 0x0040 /* 0b 00 0100 0000 */
+#define CLKOUT_SEL_DDRPLLCLK 0x0080 /* 0b 00 1000 0000 */
+#define CLKOUT_SEL_TVCLK 0x0100 /* 0b 01 0000 0000 */
+#define CLKOUT_SEL_IRDACLK 0x0200 /* 0b 10 0000 0000 */
+
+/* CLKOUTx dividers */
+#define CLKOUT_DIV_2 0x00 /* 0b 000 */
+#define CLKOUT_DIV_4 0x01 /* 0b 001 */
+#define CLKOUT_DIV_8 0x02 /* 0b 010 */
+#define CLKOUT_DIV_16 0x03 /* 0b 011 */
+#define CLKOUT_DIV_32 0x04 /* 0b 100 */
+#define CLKOUT_DIV_64 0x05 /* 0b 101 */
+/* Values 0x06 and 0x07 will also set the CLKOUTx divider to 64. */
+
+/* PRCM_CLKOCR CLKOUTx Control registers */
+#define PRCM_CLKOCR 0x1CC
+#define PRCM_CLKOCR_CLKOUT0_SEL0_SHIFT 0
+#define PRCM_CLKOCR_CLKOUT0_SEL0_MASK BITS(0, 3)
+#define PRCM_CLKOCR_CLKOUT0_SEL_SHIFT 4
+#define PRCM_CLKOCR_CLKOUT0_SEL_MASK BITS(4, 13)
+#define PRCM_CLKOCR_CLKOUT1_SEL0_SHIFT 16
+#define PRCM_CLKOCR_CLKOUT1_SEL0_MASK BITS(16, 19)
+#define PRCM_CLKOCR_CLKOUT1_SEL_SHIFT 20
+#define PRCM_CLKOCR_CLKOUT1_SEL_MASK BITS(20, 29)
+
+/* PRCM_CLKODIV CLKOUTx Dividers */
+#define PRCM_CLKODIV 0x188
+#define PRCM_CLKODIV_CLKOUT0_DIV_SHIFT 0
+#define PRCM_CLKODIV_CLKOUT0_DIV_MASK BITS(0, 2)
+#define PRCM_CLKODIV_CLKOUT1_DIV_SHIFT 16
+#define PRCM_CLKODIV_CLKOUT1_DIV_MASK BITS(16, 18)
+
+#define PRCM_MMIP_LS_CLAMP_SET 0x420
+#define PRCM_MMIP_LS_CLAMP_CLR 0x424
+
+/* Miscellaneous unit registers */
+#define PRCM_DSI_SW_RESET 0x324
+#define PRCM_RESOUTN_SET_OFFSET 0x214
+#define PRCM_RESOUTN_CLR_OFFSET 0x218
+
+#endif
diff --git a/arch/arm/mach-ux500/prcmu-regs-db8500.h b/arch/arm/mach-ux500/prcmu-regs-db8500.h
new file mode 100644
index 00000000000..60faa143075
--- /dev/null
+++ b/arch/arm/mach-ux500/prcmu-regs-db8500.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009 ST-Ericsson SA
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+#ifndef __MACH_PRCMU_REGS_H
+#define __MACH_PRCMU_REGS_H
+
+#include <linux/bitops.h>
+#include <mach/hardware.h>
+
+#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+
+#define PRCM_ARM_PLLDIVPS 0x118
+#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE BITS(0, 5)
+#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xF
+
+#define PRCM_PLLARM_LOCKP 0x0A8
+#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 BIT(1)
+
+#define PRCM_ARM_CHGCLKREQ 0x114
+#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0)
+
+#define PRCM_PLLARM_ENABLE 0x98
+#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE BIT(0)
+#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON BIT(8)
+
+#define PRCM_ARMCLKFIX_MGT 0x0
+#define PRCM_A9PL_FORCE_CLKEN 0x19C
+#define PRCM_A9_RESETN_CLR 0x1f4
+#define PRCM_A9_RESETN_SET 0x1f0
+#define PRCM_ARM_LS_CLAMP 0x30C
+#define PRCM_SRAM_A9 0x308
+
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0)
+#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1)
+
+/* ARM WFI Standby signal register */
+#define PRCM_ARM_WFI_STANDBY 0x130
+#define PRCM_IOCR 0x310
+#define PRCM_IOCR_IOFORCE BIT(0)
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL 0x0FC
+#define PRCM_MBOX_CPU_SET 0x100
+
+/* Dual A9 core interrupt management unit registers */
+#define PRCM_A9_MASK_REQ 0x328
+#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ BIT(0)
+
+#define PRCM_A9_MASK_ACK 0x32C
+#define PRCM_ARMITMSK31TO0 0x11C
+#define PRCM_ARMITMSK63TO32 0x120
+#define PRCM_ARMITMSK95TO64 0x124
+#define PRCM_ARMITMSK127TO96 0x128
+#define PRCM_POWER_STATE_VAL 0x25C
+#define PRCM_ARMITVAL31TO0 0x260
+#define PRCM_ARMITVAL63TO32 0x264
+#define PRCM_ARMITVAL95TO64 0x268
+#define PRCM_ARMITVAL127TO96 0x26C
+
+#define PRCM_HOSTACCESS_REQ 0x334
+#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ BIT(0)
+
+#define PRCM_ARM_IT1_CLR 0x48C
+#define PRCM_ARM_IT1_VAL 0x494
+
+#define PRCM_MOD_AWAKE_STATUS 0x4A0
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1)
+#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2)
+
+#define PRCM_ITSTATUS0 0x148
+#define PRCM_ITSTATUS1 0x150
+#define PRCM_ITSTATUS2 0x158
+#define PRCM_ITSTATUS3 0x160
+#define PRCM_ITSTATUS4 0x168
+#define PRCM_ITSTATUS5 0x484
+#define PRCM_ITCLEAR5 0x488
+#define PRCM_ARMIT_MASKXP70_IT 0x1018
+
+/* System reset register */
+#define PRCM_APE_SOFTRST 0x228
+
+/* Level shifter and clamp control registers */
+#define PRCM_MMIP_LS_CLAMP_SET 0x420
+#define PRCM_MMIP_LS_CLAMP_CLR 0x424
+
+/* PRCMU HW semaphore */
+#define PRCM_SEM 0x400
+#define PRCM_SEM_PRCM_SEM BIT(0)
+
+/* PRCMU clock/PLL/reset registers */
+#define PRCM_PLLDSI_FREQ 0x500
+#define PRCM_PLLDSI_ENABLE 0x504
+#define PRCM_PLLDSI_LOCKP 0x508
+#define PRCM_DSI_PLLOUT_SEL 0x530
+#define PRCM_DSITVCLK_DIV 0x52C
+#define PRCM_APE_RESETN_SET 0x1E4
+#define PRCM_APE_RESETN_CLR 0x1E8
+
+#define PRCM_TCR 0x1C8
+#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
+#define PRCM_TCR_STOP_TIMERS BIT(16)
+#define PRCM_TCR_DOZE_MODE BIT(17)
+
+#define PRCM_CLKOCR 0x1CC
+#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
+#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
+#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
+#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
+#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
+#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
+#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
+#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
+#define PRCM_CLKOCR_CLK1TYPE BIT(28)
+
+#define PRCM_ACLK_MGT 0x004
+#define PRCM_SGACLK_MGT 0x014
+#define PRCM_UARTCLK_MGT 0x018
+#define PRCM_MSP02CLK_MGT 0x01C
+#define PRCM_MSP1CLK_MGT 0x288
+#define PRCM_I2CCLK_MGT 0x020
+#define PRCM_SDMMCCLK_MGT 0x024
+#define PRCM_SLIMCLK_MGT 0x028
+#define PRCM_PER1CLK_MGT 0x02C
+#define PRCM_PER2CLK_MGT 0x030
+#define PRCM_PER3CLK_MGT 0x034
+#define PRCM_PER5CLK_MGT 0x038
+#define PRCM_PER6CLK_MGT 0x03C
+#define PRCM_PER7CLK_MGT 0x040
+#define PRCM_LCDCLK_MGT 0x044
+#define PRCM_BMLCLK_MGT 0x04C
+#define PRCM_HSITXCLK_MGT 0x050
+#define PRCM_HSIRXCLK_MGT 0x054
+#define PRCM_HDMICLK_MGT 0x058
+#define PRCM_APEATCLK_MGT 0x05C
+#define PRCM_APETRACECLK_MGT 0x060
+#define PRCM_MCDECLK_MGT 0x064
+#define PRCM_IPI2CCLK_MGT 0x068
+#define PRCM_DSIALTCLK_MGT 0x06C
+#define PRCM_DMACLK_MGT 0x074
+#define PRCM_B2R2CLK_MGT 0x078
+#define PRCM_TVCLK_MGT 0x07C
+#define PRCM_UNIPROCLK_MGT 0x278
+#define PRCM_SSPCLK_MGT 0x280
+#define PRCM_RNGCLK_MGT 0x284
+#define PRCM_UICCCLK_MGT 0x27C
+
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN BIT(8)
+
+/* ePOD and memory power signal control registers */
+#define PRCM_EPOD_C_SET 0x410
+#define PRCM_SRAM_LS_SLEEP 0x304
+
+/* Debug power control unit registers */
+#define PRCM_POWER_STATE_SET 0x254
+
+/* Miscellaneous unit registers */
+#define PRCM_DSI_SW_RESET 0x324
+#define PRCM_GPIOCR 0x138
+
+/* GPIOCR register */
+#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
+
+#define PRCM_DDR_SUBSYS_APE_MINBW 0x438
+
+#define PRCM_CGATING_BYPASS 0x134
+#define PRCM_CGATING_BYPASS_ICN2 BIT(6)
+
+#endif /* __MACH_PRCMU__REGS_H */
diff --git a/arch/arm/mach-ux500/product.c b/arch/arm/mach-ux500/product.c
new file mode 100644
index 00000000000..c601a2e00ae
--- /dev/null
+++ b/arch/arm/mach-ux500/product.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jens Wiklander <jens.wiklander@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tee.h>
+#include <linux/module.h>
+#include <mach/hardware.h>
+
+#define STATIC_TEE_TA_START_LOW 0xBC765EDE
+#define STATIC_TEE_TA_START_MID 0x6724
+#define STATIC_TEE_TA_START_HIGH 0x11DF
+#define STATIC_TEE_TA_START_CLOCKSEQ \
+ {0x8E, 0x12, 0xEC, 0xDB, 0xDF, 0xD7, 0x20, 0x85}
+
+static struct tee_product_config product_config;
+
+bool ux500_jtag_enabled(void)
+{
+ return (product_config.rt_flags & TEE_RT_FLAGS_JTAG_ENABLED) ==
+ TEE_RT_FLAGS_JTAG_ENABLED;
+}
+
+static int __init product_detect(void)
+{
+ int err;
+ int origin_err;
+ struct tee_operation operation = { { { 0 } } };
+ struct tee_context context;
+ struct tee_session session;
+
+ /* Selects trustzone application needed for the job. */
+ struct tee_uuid static_uuid = {
+ STATIC_TEE_TA_START_LOW,
+ STATIC_TEE_TA_START_MID,
+ STATIC_TEE_TA_START_HIGH,
+ STATIC_TEE_TA_START_CLOCKSEQ,
+ };
+
+ if (cpu_is_u5500())
+ return -ENODEV;
+
+ err = teec_initialize_context(NULL, &context);
+ if (err) {
+ pr_err("ux500-product: unable to initialize tee context,"
+ " err = %d\n", err);
+ err = -EINVAL;
+ goto error0;
+ }
+
+ err = teec_open_session(&context, &session, &static_uuid,
+ TEEC_LOGIN_PUBLIC, NULL, NULL, &origin_err);
+ if (err) {
+ pr_err("ux500-product: unable to open tee session,"
+ " tee error = %d, origin error = %d\n",
+ err, origin_err);
+ err = -EINVAL;
+ goto error1;
+ }
+
+ operation.shm[0].buffer = &product_config;
+ operation.shm[0].size = sizeof(product_config);
+ operation.shm[0].flags = TEEC_MEM_OUTPUT;
+ operation.flags = TEEC_MEMREF_0_USED;
+
+ err = teec_invoke_command(&session,
+ TEE_STA_GET_PRODUCT_CONFIG,
+ &operation, &origin_err);
+ if (err) {
+ pr_err("ux500-product: fetching product settings failed, err=%d",
+ err);
+ err = -EINVAL;
+ goto error1;
+ }
+
+ pr_info("ux500-product: JTAG is %s\n",
+ ux500_jtag_enabled()? "enabled" : "disabled");
+error1:
+ (void) teec_finalize_context(&context);
+error0:
+ return err;
+}
+device_initcall(product_detect);
+
diff --git a/arch/arm/mach-ux500/product.h b/arch/arm/mach-ux500/product.h
new file mode 100644
index 00000000000..502eff4df14
--- /dev/null
+++ b/arch/arm/mach-ux500/product.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jens Wiklander <jens.wiklander@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#ifndef UX500_PRODUCT_H
+#define UX500_PRODUCT_H
+
+#ifdef CONFIG_TEE_UX500
+
+bool ux500_jtag_enabled(void);
+
+#else
+
+static inline bool ux500_jtag_enabled(void)
+{
+ return true;
+}
+
+#endif
+#endif
diff --git a/arch/arm/mach-ux500/regulator-u5500.c b/arch/arm/mach-ux500/regulator-u5500.c
new file mode 100644
index 00000000000..4aca065d6e8
--- /dev/null
+++ b/arch/arm/mach-ux500/regulator-u5500.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <mach/prcmu.h>
+
+#include "regulator-ux500.h"
+#include "regulator-u5500.h"
+
+#define U5500_REGULATOR_SWITCH(_name, reg) \
+ [U5500_REGULATOR_SWITCH_##reg] = { \
+ .desc = { \
+ .name = _name, \
+ .id = U5500_REGULATOR_SWITCH_##reg, \
+ .ops = &ux500_regulator_switch_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ .epod_id = DB5500_EPOD_ID_##reg, \
+}
+
+static struct u8500_regulator_info
+u5500_regulator_info[U5500_NUM_REGULATORS] = {
+ [U5500_REGULATOR_VAPE] = {
+ .desc = {
+ .name = "u5500-vape",
+ .id = U5500_REGULATOR_VAPE,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ U5500_REGULATOR_SWITCH("u5500-sga", SGA),
+ U5500_REGULATOR_SWITCH("u5500-hva", HVA),
+ U5500_REGULATOR_SWITCH("u5500-sia", SIA),
+ U5500_REGULATOR_SWITCH("u5500-disp", DISP),
+ U5500_REGULATOR_SWITCH("u5500-esram12", ESRAM12),
+};
+
+static int __devinit u5500_regulator_probe(struct platform_device *pdev)
+{
+ return ux500_regulator_probe(pdev, u5500_regulator_info,
+ ARRAY_SIZE(u5500_regulator_info));
+}
+
+static int __devexit u5500_regulator_remove(struct platform_device *pdev)
+{
+ return ux500_regulator_remove(pdev, u5500_regulator_info,
+ ARRAY_SIZE(u5500_regulator_info));
+}
+
+static struct platform_driver u5500_regulator_driver = {
+ .driver = {
+ .name = "u5500-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = u5500_regulator_probe,
+ .remove = __devexit_p(u5500_regulator_remove),
+};
+
+static int __init u5500_regulator_init(void)
+{
+ return platform_driver_register(&u5500_regulator_driver);
+}
+
+static void __exit u5500_regulator_exit(void)
+{
+ platform_driver_unregister(&u5500_regulator_driver);
+}
+
+arch_initcall(u5500_regulator_init);
+module_exit(u5500_regulator_exit);
+
+MODULE_DESCRIPTION("U5500 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-ux500/regulator-u5500.h b/arch/arm/mach-ux500/regulator-u5500.h
new file mode 100644
index 00000000000..cf3eeed9366
--- /dev/null
+++ b/arch/arm/mach-ux500/regulator-u5500.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __REGULATOR_U5500_H
+#define __REGULATOR_U5500_H
+
+enum u5500_regulator_id {
+ U5500_REGULATOR_VAPE,
+ U5500_REGULATOR_SWITCH_SGA,
+ U5500_REGULATOR_SWITCH_HVA,
+ U5500_REGULATOR_SWITCH_SIA,
+ U5500_REGULATOR_SWITCH_DISP,
+ U5500_REGULATOR_SWITCH_ESRAM12,
+ U5500_NUM_REGULATORS
+};
+
+#endif
diff --git a/arch/arm/mach-ux500/regulator-u8500.c b/arch/arm/mach-ux500/regulator-u8500.c
new file mode 100644
index 00000000000..2244f084822
--- /dev/null
+++ b/arch/arm/mach-ux500/regulator-u8500.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <mach/prcmu.h>
+
+#include "regulator-ux500.h"
+#include "regulator-u8500.h"
+
+static struct u8500_regulator_info
+ u8500_regulator_info[U8500_NUM_REGULATORS] = {
+ [U8500_REGULATOR_VAPE] = {
+ .desc = {
+ .name = "u8500-vape",
+ .id = U8500_REGULATOR_VAPE,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [U8500_REGULATOR_VARM] = {
+ .desc = {
+ .name = "u8500-varm",
+ .id = U8500_REGULATOR_VARM,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [U8500_REGULATOR_VMODEM] = {
+ .desc = {
+ .name = "u8500-vmodem",
+ .id = U8500_REGULATOR_VMODEM,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [U8500_REGULATOR_VPLL] = {
+ .desc = {
+ .name = "u8500-vpll",
+ .id = U8500_REGULATOR_VPLL,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [U8500_REGULATOR_VSMPS1] = {
+ .desc = {
+ .name = "u8500-vsmps1",
+ .id = U8500_REGULATOR_VSMPS1,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [U8500_REGULATOR_VSMPS2] = {
+ .desc = {
+ .name = "u8500-vsmps2",
+ .id = U8500_REGULATOR_VSMPS2,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .exclude_from_power_state = true,
+ },
+ [U8500_REGULATOR_VSMPS3] = {
+ .desc = {
+ .name = "u8500-vsmps3",
+ .id = U8500_REGULATOR_VSMPS3,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [U8500_REGULATOR_VRF1] = {
+ .desc = {
+ .name = "u8500-vrf1",
+ .id = U8500_REGULATOR_VRF1,
+ .ops = &ux500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ [U8500_REGULATOR_SWITCH_SVAMMDSP] = {
+ .desc = {
+ .name = "u8500-sva-mmdsp",
+ .id = U8500_REGULATOR_SWITCH_SVAMMDSP,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_SVAMMDSP,
+ },
+ [U8500_REGULATOR_SWITCH_SVAMMDSPRET] = {
+ .desc = {
+ .name = "u8500-sva-mmdsp-ret",
+ .id = U8500_REGULATOR_SWITCH_SVAMMDSPRET,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_SVAMMDSP,
+ .is_ramret = true,
+ },
+ [U8500_REGULATOR_SWITCH_SVAPIPE] = {
+ .desc = {
+ .name = "u8500-sva-pipe",
+ .id = U8500_REGULATOR_SWITCH_SVAPIPE,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_SVAPIPE,
+ },
+ [U8500_REGULATOR_SWITCH_SIAMMDSP] = {
+ .desc = {
+ .name = "u8500-sia-mmdsp",
+ .id = U8500_REGULATOR_SWITCH_SIAMMDSP,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_SIAMMDSP,
+ },
+ [U8500_REGULATOR_SWITCH_SIAMMDSPRET] = {
+ .desc = {
+ .name = "u8500-sia-mmdsp-ret",
+ .id = U8500_REGULATOR_SWITCH_SIAMMDSPRET,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_SIAMMDSP,
+ .is_ramret = true,
+ },
+ [U8500_REGULATOR_SWITCH_SIAPIPE] = {
+ .desc = {
+ .name = "u8500-sia-pipe",
+ .id = U8500_REGULATOR_SWITCH_SIAPIPE,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_SIAPIPE,
+ },
+ [U8500_REGULATOR_SWITCH_SGA] = {
+ .desc = {
+ .name = "u8500-sga",
+ .id = U8500_REGULATOR_SWITCH_SGA,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_SGA,
+ },
+ [U8500_REGULATOR_SWITCH_B2R2_MCDE] = {
+ .desc = {
+ .name = "u8500-b2r2-mcde",
+ .id = U8500_REGULATOR_SWITCH_B2R2_MCDE,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_B2R2_MCDE,
+ },
+ [U8500_REGULATOR_SWITCH_ESRAM12] = {
+ .desc = {
+ .name = "u8500-esram12",
+ .id = U8500_REGULATOR_SWITCH_ESRAM12,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_ESRAM12,
+ .is_enabled = true,
+ },
+ [U8500_REGULATOR_SWITCH_ESRAM12RET] = {
+ .desc = {
+ .name = "u8500-esram12-ret",
+ .id = U8500_REGULATOR_SWITCH_ESRAM12RET,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_ESRAM12,
+ .is_ramret = true,
+ },
+ [U8500_REGULATOR_SWITCH_ESRAM34] = {
+ .desc = {
+ .name = "u8500-esram34",
+ .id = U8500_REGULATOR_SWITCH_ESRAM34,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_ESRAM34,
+ .is_enabled = true,
+ },
+ [U8500_REGULATOR_SWITCH_ESRAM34RET] = {
+ .desc = {
+ .name = "u8500-esram34-ret",
+ .id = U8500_REGULATOR_SWITCH_ESRAM34RET,
+ .ops = &ux500_regulator_switch_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .epod_id = EPOD_ID_ESRAM34,
+ .is_ramret = true,
+ },
+};
+
+static int __devinit u8500_regulator_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = ux500_regulator_probe(pdev, u8500_regulator_info,
+ ARRAY_SIZE(u8500_regulator_info));
+ if (!ret)
+ regulator_has_full_constraints();
+
+ return ret;
+}
+
+static int __devexit u8500_regulator_remove(struct platform_device *pdev)
+{
+ return ux500_regulator_remove(pdev, u8500_regulator_info,
+ ARRAY_SIZE(u8500_regulator_info));
+}
+
+static struct platform_driver u8500_regulator_driver = {
+ .driver = {
+ .name = "u8500-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = u8500_regulator_probe,
+ .remove = __devexit_p(u8500_regulator_remove),
+};
+
+static int __init u8500_regulator_init(void)
+{
+ return platform_driver_register(&u8500_regulator_driver);
+}
+
+static void __exit u8500_regulator_exit(void)
+{
+ platform_driver_unregister(&u8500_regulator_driver);
+}
+
+/* replaced subsys_initcall as regulators must be turned on early */
+arch_initcall(u8500_regulator_init);
+module_exit(u8500_regulator_exit);
+
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
+MODULE_DESCRIPTION("U8500 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-ux500/regulator-u8500.h b/arch/arm/mach-ux500/regulator-u8500.h
new file mode 100644
index 00000000000..01dfeeb9da7
--- /dev/null
+++ b/arch/arm/mach-ux500/regulator-u8500.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * Interface to platform specific regulators on U8500
+ */
+
+#ifndef __REGULATOR_H__
+#define __REGULATOR_H__
+
+/* Number of U8500 regulators and regulator enumeration */
+enum u8500_regulator_id {
+ U8500_REGULATOR_VAPE,
+ U8500_REGULATOR_VARM,
+ U8500_REGULATOR_VMODEM,
+ U8500_REGULATOR_VPLL,
+ U8500_REGULATOR_VSMPS1,
+ U8500_REGULATOR_VSMPS2,
+ U8500_REGULATOR_VSMPS3,
+ U8500_REGULATOR_VRF1,
+ U8500_REGULATOR_SWITCH_SVAMMDSP,
+ U8500_REGULATOR_SWITCH_SVAMMDSPRET,
+ U8500_REGULATOR_SWITCH_SVAPIPE,
+ U8500_REGULATOR_SWITCH_SIAMMDSP,
+ U8500_REGULATOR_SWITCH_SIAMMDSPRET,
+ U8500_REGULATOR_SWITCH_SIAPIPE,
+ U8500_REGULATOR_SWITCH_SGA,
+ U8500_REGULATOR_SWITCH_B2R2_MCDE,
+ U8500_REGULATOR_SWITCH_ESRAM12,
+ U8500_REGULATOR_SWITCH_ESRAM12RET,
+ U8500_REGULATOR_SWITCH_ESRAM34,
+ U8500_REGULATOR_SWITCH_ESRAM34RET,
+ U8500_NUM_REGULATORS
+};
+
+/*
+ * Exported interface for CPUIdle only. This function is called with all
+ * interrupts turned off.
+ */
+int power_state_active_is_enabled(void);
+
+#endif
+
diff --git a/arch/arm/mach-ux500/regulator-ux500.c b/arch/arm/mach-ux500/regulator-ux500.c
new file mode 100644
index 00000000000..638ff231d60
--- /dev/null
+++ b/arch/arm/mach-ux500/regulator-ux500.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * Platform specific regulators on U8500
+ *
+ * NOTE! The power domains in here will be updated once B2R2 and MCDE are
+ * converted to use the regulator API.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include "regulator-ux500.h"
+
+#include <mach/prcmu.h>
+
+/*
+ * power state reference count
+ */
+static int power_state_active_cnt; /* will initialize to zero */
+static DEFINE_SPINLOCK(power_state_active_lock);
+
+static void power_state_active_enable(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&power_state_active_lock, flags);
+ power_state_active_cnt++;
+ spin_unlock_irqrestore(&power_state_active_lock, flags);
+}
+
+static int power_state_active_disable(void)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&power_state_active_lock, flags);
+ if (power_state_active_cnt <= 0) {
+ pr_err("power state: unbalanced enable/disable calls\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ power_state_active_cnt--;
+out:
+ spin_unlock_irqrestore(&power_state_active_lock, flags);
+ return ret;
+}
+
+/*
+ * Exported interface for CPUIdle only. This function is called when interrupts
+ * are turned off. Hence, no locking.
+ */
+int power_state_active_is_enabled(void)
+{
+ return (power_state_active_cnt > 0);
+}
+
+struct ux500_regulator {
+ char *name;
+ void (*enable)(void);
+ int (*disable)(void);
+};
+
+/*
+ * Don't add any clients to this struct without checking with regulator
+ * responsible!
+ */
+static struct ux500_regulator ux500_atomic_regulators[] = {
+ {
+ .name = "dma40.0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "ssp0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "ssp1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi2",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi3",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "cryp1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "hash1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+};
+
+struct ux500_regulator *__must_check ux500_regulator_get(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ux500_atomic_regulators); i++) {
+ if (!strcmp(dev_name(dev), ux500_atomic_regulators[i].name))
+ return &ux500_atomic_regulators[i];
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_get);
+
+int ux500_regulator_atomic_enable(struct ux500_regulator *regulator)
+{
+ if (regulator) {
+ regulator->enable();
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_atomic_enable);
+
+int ux500_regulator_atomic_disable(struct ux500_regulator *regulator)
+{
+ if (regulator)
+ return regulator->disable();
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_atomic_disable);
+
+void ux500_regulator_put(struct ux500_regulator *regulator)
+{
+ /* Here for symetric reasons and for possible future use */
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_put);
+
+static int u8500_regulator_enable(struct regulator_dev *rdev)
+{
+ struct u8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-enable\n",
+ info->desc.name);
+
+ info->is_enabled = 1;
+ if (!info->exclude_from_power_state)
+ power_state_active_enable();
+
+ return 0;
+}
+
+static int u8500_regulator_disable(struct regulator_dev *rdev)
+{
+ struct u8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-disable\n",
+ info->desc.name);
+
+ info->is_enabled = 0;
+ if (!info->exclude_from_power_state)
+ ret = power_state_active_disable();
+
+ return ret;
+}
+
+static int u8500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct u8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-is_enabled (is_enabled):"
+ " %i\n", info->desc.name, info->is_enabled);
+
+ return info->is_enabled;
+}
+
+/* u8500 regulator operations */
+struct regulator_ops ux500_regulator_ops = {
+ .enable = u8500_regulator_enable,
+ .disable = u8500_regulator_disable,
+ .is_enabled = u8500_regulator_is_enabled,
+};
+
+/*
+ * EPOD control
+ */
+static bool epod_on[NUM_EPOD_ID];
+static bool epod_ramret[NUM_EPOD_ID];
+
+static int epod_id_to_index(u16 epod_id)
+{
+ if (cpu_is_u5500())
+ return epod_id - DB5500_EPOD_ID_BASE;
+ else
+ return epod_id;
+}
+
+static int enable_epod(u16 epod_id, bool ramret)
+{
+ int idx = epod_id_to_index(epod_id);
+ int ret;
+
+ if (ramret) {
+ if (!epod_on[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
+ if (ret < 0)
+ return ret;
+ }
+ epod_ramret[idx] = true;
+ } else {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_ON);
+ if (ret < 0)
+ return ret;
+ epod_on[idx] = true;
+ }
+
+ return 0;
+}
+
+static int disable_epod(u16 epod_id, bool ramret)
+{
+ int idx = epod_id_to_index(epod_id);
+ int ret;
+
+ if (ramret) {
+ if (!epod_on[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
+ if (ret < 0)
+ return ret;
+ }
+ epod_ramret[idx] = false;
+ } else {
+ if (epod_ramret[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
+ if (ret < 0)
+ return ret;
+ }
+ epod_on[idx] = false;
+ }
+
+ return 0;
+}
+
+/*
+ * Regulator switch
+ */
+static int u8500_regulator_switch_enable(struct regulator_dev *rdev)
+{
+ struct u8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-enable\n",
+ info->desc.name);
+
+ ret = enable_epod(info->epod_id, info->is_ramret);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "regulator-switch-%s-enable: prcmu call failed\n",
+ info->desc.name);
+ goto out;
+ }
+
+ info->is_enabled = 1;
+out:
+ return ret;
+}
+
+static int u8500_regulator_switch_disable(struct regulator_dev *rdev)
+{
+ struct u8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-disable\n",
+ info->desc.name);
+
+ ret = disable_epod(info->epod_id, info->is_ramret);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "regulator_switch-%s-disable: prcmu call failed\n",
+ info->desc.name);
+ goto out;
+ }
+
+ info->is_enabled = 0;
+out:
+ return ret;
+}
+
+static int u8500_regulator_switch_is_enabled(struct regulator_dev *rdev)
+{
+ struct u8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "regulator-switch-%s-is_enabled (is_enabled): %i\n",
+ info->desc.name, info->is_enabled);
+
+ return info->is_enabled;
+}
+
+struct regulator_ops ux500_regulator_switch_ops = {
+ .enable = u8500_regulator_switch_enable,
+ .disable = u8500_regulator_switch_disable,
+ .is_enabled = u8500_regulator_switch_is_enabled,
+};
+
+int __devinit
+ux500_regulator_probe(struct platform_device *pdev,
+ struct u8500_regulator_info *regulator_info,
+ int num_regulators)
+{
+ struct regulator_init_data **u8500_init_data =
+ dev_get_platdata(&pdev->dev);
+ int i, err;
+
+ /* register all regulators */
+ for (i = 0; i < num_regulators; i++) {
+ struct u8500_regulator_info *info;
+ struct regulator_init_data *init_data = u8500_init_data[i];
+
+ /* assign per-regulator data */
+ info = &regulator_info[i];
+ info->dev = &pdev->dev;
+
+ /* register with the regulator framework */
+ info->rdev = regulator_register(&info->desc, &pdev->dev,
+ init_data, info);
+ if (IS_ERR(info->rdev)) {
+ err = PTR_ERR(info->rdev);
+ dev_err(&pdev->dev, "failed to register %s: err %i\n",
+ info->desc.name, err);
+
+ /* if failing, unregister all earlier regulators */
+ i--;
+ while (i >= 0) {
+ info = &regulator_info[i];
+ regulator_unregister(info->rdev);
+ i--;
+ }
+ return err;
+ }
+
+ dev_vdbg(rdev_get_dev(info->rdev),
+ "regulator-%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+int __devexit
+ux500_regulator_remove(struct platform_device *pdev,
+ struct u8500_regulator_info *regulator_info,
+ int num_regulators)
+{
+ int i;
+
+ for (i = 0; i < num_regulators; i++) {
+ struct u8500_regulator_info *info;
+
+ info = &regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->rdev),
+ "regulator-%s-remove\n", info->desc.name);
+
+ regulator_unregister(info->rdev);
+ }
+
+ return 0;
+}
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index a194847baac..2629657f9ac 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -41,5 +41,5 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
##################################################################################d
# ARM SoC drivers
-obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
-obj-$(CONFIG_UX500_SOC_DB5500) += db5500-cpufreq.o
+#obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
+#obj-$(CONFIG_UX500_SOC_DB5500) += db5500-cpufreq.o
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a25c33b6942..2b87c825f83 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -584,7 +584,7 @@ config AB8500_CORE
config AB8500_I2C_CORE
bool "AB8500 register access via PRCMU I2C"
- depends on AB8500_CORE && MFD_DB8500_PRCMU
+ depends on AB8500_CORE
default y
help
This enables register access to the AB8500 chip via PRCMU I2C.
diff --git a/drivers/tee/tee_driver.c b/drivers/tee/tee_driver.c
index 73c62871ddc..5ae0cc9508b 100644
--- a/drivers/tee/tee_driver.c
+++ b/drivers/tee/tee_driver.c
@@ -636,7 +636,7 @@ static void __exit tee_exit(void)
misc_deregister(&tee_dev);
}
-module_init(tee_init);
+subsys_initcall(tee_init);
module_exit(tee_exit);
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/include/linux/tee.h b/include/linux/tee.h
index 4928e4dca1f..8b71224ac77 100644
--- a/include/linux/tee.h
+++ b/include/linux/tee.h
@@ -74,8 +74,44 @@
/*
* Exposed functions (command_id) in the static TA
*/
+#define TEE_STA_GET_PRODUCT_CONFIG 10
#define TEE_STA_SET_L2CC_PREFETCH_CTRL_REGISTER 11
+/* Flags indicating run-time environment */
+#define TEE_RT_FLAGS_NORMAL 0x00000000
+#define TEE_RT_FLAGS_MASK_ITP_PROD 0x00000001
+#define TEE_RT_FLAGS_MODEM_DEBUG 0x00000002
+#define TEE_RT_FLAGS_RNG_REG_PUBLIC 0x00000004
+#define TEE_RT_FLAGS_JTAG_ENABLED 0x00000008
+
+/*
+ * Product id numbers
+ */
+#define TEE_PRODUCT_ID_UNKNOWN 0
+#define TEE_PRODUCT_ID_8400 1
+#define TEE_PRODUCT_ID_8500 2
+#define TEE_PRODUCT_ID_9500 3
+#define TEE_PRODUCT_ID_5500 4
+#define TEE_PRODUCT_ID_7400 5
+#define TEE_PRODUCT_ID_8500C 6
+
+/* Flags indicating fuses */
+#define TEE_FUSE_FLAGS_MODEM_DISABLE 0x00000001
+
+/**
+ * struct tee_product_config - System configuration structure.
+ *
+ * @product_id: Product identification.
+ * @rt_flags: Runtime configuration flags.
+ * @fuse_flags: Fuse flags.
+ *
+ */
+struct tee_product_config {
+ uint32_t product_id;
+ uint32_t rt_flags;
+ uint32_t fuse_flags;
+};
+
/**
* struct tee_uuid - Structure that represent an uuid.
* @timeLow: The low field of the time stamp.