diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0121-Add-a-WA-to-defer-flash-writes-on-PS_ALERT_N-asserti.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0121-Add-a-WA-to-defer-flash-writes-on-PS_ALERT_N-asserti.patch | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0121-Add-a-WA-to-defer-flash-writes-on-PS_ALERT_N-asserti.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0121-Add-a-WA-to-defer-flash-writes-on-PS_ALERT_N-asserti.patch new file mode 100644 index 000000000..0c9bae00d --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0121-Add-a-WA-to-defer-flash-writes-on-PS_ALERT_N-asserti.patch @@ -0,0 +1,191 @@ +From 90035d4ef6ffb7893f629fa427db77c79e1e50e7 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Sat, 24 Oct 2020 13:43:23 -0700 +Subject: [PATCH] Add a WA to defer flash writes on PS_ALERT_N assertion + +To prevent SPI flash corruption, this commit adds a WA which monitors +PS_ALERT_N signal for detecting AC loss and it defers flash writes +when the signal is asserted. Actually, the PS_ALERT_N is asserted +even when PSU is in an unhealthy state so it also adds 10 seconds +of timeout for the deferring that covers AC loss case effectively. +If PSU gets back to healthy state, flash writes will be continued +immediately. + +Note: This would be a customization for some specific platforms +and this is a WA to cover a defect of H/W design. Do not try +upstream it. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + .../arm/boot/dts/aspeed-bmc-intel-ast2500.dts | 7 ++- + .../arm/boot/dts/aspeed-bmc-intel-ast2600.dts | 4 ++ + drivers/mtd/spi-nor/aspeed-smc.c | 61 +++++++++++++++++++ + 3 files changed, 71 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +index e4ac4e3696f5..74fd5c52d7e3 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2500.dts +@@ -3,6 +3,7 @@ + #include "aspeed-g5.dtsi" + #include <dt-bindings/gpio/aspeed-gpio.h> + #include <dt-bindings/i2c/i2c.h> ++#include <dt-bindings/interrupt-controller/irq.h> + + / { + model = "Intel AST2500 BMC"; +@@ -92,6 +93,10 @@ + }; + + &fmc { ++ /delete-property/ interrupts; ++ interrupts-extended = <&vic 19>, ++ <&gpio ASPEED_GPIO(AA, 1) IRQ_TYPE_EDGE_BOTH>; ++ ps-alert-gpio = <&gpio ASPEED_GPIO(AA, 1) GPIO_ACTIVE_HIGH>; + status = "okay"; + flash@0 { + status = "okay"; +@@ -185,7 +190,7 @@ + /*X0-X7*/ "","","","","","","","", + /*Y0-Y7*/ "SIO_S3","SIO_S5","","SIO_ONCONTROL","","","","", + /*Z0-Z7*/ "","SIO_POWER_GOOD","","","","","","", +- /*AA0-AA7*/ "P3VBAT_BRIDGE_EN","","","","PREQ_N","TCK_MUX_SEL","SMI","POST_COMPLETE", ++ /*AA0-AA7*/ "P3VBAT_BRIDGE_EN","IRQ_SML1_PMBUS_BMC_ALERT_N","","","PREQ_N","TCK_MUX_SEL","SMI","POST_COMPLETE", + /*AB0-AB7*/ "","NMI_BUTTON","ID_BUTTON","PS_PWROK","","","","", + /*AC0-AC7*/ "","","","","","","",""; + }; +diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +index e9cea7b63836..0ff929a68dd4 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-intel-ast2600.dts +@@ -85,6 +85,10 @@ + }; + + &fmc { ++ /delete-property/ interrupts; ++ interrupts-extended = <&gic GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>, ++ <&gpio0 ASPEED_GPIO(Y, 3) IRQ_TYPE_EDGE_BOTH>; ++ ps-alert-gpio = <&gpio0 ASPEED_GPIO(Y, 3) GPIO_ACTIVE_HIGH>; + status = "okay"; + flash@0 { + status = "okay"; +diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c +index 6e2f3802d162..397c2998e620 100644 +--- a/drivers/mtd/spi-nor/aspeed-smc.c ++++ b/drivers/mtd/spi-nor/aspeed-smc.c +@@ -9,12 +9,14 @@ + #include <linux/clk.h> + #include <linux/device.h> + #include <linux/io.h> ++#include <linux/gpio.h> + #include <linux/module.h> + #include <linux/mutex.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> + #include <linux/mtd/spi-nor.h> + #include <linux/of.h> ++#include <linux/of_gpio.h> + #include <linux/of_platform.h> + #include <linux/sizes.h> + #include <linux/slab.h> +@@ -216,10 +218,16 @@ struct aspeed_smc_controller { + u32 ahb_window_size; /* full mapping window size */ + + unsigned long clk_frequency; ++ unsigned int ps_alert_gpio; + + struct aspeed_smc_chip *chips[0]; /* pointers to attached chips */ + }; + ++static unsigned long aspeed_smc_flags = 0; ++#define FLAG_DEFER_WRITE 0 ++#define WRITE_DEFER_MSEC 100 /* 100ms */ ++#define WRITE_DEFER_MAX_COUNT 100 /* 100 x 100 = 10secs */ ++ + #define ASPEED_SPI_DEFAULT_FREQ 50000000 + + /* +@@ -411,6 +419,17 @@ static int aspeed_smc_write_to_ahb(void __iomem *dst, const void *buf, + size_t len) + { + size_t offset = 0; ++ int defer_cnt = 0; ++ ++ while (test_bit(FLAG_DEFER_WRITE, &aspeed_smc_flags)) { ++ pr_warn("%s deferring write, count: %d\n", DEVICE_NAME, ++ defer_cnt); ++ msleep(WRITE_DEFER_MSEC); ++ if (defer_cnt++ > WRITE_DEFER_MAX_COUNT) { ++ clear_bit(FLAG_DEFER_WRITE, &aspeed_smc_flags); ++ break; ++ } ++ } + + if (IS_ALIGNED((uintptr_t)dst, sizeof(uintptr_t)) && + IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { +@@ -1363,6 +1382,21 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, + return ret; + } + ++static irqreturn_t aspeed_smc_ps_alert_irq(int irq, void *arg) ++{ ++ struct aspeed_smc_controller *controller = arg; ++ ++ if (gpio_get_value(controller->ps_alert_gpio)) { ++ clear_bit(FLAG_DEFER_WRITE, &aspeed_smc_flags); ++ dev_warn(controller->dev, "clear FLAG_DEFER_WRITE\n"); ++ } else { ++ set_bit(FLAG_DEFER_WRITE, &aspeed_smc_flags); ++ dev_warn(controller->dev, "set FLAG_DEFER_WRITE\n"); ++ } ++ ++ return IRQ_HANDLED; ++} ++ + static int aspeed_smc_probe(struct platform_device *pdev) + { + struct device_node *np = pdev->dev.of_node; +@@ -1373,6 +1407,7 @@ static int aspeed_smc_probe(struct platform_device *pdev) + struct clk *clk; + struct resource *res; + int ret; ++ int irq; + + match = of_match_device(aspeed_smc_matches, &pdev->dev); + if (!match || !match->data) +@@ -1409,6 +1444,32 @@ static int aspeed_smc_probe(struct platform_device *pdev) + controller->clk_frequency = clk_get_rate(clk); + devm_clk_put(&pdev->dev, clk); + ++ controller->ps_alert_gpio = of_get_named_gpio(np, "ps-alert-gpio", 0); ++ if (!gpio_is_valid(controller->ps_alert_gpio)) { ++ dev_err(dev, "No valid ps-alert-gpio\n"); ++ ret = controller->ps_alert_gpio; ++ return ret; ++ } ++ ++ ret = devm_gpio_request_one(dev, controller->ps_alert_gpio, ++ GPIOF_DIR_IN, "ps-alert-gpio"); ++ if (ret) { ++ dev_err(dev, "request gpio failed %d\n", ret); ++ return ret; ++ } ++ ++ irq = platform_get_irq(pdev, 1); ++ if (irq < 0) ++ return irq; ++ ++ ret = devm_request_irq(&pdev->dev, irq, aspeed_smc_ps_alert_irq, ++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ "ps-alert-irq", controller); ++ if (ret) { ++ dev_err(dev, "request irq failed %d\n", ret); ++ return ret; ++ } ++ + ret = aspeed_smc_setup_flash(controller, np, res); + if (ret) + dev_err(dev, "Aspeed SMC probe failed %d\n", ret); +-- +2.17.1 + |