From patchwork Fri Jun 3 00:20:31 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Benjamin Herrenschmidt X-Patchwork-Id: 629564 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3rLRRF3Lc0z9t8V for ; Fri, 3 Jun 2016 11:32:25 +1000 (AEST) Received: from ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3rLRRF2hs7zDql9 for ; Fri, 3 Jun 2016 11:32:25 +1000 (AEST) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3rLRR60PM3zDqfv for ; Fri, 3 Jun 2016 11:32:17 +1000 (AEST) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by gate.crashing.org (8.14.1/8.13.8) with ESMTP id u530KWa0021504; Thu, 2 Jun 2016 19:20:33 -0500 Message-ID: <1464913231.3078.319.camel@kernel.crashing.org> From: Benjamin Herrenschmidt To: skiboot@lists.ozlabs.org Date: Fri, 03 Jun 2016 10:20:31 +1000 X-Mailer: Evolution 3.18.5.2 (3.18.5.2-1.fc23) Mime-Version: 1.0 Subject: [Skiboot] [RFC UNTESTED PATCH] ast-sf-ctrl: Try to harden vs. BMC collisions & reboots X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Jeremy Kerr Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" We try to detect collisions and reboots via changes to the control register. When they happen, we offline ourselves for 2mn and restore all settings when done. Signed-off-by: Benjamin Herrenschmidt ---  hw/ast-bmc/ast-sf-ctrl.c | 172 +++++++++++++++++++++++++++++++++++++----------  include/timebase.h       |  11 +++  libflash/errors.h        |   1 +  libflash/libflash.c      |   2 +-  4 files changed, 148 insertions(+), 38 deletions(-) diff --git a/hw/ast-bmc/ast-sf-ctrl.c b/hw/ast-bmc/ast-sf-ctrl.c index bf42d32..df85934 100644 --- a/hw/ast-bmc/ast-sf-ctrl.c +++ b/hw/ast-bmc/ast-sf-ctrl.c @@ -20,6 +20,12 @@  #include  #include   +#ifdef __SKIBOOT__ +#include +/* 2 minutes delay when something bad detected with BMC */ +#define OFFLINE_DELAY secs_to_tb(2 * 60) +#endif +  #include  #include   @@ -54,6 +60,9 @@ struct ast_sf_ctrl {   /* Current 4b mode */   bool mode_4b;   + /* offline target for when BMC reboots */ + uint64_t offline_target; +   /* Callbacks */   struct spi_flash_ctrl ops;  }; @@ -68,8 +77,114 @@ static const uint32_t ast_ct_hclk_divs[] = {   0xd, /* HCLK/5 */  };   +static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable) +{ + struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); + uint32_t ce_ctrl = 0; + + if (ct->type == AST_SF_TYPE_BMC && ct->ops.finfo->size > 0x1000000) + ce_ctrl = ast_ahb_readl(BMC_SPI_FCTL_CE_CTRL); + else if (ct->type != AST_SF_TYPE_PNOR) + return enable ? FLASH_ERR_4B_NOT_SUPPORTED : 0; + + /* +  * We update the "old" value as well since when quitting +  * we don't restore the mode of the flash itself so we need +  * to leave the controller in a compatible setup +  */ + if (enable) { + ct->ctl_val |= 0x2000; + ct->ctl_read_val |= 0x2000; + ce_ctrl |= 0x1; + } else { + ct->ctl_val &= ~0x2000; + ct->ctl_read_val &= ~0x2000; + ce_ctrl &= ~0x1; + } + ct->mode_4b = enable; + + /* Update read mode */ + ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); + + if (ce_ctrl && ct->type == AST_SF_TYPE_BMC) + ast_ahb_writel(ce_ctrl, BMC_SPI_FCTL_CE_CTRL); + + return 0; +} + +static bool ast_sf_check_bmc(struct ast_sf_ctrl *ct) +{ +#ifdef __SKIBOOT__ + uint32_t ctrl; + + /* If BMC is marked offline, just return */ + if (time_before(ct->offline_target)) + return false; + + /* Read control register */ + ctrl = ast_ahb_readl(ct->ctl_reg); + + /* If we didn't have an offline target, check the BMC state */ + /* We had an offline target, it's now expired, check if the BMC +  * is responding and restore registers. +  */ + if (!ct->offline_target) { + /* Expected value, we are good */ + if (((ct->ctl_reg ^ ctrl) & ~0x7) == 0) + return true; + + /* Unexpected value, we mark ourselves offline for a couple +  * of minutes as the BMC might be rebooting +  */ + prlog(PR_WARNING, "AST: Flash controller settings changed, " +       "BMC rebooted ?\n"); + prlog(PR_WARNING, "AST: Flash access off for a while...\n"); + prlog(PR_WARNING, "AST: ctrl reg was: 0x%08x\n", ctrl); + ct->offline_target = mftb() + OFFLINE_DELAY; + + /* We also whack the register to clear CE# since a collision +  * did occur +  */ + ast_ahb_writel(ctrl | 7, ct->ctl_reg); + ast_ahb_writel(ctrl & ~7, ct->ctl_reg); + return false; + } + + /* We had an offline target and we passed it, check if the BMC +  * is responding (also clear CE# just in case) +  */ + ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg); + ctrl = ast_ahb_readl(ct->ctl_reg); + if (((ct->ctl_reg ^ ctrl) & ~0x7) != 0) { + prlog(PR_WARNING, "AST: Flash controller still not" +       " responding, waiting longer...\n"); + ct->offline_target = mftb() + OFFLINE_DELAY; + return false; + } + + /* Ok, we are good now, reconfigure all */ + ct->offline_target = 0; + + /* Restore timing calibration */ + ast_ahb_writel(ct->fread_timing_val, ct->fread_timing_reg); + + /* Update read mode */ + ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); + + /* Restore 4b mode */ + ast_sf_set_4b(&ct->ops, ct->mode_4b); +#endif /* __SKIBOOT__ */ + + return true; + +} +  static int ast_sf_start_cmd(struct ast_sf_ctrl *ct, uint8_t cmd)  { + /* Check for a BMC reboot having lost settings */ + if (!ast_sf_check_bmc(ct)) + return FLASH_ERR_OFFLINE; +   /* Switch to user mode, CE# dropped */   ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);   @@ -80,13 +195,21 @@ static int ast_sf_start_cmd(struct ast_sf_ctrl *ct, uint8_t cmd)   return ast_copy_to_ahb(ct->flash, &cmd, 1);  }   -static void ast_sf_end_cmd(struct ast_sf_ctrl *ct) +static bool ast_sf_end_cmd(struct ast_sf_ctrl *ct)  { + bool bmc_lost; + + /* Check for a BMC reboot having lost settings */ + if (!ast_sf_check_bmc(ct)) + return FLASH_ERR_OFFLINE; +   /* clear CE# */   ast_ahb_writel(ct->ctl_val | 7, ct->ctl_reg);     /* Switch back to read mode */   ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); + + return bmc_lost;  }    static int ast_sf_send_addr(struct ast_sf_ctrl *ct, uint32_t addr) @@ -149,51 +272,26 @@ static int ast_sf_cmd_wr(struct spi_flash_ctrl *ctrl, uint8_t cmd,   return rc;  }   -static int ast_sf_set_4b(struct spi_flash_ctrl *ctrl, bool enable) -{ - struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); - uint32_t ce_ctrl = 0; - - if (ct->type == AST_SF_TYPE_BMC && ct->ops.finfo->size > 0x1000000) - ce_ctrl = ast_ahb_readl(BMC_SPI_FCTL_CE_CTRL); - else if (ct->type != AST_SF_TYPE_PNOR) - return enable ? FLASH_ERR_4B_NOT_SUPPORTED : 0; - - /* -  * We update the "old" value as well since when quitting -  * we don't restore the mode of the flash itself so we need -  * to leave the controller in a compatible setup -  */ - if (enable) { - ct->ctl_val |= 0x2000; - ct->ctl_read_val |= 0x2000; - ce_ctrl |= 0x1; - } else { - ct->ctl_val &= ~0x2000; - ct->ctl_read_val &= ~0x2000; - ce_ctrl &= ~0x1; - } - ct->mode_4b = enable; - - /* Update read mode */ - ast_ahb_writel(ct->ctl_read_val, ct->ctl_reg); - - if (ce_ctrl && ct->type == AST_SF_TYPE_BMC) - ast_ahb_writel(ce_ctrl, BMC_SPI_FCTL_CE_CTRL); - - return 0; -} -  static int ast_sf_read(struct spi_flash_ctrl *ctrl, uint32_t pos,          void *buf, uint32_t len)  {   struct ast_sf_ctrl *ct = container_of(ctrl, struct ast_sf_ctrl, ops); + int rc;   + if (!ast_sf_check_bmc(ct)) + return FLASH_ERR_OFFLINE;   /*    * We are in read mode by default. We don't yet support fancy    * things like fast read or X2 mode    */ - return ast_copy_from_ahb(buf, ct->flash + pos, len); + rc = ast_copy_from_ahb(buf, ct->flash + pos, len); + if (rc) + return rc; + + if (!ast_sf_check_bmc(ct)) + return FLASH_ERR_OFFLINE; + + return 0;  }    static void ast_get_ahb_freq(void) diff --git a/include/timebase.h b/include/timebase.h index 7f5afe1..a1d8a1c 100644 --- a/include/timebase.h +++ b/include/timebase.h @@ -23,6 +23,7 @@  #define __TIME_H    #include +#include    #ifndef __TEST__  static inline unsigned long mftb(void) @@ -51,6 +52,16 @@ static inline enum tb_cmpval tb_compare(unsigned long a,   return ((long)(b - a)) > 0 ? TB_ABEFOREB : TB_AAFTERB;  }   +static inline bool time_after(unsigned long target) +{ + return tb_compare(mftb(), target) == TB_AAFTERB; +} + +static inline bool time_before(unsigned long target) +{ + return tb_compare(mftb(), target) == TB_ABEFOREB; +} +  /* Architected timebase */  static const unsigned long tb_hz = 512000000;   diff --git a/libflash/errors.h b/libflash/errors.h index 99dcfc2..3381f48 100644 --- a/libflash/errors.h +++ b/libflash/errors.h @@ -31,5 +31,6 @@  #define FLASH_ERR_CTRL_TIMEOUT 13  #define FLASH_ERR_ECC_INVALID 14  #define FLASH_ERR_BAD_READ 15 +#define FLASH_ERR_OFFLINE 16    #endif /* __LIBFLASH_ERRORS_H */ diff --git a/libflash/libflash.c b/libflash/libflash.c index d3e594a..ec93284 100644 --- a/libflash/libflash.c +++ b/libflash/libflash.c @@ -286,7 +286,7 @@ int flash_erase_chip(struct flash_chip *c)   return FLASH_ERR_CHIP_ER_NOT_SUPPORTED;     FL_DBG("LIBFLASH: Erasing chip...\n"); - +   /* Use controller erase if supported */   if (ct->erase)   return ct->erase(ct, 0, 0xffffffff);