Message ID | 20191220113155.28177-5-yamada.masahiro@socionext.com |
---|---|
State | New |
Headers | show |
Series | mtd: rawnand: denali: a bungle of denali patches that is cleanly applicable | expand |
On Fri, 2019-12-20 at 11:31:54 UTC, Masahiro Yamada wrote: > According to the Denali NAND Flash Memory Controller User's Guide, > this IP has two reset signals. > > rst_n: reset most of FFs in the controller core > reg_rst_n: reset all FFs in the register interface, and in the > initialization sequencer > > This commit supports controlling those reset signals. > > It is possible to control them separately from the IP point of view > although they might be often tied up together in actual SoC integration. > > The IP spec says, asserting only the reg_rst_n without asserting rst_n > will cause unpredictable behavior in the controller. So, the driver > deasserts ->rst_reg and ->rst in this order. > > Another thing that should be kept in mind is the automated initialization > sequence (a.k.a. 'bootstrap' process) is kicked off when reg_rst_n is > deasserted. > > When the reset is deasserted, the controller issues a RESET command > to the chip select 0, and attempts to read out the chip ID, and further > more, ONFI parameters if it is an ONFI-compliant device. Then, the > controller sets up the relevant registers based on the detected > device parameters. > > This process might be useful for tiny boot firmware, but is redundant > for Linux Kernel because nand_scan_ident() probes devices and sets up > parameters accordingly. Rather, this hardware feature is annoying > because it ends up with misdetection due to bugs. > > So, commit 0615e7ad5d52 ("mtd: nand: denali: remove Toshiba and Hynix > specific fixup code") changed the driver to not rely on it. > > However, there is no way to prevent it from running. The IP provides > the 'bootstrap_inhibit_init' port to suppress this sequence, but it is > usually out of software control, and dependent on SoC implementation. > As for the Socionext UniPhier platform, LD4 always enables it. For the > later SoCs, the bootstrap sequence runs depending on the boot mode. > > I added usleep_range() to make the driver wait until the sequence > finishes. Otherwise, the driver would fail to detect the chip due > to the race between the driver and hardware-controlled sequence. > > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> > Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de> Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next, thanks. Miquel ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/
diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index 699255fb2dd8..f08740ae282b 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -6,6 +6,7 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> #include <linux/ioport.h> @@ -14,6 +15,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/reset.h> #include "denali.h" @@ -22,6 +24,8 @@ struct denali_dt { struct clk *clk; /* core clock */ struct clk *clk_x; /* bus interface clock */ struct clk *clk_ecc; /* ECC circuit clock */ + struct reset_control *rst; /* core reset */ + struct reset_control *rst_reg; /* register reset */ }; struct denali_dt_data { @@ -157,6 +161,14 @@ static int denali_dt_probe(struct platform_device *pdev) if (IS_ERR(dt->clk_ecc)) return PTR_ERR(dt->clk_ecc); + dt->rst = devm_reset_control_get_optional_shared(dev, "nand"); + if (IS_ERR(dt->rst)) + return PTR_ERR(dt->rst); + + dt->rst_reg = devm_reset_control_get_optional_shared(dev, "reg"); + if (IS_ERR(dt->rst_reg)) + return PTR_ERR(dt->rst_reg); + ret = clk_prepare_enable(dt->clk); if (ret) return ret; @@ -172,10 +184,30 @@ static int denali_dt_probe(struct platform_device *pdev) denali->clk_rate = clk_get_rate(dt->clk); denali->clk_x_rate = clk_get_rate(dt->clk_x); - ret = denali_init(denali); + /* + * Deassert the register reset, and the core reset in this order. + * Deasserting the core reset while the register reset is asserted + * will cause unpredictable behavior in the controller. + */ + ret = reset_control_deassert(dt->rst_reg); if (ret) goto out_disable_clk_ecc; + ret = reset_control_deassert(dt->rst); + if (ret) + goto out_assert_rst_reg; + + /* + * When the reset is deasserted, the initialization sequence is kicked + * (bootstrap process). The driver must wait until it finished. + * Otherwise, it will result in unpredictable behavior. + */ + usleep_range(200, 1000); + + ret = denali_init(denali); + if (ret) + goto out_assert_rst; + for_each_child_of_node(dev->of_node, np) { ret = denali_dt_chip_init(denali, np); if (ret) { @@ -190,6 +222,10 @@ static int denali_dt_probe(struct platform_device *pdev) out_remove_denali: denali_remove(denali); +out_assert_rst: + reset_control_assert(dt->rst); +out_assert_rst_reg: + reset_control_assert(dt->rst_reg); out_disable_clk_ecc: clk_disable_unprepare(dt->clk_ecc); out_disable_clk_x: @@ -205,6 +241,8 @@ static int denali_dt_remove(struct platform_device *pdev) struct denali_dt *dt = platform_get_drvdata(pdev); denali_remove(&dt->controller); + reset_control_assert(dt->rst); + reset_control_assert(dt->rst_reg); clk_disable_unprepare(dt->clk_ecc); clk_disable_unprepare(dt->clk_x); clk_disable_unprepare(dt->clk);