diff options
-rw-r--r-- | drivers/mfd/db5500-prcmu-regs.h | 9 | ||||
-rw-r--r-- | drivers/mfd/db5500-prcmu.c | 120 | ||||
-rw-r--r-- | include/linux/mfd/db5500-prcmu.h | 32 | ||||
-rw-r--r-- | include/linux/mfd/dbx500-prcmu.h | 10 |
4 files changed, 165 insertions, 6 deletions
diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/db5500-prcmu-regs.h index 30234a86263..0428b5e95ae 100644 --- a/drivers/mfd/db5500-prcmu-regs.h +++ b/drivers/mfd/db5500-prcmu-regs.h @@ -129,4 +129,13 @@ #define PRCM_HOSTACCESS_REQ_BIT BIT(0) #define PRCM_APE_ACK 0x49c #define PRCM_APE_ACK_BIT 0x01 + +/* Watchdog - mtimer registers */ +#define PRCM_TIMER0_RTOS_COMP1_OFFSET 0x4C +#define PRCM_TIMER0_RTOS_COUNTER_OFFSET 0x40 +#define PRCM_TIMER0_IRQ_EN_SET_OFFSET 0x70 +#define PRCM_TIMER0_IRQ_EN_CLR_OFFSET 0x6C +#define PRCM_TIMER0_IRQ_RTOS1_SET 0x08 +#define PRCM_TIMER0_IRQ_RTOS1_CLR 0x08 + #endif diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c index 9a944f08ef3..76d8a3132ae 100644 --- a/drivers/mfd/db5500-prcmu.c +++ b/drivers/mfd/db5500-prcmu.c @@ -164,6 +164,9 @@ enum mb4_header { MB4H_CFG_HOTMON = 8, MB4H_CFG_HOTPERIOD = 10, MB4H_CGF_MODEM_RESET = 13, + MB4H_CGF_A9WDOG_EN_PREBARK = 14, + MB4H_CGF_A9WDOG_EN_NOPREBARK = 15, + MB4H_CGF_A9WDOG_DIS = 16, }; /* Mailbox 4 ACK headers */ @@ -172,6 +175,9 @@ enum mb4_ack_header { MB4H_ACK_CFG_HOTMON = 6, MB4H_ACK_CFG_HOTPERIOD = 8, MB4H_ACK_CFG_MODEM_RESET = 11, + MB4H_ACK_CGF_A9WDOG_EN_PREBARK = 12, + MB4H_ACK_CGF_A9WDOG_EN_NOPREBARK = 13, + MB4H_ACK_CGF_A9WDOG_DIS = 14, }; /* Mailbox 5 headers. */ @@ -473,9 +479,12 @@ static struct { /* Spinlocks */ static DEFINE_SPINLOCK(clkout_lock); -/* PRCMU TCDM base IO address. */ +/* PRCMU TCDM base IO address */ static __iomem void *tcdm_base; +/* PRCMU MTIMER base IO address */ +static __iomem void *mtimer_base; + struct clk_mgt { unsigned int offset; u32 pllsw; @@ -491,6 +500,11 @@ static struct { u8 api_version; } prcmu_version; +static struct { + u32 timeout; + bool enabled; +} a9wdog_timer; + static DEFINE_SPINLOCK(clk_mgt_lock); #define CLK_MGT_ENTRY(_name, _scalable)[PRCMU_##_name] = { \ @@ -1182,6 +1196,106 @@ int db5500_prcmu_stop_temp_sense(void) return config_hot_period(0xFFFF); } +static int prcmu_a9wdog(u8 req, u8 ack) +{ + int r = 0; + + mutex_lock(&mb4_transfer.lock); + + while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4)) + cpu_relax(); + + r = mailbox4_request(req, ack); + + mutex_unlock(&mb4_transfer.lock); + + return r; +} + +static void prcmu_a9wdog_set_interrupt(bool enable) +{ + if (enable) { + writel(PRCM_TIMER0_IRQ_RTOS1_SET, + (mtimer_base + PRCM_TIMER0_IRQ_EN_SET_OFFSET)); + } else { + writel(PRCM_TIMER0_IRQ_RTOS1_CLR, + (mtimer_base + PRCM_TIMER0_IRQ_EN_CLR_OFFSET)); + } +} + +static void prcmu_a9wdog_set_timeout(u32 timeout) +{ + u32 comp_timeout; + + comp_timeout = readl(mtimer_base + PRCM_TIMER0_RTOS_COUNTER_OFFSET) + + timeout; + writel(comp_timeout, mtimer_base + PRCM_TIMER0_RTOS_COMP1_OFFSET); +} + +int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off) +{ + /* + * Sleep auto off feature is not supported. Resume and + * suspend will be handled by watchdog driver. + */ + return 0; +} + +int db5500_prcmu_enable_a9wdog(u8 id) +{ + int r = 0; + + if (a9wdog_timer.enabled) + return -EPERM; + + prcmu_a9wdog_set_interrupt(true); + + r = prcmu_a9wdog(MB4H_CGF_A9WDOG_EN_PREBARK, + MB4H_ACK_CGF_A9WDOG_EN_PREBARK); + if (!r) + a9wdog_timer.enabled = true; + else + prcmu_a9wdog_set_interrupt(false); + + return r; +} + +int db5500_prcmu_disable_a9wdog(u8 id) +{ + if (!a9wdog_timer.enabled) + return -EPERM; + + prcmu_a9wdog_set_interrupt(false); + + a9wdog_timer.enabled = false; + + return prcmu_a9wdog(MB4H_CGF_A9WDOG_DIS, + MB4H_ACK_CGF_A9WDOG_DIS); +} + +int db5500_prcmu_kick_a9wdog(u8 id) +{ + int r = 0; + + if (a9wdog_timer.enabled) + prcmu_a9wdog_set_timeout(a9wdog_timer.timeout); + else + r = -EPERM; + + return r; +} + +int db5500_prcmu_load_a9wdog(u8 id, u32 timeout) +{ + if (a9wdog_timer.enabled) + return -EPERM; + + prcmu_a9wdog_set_timeout(timeout); + a9wdog_timer.timeout = timeout; + + return 0; +} + /** * db5500_prcmu_abb_read() - Read register value(s) from the ABB. * @slave: The I2C slave address. @@ -1885,6 +1999,9 @@ static bool read_mailbox_4(void) case MB4H_ACK_CFG_HOTMON: case MB4H_ACK_CFG_HOTPERIOD: case MB4H_ACK_CFG_MODEM_RESET: + case MB4H_ACK_CGF_A9WDOG_EN_PREBARK: + case MB4H_ACK_CGF_A9WDOG_EN_NOPREBARK: + case MB4H_ACK_CGF_A9WDOG_DIS: mb4_transfer.ack.status = readb(PRCM_ACK_MB4_REQUESTS); break; default: @@ -2059,6 +2176,7 @@ void __init db5500_prcmu_early_init(void) } tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE); + mtimer_base = __io_address(U5500_MTIMER_BASE); spin_lock_init(&mb0_transfer.lock); spin_lock_init(&mb0_transfer.dbb_irqs_lock); mutex_init(&mb0_transfer.ac_wake_lock); diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h index cf97d25a913..681c8f99bf1 100644 --- a/include/linux/mfd/db5500-prcmu.h +++ b/include/linux/mfd/db5500-prcmu.h @@ -65,6 +65,13 @@ int db5500_prcmu_config_hotdog(u8 threshold); int db5500_prcmu_config_hotmon(u8 low, u8 high); int db5500_prcmu_start_temp_sense(u16 cycles32k); int db5500_prcmu_stop_temp_sense(void); + +int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off); +int db5500_prcmu_enable_a9wdog(u8 id); +int db5500_prcmu_disable_a9wdog(u8 id); +int db5500_prcmu_kick_a9wdog(u8 id); +int db5500_prcmu_load_a9wdog(u8 id, u32 timeout); + #else /* !CONFIG_UX500_SOC_DB5500 */ static inline void db5500_prcmu_early_init(void) {} @@ -227,6 +234,31 @@ static inline int db5500_prcmu_stop_temp_sense(void) return 0; } +static inline int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off) +{ + return 0; +} + +static inline int db5500_prcmu_enable_a9wdog(u8 id) +{ + return 0; +} + +static inline int db5500_prcmu_disable_a9wdog(u8 id) +{ + return 0; +} + +static inline int db5500_prcmu_kick_a9wdog(u8 id) +{ + return 0; +} + +static inline int db5500_prcmu_load_a9wdog(u8 id, u32 timeout) +{ + return 0; +} + #endif /* CONFIG_MFD_DB5500_PRCMU */ #endif /* __MFD_DB5500_PRCMU_H */ diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 45a0ae0cc39..eb9cee31407 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -556,7 +556,7 @@ static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value) static inline int prcmu_enable_a9wdog(u8 id) { if (cpu_is_u5500()) - return -EINVAL; + return db5500_prcmu_enable_a9wdog(id); else return db8500_prcmu_enable_a9wdog(id); } @@ -564,7 +564,7 @@ static inline int prcmu_enable_a9wdog(u8 id) static inline int prcmu_disable_a9wdog(u8 id) { if (cpu_is_u5500()) - return -EINVAL; + return db5500_prcmu_disable_a9wdog(id); else return db8500_prcmu_disable_a9wdog(id); } @@ -572,7 +572,7 @@ static inline int prcmu_disable_a9wdog(u8 id) static inline int prcmu_kick_a9wdog(u8 id) { if (cpu_is_u5500()) - return -EINVAL; + return db5500_prcmu_kick_a9wdog(id); else return db8500_prcmu_kick_a9wdog(id); } @@ -580,7 +580,7 @@ static inline int prcmu_kick_a9wdog(u8 id) static inline int prcmu_load_a9wdog(u8 id, u32 timeout) { if (cpu_is_u5500()) - return -EINVAL; + return db5500_prcmu_load_a9wdog(id, timeout); else return db8500_prcmu_load_a9wdog(id, timeout); } @@ -588,7 +588,7 @@ static inline int prcmu_load_a9wdog(u8 id, u32 timeout) static inline int prcmu_config_a9wdog(u8 num, bool sleep_auto_off) { if (cpu_is_u5500()) - return -EINVAL; + return db5500_prcmu_config_a9wdog(num, sleep_auto_off); else return db8500_prcmu_config_a9wdog(num, sleep_auto_off); } |