From c2a6e471cfcad3fab3ff5c089acbcbda3c3cb38a Mon Sep 17 00:00:00 2001 From: Shreshtha Kumar Sahu Date: Fri, 17 Jun 2011 18:46:54 +0530 Subject: u5500: add mailbox1 and related function support Add cpufreq-db5500.c file for db5500 CPUfreq support. PRCMU mailbox1 and related functions' support is added. List of functions implemented: - prcmu_get/set_arm_opp - read_mailbox_1 ST-Ericsson Linux next: - ST-Ericsson ID: WP334774 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I6dabc714dac70d4302450a2216644dc63ae88084 Signed-off-by: Shreshtha Kumar Sahu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24956 Reviewed-by: QATEST Reviewed-by: Linus WALLEIJ Reviewed-by: Per-Inge TALLBERG Reviewed-by: Jonas ABERG Fix for PRCMU --- arch/arm/mach-ux500/include/mach/prcmu-db5500.h | 31 +++++ arch/arm/mach-ux500/include/mach/prcmu-db8500.h | 19 +++ arch/arm/mach-ux500/include/mach/prcmu.h | 26 ++++- arch/arm/mach-ux500/pm/Kconfig | 8 +- arch/arm/mach-ux500/pm/Makefile | 8 +- arch/arm/mach-ux500/pm/cpufreq-db5500.c | 51 ++++++++ arch/arm/mach-ux500/pm/cpufreq.c | 34 +++--- arch/arm/mach-ux500/prcmu-db5500.c | 149 +++++++++++++++++++++++- arch/arm/mach-ux500/prcmu-db8500.c | 10 +- 9 files changed, 303 insertions(+), 33 deletions(-) create mode 100644 arch/arm/mach-ux500/pm/cpufreq-db5500.c diff --git a/arch/arm/mach-ux500/include/mach/prcmu-db5500.h b/arch/arm/mach-ux500/include/mach/prcmu-db5500.h index 6e786bf0522..97f0f5e7e68 100644 --- a/arch/arm/mach-ux500/include/mach/prcmu-db5500.h +++ b/arch/arm/mach-ux500/include/mach/prcmu-db5500.h @@ -28,6 +28,11 @@ int db5500_prcmu_config_esram0_deep_sleep(u8 state); static inline void db5500_prcmu_system_reset(u16 reset_code) {} +u16 db5500_prcmu_get_reset_code(void); +bool db5500_prcmu_is_ac_wake_requested(void); +int db5500_prcmu_set_arm_opp(u8 opp); +int db5500_prcmu_get_arm_opp(void); + #else /* !CONFIG_UX500_SOC_DB5500 */ static inline void db5500_prcmu_early_init(void) @@ -44,6 +49,11 @@ static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) return -ENOSYS; } +static inline int db5500_prcmu_request_clock(u8 clock, bool enable) +{ + return 0; +} + static inline int db5500_prcmu_set_display_clocks(void) { return 0; @@ -85,6 +95,27 @@ static inline int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, return 0; } +static inline u16 db5500_prcmu_get_reset_code(void) +{ + return 0; +} + +static inline bool db5500_prcmu_is_ac_wake_requested(void) +{ + return 0; +} + +static inline int db5500_prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int db5500_prcmu_get_arm_opp(void) +{ + return 0; +} + + #endif /* CONFIG_UX500_SOC_DB5500 */ #endif /* __MACH_PRCMU_U5500_H */ diff --git a/arch/arm/mach-ux500/include/mach/prcmu-db8500.h b/arch/arm/mach-ux500/include/mach/prcmu-db8500.h index 659f9205d2e..4545fb9b2cb 100644 --- a/arch/arm/mach-ux500/include/mach/prcmu-db8500.h +++ b/arch/arm/mach-ux500/include/mach/prcmu-db8500.h @@ -204,6 +204,10 @@ int db8500_prcmu_enable_dsipll(void); void db8500_prcmu_config_abb_event_readout(u32 abb_events); void db8500_prcmu_get_abb_event_buffer(void __iomem **buf); int db8500_prcmu_config_esram0_deep_sleep(u8 state); +u16 db8500_prcmu_get_reset_code(void); +bool db8500_prcmu_is_ac_wake_requested(void); +int db8500_prcmu_set_arm_opp(u8 opp); +int db8500_prcmu_get_arm_opp(void); #else /* !CONFIG_UX500_SOC_DB8500 */ @@ -360,6 +364,21 @@ static inline int prcmu_load_a9wdog(u8 id, u32 val) return 0; } +static inline bool db8500_prcmu_is_ac_wake_requested(void) +{ + return 0; +} + +static inline int db8500_prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int db8500_prcmu_get_arm_opp(void) +{ + return 0; +} + #endif /* CONFIG_UX500_SOC_DB8500 */ #endif /* __MACH_PRCMU_DB8500_H */ diff --git a/arch/arm/mach-ux500/include/mach/prcmu.h b/arch/arm/mach-ux500/include/mach/prcmu.h index bf26f0c3408..802daf8f060 100644 --- a/arch/arm/mach-ux500/include/mach/prcmu.h +++ b/arch/arm/mach-ux500/include/mach/prcmu.h @@ -290,11 +290,25 @@ static inline int prcmu_request_clock(u8 clock, bool enable) int prcmu_set_ape_opp(u8 opp); int prcmu_get_ape_opp(void); -int prcmu_set_arm_opp(u8 opp); -int prcmu_get_arm_opp(void); int prcmu_set_ddr_opp(u8 opp); int prcmu_get_ddr_opp(void); +static inline int prcmu_set_arm_opp(u8 opp) +{ + if (machine_is_u5500()) + return db5500_prcmu_set_arm_opp(opp); + else + return db8500_prcmu_set_arm_opp(opp); +} + +static inline int prcmu_get_arm_opp(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_get_arm_opp(); + else + return db8500_prcmu_get_arm_opp(); +} + static inline void prcmu_system_reset(u16 reset_code) { if (machine_is_u5500()) @@ -308,7 +322,13 @@ u16 prcmu_get_reset_code(void); void prcmu_ac_wake_req(void); void prcmu_ac_sleep_req(void); void prcmu_modem_reset(void); -bool prcmu_is_ac_wake_requested(void); +static inline bool prcmu_is_ac_wake_requested(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_is_ac_wake_requested(); + else + return db8500_prcmu_is_ac_wake_requested(); +} static inline int prcmu_set_display_clocks(void) { diff --git a/arch/arm/mach-ux500/pm/Kconfig b/arch/arm/mach-ux500/pm/Kconfig index d6454eab2a5..bdfeb2fe02a 100644 --- a/arch/arm/mach-ux500/pm/Kconfig +++ b/arch/arm/mach-ux500/pm/Kconfig @@ -1,9 +1,11 @@ -config U8500_CPUFREQ +config UX500_CPUFREQ tristate "CPUFreq support" - depends on UX500_SOC_DB8500 && CPU_FREQ && PM + depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && CPU_FREQ && PM default y help - Add support for CPU Frequency scaling for U8500. + Add support for CPU Frequency scaling for Ux500 SOCs. + ARM power domains operating points can be set + dynamically. It depends on CPUfreq and PM subsystem. config U8500_CPUIDLE tristate "CPUIdle support" diff --git a/arch/arm/mach-ux500/pm/Makefile b/arch/arm/mach-ux500/pm/Makefile index 6b435576560..530c4a02571 100644 --- a/arch/arm/mach-ux500/pm/Makefile +++ b/arch/arm/mach-ux500/pm/Makefile @@ -6,10 +6,14 @@ obj-y := pm.o runtime.o obj-$(CONFIG_U8500_CPUIDLE) += cpuidle.o timer.o obj-$(CONFIG_U8500_CPUIDLE_DEBUG) += cpuidle_dbg.o obj-$(CONFIG_UX500_CONTEXT) += context.o context_arm.o context-db8500.o context-db5500.o -obj-$(CONFIG_U8500_CPUFREQ) += cpufreq.o +obj-$(CONFIG_UX500_CPUFREQ) += cpufreq.o obj-$(CONFIG_UX500_SUSPEND) += suspend.o obj-$(CONFIG_UX500_SUSPEND_DBG) += suspend_dbg.o ifeq ($(CONFIG_UX500_SOC_DB8500), y) -obj-$(CONFIG_U8500_CPUFREQ) += cpufreq-db8500.o +obj-$(CONFIG_UX500_CPUFREQ) += cpufreq-db8500.o +endif + +ifeq ($(CONFIG_UX500_SOC_DB5500), y) +obj-$(CONFIG_UX500_CPUFREQ) += cpufreq-db5500.o endif diff --git a/arch/arm/mach-ux500/pm/cpufreq-db5500.c b/arch/arm/mach-ux500/pm/cpufreq-db5500.c new file mode 100644 index 00000000000..c68caa064e1 --- /dev/null +++ b/arch/arm/mach-ux500/pm/cpufreq-db5500.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + */ + +#include +#include + +#include + +#include "cpufreq.h" + +static struct cpufreq_frequency_table freq_table[] = { + [0] = { + .index = 0, + .frequency = 200000, + }, + [1] = { + .index = 1, + .frequency = 300000, + }, + [2] = { + .index = 2, + .frequency = 600000, + }, + [3] = { + .index = 3, + .frequency = CPUFREQ_TABLE_END, + }, +}; + +static enum arm_opp idx2opp[] = { + ARM_EXTCLK, + ARM_50_OPP, + ARM_100_OPP, +}; + +static int __init u5500_cpufreq_register(void) +{ + int i = 0; + + BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); + + pr_info("u5500-cpufreq : Available frequencies:\n"); + while (freq_table[i].frequency != CPUFREQ_TABLE_END) + pr_info(" %d Mhz\n", freq_table[i++].frequency/1000); + + return ux500_cpufreq_register(freq_table, idx2opp); +} +device_initcall(u5500_cpufreq_register); diff --git a/arch/arm/mach-ux500/pm/cpufreq.c b/arch/arm/mach-ux500/pm/cpufreq.c index 0c28eb79906..52606c1a117 100644 --- a/arch/arm/mach-ux500/pm/cpufreq.c +++ b/arch/arm/mach-ux500/pm/cpufreq.c @@ -20,19 +20,19 @@ static struct cpufreq_frequency_table *freq_table; static enum arm_opp *idx2opp; -static struct freq_attr *u8500_cpufreq_attr[] = { +static struct freq_attr *ux500_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL, }; -static int u8500_cpufreq_verify_speed(struct cpufreq_policy *policy) +static int ux500_cpufreq_verify_speed(struct cpufreq_policy *policy) { return cpufreq_frequency_table_verify(policy, freq_table); } extern bool wlan_mode_on; -static int u8500_cpufreq_target(struct cpufreq_policy *policy, +static int ux500_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { @@ -67,7 +67,7 @@ static int u8500_cpufreq_target(struct cpufreq_policy *policy, /* request the PRCM unit for opp change */ if (prcmu_set_arm_opp(idx2opp[idx])) { - pr_err("u8500-cpufreq: Failed to set OPP level\n"); + pr_err("ux500-cpufreq: Failed to set OPP level\n"); return -EINVAL; } @@ -77,7 +77,7 @@ static int u8500_cpufreq_target(struct cpufreq_policy *policy, return 0; } -static unsigned int u8500_cpufreq_getspeed(unsigned int cpu) +static unsigned int ux500_cpufreq_getspeed(unsigned int cpu) { int i; /* request the prcm to get the current ARM opp */ @@ -86,7 +86,7 @@ static unsigned int u8500_cpufreq_getspeed(unsigned int cpu) return freq_table[i].frequency; } -static int __cpuinit u8500_cpufreq_init(struct cpufreq_policy *policy) +static int __cpuinit ux500_cpufreq_init(struct cpufreq_policy *policy) { int res; int i; @@ -96,13 +96,13 @@ static int __cpuinit u8500_cpufreq_init(struct cpufreq_policy *policy) if (!res) cpufreq_frequency_table_get_attr(freq_table, policy->cpu); else { - pr_err("u8500-cpufreq : Failed to read policy table\n"); + pr_err("ux500-cpufreq : Failed to read policy table\n"); return res; } policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; - policy->cur = u8500_cpufreq_getspeed(policy->cpu); + policy->cur = ux500_cpufreq_getspeed(policy->cpu); for (i = 0; freq_table[i].frequency != policy->cur; i++) ; @@ -124,14 +124,14 @@ static int __cpuinit u8500_cpufreq_init(struct cpufreq_policy *policy) return 0; } -static struct cpufreq_driver u8500_cpufreq_driver = { +static struct cpufreq_driver ux500_cpufreq_driver = { .flags = CPUFREQ_STICKY, - .verify = u8500_cpufreq_verify_speed, - .target = u8500_cpufreq_target, - .get = u8500_cpufreq_getspeed, - .init = u8500_cpufreq_init, - .name = "U8500", - .attr = u8500_cpufreq_attr, + .verify = ux500_cpufreq_verify_speed, + .target = ux500_cpufreq_target, + .get = ux500_cpufreq_getspeed, + .init = ux500_cpufreq_init, + .name = "UX500", + .attr = ux500_cpufreq_attr, }; int __init @@ -144,6 +144,6 @@ ux500_cpufreq_register(struct cpufreq_frequency_table *table, if (cpu_is_u8500v1() || ux500_is_svp()) return -ENODEV; - pr_info("cpufreq for u8500 started\n"); - return cpufreq_register_driver(&u8500_cpufreq_driver); + pr_info("cpufreq for ux500 started\n"); + return cpufreq_register_driver(&ux500_cpufreq_driver); } diff --git a/arch/arm/mach-ux500/prcmu-db5500.c b/arch/arm/mach-ux500/prcmu-db5500.c index f6dd2e10f61..44fa2eaef20 100644 --- a/arch/arm/mach-ux500/prcmu-db5500.c +++ b/arch/arm/mach-ux500/prcmu-db5500.c @@ -84,6 +84,16 @@ #define PRCM_ACK_MB0_WAKEUP_1_ABB (PRCM_ACK_MB0 + 0x2C) #define PRCM_ACK_MB0_EVENT_ABB_NUMBERS 20 +/* Request mailbox 1 fields. */ +#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0) +#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1) + +/* Mailbox 1 ACKs */ +#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0) +#define PRCM_ACK_MB1_CURRENT_APE_OPP (PRCM_ACK_MB1 + 0x1) +#define PRCM_ACK_MB1_ARM_VOLT_STATUS (PRCM_ACK_MB1 + 0x2) +#define PRCM_ACK_MB1_APE_VOLT_STATUS (PRCM_ACK_MB1 + 0x3) + /* Mailbox 2 REQs */ #define PRCM_REQ_MB2_EPOD_CLIENT (PRCM_REQ_MB2 + 0x0) #define PRCM_REQ_MB2_EPOD_STATE (PRCM_REQ_MB2 + 0x1) @@ -107,11 +117,18 @@ enum mb0_header { /* acknowledge */ MB0H_WAKE_UP = 0, /* request */ - MB0H_PWR_STATE_TRANS = 1, + MB0H_PWR_STATE_TRANS, MB0H_WAKE_UP_CFG, MB0H_RD_WAKE_UP_ACK, }; +/* Mailbox 1 headers.*/ +enum mb1_header { + MB1H_ARM_OPP = 1, + MB1H_APE_OPP, + MB1H_ARM_APE_OPP, +}; + /* Mailbox 2 headers. */ enum mb2_header { MB2H_EPOD_REQUEST = 1, @@ -135,6 +152,12 @@ enum mb5_header { MB5H_I2C_READ, }; +enum db5500_arm_opp { + DB5500_ARM_100_OPP = 1, + DB5500_ARM_50_OPP, + DB5500_ARM_EXT_OPP, +}; + enum epod_state { EPOD_OFF, EPOD_ON, @@ -300,6 +323,29 @@ static struct { } req; } mb0_transfer; + +/* + * mb1_transfer - state needed for mailbox 1 communication. + * @lock: The transaction lock. + * @work: The transaction completion structure. + * @req_arm_opp Requested arm opp + * @req_ape_opp Requested ape opp + * @ack: Reply ("acknowledge") data. + */ +static struct { + struct mutex lock; + struct completion work; + u8 req_arm_opp; + u8 req_ape_opp; + struct { + u8 header; + u8 arm_opp; + u8 ape_opp; + u8 arm_voltage_st; + u8 ape_voltage_st; + } ack; +} mb1_transfer; + /* * mb2_transfer - state needed for mailbox 2 communication. * @lock: The transaction lock. @@ -393,7 +439,7 @@ static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { CLK_MGT_ENTRY(SVACLK), }; -bool prcmu_is_ac_wake_requested(void) +bool db5500_prcmu_is_ac_wake_requested(void) { return false; } @@ -618,7 +664,7 @@ int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) case PRCMU_AP_IDLE: writeb(DB5500_AP_IDLE, PRCM_REQ_MB0_AP_POWER_STATE); /* TODO: Can be high latency */ - writeb(DDR_PWR_STATE_OFFLOWLAT, PRCM_REQ_MB0_DDR_STATE); + writeb(DDR_PWR_STATE_UNCHANGED, PRCM_REQ_MB0_DDR_STATE); break; case PRCMU_AP_SLEEP: writeb(DB5500_AP_SLEEP, PRCM_REQ_MB0_AP_POWER_STATE); @@ -769,6 +815,75 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) return r; } +/** + * db5500_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 db5500_prcmu_set_arm_opp(u8 opp) +{ + int r; + u8 db5500_opp; + + r = 0; + + switch (opp) { + case ARM_EXTCLK: + db5500_opp = DB5500_ARM_EXT_OPP; + break; + case ARM_50_OPP: + db5500_opp = DB5500_ARM_50_OPP; + break; + case ARM_100_OPP: + db5500_opp = DB5500_ARM_100_OPP; + break; + default: + pr_err("prcmu: %s() received wrong opp value: %d\n", + __func__, opp); + r = -EINVAL; + goto bailout; + } + + mutex_lock(&mb1_transfer.lock); + + while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) + cpu_relax(); + + writeb(MB1H_ARM_OPP, PRCM_REQ_MB1_HEADER); + + writeb(db5500_opp, PRCM_REQ_MB1_ARM_OPP); + writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET)); + + if (!wait_for_completion_timeout(&mb1_transfer.work, + msecs_to_jiffies(500))) { + r = -EIO; + WARN(1, "prcmu: failed to set arm opp"); + goto unlock_and_return; + } + + if (mb1_transfer.ack.header != MB1H_ARM_OPP || + (mb1_transfer.ack.arm_opp != db5500_opp) || + (mb1_transfer.ack.arm_voltage_st != RC_SUCCESS)) + r = -EIO; + +unlock_and_return: + mutex_unlock(&mb1_transfer.lock); +bailout: + return r; +} + +/** + * db5500_prcmu_get_arm_opp - get the current ARM OPP + * + * Returns: the current ARM OPP + */ +int db5500_prcmu_get_arm_opp(void) +{ + return readb(PRCM_ACK_MB1_CURRENT_ARM_OPP); +} + int prcmu_resetout(u8 resoutn, u8 state) { int offset; @@ -982,7 +1097,33 @@ static bool read_mailbox_0(void) static bool read_mailbox_1(void) { + u8 header; + bool do_complete = true; + + header = mb1_transfer.ack.header = readb(PRCM_ACK_MB1_HEADER); + + switch (header) { + case MB1H_ARM_OPP: + mb1_transfer.ack.arm_opp = readb(PRCM_ACK_MB1_CURRENT_ARM_OPP); + mb1_transfer.ack.arm_voltage_st = + readb(PRCM_ACK_MB1_ARM_VOLT_STATUS); + break; + case MB1H_ARM_APE_OPP: + mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP); + mb1_transfer.ack.ape_voltage_st = + readb(PRCM_ACK_MB1_APE_VOLT_STATUS); + break; + default: + print_unknown_header_warning(1, header); + do_complete = false; + break; + } + writel(MBOX_BIT(1), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR); + + if (do_complete) + complete(&mb1_transfer.work); + return false; } @@ -1165,6 +1306,8 @@ void __init db5500_prcmu_early_init(void) tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE); spin_lock_init(&mb0_transfer.lock); spin_lock_init(&mb0_transfer.dbb_irqs_lock); + mutex_init(&mb1_transfer.lock); + init_completion(&mb1_transfer.work); mutex_init(&mb2_transfer.lock); init_completion(&mb2_transfer.work); mutex_init(&mb3_transfer.sysclk_lock); diff --git a/arch/arm/mach-ux500/prcmu-db8500.c b/arch/arm/mach-ux500/prcmu-db8500.c index 8ba0ac315f2..11a35a15a81 100644 --- a/arch/arm/mach-ux500/prcmu-db8500.c +++ b/arch/arm/mach-ux500/prcmu-db8500.c @@ -875,13 +875,13 @@ void db8500_prcmu_get_abb_event_buffer(void __iomem **buf) } /** - * prcmu_set_arm_opp - set the appropriate ARM OPP + * db8500_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 db8500_prcmu_set_arm_opp(u8 opp) { int r; @@ -912,11 +912,11 @@ int prcmu_set_arm_opp(u8 opp) } /** - * prcmu_get_arm_opp - get the current ARM OPP + * db8500_prcmu_get_arm_opp - get the current ARM OPP * * Returns: the current ARM OPP */ -int prcmu_get_arm_opp(void) +int db8500_prcmu_get_arm_opp(void) { return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP); } @@ -1834,7 +1834,7 @@ unlock_and_return: mutex_unlock(&mb0_transfer.ac_wake_lock); } -bool prcmu_is_ac_wake_requested(void) +bool db8500_prcmu_is_ac_wake_requested(void) { return (atomic_read(&ac_wake_req_state) != 0); } -- cgit v1.2.3