From 4d7aca1484d6363636410077635661e58c74969d Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Fri, 25 Aug 2017 13:23:21 -0500 Subject: Acknowledge LPC reset and related events in the KCS interface module This resolves an IRQ storm / LPC hang on host boot when the BMC is active --- arch/arm/mach-aspeed/include/mach/ast_kcs.h | 32 +++++++----- drivers/char/aspeed/ast_kcs.c | 77 +++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-aspeed/include/mach/ast_kcs.h b/arch/arm/mach-aspeed/include/mach/ast_kcs.h index 9bcd6fc..2417c25 100644 --- a/arch/arm/mach-aspeed/include/mach/ast_kcs.h +++ b/arch/arm/mach-aspeed/include/mach/ast_kcs.h @@ -96,9 +96,9 @@ #define AST_LPC_HICR0_PMEE 0x04 /* bits of HICR1 */ -#define AST_LPC_HICR1_LPCBSY 0x80 -#define AST_LPC_HICR1_CLKREQ 0x40 -#define AST_LPC_HICR1_IRQBSY 0x20 +#define AST_LPC_HICR1_LPCBSY 0x80 +#define AST_LPC_HICR1_CLKREQ 0x40 +#define AST_LPC_HICR1_IRQBSY 0x20 #define AST_LPC_HICR1_LRSTB 0x10 #define AST_LPC_HICR1_SDWNB 0x08 #define AST_LPC_HICR1_PMEB 0x04 @@ -107,23 +107,23 @@ #define AST_LPC_HICR2_LRST 0x40 #define AST_LPC_HICR2_SDWN 0x20 #define AST_LPC_HICR2_ABRT 0x10 -#define AST_LPC_HICR2_IBFIE3 0x08 -#define AST_LPC_HICR2_IBFIE2 0x04 -#define AST_LPC_HICR2_IBFIE1 0x02 +#define AST_LPC_HICR2_IBFIE3 0x08 +#define AST_LPC_HICR2_IBFIE2 0x04 +#define AST_LPC_HICR2_IBFIE1 0x02 #define AST_LPC_HICR2_ERRIE 0x01 /* bits of HICR3, pin states regsiter */ -#define AST_LPC_HICR3_LFRAME 0x80 -#define AST_LPC_HICR3_CLKRUN 0x40 -#define AST_LPC_HICR3_SERIRQ 0x20 -#define AST_LPC_HICR3_LRESET 0x10 +#define AST_LPC_HICR3_LFRAME 0x80 +#define AST_LPC_HICR3_CLKRUN 0x40 +#define AST_LPC_HICR3_SERIRQ 0x20 +#define AST_LPC_HICR3_LRESET 0x10 #define AST_LPC_HICR3_LPCPD 0x08 #define AST_LPC_HICR3_PME 0x04 /* bits of HICR4, selection register */ -#define AST_LPC_HICR4_LADR12SEL 0x80 -#define AST_LPC_HICR4_KCSENBL 0x04 -#define AST_LPC_HICR4_BTENBL 0x01 +#define AST_LPC_HICR4_LADR12SEL 0x80 +#define AST_LPC_HICR4_KCSENBL 0x04 +#define AST_LPC_HICR4_BTENBL 0x01 /* bits of STR[1:3], data full register */ #define AST_LPC_STR_CD 0x08 @@ -131,6 +131,12 @@ #define AST_LPC_STR_IBFA 0x02 #define AST_LPC_STR_OBFA 0x01 +/* bits of HICR5 */ +#define AST_LPC_HICR5_SNP1_ENINT 0x08 +#define AST_LPC_HICR5_SNP1_EN 0x04 +#define AST_LPC_HICR5_SNP0_ENINT 0x02 +#define AST_LPC_HICR5_SNP0_EN 0x01 + /* bits of HICR6 */ #define AST_LPC_HICR6_SNP1_STR 0x02 #define AST_LPC_HICR6_SNP0_STR 0x01 diff --git a/drivers/char/aspeed/ast_kcs.c b/drivers/char/aspeed/ast_kcs.c index 93c079f..2eca42c 100644 --- a/drivers/char/aspeed/ast_kcs.c +++ b/drivers/char/aspeed/ast_kcs.c @@ -41,6 +41,16 @@ inline void ast_kcs_write_reg(uint32_t data, uint32_t reg) iowrite32(data, ast_kcs_virt_base + reg); } +inline uint32_t ast_lpcreset_read_reg(uint32_t reg) +{ + return ioread32(ast_kcs_virt_base + reg); +} + +inline void ast_lpcreset_write_reg(uint32_t data, uint32_t reg) +{ + iowrite32(data, ast_kcs_virt_base + reg); +} + static void ast_kcs_enable_channel(void) { uint32_t reg; @@ -163,6 +173,24 @@ static void ast_kcs_disable_interrupt(void) #endif } +static void ast_lpcreset_enable_interrupt(void) +{ + uint32_t reg; + + reg = ast_lpcreset_read_reg(AST_LPC_HICR2); + reg |= AST_LPC_HICR2_ERRIE; + ast_lpcreset_write_reg(reg, AST_LPC_HICR2); +} + +static void ast_lpcreset_disable_interrupt(void) +{ + uint32_t reg; + + reg = ast_lpcreset_read_reg(AST_LPC_HICR2); + reg &= ~(AST_LPC_HICR2_ERRIE); + ast_lpcreset_write_reg(reg, AST_LPC_HICR2); +} + static void ast_kcs_read_status(u8 channel, u8 *status) { if (channel == 3) @@ -261,15 +289,42 @@ static irqreturn_t ast_kcs_irq_handler(int irq, void *dev_id) reg = ast_kcs_read_reg(AST_LPC_HICR6); + if (reg & (AST_LPC_HICR6_SNP0_STR | AST_LPC_HICR6_SNP1_STR)) { /* snoop interrupt is occured */ - return IRQ_NONE; /* handled by snoop driver */ + printk(KERN_WARNING "ast_kcs: Unhandled snoop request!\n"); + return IRQ_NONE; } reg = ast_kcs_read_reg(AST_LPC_HICR2); - if (reg & (AST_LPC_HICR2_LRST | AST_LPC_HICR2_SDWN | AST_LPC_HICR2_ABRT)) { /* LRESET | SDWN | ABRT interrupt is occured */ - return IRQ_NONE; /* handled by LPC-reset driver */ + if (reg & AST_LPC_HICR2_LRST) { + /* clear interrupt flag */ + reg &= ~(AST_LPC_HICR2_LRST); + ast_lpcreset_write_reg(reg, AST_LPC_HICR2); + + // FIXME + // How to signal reset request to userspace application / kernel kcs state machine? + // Ignore reset for now... + printk(KERN_DEBUG "LPC RESET [IGNORED]\n"); + handled = 1; + } + + if (reg & AST_LPC_HICR2_SDWN) { + /* clear interrupt flag */ + reg &= ~AST_LPC_HICR2_SDWN; + ast_lpcreset_write_reg(reg, AST_LPC_HICR2); + printk(KERN_DEBUG "LPC SWDN\n"); + handled = 1; } + + if (reg & AST_LPC_HICR2_ABRT) { + /* clear interrupt flag */ + reg &= ~AST_LPC_HICR2_ABRT; + ast_lpcreset_write_reg(reg, AST_LPC_HICR2); + printk(KERN_DEBUG "LPC ABORT\n"); + handled = 1; + } + for (ch = 0; ch < AST_KCS_CHANNEL_NUM; ch ++) { ast_kcs_read_status(ch, &status); if (status & 0x02) { /* Command or Data_In register has been written by system-side software */ @@ -332,6 +387,7 @@ static void ast_kcs_init_hw(void) static int ast_kcs_probe(struct platform_device *pdev) { int ret = 0; + uint32_t reg; struct resource *res0; dev_dbg(&pdev->dev, "ast_kcs_probe() \n\n\n"); @@ -355,6 +411,21 @@ static int ast_kcs_probe(struct platform_device *pdev) { goto err_no_io_res; } + /* clear interrupt status and disable interrupt of KCS and LPC-reset */ + reg = ast_lpcreset_read_reg(AST_LPC_HICR2); + reg &= ~(AST_LPC_HICR2_LRST | AST_LPC_HICR2_SDWN | AST_LPC_HICR2_ABRT | AST_LPC_HICR2_IBFIE1 | AST_LPC_HICR2_IBFIE2 | AST_LPC_HICR2_IBFIE3 | AST_LPC_HICR2_ERRIE); + ast_lpcreset_write_reg(reg, AST_LPC_HICR2); + + /* disable interrupt of snoop */ + reg = ast_lpcreset_read_reg(AST_LPC_HICR5); + reg &= ~(AST_LPC_HICR5_SNP0_EN | AST_LPC_HICR5_SNP0_ENINT | AST_LPC_HICR5_SNP1_EN | AST_LPC_HICR5_SNP1_ENINT); + ast_lpcreset_write_reg(reg, AST_LPC_HICR5); + + /* clear interrupt status of snoop */ + reg = ast_lpcreset_read_reg(AST_LPC_HICR6); + reg |= (AST_LPC_HICR6_SNP0_STR | AST_LPC_HICR6_SNP1_STR); + ast_lpcreset_write_reg(reg, AST_LPC_HICR6); + IRQ_SET_LEVEL_TRIGGER(0, AST_KCS_IRQ); IRQ_SET_HIGH_LEVEL(0, AST_KCS_IRQ); -- cgit v1.1