diff options
Diffstat (limited to 'drivers/mfd/db8500-prcmu.c')
-rw-r--r-- | drivers/mfd/db8500-prcmu.c | 141 |
1 files changed, 105 insertions, 36 deletions
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 5be32489714..7c26c41a7ef 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -30,11 +30,13 @@ #include <linux/mfd/dbx500-prcmu.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h> +#include <linux/mfd/abx500.h> #include <asm/hardware/gic.h> #include <mach/hardware.h> #include <mach/irqs.h> #include <mach/db8500-regs.h> #include <mach/id.h> +#include <mach/prcmu-debug.h> #include "dbx500-prcmu-regs.h" /* Offset for the firmware version within the TCPM */ @@ -70,6 +72,8 @@ #define PRCM_SW_RST_REASON 0xFF8 /* 2 bytes */ +#define PRCM_TCDM_VOICE_CALL_FLAG 0xDD4 /* 4 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) @@ -214,10 +218,8 @@ #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_WRITE(slave) (((slave) << 1) | BIT(6)) +#define PRCMU_I2C_READ(slave) (((slave) << 1) | BIT(0) | BIT(6)) #define PRCMU_I2C_STOP_EN BIT(3) /* Mailbox 5 ACKs */ @@ -424,6 +426,13 @@ static DEFINE_SPINLOCK(clkout_lock); /* Global var to runtime determine TCDM base for v2 or v1 */ static __iomem void *tcdm_base; +/* + * Copies of the startup values of the reset status register and the SW reset + * code. + */ +static u32 reset_status_copy; +static u16 reset_code_copy; + struct clk_mgt { void __iomem *reg; u32 pllsw; @@ -637,6 +646,26 @@ void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value) spin_unlock_irqrestore(&prcmu_lock, flags); } +/* + * Dump AB8500 registers, PRCMU registers and PRCMU data memory + * on critical errors. + */ +static void db8500_prcmu_debug_dump(const char *func, + bool dump_prcmu, bool dump_abb) +{ + printk(KERN_DEBUG"%s: timeout\n", func); + + /* Dump AB8500 registers */ + if (dump_abb) + abx500_dump_all_banks(); + + /* Dump prcmu registers and data memory */ + if (dump_prcmu) { + prcmu_debug_dump_regs(); + prcmu_debug_dump_data_mem(); + } +} + struct prcmu_fw_version *prcmu_get_fw_version(void) { return fw_info.valid ? &fw_info.version : NULL; @@ -648,6 +677,11 @@ bool prcmu_has_arm_maxopp(void) PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK; } +void db8500_prcmu_vc(bool enable) +{ + writel((enable ? 0xF : 0), (tcdm_base + PRCM_TCDM_VOICE_CALL_FLAG)); +} + /** * prcmu_get_boot_status - PRCMU boot status checking * Returns: the current PRCMU boot status @@ -1049,7 +1083,7 @@ int db8500_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()) + if (!cpu_is_u8500v20()) writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW); return 0; @@ -1111,12 +1145,14 @@ unlock_and_return: int db8500_prcmu_set_ape_opp(u8 opp) { int r = 0; + u8 prcmu_opp_req; if (opp == mb1_transfer.ape_opp) return 0; mutex_lock(&mb1_transfer.lock); + /* Exit APE_50_PARTLY_25_OPP */ if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP) request_even_slower_clocks(false); @@ -1126,20 +1162,22 @@ int db8500_prcmu_set_ape_opp(u8 opp) while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1)) cpu_relax(); + prcmu_opp_req = (opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp; + writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1)); writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); - writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp), - (tcdm_base + PRCM_REQ_MB1_APE_OPP)); + writeb(prcmu_opp_req, (tcdm_base + PRCM_REQ_MB1_APE_OPP)); writel(MBOX_BIT(1), 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)) + (mb1_transfer.ack.ape_opp != prcmu_opp_req)) r = -EIO; skip_message: if ((!r && (opp == APE_50_PARTLY_25_OPP)) || + /* Set APE_50_PARTLY_25_OPP back in case new opp failed */ (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP))) request_even_slower_clocks(true); if (!r) @@ -1322,6 +1360,7 @@ int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", __func__); r = -EIO; + db8500_prcmu_debug_dump(__func__, true, true); goto unlock_and_return; } @@ -1416,6 +1455,7 @@ static int request_sysclk(bool enable) pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", __func__); r = -EIO; + db8500_prcmu_debug_dump(__func__, true, true); } mutex_unlock(&mb3_transfer.sysclk_lock); @@ -2190,6 +2230,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", __func__); r = -EIO; + db8500_prcmu_debug_dump(__func__, true, false); } else { r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO); } @@ -2240,6 +2281,7 @@ int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size) pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n", __func__); r = -EIO; + db8500_prcmu_debug_dump(__func__, true, false); } else { r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO); } @@ -2287,6 +2329,7 @@ retry: if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) { + db8500_prcmu_debug_dump(__func__, true, true); pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); goto unlock_and_return; @@ -2309,6 +2352,7 @@ retry: if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) goto retry; + db8500_prcmu_debug_dump(__func__, true, true); pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n", __func__); } @@ -2335,6 +2379,7 @@ void prcmu_ac_sleep_req() if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) { + db8500_prcmu_debug_dump(__func__, true, true); pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); } @@ -2370,7 +2415,17 @@ void db8500_prcmu_system_reset(u16 reset_code) */ u16 db8500_prcmu_get_reset_code(void) { - return readw(tcdm_base + PRCM_SW_RST_REASON); + return reset_code_copy; +} + +/** + * db8500_prcmu_get_reset_status - Retrieve reset status + * + * Retrieves the value of the reset status register as read at startup. + */ +u32 db8500_prcmu_get_reset_status(void) +{ + return reset_status_copy; } /** @@ -2437,6 +2492,13 @@ static bool read_mailbox_0(void) if (ev & WAKEUP_BIT_SYSCLK_OK) complete(&mb3_transfer.sysclk_work); + prcmu_debug_register_mbox0_event(ev, + (mb0_transfer.req.dbb_irqs | + mb0_transfer.req.dbb_wakeups | + WAKEUP_BIT_AC_WAKE_ACK | + WAKEUP_BIT_AC_SLEEP_ACK | + WAKEUP_BIT_SYSCLK_OK)); + ev &= mb0_transfer.req.dbb_irqs; for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) { @@ -2561,6 +2623,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data) bits -= MBOX_BIT(n); if (read_mailbox[n]()) r = IRQ_WAKE_THREAD; + prcmu_debug_register_interrupt(n); } } return r; @@ -2646,29 +2709,38 @@ static char *fw_project_name(u8 project) void __init db8500_prcmu_early_init(void) { unsigned int i; - if (cpu_is_u8500v2()) { - void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K); - - if (tcpm_base != NULL) { - u32 version; - version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET); - fw_info.version.project = version & 0xFF; - fw_info.version.api_version = (version >> 8) & 0xFF; - fw_info.version.func_version = (version >> 16) & 0xFF; - fw_info.version.errata = (version >> 24) & 0xFF; - fw_info.valid = true; - pr_info("PRCMU firmware: %s, version %d.%d.%d\n", - fw_project_name(fw_info.version.project), - (version >> 8) & 0xFF, (version >> 16) & 0xFF, - (version >> 24) & 0xFF); - iounmap(tcpm_base); - } + void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K); + void __iomem *sec_base; + + if (tcpm_base != NULL) { + u32 version; + version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET); + fw_info.version.project = version & 0xFF; + fw_info.version.api_version = (version >> 8) & 0xFF; + fw_info.version.func_version = (version >> 16) & 0xFF; + fw_info.version.errata = (version >> 24) & 0xFF; + fw_info.valid = true; + pr_info("PRCMU firmware: %s, version %d.%d.%d\n", + fw_project_name(fw_info.version.project), + (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(); + tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); + + /* + * Copy the value of the reset status register and if needed also + * the software reset code. + */ + sec_base = ioremap_nocache(U8500_PRCMU_SEC_BASE, SZ_4K); + if (sec_base != NULL) { + reset_status_copy = readl(sec_base + + DB8500_SEC_PRCM_RESET_STATUS); + iounmap(sec_base); } + if (reset_status_copy & DB8500_SEC_PRCM_RESET_STATUS_APE_SOFTWARE_RESET) + reset_code_copy = readw(tcdm_base + PRCM_SW_RST_REASON); spin_lock_init(&mb0_transfer.lock); spin_lock_init(&mb0_transfer.dbb_irqs_lock); @@ -2734,6 +2806,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = { REGULATOR_SUPPLY("vcore", "uart2"), REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"), REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"), + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), }; static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { @@ -2743,7 +2816,7 @@ static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { }; static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = { - REGULATOR_SUPPLY("vsupply", "b2r2_bus"), + REGULATOR_SUPPLY("vsupply", "b2r2_core"), REGULATOR_SUPPLY("vsupply", "mcde"), }; @@ -2962,9 +3035,6 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) { int err = 0; - if (ux500_is_svp()) - return -ENODEV; - init_prcm_registers(); /* Clean up the mailbox interrupts after pre-kernel code. */ @@ -2978,8 +3048,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) goto no_irq_return; } - if (cpu_is_u8500v20_or_later()) - prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); + 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, |