diff options
33 files changed, 4755 insertions, 707 deletions
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index 58626013aa3..54429d01595 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -12,9 +12,12 @@ menu "Ux500 SoC" config UX500_SOC_DB5500 bool "DB5500" + select MFD_DB5500_PRCMU config UX500_SOC_DB8500 bool "DB8500" + select MFD_DB8500_PRCMU + select REGULATOR_DB8500_PRCMU endmenu diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index b549a8fb423..1694916e682 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -5,7 +5,7 @@ obj-y := clock.o cpu.o devices.o devices-common.o \ id.o usb.o obj-$(CONFIG_UX500_SOC_DB5500) += cpu-db5500.o dma-db5500.o -obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o prcmu.o +obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o obj-$(CONFIG_MACH_U8500) += board-mop500.o board-mop500-sdi.o \ board-mop500-regulators.o \ board-mop500-uib.o board-mop500-stuib.o \ @@ -17,4 +17,4 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o obj-$(CONFIG_U5500_MODEM_IRQ) += modem-irq-db5500.o obj-$(CONFIG_U5500_MBOX) += mbox-db5500.o -obj-$(CONFIG_CPU_FREQ) += cpufreq.o + diff --git a/arch/arm/mach-ux500/cpu-db5500.c b/arch/arm/mach-ux500/cpu-db5500.c index c9dc2eff3cb..c01bc19e3c5 100644 --- a/arch/arm/mach-ux500/cpu-db5500.c +++ b/arch/arm/mach-ux500/cpu-db5500.c @@ -188,6 +188,8 @@ void __init u5500_map_io(void) ux500_map_io(); iotable_init(u5500_io_desc, ARRAY_SIZE(u5500_io_desc)); + + _PRCMU_BASE = __io_address(U5500_PRCMU_BASE); } static int usb_db5500_rx_dma_cfg[] = { diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 516126cb357..c3c417656bd 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -87,6 +87,8 @@ void __init u8500_map_io(void) iotable_init(u8500_v1_io_desc, ARRAY_SIZE(u8500_v1_io_desc)); else if (cpu_is_u8500v2()) iotable_init(u8500_v2_io_desc, ARRAY_SIZE(u8500_v2_io_desc)); + + _PRCMU_BASE = __io_address(U8500_PRCMU_BASE); } static struct resource db8500_pmu_resources[] = { @@ -129,9 +131,14 @@ static struct platform_device db8500_pmu_device = { .dev.platform_data = &db8500_pmu_platdata, }; +static struct platform_device db8500_prcmu_device = { + .name = "db8500-prcmu", +}; + static struct platform_device *platform_devs[] __initdata = { &u8500_dma40_device, &db8500_pmu_device, + &db8500_prcmu_device, }; static resource_size_t __initdata db8500_gpio_base[] = { diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index 5a43107c623..1da23bb87c1 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -8,6 +8,8 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/clk.h> +#include <linux/mfd/db8500-prcmu.h> +#include <linux/mfd/db5500-prcmu.h> #include <asm/cacheflush.h> #include <asm/hardware/cache-l2x0.h> @@ -19,10 +21,11 @@ #include <mach/hardware.h> #include <mach/setup.h> #include <mach/devices.h> -#include <mach/prcmu.h> #include "clock.h" +void __iomem *_PRCMU_BASE; + #ifdef CONFIG_CACHE_L2X0 static void __iomem *l2x0_base; #endif @@ -47,6 +50,8 @@ void __init ux500_init_irq(void) * Init clocks here so that they are available for system timer * initialization. */ + if (cpu_is_u5500()) + db5500_prcmu_early_init(); if (cpu_is_u8500()) prcmu_early_init(); clk_init(); diff --git a/arch/arm/mach-ux500/cpufreq.c b/arch/arm/mach-ux500/cpufreq.c deleted file mode 100644 index 5c5b747f134..00000000000 --- a/arch/arm/mach-ux500/cpufreq.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * CPU frequency scaling for u8500 - * Inspired by linux/arch/arm/mach-davinci/cpufreq.c - * - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License v2 - * - * Author: Sundar Iyer <sundar.iyer@stericsson.com> - * Author: Martin Persson <martin.persson@stericsson.com> - * Author: Jonas Aaberg <jonas.aberg@stericsson.com> - * - */ - -#include <linux/platform_device.h> -#include <linux/kernel.h> -#include <linux/cpufreq.h> -#include <linux/delay.h> - -#include <mach/hardware.h> -#include <mach/prcmu.h> -#include <mach/prcmu-defs.h> - -#define DRIVER_NAME "cpufreq-u8500" -#define CPUFREQ_NAME "u8500" - -static struct device *dev; - -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 CPU_OPP_MAX, if available */ - .index = 3, - .frequency = CPUFREQ_TABLE_END, - }, - [4] = { - .index = 4, - .frequency = CPUFREQ_TABLE_END, - }, -}; - -static enum prcmu_cpu_opp index2opp[] = { - CPU_OPP_EXT_CLK, - CPU_OPP_50, - CPU_OPP_100, - CPU_OPP_MAX -}; - -static int u8500_cpufreq_verify_speed(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, freq_table); -} - -static int u8500_cpufreq_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) -{ - struct cpufreq_freqs freqs; - unsigned int index; - int ret = 0; - - /* - * Ensure desired rate is within allowed range. Some govenors - * (ondemand) will just pass target_freq=0 to get the minimum. - */ - 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; - - ret = cpufreq_frequency_table_target(policy, freq_table, - target_freq, relation, &index); - if (ret < 0) { - dev_err(dev, "Could not look up next frequency\n"); - return ret; - } - - freqs.old = policy->cur; - freqs.new = freq_table[index].frequency; - freqs.cpu = policy->cpu; - - if (freqs.old == freqs.new) { - dev_dbg(dev, "Current and target frequencies are equal\n"); - return 0; - } - - dev_dbg(dev, "transition: %u --> %u\n", freqs.old, freqs.new); - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - - ret = prcmu_set_cpu_opp(index2opp[index]); - if (ret < 0) { - dev_err(dev, "Failed to set OPP level\n"); - return ret; - } - - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - - return ret; -} - -static unsigned int u8500_cpufreq_getspeed(unsigned int cpu) -{ - int i; - - for (i = 0; prcmu_get_cpu_opp() != index2opp[i]; i++) - ; - return freq_table[i].frequency; -} - -static int __cpuinit u8500_cpu_init(struct cpufreq_policy *policy) -{ - int res; - - BUILD_BUG_ON(ARRAY_SIZE(index2opp) + 1 != ARRAY_SIZE(freq_table)); - - if (cpu_is_u8500v2()) { - freq_table[1].frequency = 400000; - freq_table[2].frequency = 800000; - if (prcmu_has_arm_maxopp()) - freq_table[3].frequency = 1000000; - } - - /* 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 { - dev_err(dev, "u8500-cpufreq : Failed to read policy table\n"); - return res; - } - - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - policy->cur = u8500_cpufreq_getspeed(policy->cpu); - 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 = 200 * 1000; /* in ns */ - - /* policy sharing between dual CPUs */ - cpumask_copy(policy->cpus, &cpu_present_map); - - policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; - - return res; -} - -static struct freq_attr *u8500_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; -static int u8500_cpu_exit(struct cpufreq_policy *policy) -{ - cpufreq_frequency_table_put_attr(policy->cpu); - return 0; -} - -static struct cpufreq_driver u8500_driver = { - .owner = THIS_MODULE, - .flags = CPUFREQ_STICKY, - .verify = u8500_cpufreq_verify_speed, - .target = u8500_cpufreq_target, - .get = u8500_cpufreq_getspeed, - .init = u8500_cpu_init, - .exit = u8500_cpu_exit, - .name = CPUFREQ_NAME, - .attr = u8500_cpufreq_attr, -}; - -static int __init u8500_cpufreq_probe(struct platform_device *pdev) -{ - dev = &pdev->dev; - return cpufreq_register_driver(&u8500_driver); -} - -static int __exit u8500_cpufreq_remove(struct platform_device *pdev) -{ - return cpufreq_unregister_driver(&u8500_driver); -} - -static struct platform_driver u8500_cpufreq_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, - .remove = __exit_p(u8500_cpufreq_remove), -}; - -static int __init u8500_cpufreq_init(void) -{ - return platform_driver_probe(&u8500_cpufreq_driver, - &u8500_cpufreq_probe); -} - -device_initcall(u8500_cpufreq_init); diff --git a/arch/arm/mach-ux500/include/mach/db5500-regs.h b/arch/arm/mach-ux500/include/mach/db5500-regs.h index bd88c1e7406..6ad98329410 100644 --- a/arch/arm/mach-ux500/include/mach/db5500-regs.h +++ b/arch/arm/mach-ux500/include/mach/db5500-regs.h @@ -17,6 +17,8 @@ #define U5500_GIC_DIST_BASE 0xA0411000 #define U5500_GIC_CPU_BASE 0xA0410100 #define U5500_DMA_BASE 0x90030000 +#define U5500_STM_BASE 0x90020000 +#define U5500_STM_REG_BASE (U5500_STM_BASE + 0xF000) #define U5500_MCDE_BASE 0xA0400000 #define U5500_MODEM_BASE 0xB0000000 #define U5500_L2CC_BASE 0xA0412000 @@ -29,7 +31,9 @@ #define U5500_NAND0_BASE 0x60000000 #define U5500_NAND1_BASE 0x70000000 #define U5500_TWD_BASE 0xa0410600 +#define U5500_ICN_BASE 0xA0040000 #define U5500_B2R2_BASE 0xa0200000 +#define U5500_BOOT_ROM_BASE 0x90000000 #define U5500_FSMC_BASE (U5500_PER1_BASE + 0x0000) #define U5500_SDI0_BASE (U5500_PER1_BASE + 0x1000) @@ -60,6 +64,7 @@ #define U5500_MSP1_BASE (U5500_PER4_BASE + 0x9000) #define U5500_GPIO2_BASE (U5500_PER4_BASE + 0xA000) #define U5500_CDETECT_BASE (U5500_PER4_BASE + 0xF000) +#define U5500_PRCMU_TCDM_BASE (U5500_PER4_BASE + 0x18000) #define U5500_SPI0_BASE (U5500_PER5_BASE + 0x0000) #define U5500_SPI1_BASE (U5500_PER5_BASE + 0x1000) @@ -83,7 +88,7 @@ #define U5500_HASH0_BASE (U5500_PER6_BASE + 0x1000) #define U5500_HASH1_BASE (U5500_PER6_BASE + 0x2000) #define U5500_PKA_BASE (U5500_PER6_BASE + 0x4000) -#define U5500_PKAM_BASE (U5500_PER6_BASE + 0x5000) +#define U5500_PKAM_BASE (U5500_PER6_BASE + 0x5100) #define U5500_MTU0_BASE (U5500_PER6_BASE + 0x6000) #define U5500_MTU1_BASE (U5500_PER6_BASE + 0x7000) #define U5500_CR_BASE (U5500_PER6_BASE + 0x8000) @@ -114,8 +119,19 @@ #define U5500_MBOX2_LOCAL_START (U5500_MBOX_BASE + 0x20) #define U5500_MBOX2_LOCAL_END (U5500_MBOX_BASE + 0x3F) -#define U5500_ESRAM_BASE 0x40000000 +#define U5500_ACCCON_BASE_SEC (0xBFFF0000) +#define U5500_ACCCON_BASE (0xBFFF1000) +#define U5500_ACCCON_CPUVEC_RESET_ADDR_OFFSET (0x00000020) +#define U5500_ACCCON_ACC_CPU_CTRL_OFFSET (0x000000BC) + +#define U5500_ESRAM_BASE 0x40000000 #define U5500_ESRAM_DMA_LCPA_OFFSET 0x10000 #define U5500_DMA_LCPA_BASE (U5500_ESRAM_BASE + U5500_ESRAM_DMA_LCPA_OFFSET) +#define U5500_MCDE_SIZE 0x1000 +#define U5500_DSI_LINK_SIZE 0x1000 +#define U5500_DSI_LINK_COUNT 0x2 +#define U5500_DSI_LINK1_BASE (U5500_MCDE_BASE + U5500_MCDE_SIZE) +#define U5500_DSI_LINK2_BASE (U5500_DSI_LINK1_BASE + U5500_DSI_LINK_SIZE) + #endif diff --git a/arch/arm/mach-ux500/include/mach/db8500-regs.h b/arch/arm/mach-ux500/include/mach/db8500-regs.h index 16647b25537..049997109cf 100644 --- a/arch/arm/mach-ux500/include/mach/db8500-regs.h +++ b/arch/arm/mach-ux500/include/mach/db8500-regs.h @@ -15,8 +15,13 @@ #define U8500_ESRAM_BANK2 (U8500_ESRAM_BANK1 + U8500_ESRAM_BANK_SIZE) #define U8500_ESRAM_BANK3 (U8500_ESRAM_BANK2 + U8500_ESRAM_BANK_SIZE) #define U8500_ESRAM_BANK4 (U8500_ESRAM_BANK3 + U8500_ESRAM_BANK_SIZE) -/* Use bank 4 for DMA LCPA */ -#define U8500_DMA_LCPA_BASE U8500_ESRAM_BANK4 +/* + * on V1 DMA uses 4KB for logical parameters position is right after the 64KB + * reserved for security + */ +#define U8500_ESRAM_DMA_LCPA_OFFSET 0x10000 + +#define U8500_DMA_LCPA_BASE (U8500_ESRAM_BANK0 + U8500_ESRAM_DMA_LCPA_OFFSET) #define U8500_DMA_LCPA_BASE_ED (U8500_ESRAM_BANK4 + 0x4000) #define U8500_PER3_BASE 0x80000000 @@ -27,9 +32,12 @@ #define U8500_B2R2_BASE 0x80130000 #define U8500_HSEM_BASE 0x80140000 #define U8500_PER4_BASE 0x80150000 +#define U8500_TPIU_BASE 0x80190000 #define U8500_ICN_BASE 0x81000000 #define U8500_BOOT_ROM_BASE 0x90000000 +/* ASIC ID is at 0xbf4 offset within this region */ +#define U8500_ASIC_ID_BASE 0x9001D000 #define U8500_PER6_BASE 0xa03c0000 #define U8500_PER5_BASE 0xa03e0000 @@ -70,13 +78,15 @@ /* per6 base addresses */ #define U8500_RNG_BASE (U8500_PER6_BASE + 0x0000) -#define U8500_PKA_BASE (U8500_PER6_BASE + 0x1000) -#define U8500_PKAM_BASE (U8500_PER6_BASE + 0x2000) +#define U8500_HASH0_BASE (U8500_PER6_BASE + 0x1000) +#define U8500_HASH1_BASE (U8500_PER6_BASE + 0x2000) +#define U8500_PKA_BASE (U8500_PER6_BASE + 0x4000) +#define U8500_PKAM_BASE (U8500_PER6_BASE + 0x5100) #define U8500_MTU0_BASE (U8500_PER6_BASE + 0x6000) /* v1 */ #define U8500_MTU1_BASE (U8500_PER6_BASE + 0x7000) /* v1 */ #define U8500_CR_BASE (U8500_PER6_BASE + 0x8000) /* v1 */ -#define U8500_CRYPTO0_BASE (U8500_PER6_BASE + 0xa000) -#define U8500_CRYPTO1_BASE (U8500_PER6_BASE + 0xb000) +#define U8500_CRYP0_BASE (U8500_PER6_BASE + 0xa000) +#define U8500_CRYP1_BASE (U8500_PER6_BASE + 0xb000) #define U8500_CLKRST6_BASE (U8500_PER6_BASE + 0xf000) /* per5 base addresses */ @@ -93,7 +103,8 @@ #define U8500_DMC_BASE (U8500_PER4_BASE + 0x06000) #define U8500_PRCMU_BASE (U8500_PER4_BASE + 0x07000) #define U8500_PRCMU_TCDM_BASE_V1 (U8500_PER4_BASE + 0x0f000) -#define U8500_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x68000) +#define U8500_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x68000) +#define U8500_PRCMU_TCPM_BASE (U8500_PER4_BASE + 0x60000) /* per3 base addresses */ #define U8500_FSMC_BASE (U8500_PER3_BASE + 0x0000) @@ -124,6 +135,7 @@ #define U8500_I2C1_BASE (U8500_PER1_BASE + 0x2000) #define U8500_MSP0_BASE (U8500_PER1_BASE + 0x3000) #define U8500_MSP1_BASE (U8500_PER1_BASE + 0x4000) +#define U8500_MSP3_BASE (U8500_PER1_BASE + 0x5000) #define U8500_SDI0_BASE (U8500_PER1_BASE + 0x6000) #define U8500_I2C2_BASE (U8500_PER1_BASE + 0x8000) #define U8500_SPI3_BASE (U8500_PER1_BASE + 0x9000) @@ -143,4 +155,15 @@ #define U8500_GPIOBANK7_BASE (U8500_GPIO2_BASE + 0x80) #define U8500_GPIOBANK8_BASE U8500_GPIO3_BASE +#define U8500_MCDE_SIZE 0x1000 +#define U8500_DSI_LINK_SIZE 0x1000 +#define U8500_DSI_LINK1_BASE (U8500_MCDE_BASE + U8500_MCDE_SIZE) +#define U8500_DSI_LINK2_BASE (U8500_DSI_LINK1_BASE + U8500_DSI_LINK_SIZE) +#define U8500_DSI_LINK3_BASE (U8500_DSI_LINK2_BASE + U8500_DSI_LINK_SIZE) +#define U8500_DSI_LINK_COUNT 0x3 + +/* Modem and APE physical addresses */ +#define U8500_MODEM_BASE 0xe000000 +#define U8500_APE_BASE 0x6000000 + #endif diff --git a/arch/arm/mach-ux500/include/mach/hardware.h b/arch/arm/mach-ux500/include/mach/hardware.h index bf63f2631ba..2c6f71049f2 100644 --- a/arch/arm/mach-ux500/include/mach/hardware.h +++ b/arch/arm/mach-ux500/include/mach/hardware.h @@ -35,6 +35,7 @@ #ifndef __ASSEMBLY__ #include <mach/id.h> +extern void __iomem *_PRCMU_BASE; #define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) diff --git a/arch/arm/mach-ux500/include/mach/id.h b/arch/arm/mach-ux500/include/mach/id.h index f1288d10b6a..02b541a37ee 100644 --- a/arch/arm/mach-ux500/include/mach/id.h +++ b/arch/arm/mach-ux500/include/mach/id.h @@ -75,6 +75,26 @@ static inline bool __attribute_const__ cpu_is_u8500v2(void) return cpu_is_u8500() && ((dbx500_revision() & 0xf0) == 0xB0); } +static inline bool cpu_is_u8500v20(void) +{ + return cpu_is_u8500() && (dbx500_revision() == 0xB0); +} + +static inline bool cpu_is_u8500v21(void) +{ + return cpu_is_u8500() && (dbx500_revision() == 0xB1); +} + +static inline bool cpu_is_u8500v20_or_later(void) +{ + return cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11(); +} + +static inline bool ux500_is_svp(void) +{ + return false; +} + #define ux500_unknown_soc() BUG() #endif diff --git a/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h b/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h index 97ef55f8493..47969909836 100644 --- a/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h +++ b/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h @@ -50,6 +50,11 @@ #define MOP500_IRQ_END MOP500_NR_IRQS +/* + * We may have several boards, but only one will run at a + * time, so the one with most IRQs will bump this ahead, + * but the IRQ_BOARD_START remains the same for either board. + */ #if MOP500_IRQ_END > IRQ_BOARD_END #undef IRQ_BOARD_END #define IRQ_BOARD_END MOP500_IRQ_END diff --git a/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h b/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h new file mode 100644 index 00000000000..29d972c7717 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __MACH_IRQS_BOARD_U5500_H +#define __MACH_IRQS_BOARD_U5500_H + +#define AB5500_NR_IRQS 5 +#define IRQ_AB5500_BASE IRQ_BOARD_START +#define IRQ_AB5500_END (IRQ_AB5500_BASE + AB5500_NR_IRQS) + +#define U5500_IRQ_END IRQ_AB5500_END + +#if IRQ_BOARD_END < U5500_IRQ_END +#undef IRQ_BOARD_END +#define IRQ_BOARD_END U5500_IRQ_END +#endif + +#endif diff --git a/arch/arm/mach-ux500/include/mach/irqs-db5500.h b/arch/arm/mach-ux500/include/mach/irqs-db5500.h index bfa123dbec3..77239776a6f 100644 --- a/arch/arm/mach-ux500/include/mach/irqs-db5500.h +++ b/arch/arm/mach-ux500/include/mach/irqs-db5500.h @@ -83,4 +83,31 @@ #define IRQ_DB5500_GPIO6 (IRQ_SHPI_START + 125) #define IRQ_DB5500_GPIO7 (IRQ_SHPI_START + 126) +#ifdef CONFIG_UX500_SOC_DB5500 + +/* + * After the GPIO ones we reserve a range of IRQ:s in which virtual + * IRQ:s representing modem IRQ:s can be allocated + */ +#define IRQ_MODEM_EVENTS_BASE IRQ_SOC_START +#define IRQ_MODEM_EVENTS_NBR 72 +#define IRQ_MODEM_EVENTS_END (IRQ_MODEM_EVENTS_BASE + IRQ_MODEM_EVENTS_NBR) + +/* List of virtual IRQ:s that are allocated from the range above */ +#define MBOX_PAIR0_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 43) +#define MBOX_PAIR1_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 45) +#define MBOX_PAIR2_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 41) + +/* + * We may have several SoCs, but only one will run at a + * time, so the one with most IRQs will bump this ahead, + * but the IRQ_SOC_START remains the same for either SoC. + */ +#if IRQ_SOC_END < IRQ_MODEM_EVENTS_END +#undef IRQ_SOC_END +#define IRQ_SOC_END IRQ_MODEM_EVENTS_END +#endif + +#endif /* CONFIG_UX500_SOC_DB5500 */ + #endif diff --git a/arch/arm/mach-ux500/include/mach/irqs-db8500.h b/arch/arm/mach-ux500/include/mach/irqs-db8500.h index 8b5d9f0a163..68bc1497460 100644 --- a/arch/arm/mach-ux500/include/mach/irqs-db8500.h +++ b/arch/arm/mach-ux500/include/mach/irqs-db8500.h @@ -93,4 +93,58 @@ #define IRQ_DB8500_GPIO7 (IRQ_SHPI_START + 126) #define IRQ_DB8500_GPIO8 (IRQ_SHPI_START + 127) +#define IRQ_CA_WAKE_REQ_ED (IRQ_SHPI_START + 71) +#define IRQ_AC_READ_NOTIFICATION_0_ED (IRQ_SHPI_START + 66) +#define IRQ_AC_READ_NOTIFICATION_1_ED (IRQ_SHPI_START + 64) +#define IRQ_CA_MSG_PEND_NOTIFICATION_0_ED (IRQ_SHPI_START + 67) +#define IRQ_CA_MSG_PEND_NOTIFICATION_1_ED (IRQ_SHPI_START + 65) + +#define IRQ_CA_WAKE_REQ_V1 (IRQ_SHPI_START + 83) +#define IRQ_AC_READ_NOTIFICATION_0_V1 (IRQ_SHPI_START + 78) +#define IRQ_AC_READ_NOTIFICATION_1_V1 (IRQ_SHPI_START + 76) +#define IRQ_CA_MSG_PEND_NOTIFICATION_0_V1 (IRQ_SHPI_START + 79) +#define IRQ_CA_MSG_PEND_NOTIFICATION_1_V1 (IRQ_SHPI_START + 77) + +#ifdef CONFIG_UX500_SOC_DB8500 + +/* Virtual interrupts corresponding to the PRCMU wakeups. */ +#define IRQ_PRCMU_BASE IRQ_SOC_START +#define NUM_PRCMU_WAKEUPS (IRQ_PRCMU_END - IRQ_PRCMU_BASE) + +#define IRQ_PRCMU_RTC (IRQ_PRCMU_BASE) +#define IRQ_PRCMU_RTT0 (IRQ_PRCMU_BASE + 1) +#define IRQ_PRCMU_RTT1 (IRQ_PRCMU_BASE + 2) +#define IRQ_PRCMU_HSI0 (IRQ_PRCMU_BASE + 3) +#define IRQ_PRCMU_HSI1 (IRQ_PRCMU_BASE + 4) +#define IRQ_PRCMU_CA_WAKE (IRQ_PRCMU_BASE + 5) +#define IRQ_PRCMU_USB (IRQ_PRCMU_BASE + 6) +#define IRQ_PRCMU_ABB (IRQ_PRCMU_BASE + 7) +#define IRQ_PRCMU_ABB_FIFO (IRQ_PRCMU_BASE + 8) +#define IRQ_PRCMU_ARM (IRQ_PRCMU_BASE + 9) +#define IRQ_PRCMU_MODEM_SW_RESET_REQ (IRQ_PRCMU_BASE + 10) +#define IRQ_PRCMU_GPIO0 (IRQ_PRCMU_BASE + 11) +#define IRQ_PRCMU_GPIO1 (IRQ_PRCMU_BASE + 12) +#define IRQ_PRCMU_GPIO2 (IRQ_PRCMU_BASE + 13) +#define IRQ_PRCMU_GPIO3 (IRQ_PRCMU_BASE + 14) +#define IRQ_PRCMU_GPIO4 (IRQ_PRCMU_BASE + 15) +#define IRQ_PRCMU_GPIO5 (IRQ_PRCMU_BASE + 16) +#define IRQ_PRCMU_GPIO6 (IRQ_PRCMU_BASE + 17) +#define IRQ_PRCMU_GPIO7 (IRQ_PRCMU_BASE + 18) +#define IRQ_PRCMU_GPIO8 (IRQ_PRCMU_BASE + 19) +#define IRQ_PRCMU_CA_SLEEP (IRQ_PRCMU_BASE + 20) +#define IRQ_PRCMU_HOTMON_LOW (IRQ_PRCMU_BASE + 21) +#define IRQ_PRCMU_HOTMON_HIGH (IRQ_PRCMU_BASE + 22) +#define IRQ_PRCMU_END (IRQ_PRCMU_BASE + 23) + +/* + * We may have several SoCs, but only one will run at a + * time, so the one with most IRQs will bump this ahead, + * but the IRQ_SOC_START remains the same for either SoC. + */ +#if IRQ_SOC_END < IRQ_PRCMU_END +#undef IRQ_SOC_END +#define IRQ_SOC_END IRQ_PRCMU_END +#endif + +#endif /* CONFIG_UX500_SOC_DB8500 */ #endif diff --git a/arch/arm/mach-ux500/include/mach/irqs.h b/arch/arm/mach-ux500/include/mach/irqs.h index ba1294c13c4..9db68d264c5 100644 --- a/arch/arm/mach-ux500/include/mach/irqs.h +++ b/arch/arm/mach-ux500/include/mach/irqs.h @@ -10,49 +10,47 @@ #ifndef ASM_ARCH_IRQS_H #define ASM_ARCH_IRQS_H -#include <mach/irqs-db5500.h> -#include <mach/irqs-db8500.h> +#include <mach/hardware.h> -#define IRQ_LOCALTIMER 29 -#define IRQ_LOCALWDOG 30 +#define IRQ_LOCALTIMER 29 +#define IRQ_LOCALWDOG 30 /* Shared Peripheral Interrupt (SHPI) */ #define IRQ_SHPI_START 32 -/* Interrupt numbers generic for shared peripheral */ +/* + * MTU0 preserved for now until plat-nomadik is taught not to use it. Don't + * add any other IRQs here, use the irqs-dbx500.h files. + */ #define IRQ_MTU0 (IRQ_SHPI_START + 4) -/* There are 128 shared peripheral interrupts assigned to - * INTID[160:32]. The first 32 interrupts are reserved. - */ -#define DBX500_NR_INTERNAL_IRQS 161 +#define DBX500_NR_INTERNAL_IRQS 160 /* After chip-specific IRQ numbers we have the GPIO ones */ #define NOMADIK_NR_GPIO 288 #define NOMADIK_GPIO_TO_IRQ(gpio) ((gpio) + DBX500_NR_INTERNAL_IRQS) #define NOMADIK_IRQ_TO_GPIO(irq) ((irq) - DBX500_NR_INTERNAL_IRQS) -#define IRQ_BOARD_START NOMADIK_GPIO_TO_IRQ(NOMADIK_NR_GPIO) +#define IRQ_GPIO_END NOMADIK_GPIO_TO_IRQ(NOMADIK_NR_GPIO) + +#define IRQ_SOC_START IRQ_GPIO_END +/* This will be overridden by SoC-specific irq headers */ +#define IRQ_SOC_END IRQ_SOC_START +#include <mach/irqs-db5500.h> +#include <mach/irqs-db8500.h> + +#define IRQ_BOARD_START IRQ_SOC_END /* This will be overridden by board-specific irq headers */ -#define IRQ_BOARD_END IRQ_BOARD_START +#define IRQ_BOARD_END IRQ_BOARD_START #ifdef CONFIG_MACH_U8500 #include <mach/irqs-board-mop500.h> #endif -/* - * After the board specific IRQ:s we reserve a range of IRQ:s in which virtual - * IRQ:s representing modem IRQ:s can be allocated - */ -#define IRQ_MODEM_EVENTS_BASE (IRQ_BOARD_END + 1) -#define IRQ_MODEM_EVENTS_NBR 72 -#define IRQ_MODEM_EVENTS_END (IRQ_MODEM_EVENTS_BASE + IRQ_MODEM_EVENTS_NBR) - -/* List of virtual IRQ:s that are allocated from the range above */ -#define MBOX_PAIR0_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 43) -#define MBOX_PAIR1_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 45) -#define MBOX_PAIR2_VIRT_IRQ (IRQ_MODEM_EVENTS_BASE + 41) +#ifdef CONFIG_MACH_U5500 +#include <mach/irqs-board-u5500.h> +#endif -#define NR_IRQS IRQ_MODEM_EVENTS_END +#define NR_IRQS IRQ_BOARD_END #endif /* ASM_ARCH_IRQS_H */ diff --git a/arch/arm/mach-ux500/include/mach/prcmu-defs.h b/arch/arm/mach-ux500/include/mach/prcmu-defs.h deleted file mode 100644 index 848ba64b561..00000000000 --- a/arch/arm/mach-ux500/include/mach/prcmu-defs.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Sundar Iyer <sundar.iyer@stericsson.com> - * Author: Martin Persson <martin.persson@stericsson.com> - * - * License Terms: GNU General Public License v2 - * - * PRCM Unit definitions - */ - -#ifndef __MACH_PRCMU_DEFS_H -#define __MACH_PRCMU_DEFS_H - -enum prcmu_cpu_opp { - CPU_OPP_INIT = 0x00, - CPU_OPP_NO_CHANGE = 0x01, - CPU_OPP_100 = 0x02, - CPU_OPP_50 = 0x03, - CPU_OPP_MAX = 0x04, - CPU_OPP_EXT_CLK = 0x07 -}; -enum prcmu_ape_opp { - APE_OPP_NO_CHANGE = 0x00, - APE_OPP_100 = 0x02, - APE_OPP_50 = 0x03, -}; - -#endif /* __MACH_PRCMU_DEFS_H */ diff --git a/arch/arm/mach-ux500/include/mach/prcmu.h b/arch/arm/mach-ux500/include/mach/prcmu.h deleted file mode 100644 index c49e456162e..00000000000 --- a/arch/arm/mach-ux500/include/mach/prcmu.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> - * Author: Sundar Iyer <sundar.iyer@stericsson.com> - * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> - * - * License Terms: GNU General Public License v2 - * - * PRCM Unit f/w API - */ -#ifndef __MACH_PRCMU_H -#define __MACH_PRCMU_H -#include <mach/prcmu-defs.h> - -void __init prcmu_early_init(void); -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_set_ape_opp(enum prcmu_ape_opp opp); -int prcmu_set_cpu_opp(enum prcmu_cpu_opp opp); -int prcmu_set_ape_cpu_opps(enum prcmu_ape_opp ape_opp, - enum prcmu_cpu_opp cpu_opp); -int prcmu_get_ape_opp(void); -int prcmu_get_cpu_opp(void); -bool prcmu_has_arm_maxopp(void); - -#endif /* __MACH_PRCMU_H */ diff --git a/arch/arm/mach-ux500/prcmu.c b/arch/arm/mach-ux500/prcmu.c deleted file mode 100644 index c522d26ef34..00000000000 --- a/arch/arm/mach-ux500/prcmu.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * 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/kernel.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/mutex.h> -#include <linux/completion.h> -#include <linux/jiffies.h> -#include <linux/bitops.h> -#include <linux/interrupt.h> - -#include <mach/hardware.h> -#include <mach/prcmu-regs.h> -#include <mach/prcmu-defs.h> - -/* Global var to runtime determine TCDM base for v2 or v1 */ -static __iomem void *tcdm_base; - -#define _MBOX_HEADER (tcdm_base + 0xFE8) -#define MBOX_HEADER_REQ_MB0 (_MBOX_HEADER + 0x0) - -#define REQ_MB1 (tcdm_base + 0xFD0) -#define REQ_MB5 (tcdm_base + 0xE44) - -#define REQ_MB1_ARMOPP (REQ_MB1 + 0x0) -#define REQ_MB1_APEOPP (REQ_MB1 + 0x1) -#define REQ_MB1_BOOSTOPP (REQ_MB1 + 0x2) - -#define ACK_MB1 (tcdm_base + 0xE04) -#define ACK_MB5 (tcdm_base + 0xDF4) - -#define ACK_MB1_CURR_ARMOPP (ACK_MB1 + 0x0) -#define ACK_MB1_CURR_APEOPP (ACK_MB1 + 0x1) - -#define REQ_MB5_I2C_SLAVE_OP (REQ_MB5) -#define REQ_MB5_I2C_HW_BITS (REQ_MB5 + 1) -#define REQ_MB5_I2C_REG (REQ_MB5 + 2) -#define REQ_MB5_I2C_VAL (REQ_MB5 + 3) - -#define ACK_MB5_I2C_STATUS (ACK_MB5 + 1) -#define ACK_MB5_I2C_VAL (ACK_MB5 + 3) - -#define PRCM_AVS_VARM_MAX_OPP (tcdm_base + 0x2E4) -#define PRCM_AVS_ISMODEENABLE 7 -#define PRCM_AVS_ISMODEENABLE_MASK (1 << PRCM_AVS_ISMODEENABLE) - -#define I2C_WRITE(slave) \ - (((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0)) -#define I2C_READ(slave) \ - (((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0) | BIT(0)) -#define I2C_STOP_EN BIT(3) - -enum mb1_h { - MB1H_ARM_OPP = 1, - MB1H_APE_OPP, - MB1H_ARM_APE_OPP, -}; - -static struct { - struct mutex lock; - struct completion work; - struct { - u8 arm_opp; - u8 ape_opp; - u8 arm_status; - u8 ape_status; - } ack; -} mb1_transfer; - -enum ack_mb5_status { - I2C_WR_OK = 0x01, - I2C_RD_OK = 0x02, -}; - -#define MBOX_BIT BIT -#define NUM_MBOX 8 - -static struct { - struct mutex lock; - struct completion work; - bool failed; - struct { - u8 status; - u8 value; - } ack; -} mb5_transfer; - -/** - * 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; - - r = mutex_lock_interruptible(&mb5_transfer.lock); - if (r) - return r; - - while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) - cpu_relax(); - - writeb(I2C_READ(slave), REQ_MB5_I2C_SLAVE_OP); - writeb(I2C_STOP_EN, REQ_MB5_I2C_HW_BITS); - writeb(reg, REQ_MB5_I2C_REG); - - writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); - if (!wait_for_completion_timeout(&mb5_transfer.work, - msecs_to_jiffies(500))) { - pr_err("prcmu: prcmu_abb_read timed out.\n"); - r = -EIO; - goto unlock_and_return; - } - r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO); - if (!r) - *value = mb5_transfer.ack.value; - -unlock_and_return: - mutex_unlock(&mb5_transfer.lock); - return r; -} -EXPORT_SYMBOL(prcmu_abb_read); - -/** - * 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; - - r = mutex_lock_interruptible(&mb5_transfer.lock); - if (r) - return r; - - - while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) - cpu_relax(); - - writeb(I2C_WRITE(slave), REQ_MB5_I2C_SLAVE_OP); - writeb(I2C_STOP_EN, REQ_MB5_I2C_HW_BITS); - writeb(reg, REQ_MB5_I2C_REG); - writeb(*value, REQ_MB5_I2C_VAL); - - writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); - if (!wait_for_completion_timeout(&mb5_transfer.work, - msecs_to_jiffies(500))) { - pr_err("prcmu: prcmu_abb_write timed out.\n"); - r = -EIO; - goto unlock_and_return; - } - r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO); - -unlock_and_return: - mutex_unlock(&mb5_transfer.lock); - return r; -} -EXPORT_SYMBOL(prcmu_abb_write); - -static int set_ape_cpu_opps(u8 header, enum prcmu_ape_opp ape_opp, - enum prcmu_cpu_opp cpu_opp) -{ - bool do_ape; - bool do_arm; - int err = 0; - - do_ape = ((header == MB1H_APE_OPP) || (header == MB1H_ARM_APE_OPP)); - do_arm = ((header == MB1H_ARM_OPP) || (header == MB1H_ARM_APE_OPP)); - - mutex_lock(&mb1_transfer.lock); - - while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) - cpu_relax(); - - writeb(0, MBOX_HEADER_REQ_MB0); - writeb(cpu_opp, REQ_MB1_ARMOPP); - writeb(ape_opp, REQ_MB1_APEOPP); - writeb(0, REQ_MB1_BOOSTOPP); - writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET); - wait_for_completion(&mb1_transfer.work); - if ((do_ape) && (mb1_transfer.ack.ape_status != 0)) - err = -EIO; - if ((do_arm) && (mb1_transfer.ack.arm_status != 0)) - err = -EIO; - - mutex_unlock(&mb1_transfer.lock); - - return err; -} - -/** - * prcmu_set_ape_opp() - Set the OPP of the APE. - * @opp: The OPP to set. - * - * This function sets the OPP of the APE. - */ -int prcmu_set_ape_opp(enum prcmu_ape_opp opp) -{ - return set_ape_cpu_opps(MB1H_APE_OPP, opp, APE_OPP_NO_CHANGE); -} -EXPORT_SYMBOL(prcmu_set_ape_opp); - -/** - * prcmu_set_cpu_opp() - Set the OPP of the CPU. - * @opp: The OPP to set. - * - * This function sets the OPP of the CPU. - */ -int prcmu_set_cpu_opp(enum prcmu_cpu_opp opp) -{ - return set_ape_cpu_opps(MB1H_ARM_OPP, CPU_OPP_NO_CHANGE, opp); -} -EXPORT_SYMBOL(prcmu_set_cpu_opp); - -/** - * prcmu_set_ape_cpu_opps() - Set the OPPs of the APE and the CPU. - * @ape_opp: The APE OPP to set. - * @cpu_opp: The CPU OPP to set. - * - * This function sets the OPPs of the APE and the CPU. - */ -int prcmu_set_ape_cpu_opps(enum prcmu_ape_opp ape_opp, - enum prcmu_cpu_opp cpu_opp) -{ - return set_ape_cpu_opps(MB1H_ARM_APE_OPP, ape_opp, cpu_opp); -} -EXPORT_SYMBOL(prcmu_set_ape_cpu_opps); - -/** - * prcmu_get_ape_opp() - Get the OPP of the APE. - * - * This function gets the OPP of the APE. - */ -enum prcmu_ape_opp prcmu_get_ape_opp(void) -{ - return readb(ACK_MB1_CURR_APEOPP); -} -EXPORT_SYMBOL(prcmu_get_ape_opp); - -/** - * prcmu_get_cpu_opp() - Get the OPP of the CPU. - * - * This function gets the OPP of the CPU. The OPP is specified in %%. - * PRCMU_OPP_EXT is a special OPP value, not specified in %%. - */ -int prcmu_get_cpu_opp(void) -{ - return readb(ACK_MB1_CURR_ARMOPP); -} -EXPORT_SYMBOL(prcmu_get_cpu_opp); - -bool prcmu_has_arm_maxopp(void) -{ - return (readb(PRCM_AVS_VARM_MAX_OPP) & PRCM_AVS_ISMODEENABLE_MASK) - == PRCM_AVS_ISMODEENABLE_MASK; -} - -static void read_mailbox_0(void) -{ - writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR); -} - -static void read_mailbox_1(void) -{ - mb1_transfer.ack.arm_opp = readb(ACK_MB1_CURR_ARMOPP); - mb1_transfer.ack.ape_opp = readb(ACK_MB1_CURR_APEOPP); - complete(&mb1_transfer.work); - writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR); -} - -static void read_mailbox_2(void) -{ - writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR); -} - -static void read_mailbox_3(void) -{ - writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR); -} - -static void read_mailbox_4(void) -{ - writel(MBOX_BIT(4), PRCM_ARM_IT1_CLEAR); -} - -static void read_mailbox_5(void) -{ - mb5_transfer.ack.status = readb(ACK_MB5_I2C_STATUS); - mb5_transfer.ack.value = readb(ACK_MB5_I2C_VAL); - complete(&mb5_transfer.work); - writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR); -} - -static void read_mailbox_6(void) -{ - writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR); -} - -static void read_mailbox_7(void) -{ - writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR); -} - -static void (* const read_mailbox[NUM_MBOX])(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; - - bits = (readl(PRCM_ARM_IT1_VAL) & (MBOX_BIT(NUM_MBOX) - 1)); - if (unlikely(!bits)) - return IRQ_NONE; - - for (n = 0; bits; n++) { - if (bits & MBOX_BIT(n)) { - bits -= MBOX_BIT(n); - read_mailbox[n](); - } - } - return IRQ_HANDLED; -} - -void __init prcmu_early_init(void) -{ - if (cpu_is_u8500v11() || cpu_is_u8500ed()) { - tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE_V1); - } else if (cpu_is_u8500v2()) { - tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); - } else { - pr_err("prcmu: Unsupported chip version\n"); - BUG(); - } -} - -static int __init prcmu_init(void) -{ - if (cpu_is_u8500ed()) { - pr_err("prcmu: Unsupported chip version\n"); - return 0; - } - - mutex_init(&mb1_transfer.lock); - init_completion(&mb1_transfer.work); - mutex_init(&mb5_transfer.lock); - init_completion(&mb5_transfer.work); - - /* Clean up the mailbox interrupts after pre-kernel code. */ - writel((MBOX_BIT(NUM_MBOX) - 1), PRCM_ARM_IT1_CLEAR); - - return request_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler, 0, - "prcmu", NULL); -} - -arch_initcall(prcmu_init); diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index c7f1a6f16b6..e2fc2d21fa6 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -39,3 +39,5 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o ##################################################################################d +# ARM SoC drivers +obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c new file mode 100644 index 00000000000..d90456a809f --- /dev/null +++ b/drivers/cpufreq/db8500-cpufreq.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) STMicroelectronics 2009 + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Sundar Iyer <sundar.iyer@stericsson.com> + * Author: Martin Persson <martin.persson@stericsson.com> + * Author: Jonas Aaberg <jonas.aberg@stericsson.com> + * + */ +#include <linux/kernel.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/mfd/db8500-prcmu.h> +#include <mach/id.h> + +static struct cpufreq_frequency_table freq_table[] = { + [0] = { + .index = 0, + .frequency = 300000, + }, + [1] = { + .index = 1, + .frequency = 600000, + }, + [2] = { + /* Used for MAX_OPP, if available */ + .index = 2, + .frequency = CPUFREQ_TABLE_END, + }, + [3] = { + .index = 3, + .frequency = CPUFREQ_TABLE_END, + }, +}; + +static enum arm_opp idx2opp[] = { + ARM_50_OPP, + ARM_100_OPP, + ARM_MAX_OPP +}; + +static struct freq_attr *db8500_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static int db8500_cpufreq_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +static int db8500_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("db8500-cpufreq: Failed to set OPP level\n"); + return -EINVAL; + } + + /* post change notification */ + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static unsigned int db8500_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 db8500_cpufreq_init(struct cpufreq_policy *policy) +{ + int res; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); + + if (cpu_is_u8500v2() && !prcmu_is_u8400()) { + freq_table[0].frequency = 400000; + freq_table[1].frequency = 800000; + if (prcmu_has_arm_maxopp()) + freq_table[2].frequency = 1000000; + } + + /* 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("db8500-cpufreq : Failed to read policy table\n"); + return res; + } + + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; + policy->cur = db8500_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 db8500_cpufreq_driver = { + .flags = CPUFREQ_STICKY, + .verify = db8500_cpufreq_verify_speed, + .target = db8500_cpufreq_target, + .get = db8500_cpufreq_getspeed, + .init = db8500_cpufreq_init, + .name = "DB8500", + .attr = db8500_cpufreq_attr, +}; + +static int __init db8500_cpufreq_register(void) +{ + if (!cpu_is_u8500v20_or_later()) + return -ENODEV; + + pr_info("cpufreq for DB8500 started\n"); + return cpufreq_register_driver(&db8500_cpufreq_driver); +} +device_initcall(db8500_cpufreq_register); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3ed3ff06be5..481770ab271 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -538,7 +538,7 @@ config AB8500_CORE config AB8500_I2C_CORE bool "AB8500 register access via PRCMU I2C" - depends on AB8500_CORE && UX500_SOC_DB8500 + depends on AB8500_CORE && MFD_DB8500_PRCMU default y help This enables register access to the AB8500 chip via PRCMU I2C. @@ -575,6 +575,26 @@ config AB3550_CORE LEDs, vibrator, system power and temperature, power management and ALSA sound. +config MFD_DB8500_PRCMU + bool "ST-Ericsson DB8500 Power Reset Control Management Unit" + depends on UX500_SOC_DB8500 + select MFD_CORE + help + Select this option to enable support for the DB8500 Power Reset + and Control Management Unit. This is basically an autonomous + system controller running an XP70 microprocessor, which is accessed + through a register map. + +config MFD_DB5500_PRCMU + bool "ST-Ericsson DB5500 Power Reset Control Management Unit" + depends on UX500_SOC_DB5500 + select MFD_CORE + help + Select this option to enable support for the DB5500 Power Reset + and Control Management Unit. This is basically an autonomous + system controller running an XP70 microprocessor, which is accessed + through a register map. + config MFD_CS5535 tristate "Support for CS5535 and CS5536 southbridge core functions" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 419caa9d7dc..24aa44448da 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -74,9 +74,12 @@ obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o -obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o +obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o +# ab8500-i2c need to come after db8500-prcmu (which provides the channel) +obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o +obj-$(CONFIG_MFD_DB5500_PRCMU) += db5500-prcmu.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c index 821e6b86afd..9be541c6b00 100644 --- a/drivers/mfd/ab8500-i2c.c +++ b/drivers/mfd/ab8500-i2c.c @@ -11,8 +11,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mfd/ab8500.h> - -#include <mach/prcmu.h> +#include <linux/mfd/db8500-prcmu.h> static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data) { diff --git a/arch/arm/mach-ux500/include/mach/prcmu-regs.h b/drivers/mfd/db5500-prcmu-regs.h index 455467e8879..9a8e9e4ddd3 100644 --- a/arch/arm/mach-ux500/include/mach/prcmu-regs.h +++ b/drivers/mfd/db5500-prcmu-regs.h @@ -15,11 +15,20 @@ #include <mach/hardware.h> -#define _PRCMU_BASE IO_ADDRESS(U8500_PRCMU_BASE) - #define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) +#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f +#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf + +#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) +#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 + #define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 + #define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 + #define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) #define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) #define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) @@ -28,7 +37,8 @@ /* ARM WFI Standby signal register */ #define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) -#define PRCMU_IOCR (_PRCMU_BASE + 0x310) +#define PRCM_IOCR (_PRCMU_BASE + 0x310) +#define PRCM_IOCR_IOFORCE 0x1 /* CPU mailbox registers */ #define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) @@ -37,6 +47,8 @@ /* Dual A9 core interrupt management unit registers */ #define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) +#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 + #define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) #define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) #define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) @@ -74,14 +86,17 @@ /* PRCMU clock/PLL/reset registers */ #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 + 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) /* ePOD and memory power signal control registers */ #define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) @@ -92,5 +107,9 @@ /* Miscellaneous unit registers */ #define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) +#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) +#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 +#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 + -#endif /* __MACH_PRCMU_REGS_H */ +#endif /* __MACH_PRCMU__REGS_H */ diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c new file mode 100644 index 00000000000..9dbb3cab4a6 --- /dev/null +++ b/drivers/mfd/db5500-prcmu.c @@ -0,0 +1,448 @@ +/* + * 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 <linux/mfd/db5500-prcmu.h> +#include <mach/hardware.h> +#include <mach/irqs.h> +#include <mach/db5500-regs.h> +#include "db5500-prcmu-regs.h" + +#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) + +enum mb_return_code { + RC_SUCCESS, + RC_FAIL, +}; + +/* Mailbox 0 headers. */ +enum mb0_header { + /* request */ + RMB0H_PWR_STATE_TRANS = 1, + RMB0H_WAKE_UP_CFG, + RMB0H_RD_WAKE_UP_ACK, + /* acknowledge */ + AMB0H_WAKE_UP = 1, +}; + +/* Mailbox 5 headers. */ +enum mb5_header { + MB5H_I2C_WRITE = 1, + MB5H_I2C_READ, +}; + +/* 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 0x0004013C +#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002 +#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000101 +#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_PLLDSI_LOCKP_LOCKED 0x3 + +/* + * mb0_transfer - state needed for mailbox 0 communication. + * @lock: The transaction lock. + */ +static struct { + spinlock_t lock; +} mb0_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; + +/* PRCMU TCDM base IO address. */ +static __iomem void *tcdm_base; + +/** + * 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(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), 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(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), 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; +} + +int db5500_prcmu_enable_dsipll(void) +{ + int i; + + /* Enable DSIPLL_RESETN resets */ + writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR); + /* Unclamp DSIPLL in/out */ + writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR); + /* Set DSI PLL FREQ */ + writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ); + writel(PRCMU_DSI_PLLOUT_SEL_SETTING, + PRCM_DSI_PLLOUT_SEL); + /* Enable Escape clocks */ + writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); + + /* Start DSI PLL */ + writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE); + /* Reset DSI PLL */ + writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET); + for (i = 0; i < 10; i++) { + if ((readl(PRCM_PLLDSI_LOCKP) & + PRCMU_PLLDSI_LOCKP_LOCKED) == PRCMU_PLLDSI_LOCKP_LOCKED) + break; + udelay(100); + } + /* Release DSIPLL_RESETN */ + writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET); + return 0; +} + +int db5500_prcmu_disable_dsipll(void) +{ + /* Disable dsi pll */ + writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE); + /* Disable escapeclock */ + writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); + return 0; +} + +int db5500_prcmu_set_display_clocks(void) +{ + /* HDMI and TVCLK Should be handled somewhere else */ + /* PLLDIV=8, PLLSW=2, CLKEN=1 */ + writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT); + /* PLLDIV=14, PLLSW=2, CLKEN=1 */ + writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT); + return 0; +} + +static void ack_dbb_wakeup(void) +{ + unsigned long flags; + + spin_lock_irqsave(&mb0_transfer.lock, flags); + + while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) + cpu_relax(); + + writeb(RMB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER); + writel(MBOX_BIT(0), 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; + u8 header; + + header = readb(PRCM_ACK_MB0_HEADER); + switch (header) { + case AMB0H_WAKE_UP: + r = true; + break; + default: + print_unknown_header_warning(0, header); + r = false; + break; + } + writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR); + return r; +} + +static bool read_mailbox_1(void) +{ + writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR); + return false; +} + +static bool read_mailbox_2(void) +{ + writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR); + return false; +} + +static bool read_mailbox_3(void) +{ + writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR); + return false; +} + +static bool read_mailbox_4(void) +{ + writel(MBOX_BIT(4), 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); + complete(&mb5_transfer.work); + break; + default: + print_unknown_header_warning(5, header); + break; + } + writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR); + return false; +} + +static bool read_mailbox_6(void) +{ + writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR); + return false; +} + +static bool read_mailbox_7(void) +{ + writel(MBOX_BIT(7), 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(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; +} + +void __init db5500_prcmu_early_init(void) +{ + tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE); + spin_lock_init(&mb0_transfer.lock); + mutex_init(&mb5_transfer.lock); + init_completion(&mb5_transfer.work); +} + +/** + * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic + * + */ +int __init db5500_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, PRCM_ARM_IT1_CLEAR); + + r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler, + prcmu_irq_thread_fn, 0, "prcmu", NULL); + if (r < 0) { + pr_err("prcmu: Failed to allocate IRQ_DB5500_PRCMU1.\n"); + return -EBUSY; + } + return 0; +} + +arch_initcall(db5500_prcmu_init); diff --git a/drivers/mfd/db8500-prcmu-regs.h b/drivers/mfd/db8500-prcmu-regs.h new file mode 100644 index 00000000000..3bbf04d5804 --- /dev/null +++ b/drivers/mfd/db8500-prcmu-regs.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) STMicroelectronics 2009 + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> + * Author: Sundar Iyer <sundar.iyer@stericsson.com> + * + * License Terms: GNU General Public License v2 + * + * PRCM Unit registers + */ +#ifndef __DB8500_PRCMU_REGS_H +#define __DB8500_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_A9_RESETN_CLR 0x1f4 +#define PRCM_A9_RESETN_SET 0x1f0 +#define PRCM_ARM_LS_CLAMP 0x30C +#define PRCM_SRAM_A9 0x308 + +/* 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_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_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 + +#endif /* __DB8500_PRCMU_REGS_H */ diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c new file mode 100644 index 00000000000..e63782107e2 --- /dev/null +++ b/drivers/mfd/db8500-prcmu.c @@ -0,0 +1,2069 @@ +/* + * 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/platform_device.h> +#include <linux/uaccess.h> +#include <linux/mfd/core.h> +#include <linux/mfd/db8500-prcmu.h> +#include <linux/regulator/db8500-prcmu.h> +#include <linux/regulator/machine.h> +#include <mach/hardware.h> +#include <mach/irqs.h> +#include <mach/db8500-regs.h> +#include <mach/id.h> +#include "db8500-prcmu-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_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 + +/* 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) + +/* 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 + +/* 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) + +/* 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) | (cpu_is_u8500v2() ? BIT(6) : 0)) +#define PRCMU_I2C_READ(slave) \ + (((slave) << 1) | BIT(0) | (cpu_is_u8500v2() ? BIT(6) : 0)) +#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. + * @ack: Reply ("acknowledge") data. + */ +static struct { + struct mutex lock; + struct completion work; + 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), +}; + +/* +* 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; +} + +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); + + 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; +} +/** + * 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; + + 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(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); + writeb(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; + + 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; +} + +/** + * 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 ? ON : OFF), (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; +} + +/** + * 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_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 + 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); +} + +/** + * 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; + + 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); + + 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(20000))) { + pr_err("prcmu: %s timed out (20 s) waiting for a reply.\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(20000))) { + pr_err("prcmu: %s timed out (20 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_SOFRST 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_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: + 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; + + if (cpu_is_u8500v1()) { + tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE_V1); + } else if (cpu_is_u8500v2()) { + 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); + } else { + pr_err("prcmu: Unsupported chip version\n"); + BUG(); + } + + 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); + 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; + irq_set_chip_and_handler(irq, &prcmu_irq_chip, + handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + } +} + +/* + * Power domain switches (ePODs) modeled as regulators for the DB8500 SoC + */ +static struct regulator_consumer_supply db8500_vape_consumers[] = { + REGULATOR_SUPPLY("v-ape", NULL), + REGULATOR_SUPPLY("v-i2c", "nmk-i2c.0"), + REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"), + REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"), + REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"), + /* "v-mmc" changed to "vcore" in the mainline kernel */ + REGULATOR_SUPPLY("vcore", "sdi0"), + REGULATOR_SUPPLY("vcore", "sdi1"), + REGULATOR_SUPPLY("vcore", "sdi2"), + REGULATOR_SUPPLY("vcore", "sdi3"), + REGULATOR_SUPPLY("vcore", "sdi4"), + REGULATOR_SUPPLY("v-dma", "dma40.0"), + REGULATOR_SUPPLY("v-ape", "ab8500-usb.0"), + /* "v-uart" changed to "vcore" in the mainline kernel */ + REGULATOR_SUPPLY("vcore", "uart0"), + REGULATOR_SUPPLY("vcore", "uart1"), + REGULATOR_SUPPLY("vcore", "uart2"), + REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"), +}; + +static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { + /* CG2900 and CW1200 power to off-chip peripherals */ + REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"), + REGULATOR_SUPPLY("wlan_1v8", "cw1200.0"), + REGULATOR_SUPPLY("musb_1v8", "ab8500-usb.0"), + /* AV8100 regulator */ + REGULATOR_SUPPLY("hdmi_1v8", "0-0070"), +}; + +static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = { + REGULATOR_SUPPLY("vsupply", "b2r2.0"), + REGULATOR_SUPPLY("vsupply", "mcde.0"), +}; + +static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { + [DB8500_REGULATOR_VAPE] = { + .constraints = { + .name = "db8500-vape", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = db8500_vape_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_vape_consumers), + }, + [DB8500_REGULATOR_VARM] = { + .constraints = { + .name = "db8500-varm", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_VMODEM] = { + .constraints = { + .name = "db8500-vmodem", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_VPLL] = { + .constraints = { + .name = "db8500-vpll", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_VSMPS1] = { + .constraints = { + .name = "db8500-vsmps1", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_VSMPS2] = { + .constraints = { + .name = "db8500-vsmps2", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = db8500_vsmps2_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_vsmps2_consumers), + }, + [DB8500_REGULATOR_VSMPS3] = { + .constraints = { + .name = "db8500-vsmps3", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_VRF1] = { + .constraints = { + .name = "db8500-vrf1", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_SVAMMDSP] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-sva-mmdsp", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = { + .constraints = { + /* "ret" means "retention" */ + .name = "db8500-sva-mmdsp-ret", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_SVAPIPE] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-sva-pipe", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_SIAMMDSP] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-sia-mmdsp", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = { + .constraints = { + .name = "db8500-sia-mmdsp-ret", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_SIAPIPE] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-sia-pipe", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_SGA] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-sga", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_B2R2_MCDE] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-b2r2-mcde", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = db8500_b2r2_mcde_consumers, + .num_consumer_supplies = ARRAY_SIZE(db8500_b2r2_mcde_consumers), + }, + [DB8500_REGULATOR_SWITCH_ESRAM12] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-esram12", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_ESRAM12RET] = { + .constraints = { + .name = "db8500-esram12-ret", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_ESRAM34] = { + .supply_regulator = "db8500-vape", + .constraints = { + .name = "db8500-esram34", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, + [DB8500_REGULATOR_SWITCH_ESRAM34RET] = { + .constraints = { + .name = "db8500-esram34-ret", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, +}; + +static struct mfd_cell db8500_prcmu_devs[] = { + { + .name = "db8500-prcmu-regulators", + .mfd_data = &db8500_regulators, + }, + { + .name = "cpufreq-u8500", + }, +}; + +/** + * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic + * + */ +static int __init db8500_prcmu_probe(struct platform_device *pdev) +{ + int err = 0; + + if (ux500_is_svp()) + return -ENODEV; + + /* 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; + } + + if (cpu_is_u8500v20_or_later()) + prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); + + err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, + ARRAY_SIZE(db8500_prcmu_devs), NULL, + 0); + + if (err) + pr_err("prcmu: Failed to add subdevices\n"); + else + pr_info("DB8500 PRCMU initialized\n"); + +no_irq_return: + return err; +} + +static struct platform_driver db8500_prcmu_driver = { + .driver = { + .name = "db8500-prcmu", + .owner = THIS_MODULE, + }, +}; + +static int __init db8500_prcmu_init(void) +{ + return platform_driver_probe(&db8500_prcmu_driver, db8500_prcmu_probe); +} + +arch_initcall(db8500_prcmu_init); + +MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>"); +MODULE_DESCRIPTION("DB8500 PRCM Unit driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index b9f29e0d429..f0b13a0d185 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -274,6 +274,13 @@ config REGULATOR_AB8500 This driver supports the regulators found on the ST-Ericsson mixed signal AB8500 PMIC +config REGULATOR_DB8500_PRCMU + bool "ST-Ericsson DB8500 Voltage Domain Regulators" + depends on MFD_DB8500_PRCMU + help + This driver supports the voltage domain regulators controlled by the + DB8500 PRCMU + config REGULATOR_TPS6586X tristate "TI TPS6586X Power regulators" depends on MFD_TPS6586X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d72a4275677..165ff5371e9 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -41,5 +41,6 @@ obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o +obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c new file mode 100644 index 00000000000..1089a961616 --- /dev/null +++ b/drivers/regulator/db8500-prcmu.c @@ -0,0 +1,558 @@ +/* + * 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 + * + * Power domain regulators on DB8500 + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/db8500-prcmu.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/db8500-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 db8500_regulator_info - db8500 regulator information + * @dev: device pointer + * @desc: regulator description + * @rdev: regulator device pointer + * @is_enabled: status of the regulator + * @epod_id: id for EPOD (power domain) + * @is_ramret: RAM retention switch for EPOD (power domain) + * @operating_point: operating point (only for vape, to be removed) + * + */ +struct db8500_regulator_info { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + bool is_enabled; + u16 epod_id; + bool is_ramret; + bool exclude_from_power_state; + unsigned int operating_point; +}; + +static int db8500_regulator_enable(struct regulator_dev *rdev) +{ + struct db8500_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 = true; + if (!info->exclude_from_power_state) + power_state_active_enable(); + + return 0; +} + +static int db8500_regulator_disable(struct regulator_dev *rdev) +{ + struct db8500_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 = false; + if (!info->exclude_from_power_state) + ret = power_state_active_disable(); + + return ret; +} + +static int db8500_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct db8500_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; +} + +/* db8500 regulator operations */ +static struct regulator_ops db8500_regulator_ops = { + .enable = db8500_regulator_enable, + .disable = db8500_regulator_disable, + .is_enabled = db8500_regulator_is_enabled, +}; + +/* + * EPOD control + */ +static bool epod_on[NUM_EPOD_ID]; +static bool epod_ramret[NUM_EPOD_ID]; + +static int enable_epod(u16 epod_id, bool ramret) +{ + int ret; + + if (ramret) { + if (!epod_on[epod_id]) { + ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET); + if (ret < 0) + return ret; + } + epod_ramret[epod_id] = true; + } else { + ret = prcmu_set_epod(epod_id, EPOD_STATE_ON); + if (ret < 0) + return ret; + epod_on[epod_id] = true; + } + + return 0; +} + +static int disable_epod(u16 epod_id, bool ramret) +{ + int ret; + + if (ramret) { + if (!epod_on[epod_id]) { + ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF); + if (ret < 0) + return ret; + } + epod_ramret[epod_id] = false; + } else { + if (epod_ramret[epod_id]) { + 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[epod_id] = false; + } + + return 0; +} + +/* + * Regulator switch + */ +static int db8500_regulator_switch_enable(struct regulator_dev *rdev) +{ + struct db8500_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 = true; +out: + return ret; +} + +static int db8500_regulator_switch_disable(struct regulator_dev *rdev) +{ + struct db8500_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 db8500_regulator_switch_is_enabled(struct regulator_dev *rdev) +{ + struct db8500_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; +} + +static struct regulator_ops db8500_regulator_switch_ops = { + .enable = db8500_regulator_switch_enable, + .disable = db8500_regulator_switch_disable, + .is_enabled = db8500_regulator_switch_is_enabled, +}; + +/* + * Regulator information + */ +static struct db8500_regulator_info + db8500_regulator_info[DB8500_NUM_REGULATORS] = { + [DB8500_REGULATOR_VAPE] = { + .desc = { + .name = "db8500-vape", + .id = DB8500_REGULATOR_VAPE, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VARM] = { + .desc = { + .name = "db8500-varm", + .id = DB8500_REGULATOR_VARM, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VMODEM] = { + .desc = { + .name = "db8500-vmodem", + .id = DB8500_REGULATOR_VMODEM, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VPLL] = { + .desc = { + .name = "db8500-vpll", + .id = DB8500_REGULATOR_VPLL, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VSMPS1] = { + .desc = { + .name = "db8500-vsmps1", + .id = DB8500_REGULATOR_VSMPS1, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VSMPS2] = { + .desc = { + .name = "db8500-vsmps2", + .id = DB8500_REGULATOR_VSMPS2, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .exclude_from_power_state = true, + }, + [DB8500_REGULATOR_VSMPS3] = { + .desc = { + .name = "db8500-vsmps3", + .id = DB8500_REGULATOR_VSMPS3, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_VRF1] = { + .desc = { + .name = "db8500-vrf1", + .id = DB8500_REGULATOR_VRF1, + .ops = &db8500_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + }, + [DB8500_REGULATOR_SWITCH_SVAMMDSP] = { + .desc = { + .name = "db8500-sva-mmdsp", + .id = DB8500_REGULATOR_SWITCH_SVAMMDSP, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SVAMMDSP, + }, + [DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = { + .desc = { + .name = "db8500-sva-mmdsp-ret", + .id = DB8500_REGULATOR_SWITCH_SVAMMDSPRET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SVAMMDSP, + .is_ramret = true, + }, + [DB8500_REGULATOR_SWITCH_SVAPIPE] = { + .desc = { + .name = "db8500-sva-pipe", + .id = DB8500_REGULATOR_SWITCH_SVAPIPE, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SVAPIPE, + }, + [DB8500_REGULATOR_SWITCH_SIAMMDSP] = { + .desc = { + .name = "db8500-sia-mmdsp", + .id = DB8500_REGULATOR_SWITCH_SIAMMDSP, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SIAMMDSP, + }, + [DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = { + .desc = { + .name = "db8500-sia-mmdsp-ret", + .id = DB8500_REGULATOR_SWITCH_SIAMMDSPRET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SIAMMDSP, + .is_ramret = true, + }, + [DB8500_REGULATOR_SWITCH_SIAPIPE] = { + .desc = { + .name = "db8500-sia-pipe", + .id = DB8500_REGULATOR_SWITCH_SIAPIPE, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SIAPIPE, + }, + [DB8500_REGULATOR_SWITCH_SGA] = { + .desc = { + .name = "db8500-sga", + .id = DB8500_REGULATOR_SWITCH_SGA, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_SGA, + }, + [DB8500_REGULATOR_SWITCH_B2R2_MCDE] = { + .desc = { + .name = "db8500-b2r2-mcde", + .id = DB8500_REGULATOR_SWITCH_B2R2_MCDE, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_B2R2_MCDE, + }, + [DB8500_REGULATOR_SWITCH_ESRAM12] = { + .desc = { + .name = "db8500-esram12", + .id = DB8500_REGULATOR_SWITCH_ESRAM12, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM12, + .is_enabled = true, + }, + [DB8500_REGULATOR_SWITCH_ESRAM12RET] = { + .desc = { + .name = "db8500-esram12-ret", + .id = DB8500_REGULATOR_SWITCH_ESRAM12RET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM12, + .is_ramret = true, + }, + [DB8500_REGULATOR_SWITCH_ESRAM34] = { + .desc = { + .name = "db8500-esram34", + .id = DB8500_REGULATOR_SWITCH_ESRAM34, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM34, + .is_enabled = true, + }, + [DB8500_REGULATOR_SWITCH_ESRAM34RET] = { + .desc = { + .name = "db8500-esram34-ret", + .id = DB8500_REGULATOR_SWITCH_ESRAM34RET, + .ops = &db8500_regulator_switch_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + .epod_id = EPOD_ID_ESRAM34, + .is_ramret = true, + }, +}; + +static int __devinit db8500_regulator_probe(struct platform_device *pdev) +{ + struct regulator_init_data *db8500_init_data = mfd_get_data(pdev); + int i, err; + + /* register all regulators */ + for (i = 0; i < ARRAY_SIZE(db8500_regulator_info); i++) { + struct db8500_regulator_info *info; + struct regulator_init_data *init_data = &db8500_init_data[i]; + + /* assign per-regulator data */ + info = &db8500_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 = &db8500_regulator_info[i]; + regulator_unregister(info->rdev); + i--; + } + return err; + } + + dev_dbg(rdev_get_dev(info->rdev), + "regulator-%s-probed\n", info->desc.name); + } + + return 0; +} + +static int __exit db8500_regulator_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(db8500_regulator_info); i++) { + struct db8500_regulator_info *info; + info = &db8500_regulator_info[i]; + + dev_vdbg(rdev_get_dev(info->rdev), + "regulator-%s-remove\n", info->desc.name); + + regulator_unregister(info->rdev); + } + + return 0; +} + +static struct platform_driver db8500_regulator_driver = { + .driver = { + .name = "db8500-prcmu-regulators", + .owner = THIS_MODULE, + }, + .probe = db8500_regulator_probe, + .remove = __exit_p(db8500_regulator_remove), +}; + +static int __init db8500_regulator_init(void) +{ + int ret; + + ret = platform_driver_register(&db8500_regulator_driver); + if (ret < 0) + return -ENODEV; + + return 0; +} + +static void __exit db8500_regulator_exit(void) +{ + platform_driver_unregister(&db8500_regulator_driver); +} + +arch_initcall(db8500_regulator_init); +module_exit(db8500_regulator_exit); + +MODULE_AUTHOR("STMicroelectronics/ST-Ericsson"); +MODULE_DESCRIPTION("DB8500 regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h new file mode 100644 index 00000000000..f0977986402 --- /dev/null +++ b/include/linux/mfd/db5500-prcmu.h @@ -0,0 +1,45 @@ +/* + * 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_UX500_SOC_DB5500 + +void db5500_prcmu_early_init(void); + +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); + +#else /* !CONFIG_UX500_SOC_DB5500 */ + +static inline void db5500_prcmu_early_init(void) +{ +} + +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; +} + +#endif /* CONFIG_UX500_SOC_DB5500 */ + +static inline int db5500_prcmu_config_abb_event_readout(u32 abb_events) +{ +#ifdef CONFIG_MACH_U5500_SIMULATOR + return 0; +#else + return -1; +#endif +} + +#endif /* __MACH_PRCMU_U5500_H */ diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h new file mode 100644 index 00000000000..917dbcab701 --- /dev/null +++ b/include/linux/mfd/db8500-prcmu.h @@ -0,0 +1,978 @@ +/* + * 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 __MFD_DB8500_PRCMU_H +#define __MFD_DB8500_PRCMU_H + +#include <linux/interrupt.h> +#include <linux/notifier.h> + +/* This portion previously known as <mach/prcmu-fw-defs_v1.h> */ + +/** + * enum state - ON/OFF state definition + * @OFF: State is ON + * @ON: State is OFF + * + */ +enum state { + OFF = 0x0, + ON = 0x1, +}; + +/** + * enum ret_state - general purpose On/Off/Retention states + * + */ +enum ret_state { + OFFST = 0, + ONST = 1, + RETST = 2 +}; + +/** + * enum clk_arm - ARM Cortex A9 clock schemes + * @A9_OFF: + * @A9_BOOT: + * @A9_OPPT1: + * @A9_OPPT2: + * @A9_EXTCLK: + */ +enum clk_arm { + A9_OFF, + A9_BOOT, + A9_OPPT1, + A9_OPPT2, + A9_EXTCLK +}; + +/** + * enum clk_gen - GEN#0/GEN#1 clock schemes + * @GEN_OFF: + * @GEN_BOOT: + * @GEN_OPPT1: + */ +enum clk_gen { + GEN_OFF, + GEN_BOOT, + GEN_OPPT1, +}; + +/* some information between arm and xp70 */ + +/** + * 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 +}; + +/** + * enum ap_pwrst_trans - Transition states defined in PRCMU firmware + * @NO_TRANSITION: No power state transition + * @APEXECUTE_TO_APSLEEP: Power state transition from ApExecute to ApSleep + * @APIDLE_TO_APSLEEP: Power state transition from ApIdle to ApSleep + * @APBOOT_TO_APEXECUTE: Power state transition from ApBoot to ApExecute + * @APEXECUTE_TO_APDEEPSLEEP: Power state transition from ApExecute to + * ApDeepSleep + * @APEXECUTE_TO_APIDLE: Power state transition from ApExecute to ApIdle + */ +enum ap_pwrst_trans { + NO_TRANSITION = 0x00, + APEXECUTE_TO_APSLEEP = 0x01, + APIDLE_TO_APSLEEP = 0x02, /* To be removed */ + PRCMU_AP_SLEEP = 0x01, + APBOOT_TO_APEXECUTE = 0x03, + APEXECUTE_TO_APDEEPSLEEP = 0x04, /* To be removed */ + PRCMU_AP_DEEP_SLEEP = 0x04, + APEXECUTE_TO_APIDLE = 0x05, /* To be removed */ + PRCMU_AP_IDLE = 0x05, + PRCMU_AP_DEEP_IDLE = 0x07, +}; + +/** + * enum ddr_pwrst - DDR power states definition + * @DDR_PWR_STATE_UNCHANGED: SDRAM and DDR controller state is unchanged + * @DDR_PWR_STATE_ON: + * @DDR_PWR_STATE_OFFLOWLAT: + * @DDR_PWR_STATE_OFFHIGHLAT: + */ +enum ddr_pwrst { + DDR_PWR_STATE_UNCHANGED = 0x00, + DDR_PWR_STATE_ON = 0x01, + DDR_PWR_STATE_OFFLOWLAT = 0x02, + DDR_PWR_STATE_OFFHIGHLAT = 0x03 +}; + +/** + * enum arm_opp - ARM OPP states definition + * @ARM_OPP_INIT: + * @ARM_NO_CHANGE: The ARM operating point is unchanged + * @ARM_100_OPP: The new ARM operating point is arm100opp + * @ARM_50_OPP: The new ARM operating point is arm50opp + * @ARM_MAX_OPP: Operating point is "max" (more than 100) + * @ARM_MAX_FREQ100OPP: Set max opp if available, else 100 + * @ARM_EXTCLK: The new ARM operating point is armExtClk + */ +enum arm_opp { + ARM_OPP_INIT = 0x00, + ARM_NO_CHANGE = 0x01, + ARM_100_OPP = 0x02, + ARM_50_OPP = 0x03, + ARM_MAX_OPP = 0x04, + ARM_MAX_FREQ100OPP = 0x05, + ARM_EXTCLK = 0x07 +}; + +/** + * enum ape_opp - APE OPP states definition + * @APE_OPP_INIT: + * @APE_NO_CHANGE: The APE operating point is unchanged + * @APE_100_OPP: The new APE operating point is ape100opp + * @APE_50_OPP: 50% + */ +enum ape_opp { + APE_OPP_INIT = 0x00, + APE_NO_CHANGE = 0x01, + APE_100_OPP = 0x02, + APE_50_OPP = 0x03 +}; + +/** + * 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 mbox_2_arm_stat - Status messages definition for mbox_arm + * @BOOT_TO_EXECUTEOK: The apBoot to apExecute state transition has been + * completed + * @DEEPSLEEPOK: The apExecute to apDeepSleep state transition has been + * completed + * @SLEEPOK: The apExecute to apSleep state transition has been completed + * @IDLEOK: The apExecute to apIdle state transition has been completed + * @SOFTRESETOK: The A9 watchdog/ SoftReset state has been completed + * @SOFTRESETGO : The A9 watchdog/SoftReset state is on going + * @BOOT_TO_EXECUTE: The apBoot to apExecute state transition is on going + * @EXECUTE_TO_DEEPSLEEP: The apExecute to apDeepSleep state transition is on + * going + * @DEEPSLEEP_TO_EXECUTE: The apDeepSleep to apExecute state transition is on + * going + * @DEEPSLEEP_TO_EXECUTEOK: The apDeepSleep to apExecute state transition has + * been completed + * @EXECUTE_TO_SLEEP: The apExecute to apSleep state transition is on going + * @SLEEP_TO_EXECUTE: The apSleep to apExecute state transition is on going + * @SLEEP_TO_EXECUTEOK: The apSleep to apExecute state transition has been + * completed + * @EXECUTE_TO_IDLE: The apExecute to apIdle state transition is on going + * @IDLE_TO_EXECUTE: The apIdle to apExecute state transition is on going + * @IDLE_TO_EXECUTEOK: The apIdle to apExecute state transition has been + * completed + * @INIT_STATUS: Status init + */ +enum ap_pwrsttr_status { + BOOT_TO_EXECUTEOK = 0xFF, + DEEPSLEEPOK = 0xFE, + SLEEPOK = 0xFD, + IDLEOK = 0xFC, + SOFTRESETOK = 0xFB, + SOFTRESETGO = 0xFA, + BOOT_TO_EXECUTE = 0xF9, + EXECUTE_TO_DEEPSLEEP = 0xF8, + DEEPSLEEP_TO_EXECUTE = 0xF7, + DEEPSLEEP_TO_EXECUTEOK = 0xF6, + EXECUTE_TO_SLEEP = 0xF5, + SLEEP_TO_EXECUTE = 0xF4, + SLEEP_TO_EXECUTEOK = 0xF3, + EXECUTE_TO_IDLE = 0xF2, + IDLE_TO_EXECUTE = 0xF1, + IDLE_TO_EXECUTEOK = 0xF0, + RDYTODS_RETURNTOEXE = 0xEF, + NORDYTODS_RETURNTOEXE = 0xEE, + EXETOSLEEP_RETURNTOEXE = 0xED, + EXETOIDLE_RETURNTOEXE = 0xEC, + INIT_STATUS = 0xEB, + + /*error messages */ + INITERROR = 0x00, + PLLARMLOCKP_ER = 0x01, + PLLDDRLOCKP_ER = 0x02, + PLLSOCLOCKP_ER = 0x03, + PLLSOCK1LOCKP_ER = 0x04, + ARMWFI_ER = 0x05, + SYSCLKOK_ER = 0x06, + I2C_NACK_DATA_ER = 0x07, + BOOT_ER = 0x08, + I2C_STATUS_ALWAYS_1 = 0x0A, + I2C_NACK_REG_ADDR_ER = 0x0B, + I2C_NACK_DATA0123_ER = 0x1B, + I2C_NACK_ADDR_ER = 0x1F, + CURAPPWRSTISNOT_BOOT = 0x20, + CURAPPWRSTISNOT_EXECUTE = 0x21, + CURAPPWRSTISNOT_SLEEPMODE = 0x22, + CURAPPWRSTISNOT_CORRECTFORIT10 = 0x23, + FIFO4500WUISNOT_WUPEVENT = 0x24, + PLL32KLOCKP_ER = 0x29, + DDRDEEPSLEEPOK_ER = 0x2A, + ROMCODEREADY_ER = 0x50, + WUPBEFOREDS = 0x51, + DDRCONFIG_ER = 0x52, + WUPBEFORESLEEP = 0x53, + WUPBEFOREIDLE = 0x54 +}; /* earlier called as mbox_2_arm_stat */ + +/** + * enum dvfs_stat - DVFS status messages definition + * @DVFS_GO: A state transition DVFS is on going + * @DVFS_ARM100OPPOK: The state transition DVFS has been completed for 100OPP + * @DVFS_ARM50OPPOK: The state transition DVFS has been completed for 50OPP + * @DVFS_ARMEXTCLKOK: The state transition DVFS has been completed for EXTCLK + * @DVFS_NOCHGTCLKOK: The state transition DVFS has been completed for + * NOCHGCLK + * @DVFS_INITSTATUS: Value init + */ +enum dvfs_stat { + DVFS_GO = 0xFF, + DVFS_ARM100OPPOK = 0xFE, + DVFS_ARM50OPPOK = 0xFD, + DVFS_ARMEXTCLKOK = 0xFC, + DVFS_NOCHGTCLKOK = 0xFB, + DVFS_INITSTATUS = 0x00 +}; + +/** + * enum sva_mmdsp_stat - SVA MMDSP status messages + * @SVA_MMDSP_GO: SVAMMDSP interrupt has happened + * @SVA_MMDSP_INIT: Status init + */ +enum sva_mmdsp_stat { + SVA_MMDSP_GO = 0xFF, + SVA_MMDSP_INIT = 0x00 +}; + +/** + * enum sia_mmdsp_stat - SIA MMDSP status messages + * @SIA_MMDSP_GO: SIAMMDSP interrupt has happened + * @SIA_MMDSP_INIT: Status init + */ +enum sia_mmdsp_stat { + SIA_MMDSP_GO = 0xFF, + SIA_MMDSP_INIT = 0x00 +}; + +/** + * enum mbox_to_arm_err - Error messages definition + * @INIT_ERR: Init value + * @PLLARMLOCKP_ERR: PLLARM has not been correctly locked in given time + * @PLLDDRLOCKP_ERR: PLLDDR has not been correctly locked in the given time + * @PLLSOC0LOCKP_ERR: PLLSOC0 has not been correctly locked in the given time + * @PLLSOC1LOCKP_ERR: PLLSOC1 has not been correctly locked in the given time + * @ARMWFI_ERR: The ARM WFI has not been correctly executed in the given time + * @SYSCLKOK_ERR: The SYSCLK is not available in the given time + * @BOOT_ERR: Romcode has not validated the XP70 self reset in the given time + * @ROMCODESAVECONTEXT: The Romcode didn.t correctly save it secure context + * @VARMHIGHSPEEDVALTO_ERR: The ARM high speed supply value transfered + * through I2C has not been correctly executed in the given time + * @VARMHIGHSPEEDACCESS_ERR: The command value of VarmHighSpeedVal transfered + * through I2C has not been correctly executed in the given time + * @VARMLOWSPEEDVALTO_ERR:The ARM low speed supply value transfered through + * I2C has not been correctly executed in the given time + * @VARMLOWSPEEDACCESS_ERR: The command value of VarmLowSpeedVal transfered + * through I2C has not been correctly executed in the given time + * @VARMRETENTIONVALTO_ERR: The ARM retention supply value transfered through + * I2C has not been correctly executed in the given time + * @VARMRETENTIONACCESS_ERR: The command value of VarmRetentionVal transfered + * through I2C has not been correctly executed in the given time + * @VAPEHIGHSPEEDVALTO_ERR: The APE highspeed supply value transfered through + * I2C has not been correctly executed in the given time + * @VSAFEHPVALTO_ERR: The SAFE high power supply value transfered through I2C + * has not been correctly executed in the given time + * @VMODSEL1VALTO_ERR: The MODEM sel1 supply value transfered through I2C has + * not been correctly executed in the given time + * @VMODSEL2VALTO_ERR: The MODEM sel2 supply value transfered through I2C has + * not been correctly executed in the given time + * @VARMOFFACCESS_ERR: The command value of Varm ON/OFF transfered through + * I2C has not been correctly executed in the given time + * @VAPEOFFACCESS_ERR: The command value of Vape ON/OFF transfered through + * I2C has not been correctly executed in the given time + * @VARMRETACCES_ERR: The command value of Varm retention ON/OFF transfered + * through I2C has not been correctly executed in the given time + * @CURAPPWRSTISNOTBOOT:Generated when Arm want to do power state transition + * ApBoot to ApExecute but the power current state is not Apboot + * @CURAPPWRSTISNOTEXECUTE: Generated when Arm want to do power state + * transition from ApExecute to others power state but the + * power current state is not ApExecute + * @CURAPPWRSTISNOTSLEEPMODE: Generated when wake up events are transmitted + * but the power current state is not ApDeepSleep/ApSleep/ApIdle + * @CURAPPWRSTISNOTCORRECTDBG: Generated when wake up events are transmitted + * but the power current state is not correct + * @ARMREGU1VALTO_ERR:The ArmRegu1 value transferred through I2C has not + * been correctly executed in the given time + * @ARMREGU2VALTO_ERR: The ArmRegu2 value transferred through I2C has not + * been correctly executed in the given time + * @VAPEREGUVALTO_ERR: The VApeRegu value transfered through I2C has not + * been correctly executed in the given time + * @VSMPS3REGUVALTO_ERR: The VSmps3Regu value transfered through I2C has not + * been correctly executed in the given time + * @VMODREGUVALTO_ERR: The VModemRegu value transfered through I2C has not + * been correctly executed in the given time + */ +enum mbox_to_arm_err { + INIT_ERR = 0x00, + PLLARMLOCKP_ERR = 0x01, + PLLDDRLOCKP_ERR = 0x02, + PLLSOC0LOCKP_ERR = 0x03, + PLLSOC1LOCKP_ERR = 0x04, + ARMWFI_ERR = 0x05, + SYSCLKOK_ERR = 0x06, + BOOT_ERR = 0x07, + ROMCODESAVECONTEXT = 0x08, + VARMHIGHSPEEDVALTO_ERR = 0x10, + VARMHIGHSPEEDACCESS_ERR = 0x11, + VARMLOWSPEEDVALTO_ERR = 0x12, + VARMLOWSPEEDACCESS_ERR = 0x13, + VARMRETENTIONVALTO_ERR = 0x14, + VARMRETENTIONACCESS_ERR = 0x15, + VAPEHIGHSPEEDVALTO_ERR = 0x16, + VSAFEHPVALTO_ERR = 0x17, + VMODSEL1VALTO_ERR = 0x18, + VMODSEL2VALTO_ERR = 0x19, + VARMOFFACCESS_ERR = 0x1A, + VAPEOFFACCESS_ERR = 0x1B, + VARMRETACCES_ERR = 0x1C, + CURAPPWRSTISNOTBOOT = 0x20, + CURAPPWRSTISNOTEXECUTE = 0x21, + CURAPPWRSTISNOTSLEEPMODE = 0x22, + CURAPPWRSTISNOTCORRECTDBG = 0x23, + ARMREGU1VALTO_ERR = 0x24, + ARMREGU2VALTO_ERR = 0x25, + VAPEREGUVALTO_ERR = 0x26, + VSMPS3REGUVALTO_ERR = 0x27, + VMODREGUVALTO_ERR = 0x28 +}; + +enum hw_acc { + SVAMMDSP = 0, + SVAPIPE = 1, + SIAMMDSP = 2, + SIAPIPE = 3, + SGA = 4, + B2R2MCDE = 5, + ESRAM12 = 6, + ESRAM34 = 7, +}; + +enum cs_pwrmgt { + PWRDNCS0 = 0, + WKUPCS0 = 1, + PWRDNCS1 = 2, + WKUPCS1 = 3 +}; + +/* Defs related to autonomous power management */ + +/** + * enum sia_sva_pwr_policy - Power policy + * @NO_CHGT: No change + * @DSPOFF_HWPOFF: + * @DSPOFFRAMRET_HWPOFF: + * @DSPCLKOFF_HWPOFF: + * @DSPCLKOFF_HWPCLKOFF: + * + */ +enum sia_sva_pwr_policy { + NO_CHGT = 0x0, + DSPOFF_HWPOFF = 0x1, + DSPOFFRAMRET_HWPOFF = 0x2, + DSPCLKOFF_HWPOFF = 0x3, + DSPCLKOFF_HWPCLKOFF = 0x4, +}; + +/** + * enum auto_enable - Auto Power enable + * @AUTO_OFF: + * @AUTO_ON: + * + */ +enum auto_enable { + AUTO_OFF = 0x0, + AUTO_ON = 0x1, +}; + +/* End of file previously known as prcmu-fw-defs_v1.h */ + +/* PRCMU Wakeup defines */ +enum prcmu_wakeup_index { + PRCMU_WAKEUP_INDEX_RTC, + PRCMU_WAKEUP_INDEX_RTT0, + PRCMU_WAKEUP_INDEX_RTT1, + PRCMU_WAKEUP_INDEX_HSI0, + PRCMU_WAKEUP_INDEX_HSI1, + PRCMU_WAKEUP_INDEX_USB, + PRCMU_WAKEUP_INDEX_ABB, + PRCMU_WAKEUP_INDEX_ABB_FIFO, + PRCMU_WAKEUP_INDEX_ARM, + NUM_PRCMU_WAKEUP_INDICES +}; +#define PRCMU_WAKEUP(_name) (BIT(PRCMU_WAKEUP_INDEX_##_name)) + +/* PRCMU QoS APE OPP class */ +#define PRCMU_QOS_APE_OPP 1 +#define PRCMU_QOS_DDR_OPP 2 +#define PRCMU_QOS_DEFAULT_VALUE -1 + +/** + * 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 +}; + +/* + * Ids for all EPODs (power domains) + * - EPOD_ID_SVAMMDSP: power domain for SVA MMDSP + * - EPOD_ID_SVAPIPE: power domain for SVA pipe + * - EPOD_ID_SIAMMDSP: power domain for SIA MMDSP + * - EPOD_ID_SIAPIPE: power domain for SIA pipe + * - EPOD_ID_SGA: power domain for SGA + * - EPOD_ID_B2R2_MCDE: power domain for B2R2 and MCDE + * - EPOD_ID_ESRAM12: power domain for ESRAM 1 and 2 + * - EPOD_ID_ESRAM34: power domain for ESRAM 3 and 4 + * - NUM_EPOD_ID: number of power domains + */ +#define EPOD_ID_SVAMMDSP 0 +#define EPOD_ID_SVAPIPE 1 +#define EPOD_ID_SIAMMDSP 2 +#define EPOD_ID_SIAPIPE 3 +#define EPOD_ID_SGA 4 +#define EPOD_ID_B2R2_MCDE 5 +#define EPOD_ID_ESRAM12 6 +#define EPOD_ID_ESRAM34 7 +#define NUM_EPOD_ID 8 + +/* + * state definition for EPOD (power domain) + * - EPOD_STATE_NO_CHANGE: The EPOD should remain unchanged + * - EPOD_STATE_OFF: The EPOD is switched off + * - EPOD_STATE_RAMRET: The EPOD is switched off with its internal RAM in + * retention + * - EPOD_STATE_ON_CLK_OFF: The EPOD is switched on, clock is still off + * - EPOD_STATE_ON: Same as above, but with clock enabled + */ +#define EPOD_STATE_NO_CHANGE 0x00 +#define EPOD_STATE_OFF 0x01 +#define EPOD_STATE_RAMRET 0x02 +#define EPOD_STATE_ON_CLK_OFF 0x03 +#define EPOD_STATE_ON 0x04 + +/* + * CLKOUT sources + */ +#define PRCMU_CLKSRC_CLK38M 0x00 +#define PRCMU_CLKSRC_ACLK 0x01 +#define PRCMU_CLKSRC_SYSCLK 0x02 +#define PRCMU_CLKSRC_LCDCLK 0x03 +#define PRCMU_CLKSRC_SDMMCCLK 0x04 +#define PRCMU_CLKSRC_TVCLK 0x05 +#define PRCMU_CLKSRC_TIMCLK 0x06 +#define PRCMU_CLKSRC_CLK009 0x07 +/* These are only valid for CLKOUT1: */ +#define PRCMU_CLKSRC_SIAMMDSPCLK 0x40 +#define PRCMU_CLKSRC_I2CCLK 0x41 +#define PRCMU_CLKSRC_MSP02CLK 0x42 +#define PRCMU_CLKSRC_ARMPLL_OBSCLK 0x43 +#define PRCMU_CLKSRC_HSIRXCLK 0x44 +#define PRCMU_CLKSRC_HSITXCLK 0x45 +#define PRCMU_CLKSRC_ARMCLKFIX 0x46 +#define PRCMU_CLKSRC_HDMICLK 0x47 + +/* + * 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 ddr_opp - DDR OPP states definition + * @DDR_100_OPP: The new DDR operating point is ddr100opp + * @DDR_50_OPP: The new DDR operating point is ddr50opp + * @DDR_25_OPP: The new DDR operating point is ddr25opp + */ +enum ddr_opp { + DDR_100_OPP = 0x00, + DDR_50_OPP = 0x01, + DDR_25_OPP = 0x02, +}; + +/* + * Clock identifiers. + */ +enum prcmu_clock { + PRCMU_SGACLK, + PRCMU_UARTCLK, + PRCMU_MSP02CLK, + PRCMU_MSP1CLK, + PRCMU_I2CCLK, + PRCMU_SDMMCCLK, + PRCMU_SLIMCLK, + PRCMU_PER1CLK, + PRCMU_PER2CLK, + PRCMU_PER3CLK, + PRCMU_PER5CLK, + PRCMU_PER6CLK, + PRCMU_PER7CLK, + PRCMU_LCDCLK, + PRCMU_BMLCLK, + PRCMU_HSITXCLK, + PRCMU_HSIRXCLK, + PRCMU_HDMICLK, + PRCMU_APEATCLK, + PRCMU_APETRACECLK, + PRCMU_MCDECLK, + PRCMU_IPI2CCLK, + PRCMU_DSIALTCLK, + PRCMU_DMACLK, + PRCMU_B2R2CLK, + PRCMU_TVCLK, + PRCMU_SSPCLK, + PRCMU_RNGCLK, + PRCMU_UICCCLK, + PRCMU_NUM_REG_CLOCKS, + PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS, + PRCMU_TIMCLK, +}; + +/* + * Definitions for controlling ESRAM0 in deep sleep. + */ +#define ESRAM0_DEEP_SLEEP_STATE_OFF 1 +#define ESRAM0_DEEP_SLEEP_STATE_RET 2 + +#ifdef CONFIG_MFD_DB8500_PRCMU +void __init prcmu_early_init(void); +int prcmu_set_display_clocks(void); +int prcmu_disable_dsipll(void); +int prcmu_enable_dsipll(void); +#else +static inline void __init prcmu_early_init(void) {} +#endif + +#ifdef CONFIG_MFD_DB8500_PRCMU + +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); +int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); + +void prcmu_enable_wakeups(u32 wakeups); +static inline void prcmu_disable_wakeups(void) +{ + prcmu_enable_wakeups(0); +} + +void prcmu_config_abb_event_readout(u32 abb_events); +void prcmu_get_abb_event_buffer(void __iomem **buf); +int prcmu_set_arm_opp(u8 opp); +int prcmu_get_arm_opp(void); +bool prcmu_has_arm_maxopp(void); +bool prcmu_is_u8400(void); +int prcmu_set_ape_opp(u8 opp); +int prcmu_get_ape_opp(void); +int prcmu_request_ape_opp_100_voltage(bool enable); +int prcmu_release_usb_wakeup_state(void); +int prcmu_set_ddr_opp(u8 opp); +int prcmu_get_ddr_opp(void); +unsigned long prcmu_qos_get_cpufreq_opp_delay(void); +void prcmu_qos_set_cpufreq_opp_delay(unsigned long); +/* NOTE! Use regulator framework instead */ +int prcmu_set_hwacc(u16 hw_acc_dev, u8 state); +int prcmu_set_epod(u16 epod_id, u8 epod_state); +void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep, + struct prcmu_auto_pm_config *idle); +bool prcmu_is_auto_pm_enabled(void); + +int prcmu_config_clkout(u8 clkout, u8 source, u8 div); +int prcmu_request_clock(u8 clock, bool enable); +int prcmu_set_clock_divider(u8 clock, u8 divider); +int prcmu_config_esram0_deep_sleep(u8 state); +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); +int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); +int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); + +void prcmu_ac_wake_req(void); +void prcmu_ac_sleep_req(void); +void prcmu_system_reset(u16 reset_code); +void prcmu_modem_reset(void); +bool prcmu_is_ac_wake_requested(void); +void prcmu_enable_spi2(void); +void prcmu_disable_spi2(void); + +#else /* !CONFIG_MFD_DB8500_PRCMU */ + +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 int prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) +{ + return 0; +} + +static inline void prcmu_enable_wakeups(u32 wakeups) {} + +static inline void prcmu_disable_wakeups(void) {} + +static inline void prcmu_config_abb_event_readout(u32 abb_events) {} + +static inline int prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int prcmu_get_arm_opp(void) +{ + return ARM_100_OPP; +} + +static bool prcmu_has_arm_maxopp(void) +{ + return false; +} + +static bool prcmu_is_u8400(void) +{ + return false; +} + +static inline int prcmu_set_ape_opp(u8 opp) +{ + return 0; +} + +static inline int prcmu_get_ape_opp(void) +{ + return APE_100_OPP; +} + +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_ddr_opp(u8 opp) +{ + return 0; +} + +static inline int prcmu_get_ddr_opp(void) +{ + return DDR_100_OPP; +} + +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 int prcmu_set_hwacc(u16 hw_acc_dev, u8 state) +{ + 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_config_clkout(u8 clkout, u8 source, u8 div) +{ + return 0; +} + +static inline int prcmu_request_clock(u8 clock, bool enable) +{ + return 0; +} + +static inline int prcmu_set_clock_divider(u8 clock, u8 divider) +{ + return 0; +} + +int prcmu_config_esram0_deep_sleep(u8 state) +{ + return 0; +} + +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_abb_read(u8 slave, u8 reg, u8 *value, u8 size) +{ + return -ENOSYS; +} + +static inline int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +{ + return -ENOSYS; +} + +static inline void prcmu_ac_wake_req(void) {} + +static inline void prcmu_ac_sleep_req(void) {} + +static inline void prcmu_system_reset(u16 reset_code) {} + +static inline void prcmu_modem_reset(void) {} + +static inline bool prcmu_is_ac_wake_requested(void) +{ + return false; +} + +#ifndef CONFIG_UX500_SOC_DB5500 +static inline int prcmu_set_display_clocks(void) +{ + return 0; +} + +static inline int prcmu_disable_dsipll(void) +{ + return 0; +} + +static inline int prcmu_enable_dsipll(void) +{ + return 0; +} +#endif + +static inline int prcmu_enable_spi2(void) +{ + return 0; +} + +static inline int prcmu_disable_spi2(void) +{ + return 0; +} + +#endif /* !CONFIG_MFD_DB8500_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 /* __MFD_DB8500_PRCMU_H */ diff --git a/include/linux/regulator/db8500-prcmu.h b/include/linux/regulator/db8500-prcmu.h new file mode 100644 index 00000000000..612062313b6 --- /dev/null +++ b/include/linux/regulator/db8500-prcmu.h @@ -0,0 +1,45 @@ +/* + * 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 power domain regulators on DB8500 + */ + +#ifndef __REGULATOR_H__ +#define __REGULATOR_H__ + +/* Number of DB8500 regulators and regulator enumeration */ +enum db8500_regulator_id { + DB8500_REGULATOR_VAPE, + DB8500_REGULATOR_VARM, + DB8500_REGULATOR_VMODEM, + DB8500_REGULATOR_VPLL, + DB8500_REGULATOR_VSMPS1, + DB8500_REGULATOR_VSMPS2, + DB8500_REGULATOR_VSMPS3, + DB8500_REGULATOR_VRF1, + DB8500_REGULATOR_SWITCH_SVAMMDSP, + DB8500_REGULATOR_SWITCH_SVAMMDSPRET, + DB8500_REGULATOR_SWITCH_SVAPIPE, + DB8500_REGULATOR_SWITCH_SIAMMDSP, + DB8500_REGULATOR_SWITCH_SIAMMDSPRET, + DB8500_REGULATOR_SWITCH_SIAPIPE, + DB8500_REGULATOR_SWITCH_SGA, + DB8500_REGULATOR_SWITCH_B2R2_MCDE, + DB8500_REGULATOR_SWITCH_ESRAM12, + DB8500_REGULATOR_SWITCH_ESRAM12RET, + DB8500_REGULATOR_SWITCH_ESRAM34, + DB8500_REGULATOR_SWITCH_ESRAM34RET, + DB8500_NUM_REGULATORS +}; + +/* + * Exported interface for CPUIdle only. This function is called with all + * interrupts turned off. + */ +int power_state_active_is_enabled(void); + +#endif |