diff options
author | Timothy Pearson <tpearson@raptorengineering.com> | 2017-08-25 13:23:21 -0500 |
---|---|---|
committer | Timothy Pearson <tpearson@raptorengineering.com> | 2017-08-25 13:23:21 -0500 |
commit | 4d7aca1484d6363636410077635661e58c74969d (patch) | |
tree | 9490f8ed60cc8fdfb7723162957f3915558134d3 /drivers/char | |
parent | 30bdf6fabf97b0d46c73093f4f25e933c70c7b5e (diff) | |
download | ast2050-linux-kernel-4d7aca1484d6363636410077635661e58c74969d.zip ast2050-linux-kernel-4d7aca1484d6363636410077635661e58c74969d.tar.gz |
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
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/aspeed/ast_kcs.c | 77 |
1 files changed, 74 insertions, 3 deletions
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); |